summaryrefslogtreecommitdiff
path: root/Userland/Services
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2021-04-05 23:03:55 +0200
committerAndreas Kling <kling@serenityos.org>2021-04-05 23:15:43 +0200
commitd80fb6d9c02a6755218007a63c1a1ee842e579f5 (patch)
tree5d90580d78c0c0c1fe2abab612df414bc039eb6b /Userland/Services
parent89bc6557656759ab23d18c7c9e48bae6315d2e2e (diff)
downloadserenity-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.cpp16
-rw-r--r--Userland/Services/WindowServer/Menu.h5
-rw-r--r--Userland/Services/WindowServer/Window.cpp27
-rw-r--r--Userland/Services/WindowServer/Window.h2
-rw-r--r--Userland/Services/WindowServer/WindowFrame.cpp23
-rw-r--r--Userland/Services/WindowServer/WindowFrame.h2
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&);