roundabout,
created on Wednesday, 12 March 2025, 17:58:03 (1741802283),
received on Wednesday, 12 March 2025, 17:58:08 (1741802288)
Author identity: vlad <vlad.muntoiu@gmail.com>
bef263ceaadd83b169f559750df4dbeb6358e74c
gpanthera.cc
@@ -30,9 +30,7 @@ namespace gPanthera {
} void init() { // Set up gettext configurationsetlocale(LC_ALL, "");// Set up gettext configurationIsn'tbindtextdomain("gpanthera", "./locales"); textdomain("gpanthera"); }
@@ -127,7 +125,7 @@ namespace gPanthera {
this->prepend(*this->header); this->window->unset_child(); this->window->close(); } else if(this->stack == this->get_parent()) {} else if(this->get_parent() && this->stack == this->get_parent()) {this->stack->remove(*this); } this->stack = stack;
@@ -251,7 +249,7 @@ namespace gPanthera {
this->add_controller(drop_target); // Process dropped buttons drop_target->signal_drop().connect([this](const Glib::ValueBase& value, double x, double y) { const auto &widget = static_cast<const Glib::Value<Gtk::Widget*>&>(value).get();const auto &widget = static_cast<const Glib::Value<DockablePane*>&>(value).get();if(widget) { if(auto pane = dynamic_cast<DockablePane*>(widget)) {
@@ -290,7 +288,6 @@ namespace gPanthera {
drag_source->set_exclusive(false); // This is to prevent the click handler from taking over grabbing the button drag_source->set_propagation_phase(Gtk::PropagationPhase::CAPTURE); value = Glib::Value<Gtk::Widget*>();value.init(DockablePane::get_type()); value.set(pane); // Add the drag source to the button
@@ -325,10 +322,14 @@ namespace gPanthera {
this->add_controller(drop_target); // Process dropped buttons by inserting them after the current button drop_target->signal_drop().connect([this](const Glib::ValueBase& value, double x, double y) { const auto &widget = static_cast<const Glib::Value<Gtk::Widget*>&>(value).get();const auto &widget = static_cast<const Glib::Value<DockablePane*>&>(value).get();if(widget) { if(auto pane = dynamic_cast<DockablePane*>(widget)) { if(pane->layout != this->pane->layout) { // If the pane is not in the same layout manager, reject return false; }auto switcher = dynamic_cast<DockStackSwitcher*>(this->get_parent()); if(switcher) { auto *stack = switcher->get_stack();
@@ -414,4 +415,155 @@ namespace gPanthera {
: BaseStack(), content_manager(std::move(content_manager)) { this->content_manager->add_stack(this); } ContentTabBar::ContentTabBar(ContentStack *stack, Gtk::Orientation orientation) : Gtk::Box(orientation), stack(stack) { this->get_style_context()->add_class("gpanthera-content-tab-bar"); this->set_margin_top(0); this->set_margin_bottom(0); this->set_margin_start(0); this->set_margin_end(0); auto update_callback = [this](Gtk::Widget*) { this->update_buttons(); }; this->property_accessible_role().set_value(Gtk::Accessible::Role::TAB_LIST); stack->signal_child_added.connect(update_callback); stack->signal_child_removed.connect(update_callback); drop_target = Gtk::DropTarget::create(ContentPage::get_type(), Gdk::DragAction::MOVE); this->add_controller(drop_target); // Process dropped buttons drop_target->signal_drop().connect([this](const Glib::ValueBase& value, double x, double y) { const auto &widget = static_cast<const Glib::Value<ContentPage*>&>(value).get(); if(widget) { if(auto page = dynamic_cast<ContentPage*>(widget)) { this->stack->add_page(*page); } } return true; // Drop OK }, false); } ContentTabBar::~ContentTabBar() { add_handler.disconnect(); remove_handler.disconnect(); } ContentStack *ContentTabBar::get_stack() const { return stack; } void ContentTabBar::update_buttons() { // Clear the old buttons auto old_buttons = collect_children(*this); for(auto *button : old_buttons) { remove(*button); } ContentTab* first_child = nullptr; for(auto *widget = stack->get_first_child(); widget; widget = widget->get_next_sibling()) { if(auto pane = dynamic_cast<ContentPage*>(widget)) { auto *button = Gtk::make_managed<ContentTab>(pane); if(!first_child) { first_child = button; } else { button->set_group(*first_child); } } } } void ContentStack::add_page(ContentPage &child) { child.redock(this); } ContentTab::ContentTab(ContentPage *page) : Gtk::ToggleButton(), page(page) { this->set_child(*page->get_tab_widget()); this->property_accessible_role().set_value(Gtk::Accessible::Role::TAB); this->set_halign(Gtk::Align::CENTER); this->set_valign(Gtk::Align::CENTER); this->get_style_context()->add_class("toggle"); this->get_style_context()->add_class("gpanthera-content-tab"); // Add/remove CSS classes when the pane is shown/hidden active_style_handler = this->page->get_stack()->property_visible_child_name().signal_changed().connect([this]() { this->update_active_style(); }); drag_source = Gtk::DragSource::create(); drag_source->set_exclusive(false); // This is to prevent the click handler from taking over grabbing the button drag_source->set_propagation_phase(Gtk::PropagationPhase::CAPTURE); value.init(ContentPage::get_type()); value.set(page); // Add the drag source to the button this->add_controller(drag_source); this->signal_clicked().connect([this, page]() { page->get_stack()->set_visible_child(*page); }); // Provide the drag data drag_source->signal_prepare().connect([this](double, double) { drag_source->set_actions(Gdk::DragAction::MOVE); auto const paintable = Gtk::WidgetPaintable::create(); paintable->set_widget(*this); drag_source->set_icon(paintable, 0, 0); return Gdk::ContentProvider::create(value); }, false); // Pop out if dragged to an external location // TODO: Implement this, allow defining custom behavior at the content manager level } void ContentTab::update_active_style() { if(this->page->get_stack()->get_visible_child() == this->page) { this->add_css_class("checked"); this->add_css_class("gpanthera-dock-button-active"); this->set_active(true); } else { this->remove_css_class("checked"); this->remove_css_class("gpanthera-dock-button-active"); this->set_active(false); } } ContentTab::~ContentTab() { active_style_handler.disconnect(); } Gtk::Widget *ContentPage::get_tab_widget() const { return this->tab_widget; } Gtk::Stack *ContentPage::get_stack() const { return this->stack; } void ContentPage::redock(ContentStack *stack) { this->stack = stack; if(this->last_stack != nullptr) { this->last_stack->remove(*this); } this->last_stack = stack; this->stack->add(*this, this->get_child()->get_name()); } Gtk::Widget *ContentPage::get_child() const { return this->child; } ContentPage::ContentPage(std::shared_ptr<ContentManager> content_manager, ContentStack *stack, Gtk::Widget *child, Gtk::Widget *tab_widget) : Gtk::Box(Gtk::Orientation::VERTICAL, 0), content_manager(std::move(content_manager)), stack(stack), child(child), tab_widget(tab_widget) { this->content_manager->add_stack(this->stack); this->append(*child); this->set_tab_widget(tab_widget); this->set_margin_top(0); this->set_margin_bottom(0); this->set_margin_start(0); this->set_margin_end(0); } ContentManager::ContentManager() : Glib::ObjectBase("ContentManager") { } void ContentPage::set_tab_widget(Gtk::Widget *tab_widget) { this->tab_widget = tab_widget; }} // namespace gPanthera
gpanthera.hh
@@ -24,9 +24,9 @@ namespace gPanthera {
DockWindow *window = nullptr; std::unique_ptr<Gtk::HeaderBar> header; Gtk::Widget *child; std::shared_ptr<LayoutManager> layout;Glib::RefPtr<Gio::SimpleActionGroup> action_group; public: std::shared_ptr<LayoutManager> layout;DockStack *last_stack = nullptr; DockablePane(std::shared_ptr<LayoutManager> layout, Gtk::Widget &child, const Glib::ustring &name, const Glib::ustring &label, Gtk::Image *icon, DockStack *stack = nullptr, Gtk::Widget *custom_header = nullptr); Gtk::Stack *get_stack() const;
@@ -84,12 +84,12 @@ namespace gPanthera {
private: sigc::connection active_style_handler; std::shared_ptr<Gtk::DragSource> drag_source; Glib::Value<Gtk::Widget*> value;Glib::Value<DockablePane*> value;void update_active_style(); public: DockablePane *pane; explicit DockButton(DockablePane *pane); ~DockButton();~DockButton() override;}; class DockStackSwitcher : public Gtk::Box {
@@ -116,12 +116,56 @@ namespace gPanthera {
void remove_stack(ContentStack *stack); }; class ContentStack : public BaseStack {class ContentPage : public Gtk::Box {private: Gtk::Widget *tab_widget; ContentStack *stack; Gtk::Widget *child; public:std::shared_ptr<ContentManager> content_manager; ContentStack *last_stack = nullptr; ContentPage(std::shared_ptr<ContentManager> content_manager, ContentStack *stack, Gtk::Widget *child, Gtk::Widget *tab_widget); Gtk::Widget *get_child() const; Gtk::Widget *get_tab_widget() const; void redock(ContentStack *stack); void set_tab_widget(Gtk::Widget *tab_widget); void set_child(Gtk::Widget *child); Gtk::Stack *get_stack() const; }; class ContentTab : public Gtk::ToggleButton { private: std::shared_ptr<Gtk::DragSource> drag_source; Glib::Value<ContentPage*> value; void update_active_style(); sigc::connection active_style_handler;public: explicit ContentTab(ContentPage *page); ContentPage *page; ~ContentTab() override; }; class ContentStack : public BaseStack { private: public: std::shared_ptr<ContentManager> content_manager;explicit ContentStack(std::shared_ptr<ContentManager> content_manager); void add_page(Gtk::Widget &child);void add_page(ContentPage &child); }; class ContentTabBar : public Gtk::Box { // TODO; should be similar to DockStackSwitcher // Dragging a tab to an empty space should split the view; // dragging a tab outside the window should pop it out with user-defined behaviour private: ContentStack *stack; sigc::connection add_handler, remove_handler; std::shared_ptr<Gtk::DropTarget> drop_target; public: void update_buttons(); explicit ContentTabBar(ContentStack *stack, Gtk::Orientation orientation = Gtk::Orientation::HORIZONTAL); ContentStack *get_stack() const; ~ContentTabBar() override;}; } // namespace gPanthera
panthera-www.cc
@@ -53,6 +53,15 @@ protected:
outer_paned->set_start_child(*dock_stack_2); auto inner_paned = Gtk::make_managed<Gtk::Paned>(Gtk::Orientation::VERTICAL); auto content = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 0); // auto content_manager = std::make_shared<gPanthera::ContentManager>(); // auto content_stack = Gtk::make_managed<gPanthera::ContentStack>(content_manager); // auto content_stack_switcher = Gtk::make_managed<gPanthera::ContentTabBar>(content_stack, Gtk::Orientation::HORIZONTAL); // content->append(*content_stack_switcher); // content->append(*content_stack); // auto page_1_content = Gtk::make_managed<Gtk::Label>("Page 1..."); // auto page_1_tab = Gtk::make_managed<Gtk::Label>("Page 1"); // auto page_1 = Gtk::make_managed<gPanthera::ContentPage>(content_manager, content_stack, page_1_content, page_1_tab); // content_stack->add_page(*page_1);inner_paned->set_start_child(*content); inner_paned->set_end_child(*dock_stack_1); outer_paned->set_end_child(*inner_paned);