diff --git a/Userland/Libraries/LibGUI/Event.cpp b/Userland/Libraries/LibGUI/Event.cpp index 81e92036ef..c8da21ac16 100644 --- a/Userland/Libraries/LibGUI/Event.cpp +++ b/Userland/Libraries/LibGUI/Event.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Andreas Kling + * Copyright (c) 2020-2021, Andreas Kling * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,6 +26,7 @@ #include #include +#include #include namespace GUI { @@ -69,4 +70,14 @@ String KeyEvent::to_string() const return builder.to_string(); } +ActionEvent::ActionEvent(Type type, Action& action) + : Event(type) + , m_action(action) +{ +} + +ActionEvent::~ActionEvent() +{ +} + } diff --git a/Userland/Libraries/LibGUI/Event.h b/Userland/Libraries/LibGUI/Event.h index 5f841cff02..aa2338f3d6 100644 --- a/Userland/Libraries/LibGUI/Event.h +++ b/Userland/Libraries/LibGUI/Event.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -72,6 +73,8 @@ public: Drop, ThemeChange, ScreenRectChange, + ActionEnter, + ActionLeave, __Begin_WM_Events, WM_WindowRemoved, @@ -428,4 +431,16 @@ private: FocusSource m_source { FocusSource::Programmatic }; }; +class ActionEvent final : public Event { +public: + ActionEvent(Type, Action&); + ~ActionEvent(); + + Action const& action() const { return *m_action; } + Action& action() { return *m_action; } + +private: + NonnullRefPtr m_action; +}; + } diff --git a/Userland/Libraries/LibGUI/WindowServerConnection.cpp b/Userland/Libraries/LibGUI/WindowServerConnection.cpp index dc86c7cd24..a9246143d3 100644 --- a/Userland/Libraries/LibGUI/WindowServerConnection.cpp +++ b/Userland/Libraries/LibGUI/WindowServerConnection.cpp @@ -273,6 +273,38 @@ void WindowServerConnection::handle(const Messages::WindowClient::MenuItemActiva action->activate(menu); } +void WindowServerConnection::handle(Messages::WindowClient::MenuItemEntered const& message) +{ + auto* menu = Menu::from_menu_id(message.menu_id()); + if (!menu) { + dbgln("WindowServerConnection received MenuItemEntered for invalid menu ID {}", message.menu_id()); + return; + } + auto* action = menu->action_at(message.identifier()); + if (!action) + return; + auto* app = Application::the(); + if (!app) + return; + Core::EventLoop::current().post_event(*app, make(GUI::Event::ActionEnter, *action)); +} + +void WindowServerConnection::handle(Messages::WindowClient::MenuItemLeft const& message) +{ + auto* menu = Menu::from_menu_id(message.menu_id()); + if (!menu) { + dbgln("WindowServerConnection received MenuItemLeft for invalid menu ID {}", message.menu_id()); + return; + } + auto* action = menu->action_at(message.identifier()); + if (!action) + return; + auto* app = Application::the(); + if (!app) + return; + Core::EventLoop::current().post_event(*app, make(GUI::Event::ActionLeave, *action)); +} + void WindowServerConnection::handle(const Messages::WindowClient::ScreenRectChanged& message) { Desktop::the().did_receive_screen_rect({}, message.rect()); diff --git a/Userland/Libraries/LibGUI/WindowServerConnection.h b/Userland/Libraries/LibGUI/WindowServerConnection.h index 80975b04c4..00093386cb 100644 --- a/Userland/Libraries/LibGUI/WindowServerConnection.h +++ b/Userland/Libraries/LibGUI/WindowServerConnection.h @@ -64,6 +64,8 @@ private: virtual void handle(const Messages::WindowClient::WindowCloseRequest&) override; virtual void handle(const Messages::WindowClient::WindowResized&) override; virtual void handle(const Messages::WindowClient::MenuItemActivated&) override; + virtual void handle(const Messages::WindowClient::MenuItemEntered&) override; + virtual void handle(const Messages::WindowClient::MenuItemLeft&) override; virtual void handle(const Messages::WindowClient::MenuVisibilityDidChange&) override; virtual void handle(const Messages::WindowClient::ScreenRectChanged&) override; virtual void handle(const Messages::WindowClient::AsyncSetWallpaperFinished&) override; diff --git a/Userland/Services/WindowServer/Menu.cpp b/Userland/Services/WindowServer/Menu.cpp index 8d1208f371..fe5d3d794d 100644 --- a/Userland/Services/WindowServer/Menu.cpp +++ b/Userland/Services/WindowServer/Menu.cpp @@ -300,15 +300,16 @@ MenuItem* Menu::hovered_item() const void Menu::update_for_new_hovered_item(bool make_input) { - auto* hovered_item = this->hovered_item(); - if (hovered_item && hovered_item->is_submenu()) { - VERIFY(menu_window()); - MenuManager::the().close_everyone_not_in_lineage(*hovered_item->submenu()); - hovered_item->submenu()->do_popup(hovered_item->rect().top_right().translated(menu_window()->rect().location()), make_input, true); - } else { - MenuManager::the().close_everyone_not_in_lineage(*this); - ensure_menu_window(); - set_visible(true); + if (auto* hovered_item = this->hovered_item()) { + if (hovered_item->is_submenu()) { + VERIFY(menu_window()); + MenuManager::the().close_everyone_not_in_lineage(*hovered_item->submenu()); + hovered_item->submenu()->do_popup(hovered_item->rect().top_right().translated(menu_window()->rect().location()), make_input, true); + } else { + MenuManager::the().close_everyone_not_in_lineage(*this); + ensure_menu_window(); + set_visible(true); + } } redraw(); } @@ -461,10 +462,7 @@ void Menu::event(Core::Event& event) void Menu::clear_hovered_item() { - if (!hovered_item()) - return; - m_hovered_item_index = -1; - redraw(); + set_hovered_index(-1); } void Menu::start_activation_animation(MenuItem& item) @@ -650,4 +648,20 @@ const Vector* Menu::items_with_alt_shortcut(u32 alt_shortcut) const return &it->value; } +void Menu::set_hovered_index(int index, bool make_input) +{ + if (m_hovered_item_index == index) + return; + if (auto* old_hovered_item = hovered_item()) { + if (client()) + client()->post_message(Messages::WindowClient::MenuItemLeft(m_menu_id, old_hovered_item->identifier())); + } + m_hovered_item_index = index; + update_for_new_hovered_item(make_input); + if (auto* new_hovered_item = hovered_item()) { + if (client()) + client()->post_message(Messages::WindowClient::MenuItemEntered(m_menu_id, new_hovered_item->identifier())); + } +} + } diff --git a/Userland/Services/WindowServer/Menu.h b/Userland/Services/WindowServer/Menu.h index d289c5022f..9bc60bce1f 100644 --- a/Userland/Services/WindowServer/Menu.h +++ b/Userland/Services/WindowServer/Menu.h @@ -101,11 +101,7 @@ public: MenuItem* hovered_item() const; - void set_hovered_index(int index, bool make_input = false) - { - m_hovered_item_index = index; - update_for_new_hovered_item(make_input); - } + void set_hovered_index(int index, bool make_input = false); void clear_hovered_item(); diff --git a/Userland/Services/WindowServer/WindowClient.ipc b/Userland/Services/WindowServer/WindowClient.ipc index c308c1b3df..f2c7c9c899 100644 --- a/Userland/Services/WindowServer/WindowClient.ipc +++ b/Userland/Services/WindowServer/WindowClient.ipc @@ -19,6 +19,8 @@ endpoint WindowClient = 4 WindowResized(i32 window_id, Gfx::IntRect new_rect) =| MenuItemActivated(i32 menu_id, i32 identifier) =| + MenuItemEntered(i32 menu_id, u32 identifier) =| + MenuItemLeft(i32 menu_id, u32 identifier) =| MenuVisibilityDidChange(i32 menu_id, bool visible) =| ScreenRectChanged(Gfx::IntRect rect) =|