mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 16:22:43 +00:00 
			
		
		
		
	LibGUI+WindowServer: Make it possible to have checkable GActions.
They show up as checkable GButtons in GToolBar, and with (or without) check marks in menus. There are a bunch of places to make use of this. This patch only takes advantage of it in the FileManager for the view type actions.
This commit is contained in:
		
							parent
							
								
									9ff36afeaa
								
							
						
					
					
						commit
						8f81a3f9dd
					
				
					 15 changed files with 148 additions and 20 deletions
				
			
		|  | @ -98,13 +98,24 @@ int main(int argc, char** argv) | ||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     auto view_as_table_action = GAction::create("Table view", { Mod_Ctrl, KeyCode::Key_L }, GraphicsBitmap::load_from_file("/res/icons/16x16/table-view.png"), [&] (const GAction&) { |     RetainPtr<GAction> view_as_table_action; | ||||||
|         directory_view->set_view_mode(DirectoryView::ViewMode::List); |     RetainPtr<GAction> view_as_icons_action; | ||||||
|     }); |  | ||||||
| 
 | 
 | ||||||
|     auto view_as_icons_action = GAction::create("Icon view", { Mod_Ctrl, KeyCode::Key_I }, GraphicsBitmap::load_from_file("/res/icons/16x16/icon-view.png"), [&] (const GAction&) { |     view_as_table_action = GAction::create("Table view", { Mod_Ctrl, KeyCode::Key_L }, GraphicsBitmap::load_from_file("/res/icons/16x16/table-view.png"), [&] (const GAction&) { | ||||||
|         directory_view->set_view_mode(DirectoryView::ViewMode::Icon); |         directory_view->set_view_mode(DirectoryView::ViewMode::List); | ||||||
|  |         view_as_icons_action->set_checked(false); | ||||||
|  |         view_as_table_action->set_checked(true); | ||||||
|     }); |     }); | ||||||
|  |     view_as_table_action->set_checkable(true); | ||||||
|  |     view_as_table_action->set_checked(false); | ||||||
|  | 
 | ||||||
|  |     view_as_icons_action = GAction::create("Icon view", { Mod_Ctrl, KeyCode::Key_I }, GraphicsBitmap::load_from_file("/res/icons/16x16/icon-view.png"), [&] (const GAction&) { | ||||||
|  |         directory_view->set_view_mode(DirectoryView::ViewMode::Icon); | ||||||
|  |         view_as_table_action->set_checked(false); | ||||||
|  |         view_as_icons_action->set_checked(true); | ||||||
|  |     }); | ||||||
|  |     view_as_icons_action->set_checkable(true); | ||||||
|  |     view_as_icons_action->set_checked(true); | ||||||
| 
 | 
 | ||||||
