diff --git a/Libraries/LibGUI/Menu.cpp b/Libraries/LibGUI/Menu.cpp index ac3516b08a..77623dd267 100644 --- a/Libraries/LibGUI/Menu.cpp +++ b/Libraries/LibGUI/Menu.cpp @@ -83,15 +83,15 @@ void Menu::add_separator() m_items.append(make(m_menu_id, MenuItem::Type::Separator)); } -void Menu::realize_if_needed() +void Menu::realize_if_needed(const RefPtr& default_action) { - if (m_menu_id == -1) - realize_menu(); + if (m_menu_id == -1 || m_last_default_action.ptr() != default_action) + realize_menu(default_action); } -void Menu::popup(const Gfx::IntPoint& screen_position) +void Menu::popup(const Gfx::IntPoint& screen_position, const RefPtr& default_action) { - realize_if_needed(); + realize_if_needed(default_action); WindowServerConnection::the().post_message(Messages::WindowServer::PopupMenu(m_menu_id, screen_position)); } @@ -102,7 +102,7 @@ void Menu::dismiss() WindowServerConnection::the().post_message(Messages::WindowServer::DismissMenu(m_menu_id)); } -int Menu::realize_menu() +int Menu::realize_menu(RefPtr default_action) { m_menu_id = WindowServerConnection::the().send_sync(m_name)->menu_id(); @@ -120,8 +120,8 @@ int Menu::realize_menu() } if (item.type() == MenuItem::Type::Submenu) { auto& submenu = *item.submenu(); - submenu.realize_if_needed(); - WindowServerConnection::the().send_sync(m_menu_id, i, submenu.menu_id(), submenu.name(), true, false, false, "", -1, false); + submenu.realize_if_needed(default_action); + WindowServerConnection::the().send_sync(m_menu_id, i, submenu.menu_id(), submenu.name(), true, false, false, false, "", -1, false); continue; } if (item.type() == MenuItem::Type::Action) { @@ -143,10 +143,12 @@ int Menu::realize_menu() } auto shortcut_text = action.shortcut().is_valid() ? action.shortcut().to_string() : String(); bool exclusive = action.group() && action.group()->is_exclusive() && action.is_checkable(); - WindowServerConnection::the().send_sync(m_menu_id, i, -1, action.text(), action.is_enabled(), action.is_checkable(), action.is_checkable() ? action.is_checked() : false, shortcut_text, icon_buffer_id, exclusive); + bool is_default = (default_action.ptr() == &action); + WindowServerConnection::the().send_sync(m_menu_id, i, -1, action.text(), action.is_enabled(), action.is_checkable(), action.is_checkable() ? action.is_checked() : false, is_default, shortcut_text, icon_buffer_id, exclusive); } } all_menus().set(m_menu_id, this); + m_last_default_action = default_action ? default_action->make_weak_ptr() : nullptr; return m_menu_id; } @@ -159,6 +161,12 @@ void Menu::unrealize_menu() m_menu_id = 0; } +void Menu::realize_menu_if_needed() +{ + if (menu_id() == -1) + realize_menu(); +} + Action* Menu::action_at(size_t index) { if (index >= m_items.size()) diff --git a/Libraries/LibGUI/Menu.h b/Libraries/LibGUI/Menu.h index 2b56e04d35..211e174798 100644 --- a/Libraries/LibGUI/Menu.h +++ b/Libraries/LibGUI/Menu.h @@ -27,7 +27,9 @@ #pragma once #include +#include #include +#include #include #include @@ -39,11 +41,7 @@ public: explicit Menu(const StringView& name = ""); virtual ~Menu() override; - void realize_menu_if_needed() - { - if (menu_id() == -1) - realize_menu(); - } + void realize_menu_if_needed(); static Menu* from_menu_id(int); int menu_id() const { return m_menu_id; } @@ -56,19 +54,20 @@ public: void add_separator(); Menu& add_submenu(const String& name); - void popup(const Gfx::IntPoint& screen_position); + void popup(const Gfx::IntPoint& screen_position, const RefPtr& default_action = nullptr); void dismiss(); private: friend class MenuBar; - int realize_menu(); + int realize_menu(RefPtr default_action = nullptr); void unrealize_menu(); - void realize_if_needed(); + void realize_if_needed(const RefPtr& default_action); int m_menu_id { -1 }; String m_name; NonnullOwnPtrVector m_items; + WeakPtr m_last_default_action; }; } diff --git a/Libraries/LibGUI/MenuItem.cpp b/Libraries/LibGUI/MenuItem.cpp index cc6aa81b22..8c6e5a92ec 100644 --- a/Libraries/LibGUI/MenuItem.cpp +++ b/Libraries/LibGUI/MenuItem.cpp @@ -79,13 +79,22 @@ void MenuItem::set_checked(bool checked) update_window_server(); } +void MenuItem::set_default(bool is_default) +{ + ASSERT(is_checkable()); + if (m_default == is_default) + return; + m_default = is_default; + update_window_server(); +} + void MenuItem::update_window_server() { if (m_menu_id < 0) return; auto& action = *m_action; auto shortcut_text = action.shortcut().is_valid() ? action.shortcut().to_string() : String(); - WindowServerConnection::the().send_sync(m_menu_id, m_identifier, -1, action.text(), action.is_enabled(), action.is_checkable(), action.is_checkable() ? action.is_checked() : false, shortcut_text); + WindowServerConnection::the().send_sync(m_menu_id, m_identifier, -1, action.text(), action.is_enabled(), action.is_checkable(), action.is_checkable() ? action.is_checked() : false, m_default, shortcut_text); } void MenuItem::set_menu_id(Badge, unsigned int menu_id) diff --git a/Libraries/LibGUI/MenuItem.h b/Libraries/LibGUI/MenuItem.h index 871690a805..1441061661 100644 --- a/Libraries/LibGUI/MenuItem.h +++ b/Libraries/LibGUI/MenuItem.h @@ -47,7 +47,7 @@ public: ~MenuItem(); Type type() const { return m_type; } - String text() const; + const Action* action() const { return m_action.ptr(); } Action* action() { return m_action.ptr(); } unsigned identifier() const { return m_identifier; } @@ -64,6 +64,9 @@ public: bool is_enabled() const { return m_enabled; } void set_enabled(bool); + bool is_default() const { return m_default; } + void set_default(bool); + void set_menu_id(Badge, unsigned menu_id); void set_identifier(Badge, unsigned identifier); @@ -76,6 +79,7 @@ private: bool m_enabled { true }; bool m_checkable { false }; bool m_checked { false }; + bool m_default { false }; RefPtr m_action; RefPtr m_submenu; }; diff --git a/Services/WindowServer/ClientConnection.cpp b/Services/WindowServer/ClientConnection.cpp index 4934d6a03d..d62d92a930 100644 --- a/Services/WindowServer/ClientConnection.cpp +++ b/Services/WindowServer/ClientConnection.cpp @@ -201,6 +201,8 @@ OwnPtr ClientConnection::handle(con } auto& menu = *(*it).value; auto menu_item = make(menu, identifier, message.text(), message.shortcut(), message.enabled(), message.checkable(), message.checked()); + if (message.is_default()) + menu_item->set_default(true); if (message.icon_buffer_id() != -1) { auto icon_buffer = SharedBuffer::create_from_shbuf_id(message.icon_buffer_id()); if (!icon_buffer) @@ -260,6 +262,7 @@ OwnPtr ClientConnection::handle( menu_item->set_shortcut_text(message.shortcut()); menu_item->set_enabled(message.enabled()); menu_item->set_checkable(message.checkable()); + menu_item->set_default(message.is_default()); if (message.checkable()) menu_item->set_checked(message.checked()); return make(); diff --git a/Services/WindowServer/WindowServer.ipc b/Services/WindowServer/WindowServer.ipc index 3e3801247f..8fb0b93318 100644 --- a/Services/WindowServer/WindowServer.ipc +++ b/Services/WindowServer/WindowServer.ipc @@ -21,13 +21,14 @@ endpoint WindowServer = 2 bool enabled, bool checkable, bool checked, + bool is_default, [UTF8] String shortcut, i32 icon_buffer_id, bool exclusive) => () AddMenuSeparator(i32 menu_id) => () - UpdateMenuItem(i32 menu_id, i32 identifier, i32 submenu_id, [UTF8] String text, bool enabled, bool checkable, bool checked, [UTF8] String shortcut) => () + UpdateMenuItem(i32 menu_id, i32 identifier, i32 submenu_id, [UTF8] String text, bool enabled, bool checkable, bool checked, bool is_default, [UTF8] String shortcut) => () CreateWindow( Gfx::IntRect rect,