diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-11-11 10:43:03 +0100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-11-11 13:13:08 +0100 |
commit | 74be54cce801464d8f791444659962700937b2a7 (patch) | |
tree | ffb64c26ddf3a61d1a47cccc91a0b9e1f8f5da3d | |
parent | 3a71c018bfd2f015d459462177188e06db2af567 (diff) | |
download | serenity-74be54cce801464d8f791444659962700937b2a7.zip |
WindowServer: Organize system menu app shortcuts into categories
If the .af file for an app contains the App/Category key, we'll now put
it in a submenu of the system menu, together with all the other apps in
that same category. This is pretty neat! :^)
-rw-r--r-- | Servers/WindowServer/WSMenu.cpp | 40 | ||||
-rw-r--r-- | Servers/WindowServer/WSMenu.h | 2 | ||||
-rw-r--r-- | Servers/WindowServer/WSMenuItem.cpp | 6 | ||||
-rw-r--r-- | Servers/WindowServer/WSWindowManager.cpp | 45 | ||||
-rw-r--r-- | Servers/WindowServer/WSWindowManager.h | 4 |
5 files changed, 77 insertions, 20 deletions
diff --git a/Servers/WindowServer/WSMenu.cpp b/Servers/WindowServer/WSMenu.cpp index 710332df2d..927f247852 100644 --- a/Servers/WindowServer/WSMenu.cpp +++ b/Servers/WindowServer/WSMenu.cpp @@ -198,6 +198,17 @@ void WSMenu::draw() } } +void close_everyone_not_in_lineage(WSMenu& menu) +{ + for (auto& open_menu : WSWindowManager::the().menu_manager().open_menu_stack()) { + if (!open_menu) + continue; + if (&menu == open_menu.ptr() || open_menu->is_menu_ancestor_of(menu)) + continue; + open_menu->menu_window()->set_visible(false); + } +} + void WSMenu::event(CEvent& event) { if (event.type() == WSEvent::MouseMove) { @@ -207,21 +218,10 @@ void WSMenu::event(CEvent& event) return; m_hovered_item = item; if (m_hovered_item->is_submenu()) { + close_everyone_not_in_lineage(*m_hovered_item->submenu()); m_hovered_item->submenu()->popup(m_hovered_item->rect().top_right().translated(menu_window()->rect().location()), true); } else { - bool close_remaining_menus = false; - for (auto& open_menu : WSWindowManager::the().menu_manager().open_menu_stack()) { - if (!open_menu) - continue; - if (close_remaining_menus) { - open_menu->menu_window()->set_visible(false); - continue; - } - if (open_menu == this) { - close_remaining_menus = true; - continue; - } - } + close_everyone_not_in_lineage(*this); } redraw(); return; @@ -307,3 +307,17 @@ void WSMenu::popup(const Point& position, bool is_submenu) window.set_visible(true); WSWindowManager::the().set_current_menu(this, is_submenu); } + +bool WSMenu::is_menu_ancestor_of(const WSMenu& other) const +{ + for (auto& item : m_items) { + if (!item.is_submenu()) + continue; + auto& submenu = *const_cast<WSMenuItem&>(item).submenu(); + if (&submenu == &other) + return true; + if (submenu.is_menu_ancestor_of(other)) + return true; + } + return false; +} diff --git a/Servers/WindowServer/WSMenu.h b/Servers/WindowServer/WSMenu.h index 5bfebb9b18..72407b78a8 100644 --- a/Servers/WindowServer/WSMenu.h +++ b/Servers/WindowServer/WSMenu.h @@ -75,6 +75,8 @@ public: void popup(const Point&, bool is_submenu = false); + bool is_menu_ancestor_of(const WSMenu&) const; + private: virtual void event(CEvent&) override; diff --git a/Servers/WindowServer/WSMenuItem.cpp b/Servers/WindowServer/WSMenuItem.cpp index 477497b9dc..fe56a74ee3 100644 --- a/Servers/WindowServer/WSMenuItem.cpp +++ b/Servers/WindowServer/WSMenuItem.cpp @@ -1,6 +1,7 @@ #include "WSMenuItem.h" #include "WSClientConnection.h" #include "WSMenu.h" +#include "WSWindowManager.h" #include <LibDraw/GraphicsBitmap.h> WSMenuItem::WSMenuItem(WSMenu& menu, unsigned identifier, const String& text, const String& shortcut_text, bool enabled, bool checkable, bool checked, const GraphicsBitmap* icon) @@ -45,6 +46,7 @@ void WSMenuItem::set_checked(bool checked) WSMenu* WSMenuItem::submenu() { ASSERT(is_submenu()); - ASSERT(m_menu.client()); - return m_menu.client()->find_menu_by_id(m_submenu_id); + if (m_menu.client()) + return m_menu.client()->find_menu_by_id(m_submenu_id); + return WSWindowManager::the().find_internal_menu_by_id(m_submenu_id); } diff --git a/Servers/WindowServer/WSWindowManager.cpp b/Servers/WindowServer/WSWindowManager.cpp index 672bef66db..94a0e57657 100644 --- a/Servers/WindowServer/WSWindowManager.cpp +++ b/Servers/WindowServer/WSWindowManager.cpp @@ -9,8 +9,8 @@ #include <AK/LogStream.h> #include <AK/StdLibExtras.h> #include <AK/Vector.h> -#include <LibCore/CTimer.h> #include <LibCore/CDirIterator.h> +#include <LibCore/CTimer.h> #include <LibDraw/CharacterBitmap.h> #include <LibDraw/Font.h> #include <LibDraw/PNGLoader.h> @@ -49,6 +49,7 @@ WSWindowManager::WSWindowManager() String binary_name; String description; String icon_path; + String category; }; Vector<AppMenuItem> apps; @@ -62,16 +63,41 @@ WSWindowManager::WSWindowManager() continue; auto app_name = af->read_entry("App", "Name"); auto app_executable = af->read_entry("App", "Executable"); + auto app_category = af->read_entry("App", "Category"); auto app_icon_path = af->read_entry("Icons", "16x16"); - apps.append({ app_executable, app_name, app_icon_path }); + apps.append({ app_executable, app_name, app_icon_path, app_category }); } u8 system_menu_name[] = { 0xc3, 0xb8, 0 }; m_system_menu = WSMenu::construct(nullptr, -1, String((const char*)system_menu_name)); - int appIndex = 1; + // First we construct all the necessary app category submenus. for (const auto& app : apps) { - m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, appIndex++, app.description, String(), true, false, false, load_png(app.icon_path))); + if (app.category.is_null()) + continue; + if (m_app_category_menus.contains(app.category)) + continue; + auto category_menu = WSMenu::construct(nullptr, 5000 + m_app_category_menus.size(), app.category); + category_menu->on_item_activation = [apps](auto& item) { + if (item.identifier() >= 1 && item.identifier() <= 1u + apps.size() - 1) { + if (fork() == 0) { + const auto& bin = apps[item.identifier() - 1].binary_name; + execl(bin.characters(), bin.characters(), nullptr); + ASSERT_NOT_REACHED(); + } + } + }; + auto item = make<WSMenuItem>(*m_system_menu, -1, app.category); + item->set_submenu_id(category_menu->menu_id()); + m_system_menu->add_item(move(item)); + m_app_category_menus.set(app.category, move(category_menu)); + } + + // Then we create and insert all the app menu items into the right place. + int app_identifier = 1; + for (const auto& app : apps) { + auto parent_menu = m_app_category_menus.get(app.category).value_or(*m_system_menu); + parent_menu->add_item(make<WSMenuItem>(*m_system_menu, app_identifier++, app.description, String(), true, false, false, load_png(app.icon_path))); } m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, WSMenuItem::Separator)); @@ -494,7 +520,7 @@ bool WSWindowManager::process_ongoing_window_drag(WSMouseEvent& event, WSWindow* return true; } if (event.type() == WSEvent::MouseMove) { - + #ifdef DRAG_DEBUG dbg() << "[WM] Dragging, origin: " << m_drag_origin << ", now: " << event.position(); if (m_drag_window->is_maximized()) { @@ -1121,3 +1147,12 @@ Rect WSWindowManager::maximized_window_rect(const WSWindow& window) const return rect; } + +WSMenu* WSWindowManager::find_internal_menu_by_id(int menu_id) +{ + for (auto& it : m_app_category_menus) { + if (menu_id == it.value->menu_id()) + return it.value; + } + return nullptr; +} diff --git a/Servers/WindowServer/WSWindowManager.h b/Servers/WindowServer/WSWindowManager.h index 1494f595fb..6595233099 100644 --- a/Servers/WindowServer/WSWindowManager.h +++ b/Servers/WindowServer/WSWindowManager.h @@ -148,6 +148,8 @@ public: m_current_menubar->for_each_menu(callback); } + WSMenu* find_internal_menu_by_id(int); + private: NonnullRefPtr<WSCursor> get_cursor(const String& name); NonnullRefPtr<WSCursor> get_cursor(const String& name, const Point& hotspot); @@ -262,6 +264,8 @@ private: WeakPtr<WSButton> m_hovered_button; RefPtr<CConfigFile> m_wm_config; + + HashMap<String, NonnullRefPtr<WSMenu>> m_app_category_menus; }; template<typename Callback> |