|     auto copy_action = GAction::create("Copy", GraphicsBitmap::load_from_file("/res/icons/16x16/edit-copy.png"), [] (const GAction&) { |     auto copy_action = GAction::create("Copy", GraphicsBitmap::load_from_file("/res/icons/16x16/edit-copy.png"), [] (const GAction&) { | ||||||
|         dbgprintf("'Copy' action activated!\n"); |         dbgprintf("'Copy' action activated!\n"); | ||||||
|  | @ -138,8 +149,8 @@ int main(int argc, char** argv) | ||||||
|     menubar->add_menu(move(file_menu)); |     menubar->add_menu(move(file_menu)); | ||||||
| 
 | 
 | ||||||
|     auto view_menu = make<GMenu>("View"); |     auto view_menu = make<GMenu>("View"); | ||||||
|     view_menu->add_action(view_as_table_action.copy_ref()); |     view_menu->add_action(*view_as_icons_action); | ||||||
|     view_menu->add_action(view_as_icons_action.copy_ref()); |     view_menu->add_action(*view_as_table_action); | ||||||
|     menubar->add_menu(move(view_menu)); |     menubar->add_menu(move(view_menu)); | ||||||
| 
 | 
 | ||||||
|     auto go_menu = make<GMenu>("Go"); |     auto go_menu = make<GMenu>("Go"); | ||||||
|  | @ -165,8 +176,8 @@ int main(int argc, char** argv) | ||||||
|     main_toolbar->add_action(delete_action.copy_ref()); |     main_toolbar->add_action(delete_action.copy_ref()); | ||||||
| 
 | 
 | ||||||
|     main_toolbar->add_separator(); |     main_toolbar->add_separator(); | ||||||
|     main_toolbar->add_action(view_as_icons_action.copy_ref()); |     main_toolbar->add_action(*view_as_icons_action); | ||||||
|     main_toolbar->add_action(view_as_table_action.copy_ref()); |     main_toolbar->add_action(*view_as_table_action); | ||||||
| 
 | 
 | ||||||
|     directory_view->on_path_change = [window, location_textbox, &file_system_model, tree_view] (const String& new_path) { |     directory_view->on_path_change = [window, location_textbox, &file_system_model, tree_view] (const String& new_path) { | ||||||
|         window->set_title(String::format("FileManager: %s", new_path.characters())); |         window->set_title(String::format("FileManager: %s", new_path.characters())); | ||||||
|  |  | ||||||
|  | @ -106,3 +106,16 @@ void GAction::set_enabled(bool enabled) | ||||||
|         item.set_enabled(enabled); |         item.set_enabled(enabled); | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void GAction::set_checked(bool checked) | ||||||
|  | { | ||||||
|  |     if (m_checked == checked) | ||||||
|  |         return; | ||||||
|  |     m_checked = checked; | ||||||
|  |     for_each_toolbar_button([checked] (GButton& button) { | ||||||
|  |         button.set_checked(checked); | ||||||
|  |     }); | ||||||
|  |     for_each_menu_item([checked] (GMenuItem& item) { | ||||||
|  |         item.set_checked(checked); | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -59,6 +59,12 @@ public: | ||||||
|     bool is_enabled() const { return m_enabled; } |     bool is_enabled() const { return m_enabled; } | ||||||
|     void set_enabled(bool); |     void set_enabled(bool); | ||||||
| 
 | 
 | ||||||
|  |     bool is_checkable() const { return m_checkable; } | ||||||
|  |     void set_checkable(bool checkable) { m_checkable = checkable; } | ||||||
|  | 
 | ||||||
|  |     bool is_checked() const { ASSERT(is_checkable()); return m_checked; } | ||||||
|  |     void set_checked(bool); | ||||||
|  | 
 | ||||||
|     void register_button(Badge<GButton>, GButton&); |     void register_button(Badge<GButton>, GButton&); | ||||||
|     void unregister_button(Badge<GButton>, GButton&); |     void unregister_button(Badge<GButton>, GButton&); | ||||||
|     void register_menu_item(Badge<GMenuItem>, GMenuItem&); |     void register_menu_item(Badge<GMenuItem>, GMenuItem&); | ||||||
|  | @ -79,6 +85,8 @@ private: | ||||||
|     RetainPtr<GraphicsBitmap> m_icon; |     RetainPtr<GraphicsBitmap> m_icon; | ||||||
|     GShortcut m_shortcut; |     GShortcut m_shortcut; | ||||||
|     bool m_enabled { true }; |     bool m_enabled { true }; | ||||||
|  |     bool m_checkable { false }; | ||||||
|  |     bool m_checked { false }; | ||||||
|     ShortcutScope m_scope { ShortcutScope::None }; |     ShortcutScope m_scope { ShortcutScope::None }; | ||||||
| 
 | 
 | ||||||
|     HashTable<GButton*> m_buttons; |     HashTable<GButton*> m_buttons; | ||||||
|  |  | ||||||
|  | @ -133,6 +133,9 @@ void GButton::set_action(GAction& action) | ||||||
|     m_action = action.make_weak_ptr(); |     m_action = action.make_weak_ptr(); | ||||||
|     action.register_button({ }, *this); |     action.register_button({ }, *this); | ||||||
|     set_enabled(action.is_enabled()); |     set_enabled(action.is_enabled()); | ||||||
|  |     set_checkable(action.is_checkable()); | ||||||
|  |     if (action.is_checkable()) | ||||||
|  |         set_checked(action.is_checked()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GButton::set_icon(RetainPtr<GraphicsBitmap>&& icon) | void GButton::set_icon(RetainPtr<GraphicsBitmap>&& icon) | ||||||
|  |  | ||||||
|  | @ -90,6 +90,9 @@ int GMenu::realize_menu() | ||||||
|             request.menu.menu_id = m_menu_id; |             request.menu.menu_id = m_menu_id; | ||||||
|             request.menu.identifier = i; |             request.menu.identifier = i; | ||||||
|             request.menu.enabled = action.is_enabled(); |             request.menu.enabled = action.is_enabled(); | ||||||
|  |             request.menu.checkable = action.is_checkable(); | ||||||
|  |             if (action.is_checkable()) | ||||||
|  |                 request.menu.checked = action.is_checked(); | ||||||
|             ASSERT(action.text().length() < (ssize_t)sizeof(request.text)); |             ASSERT(action.text().length() < (ssize_t)sizeof(request.text)); | ||||||
|             strcpy(request.text, action.text().characters()); |             strcpy(request.text, action.text().characters()); | ||||||
|             request.text_length = action.text().length(); |             request.text_length = action.text().length(); | ||||||
|  |  | ||||||
|  | @ -16,6 +16,9 @@ GMenuItem::GMenuItem(unsigned menu_id, Retained<GAction>&& action) | ||||||
| { | { | ||||||
|     m_action->register_menu_item({ }, *this); |     m_action->register_menu_item({ }, *this); | ||||||
|     m_enabled = m_action->is_enabled(); |     m_enabled = m_action->is_enabled(); | ||||||
|  |     m_checkable = m_action->is_checkable(); | ||||||
|  |     if (m_checkable) | ||||||
|  |         m_checked = m_action->is_checked(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| GMenuItem::~GMenuItem() | GMenuItem::~GMenuItem() | ||||||
|  | @ -32,6 +35,15 @@ void GMenuItem::set_enabled(bool enabled) | ||||||
|     update_window_server(); |     update_window_server(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void GMenuItem::set_checked(bool checked) | ||||||
|  | { | ||||||
|  |     ASSERT(is_checkable()); | ||||||
|  |     if (m_checked == checked) | ||||||
|  |         return; | ||||||
|  |     m_checked = checked; | ||||||
|  |     update_window_server(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void GMenuItem::update_window_server() | void GMenuItem::update_window_server() | ||||||
| { | { | ||||||
|     auto& action = *m_action; |     auto& action = *m_action; | ||||||
|  | @ -40,6 +52,9 @@ void GMenuItem::update_window_server() | ||||||
|     request.menu.menu_id = m_menu_id; |     request.menu.menu_id = m_menu_id; | ||||||
|     request.menu.identifier = m_identifier; |     request.menu.identifier = m_identifier; | ||||||
|     request.menu.enabled = action.is_enabled(); |     request.menu.enabled = action.is_enabled(); | ||||||
|  |     request.menu.checkable = action.is_checkable(); | ||||||
|  |     if (action.is_checkable()) | ||||||
|  |         request.menu.checked = action.is_checked(); | ||||||
|     ASSERT(action.text().length() < (ssize_t)sizeof(request.text)); |     ASSERT(action.text().length() < (ssize_t)sizeof(request.text)); | ||||||
|     strcpy(request.text, action.text().characters()); |     strcpy(request.text, action.text().characters()); | ||||||
|     request.text_length = action.text().length(); |     request.text_length = action.text().length(); | ||||||
|  |  | ||||||
|  | @ -20,6 +20,12 @@ public: | ||||||
|     GAction* action() { return m_action.ptr(); } |     GAction* action() { return m_action.ptr(); } | ||||||
|     unsigned identifier() const { return m_identifier; } |     unsigned identifier() const { return m_identifier; } | ||||||
| 
 | 
 | ||||||
|  |     bool is_checkable() const { return m_checkable; } | ||||||
|  |     void set_checkable(bool checkable) { m_checkable = checkable; } | ||||||
|  | 
 | ||||||
|  |     bool is_checked() const { return m_checked; } | ||||||
|  |     void set_checked(bool); | ||||||
|  | 
 | ||||||
|     bool is_enabled() const { return m_enabled; } |     bool is_enabled() const { return m_enabled; } | ||||||
|     void set_enabled(bool); |     void set_enabled(bool); | ||||||
| 
 | 
 | ||||||
|  | @ -33,6 +39,8 @@ private: | ||||||
|     unsigned m_menu_id { 0 }; |     unsigned m_menu_id { 0 }; | ||||||
|     unsigned m_identifier { 0 }; |     unsigned m_identifier { 0 }; | ||||||
|     bool m_enabled { true }; |     bool m_enabled { true }; | ||||||
|  |     bool m_checkable { false }; | ||||||
|  |     bool m_checked { false }; | ||||||
|     RetainPtr<GAction> m_action; |     RetainPtr<GAction> m_action; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -246,6 +246,8 @@ struct WSAPI_ClientMessage { | ||||||
|             char shortcut_text[32]; |             char shortcut_text[32]; | ||||||
|             int shortcut_text_length; |             int shortcut_text_length; | ||||||
|             bool enabled; |             bool enabled; | ||||||
|  |             bool checkable; | ||||||
|  |             bool checked; | ||||||
|             WSAPI_Point position; |             WSAPI_Point position; | ||||||
|             bool top_anchored; |             bool top_anchored; | ||||||
|         } menu; |         } menu; | ||||||
|  |  | ||||||
|  | @ -241,7 +241,7 @@ void WSClientConnection::handle_request(const WSAPIAddMenuItemRequest& request) | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     auto& menu = *(*it).value; |     auto& menu = *(*it).value; | ||||||
|     menu.add_item(make<WSMenuItem>(menu, identifier, request.text(), request.shortcut_text(), request.is_enabled())); |     menu.add_item(make<WSMenuItem>(menu, identifier, request.text(), request.shortcut_text(), request.is_enabled(), request.is_checkable(), request.is_checked())); | ||||||
|     WSAPI_ServerMessage response; |     WSAPI_ServerMessage response; | ||||||
|     response.type = WSAPI_ServerMessage::Type::DidAddMenuItem; |     response.type = WSAPI_ServerMessage::Type::DidAddMenuItem; | ||||||
|     response.menu.menu_id = menu_id; |     response.menu.menu_id = menu_id; | ||||||
|  | @ -292,6 +292,9 @@ void WSClientConnection::handle_request(const WSAPIUpdateMenuItemRequest& reques | ||||||
|     menu_item->set_text(request.text()); |     menu_item->set_text(request.text()); | ||||||
|     menu_item->set_shortcut_text(request.shortcut_text()); |     menu_item->set_shortcut_text(request.shortcut_text()); | ||||||
|     menu_item->set_enabled(request.is_enabled()); |     menu_item->set_enabled(request.is_enabled()); | ||||||
|  |     menu_item->set_checkable(request.is_checkable()); | ||||||
|  |     if (request.is_checkable()) | ||||||
|  |         menu_item->set_checked(request.is_checked()); | ||||||
|     WSAPI_ServerMessage response; |     WSAPI_ServerMessage response; | ||||||
|     response.type = WSAPI_ServerMessage::Type::DidUpdateMenuItem; |     response.type = WSAPI_ServerMessage::Type::DidUpdateMenuItem; | ||||||
|     response.menu.menu_id = menu_id; |     response.menu.menu_id = menu_id; | ||||||
|  |  | ||||||
|  | @ -276,13 +276,15 @@ private: | ||||||
| 
 | 
 | ||||||
| class WSAPIAddMenuItemRequest : public WSAPIClientRequest { | class WSAPIAddMenuItemRequest : public WSAPIClientRequest { | ||||||
| public: | public: | ||||||
|     WSAPIAddMenuItemRequest(int client_id, int menu_id, unsigned identifier, const String& text, const String& shortcut_text, bool enabled) |     WSAPIAddMenuItemRequest(int client_id, int menu_id, unsigned identifier, const String& text, const String& shortcut_text, bool enabled, bool checkable, bool checked) | ||||||
|         : WSAPIClientRequest(WSEvent::APIAddMenuItemRequest, client_id) |         : WSAPIClientRequest(WSEvent::APIAddMenuItemRequest, client_id) | ||||||
|         , m_menu_id(menu_id) |         , m_menu_id(menu_id) | ||||||
|         , m_identifier(identifier) |         , m_identifier(identifier) | ||||||
|         , m_text(text) |         , m_text(text) | ||||||
|         , m_shortcut_text(shortcut_text) |         , m_shortcut_text(shortcut_text) | ||||||
|         , m_enabled(enabled) |         , m_enabled(enabled) | ||||||
|  |         , m_checkable(checkable) | ||||||
|  |         , m_checked(checked) | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -291,24 +293,30 @@ public: | ||||||
|     String text() const { return m_text; } |     String text() const { return m_text; } | ||||||
|     String shortcut_text() const { return m_shortcut_text; } |     String shortcut_text() const { return m_shortcut_text; } | ||||||
|     bool is_enabled() const { return m_enabled; } |     bool is_enabled() const { return m_enabled; } | ||||||
|  |     bool is_checkable() const { return m_checkable; } | ||||||
|  |     bool is_checked() const { return m_checked; } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     int m_menu_id { 0 }; |     int m_menu_id { 0 }; | ||||||
|     unsigned m_identifier { 0 }; |     unsigned m_identifier { 0 }; | ||||||
|     String m_text; |     String m_text; | ||||||
|     String m_shortcut_text; |     String m_shortcut_text; | ||||||
|     bool m_enabled { true }; |     bool m_enabled; | ||||||
|  |     bool m_checkable; | ||||||
|  |     bool m_checked; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class WSAPIUpdateMenuItemRequest : public WSAPIClientRequest { | class WSAPIUpdateMenuItemRequest : public WSAPIClientRequest { | ||||||
| public: | public: | ||||||
|     WSAPIUpdateMenuItemRequest(int client_id, int menu_id, unsigned identifier, const String& text, const String& shortcut_text, bool enabled) |     WSAPIUpdateMenuItemRequest(int client_id, int menu_id, unsigned identifier, const String& text, const String& shortcut_text, bool enabled, bool checkable, bool checked) | ||||||
|         : WSAPIClientRequest(WSEvent::APIUpdateMenuItemRequest, client_id) |         : WSAPIClientRequest(WSEvent::APIUpdateMenuItemRequest, client_id) | ||||||
|         , m_menu_id(menu_id) |         , m_menu_id(menu_id) | ||||||
|         , m_identifier(identifier) |         , m_identifier(identifier) | ||||||
|         , m_text(text) |         , m_text(text) | ||||||
|         , m_shortcut_text(shortcut_text) |         , m_shortcut_text(shortcut_text) | ||||||
|         , m_enabled(enabled) |         , m_enabled(enabled) | ||||||
|  |         , m_checkable(checkable) | ||||||
|  |         , m_checked(checked) | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -317,6 +325,8 @@ public: | ||||||
|     String text() const { return m_text; } |     String text() const { return m_text; } | ||||||
|     String shortcut_text() const { return m_shortcut_text; } |     String shortcut_text() const { return m_shortcut_text; } | ||||||
|     bool is_enabled() const { return m_enabled; } |     bool is_enabled() const { return m_enabled; } | ||||||
|  |     bool is_checkable() const { return m_checkable; } | ||||||
|  |     bool is_checked() const { return m_checked; } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     int m_menu_id { 0 }; |     int m_menu_id { 0 }; | ||||||
|  | @ -324,6 +334,8 @@ private: | ||||||
|     String m_text; |     String m_text; | ||||||
|     String m_shortcut_text; |     String m_shortcut_text; | ||||||
|     bool m_enabled { true }; |     bool m_enabled { true }; | ||||||
|  |     bool m_checkable; | ||||||
|  |     bool m_checked; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class WSAPIAddMenuSeparatorRequest : public WSAPIClientRequest { | class WSAPIAddMenuSeparatorRequest : public WSAPIClientRequest { | ||||||
|  |  | ||||||
|  | @ -170,12 +170,12 @@ bool WSEventLoop::on_receive_from_client(int client_id, const WSAPI_ClientMessag | ||||||
|     case WSAPI_ClientMessage::Type::AddMenuItem: |     case WSAPI_ClientMessage::Type::AddMenuItem: | ||||||
|         ASSERT(message.text_length < (ssize_t)sizeof(message.text)); |         ASSERT(message.text_length < (ssize_t)sizeof(message.text)); | ||||||
|         ASSERT(message.menu.shortcut_text_length < (ssize_t)sizeof(message.menu.shortcut_text)); |         ASSERT(message.menu.shortcut_text_length < (ssize_t)sizeof(message.menu.shortcut_text)); | ||||||
|         post_event(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), message.menu.enabled)); |         post_event(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), message.menu.enabled, message.menu.checkable, message.menu.checked)); | ||||||
|         break; |         break; | ||||||
|     case WSAPI_ClientMessage::Type::UpdateMenuItem: |     case WSAPI_ClientMessage::Type::UpdateMenuItem: | ||||||
|         ASSERT(message.text_length < (ssize_t)sizeof(message.text)); |         ASSERT(message.text_length < (ssize_t)sizeof(message.text)); | ||||||
|         ASSERT(message.menu.shortcut_text_length < (ssize_t)sizeof(message.menu.shortcut_text)); |         ASSERT(message.menu.shortcut_text_length < (ssize_t)sizeof(message.menu.shortcut_text)); | ||||||
|         post_event(client, make<WSAPIUpdateMenuItemRequest>(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), message.menu.enabled)); |         post_event(client, make<WSAPIUpdateMenuItemRequest>(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), message.menu.enabled, message.menu.checkable, message.menu.checked)); | ||||||
|         break; |         break; | ||||||
|     case WSAPI_ClientMessage::Type::AddMenuSeparator: |     case WSAPI_ClientMessage::Type::AddMenuSeparator: | ||||||
|         post_event(client, make<WSAPIAddMenuSeparatorRequest>(client_id, message.menu.menu_id)); |         post_event(client, make<WSAPIAddMenuSeparatorRequest>(client_id, message.menu.menu_id)); | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ | ||||||
| #include "WSWindowManager.h" | #include "WSWindowManager.h" | ||||||
| #include <WindowServer/WSAPITypes.h> | #include <WindowServer/WSAPITypes.h> | ||||||
| #include <WindowServer/WSClientConnection.h> | #include <WindowServer/WSClientConnection.h> | ||||||
|  | #include <SharedGraphics/CharacterBitmap.h> | ||||||
| #include <SharedGraphics/Painter.h> | #include <SharedGraphics/Painter.h> | ||||||
| #include <SharedGraphics/StylePainter.h> | #include <SharedGraphics/StylePainter.h> | ||||||
| #include <SharedGraphics/Font.h> | #include <SharedGraphics/Font.h> | ||||||
|  | @ -26,6 +27,23 @@ const Font& WSMenu::font() const | ||||||
|     return Font::default_font(); |     return Font::default_font(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static const char* s_checked_bitmap_data = { | ||||||
|  |     "         " | ||||||
|  |     "      ## " | ||||||
|  |     "     ##  " | ||||||
|  |     "     ##  " | ||||||
|  |     "    ##   " | ||||||
|  |     " ## ##   " | ||||||
|  |     "  ####   " | ||||||
|  |     "   ##    " | ||||||
|  |     "         " | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static CharacterBitmap* s_checked_bitmap; | ||||||
|  | static const int s_checked_bitmap_width = 9; | ||||||
|  | static const int s_checked_bitmap_height = 9; | ||||||
|  | static const int s_checked_bitmap_padding = 6; | ||||||
|  | 
 | ||||||
| int WSMenu::width() const | int WSMenu::width() const | ||||||
| { | { | ||||||
|     int longest = 0; |     int longest = 0; | ||||||
|  | @ -34,6 +52,8 @@ int WSMenu::width() const | ||||||
|             int item_width = font().width(item->text()); |             int item_width = font().width(item->text()); | ||||||
|             if (!item->shortcut_text().is_empty()) |             if (!item->shortcut_text().is_empty()) | ||||||
|                 item_width += padding_between_text_and_shortcut() + font().width(item->shortcut_text()); |                 item_width += padding_between_text_and_shortcut() + font().width(item->shortcut_text()); | ||||||
|  |             if (item->is_checkable()) | ||||||
|  |                 item_width += s_checked_bitmap_width + s_checked_bitmap_padding; | ||||||
| 
 | 
 | ||||||
|             longest = max(longest, item_width); |             longest = max(longest, item_width); | ||||||
|         } |         } | ||||||
|  | @ -92,6 +112,9 @@ void WSMenu::draw() | ||||||
|     StylePainter::paint_menu_frame(painter, rect); |     StylePainter::paint_menu_frame(painter, rect); | ||||||
|     int width = this->width(); |     int width = this->width(); | ||||||
| 
 | 
 | ||||||
|  |     if (!s_checked_bitmap) | ||||||
|  |         s_checked_bitmap = &CharacterBitmap::create_from_ascii(s_checked_bitmap_data, s_checked_bitmap_width, s_checked_bitmap_height).leak_ref(); | ||||||
|  | 
 | ||||||
|     for (auto& item : m_items) { |     for (auto& item : m_items) { | ||||||
|         if (item->type() == WSMenuItem::Text) { |         if (item->type() == WSMenuItem::Text) { | ||||||
|             Color text_color = Color::Black; |             Color text_color = Color::Black; | ||||||
|  | @ -101,7 +124,16 @@ void WSMenu::draw() | ||||||
|             } |             } | ||||||
|             if (!item->is_enabled()) |             if (!item->is_enabled()) | ||||||
|                 text_color = Color::MidGray; |                 text_color = Color::MidGray; | ||||||
|             painter.draw_text(item->rect().translated(left_padding(), 0), item->text(), TextAlignment::CenterLeft, text_color); |             Rect text_rect = item->rect().translated(left_padding(), 0); | ||||||
|  |             if (item->is_checkable()) { | ||||||
|  |                 if (item->is_checked()) { | ||||||
|  |                     Rect checkmark_rect { text_rect.location().x(), 0, s_checked_bitmap_width, s_checked_bitmap_height }; | ||||||
|  |                     checkmark_rect.center_vertically_within(text_rect); | ||||||
|  |                     painter.draw_bitmap(checkmark_rect.location(), *s_checked_bitmap, Color::Black); | ||||||
|  |                 } | ||||||
|  |                 text_rect.move_by(s_checked_bitmap_width + s_checked_bitmap_padding, 0); | ||||||
|  |             } | ||||||
|  |             painter.draw_text(text_rect, item->text(), TextAlignment::CenterLeft, text_color); | ||||||
|             if (!item->shortcut_text().is_empty()) { |             if (!item->shortcut_text().is_empty()) { | ||||||
|                 painter.draw_text(item->rect().translated(-right_padding(), 0), item->shortcut_text(), TextAlignment::CenterRight, text_color); |                 painter.draw_text(item->rect().translated(-right_padding(), 0), item->shortcut_text(), TextAlignment::CenterRight, text_color); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  | @ -1,10 +1,12 @@ | ||||||
| #include "WSMenuItem.h" | #include "WSMenuItem.h" | ||||||
| #include "WSMenu.h" | #include "WSMenu.h" | ||||||
| 
 | 
 | ||||||
| WSMenuItem::WSMenuItem(WSMenu& menu, unsigned identifier, const String& text, const String& shortcut_text, bool enabled) | WSMenuItem::WSMenuItem(WSMenu& menu, unsigned identifier, const String& text, const String& shortcut_text, bool enabled, bool checkable, bool checked) | ||||||
|     : m_menu(menu) |     : m_menu(menu) | ||||||
|     , m_type(Text) |     , m_type(Text) | ||||||
|     , m_enabled(enabled) |     , m_enabled(enabled) | ||||||
|  |     , m_checkable(checkable) | ||||||
|  |     , m_checked(checked) | ||||||
|     , m_identifier(identifier) |     , m_identifier(identifier) | ||||||
|     , m_text(text) |     , m_text(text) | ||||||
|     , m_shortcut_text(shortcut_text) |     , m_shortcut_text(shortcut_text) | ||||||
|  | @ -28,3 +30,11 @@ void WSMenuItem::set_enabled(bool enabled) | ||||||
|     m_enabled = enabled; |     m_enabled = enabled; | ||||||
|     m_menu.redraw(); |     m_menu.redraw(); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void WSMenuItem::set_checked(bool checked) | ||||||
|  | { | ||||||
|  |     if (m_checked == checked) | ||||||
|  |         return; | ||||||
|  |     m_checked = checked; | ||||||
|  |     m_menu.redraw(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -14,7 +14,7 @@ public: | ||||||
|         Separator, |         Separator, | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     WSMenuItem(WSMenu&, unsigned identifier, const String& text, const String& shortcut_text = { }, bool enabled = true); |     WSMenuItem(WSMenu&, unsigned identifier, const String& text, const String& shortcut_text = { }, bool enabled = true, bool checkable = false, bool checked = false); | ||||||
|     WSMenuItem(WSMenu&, Type); |     WSMenuItem(WSMenu&, Type); | ||||||
|     ~WSMenuItem(); |     ~WSMenuItem(); | ||||||
| 
 | 
 | ||||||
|  | @ -23,6 +23,12 @@ public: | ||||||
|     bool is_enabled() const { return m_enabled; } |     bool is_enabled() const { return m_enabled; } | ||||||
|     void set_enabled(bool); |     void set_enabled(bool); | ||||||
| 
 | 
 | ||||||
|  |     bool is_checkable() const { return m_checkable; } | ||||||
|  |     void set_checkable(bool checkable) { m_checkable = checkable; } | ||||||
|  | 
 | ||||||
|  |     bool is_checked() const { return m_checked; } | ||||||
|  |     void set_checked(bool); | ||||||
|  | 
 | ||||||
|     String text() const { return m_text; } |     String text() const { return m_text; } | ||||||
|     void set_text(const String& text) { m_text = text; } |     void set_text(const String& text) { m_text = text; } | ||||||
| 
 | 
 | ||||||
|  | @ -38,6 +44,8 @@ private: | ||||||
|     WSMenu& m_menu; |     WSMenu& m_menu; | ||||||
|     Type m_type { None }; |     Type m_type { None }; | ||||||
|     bool m_enabled { true }; |     bool m_enabled { true }; | ||||||
|  |     bool m_checkable { false }; | ||||||
|  |     bool m_checked { false }; | ||||||
|     unsigned m_identifier { 0 }; |     unsigned m_identifier { 0 }; | ||||||
|     String m_text; |     String m_text; | ||||||
|     String m_shortcut_text; |     String m_shortcut_text; | ||||||
|  |  | ||||||
|  | @ -58,7 +58,7 @@ void StylePainter::paint_button(Painter& painter, const Rect& rect, ButtonStyle | ||||||
|     if (button_style == ButtonStyle::Normal) |     if (button_style == ButtonStyle::Normal) | ||||||
|         return paint_button_new(painter, rect, pressed, checked, hovered); |         return paint_button_new(painter, rect, pressed, checked, hovered); | ||||||
| 
 | 
 | ||||||
|     Color button_color = Color::LightGray; |     Color button_color = checked ? Color::from_rgb(0xd6d2ce) : Color::LightGray; | ||||||
|     Color highlight_color = Color::White; |     Color highlight_color = Color::White; | ||||||
|     Color shadow_color = Color(96, 96, 96); |     Color shadow_color = Color(96, 96, 96); | ||||||
| 
 | 
 | ||||||
|  | @ -71,7 +71,7 @@ void StylePainter::paint_button(Painter& painter, const Rect& rect, ButtonStyle | ||||||
|     PainterStateSaver saver(painter); |     PainterStateSaver saver(painter); | ||||||
|     painter.translate(rect.location()); |     painter.translate(rect.location()); | ||||||
| 
 | 
 | ||||||
|     if (pressed) { |     if (pressed || checked) { | ||||||
|         // Base
 |         // Base
 | ||||||
|         painter.fill_rect({ 1, 1, rect.width() - 2, rect.height() - 2 }, button_color); |         painter.fill_rect({ 1, 1, rect.width() - 2, rect.height() - 2 }, button_color); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling