roundabout,
created on Wednesday, 19 March 2025, 17:16:56 (1742404616),
received on Wednesday, 19 March 2025, 17:16:59 (1742404619)
Author identity: vlad <vlad.muntoiu@gmail.com>
8a73668a4a8196e18f7769e582ed7fb61121162b
gpanthera.cc
@@ -1,4 +1,6 @@
#include "gpanthera.hh" #include <cassert>#include <iostream> #include <utility> #include <libintl.h>
@@ -30,6 +32,7 @@ namespace gPanthera {
} void init() { g_log_set_always_fatal(G_LOG_LEVEL_CRITICAL);// Set up gettext configurationIsn't bindtextdomain("gpanthera", "./locales"); textdomain("gpanthera");
@@ -430,7 +433,98 @@ namespace gPanthera {
ContentStack::ContentStack(std::shared_ptr<ContentManager> content_manager) : BaseStack(), content_manager(std::move(content_manager)) { this->set_name("gpanthera_content_stack");this->content_manager->add_stack(this); 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(auto page = dynamic_cast<ContentPage*>(widget)) { if(page->get_stack() == this && !this->get_first_child()->get_next_sibling()) { // Don't allow splitting if there are no more pages return false; } if(!(page->content_manager == this->content_manager)) { // If the page is not in the same content manager, reject return false; } double width = this->get_allocated_width(), height = this->get_allocated_height(); // Split based on the drop position if(!(x < width / 4 || x > width * 3 / 4 || y < height / 4 || y > height * 3 / 4)) { // If the drop position is not at a quarter to the edges, move to the same stack this->add_page(*page); return true; } auto new_stack = Gtk::make_managed<ContentStack>(this->content_manager); auto new_switcher = Gtk::make_managed<ContentTabBar>(new_stack, Gtk::Orientation::HORIZONTAL); auto new_notebook = Gtk::make_managed<ContentNotebook>(new_stack, new_switcher); new_stack->add_page(*page); new_stack->set_visible_child(*page); if(this->get_first_child()) { this->set_visible_child(*this->get_first_child()); } if(x < width / 4) { this->make_paned(Gtk::Orientation::HORIZONTAL, Gtk::PackType::START); if(auto paned = dynamic_cast<Gtk::Paned*>(this->get_parent()->get_parent())) { paned->set_start_child(*new_notebook); } } else if(x > width * 3 / 4) { this->make_paned(Gtk::Orientation::HORIZONTAL, Gtk::PackType::END); if(auto paned = dynamic_cast<Gtk::Paned*>(this->get_parent()->get_parent())) { paned->set_end_child(*new_notebook); } } else if(y < height / 4) { this->make_paned(Gtk::Orientation::VERTICAL, Gtk::PackType::START); if(auto paned = dynamic_cast<Gtk::Paned*>(this->get_parent()->get_parent())) { paned->set_start_child(*new_notebook); } } else if(y > height * 3 / 4) { this->make_paned(Gtk::Orientation::VERTICAL, Gtk::PackType::END); if(auto paned = dynamic_cast<Gtk::Paned*>(this->get_parent()->get_parent())) { paned->set_end_child(*new_notebook); } } } return true; // Drop OK }, false); } ContentNotebook::ContentNotebook(ContentStack *stack, ContentTabBar *switcher) : Gtk::Box(Gtk::Orientation::VERTICAL), stack(stack), switcher(switcher) { this->prepend(*switcher); this->append(*stack); } void ContentStack::make_paned(Gtk::Orientation orientation, Gtk::PackType pack_type) { auto *parent = this->get_parent(); if(auto notebook = dynamic_cast<ContentNotebook*>(parent)) { auto *paned = Gtk::make_managed<Gtk::Paned>(orientation); if(auto parent_paned = dynamic_cast<Gtk::Paned*>(notebook->get_parent())) { if(parent_paned->get_start_child() == notebook) { notebook->unparent(); parent_paned->set_start_child(*paned); } else if(parent_paned->get_end_child() == notebook) { notebook->unparent(); parent_paned->set_end_child(*paned); } } else if(auto box = dynamic_cast<Gtk::Box*>(notebook->get_parent())) { auto previous_child = notebook->get_prev_sibling(); box->remove(*notebook); if(previous_child) { paned->insert_after(*box, *previous_child); } else { box->prepend(*paned); } } if(pack_type == Gtk::PackType::START) { paned->set_end_child(*notebook); } else if(pack_type == Gtk::PackType::END) { paned->set_start_child(*notebook); } }} ContentTabBar::ContentTabBar(ContentStack *stack, Gtk::Orientation orientation) : Gtk::Box(orientation), stack(stack) {
@@ -450,9 +544,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<ContentPage*>&>(value).get();if(widget) {if(const auto &widget = static_cast<const Glib::Value<ContentPage*>&>(value).get()) {if(auto page = dynamic_cast<ContentPage*>(widget)) { this->stack->add_page(*page); }
@@ -498,6 +590,32 @@ namespace gPanthera {
child.redock(this); } void ContentStack::remove_with_paned() { if(auto paned = dynamic_cast<Gtk::Paned*>(this->get_parent()->get_parent())) { Gtk::Widget *child = nullptr; if(this->get_parent() == paned->get_start_child()) { child = paned->get_end_child(); paned->property_end_child().reset_value(); } else if(this->get_parent() == paned->get_end_child()) { child = paned->get_start_child(); paned->property_start_child().reset_value(); } else { return; } if(auto parent_paned = dynamic_cast<Gtk::Paned*>(paned->get_parent())) { if(parent_paned->get_start_child() == paned) { parent_paned->set_start_child(*child); } else if(parent_paned->get_end_child() == paned) { parent_paned->set_end_child(*child); } } else if(auto box = dynamic_cast<Gtk::Box*>(paned->get_parent())) { child->insert_after(*box, *paned); paned->unparent(); } } } 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);
@@ -589,6 +707,10 @@ namespace gPanthera {
this->set_opacity(1); return false; }, false); // Pop out if dragged to an external location drag_source->signal_drag_end().connect([this](const Glib::RefPtr<Gdk::Drag>&, bool) { this->set_opacity(1); }, false);} void ContentTab::update_active_style() {
@@ -616,10 +738,19 @@ namespace gPanthera {
} void ContentPage::redock(ContentStack *stack) { this->stack = stack;if(this->last_stack != nullptr) {this->last_stack->remove(*this);// Check if the stack is now empty, in which case we should remove it if(this->stack == stack) { return; } if(this->stack != nullptr) { if(dynamic_cast<ContentNotebook*>(this->stack->get_parent()) && (!this->stack->get_first_child() || (this->stack->get_first_child() == this && !this->stack->get_first_child()->get_next_sibling()))) { this->stack->remove(*this); this->stack->remove_with_paned(); } else if(this->get_parent() == this->stack) { this->stack->remove(*this); }} this->stack = stack;this->last_stack = stack; this->stack->add(*this); }
@@ -629,7 +760,8 @@ namespace gPanthera {
} 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) {Gtk::Box(Gtk::Orientation::VERTICAL, 0), content_manager(std::move(content_manager)), child(child), tab_widget(tab_widget) { this->set_name("gpanthera_content_page");this->append(*child); this->set_tab_widget(tab_widget); this->set_margin_top(0);
@@ -637,8 +769,8 @@ namespace gPanthera {
this->set_margin_start(0); this->set_margin_end(0); if(stack) { stack->add_page(*this);this->content_manager->add_stack(this->stack); this->stack->add_page(*this);} }
@@ -648,5 +780,4 @@ namespace gPanthera {
void ContentPage::set_tab_widget(Gtk::Widget *tab_widget) { this->tab_widget = tab_widget; } } // namespace gPanthera
gpanthera.hh
@@ -151,8 +151,11 @@ namespace gPanthera {
public: sigc::signal<bool(ContentPage*)> signal_detach; std::shared_ptr<ContentManager> content_manager; std::shared_ptr<Gtk::DropTarget> drop_target;explicit ContentStack(std::shared_ptr<ContentManager> content_manager); void make_paned(Gtk::Orientation orientation, Gtk::PackType pack_type);void add_page(ContentPage &child); void remove_with_paned();}; class ContentTabBar : public Gtk::Box {
@@ -169,6 +172,14 @@ namespace gPanthera {
ContentStack *get_stack() const; ~ContentTabBar() override; }; class ContentNotebook : public Gtk::Box { private: ContentStack *stack; ContentTabBar *switcher; public: ContentNotebook(ContentStack *stack, ContentTabBar *switcher); };} // namespace gPanthera #endif // GPANTHERA_LIBRARY_H
panthera-www.cc
@@ -57,8 +57,6 @@ protected:
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_manager->add_stack(content_stack); content->append(*content_stack_switcher);content->append(*content_stack);auto page_1_content = Gtk::make_managed<Gtk::Label>("Page 1..."); auto page_1_tab = new Gtk::Label("Page 1"); auto page_1 = Gtk::make_managed<gPanthera::ContentPage>(content_manager, content_stack, page_1_content, page_1_tab);
@@ -69,6 +67,9 @@ protected:
std::cout << "Detaching " << widget->get_name() << std::endl; return false; // Widget not actually detached }); content->set_name("content_box"); auto content_notebook = Gtk::make_managed<gPanthera::ContentNotebook>(content_stack, content_stack_switcher); content->append(*content_notebook);inner_paned->set_start_child(*content); inner_paned->set_end_child(*dock_stack_1); outer_paned->set_end_child(*inner_paned);