mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 14:02:46 +00:00 
			
		
		
		
	LibGUI+WindowServer: Add app-global keyboard shortcuts.
This patch adds a GShortcut class. Each GAction can have a GShortcut which will cause the event loop to listen for that key combination app-globally and activate the event in case it's pressed. The shortcut will also be displayed when the action is added to a menu. Use this to hook up Alt+Up with the "open parent directory" action in the FileManager app. :^)
This commit is contained in:
		
							parent
							
								
									5c0fca0a95
								
							
						
					
					
						commit
						596a5ce5a4
					
				
					 17 changed files with 263 additions and 17 deletions
				
			
		|  | @ -37,7 +37,7 @@ int main(int argc, char** argv) | |||
|     auto* directory_table_view = new DirectoryTableView(widget); | ||||
|     auto* statusbar = new GStatusBar(widget); | ||||
| 
 | ||||
|     auto open_parent_directory_action = GAction::create("Open parent directory", GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/parentdirectory16.rgb", { 16, 16 }), [directory_table_view] (const GAction&) { | ||||
|     auto open_parent_directory_action = GAction::create("Open parent directory", { Mod_Alt, Key_Up }, GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/parentdirectory16.rgb", { 16, 16 }), [directory_table_view] (const GAction&) { | ||||
|         directory_table_view->open_parent_directory(); | ||||
|     }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| #include <LibGUI/GAction.h> | ||||
| #include <LibGUI/GEventLoop.h> | ||||
| 
 | ||||
| GAction::GAction(const String& text, const String& custom_data, Function<void(const GAction&)> on_activation_callback) | ||||
|     : on_activation(move(on_activation_callback)) | ||||
|  | @ -19,8 +20,19 @@ GAction::GAction(const String& text, RetainPtr<GraphicsBitmap>&& icon, Function< | |||
| { | ||||
| } | ||||
| 
 | ||||
| GAction::GAction(const String& text, const GShortcut& shortcut, RetainPtr<GraphicsBitmap>&& icon, Function<void(const GAction&)> on_activation_callback) | ||||
|     : on_activation(move(on_activation_callback)) | ||||
|     , m_text(text) | ||||
|     , m_icon(move(icon)) | ||||
|     , m_shortcut(shortcut) | ||||
| { | ||||
|     GEventLoop::register_action_with_shortcut(Badge<GAction>(), *this); | ||||
| } | ||||
| 
 | ||||
| GAction::~GAction() | ||||
| { | ||||
|     if (m_shortcut.is_valid()) | ||||
|         GEventLoop::unregister_action_with_shortcut(Badge<GAction>(), *this); | ||||
| } | ||||
| 
 | ||||
| void GAction::activate() | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| #include <AK/Retainable.h> | ||||
| #include <AK/Retained.h> | ||||
| #include <SharedGraphics/GraphicsBitmap.h> | ||||
| #include <LibGUI/GShortcut.h> | ||||
| 
 | ||||
| class GAction : public Retainable<GAction> { | ||||
| public: | ||||
|  | @ -20,9 +21,14 @@ public: | |||
|     { | ||||
|         return adopt(*new GAction(text, move(icon), move(callback))); | ||||
|     } | ||||
|     static Retained<GAction> create(const String& text, const GShortcut& shortcut, RetainPtr<GraphicsBitmap>&& icon, Function<void(const GAction&)> callback) | ||||
|     { | ||||
|         return adopt(*new GAction(text, shortcut, move(icon), move(callback))); | ||||
|     } | ||||
|     ~GAction(); | ||||
| 
 | ||||
|     String text() const { return m_text; } | ||||
|     GShortcut shortcut() const { return m_shortcut; } | ||||
|     String custom_data() const { return m_custom_data; } | ||||
|     const GraphicsBitmap* icon() const { return m_icon.ptr(); } | ||||
| 
 | ||||
|  | @ -32,11 +38,13 @@ public: | |||
| 
 | ||||
| private: | ||||
|     GAction(const String& text, Function<void(const GAction&)> = nullptr); | ||||
|     GAction(const String& text, const GShortcut&, RetainPtr<GraphicsBitmap>&& icon, Function<void(const GAction&)> = nullptr); | ||||
|     GAction(const String& text, RetainPtr<GraphicsBitmap>&& icon, Function<void(const GAction&)> = nullptr); | ||||
|     GAction(const String& text, const String& custom_data = String(), Function<void(const GAction&)> = nullptr); | ||||
| 
 | ||||
|     String m_text; | ||||
|     String m_custom_data; | ||||
|     RetainPtr<GraphicsBitmap> m_icon; | ||||
|     GShortcut m_shortcut; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -18,18 +18,17 @@ | |||
| 
 | ||||
| //#define GEVENTLOOP_DEBUG
 | ||||
| 
 | ||||
| static HashMap<GShortcut, GAction*>* g_actions; | ||||
| static GEventLoop* s_mainGEventLoop; | ||||
| 
 | ||||
| void GEventLoop::initialize() | ||||
| { | ||||
|     s_mainGEventLoop = nullptr; | ||||
| } | ||||
| 
 | ||||
| GEventLoop::GEventLoop() | ||||
| { | ||||
|     if (!s_mainGEventLoop) | ||||
|         s_mainGEventLoop = this; | ||||
| 
 | ||||
|     if (!g_actions) | ||||
|         g_actions = new HashMap<GShortcut, GAction*>; | ||||
| 
 | ||||
|     m_event_fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); | ||||
|     if (m_event_fd < 0) { | ||||
|         perror("socket"); | ||||
|  | @ -154,6 +153,14 @@ void GEventLoop::handle_key_event(const WSAPI_ServerMessage& event, GWindow& win | |||
| #ifdef GEVENTLOOP_DEBUG | ||||
|     dbgprintf("WID=%x KeyEvent character=0x%b\n", event.window_id, event.key.character); | ||||
| #endif | ||||
| 
 | ||||
|     unsigned modifiers = (event.key.alt * Mod_Alt) + (event.key.ctrl * Mod_Ctrl) + (event.key.shift * Mod_Shift); | ||||
|     auto it = g_actions->find(GShortcut(modifiers, (KeyCode)event.key.key)); | ||||
|     if (it != g_actions->end()) { | ||||
|         (*it).value->activate(); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     auto key_event = make<GKeyEvent>(event.type == WSAPI_ServerMessage::Type::KeyDown ? GEvent::KeyDown : GEvent::KeyUp, event.key.key); | ||||
|     key_event->m_alt = event.key.alt; | ||||
|     key_event->m_ctrl = event.key.ctrl; | ||||
|  | @ -448,3 +455,13 @@ WSAPI_ServerMessage GEventLoop::sync_request(const WSAPI_ClientMessage& request, | |||
|     ASSERT(success); | ||||
|     return response; | ||||
| } | ||||
| 
 | ||||
| void GEventLoop::register_action_with_shortcut(Badge<GAction>, GAction& action) | ||||
| { | ||||
|     g_actions->set(action.shortcut(), &action); | ||||
| } | ||||
| 
 | ||||
| void GEventLoop::unregister_action_with_shortcut(Badge<GAction>, GAction& action) | ||||
| { | ||||
|     g_actions->remove(action.shortcut()); | ||||
| } | ||||
|  |  | |||
|  | @ -1,13 +1,14 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "GEvent.h" | ||||
| #include <AK/Badge.h> | ||||
| #include <AK/HashMap.h> | ||||
| #include <AK/OwnPtr.h> | ||||
| #include <AK/Vector.h> | ||||
| #include <AK/WeakPtr.h> | ||||
| #include <WindowServer/WSAPITypes.h> | ||||
| #include <LibGUI/GEvent.h> | ||||
| 
 | ||||
| class GAction; | ||||
| class GObject; | ||||
| class GNotifier; | ||||
| class GWindow; | ||||
|  | @ -23,8 +24,6 @@ public: | |||
| 
 | ||||
|     static GEventLoop& main(); | ||||
| 
 | ||||
|     static void initialize(); | ||||
| 
 | ||||
|     bool running() const { return m_running; } | ||||
| 
 | ||||
|     int register_timer(GObject&, int milliseconds, bool should_reload); | ||||
|  | @ -42,6 +41,9 @@ public: | |||
| 
 | ||||
|     pid_t server_pid() const { return m_server_pid; } | ||||
| 
 | ||||
|     static void register_action_with_shortcut(Badge<GAction>, GAction&); | ||||
|     static void unregister_action_with_shortcut(Badge<GAction>, GAction&); | ||||
| 
 | ||||
| private: | ||||
|     void wait_for_event(); | ||||
|     bool drain_messages_from_server(); | ||||
|  |  | |||
|  | @ -68,6 +68,16 @@ int GMenu::realize_menu() | |||
|             ASSERT(action.text().length() < (ssize_t)sizeof(request.text)); | ||||
|             strcpy(request.text, action.text().characters()); | ||||
|             request.text_length = action.text().length(); | ||||
| 
 | ||||
|             if (action.shortcut().is_valid()) { | ||||
|                 auto shortcut_text = action.shortcut().to_string(); | ||||
|                 ASSERT(shortcut_text.length() < (ssize_t)sizeof(request.menu.shortcut_text)); | ||||
|                 strcpy(request.menu.shortcut_text, shortcut_text.characters()); | ||||
|                 request.menu.shortcut_text_length = shortcut_text.length(); | ||||
|             } else { | ||||
|                 request.menu.shortcut_text_length = 0; | ||||
|             } | ||||
| 
 | ||||
|             GEventLoop::main().sync_request(request, WSAPI_ServerMessage::Type::DidAddMenuItem); | ||||
|         } | ||||
|     } | ||||
|  |  | |||
							
								
								
									
										137
									
								
								LibGUI/GShortcut.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								LibGUI/GShortcut.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,137 @@ | |||
| #include <LibGUI/GShortcut.h> | ||||
| #include <AK/StringBuilder.h> | ||||
| 
 | ||||
| static String to_string(KeyCode key) | ||||
| { | ||||
|     switch (key) { | ||||
|     case Key_Escape: return "Escape"; | ||||
|     case Key_Tab: return "Tab"; | ||||
|     case Key_Backspace: return "Backspace"; | ||||
|     case Key_Return: return "Return"; | ||||
|     case Key_Insert: return "Insert"; | ||||
|     case Key_Delete: return "Delete"; | ||||
|     case Key_PrintScreen: return "PrintScreen"; | ||||
|     case Key_SysRq: return "SysRq"; | ||||
|     case Key_Home: return "Home"; | ||||
|     case Key_End: return "End"; | ||||
|     case Key_Left: return "Left"; | ||||
|     case Key_Up: return "Up"; | ||||
|     case Key_Right: return "Right"; | ||||
|     case Key_Down: return "Down"; | ||||
|     case Key_PageUp: return "PageUp"; | ||||
|     case Key_PageDown: return "PageDown"; | ||||
|     case Key_Shift: return "Shift"; | ||||
|     case Key_Control: return "Control"; | ||||
|     case Key_Alt: return "Alt"; | ||||
|     case Key_CapsLock: return "CapsLock"; | ||||
|     case Key_NumLock: return "NumLock"; | ||||
|     case Key_ScrollLock: return "ScrollLock"; | ||||
|     case Key_F1: return "F1"; | ||||
|     case Key_F2: return "F2"; | ||||
|     case Key_F3: return "F3"; | ||||
|     case Key_F4: return "F4"; | ||||
|     case Key_F5: return "F5"; | ||||
|     case Key_F6: return "F6"; | ||||
|     case Key_F7: return "F7"; | ||||
|     case Key_F8: return "F8"; | ||||
|     case Key_F9: return "F9"; | ||||
|     case Key_F10: return "F10"; | ||||
|     case Key_F11: return "F11"; | ||||
|     case Key_F12: return "F12"; | ||||
|     case Key_Space: return "Space"; | ||||
|     case Key_ExclamationPoint: return "!"; | ||||
|     case Key_DoubleQuote: return "\""; | ||||
|     case Key_Hashtag: return "#"; | ||||
|     case Key_Dollar: return "$"; | ||||
|     case Key_Percent: return "%"; | ||||
|     case Key_Ampersand: return "&"; | ||||
|     case Key_Apostrophe: return "'"; | ||||
|     case Key_LeftParen: return "("; | ||||
|     case Key_RightParen: return ")"; | ||||
|     case Key_Asterisk: return "*"; | ||||
|     case Key_Plus: return "+"; | ||||
|     case Key_Comma: return ","; | ||||
|     case Key_Minus: return "-"; | ||||
|     case Key_Period: return ","; | ||||
|     case Key_Slash: return "/"; | ||||
|     case Key_0: return "0"; | ||||
|     case Key_1: return "1"; | ||||
|     case Key_2: return "2"; | ||||
|     case Key_3: return "3"; | ||||
|     case Key_4: return "4"; | ||||
|     case Key_5: return "5"; | ||||
|     case Key_6: return "6"; | ||||
|     case Key_7: return "7"; | ||||
|     case Key_8: return "8"; | ||||
|     case Key_9: return "9"; | ||||
|     case Key_Colon: return ":"; | ||||
|     case Key_Semicolon: return ";"; | ||||
|     case Key_LessThan: return "<"; | ||||
|     case Key_Equal: return "="; | ||||
|     case Key_GreaterThan: return ">"; | ||||
|     case Key_QuestionMark: return "?"; | ||||
|     case Key_AtSign: return "@"; | ||||
|     case Key_A: return "A"; | ||||
|     case Key_B: return "B"; | ||||
|     case Key_C: return "C"; | ||||
|     case Key_D: return "D"; | ||||
|     case Key_E: return "E"; | ||||
|     case Key_F: return "F"; | ||||
|     case Key_G: return "G"; | ||||
|     case Key_H: return "H"; | ||||
|     case Key_I: return "I"; | ||||
|     case Key_J: return "J"; | ||||
|     case Key_K: return "K"; | ||||
|     case Key_L: return "L"; | ||||
|     case Key_M: return "M"; | ||||
|     case Key_N: return "N"; | ||||
|     case Key_O: return "O"; | ||||
|     case Key_P: return "P"; | ||||
|     case Key_Q: return "Q"; | ||||
|     case Key_R: return "R"; | ||||
|     case Key_S: return "S"; | ||||
|     case Key_T: return "T"; | ||||
|     case Key_U: return "U"; | ||||
|     case Key_V: return "V"; | ||||
|     case Key_W: return "W"; | ||||
|     case Key_X: return "X"; | ||||
|     case Key_Y: return "Y"; | ||||
|     case Key_Z: return "Z"; | ||||
|     case Key_LeftBracket: return "["; | ||||
|     case Key_RightBracket: return "]"; | ||||
|     case Key_Backslash: return "\\"; | ||||
|     case Key_Circumflex: return "^"; | ||||
|     case Key_Underscore: return "_"; | ||||
|     case Key_LeftBrace: return "{"; | ||||
|     case Key_RightBrace: return "}"; | ||||
|     case Key_Pipe: return "|"; | ||||
|     case Key_Tilde: return "~"; | ||||
|     case Key_Backtick: return "`"; | ||||
| 
 | ||||
|     case Key_Invalid: return "Invalid"; | ||||
|     default: | ||||
|         ASSERT_NOT_REACHED(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| String GShortcut::to_string() const | ||||
| { | ||||
|     Vector<String> parts; | ||||
| 
 | ||||
|     if (m_modifiers & Mod_Ctrl) | ||||
|         parts.append("Ctrl"); | ||||
|     if (m_modifiers & Mod_Shift) | ||||
|         parts.append("Shift"); | ||||
|     if (m_modifiers & Mod_Alt) | ||||
|         parts.append("Alt"); | ||||
| 
 | ||||
|     parts.append(::to_string(m_key)); | ||||
| 
 | ||||
|     StringBuilder builder; | ||||
|     for (int i = 0; i < parts.size(); ++i) { | ||||
|         builder.append(parts[i]); | ||||
|         if (i != parts.size() - 1) | ||||
|             builder.append('+'); | ||||
|     } | ||||
|     return builder.to_string(); | ||||
| } | ||||
							
								
								
									
										42
									
								
								LibGUI/GShortcut.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								LibGUI/GShortcut.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,42 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <Kernel/KeyCode.h> | ||||
| #include <AK/AKString.h> | ||||
| #include <AK/Traits.h> | ||||
| 
 | ||||
| class GShortcut { | ||||
| public: | ||||
|     GShortcut() { } | ||||
|     GShortcut(unsigned modifiers, KeyCode key) | ||||
|         : m_modifiers(modifiers) | ||||
|         , m_key(key) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     bool is_valid() const { return m_key != KeyCode::Key_Invalid; } | ||||
|     unsigned modifiers() const { return m_modifiers; } | ||||
|     KeyCode key() const { return m_key; } | ||||
|     String to_string() const; | ||||
| 
 | ||||
|     bool operator==(const GShortcut& other) const | ||||
|     { | ||||
|         return m_modifiers == other.m_modifiers | ||||
|             && m_key == other.m_key; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     unsigned m_modifiers { 0 }; | ||||
|     KeyCode m_key { KeyCode::Key_Invalid }; | ||||
| }; | ||||
| 
 | ||||
| namespace AK { | ||||
| 
 | ||||
| template<> | ||||
| struct Traits<GShortcut> { | ||||
|     static unsigned hash(const GShortcut& shortcut) | ||||
|     { | ||||
|         return pair_int_hash(shortcut.modifiers(), shortcut.key()); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | @ -31,6 +31,7 @@ LIBGUI_OBJS = \ | |||
|     GTableView.o \
 | ||||
|     GTableModel.o \
 | ||||
|     GVariant.o \
 | ||||
|     GShortcut.o \
 | ||||
|     GWindow.o | ||||
| 
 | ||||
| OBJS = $(SHAREDGRAPHICS_OBJS) $(LIBGUI_OBJS) | ||||
|  |  | |||
|  | @ -166,6 +166,8 @@ struct WSAPI_ClientMessage { | |||
|             int menubar_id; | ||||
|             int menu_id; | ||||
|             unsigned identifier; | ||||
|             char shortcut_text[32]; | ||||
|             int shortcut_text_length; | ||||
|         } menu; | ||||
|         struct { | ||||
|             WSAPI_Rect rect; | ||||
|  |  | |||
|  | @ -210,14 +210,13 @@ void WSClientConnection::handle_request(WSAPIAddMenuItemRequest& request) | |||
| { | ||||
|     int menu_id = request.menu_id(); | ||||
|     unsigned identifier = request.identifier(); | ||||
|     String text = request.text(); | ||||
|     auto it = m_menus.find(menu_id); | ||||
|     if (it == m_menus.end()) { | ||||
|         post_error("Bad menu ID"); | ||||
|         return; | ||||
|     } | ||||
|     auto& menu = *(*it).value; | ||||
|     menu.add_item(make<WSMenuItem>(identifier, move(text))); | ||||
|     menu.add_item(make<WSMenuItem>(identifier, request.text(), request.shortcut_text())); | ||||
|     WSAPI_ServerMessage response; | ||||
|     response.type = WSAPI_ServerMessage::Type::DidAddMenuItem; | ||||
|     response.menu.menu_id = menu_id; | ||||
|  |  | |||
|  | @ -29,8 +29,13 @@ int WSMenu::width() const | |||
| { | ||||
|     int longest = 0; | ||||
|     for (auto& item : m_items) { | ||||
|         if (item->type() == WSMenuItem::Text) | ||||
|             longest = max(longest, font().width(item->text())); | ||||
|         if (item->type() == WSMenuItem::Text) { | ||||
|             int item_width = font().width(item->text()); | ||||
|             if (!item->shortcut_text().is_empty()) | ||||
|                 item_width += padding_between_text_and_shortcut() + font().width(item->shortcut_text()); | ||||
| 
 | ||||
|             longest = max(longest, item_width); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return max(longest, rect_in_menubar().width()) + horizontal_padding(); | ||||
|  | @ -91,6 +96,9 @@ void WSMenu::draw() | |||
|                 text_color = Color::White; | ||||
|             } | ||||
|             painter.draw_text(item->rect().translated(left_padding(), 0), item->text(), TextAlignment::CenterLeft, text_color); | ||||
|             if (!item->shortcut_text().is_empty()) { | ||||
|                 painter.draw_text(item->rect().translated(-right_padding(), 0), item->shortcut_text(), TextAlignment::CenterRight, text_color); | ||||
|             } | ||||
|         } else if (item->type() == WSMenuItem::Separator) { | ||||
|             Point p1(1, item->rect().center().y()); | ||||
|             Point p2(width() - 2, item->rect().center().y()); | ||||
|  |  | |||
|  | @ -73,6 +73,7 @@ public: | |||
|     void close(); | ||||
| 
 | ||||
| private: | ||||
|     int padding_between_text_and_shortcut() const { return 50; } | ||||
|     void did_activate(WSMenuItem&); | ||||
|     WSClientConnection* m_client { nullptr }; | ||||
|     int m_menu_id { 0 }; | ||||
|  |  | |||
|  | @ -1,9 +1,10 @@ | |||
| #include "WSMenuItem.h" | ||||
| 
 | ||||
| WSMenuItem::WSMenuItem(unsigned identifier, const String& text) | ||||
| WSMenuItem::WSMenuItem(unsigned identifier, const String& text, const String& shortcut_text) | ||||
|     : m_type(Text) | ||||
|     , m_identifier(identifier) | ||||
|     , m_text(text) | ||||
|     , m_shortcut_text(shortcut_text) | ||||
| { | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ public: | |||
|         Separator, | ||||
|     }; | ||||
| 
 | ||||
|     explicit WSMenuItem(unsigned identifier, const String& text); | ||||
|     explicit WSMenuItem(unsigned identifier, const String& text, const String& shortcut_text = { }); | ||||
|     explicit WSMenuItem(Type); | ||||
|     ~WSMenuItem(); | ||||
| 
 | ||||
|  | @ -20,6 +20,7 @@ public: | |||
|     bool enabled() const { return m_enabled; } | ||||
| 
 | ||||
|     String text() const { return m_text; } | ||||
|     String shortcut_text() const { return m_shortcut_text; } | ||||
| 
 | ||||
|     void set_rect(const Rect& rect) { m_rect = rect; } | ||||
|     Rect rect() const { return m_rect; } | ||||
|  | @ -31,6 +32,7 @@ private: | |||
|     bool m_enabled { true }; | ||||
|     unsigned m_identifier { 0 }; | ||||
|     String m_text; | ||||
|     String m_shortcut_text; | ||||
|     Rect m_rect; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -189,22 +189,25 @@ private: | |||
| 
 | ||||
| class WSAPIAddMenuItemRequest : public WSAPIClientRequest { | ||||
| public: | ||||
|     WSAPIAddMenuItemRequest(int client_id, int menu_id, unsigned identifier, const String& text) | ||||
|     WSAPIAddMenuItemRequest(int client_id, int menu_id, unsigned identifier, const String& text, const String& shortcut_text) | ||||
|         : WSAPIClientRequest(WSMessage::APIAddMenuItemRequest, client_id) | ||||
|         , m_menu_id(menu_id) | ||||
|         , m_identifier(identifier) | ||||
|         , m_text(text) | ||||
|         , m_shortcut_text(shortcut_text) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     int menu_id() const { return m_menu_id; } | ||||
|     unsigned identifier() const { return m_identifier; } | ||||
|     String text() const { return m_text; } | ||||
|     String shortcut_text() const { return m_shortcut_text; } | ||||
| 
 | ||||
| private: | ||||
|     int m_menu_id { 0 }; | ||||
|     unsigned m_identifier { 0 }; | ||||
|     String m_text; | ||||
|     String m_shortcut_text; | ||||
| }; | ||||
| 
 | ||||
| class WSAPIAddMenuSeparatorRequest : public WSAPIClientRequest { | ||||
|  |  | |||
|  | @ -294,7 +294,8 @@ void WSMessageLoop::on_receive_from_client(int client_id, const WSAPI_ClientMess | |||
|         break; | ||||
|     case WSAPI_ClientMessage::Type::AddMenuItem: | ||||
|         ASSERT(message.text_length < (ssize_t)sizeof(message.text)); | ||||
|         post_message(client, make<WSAPIAddMenuItemRequest>(client_id, message.menu.menu_id, message.menu.identifier, String(message.text, message.text_length))); | ||||
|         ASSERT(message.menu.shortcut_text_length < (ssize_t)sizeof(message.menu.shortcut_text)); | ||||
|         post_message(client, make<WSAPIAddMenuItemRequest>(client_id, message.menu.menu_id, message.menu.identifier, String(message.text, message.text_length), String(message.menu.shortcut_text, message.menu.shortcut_text_length))); | ||||
|         break; | ||||
|     case WSAPI_ClientMessage::Type::CreateWindow: | ||||
|         ASSERT(message.text_length < (ssize_t)sizeof(message.text)); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling