diff options
author | Andreas Kling <kling@serenityos.org> | 2021-04-05 23:03:55 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-04-05 23:15:43 +0200 |
commit | d80fb6d9c02a6755218007a63c1a1ee842e579f5 (patch) | |
tree | 5d90580d78c0c0c1fe2abab612df414bc039eb6b /Userland/Services | |
parent | 89bc6557656759ab23d18c7c9e48bae6315d2e2e (diff) | |
download | serenity-d80fb6d9c02a6755218007a63c1a1ee842e579f5.zip |
WindowServer: Support Alt+Character menu shortcuts :^)
This patch adds support for opening menus via keyboard shortcuts.
Use an ampersand in a menu name to automatically create a keyboard
shortcut (Alt + the character following the ampersand.)
Menus with an Alt shortcut have a small underline under the shortcut
character for discoverability.
Diffstat (limited to 'Userland/Services')
-rw-r--r-- | Userland/Services/WindowServer/Menu.cpp | 16 | ||||
-rw-r--r-- | Userland/Services/WindowServer/Menu.h | 5 | ||||
-rw-r--r-- | Userland/Services/WindowServer/Window.cpp | 27 | ||||
-rw-r--r-- | Userland/Services/WindowServer/Window.h | 2 | ||||
-rw-r--r-- | Userland/Services/WindowServer/WindowFrame.cpp | 23 | ||||
-rw-r--r-- | Userland/Services/WindowServer/WindowFrame.h | 2 |
6 files changed, 56 insertions, 19 deletions
diff --git a/Userland/Services/WindowServer/Menu.cpp b/Userland/Services/WindowServer/Menu.cpp index 7980d3810a..f99fceba5c 100644 --- a/Userland/Services/WindowServer/Menu.cpp +++ b/Userland/Services/WindowServer/Menu.cpp @@ -43,12 +43,26 @@ namespace WindowServer { -Menu::Menu(ClientConnection* client, int menu_id, const String& name) +static u32 find_ampersand_shortcut_character(const String& string) +{ + Utf8View utf8_view { string }; + for (auto it = utf8_view.begin(); it != utf8_view.end(); ++it) { + if (*it == '&') { + ++it; + if (it != utf8_view.end() && *it != '&') + return *it; + } + } + return 0; +} + +Menu::Menu(ClientConnection* client, int menu_id, String name) : Core::Object(client) , m_client(client) , m_menu_id(menu_id) , m_name(move(name)) { + m_alt_shortcut_character = find_ampersand_shortcut_character(m_name); } Menu::~Menu() diff --git a/Userland/Services/WindowServer/Menu.h b/Userland/Services/WindowServer/Menu.h index c438cf270c..42f5400ac1 100644 --- a/Userland/Services/WindowServer/Menu.h +++ b/Userland/Services/WindowServer/Menu.h @@ -47,13 +47,15 @@ class Event; class Menu final : public Core::Object { C_OBJECT(Menu) public: - Menu(ClientConnection*, int menu_id, const String& name); + Menu(ClientConnection*, int menu_id, String name); virtual ~Menu() override; ClientConnection* client() { return m_client; } const ClientConnection* client() const { return m_client; } int menu_id() const { return m_menu_id; } + u32 alt_shortcut_character() const { return m_alt_shortcut_character; } + bool is_empty() const { return m_items.is_empty(); } int item_count() const { return m_items.size(); } const MenuItem& item(int index) const { return m_items.at(index); } @@ -142,6 +144,7 @@ private: ClientConnection* m_client { nullptr }; int m_menu_id { 0 }; String m_name; + u32 m_alt_shortcut_character { 0 }; Gfx::IntRect m_rect_in_window_menubar; NonnullOwnPtrVector<MenuItem> m_items; RefPtr<Window> m_menu_window; diff --git a/Userland/Services/WindowServer/Window.cpp b/Userland/Services/WindowServer/Window.cpp index e0ce5afa1d..cfbe2a9875 100644 --- a/Userland/Services/WindowServer/Window.cpp +++ b/Userland/Services/WindowServer/Window.cpp @@ -34,6 +34,7 @@ #include "WindowManager.h" #include <AK/Badge.h> #include <WindowServer/WindowClientEndpoint.h> +#include <ctype.h> namespace WindowServer { @@ -445,12 +446,7 @@ void Window::event(Core::Event& event) m_client->post_message(Messages::WindowClient::WindowLeft(m_window_id)); break; case Event::KeyDown: - m_client->post_message( - Messages::WindowClient::KeyDown(m_window_id, - (u32) static_cast<const KeyEvent&>(event).code_point(), - (u32) static_cast<const KeyEvent&>(event).key(), - static_cast<const KeyEvent&>(event).modifiers(), - (u32) static_cast<const KeyEvent&>(event).scancode())); + handle_keydown_event(static_cast<const KeyEvent&>(event)); break; case Event::KeyUp: m_client->post_message( @@ -483,6 +479,25 @@ void Window::event(Core::Event& event) } } +void Window::handle_keydown_event(const KeyEvent& event) +{ + if (event.modifiers() == Mod_Alt && event.code_point() && menubar()) { + Menu* menu_to_open = nullptr; + menubar()->for_each_menu([&](Menu& menu) { + if (tolower(menu.alt_shortcut_character()) == tolower(event.code_point())) { + menu_to_open = &menu; + return IterationDecision::Break; + } + return IterationDecision::Continue; + }); + if (menu_to_open) { + frame().open_menubar_menu(*menu_to_open); + return; + } + } + m_client->post_message(Messages::WindowClient::KeyDown(m_window_id, (u32)event.code_point(), (u32)event.key(), event.modifiers(), (u32)event.scancode())); +} + void Window::set_global_cursor_tracking_enabled(bool enabled) { m_global_cursor_tracking_enabled = enabled; diff --git a/Userland/Services/WindowServer/Window.h b/Userland/Services/WindowServer/Window.h index ed8677cc8a..a46ec66151 100644 --- a/Userland/Services/WindowServer/Window.h +++ b/Userland/Services/WindowServer/Window.h @@ -41,6 +41,7 @@ namespace WindowServer { class ClientConnection; class Cursor; +class KeyEvent; class Menu; class MenuBar; class MenuItem; @@ -339,6 +340,7 @@ public: private: virtual void event(Core::Event&) override; void handle_mouse_event(const MouseEvent&); + void handle_keydown_event(const KeyEvent&); void update_menu_item_text(PopupMenuItem item); void update_menu_item_enabled(PopupMenuItem item); void add_child_window(Window&); diff --git a/Userland/Services/WindowServer/WindowFrame.cpp b/Userland/Services/WindowServer/WindowFrame.cpp index e40fe4b106..f29a7bf824 100644 --- a/Userland/Services/WindowServer/WindowFrame.cpp +++ b/Userland/Services/WindowServer/WindowFrame.cpp @@ -317,12 +317,7 @@ void WindowFrame::paint_menubar(Gfx::Painter& painter) if (paint_as_pressed || paint_as_hovered) { Gfx::StylePainter::paint_button(painter, menu.rect_in_window_menubar(), palette, Gfx::ButtonStyle::CoolBar, paint_as_pressed, paint_as_hovered); } - painter.draw_text( - text_rect, - menu.name(), - font, - Gfx::TextAlignment::Center, - text_color); + painter.draw_ui_text(menu.name(), text_rect, font, text_color); return IterationDecision::Continue; }); } @@ -749,6 +744,16 @@ void WindowFrame::handle_menubar_mouse_event(const MouseEvent& event) } } +void WindowFrame::open_menubar_menu(Menu& menu) +{ + auto menubar_rect = this->menubar_rect(); + MenuManager::the().close_everyone(); + menu.ensure_menu_window().move_to(menu.rect_in_window_menubar().bottom_left().translated(rect().location()).translated(menubar_rect.location())); + MenuManager::the().open_menu(menu); + WindowManager::the().set_window_with_active_menu(&m_window); + invalidate(menubar_rect); +} + void WindowFrame::handle_menu_mouse_event(Menu& menu, const MouseEvent& event) { auto menubar_rect = this->menubar_rect(); @@ -758,11 +763,7 @@ void WindowFrame::handle_menu_mouse_event(Menu& menu, const MouseEvent& event) bool should_close_menu = &menu == MenuManager::the().current_menu() && is_mousedown_with_left_button; if (should_open_menu) { - MenuManager::the().close_everyone(); - menu.ensure_menu_window().move_to(menu.rect_in_window_menubar().bottom_left().translated(rect().location()).translated(menubar_rect.location())); - MenuManager::the().open_menu(menu); - WindowManager::the().set_window_with_active_menu(&m_window); - invalidate(menubar_rect); + open_menubar_menu(menu); return; } diff --git a/Userland/Services/WindowServer/WindowFrame.h b/Userland/Services/WindowServer/WindowFrame.h index b127c95580..187daf0dbf 100644 --- a/Userland/Services/WindowServer/WindowFrame.h +++ b/Userland/Services/WindowServer/WindowFrame.h @@ -97,6 +97,8 @@ public: bool hit_test(const Gfx::IntPoint&) const; + void open_menubar_menu(Menu&); + private: void paint_simple_rect_shadow(Gfx::Painter&, const Gfx::IntRect&, const Gfx::Bitmap&) const; void paint_notification_frame(Gfx::Painter&); |