mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 15:42:44 +00:00 
			
		
		
		
	WindowServer+LibGUI: Show action icons in the menus when possible
Any GAction that has an icon assigned will now show up with that icon when added to a menu as well. I made the menu items 2px taller to accomodate the icons. I think this turned out quite nice as well :^)
This commit is contained in:
		
							parent
							
								
									d522a6fe4c
								
							
						
					
					
						commit
						1e604b7984
					
				
					 11 changed files with 61 additions and 11 deletions
				
			
		|  | @ -48,6 +48,7 @@ public: | ||||||
|     String text() const { return m_text; } |     String text() const { return m_text; } | ||||||
|     GShortcut shortcut() const { return m_shortcut; } |     GShortcut shortcut() const { return m_shortcut; } | ||||||
|     const GraphicsBitmap* icon() const { return m_icon.ptr(); } |     const GraphicsBitmap* icon() const { return m_icon.ptr(); } | ||||||
|  |     void set_icon(const GraphicsBitmap* icon) { m_icon = icon; } | ||||||
| 
 | 
 | ||||||
|     Function<void(GAction&)> on_activation; |     Function<void(GAction&)> on_activation; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -98,6 +98,22 @@ int GMenu::realize_menu() | ||||||
|             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(); |             request.menu.checkable = action.is_checkable(); | ||||||
|  |             if (action.icon()) { | ||||||
|  |                 ASSERT(action.icon()->format() == GraphicsBitmap::Format::RGBA32); | ||||||
|  |                 ASSERT(action.icon()->size() == Size(16, 16)); | ||||||
|  |                 if (action.icon()->shared_buffer_id() == -1) { | ||||||
|  |                     auto shared_buffer = SharedBuffer::create_with_size(action.icon()->size_in_bytes()); | ||||||
|  |                     ASSERT(shared_buffer); | ||||||
|  |                     auto shared_icon = GraphicsBitmap::create_with_shared_buffer(GraphicsBitmap::Format::RGBA32, *shared_buffer, action.icon()->size()); | ||||||
|  |                     memcpy(shared_buffer->data(), action.icon()->bits(0), action.icon()->size_in_bytes()); | ||||||
|  |                     shared_buffer->seal(); | ||||||
|  |                     shared_buffer->share_with(GWindowServerConnection::the().server_pid()); | ||||||
|  |                     action.set_icon(shared_icon); | ||||||
|  |                 } | ||||||
|  |                 request.menu.icon_buffer_id = action.icon()->shared_buffer_id(); | ||||||
|  |             } else { | ||||||
|  |                 request.menu.icon_buffer_id = -1; | ||||||
|  |             } | ||||||
|             if (action.is_checkable()) |             if (action.is_checkable()) | ||||||
|                 request.menu.checked = action.is_checked(); |                 request.menu.checked = action.is_checked(); | ||||||
|             ASSERT(action.text().length() < (ssize_t)sizeof(request.text)); |             ASSERT(action.text().length() < (ssize_t)sizeof(request.text)); | ||||||
|  |  | ||||||
|  | @ -263,6 +263,7 @@ struct WSAPI_ClientMessage { | ||||||
|         struct { |         struct { | ||||||
|             int menubar_id; |             int menubar_id; | ||||||
|             int menu_id; |             int menu_id; | ||||||
|  |             int icon_buffer_id; | ||||||
|             unsigned identifier; |             unsigned identifier; | ||||||
|             char shortcut_text[32]; |             char shortcut_text[32]; | ||||||
|             int shortcut_text_length; |             int shortcut_text_length; | ||||||
|  |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| #include <LibC/SharedBuffer.h> | #include <LibC/SharedBuffer.h> | ||||||
|  | #include <LibDraw/GraphicsBitmap.h> | ||||||
| #include <SharedBuffer.h> | #include <SharedBuffer.h> | ||||||
| #include <WindowServer/WSAPITypes.h> | #include <WindowServer/WSAPITypes.h> | ||||||
| #include <WindowServer/WSClientConnection.h> | #include <WindowServer/WSClientConnection.h> | ||||||
|  | @ -158,7 +159,7 @@ bool WSClientConnection::handle_message(const WSAPI_ClientMessage& message, cons | ||||||
|             did_misbehave(); |             did_misbehave(); | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|         CEventLoop::current().post_event(*this, 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)); |         CEventLoop::current().post_event(*this, 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, message.menu.icon_buffer_id)); | ||||||
|         break; |         break; | ||||||
|     case WSAPI_ClientMessage::Type::UpdateMenuItem: |     case WSAPI_ClientMessage::Type::UpdateMenuItem: | ||||||
|         if (message.text_length > (int)sizeof(message.text)) { |         if (message.text_length > (int)sizeof(message.text)) { | ||||||
|  | @ -422,7 +423,18 @@ 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(), request.is_checkable(), request.is_checked())); |     auto menu_item = make<WSMenuItem>(menu, identifier, request.text(), request.shortcut_text(), request.is_enabled(), request.is_checkable(), request.is_checked()); | ||||||
|  |     if (request.icon_buffer_id() != -1) { | ||||||
|  |         auto icon_buffer = SharedBuffer::create_from_shared_buffer_id(request.icon_buffer_id()); | ||||||
|  |         if (!icon_buffer) { | ||||||
|  |             did_misbehave(); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         // FIXME: Verify that the icon buffer can accomodate a 16x16 bitmap view.
 | ||||||
|  |         auto shared_icon = GraphicsBitmap::create_with_shared_buffer(GraphicsBitmap::Format::RGBA32, icon_buffer.release_nonnull(), { 16, 16 }); | ||||||
|  |         menu_item->set_icon(shared_icon); | ||||||
|  |     } | ||||||
|  |     menu.add_item(move(menu_item)); | ||||||
|     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; | ||||||
|  |  | ||||||
|  | @ -303,7 +303,7 @@ 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, bool checkable, bool checked) |     WSAPIAddMenuItemRequest(int client_id, int menu_id, unsigned identifier, const String& text, const String& shortcut_text, bool enabled, bool checkable, bool checked, int icon_buffer_id) | ||||||
|         : 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) | ||||||
|  | @ -312,6 +312,7 @@ public: | ||||||
|         , m_enabled(enabled) |         , m_enabled(enabled) | ||||||
|         , m_checkable(checkable) |         , m_checkable(checkable) | ||||||
|         , m_checked(checked) |         , m_checked(checked) | ||||||
|  |         , m_icon_buffer_id(icon_buffer_id) | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -322,6 +323,7 @@ public: | ||||||
|     bool is_enabled() const { return m_enabled; } |     bool is_enabled() const { return m_enabled; } | ||||||
|     bool is_checkable() const { return m_checkable; } |     bool is_checkable() const { return m_checkable; } | ||||||
|     bool is_checked() const { return m_checked; } |     bool is_checked() const { return m_checked; } | ||||||
|  |     int icon_buffer_id() const { return m_icon_buffer_id; } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     int m_menu_id { 0 }; |     int m_menu_id { 0 }; | ||||||
|  | @ -331,6 +333,7 @@ private: | ||||||
|     bool m_enabled; |     bool m_enabled; | ||||||
|     bool m_checkable; |     bool m_checkable; | ||||||
|     bool m_checked; |     bool m_checked; | ||||||
|  |     int m_icon_buffer_id { 0 }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class WSAPIUpdateMenuItemRequest : public WSAPIClientRequest { | class WSAPIUpdateMenuItemRequest : public WSAPIClientRequest { | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ | ||||||
| #include "WSWindowManager.h" | #include "WSWindowManager.h" | ||||||
| #include <LibDraw/CharacterBitmap.h> | #include <LibDraw/CharacterBitmap.h> | ||||||
| #include <LibDraw/Font.h> | #include <LibDraw/Font.h> | ||||||
|  | #include <LibDraw/GraphicsBitmap.h> | ||||||
| #include <LibDraw/Painter.h> | #include <LibDraw/Painter.h> | ||||||
| #include <LibDraw/StylePainter.h> | #include <LibDraw/StylePainter.h> | ||||||
| #include <WindowServer/WSAPITypes.h> | #include <WindowServer/WSAPITypes.h> | ||||||
|  | @ -44,7 +45,8 @@ static const char* s_checked_bitmap_data = { | ||||||
| static CharacterBitmap* s_checked_bitmap; | static CharacterBitmap* s_checked_bitmap; | ||||||
| static const int s_checked_bitmap_width = 9; | static const int s_checked_bitmap_width = 9; | ||||||
| static const int s_checked_bitmap_height = 9; | static const int s_checked_bitmap_height = 9; | ||||||
| static const int s_checked_bitmap_padding = 6; | static const int s_item_icon_width = 16; | ||||||
|  | static const int s_checkbox_or_icon_padding = 6; | ||||||
| 
 | 
 | ||||||
| int WSMenu::width() const | int WSMenu::width() const | ||||||
| { | { | ||||||
|  | @ -58,8 +60,8 @@ int WSMenu::width() const | ||||||
|             int shortcut_width = font().width(item.shortcut_text()); |             int shortcut_width = font().width(item.shortcut_text()); | ||||||
|             widest_shortcut = max(shortcut_width, widest_shortcut); |             widest_shortcut = max(shortcut_width, widest_shortcut); | ||||||
|         } |         } | ||||||
|         if (item.is_checkable()) |         if (item.is_checkable() || item.icon()) | ||||||
|             text_width += s_checked_bitmap_width + s_checked_bitmap_padding; |             text_width += s_item_icon_width + s_checkbox_or_icon_padding; | ||||||
| 
 | 
 | ||||||
|         widest_text = max(widest_text, text_width); |         widest_text = max(widest_text, text_width); | ||||||
|     } |     } | ||||||
|  | @ -125,8 +127,11 @@ void WSMenu::draw() | ||||||
|         s_checked_bitmap = &CharacterBitmap::create_from_ascii(s_checked_bitmap_data, s_checked_bitmap_width, s_checked_bitmap_height).leak_ref(); |         s_checked_bitmap = &CharacterBitmap::create_from_ascii(s_checked_bitmap_data, s_checked_bitmap_width, s_checked_bitmap_height).leak_ref(); | ||||||
| 
 | 
 | ||||||
|     bool has_checkable_items = false; |     bool has_checkable_items = false; | ||||||
|     for (auto& item : m_items) |     bool has_items_with_icon = false; | ||||||
|  |     for (auto& item : m_items) { | ||||||
|         has_checkable_items = has_checkable_items | item.is_checkable(); |         has_checkable_items = has_checkable_items | item.is_checkable(); | ||||||
|  |         has_items_with_icon = has_items_with_icon | !!item.icon(); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     for (auto& item : m_items) { |     for (auto& item : m_items) { | ||||||
|         if (item.type() == WSMenuItem::Text) { |         if (item.type() == WSMenuItem::Text) { | ||||||
|  | @ -139,7 +144,7 @@ void WSMenu::draw() | ||||||
|                 text_color = Color::MidGray; |                 text_color = Color::MidGray; | ||||||
|             Rect text_rect = item.rect().translated(left_padding(), 0); |             Rect text_rect = item.rect().translated(left_padding(), 0); | ||||||
|             if (item.is_checkable()) { |             if (item.is_checkable()) { | ||||||
|                 Rect checkmark_rect { text_rect.location().x(), 0, s_checked_bitmap_width, s_checked_bitmap_height }; |                 Rect checkmark_rect { text_rect.location().x() + 2, 0, s_checked_bitmap_width, s_checked_bitmap_height }; | ||||||
|                 checkmark_rect.center_vertically_within(text_rect); |                 checkmark_rect.center_vertically_within(text_rect); | ||||||
|                 Rect checkbox_rect = checkmark_rect.inflated(4, 4); |                 Rect checkbox_rect = checkmark_rect.inflated(4, 4); | ||||||
|                 painter.fill_rect(checkbox_rect, Color::White); |                 painter.fill_rect(checkbox_rect, Color::White); | ||||||
|  | @ -147,9 +152,13 @@ void WSMenu::draw() | ||||||
|                 if (item.is_checked()) { |                 if (item.is_checked()) { | ||||||
|                     painter.draw_bitmap(checkmark_rect.location(), *s_checked_bitmap, Color::Black); |                     painter.draw_bitmap(checkmark_rect.location(), *s_checked_bitmap, Color::Black); | ||||||
|                 } |                 } | ||||||
|  |             } else if (item.icon()) { | ||||||
|  |                 Rect icon_rect { text_rect.location().x() - 2, 0, s_item_icon_width, s_item_icon_width }; | ||||||
|  |                 icon_rect.center_vertically_within(text_rect); | ||||||
|  |                 painter.blit(icon_rect.location(), *item.icon(), item.icon()->rect()); | ||||||
|             } |             } | ||||||
|             if (has_checkable_items) |             if (has_checkable_items || has_items_with_icon) | ||||||
|                 text_rect.move_by(s_checked_bitmap_width + s_checked_bitmap_padding, 0); |                 text_rect.move_by(s_item_icon_width + s_checkbox_or_icon_padding, 0); | ||||||
|             painter.draw_text(text_rect, item.text(), TextAlignment::CenterLeft, text_color); |             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); | ||||||
|  |  | ||||||
|  | @ -53,7 +53,7 @@ public: | ||||||
|     int width() const; |     int width() const; | ||||||
|     int height() const; |     int height() const; | ||||||
| 
 | 
 | ||||||
|     int item_height() const { return 16; } |     int item_height() const { return 20; } | ||||||
|     int frame_thickness() const { return 3; } |     int frame_thickness() const { return 3; } | ||||||
|     int horizontal_padding() const { return left_padding() + right_padding(); } |     int horizontal_padding() const { return left_padding() + right_padding(); } | ||||||
|     int left_padding() const { return 14; } |     int left_padding() const { return 14; } | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| #include "WSMenuBar.h" | #include "WSMenuBar.h" | ||||||
| #include "WSMenu.h" | #include "WSMenu.h" | ||||||
| #include "WSMenuItem.h" | #include "WSMenuItem.h" | ||||||
|  | #include <LibDraw/GraphicsBitmap.h> | ||||||
| 
 | 
 | ||||||
| WSMenuBar::WSMenuBar(WSClientConnection& client, int menubar_id) | WSMenuBar::WSMenuBar(WSClientConnection& client, int menubar_id) | ||||||
|     : m_client(client) |     : m_client(client) | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| #include "WSMenuItem.h" | #include "WSMenuItem.h" | ||||||
| #include "WSMenu.h" | #include "WSMenu.h" | ||||||
|  | #include <LibDraw/GraphicsBitmap.h> | ||||||
| 
 | 
 | ||||||
| WSMenuItem::WSMenuItem(WSMenu& menu, unsigned identifier, const String& text, const String& shortcut_text, bool enabled, bool checkable, bool checked) | 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) | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
| #include <AK/Function.h> | #include <AK/Function.h> | ||||||
| #include <LibDraw/Rect.h> | #include <LibDraw/Rect.h> | ||||||
| 
 | 
 | ||||||
|  | class GraphicsBitmap; | ||||||
| class WSMenu; | class WSMenu; | ||||||
| 
 | 
 | ||||||
| class WSMenuItem { | class WSMenuItem { | ||||||
|  | @ -40,6 +41,9 @@ public: | ||||||
| 
 | 
 | ||||||
|     unsigned identifier() const { return m_identifier; } |     unsigned identifier() const { return m_identifier; } | ||||||
| 
 | 
 | ||||||
|  |     const GraphicsBitmap* icon() const { return m_icon; } | ||||||
|  |     void set_icon(const GraphicsBitmap* icon) { m_icon = icon; } | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     WSMenu& m_menu; |     WSMenu& m_menu; | ||||||
|     Type m_type { None }; |     Type m_type { None }; | ||||||
|  | @ -50,4 +54,5 @@ private: | ||||||
|     String m_text; |     String m_text; | ||||||
|     String m_shortcut_text; |     String m_shortcut_text; | ||||||
|     Rect m_rect; |     Rect m_rect; | ||||||
|  |     RefPtr<GraphicsBitmap> m_icon; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| #include <LibDraw/Font.h> | #include <LibDraw/Font.h> | ||||||
|  | #include <LibDraw/GraphicsBitmap.h> | ||||||
| #include <LibDraw/StylePainter.h> | #include <LibDraw/StylePainter.h> | ||||||
| #include <WindowServer/WSEvent.h> | #include <WindowServer/WSEvent.h> | ||||||
| #include <WindowServer/WSScreen.h> | #include <WindowServer/WSScreen.h> | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling