diff --git a/metadata/panel.xml b/metadata/panel.xml index 6508a9db..3d3db3c0 100644 --- a/metadata/panel.xml +++ b/metadata/panel.xml @@ -189,6 +189,16 @@ 42 0 + + <_short>Menu item icon size + 48 + 0 + + + <_short>Space between menu items + 8 + 0 + <_short>Menu Minimum Category Width 200 diff --git a/src/panel/panel.cpp b/src/panel/panel.cpp index 0776963f..662967d3 100644 --- a/src/panel/panel.cpp +++ b/src/panel/panel.cpp @@ -408,6 +408,7 @@ void WayfirePanelApp::on_activate() { {"panel/minimal_height", ""}, {"panel/menu_icon_size", ".menu-icon"}, + {"panel/menu_item_icon_size", ".app-button"}, {"panel/launchers_size", ".launcher"}, {"panel/battery_icon_size", ".battery image"}, {"panel/network_icon_size", ".network"}, diff --git a/src/panel/widgets/menu.cpp b/src/panel/widgets/menu.cpp index e2295eda..5991def7 100644 --- a/src/panel/widgets/menu.cpp +++ b/src/panel/widgets/menu.cpp @@ -59,69 +59,129 @@ void WfMenuCategoryButton::on_click() menu->set_category(category); } -WfMenuMenuItem::WfMenuMenuItem(WayfireMenu *_menu, Glib::RefPtr app) : - Gtk::FlowBoxChild(), menu(_menu), m_app_info(app) +WfMenuItem::WfMenuItem(WayfireMenu *_menu, Glib::RefPtr app) : + Gtk::FlowBoxChild(), menu(_menu), app_info(app) { - m_image.set((const Glib::RefPtr&)app->get_icon()); - m_image.set_pixel_size(48); - m_label.set_text(app->get_name()); - m_label.set_xalign(0.0); - m_label.set_hexpand(true); - m_has_actions = app->list_actions().size() > 0; - m_button_box.append(m_image); - m_button_box.append(m_label); + image.set((const Glib::RefPtr&)app->get_icon()); - m_button.set_child(m_button_box); - signals.push_back(m_button.signal_clicked().connect( - [this] () + label.set_text(app->get_name()); + label.set_ellipsize(Pango::EllipsizeMode::END); + + + extra_actions_button.add_css_class("flat"); + extra_actions_button.add_css_class("app-button-extras"); + extra_actions_button.set_direction(Gtk::ArrowType::RIGHT); + extra_actions_button.set_has_frame(false); + + box.set_expand(false); + box.add_css_class("flat"); + box.add_css_class("widget-icon"); + box.add_css_class("app-button"); + + set_child(box); + + auto left_click_g = Gtk::GestureClick::create(); + auto right_click_g = Gtk::GestureClick::create(); + auto long_press_g = Gtk::GestureLongPress::create(); + left_click_g->set_button(1); + right_click_g->set_button(3); + long_press_g->set_touch_only(true); + + signals.push_back(left_click_g->signal_pressed().connect( + [=] (int c, double x, double y) { - this->on_click(); + on_click(); + left_click_g->set_state(Gtk::EventSequenceState::CLAIMED); + })); + signals.push_back(right_click_g->signal_pressed().connect( + [=] (int c, double x, double y) + { + extra_actions_button.activate(); + right_click_g->set_state(Gtk::EventSequenceState::CLAIMED); + })); + signals.push_back(long_press_g->signal_pressed().connect( + [=] (double x, double y) + { + extra_actions_button.activate(); + long_press_g->set_state(Gtk::EventSequenceState::CLAIMED); + left_click_g->set_state(Gtk::EventSequenceState::DENIED); })); - m_padding_box.append(m_button); - m_label.set_ellipsize(Pango::EllipsizeMode::END); - m_label.set_max_width_chars(5); - m_button.add_css_class("flat"); - m_extra_actions_button.add_css_class("flat"); - m_extra_actions_button.add_css_class("app-button-extras"); - m_extra_actions_button.set_halign(Gtk::Align::END); - m_extra_actions_button.set_direction(Gtk::ArrowType::RIGHT); - m_extra_actions_button.set_has_frame(false); - m_extra_actions_button.set_icon_name("arrow-right"); - m_menu = Gio::Menu::create(); - m_actions = Gio::SimpleActionGroup::create(); - m_extra_actions_button.hide(); if (menu->menu_list) { - m_padding_box.append(m_extra_actions_button); - this->set_size_request(menu->menu_min_content_width, 48); - for (auto action : app->list_actions()) + label.set_hexpand(true); + label.set_halign(Gtk::Align::FILL); + label.set_halign(Gtk::Align::START); + label.set_xalign(0.0); + list_item.set_hexpand(true); + box.set_hexpand(true); + set_hexpand(true); + box.set_orientation(Gtk::Orientation::HORIZONTAL); + extra_actions_button.set_halign(Gtk::Align::END); + extra_actions_button.set_icon_name("arrow-right"); + button.add_css_class("flat"); + + list_item.append(image); + list_item.append(label); + button.set_child(list_item); + + list_item.add_controller(left_click_g); + list_item.add_controller(right_click_g); + list_item.add_controller(long_press_g); + + box.append(button); + box.append(extra_actions_button); + } else + { + label.set_max_width_chars(0); + box.set_orientation(Gtk::Orientation::VERTICAL); + if (app->list_actions().size() == 0) { - std::stringstream ss; - ss << "app." << action; - std::string full_action = ss.str(); + button.set_child(image); + button.add_css_class("flat"); + box.append(button); + } else + { + extra_actions_button.set_child(image); + box.append(extra_actions_button); + } - auto menu_item = Gio::MenuItem::create(m_app_info->get_action_name(action), full_action); + box.add_controller(left_click_g); + box.add_controller(right_click_g); + box.add_controller(long_press_g); - auto action_obj = Gio::SimpleAction::create(action); - signals.push_back(action_obj->signal_activate().connect( - [this, action] (Glib::VariantBase vb) - { - auto ctx = Gdk::Display::get_default()->get_app_launch_context(); - m_app_info->launch_action(action, ctx); - menu->hide_menu(); - })); - m_menu->append_item(menu_item); - m_actions->add_action(action_obj); - - m_extra_actions_button.show(); - } + box.append(label); + } + + m_menu = Gio::Menu::create(); + actions = Gio::SimpleActionGroup::create(); + extra_actions_button.hide(); - m_extra_actions_button.set_menu_model(m_menu); + for (auto action : app->list_actions()) + { + std::stringstream ss; + ss << "app." << action; + std::string full_action = ss.str(); + + auto menu_item = Gio::MenuItem::create(app_info->get_action_name(action), full_action); + + auto action_obj = Gio::SimpleAction::create(action); + signals.push_back(action_obj->signal_activate().connect( + [this, action] (Glib::VariantBase vb) + { + auto ctx = Gdk::Display::get_default()->get_app_launch_context(); + app_info->launch_action(action, ctx); + menu->hide_menu(); + })); + m_menu->append_item(menu_item); + actions->add_action(action_obj); + + extra_actions_button.show(); } - set_child(m_padding_box); - add_css_class("app-button"); + extra_actions_button.set_menu_model(m_menu); + extra_actions_button.insert_action_group("app", actions); + set_has_tooltip(); signals.push_back(signal_query_tooltip().connect([=] (int x, int y, bool key_mode, const std::shared_ptr& tooltip) -> @@ -130,32 +190,9 @@ WfMenuMenuItem::WfMenuMenuItem(WayfireMenu *_menu, Glib::RefPtrset_text(app->get_name()); return true; }, false)); - m_extra_actions_button.insert_action_group("app", m_actions); - - auto click_gesture = Gtk::GestureClick::create(); - auto long_press = Gtk::GestureLongPress::create(); - long_press->set_touch_only(true); - long_press->signal_pressed().connect( - [=] (double x, double y) - { - m_extra_actions_button.activate(); - long_press->set_state(Gtk::EventSequenceState::CLAIMED); - click_gesture->set_state(Gtk::EventSequenceState::DENIED); - }); - click_gesture->set_button(3); - signals.push_back(click_gesture->signal_pressed().connect([=] (int count, double x, double y) - { - click_gesture->set_state(Gtk::EventSequenceState::CLAIMED); - })); - signals.push_back(click_gesture->signal_released().connect([=] (int count, double x, double y) - { - m_extra_actions_button.activate(); - })); - m_button.add_controller(long_press); - m_button.add_controller(click_gesture); } -WfMenuMenuItem::~WfMenuMenuItem() +WfMenuItem::~WfMenuItem() { for (auto signal : signals) { @@ -163,21 +200,21 @@ WfMenuMenuItem::~WfMenuMenuItem() } } -void WfMenuMenuItem::on_click() +void WfMenuItem::on_click() { auto ctx = Gdk::Display::get_default()->get_app_launch_context(); - m_app_info->launch(std::vector>(), ctx); + app_info->launch(std::vector>(), ctx); menu->hide_menu(); } -void WfMenuMenuItem::set_search_value(uint32_t value) +void WfMenuItem::set_search_value(uint32_t value) { - m_search_value = value; + search_value = value; } -uint32_t WfMenuMenuItem::get_search_value() +uint32_t WfMenuItem::get_search_value() { - return m_search_value; + return search_value; } /* Fuzzy search for pattern in text. We use a greedy algorithm as follows: @@ -209,12 +246,12 @@ static bool fuzzy_match(Glib::ustring text, Glib::ustring pattern) return i == pattern.length(); } -uint32_t WfMenuMenuItem::fuzzy_match(Glib::ustring pattern) +uint32_t WfMenuItem::fuzzy_match(Glib::ustring pattern) { uint32_t match_score = 0; - Glib::ustring name = m_app_info->get_name(); - Glib::ustring long_name = m_app_info->get_display_name(); - Glib::ustring progr = m_app_info->get_executable(); + Glib::ustring name = app_info->get_name(); + Glib::ustring long_name = app_info->get_display_name(); + Glib::ustring progr = app_info->get_executable(); auto name_lower = name.lowercase(); auto long_name_lower = long_name.lowercase(); @@ -239,13 +276,13 @@ uint32_t WfMenuMenuItem::fuzzy_match(Glib::ustring pattern) return match_score; } -uint32_t WfMenuMenuItem::matches(Glib::ustring pattern) +uint32_t WfMenuItem::matches(Glib::ustring pattern) { uint32_t match_score = 0; - Glib::ustring long_name = m_app_info->get_display_name(); - Glib::ustring name = m_app_info->get_name(); - Glib::ustring progr = m_app_info->get_executable(); - Glib::ustring descr = m_app_info->get_description(); + Glib::ustring long_name = app_info->get_display_name(); + Glib::ustring name = app_info->get_name(); + Glib::ustring progr = app_info->get_executable(); + Glib::ustring descr = app_info->get_description(); auto name_lower = name.lowercase(); auto long_name_lower = long_name.lowercase(); @@ -280,10 +317,10 @@ uint32_t WfMenuMenuItem::matches(Glib::ustring pattern) return match_score; } -bool WfMenuMenuItem::operator <(const WfMenuMenuItem& other) +bool WfMenuItem::operator <(const WfMenuItem& other) { - return Glib::ustring(m_app_info->get_name()).lowercase() < - Glib::ustring(other.m_app_info->get_name()).lowercase(); + return Glib::ustring(app_info->get_name()).lowercase() < + Glib::ustring(other.app_info->get_name()).lowercase(); } void WayfireMenu::load_menu_item(AppInfo app_info) @@ -384,7 +421,7 @@ void WayfireMenu::populate_menu_items(std::string category) for (auto app_info : category_list[category]->items) { - auto app = new WfMenuMenuItem(this, app_info); + auto app = new WfMenuItem(this, app_info); flowbox.append(*app); } } @@ -442,11 +479,9 @@ void WayfireMenu::load_menu_items_all() void WayfireMenu::on_search_changed() { - search_entry.set_text(search_contents); - search_entry.set_position(search_contents.length()); if (menu_show_categories) { - if (search_contents.length() == 0) + if (search_entry.get_text().length() == 0) { /* Text has been unset, show categories again */ populate_menu_items(category); @@ -459,7 +494,7 @@ void WayfireMenu::on_search_changed() } } - m_sort_names = search_contents.length() == 0; + m_sort_names = search_entry.get_text().length() == 0; fuzzy_filter = false; count_matches = 0; flowbox.unselect_all(); @@ -480,10 +515,10 @@ void WayfireMenu::on_search_changed() bool WayfireMenu::on_filter(Gtk::FlowBoxChild *child) { - auto button = dynamic_cast(child); + auto button = dynamic_cast(child); assert(button); - auto text = search_contents; + auto text = search_entry.get_text(); uint32_t match_score = this->fuzzy_filter ? button->fuzzy_match(text) : button->matches(text); @@ -499,8 +534,8 @@ bool WayfireMenu::on_filter(Gtk::FlowBoxChild *child) bool WayfireMenu::on_sort(Gtk::FlowBoxChild *a, Gtk::FlowBoxChild *b) { - auto b1 = dynamic_cast(a); - auto b2 = dynamic_cast(b); + auto b1 = dynamic_cast(a); + auto b2 = dynamic_cast(b); assert(b1 && b2); if (m_sort_names) @@ -513,7 +548,7 @@ bool WayfireMenu::on_sort(Gtk::FlowBoxChild *a, Gtk::FlowBoxChild *b) void WayfireMenu::on_popover_shown() { - search_contents = ""; + search_entry.delete_text(0, search_entry.get_text_length()); on_search_changed(); set_category("All"); flowbox.unselect_all(); @@ -574,26 +609,22 @@ void WayfireMenu::setup_popover_layout() search_entry.add_css_class("app-search"); + signals.push_back((search_entry.signal_changed().connect( + [this] () + { + on_search_changed(); + }))); auto typing_gesture = Gtk::EventControllerKey::create(); typing_gesture->set_propagation_phase(Gtk::PropagationPhase::CAPTURE); signals.push_back(typing_gesture->signal_key_pressed().connect([=] (guint keyval, guint keycode, Gdk::ModifierType state) { - if (keyval == GDK_KEY_BackSpace) - { - if (search_contents.length() > 0) - { - search_contents.pop_back(); - } - - on_search_changed(); - return true; - } else if ((keyval == GDK_KEY_Return) || (keyval == GDK_KEY_KP_Enter)) + if ((keyval == GDK_KEY_Return) || (keyval == GDK_KEY_KP_Enter)) { auto children = flowbox.get_selected_children(); if (children.size() == 1) { - auto child = dynamic_cast(children[0]); + auto child = dynamic_cast(children[0]); child->on_click(); } @@ -601,15 +632,19 @@ void WayfireMenu::setup_popover_layout() } else if (keyval == GDK_KEY_Escape) { button->get_popover()->hide(); + } else if ((keyval == GDK_KEY_Up) || + (keyval == GDK_KEY_Down) || + (keyval == GDK_KEY_Left) || + (keyval == GDK_KEY_Right)) + { + return false; + } else if (search_entry.has_focus()) + { + return false; } else { - std::string input = gdk_keyval_name(keyval); - if (input.length() == 1) - { - search_contents = search_contents + input; - on_search_changed(); - return true; - } + search_entry.grab_focus(); + on_search_changed(); } return false; @@ -651,6 +686,14 @@ void WayfireMenu::update_popover_layout() popover_layout_box.remove(separator); popover_layout_box.remove(box_bottom); + if (menu_list) + { + flowbox.set_max_children_per_line(1); + } else + { + flowbox.set_max_children_per_line(-1); + } + if ((std::string)panel_position == WF_WINDOW_POSITION_TOP) { popover_layout_box.append(search_entry); @@ -877,6 +920,11 @@ void WayfireMenu::init(Gtk::Box *container) // configuration reloading callbacks menu_icon.set_callback([=] () { update_icon(); }); + flowbox_spacing.set_callback([=] () + { + flowbox.set_column_spacing(flowbox_spacing.value()); + flowbox.set_column_spacing(flowbox_spacing.value()); + }); menu_min_category_width.set_callback([=] () { update_category_width(); }); menu_min_content_height.set_callback([=] () { update_content_height(); }); menu_min_content_width.set_callback([=] () { update_content_width(); }); @@ -957,7 +1005,6 @@ void WayfireMenu::update_content_width() void WayfireMenu::toggle_menu() { - search_contents = ""; search_entry.set_text(""); if (button->get_active()) { @@ -984,7 +1031,7 @@ void WayfireMenu::select_first_flowbox_item() auto child = flowbox.get_child_at_index(0); if (child) { - auto cast_child = dynamic_cast(child); + auto cast_child = dynamic_cast(child); if (cast_child) { flowbox.select_child(*cast_child); diff --git a/src/panel/widgets/menu.hpp b/src/panel/widgets/menu.hpp index 9f7f403f..a8d7cd7e 100644 --- a/src/panel/widgets/menu.hpp +++ b/src/panel/widgets/menu.hpp @@ -6,6 +6,8 @@ #include #include "../widget.hpp" +#include "gtkmm/enums.h" +#include "gtkmm/orientable.h" #include "wf-popover.hpp" class WayfireMenu; @@ -43,37 +45,34 @@ class WfMenuCategoryButton : public Gtk::Button void on_click(); }; -class WfMenuMenuItem : public Gtk::FlowBoxChild +class WfMenuItem : public Gtk::FlowBoxChild { public: - WfMenuMenuItem(WayfireMenu *menu, AppInfo app); - ~WfMenuMenuItem(); + WfMenuItem(WayfireMenu *menu, AppInfo app); + ~WfMenuItem(); uint32_t matches(Glib::ustring text); uint32_t fuzzy_match(Glib::ustring text); - bool operator <(const WfMenuMenuItem& other); + bool operator <(const WfMenuItem& other); void set_search_value(uint32_t value); uint32_t get_search_value(); void on_click(); private: WayfireMenu *menu; - Gtk::Box m_left_pad, m_right_pad; - Gtk::Box m_padding_box; - Gtk::Box m_button_box; - Gtk::Button m_button; - Gtk::Box m_list_box; - Gtk::Image m_image; - Gtk::Label m_label; + Gtk::Box box, list_item; + Gtk::Image image; + Gtk::Label label; Glib::RefPtr m_menu; - Glib::RefPtr m_actions; - Gtk::MenuButton m_extra_actions_button; + Glib::RefPtr actions; + Gtk::Button button; + Gtk::MenuButton extra_actions_button; std::vector signals; - bool m_has_actions = false; - uint32_t m_search_value = 0; + bool has_actions = false; + uint32_t search_value = 0; - AppInfo m_app_info; + AppInfo app_info; }; class WayfireLogoutUIButton @@ -123,8 +122,6 @@ class WayfireMenu : public WayfireWidget { WayfireOutput *output; - std::string search_contents = ""; - Gtk::Box flowbox_container; Gtk::Box box, box_bottom, scroll_pair; Gtk::Box bottom_pad; @@ -176,6 +173,7 @@ class WayfireMenu : public WayfireWidget WfOption fuzzy_search_enabled{"panel/menu_fuzzy_search"}; WfOption panel_position{"panel/position"}; WfOption menu_icon{"panel/menu_icon"}; + WfOption flowbox_spacing{"panel/menu_item_spacing"}; WfOption menu_min_category_width{"panel/menu_min_category_width"}; WfOption menu_min_content_height{"panel/menu_min_content_height"}; WfOption menu_show_categories{"panel/menu_show_categories"};