roundabout,
created on Sunday, 23 March 2025, 21:04:51 (1742763891),
received on Sunday, 23 March 2025, 21:04:54 (1742763894)
Author identity: vlad <vlad.muntoiu@gmail.com>
93e8e5734ec398d89985132ce137a361d5dbb726
gpanthera.cc
@@ -9,6 +9,35 @@
#define _(STRING) gettext(STRING)
namespace gPanthera {
sigc::signal<void(double, double)> add_context_menu(Gtk::Widget &widget) {
sigc::signal<void(double, double)> signal;
// Add a context menu to a widget
auto gesture_click = Gtk::GestureClick::create();
gesture_click->set_button(3);
auto gesture_keyboard = Gtk::EventControllerKey::create();
gesture_click->signal_pressed().connect([&widget, signal](int n_press, double x, double y) {
if(n_press == 1) {
signal.emit(x, y);
}
}, false);
gesture_keyboard->signal_key_pressed().connect([&widget, signal](int keyval, int keycode, Gdk::ModifierType state) {
if((keyval == GDK_KEY_F10) && (state == Gdk::ModifierType::SHIFT_MASK) || keyval == GDK_KEY_Menu) {
// auto popover = Gtk::make_managed<Gtk::PopoverMenu>();
// popover->set_menu_model(menu);
// popover->set_has_arrow(false);
// popover->set_halign(Gtk::Align::START);
// popover->set_pointing_to(Gdk::Rectangle(0, 0, 1, 1));
// popover->set_parent(widget);
// popover->popup();
signal.emit(0, 0);
}
return true;
}, false);
widget.add_controller(gesture_click);
widget.add_controller(gesture_keyboard);
return signal;
}
std::vector<Gtk::Widget*> collect_children(Gtk::Widget &widget) {
// Get a vector of the children of a GTK widget, since the container API was removed in GTK 4
std::vector<Gtk::Widget*> children;
@@ -58,7 +87,6 @@ namespace gPanthera {
}
header->add_css_class("gpanthera-dock-titlebar");
auto header_menu_button = Gtk::make_managed<Gtk::MenuButton>();
auto header_menu = Gio::Menu::create();
header_menu_button->set_direction(Gtk::ArrowType::NONE);
// Pane menu
@@ -610,23 +638,29 @@ namespace gPanthera {
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);
g_object_ref(child->gobj()); // Prevent the child from being automatically deleted
paned->property_start_child().reset_value(); // Some hacks because the Gtkmm API isn't complete; it doesn't have an unset_start_child() or unset_end_child()
paned->property_end_child().reset_value();
if(child) {
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())) {
std::cout << child->get_name() << std::endl;
child->insert_after(*box, *paned);
paned->unparent();
g_object_unref(child->gobj());
}
} else if(auto box = dynamic_cast<Gtk::Box*>(paned->get_parent())) {
child->insert_after(*box, *paned);
paned->unparent();
}
}
}
@@ -725,6 +759,39 @@ namespace gPanthera {
this->set_opacity(1);
return false;
}, false);
// Provide a context menu
context_menu = Gio::Menu::create();
auto action_group = Gio::SimpleActionGroup::create();
this->insert_action_group("win", action_group);
auto close_action = Gio::SimpleAction::create("close");
close_action->signal_activate().connect([this](const Glib::VariantBase&) {
if(!this->page->signal_close.emit()) {
if(this->page->get_next_sibling()) {
this->page->get_stack()->set_visible_child(*this->page->get_next_sibling());
} else if(this->page->get_prev_sibling()) {
this->page->get_stack()->set_visible_child(*this->page->get_prev_sibling());
}
this->page->redock(nullptr);
}
});
action_group->add_action(close_action);
context_menu->append(_("Close"), "win.close");
// TODO: Add more actions: "New window", "Split left", "Split right", "Split top", "Split bottom", "Close all", "Close others", "Close to the right", "Close to the left"
this->insert_action_group("win", action_group);
// Attach the context menu to the button
auto context_menu_signal = add_context_menu(*this);
context_menu_signal.connect([this, action_group](double x, double y) {
if(this->context_menu) {
auto popover = Gtk::make_managed<Gtk::PopoverMenu>();
popover->set_menu_model(context_menu);
popover->set_parent(*this);
popover->set_has_arrow(false);
popover->set_halign(Gtk::Align::START);
popover->set_pointing_to(Gdk::Rectangle(x, y, 1, 1));
popover->popup();
}
});
}
void ContentTab::update_active_style() {
@@ -747,11 +814,28 @@ namespace gPanthera {
return this->tab_widget;
}
Gtk::Stack *ContentPage::get_stack() const {
ContentStack *ContentPage::get_stack() const {
return this->stack;
}
void ContentPage::redock(ContentStack *stack) {
if(stack == nullptr) {
if(this->stack) {
this->stack->remove(*this);
if(dynamic_cast<ContentNotebook*>(this->stack->get_parent()) && !this->stack->get_first_child()) {
this->stack->remove_with_paned();
}
}
auto old_stack = this->stack;
if(old_stack) {
if(!old_stack->get_first_child()) {
old_stack->signal_leave_empty.emit();
}
}
this->stack = nullptr;
this->last_stack = nullptr;
return;
}
// Check if the stack is now empty, in which case we should remove it
if(this->stack == stack) {
return;
gpanthera.hh
@@ -10,6 +10,8 @@
namespace gPanthera {
void init();
// Utility function to create a signal that fires when a context menu is requested
sigc::signal<void(double, double)> add_context_menu(Gtk::Widget &widget);
class DockStack;
class DockWindow;
@@ -27,6 +29,7 @@ namespace gPanthera {
Glib::RefPtr<Gio::SimpleActionGroup> action_group;
public:
std::shared_ptr<LayoutManager> layout;
Glib::RefPtr<Gio::Menu> header_menu = Gio::Menu::create();
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;
@@ -119,18 +122,19 @@ namespace gPanthera {
class ContentPage : public Gtk::Box {
private:
Gtk::Widget *tab_widget;
ContentStack *stack;
ContentStack *stack = nullptr;
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;
sigc::signal<bool()> signal_close;
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;
ContentStack *get_stack() const;
};
class ContentTab : public Gtk::ToggleButton {
@@ -138,6 +142,7 @@ namespace gPanthera {
std::shared_ptr<Gtk::DragSource> drag_source;
std::shared_ptr<Gtk::DropTarget> drop_target;
Glib::Value<ContentPage*> value;
Glib::RefPtr<Gio::Menu> context_menu = Gio::Menu::create();
void update_active_style();
sigc::connection active_style_handler;
public: