From f032f8234cd88795cc3ee52b0793432d3d903066 Mon Sep 17 00:00:00 2001 From: trigg Date: Sat, 21 Mar 2026 18:42:27 +0000 Subject: [PATCH 01/18] panel: use less menubuttons panel: start standardising on popups --- src/panel/widgets/clock.cpp | 10 +- src/panel/widgets/menu.cpp | 54 +---- src/panel/widgets/mixer/mixer.cpp | 52 ++--- src/panel/widgets/mixer/mixer.hpp | 1 - src/panel/widgets/mixer/wp-common.cpp | 18 +- src/panel/widgets/network.cpp | 7 +- .../notifications/notification-center.cpp | 17 +- src/panel/widgets/volume.cpp | 22 +- src/panel/widgets/volume.hpp | 1 - src/panel/widgets/workspace-switcher.cpp | 11 +- src/util/wf-autohide-window.cpp | 11 +- src/util/wf-autohide-window.hpp | 5 + src/util/wf-popover.cpp | 190 +++++++++++++++--- src/util/wf-popover.hpp | 67 +++++- 14 files changed, 300 insertions(+), 166 deletions(-) diff --git a/src/panel/widgets/clock.cpp b/src/panel/widgets/clock.cpp index 58396823..1f8c94df 100644 --- a/src/panel/widgets/clock.cpp +++ b/src/panel/widgets/clock.cpp @@ -3,9 +3,9 @@ void WayfireClock::init(Gtk::Box *container) { - button = std::make_unique("panel"); + button = std::make_unique("panel", "clock"); button->add_css_class("clock"); - button->set_child(label); + button->append(label); button->show(); label.set_justify(Gtk::Justification::CENTER); label.show(); @@ -13,10 +13,8 @@ void WayfireClock::init(Gtk::Box *container) update_label(); calendar.show(); - button->get_popover()->add_css_class("clock-popover"); - button->get_children()[0]->add_css_class("flat"); - button->get_popover()->set_child(calendar); - btn_sig = button->get_popover()->signal_show().connect( + button->set_popover_child(calendar); + btn_sig = button->signal_popup().connect( sigc::mem_fun(*this, &WayfireClock::on_calendar_shown)); container->append(*button); diff --git a/src/panel/widgets/menu.cpp b/src/panel/widgets/menu.cpp index a9a58841..677b4401 100644 --- a/src/panel/widgets/menu.cpp +++ b/src/panel/widgets/menu.cpp @@ -578,13 +578,7 @@ bool WayfireMenu::update_icon() void WayfireMenu::setup_popover_layout() { - if (menu_fullscreen) - { - fullscreen.set_child(popover_layout_box); - } else - { - button->get_popover()->set_child(popover_layout_box); - } + button->set_popover_child(popover_layout_box); flowbox.set_selection_mode(Gtk::SelectionMode::SINGLE); flowbox.set_activate_on_single_click(true); @@ -651,7 +645,7 @@ void WayfireMenu::setup_popover_layout() return true; } else if (keyval == GDK_KEY_Escape) { - button->get_popover()->hide(); + button->popdown(); fullscreen.hide(); return true; } else if ((keyval == GDK_KEY_Up) || @@ -687,7 +681,7 @@ void WayfireMenu::setup_popover_layout() } }, false)); popover_layout_box.add_controller(typing_gesture); - signals.push_back(button->get_popover()->signal_closed().connect([=] () + signals.push_back(button->signal_popdown().connect([=] () { if (!force_show_popup.value()) { @@ -903,7 +897,7 @@ WayfireLogoutUI::~WayfireLogoutUI() void WayfireMenu::on_logout_click() { - button->get_popover()->hide(); + button->popdown(); fullscreen.hide(); if (!menu_logout_command.value().empty()) { @@ -991,14 +985,13 @@ void WayfireMenu::init(Gtk::Box *container) menu_show_categories.set_callback([=] () { update_popover_layout(); }); menu_list.set_callback([=] () { update_popover_layout(); }); - button = std::make_unique("panel"); + button = std::make_unique("panel", "menu"); fullscreen.add_css_class("menu-fullscreen"); - button->set_child(main_image); + button->append(main_image); button->add_css_class("menu-button"); button->add_css_class("flat"); - button->get_popover()->add_css_class("menu-popover"); button->get_children()[0]->add_css_class("flat"); - signals.push_back(button->get_popover()->signal_show().connect( + signals.push_back(button->signal_popup().connect( sigc::mem_fun(*this, &WayfireMenu::on_popover_shown))); /* Prepare fullscreen layer */ @@ -1020,20 +1013,7 @@ void WayfireMenu::init(Gtk::Box *container) signals.push_back(button->property_scale_factor().signal_changed().connect( [=] () {update_icon(); })); - menu_fullscreen.set_callback([=] () - { - fullscreen.hide(); - button->set_active(false); - if (menu_fullscreen) - { - gtk_popover_set_child(button->get_popover()->gobj(), nullptr); - fullscreen.set_child(popover_layout_box); - } else - { - gtk_window_set_child(fullscreen.gobj(), nullptr); - button->get_popover()->set_child(popover_layout_box); - } - }); + button->set_popover_child(popover_layout_box); container->append(box); box.append(*button); @@ -1097,27 +1077,13 @@ void WayfireMenu::update_content_width() void WayfireMenu::toggle_menu() { search_entry.set_text(""); - if (menu_fullscreen) - { - if (fullscreen.is_visible()) - { - fullscreen.hide(); - } else - { - fullscreen.show(); - on_popover_shown(); - } - return; - } - - button->set_active(!button->get_active()); + button->toggle(); } void WayfireMenu::hide_menu() { - button->set_active(false); - fullscreen.hide(); + button->popdown(); } void WayfireMenu::set_category(std::string in_category) diff --git a/src/panel/widgets/mixer/mixer.cpp b/src/panel/widgets/mixer/mixer.cpp index 89e1e8fc..145539a6 100644 --- a/src/panel/widgets/mixer/mixer.cpp +++ b/src/panel/widgets/mixer/mixer.cpp @@ -11,7 +11,7 @@ bool WayfireMixer::on_popover_timeout(int timer) { popover_timeout.disconnect(); - popover->popdown(); + button->popdown(); return false; } @@ -73,20 +73,20 @@ void WayfireMixer::reload_config() // unschedule hiding cancel_popover_timeout(); - if ((popover->get_child() == (Gtk::Widget*)&master_box) && popover->is_visible()) + if ((button->get_popover_child() == (Gtk::Widget*)&master_box) && button->is_popup_visible()) { - popover->popdown(); + button->popdown(); return; } - if (!popover->is_visible()) + if (!button->is_popup_visible()) { - button->set_active(true); + button->popup(); } - if (popover->get_child() != (Gtk::Widget*)&master_box) + if (button->get_popover_child() != (Gtk::Widget*)&master_box) { - popover->set_child(master_box); + button->set_popover_child(master_box); popover_timeout.disconnect(); } }; @@ -100,20 +100,20 @@ void WayfireMixer::reload_config() return; // no quick_target means we have nothing to show } - if ((popover->get_child() == quick_target.get()) && popover->is_visible()) + if ((button->get_popover_child() == quick_target.get()) && button->is_popup_visible()) { - popover->popdown(); + button->popdown(); return; } - if (!popover->is_visible()) + if (!button->is_popup_visible()) { - button->set_active(true); + button->popup(); } - if (popover->get_child() != quick_target.get()) + if (button->get_popover_child() != quick_target.get()) { - popover->set_child(*quick_target); + button->set_popover_child(*quick_target); popover_timeout.disconnect(); } }; @@ -137,13 +137,13 @@ void WayfireMixer::reload_config() { // unschedule hiding cancel_popover_timeout(); - if (popover->get_child() != (Gtk::Widget*)&master_box) + if (button->get_popover_child() != (Gtk::Widget*)&master_box) { - popover->set_child(master_box); + button->set_popover_child(master_box); // popdown so that when the click is processed, the popover is down, and thus pops up // not the prettiest result, as it visibly closes instead of just replacing, but i’m not sure // how to make it better - button->set_active(false); + button->popdown(); } }); } @@ -160,11 +160,11 @@ void WayfireMixer::reload_config() return; } - if (popover->get_child() != quick_target.get()) + if (button->get_popover_child() != quick_target.get()) { - popover->set_child(*quick_target); + button->set_popover_child(*quick_target); // same as above - button->set_active(false); + button->popdown(); } }); } @@ -219,12 +219,9 @@ void WayfireMixer::init(Gtk::Box *container) { // sets up the "widget part" - button = std::make_unique("panel"); + button = std::make_unique("panel", "mixer"); button->add_css_class("widget-icon"); - button->add_css_class("mixer"); - button->add_css_class("flat"); - button->get_children()[0]->add_css_class("flat"); - button->set_child(main_image); + button->append(main_image); button->show(); sinks_box.add_css_class("outputs"); sources_box.add_css_class("inputs"); @@ -232,10 +229,7 @@ void WayfireMixer::init(Gtk::Box *container) out_in_wall.add_css_class("out-in"); in_streams_wall.add_css_class("in-streams"); - popover = button->get_popover(); - popover->set_child(master_box); - popover->set_autohide(false); - popover->add_css_class("mixer-popover"); + button->set_popover_child(master_box); // scroll to change volume of the object targetted by the quick_target widget auto scroll_gesture = Gtk::EventControllerScroll::create(); @@ -318,7 +312,6 @@ void WayfireMixer::init(Gtk::Box *container) // add to the actual container container->append(*button); - button->set_child(main_image); // if there is no audio device nor application, the quick target will not be set // and the widget will apear empty. Calling this here to always have the OOR icon. @@ -357,7 +350,6 @@ void WayfireMixer::set_quick_target_from(MixerControl *from) WayfireMixer::~WayfireMixer() { WpCommon::get().rem_widget(this); - gtk_widget_unparent(GTK_WIDGET(popover->gobj())); popover_timeout.disconnect(); volume_changed_signal.disconnect(); left_conn.disconnect(); diff --git a/src/panel/widgets/mixer/mixer.hpp b/src/panel/widgets/mixer/mixer.hpp index fec78d07..7a945875 100644 --- a/src/panel/widgets/mixer/mixer.hpp +++ b/src/panel/widgets/mixer/mixer.hpp @@ -55,7 +55,6 @@ class WayfireMixer : public WayfireWidget WfOption popup_on_change{"panel/mixer_popup_on_change"}; std::unique_ptr button; - Gtk::Popover *popover; /* * the "quick_target" is the representation of the audio channel that shows it’s volume diff --git a/src/panel/widgets/mixer/wp-common.cpp b/src/panel/widgets/mixer/wp-common.cpp index 422d8e02..bf61ad3f 100644 --- a/src/panel/widgets/mixer/wp-common.cpp +++ b/src/panel/widgets/mixer/wp-common.cpp @@ -176,10 +176,10 @@ void WpCommon::add_object_to_widget(WpPipewireObject *object, WayfireMixer *widg { widget->set_quick_target_from(control); // if the full mixer is not shown, change the popover child - if ((widget->popover->get_child() != - &(widget->master_box)) && widget->popover->is_visible()) + if ((widget->button->get_popover_child() != + &(widget->master_box)) && widget->button->is_popup_visible()) { - widget->popover->set_child(*widget->quick_target); + widget->button->set_popover_child(*widget->quick_target); } widget->update_icon(); @@ -273,21 +273,21 @@ void WpCommon::on_mixer_changed(gpointer mixer_api, guint id, gpointer data) update_icons(); // if the mixer is currently being displayed, stop there - if ((widget->popover->get_child() == - &(widget->master_box)) && widget->popover->is_visible()) + if ((widget->button->get_popover_child() == + &(widget->master_box)) && widget->button->is_popup_visible()) { continue; } if (widget->quick_target && - (!widget->popover->is_visible() || - (widget->popover->get_child() != (MixerControl*)&widget->quick_target))) + (!widget->button->is_popup_visible() || + (widget->button->get_popover_child() != (MixerControl*)&widget->quick_target))) { // put the quick_target in the popover and show - widget->popover->set_child(*widget->quick_target); + widget->button->set_popover_child(*widget->quick_target); if (widget->popup_on_change && change) { - widget->popover->popup(); + widget->button->popup(); } } diff --git a/src/panel/widgets/network.cpp b/src/panel/widgets/network.cpp index c2269885..24404583 100644 --- a/src/panel/widgets/network.cpp +++ b/src/panel/widgets/network.cpp @@ -14,17 +14,16 @@ WayfireNetworkInfo::WayfireNetworkInfo() void WayfireNetworkInfo::init(Gtk::Box *container) { network_manager = NetworkManager::getInstance(); - button = std::make_unique("panel"); + button = std::make_unique("panel", "network"); button->add_css_class("widget-icon"); button->add_css_class("flat"); button->add_css_class("network"); container->append(*button); - button->set_child(button_content); + button->append(button_content); button->add_css_class("flat"); - button->set_has_frame(false); - button->get_popover()->set_child(control); + button->set_popover_child(control); button_content.set_valign(Gtk::Align::CENTER); button_content.append(icon); diff --git a/src/panel/widgets/notifications/notification-center.cpp b/src/panel/widgets/notifications/notification-center.cpp index 2ffa3007..72550cf9 100644 --- a/src/panel/widgets/notifications/notification-center.cpp +++ b/src/panel/widgets/notifications/notification-center.cpp @@ -9,23 +9,21 @@ void WayfireNotificationCenter::init(Gtk::Box *container) { - button = std::make_unique("panel"); + button = std::make_unique("panel", "notification"); icon.add_css_class("widget-icon"); button->add_css_class("notification-center"); button->get_children()[0]->add_css_class("flat"); updateIcon(); - button->set_child(icon); + button->set_popover_child(icon); container->append(*button); - auto *popover = button->get_popover(); - popover->set_size_request(WIDTH, HEIGHT); - popover->add_css_class("notification-popover"); + scrolled_window.set_size_request(WIDTH, HEIGHT); box.set_valign(Gtk::Align::START); box.set_orientation(Gtk::Orientation::VERTICAL); scrolled_window.set_child(box); - popover->set_child(scrolled_window); + button->set_popover_child(scrolled_window); button->set_tooltip_text("Middle click to toggle DND mode."); @@ -79,14 +77,13 @@ void WayfireNotificationCenter::newNotification(Notification::id_type id, bool s widget->set_reveal_child(); if (show_popup && !dnd_enabled || (show_critical_in_dnd && (notification.hints.urgency == 2))) { - auto *popover = button->get_popover(); - if ((timeout > 0) && (!popover_timeout.empty() || !popover->is_visible())) + if ((timeout > 0) && (!popover_timeout.empty() || !button->is_popup_visible())) { popover_timeout.disconnect(); popover_timeout = Glib::signal_timeout().connect( [=] { - popover->popdown(); + button->popdown(); button->set_keyboard_interactive(); popover_timeout.disconnect(); return true; @@ -95,7 +92,7 @@ void WayfireNotificationCenter::newNotification(Notification::id_type id, bool s } button->set_keyboard_interactive(false); - popover->popup(); + button->popup(); } } diff --git a/src/panel/widgets/volume.cpp b/src/panel/widgets/volume.cpp index 1aca2724..2d35e0f6 100644 --- a/src/panel/widgets/volume.cpp +++ b/src/panel/widgets/volume.cpp @@ -19,7 +19,7 @@ void WayfireVolume::update_icon() bool WayfireVolume::on_popover_timeout(int timer) { - popover->popdown(); + button->popdown(); return false; } @@ -42,9 +42,9 @@ void WayfireVolume::set_volume(pa_volume_t volume, set_volume_flags_t flags) if (flags & VOLUME_FLAG_SHOW_POPOVER) { - if (!popover->is_visible()) + if (!button->is_popup_visible()) { - popover->popup(); + button->popup(); } else { check_set_popover_timeout(); @@ -137,7 +137,7 @@ void WayfireVolume::on_volume_value_changed() void WayfireVolume::init(Gtk::Box *container) { - button = std::make_unique("panel"); + button = std::make_unique("panel", "volume"); /* Setup button */ button->add_css_class("widget-icon"); @@ -168,10 +168,7 @@ void WayfireVolume::init(Gtk::Box *container) button->add_controller(scroll_gesture); /* Setup popover */ - popover = button->get_popover(); - popover->set_autohide(false); - popover->set_child(volume_scale); - popover->add_css_class("volume-popover"); + button->set_popover_child(volume_scale); auto scroll_gesture2 = Gtk::EventControllerScroll::create(); signals.push_back(scroll_gesture2->signal_scroll().connect([=] (double dx, double dy) @@ -217,13 +214,13 @@ void WayfireVolume::init(Gtk::Box *container) }); left_click_gesture->signal_released().connect([=] (int count, double x, double y) { - if (popover->is_visible()) + if (button->is_popup_visible()) { - popover->popdown(); + button->popdown(); popover_timeout.disconnect(); } else { - popover->popup(); + button->popup(); check_set_popover_timeout(); } }); @@ -258,12 +255,11 @@ void WayfireVolume::init(Gtk::Box *container) /* Setup layout */ container->append(*button); - button->set_child(main_image); + button->set_popover_child(main_image); } WayfireVolume::~WayfireVolume() { - gtk_widget_unparent(GTK_WIDGET(popover->gobj())); disconnect_gvc_stream_signals(); if (notify_default_sink_changed) diff --git a/src/panel/widgets/volume.hpp b/src/panel/widgets/volume.hpp index d622b013..69f934f1 100644 --- a/src/panel/widgets/volume.hpp +++ b/src/panel/widgets/volume.hpp @@ -14,7 +14,6 @@ class WayfireVolume : public WayfireWidget Gtk::Image main_image; WayfireAnimatedScale volume_scale; std::unique_ptr button; - Gtk::Popover *popover; WfOption timeout{"panel/volume_display_timeout"}; WfOption scroll_sensitivity{"panel/volume_scroll_sensitivity"}; diff --git a/src/panel/widgets/workspace-switcher.cpp b/src/panel/widgets/workspace-switcher.cpp index 0071061f..56f25e6d 100644 --- a/src/panel/widgets/workspace-switcher.cpp +++ b/src/panel/widgets/workspace-switcher.cpp @@ -20,9 +20,7 @@ void WayfireWorkspaceSwitcher::init(Gtk::Box *container) box.add_css_class("workspace-switcher"); box.add_css_class("flat"); - button = std::make_unique("panel"); - button->get_children()[0]->add_css_class("flat"); - button->get_children()[0]->add_css_class("workspace-switcher"); + button = std::make_unique("panel", "workspace-switcher", "workspace_switcher"); ipc_client->subscribe(this, {"view-mapped"}); ipc_client->subscribe(this, {"view-focused"}); @@ -51,8 +49,8 @@ void WayfireWorkspaceSwitcher::init(Gtk::Box *container) switcher_box.append(overlay); } else // "grid_popover" { - button->get_popover()->set_child(overlay); - button->set_child(mini_grid); + button->set_popover_child(overlay); + button->append(mini_grid); switcher_box.append(*button); } @@ -474,8 +472,7 @@ void WayfireWorkspaceSwitcher::grid_process_workspaces(wf::json_t workspace_data } clear_box(); - button->m_popover.set_parent(mini_grid); - button->m_popover.set_child(overlay); + button->set_popover_child(overlay); overlay.set_child(switch_grid); overlay.add_css_class("workspace"); overlay.signal_get_child_position().connect(sigc::mem_fun(*this, diff --git a/src/util/wf-autohide-window.cpp b/src/util/wf-autohide-window.cpp index 194e5eb5..b5d7293f 100644 --- a/src/util/wf-autohide-window.cpp +++ b/src/util/wf-autohide-window.cpp @@ -1,5 +1,6 @@ #include "wf-autohide-window.hpp" #include "wayfire-shell-unstable-v2-client-protocol.h" +#include "wf-popover.hpp" #include #include @@ -594,13 +595,11 @@ void WayfireAutohidingWindow::set_active_popover(WayfireMenuButton& button) if (this->active_button) { this->popover_hide.disconnect(); - this->active_button->set_active(false); - this->active_button->get_popover()->popdown(); } this->active_button = &button; this->popover_hide = - this->active_button->m_popover.signal_hide().connect( + this->active_button->signal_popdown().connect( [this, &button] () { unset_active_popover(button); }); } @@ -628,7 +627,6 @@ void WayfireAutohidingWindow::unset_active_popover(WayfireMenuButton& button) } this->active_button->set_has_focus(false); - this->active_button->set_active(false); this->active_button = nullptr; this->popover_hide.disconnect(); @@ -640,6 +638,11 @@ void WayfireAutohidingWindow::unset_active_popover(WayfireMenuButton& button) } } +WayfireMenuButton*WayfireAutohidingWindow::get_active_popover() +{ + return this->active_button; +} + void WayfireAutohidingWindow::setup_autohide() { if (!output->output && autohide_opt) diff --git a/src/util/wf-autohide-window.hpp b/src/util/wf-autohide-window.hpp index 4ac84825..f257b1e2 100644 --- a/src/util/wf-autohide-window.hpp +++ b/src/util/wf-autohide-window.hpp @@ -85,6 +85,11 @@ class WayfireAutohidingWindow : public Gtk::Window */ void unset_active_popover(WayfireMenuButton& popover); + /* + * Get Active popover or null + */ + WayfireMenuButton *get_active_popover(); + private: WayfireOutput *output; diff --git a/src/util/wf-popover.cpp b/src/util/wf-popover.cpp index 045216e1..d5667a67 100644 --- a/src/util/wf-popover.cpp +++ b/src/util/wf-popover.cpp @@ -1,37 +1,118 @@ #include "wf-popover.hpp" +#include "gtkmm/eventcontrollermotion.h" +#include "gtkmm/gesture.h" +#include "gtkmm/gestureclick.h" +#include "gtkmm/widget.h" #include "wf-autohide-window.hpp" +#include +#include -WayfireMenuButton::WayfireMenuButton(const std::string& section) : +/* Helper to get panel from button. NULL if not added to one */ +WayfireAutohidingWindow *get_panel(WayfireMenuButton *button) +{ + auto window = button->get_root(); + if (!window) + { + return NULL; + } + + auto autohide_window = dynamic_cast(window); + return autohide_window; +} + +WayfirePopover::WayfirePopover(std::string class_name, std::string option_name) +{ + popover.add_css_class(class_name + "-popover"); +} + +void WayfirePopover::set_child(Gtk::Widget & widget) +{ + popover.set_child(widget); +} + +Gtk::Widget*WayfirePopover::get_child() +{ + return popover.get_child(); +} + +void WayfirePopover::set_parent(Gtk::Widget & parent) +{ + gtk_widget_set_parent(GTK_WIDGET(popover.gobj()), GTK_WIDGET(parent.gobj())); +} + +void WayfirePopover::unset_parent() +{ + gtk_widget_unparent(GTK_WIDGET(popover.gobj())); +} + +void WayfirePopover::popup() +{ + popover.popup(); +} + +void WayfirePopover::popdown() +{ + popover.popdown(); +} + +WayfireMenuButton::WayfireMenuButton(const std::string& section, const std::string class_name, + const std::string option_name) : panel_position{section + "/position"} { - add_css_class("flat"); + add_css_class(class_name); - auto cb = [=] () + m_popover = std::make_shared(class_name, option_name); + m_popover->set_parent(*this); + + auto click_gesture = Gtk::GestureClick::create(); + click_gesture->set_button(1); + /* Catch a press-start */ + signals.push_back(click_gesture->signal_pressed().connect( + [=] (int btn, double x, double y) { - if (panel_position.value() == "bottom") - { - set_direction(Gtk::ArrowType::UP); - } else if (panel_position.value() == "left") - { - set_direction(Gtk::ArrowType::RIGHT); - } else if (panel_position.value() == "right") - { - set_direction(Gtk::ArrowType::LEFT); - } else // top, is also the fallback when invalid + click_gesture->set_state(Gtk::EventSequenceState::CLAIMED); + })); + /* Action on release */ + signals.push_back(click_gesture->signal_released().connect( + [=] (int, double, double) + { + toggle(); + })); + + /* Moved to another menu */ + auto motion_gesture = Gtk::EventControllerMotion::create(); + signals.push_back(motion_gesture->signal_enter().connect( + [=] (double, double) + { + auto panel = get_panel(this); + if (panel) { - set_direction(Gtk::ArrowType::DOWN); + auto current = panel->get_active_popover(); + if ((current != nullptr) && (current != this)) + { + panel->get_active_popover()->popdown(); + popup(); + } } + })); - this->unset_popover(); - set_popover(m_popover); - }; - panel_position.set_callback(cb); - cb(); - m_popover.signal_show().connect([=] + add_controller(click_gesture); + add_controller(motion_gesture); +} + +WayfireMenuButton::WayfireMenuButton(const std::string& section, std::string name) : + WayfireMenuButton(section, name, name) +{} + +WayfireMenuButton::~WayfireMenuButton() +{ + for (auto signal : signals) { - set_active_on_window(); - }); + signal.disconnect(); + } + + m_popover->unset_parent(); } void WayfireMenuButton::set_keyboard_interactive(bool interactive) @@ -56,16 +137,10 @@ bool WayfireMenuButton::is_popover_focused() const void WayfireMenuButton::set_active_on_window() { - auto window = this->get_parent(); - while (window && window->get_parent()) + auto panel = get_panel(this); + if (panel) { - window = window->get_parent(); - } - - auto autohide_window = dynamic_cast(window); - if (autohide_window) - { - autohide_window->set_active_popover(*this); + panel->set_active_popover(*this); } } @@ -74,3 +149,56 @@ void WayfireMenuButton::grab_focus() set_keyboard_interactive(); set_active_on_window(); // actually grab focus } + +void WayfireMenuButton::set_popover_child(Gtk::Widget & widget) +{ + m_popover->set_child(widget); +} + +Gtk::Widget*WayfireMenuButton::get_popover_child() +{ + return m_popover->get_child(); +} + +void WayfireMenuButton::popup() +{ + m_popover->popup(); + popup_signal.emit(); + auto panel = get_panel(this); + if (panel) + { + panel->set_active_popover(*this); + } +} + +void WayfireMenuButton::popdown() +{ + m_popover->popdown(); + popdown_signal.emit(); +} + +void WayfireMenuButton::toggle() +{ + auto panel = get_panel(this); + if (panel) + { + if (panel->get_active_popover() == NULL) + { + popup(); + } else + { + popdown(); + } + } +} + +bool WayfireMenuButton::is_popup_visible() +{ + auto panel = get_panel(this); + if (panel) + { + return panel->get_active_popover() == this; + } + + return false; +} diff --git a/src/util/wf-popover.hpp b/src/util/wf-popover.hpp index 1bd416c8..5c7ea4dc 100644 --- a/src/util/wf-popover.hpp +++ b/src/util/wf-popover.hpp @@ -1,14 +1,41 @@ #pragma once -#include +#include "gtkmm/widget.h" +#include "sigc++/connection.h" +#include "sigc++/signal.h" +#include #include +#include #include +using type_signal_simple = sigc::signal; + +/** + * A Popover subclass for WayfireMenuButton + */ +class WayfirePopover +{ + private: + Gtk::Popover popover; + + public: + WayfirePopover(std::string class_name, std::string option_name); + + void set_child(Gtk::Widget & widget); + Gtk::Widget *get_child(); + + void popup(); + void popdown(); + + void set_parent(Gtk::Widget & widget); + void unset_parent(); +}; + /** * A button which shows a popover on click. It adjusts the popup position * automatically based on panel position (valid values are "top" and "bottom") */ -class WayfireMenuButton : public Gtk::MenuButton +class WayfireMenuButton : public Gtk::Box { bool interactive = true; bool has_focus = false; @@ -21,12 +48,36 @@ class WayfireMenuButton : public Gtk::MenuButton /* Set the has_focus property */ void set_has_focus(bool focus); + std::shared_ptr m_popover; + + std::vector signals; + + type_signal_simple popup_signal, popdown_signal; + public: - Gtk::Popover m_popover; + WayfireMenuButton(const std::string& config_section, + const std::string name); + WayfireMenuButton(const std::string& config_section, + const std::string css_name, + const std::string option_name); + ~WayfireMenuButton(); + + type_signal_simple signal_popup() + { + return popup_signal; + } + + type_signal_simple signal_popdown() + { + return popdown_signal; + } + + /** + * Set popover child + */ + void set_popover_child(Gtk::Widget & child); - WayfireMenuButton(const std::string& config_section); - virtual ~WayfireMenuButton() - {} + Gtk::Widget *get_popover_child(); /** * Set whether the popup should grab input focus when opened @@ -47,4 +98,8 @@ class WayfireMenuButton : public Gtk::MenuButton * NOTE: this works only if the popover was already opened. */ void grab_focus(); + void popup(); + void popdown(); + void toggle(); + bool is_popup_visible(); }; From 6168371a772ef9a94b22f53e55453c5671cebfee Mon Sep 17 00:00:00 2001 From: trigg Date: Sat, 21 Mar 2026 22:24:37 +0000 Subject: [PATCH 02/18] chore: rename WayfireMenuButton to WayfireMenuWidget and popover to popup --- src/dock/dock.cpp | 2 +- src/locker/plugin/volume.hpp | 1 - src/panel/widgets/clock.cpp | 4 +- src/panel/widgets/clock.hpp | 2 +- src/panel/widgets/menu.cpp | 8 +- src/panel/widgets/menu.hpp | 2 +- src/panel/widgets/mixer/mixer.cpp | 25 +-- src/panel/widgets/mixer/mixer.hpp | 2 +- src/panel/widgets/mixer/wp-common.cpp | 10 +- src/panel/widgets/mixer/wp-common.hpp | 1 - src/panel/widgets/network.cpp | 4 +- src/panel/widgets/network.hpp | 2 +- .../notifications/notification-center.cpp | 7 +- .../notifications/notification-center.hpp | 2 +- src/panel/widgets/tray/item.cpp | 27 ++- src/panel/widgets/tray/item.hpp | 5 +- src/panel/widgets/volume.cpp | 7 +- src/panel/widgets/volume.hpp | 2 +- src/panel/widgets/workspace-switcher.cpp | 7 +- src/panel/widgets/workspace-switcher.hpp | 2 +- src/util/wf-autohide-window.cpp | 23 ++- src/util/wf-autohide-window.hpp | 10 +- src/util/wf-popover.cpp | 165 +++++++++++++----- src/util/wf-popover.hpp | 63 +++++-- 24 files changed, 246 insertions(+), 137 deletions(-) diff --git a/src/dock/dock.cpp b/src/dock/dock.cpp index 510cff8d..5103c9ff 100644 --- a/src/dock/dock.cpp +++ b/src/dock/dock.cpp @@ -23,7 +23,7 @@ class WfDock::impl std::unique_ptr window; wl_surface *_wl_surface; Gtk::FlowBox box; - std::unique_ptr network_image; + std::unique_ptr network_image; std::unique_ptr network_control; std::shared_ptr network_manager; diff --git a/src/locker/plugin/volume.hpp b/src/locker/plugin/volume.hpp index 4411f061..23ece11d 100644 --- a/src/locker/plugin/volume.hpp +++ b/src/locker/plugin/volume.hpp @@ -1,7 +1,6 @@ #pragma once #include #include -#include #include #include diff --git a/src/panel/widgets/clock.cpp b/src/panel/widgets/clock.cpp index 1f8c94df..c18057b9 100644 --- a/src/panel/widgets/clock.cpp +++ b/src/panel/widgets/clock.cpp @@ -3,7 +3,7 @@ void WayfireClock::init(Gtk::Box *container) { - button = std::make_unique("panel", "clock"); + button = std::make_unique("panel", "clock"); button->add_css_class("clock"); button->append(label); button->show(); @@ -13,7 +13,7 @@ void WayfireClock::init(Gtk::Box *container) update_label(); calendar.show(); - button->set_popover_child(calendar); + button->set_popup_child(calendar); btn_sig = button->signal_popup().connect( sigc::mem_fun(*this, &WayfireClock::on_calendar_shown)); diff --git a/src/panel/widgets/clock.hpp b/src/panel/widgets/clock.hpp index 1685f2be..858f842f 100644 --- a/src/panel/widgets/clock.hpp +++ b/src/panel/widgets/clock.hpp @@ -9,7 +9,7 @@ class WayfireClock : public WayfireWidget { Gtk::Label label; Gtk::Calendar calendar; - std::unique_ptr button; + std::unique_ptr button; sigc::connection timeout, btn_sig; WfOption format{"panel/clock_format"}; diff --git a/src/panel/widgets/menu.cpp b/src/panel/widgets/menu.cpp index 677b4401..f87e4e1a 100644 --- a/src/panel/widgets/menu.cpp +++ b/src/panel/widgets/menu.cpp @@ -11,6 +11,7 @@ #include "menu.hpp" #include "gtk-utils.hpp" #include "wf-autohide-window.hpp" +#include "wf-popover.hpp" const std::string default_icon = "wayfire"; @@ -578,7 +579,7 @@ bool WayfireMenu::update_icon() void WayfireMenu::setup_popover_layout() { - button->set_popover_child(popover_layout_box); + button->set_popup_child(popover_layout_box); flowbox.set_selection_mode(Gtk::SelectionMode::SINGLE); flowbox.set_activate_on_single_click(true); @@ -985,12 +986,13 @@ void WayfireMenu::init(Gtk::Box *container) menu_show_categories.set_callback([=] () { update_popover_layout(); }); menu_list.set_callback([=] () { update_popover_layout(); }); - button = std::make_unique("panel", "menu"); + button = std::make_unique("panel", "menu"); fullscreen.add_css_class("menu-fullscreen"); button->append(main_image); button->add_css_class("menu-button"); button->add_css_class("flat"); button->get_children()[0]->add_css_class("flat"); + button->open_on(1); /* Open menu on left click */ signals.push_back(button->signal_popup().connect( sigc::mem_fun(*this, &WayfireMenu::on_popover_shown))); @@ -1013,7 +1015,7 @@ void WayfireMenu::init(Gtk::Box *container) signals.push_back(button->property_scale_factor().signal_changed().connect( [=] () {update_icon(); })); - button->set_popover_child(popover_layout_box); + button->set_popup_child(popover_layout_box); container->append(box); box.append(*button); diff --git a/src/panel/widgets/menu.hpp b/src/panel/widgets/menu.hpp index 056b54b3..3a8bdfda 100644 --- a/src/panel/widgets/menu.hpp +++ b/src/panel/widgets/menu.hpp @@ -135,7 +135,7 @@ class WayfireMenu : public WayfireWidget Gtk::Image logout_image; Gtk::ScrolledWindow app_scrolled_window, category_scrolled_window; Gtk::Window fullscreen; - std::unique_ptr button; + std::unique_ptr button; std::unique_ptr logout_ui; GAppInfoMonitor *app_info_monitor = g_app_info_monitor_get(); diff --git a/src/panel/widgets/mixer/mixer.cpp b/src/panel/widgets/mixer/mixer.cpp index 145539a6..37a566bc 100644 --- a/src/panel/widgets/mixer/mixer.cpp +++ b/src/panel/widgets/mixer/mixer.cpp @@ -73,7 +73,7 @@ void WayfireMixer::reload_config() // unschedule hiding cancel_popover_timeout(); - if ((button->get_popover_child() == (Gtk::Widget*)&master_box) && button->is_popup_visible()) + if ((button->get_popup_child() == (Gtk::Widget*)&master_box) && button->is_popup_visible()) { button->popdown(); return; @@ -84,9 +84,9 @@ void WayfireMixer::reload_config() button->popup(); } - if (button->get_popover_child() != (Gtk::Widget*)&master_box) + if (button->get_popup_child() != (Gtk::Widget*)&master_box) { - button->set_popover_child(master_box); + button->set_popup_child(master_box); popover_timeout.disconnect(); } }; @@ -100,7 +100,7 @@ void WayfireMixer::reload_config() return; // no quick_target means we have nothing to show } - if ((button->get_popover_child() == quick_target.get()) && button->is_popup_visible()) + if ((button->get_popup_child() == quick_target.get()) && button->is_popup_visible()) { button->popdown(); return; @@ -111,9 +111,9 @@ void WayfireMixer::reload_config() button->popup(); } - if (button->get_popover_child() != quick_target.get()) + if (button->get_popup_child() != quick_target.get()) { - button->set_popover_child(*quick_target); + button->set_popup_child(*quick_target); popover_timeout.disconnect(); } }; @@ -137,9 +137,9 @@ void WayfireMixer::reload_config() { // unschedule hiding cancel_popover_timeout(); - if (button->get_popover_child() != (Gtk::Widget*)&master_box) + if (button->get_popup_child() != (Gtk::Widget*)&master_box) { - button->set_popover_child(master_box); + button->set_popup_child(master_box); // popdown so that when the click is processed, the popover is down, and thus pops up // not the prettiest result, as it visibly closes instead of just replacing, but i’m not sure // how to make it better @@ -160,9 +160,9 @@ void WayfireMixer::reload_config() return; } - if (button->get_popover_child() != quick_target.get()) + if (button->get_popup_child() != quick_target.get()) { - button->set_popover_child(*quick_target); + button->set_popup_child(*quick_target); // same as above button->popdown(); } @@ -219,17 +219,18 @@ void WayfireMixer::init(Gtk::Box *container) { // sets up the "widget part" - button = std::make_unique("panel", "mixer"); + button = std::make_unique("panel", "mixer"); button->add_css_class("widget-icon"); button->append(main_image); button->show(); + button->open_on(1); sinks_box.add_css_class("outputs"); sources_box.add_css_class("inputs"); streams_box.add_css_class("streams"); out_in_wall.add_css_class("out-in"); in_streams_wall.add_css_class("in-streams"); - button->set_popover_child(master_box); + button->set_popup_child(master_box); // scroll to change volume of the object targetted by the quick_target widget auto scroll_gesture = Gtk::EventControllerScroll::create(); diff --git a/src/panel/widgets/mixer/mixer.hpp b/src/panel/widgets/mixer/mixer.hpp index 7a945875..691f298e 100644 --- a/src/panel/widgets/mixer/mixer.hpp +++ b/src/panel/widgets/mixer/mixer.hpp @@ -54,7 +54,7 @@ class WayfireMixer : public WayfireWidget WfOption invert_scroll{"panel/mixer_invert_scroll"}; WfOption popup_on_change{"panel/mixer_popup_on_change"}; - std::unique_ptr button; + std::unique_ptr button; /* * the "quick_target" is the representation of the audio channel that shows it’s volume diff --git a/src/panel/widgets/mixer/wp-common.cpp b/src/panel/widgets/mixer/wp-common.cpp index bf61ad3f..74d21c43 100644 --- a/src/panel/widgets/mixer/wp-common.cpp +++ b/src/panel/widgets/mixer/wp-common.cpp @@ -176,10 +176,10 @@ void WpCommon::add_object_to_widget(WpPipewireObject *object, WayfireMixer *widg { widget->set_quick_target_from(control); // if the full mixer is not shown, change the popover child - if ((widget->button->get_popover_child() != + if ((widget->button->get_popup_child() != &(widget->master_box)) && widget->button->is_popup_visible()) { - widget->button->set_popover_child(*widget->quick_target); + widget->button->set_popup_child(*widget->quick_target); } widget->update_icon(); @@ -273,7 +273,7 @@ void WpCommon::on_mixer_changed(gpointer mixer_api, guint id, gpointer data) update_icons(); // if the mixer is currently being displayed, stop there - if ((widget->button->get_popover_child() == + if ((widget->button->get_popup_child() == &(widget->master_box)) && widget->button->is_popup_visible()) { continue; @@ -281,10 +281,10 @@ void WpCommon::on_mixer_changed(gpointer mixer_api, guint id, gpointer data) if (widget->quick_target && (!widget->button->is_popup_visible() || - (widget->button->get_popover_child() != (MixerControl*)&widget->quick_target))) + (widget->button->get_popup_child() != (MixerControl*)&widget->quick_target))) { // put the quick_target in the popover and show - widget->button->set_popover_child(*widget->quick_target); + widget->button->set_popup_child(*widget->quick_target); if (widget->popup_on_change && change) { widget->button->popup(); diff --git a/src/panel/widgets/mixer/wp-common.hpp b/src/panel/widgets/mixer/wp-common.hpp index 5e303b5c..f7cba42b 100644 --- a/src/panel/widgets/mixer/wp-common.hpp +++ b/src/panel/widgets/mixer/wp-common.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include diff --git a/src/panel/widgets/network.cpp b/src/panel/widgets/network.cpp index 24404583..5652bbf7 100644 --- a/src/panel/widgets/network.cpp +++ b/src/panel/widgets/network.cpp @@ -14,7 +14,7 @@ WayfireNetworkInfo::WayfireNetworkInfo() void WayfireNetworkInfo::init(Gtk::Box *container) { network_manager = NetworkManager::getInstance(); - button = std::make_unique("panel", "network"); + button = std::make_unique("panel", "network"); button->add_css_class("widget-icon"); button->add_css_class("flat"); button->add_css_class("network"); @@ -23,7 +23,7 @@ void WayfireNetworkInfo::init(Gtk::Box *container) button->append(button_content); button->add_css_class("flat"); - button->set_popover_child(control); + button->set_popup_child(control); button_content.set_valign(Gtk::Align::CENTER); button_content.append(icon); diff --git a/src/panel/widgets/network.hpp b/src/panel/widgets/network.hpp index 271ac444..eb407c94 100644 --- a/src/panel/widgets/network.hpp +++ b/src/panel/widgets/network.hpp @@ -18,7 +18,7 @@ class WayfireNetworkInfo : public WayfireWidget void on_click(); std::vector signals; std::shared_ptr network_manager; - std::unique_ptr button; + std::unique_ptr button; Gtk::Box button_content; Gtk::Image icon; Gtk::Label status; diff --git a/src/panel/widgets/notifications/notification-center.cpp b/src/panel/widgets/notifications/notification-center.cpp index 72550cf9..4e500b03 100644 --- a/src/panel/widgets/notifications/notification-center.cpp +++ b/src/panel/widgets/notifications/notification-center.cpp @@ -6,16 +6,17 @@ #include #include "single-notification.hpp" +#include "wf-popover.hpp" void WayfireNotificationCenter::init(Gtk::Box *container) { - button = std::make_unique("panel", "notification"); + button = std::make_unique("panel", "notification"); icon.add_css_class("widget-icon"); button->add_css_class("notification-center"); button->get_children()[0]->add_css_class("flat"); updateIcon(); - button->set_popover_child(icon); + button->set_popup_child(icon); container->append(*button); scrolled_window.set_size_request(WIDTH, HEIGHT); @@ -23,7 +24,7 @@ void WayfireNotificationCenter::init(Gtk::Box *container) box.set_valign(Gtk::Align::START); box.set_orientation(Gtk::Orientation::VERTICAL); scrolled_window.set_child(box); - button->set_popover_child(scrolled_window); + button->set_popup_child(scrolled_window); button->set_tooltip_text("Middle click to toggle DND mode."); diff --git a/src/panel/widgets/notifications/notification-center.hpp b/src/panel/widgets/notifications/notification-center.hpp index 08995324..7014fbc1 100644 --- a/src/panel/widgets/notifications/notification-center.hpp +++ b/src/panel/widgets/notifications/notification-center.hpp @@ -19,7 +19,7 @@ class WayfireNotificationCenter : public WayfireWidget sigc::connection notification_close_conn; Gtk::Image icon; - std::unique_ptr button; + std::unique_ptr button; Gtk::ScrolledWindow scrolled_window; Gtk::Box box; diff --git a/src/panel/widgets/tray/item.cpp b/src/panel/widgets/tray/item.cpp index fb3ab9b3..a5a52bbc 100644 --- a/src/panel/widgets/tray/item.cpp +++ b/src/panel/widgets/tray/item.cpp @@ -1,4 +1,5 @@ #include "item.hpp" +#include "wf-popover.hpp" #include @@ -49,9 +50,12 @@ static Glib::RefPtr extract_pixbuf(IconData && pixbuf_data) 4 * width, [data_ptr] (auto*) { delete data_ptr; }); } -StatusNotifierItem::StatusNotifierItem(const Glib::ustring & service) +StatusNotifierItem::StatusNotifierItem(const Glib::ustring & service) : + WayfireMenuWidget("panel", + "tray-button", + "tray_button") { - set_child(icon); + append(icon); menu = std::make_shared(); const auto & [name, path] = name_and_obj_path(service); @@ -68,15 +72,6 @@ StatusNotifierItem::StatusNotifierItem(const Glib::ustring & service) }); } -StatusNotifierItem::~StatusNotifierItem() -{ - gtk_widget_unparent(GTK_WIDGET(popover.gobj())); - for (auto signal : signals) - { - signal.disconnect(); - } -} - void StatusNotifierItem::init_widget() { update_icon(); @@ -84,8 +79,6 @@ void StatusNotifierItem::init_widget() init_menu(); add_css_class("widget-icon"); add_css_class("tray-button"); - add_css_class("flat"); - gtk_widget_set_parent(GTK_WIDGET(popover.gobj()), GTK_WIDGET(gobj())); auto scroll_gesture = Gtk::EventControllerScroll::create(); scroll_gesture->set_flags(Gtk::EventControllerScroll::Flags::BOTH_AXES); @@ -103,7 +96,7 @@ void StatusNotifierItem::init_widget() long_press->signal_pressed().connect( [=] (double x, double y) { - popover.popup(); + popup(); long_press->set_state(Gtk::EventSequenceState::CLAIMED); click_gesture->set_state(Gtk::EventSequenceState::DENIED); }); @@ -127,7 +120,7 @@ void StatusNotifierItem::init_widget() { if (has_menu) { - popover.popup(); + popup(); } else { item_proxy->call("ContextMenu", ev_coords); @@ -140,7 +133,7 @@ void StatusNotifierItem::init_widget() { if (has_menu) { - popover.popup(); + popup(); } else { item_proxy->call("ContextMenu", ev_coords); @@ -235,7 +228,7 @@ void StatusNotifierItem::init_menu() { auto action_group = menu->get_action_group(); insert_action_group(action_prefix, action_group); - popover.set_menu_model(menu->get_menu()); + set_menu_model(menu->get_menu()); })); has_menu = true; } diff --git a/src/panel/widgets/tray/item.hpp b/src/panel/widgets/tray/item.hpp index b9c2bc27..aeaf7704 100644 --- a/src/panel/widgets/tray/item.hpp +++ b/src/panel/widgets/tray/item.hpp @@ -9,8 +9,9 @@ #include #include "dbusmenu.hpp" +#include "wf-popover.hpp" -class StatusNotifierItem : public Gtk::Button +class StatusNotifierItem : public WayfireMenuWidget { guint menu_handler_id; @@ -20,7 +21,6 @@ class StatusNotifierItem : public Gtk::Button Glib::RefPtr item_proxy; - Gtk::PopoverMenu popover; std::shared_ptr menu; bool has_menu = false; @@ -59,6 +59,5 @@ class StatusNotifierItem : public Gtk::Button public: void menu_update(DbusmenuClient *client); explicit StatusNotifierItem(const Glib::ustring & service); - ~StatusNotifierItem(); std::string get_unique_name(); }; diff --git a/src/panel/widgets/volume.cpp b/src/panel/widgets/volume.cpp index 2d35e0f6..e3d3040a 100644 --- a/src/panel/widgets/volume.cpp +++ b/src/panel/widgets/volume.cpp @@ -3,6 +3,7 @@ #include "volume.hpp" #include "icon-select.hpp" +#include "wf-popover.hpp" #define ICON(volume) icon_from_range(volume_icons, volume) @@ -137,7 +138,7 @@ void WayfireVolume::on_volume_value_changed() void WayfireVolume::init(Gtk::Box *container) { - button = std::make_unique("panel", "volume"); + button = std::make_unique("panel", "volume"); /* Setup button */ button->add_css_class("widget-icon"); @@ -168,7 +169,7 @@ void WayfireVolume::init(Gtk::Box *container) button->add_controller(scroll_gesture); /* Setup popover */ - button->set_popover_child(volume_scale); + button->set_popup_child(volume_scale); auto scroll_gesture2 = Gtk::EventControllerScroll::create(); signals.push_back(scroll_gesture2->signal_scroll().connect([=] (double dx, double dy) @@ -255,7 +256,7 @@ void WayfireVolume::init(Gtk::Box *container) /* Setup layout */ container->append(*button); - button->set_popover_child(main_image); + button->set_popup_child(main_image); } WayfireVolume::~WayfireVolume() diff --git a/src/panel/widgets/volume.hpp b/src/panel/widgets/volume.hpp index 69f934f1..39702132 100644 --- a/src/panel/widgets/volume.hpp +++ b/src/panel/widgets/volume.hpp @@ -13,7 +13,7 @@ class WayfireVolume : public WayfireWidget { Gtk::Image main_image; WayfireAnimatedScale volume_scale; - std::unique_ptr button; + std::unique_ptr button; WfOption timeout{"panel/volume_display_timeout"}; WfOption scroll_sensitivity{"panel/volume_scroll_sensitivity"}; diff --git a/src/panel/widgets/workspace-switcher.cpp b/src/panel/widgets/workspace-switcher.cpp index 56f25e6d..52c661de 100644 --- a/src/panel/widgets/workspace-switcher.cpp +++ b/src/panel/widgets/workspace-switcher.cpp @@ -5,6 +5,7 @@ #include #include "panel.hpp" +#include "wf-popover.hpp" #include "workspace-switcher.hpp" void WayfireWorkspaceSwitcher::init(Gtk::Box *container) @@ -20,7 +21,7 @@ void WayfireWorkspaceSwitcher::init(Gtk::Box *container) box.add_css_class("workspace-switcher"); box.add_css_class("flat"); - button = std::make_unique("panel", "workspace-switcher", "workspace_switcher"); + button = std::make_unique("panel", "workspace-switcher", "workspace_switcher"); ipc_client->subscribe(this, {"view-mapped"}); ipc_client->subscribe(this, {"view-focused"}); @@ -49,7 +50,7 @@ void WayfireWorkspaceSwitcher::init(Gtk::Box *container) switcher_box.append(overlay); } else // "grid_popover" { - button->set_popover_child(overlay); + button->set_popup_child(overlay); button->append(mini_grid); switcher_box.append(*button); } @@ -472,7 +473,7 @@ void WayfireWorkspaceSwitcher::grid_process_workspaces(wf::json_t workspace_data } clear_box(); - button->set_popover_child(overlay); + button->set_popup_child(overlay); overlay.set_child(switch_grid); overlay.add_css_class("workspace"); overlay.signal_get_child_position().connect(sigc::mem_fun(*this, diff --git a/src/panel/widgets/workspace-switcher.hpp b/src/panel/widgets/workspace-switcher.hpp index 617857f2..2417032f 100644 --- a/src/panel/widgets/workspace-switcher.hpp +++ b/src/panel/widgets/workspace-switcher.hpp @@ -51,7 +51,7 @@ class WayfireWorkspaceSwitcher : public WayfireWidget, public IIPCSubscriber Gtk::Grid switch_grid; Gtk::Overlay overlay; std::pair get_scaled_size(); - std::unique_ptr button; + std::unique_ptr button; int output_width, output_height; void init(Gtk::Box *container) override; WayfireWorkspaceSwitcher(WayfireOutput *output); diff --git a/src/util/wf-autohide-window.cpp b/src/util/wf-autohide-window.cpp index b5d7293f..6c7d949e 100644 --- a/src/util/wf-autohide-window.cpp +++ b/src/util/wf-autohide-window.cpp @@ -588,7 +588,7 @@ bool WayfireAutohidingWindow::update_margin() return false; } -void WayfireAutohidingWindow::set_active_popover(WayfireMenuButton& button) +void WayfireAutohidingWindow::set_active_popover(WayfireMenuWidget& button) { if (&button != this->active_button) { @@ -619,7 +619,7 @@ void WayfireAutohidingWindow::set_active_popover(WayfireMenuButton& button) schedule_show(0); } -void WayfireAutohidingWindow::unset_active_popover(WayfireMenuButton& button) +void WayfireAutohidingWindow::unset_active_popover(WayfireMenuWidget& button) { if (!this->active_button || (&button != this->active_button)) { @@ -638,7 +638,24 @@ void WayfireAutohidingWindow::unset_active_popover(WayfireMenuButton& button) } } -WayfireMenuButton*WayfireAutohidingWindow::get_active_popover() +void WayfireAutohidingWindow::unset_active_popover() +{ + if (this->active_button) + { + this->active_button->set_has_focus(false); + this->active_button = nullptr; + this->popover_hide.disconnect(); + } + + gtk_layer_set_keyboard_mode(this->gobj(), GTK_LAYER_SHELL_KEYBOARD_MODE_NONE); + + if (should_autohide()) + { + schedule_hide(autohide_hide_delay); + } +} + +WayfireMenuWidget*WayfireAutohidingWindow::get_active_popover() { return this->active_button; } diff --git a/src/util/wf-autohide-window.hpp b/src/util/wf-autohide-window.hpp index f257b1e2..e4236ef1 100644 --- a/src/util/wf-autohide-window.hpp +++ b/src/util/wf-autohide-window.hpp @@ -75,7 +75,7 @@ class WayfireAutohidingWindow : public Gtk::Window * In addition, if the window has an active popover, it will grab the * keyboard input and deactivate the popover when the focus is lost. */ - void set_active_popover(WayfireMenuButton& button); + void set_active_popover(WayfireMenuWidget& button); /** * No-op if the given popover is not the currently active popover. @@ -83,12 +83,14 @@ class WayfireAutohidingWindow : public Gtk::Window * Unsets the currently active popover and reverses the effects of setting * making it active with set_active_popover() */ - void unset_active_popover(WayfireMenuButton& popover); + void unset_active_popover(WayfireMenuWidget& popover); + + void unset_active_popover(); /* * Get Active popover or null */ - WayfireMenuButton *get_active_popover(); + WayfireMenuWidget *get_active_popover(); private: WayfireOutput *output; @@ -145,7 +147,7 @@ class WayfireAutohidingWindow : public Gtk::Window void setup_hotspot(); sigc::connection popover_hide; - WayfireMenuButton *active_button = nullptr; + WayfireMenuWidget *active_button = nullptr; }; diff --git a/src/util/wf-popover.cpp b/src/util/wf-popover.cpp index d5667a67..e60e81b3 100644 --- a/src/util/wf-popover.cpp +++ b/src/util/wf-popover.cpp @@ -1,4 +1,8 @@ #include "wf-popover.hpp" +#include "giomm/menu.h" +#include "giomm/menumodel.h" +#include "glibmm/refptr.h" +#include "gtk/gtk.h" #include "gtkmm/eventcontrollermotion.h" #include "gtkmm/gesture.h" #include "gtkmm/gestureclick.h" @@ -8,7 +12,7 @@ #include /* Helper to get panel from button. NULL if not added to one */ -WayfireAutohidingWindow *get_panel(WayfireMenuButton *button) +WayfireAutohidingWindow *get_panel(Gtk::Widget *button) { auto window = button->get_root(); if (!window) @@ -20,64 +24,121 @@ WayfireAutohidingWindow *get_panel(WayfireMenuButton *button) return autohide_window; } -WayfirePopover::WayfirePopover(std::string class_name, std::string option_name) +WayfirePopup::WayfirePopup(std::string class_name, std::string option_name) { popover.add_css_class(class_name + "-popover"); + + signals.push_back(popover.signal_closed().connect([=] () + { + popdown(); + })); + + signals.push_back(menu.signal_closed().connect([=] () + { + popdown(); + })); } -void WayfirePopover::set_child(Gtk::Widget & widget) +WayfirePopup::~WayfirePopup() { + for (auto signal : signals) + { + signal.disconnect(); + } +} + +void WayfirePopup::set_child(Gtk::Widget & widget) +{ + menu.popdown(); + use_menu = false; popover.set_child(widget); } -Gtk::Widget*WayfirePopover::get_child() +void WayfirePopup::set_menu_model(Glib::RefPtr new_menu) +{ + popover.popdown(); + use_menu = true; + menu.set_menu_model(new_menu); +} + +Gtk::Widget*WayfirePopup::get_child() { return popover.get_child(); } -void WayfirePopover::set_parent(Gtk::Widget & parent) +void WayfirePopup::set_parent(Gtk::Widget & parent) { gtk_widget_set_parent(GTK_WIDGET(popover.gobj()), GTK_WIDGET(parent.gobj())); + gtk_widget_set_parent(GTK_WIDGET(menu.gobj()), GTK_WIDGET(parent.gobj())); } -void WayfirePopover::unset_parent() +void WayfirePopup::unset_parent() { gtk_widget_unparent(GTK_WIDGET(popover.gobj())); + gtk_widget_unparent(GTK_WIDGET(menu.gobj())); } -void WayfirePopover::popup() +void WayfirePopup::popup() { - popover.popup(); + if (use_menu) + { + menu.popup(); + } else + { + popover.popup(); + } } -void WayfirePopover::popdown() +void WayfirePopup::popdown() { - popover.popdown(); + if (use_menu) + { + menu.popdown(); + } else + { + popover.popdown(); + } + + auto panel = get_panel(&popover); + if (panel) + { + panel->unset_active_popover(); + } } -WayfireMenuButton::WayfireMenuButton(const std::string& section, const std::string class_name, - const std::string option_name) : - panel_position{section + "/position"} +void WayfireMenuWidget::open_on(int button) { - add_css_class(class_name); + if (click_signal) + { + click_signal.disconnect(); + } - m_popover = std::make_shared(class_name, option_name); - m_popover->set_parent(*this); + if (button < 0) + { + return; + } auto click_gesture = Gtk::GestureClick::create(); - click_gesture->set_button(1); - /* Catch a press-start */ - signals.push_back(click_gesture->signal_pressed().connect( - [=] (int btn, double x, double y) - { - click_gesture->set_state(Gtk::EventSequenceState::CLAIMED); - })); + click_gesture->set_button(button); /* Action on release */ - signals.push_back(click_gesture->signal_released().connect( + click_signal = click_gesture->signal_released().connect( [=] (int, double, double) { toggle(); - })); + }); + add_controller(click_gesture); +} + +WayfireMenuWidget::WayfireMenuWidget(const std::string& section, const std::string class_name, + const std::string option_name) : + panel_position{section + "/position"} +{ + add_css_class(class_name); + + m_popup = std::make_shared(class_name, option_name); + m_popup->set_parent(*this); + + /* Moved to another menu */ auto motion_gesture = Gtk::EventControllerMotion::create(); @@ -95,47 +156,45 @@ WayfireMenuButton::WayfireMenuButton(const std::string& section, const std::stri } } })); - - - add_controller(click_gesture); add_controller(motion_gesture); } -WayfireMenuButton::WayfireMenuButton(const std::string& section, std::string name) : - WayfireMenuButton(section, name, name) +WayfireMenuWidget::WayfireMenuWidget(const std::string& section, std::string name) : + WayfireMenuWidget(section, name, name) {} -WayfireMenuButton::~WayfireMenuButton() +WayfireMenuWidget::~WayfireMenuWidget() { for (auto signal : signals) { signal.disconnect(); } - m_popover->unset_parent(); + click_signal.disconnect(); + m_popup->unset_parent(); } -void WayfireMenuButton::set_keyboard_interactive(bool interactive) +void WayfireMenuWidget::set_keyboard_interactive(bool interactive) { this->interactive = interactive; } -bool WayfireMenuButton::is_keyboard_interactive() const +bool WayfireMenuWidget::is_keyboard_interactive() const { return this->interactive; } -void WayfireMenuButton::set_has_focus(bool focus) +void WayfireMenuWidget::set_has_focus(bool focus) { this->has_focus = focus; } -bool WayfireMenuButton::is_popover_focused() const +bool WayfireMenuWidget::is_popup_focused() const { return this->has_focus; } -void WayfireMenuButton::set_active_on_window() +void WayfireMenuWidget::set_active_on_window() { auto panel = get_panel(this); if (panel) @@ -144,25 +203,25 @@ void WayfireMenuButton::set_active_on_window() } } -void WayfireMenuButton::grab_focus() +void WayfireMenuWidget::grab_focus() { set_keyboard_interactive(); set_active_on_window(); // actually grab focus } -void WayfireMenuButton::set_popover_child(Gtk::Widget & widget) +void WayfireMenuWidget::set_popup_child(Gtk::Widget & widget) { - m_popover->set_child(widget); + m_popup->set_child(widget); } -Gtk::Widget*WayfireMenuButton::get_popover_child() +Gtk::Widget*WayfireMenuWidget::get_popup_child() { - return m_popover->get_child(); + return m_popup->get_child(); } -void WayfireMenuButton::popup() +void WayfireMenuWidget::popup() { - m_popover->popup(); + m_popup->popup(); popup_signal.emit(); auto panel = get_panel(this); if (panel) @@ -171,13 +230,18 @@ void WayfireMenuButton::popup() } } -void WayfireMenuButton::popdown() +void WayfireMenuWidget::popdown() { - m_popover->popdown(); + m_popup->popdown(); popdown_signal.emit(); + auto panel = get_panel(this); + if (panel) + { + panel->unset_active_popover(*this); + } } -void WayfireMenuButton::toggle() +void WayfireMenuWidget::toggle() { auto panel = get_panel(this); if (panel) @@ -192,7 +256,7 @@ void WayfireMenuButton::toggle() } } -bool WayfireMenuButton::is_popup_visible() +bool WayfireMenuWidget::is_popup_visible() { auto panel = get_panel(this); if (panel) @@ -202,3 +266,8 @@ bool WayfireMenuButton::is_popup_visible() return false; } + +void WayfireMenuWidget::set_menu_model(Glib::RefPtr menu) +{ + m_popup->set_menu_model(menu); +} diff --git a/src/util/wf-popover.hpp b/src/util/wf-popover.hpp index 5c7ea4dc..681d03ea 100644 --- a/src/util/wf-popover.hpp +++ b/src/util/wf-popover.hpp @@ -1,26 +1,36 @@ #pragma once -#include "gtkmm/widget.h" -#include "sigc++/connection.h" -#include "sigc++/signal.h" +#include "giomm/menumodel.h" +#include "glibmm/refptr.h" +#include +#include +#include #include #include +#include #include +#include #include using type_signal_simple = sigc::signal; /** - * A Popover subclass for WayfireMenuButton + * A popup subclass for WayfireMenuButton */ -class WayfirePopover +class WayfirePopup { private: Gtk::Popover popover; + Gtk::PopoverMenu menu; + bool use_menu; + + std::vector signals; public: - WayfirePopover(std::string class_name, std::string option_name); + WayfirePopup(std::string class_name, std::string option_name); + ~WayfirePopup(); + void set_menu_model(Glib::RefPtr menu); void set_child(Gtk::Widget & widget); Gtk::Widget *get_child(); @@ -32,10 +42,10 @@ class WayfirePopover }; /** - * A button which shows a popover on click. It adjusts the popup position + * A button which shows a popup on click. It adjusts the popup position * automatically based on panel position (valid values are "top" and "bottom") */ -class WayfireMenuButton : public Gtk::Box +class WayfireMenuWidget : public Gtk::Box { bool interactive = true; bool has_focus = false; @@ -48,36 +58,39 @@ class WayfireMenuButton : public Gtk::Box /* Set the has_focus property */ void set_has_focus(bool focus); - std::shared_ptr m_popover; + std::shared_ptr m_popup; std::vector signals; + sigc::connection click_signal; type_signal_simple popup_signal, popdown_signal; public: - WayfireMenuButton(const std::string& config_section, + WayfireMenuWidget(const std::string& config_section, const std::string name); - WayfireMenuButton(const std::string& config_section, + WayfireMenuWidget(const std::string& config_section, const std::string css_name, const std::string option_name); - ~WayfireMenuButton(); + ~WayfireMenuWidget(); + /* Called when the popup is shown */ type_signal_simple signal_popup() { return popup_signal; } + /* Called when the popup is hidden */ type_signal_simple signal_popdown() { return popdown_signal; } /** - * Set popover child + * Set popup child */ - void set_popover_child(Gtk::Widget & child); + void set_popup_child(Gtk::Widget & child); - Gtk::Widget *get_popover_child(); + Gtk::Widget *get_popup_child(); /** * Set whether the popup should grab input focus when opened @@ -88,18 +101,30 @@ class WayfireMenuButton : public Gtk::Box /** @return Whether the menu button interacts with the keyboard */ bool is_keyboard_interactive() const; - /** @return Whether the popover currently has keyboard focus */ - bool is_popover_focused() const; + /** @return Whether the popup currently has keyboard focus */ + bool is_popup_focused() const; /** * Grab the keyboard focus. - * Also sets the popover to keyboard interactive. + * Also sets the popup to keyboard interactive. * - * NOTE: this works only if the popover was already opened. + * NOTE: this works only if the popup was already opened. */ void grab_focus(); + + /* Open the connected popup */ void popup(); + /* Close the connected popup */ void popdown(); + /* Toggle the popup state of the connected popup */ void toggle(); + /* Returns true if this is the open popup for this panel */ bool is_popup_visible(); + + /* Use a specific mouse button to open menu. Skip this if you're handling presses in-widget. If button < 0 + * then this callback is removed */ + void open_on(int button); + + /* Set the MenuModel of the popup. Masks the child set by set_popup_child */ + void set_menu_model(Glib::RefPtr); }; From 60bd34f689cabda26928a6cecdc46ba55b309070 Mon Sep 17 00:00:00 2001 From: trigg Date: Sun, 22 Mar 2026 11:23:31 +0000 Subject: [PATCH 03/18] panel: fix up clock, workspace and notification popups panel: fix popup too big for screen --- src/panel/widgets/clock.cpp | 2 +- src/panel/widgets/notifications/notification-center.cpp | 3 ++- src/panel/widgets/workspace-switcher.cpp | 3 +++ src/util/wf-popover.cpp | 8 +++++--- src/util/wf-popover.hpp | 2 ++ 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/panel/widgets/clock.cpp b/src/panel/widgets/clock.cpp index c18057b9..e4670031 100644 --- a/src/panel/widgets/clock.cpp +++ b/src/panel/widgets/clock.cpp @@ -6,7 +6,7 @@ void WayfireClock::init(Gtk::Box *container) button = std::make_unique("panel", "clock"); button->add_css_class("clock"); button->append(label); - button->show(); + button->open_on(1); label.set_justify(Gtk::Justification::CENTER); label.show(); diff --git a/src/panel/widgets/notifications/notification-center.cpp b/src/panel/widgets/notifications/notification-center.cpp index 4e500b03..c082cad3 100644 --- a/src/panel/widgets/notifications/notification-center.cpp +++ b/src/panel/widgets/notifications/notification-center.cpp @@ -16,7 +16,8 @@ void WayfireNotificationCenter::init(Gtk::Box *container) button->get_children()[0]->add_css_class("flat"); updateIcon(); - button->set_popup_child(icon); + button->append(icon); + button->open_on(1); container->append(*button); scrolled_window.set_size_request(WIDTH, HEIGHT); diff --git a/src/panel/widgets/workspace-switcher.cpp b/src/panel/widgets/workspace-switcher.cpp index 52c661de..822f1c00 100644 --- a/src/panel/widgets/workspace-switcher.cpp +++ b/src/panel/widgets/workspace-switcher.cpp @@ -45,14 +45,17 @@ void WayfireWorkspaceSwitcher::init(Gtk::Box *container) if (workspace_switcher_mode.value() == "row") { switcher_box.append(box); + button->open_on(-1); } else if (workspace_switcher_mode.value() == "grid") { switcher_box.append(overlay); + button->open_on(-1); } else // "grid_popover" { button->set_popup_child(overlay); button->append(mini_grid); switcher_box.append(*button); + button->open_on(1); } get_wsets(); diff --git a/src/util/wf-popover.cpp b/src/util/wf-popover.cpp index e60e81b3..52c306b1 100644 --- a/src/util/wf-popover.cpp +++ b/src/util/wf-popover.cpp @@ -1,10 +1,8 @@ #include "wf-popover.hpp" -#include "giomm/menu.h" #include "giomm/menumodel.h" #include "glibmm/refptr.h" #include "gtk/gtk.h" #include "gtkmm/eventcontrollermotion.h" -#include "gtkmm/gesture.h" #include "gtkmm/gestureclick.h" #include "gtkmm/widget.h" #include "wf-autohide-window.hpp" @@ -51,7 +49,11 @@ void WayfirePopup::set_child(Gtk::Widget & widget) { menu.popdown(); use_menu = false; - popover.set_child(widget); + popover.set_child(scroll); + scroll.set_child(widget); + scroll.set_policy(Gtk::PolicyType::AUTOMATIC, Gtk::PolicyType::AUTOMATIC); + scroll.set_propagate_natural_height(true); + scroll.set_propagate_natural_width(true); } void WayfirePopup::set_menu_model(Glib::RefPtr new_menu) diff --git a/src/util/wf-popover.hpp b/src/util/wf-popover.hpp index 681d03ea..9d8afb27 100644 --- a/src/util/wf-popover.hpp +++ b/src/util/wf-popover.hpp @@ -2,6 +2,7 @@ #include "giomm/menumodel.h" #include "glibmm/refptr.h" +#include "gtkmm/scrolledwindow.h" #include #include #include @@ -20,6 +21,7 @@ using type_signal_simple = sigc::signal; class WayfirePopup { private: + Gtk::ScrolledWindow scroll; Gtk::Popover popover; Gtk::PopoverMenu menu; bool use_menu; From c7c5c41d23d8d7ba218c394c933accc5f5f5cfd5 Mon Sep 17 00:00:00 2001 From: trigg Date: Sun, 22 Mar 2026 12:52:27 +0000 Subject: [PATCH 04/18] panel: move force-over-fullscreen up to autohide logic panel: fix menu crash before init panel: made menu-follows-mouse an option --- metadata/panel.xml | 14 +++++++---- src/dock/dock.cpp | 1 + src/panel/widgets/menu.cpp | 44 +++++---------------------------- src/panel/widgets/menu.hpp | 3 +-- src/panel/widgets/network.cpp | 1 + src/util/wf-autohide-window.cpp | 40 ++++++++++++++++++++++-------- src/util/wf-autohide-window.hpp | 2 ++ src/util/wf-popover.cpp | 5 ++++ src/util/wf-popover.hpp | 1 + 9 files changed, 56 insertions(+), 55 deletions(-) diff --git a/metadata/panel.xml b/metadata/panel.xml index d4362215..c00c7233 100644 --- a/metadata/panel.xml +++ b/metadata/panel.xml @@ -122,6 +122,15 @@ If full_span is off, both sides of the panel will take the same amount of space, <_name>Background + + -