mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 09:02:43 +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; } | ||||
|     GShortcut shortcut() const { return m_shortcut; } | ||||
|     const GraphicsBitmap* icon() const { return m_icon.ptr(); } | ||||
|     void set_icon(const GraphicsBitmap* icon) { m_icon = icon; } | ||||
| 
 | ||||
|     Function<void(GAction&)> on_activation; | ||||
| 
 | ||||
|  |  | |||
|  | @ -98,6 +98,22 @@ int GMenu::realize_menu() | |||
|             request.menu.identifier = i; | ||||
|             request.menu.enabled = action.is_enabled(); | ||||
|             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()) | ||||
|                 request.menu.checked = action.is_checked(); | ||||
|             ASSERT(action.text().length() < (ssize_t)sizeof(request.text)); | ||||
|  |  | |||
|  | @ -263,6 +263,7 @@ struct WSAPI_ClientMessage { | |||
|         struct { | ||||
|             int menubar_id; | ||||
|             int menu_id; | ||||
|             int icon_buffer_id; | ||||
|             unsigned identifier; | ||||
|             char shortcut_text[32]; | ||||
|             int shortcut_text_length; | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| #include <LibC/SharedBuffer.h> | ||||
| #include <LibDraw/GraphicsBitmap.h> | ||||
| #include <SharedBuffer.h> | ||||
| #include <WindowServer/WSAPITypes.h> | ||||
| #include <WindowServer/WSClientConnection.h> | ||||
|  | @ -158,7 +159,7 @@ bool WSClientConnection::handle_message(const WSAPI_ClientMessage& message, cons | |||
|             did_misbehave(); | ||||
|             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; | ||||
|     case WSAPI_ClientMessage::Type::UpdateMenuItem: | ||||
|         if (message.text_length > (int)sizeof(message.text)) { | ||||
|  | @ -422,7 +423,18 @@ void WSClientConnection::handle_request(const WSAPIAddMenuItemRequest& request) | |||
|         return; | ||||
|     } | ||||
|     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; | ||||
|     response.type = WSAPI_ServerMessage::Type::DidAddMenuItem; | ||||
|     response.menu.menu_id = menu_id; | ||||
|  |  | |||
|  | @ -303,7 +303,7 @@ private: | |||
| 
 | ||||
| class WSAPIAddMenuItemRequest : public WSAPIClientRequest { | ||||
| 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) | ||||
|         , m_menu_id(menu_id) | ||||
|         , m_identifier(identifier) | ||||
|  | @ -312,6 +312,7 @@ public: | |||
|         , m_enabled(enabled) | ||||
|         , m_checkable(checkable) | ||||
|         , m_checked(checked) | ||||
|         , m_icon_buffer_id(icon_buffer_id) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|  | @ -322,6 +323,7 @@ public: | |||
|     bool is_enabled() const { return m_enabled; } | ||||
|     bool is_checkable() const { return m_checkable; } | ||||
|     bool is_checked() const { return m_checked; } | ||||
|     int icon_buffer_id() const { return m_icon_buffer_id; } | ||||
| 
 | ||||
| private: | ||||
|     int m_menu_id { 0 }; | ||||
|  | @ -331,6 +333,7 @@ private: | |||
|     bool m_enabled; | ||||
|     bool m_checkable; | ||||
|     bool m_checked; | ||||
|     int m_icon_buffer_id { 0 }; | ||||
| }; | ||||
| 
 | ||||
| class WSAPIUpdateMenuItemRequest : public WSAPIClientRequest { | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
| #include "WSWindowManager.h" | ||||
| #include <LibDraw/CharacterBitmap.h> | ||||
| #include <LibDraw/Font.h> | ||||
| #include <LibDraw/GraphicsBitmap.h> | ||||
| #include <LibDraw/Painter.h> | ||||
| #include <LibDraw/StylePainter.h> | ||||
| #include <WindowServer/WSAPITypes.h> | ||||
|  | @ -44,7 +45,8 @@ 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; | ||||
| static const int s_item_icon_width = 16; | ||||
| static const int s_checkbox_or_icon_padding = 6; | ||||
| 
 | ||||
| int WSMenu::width() const | ||||
| { | ||||
|  | @ -58,8 +60,8 @@ int WSMenu::width() const | |||
|             int shortcut_width = font().width(item.shortcut_text()); | ||||
|             widest_shortcut = max(shortcut_width, widest_shortcut); | ||||
|         } | ||||
|         if (item.is_checkable()) | ||||
|             text_width += s_checked_bitmap_width + s_checked_bitmap_padding; | ||||
|         if (item.is_checkable() || item.icon()) | ||||
|             text_width += s_item_icon_width + s_checkbox_or_icon_padding; | ||||
| 
 | ||||
|         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(); | ||||
| 
 | ||||
|     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_items_with_icon = has_items_with_icon | !!item.icon(); | ||||
|     } | ||||
| 
 | ||||
|     for (auto& item : m_items) { | ||||
|         if (item.type() == WSMenuItem::Text) { | ||||
|  | @ -139,7 +144,7 @@ void WSMenu::draw() | |||
|                 text_color = Color::MidGray; | ||||
|             Rect text_rect = item.rect().translated(left_padding(), 0); | ||||
|             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); | ||||
|                 Rect checkbox_rect = checkmark_rect.inflated(4, 4); | ||||
|                 painter.fill_rect(checkbox_rect, Color::White); | ||||
|  | @ -147,9 +152,13 @@ void WSMenu::draw() | |||
|                 if (item.is_checked()) { | ||||
|                     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) | ||||
|                 text_rect.move_by(s_checked_bitmap_width + s_checked_bitmap_padding, 0); | ||||
|             if (has_checkable_items || has_items_with_icon) | ||||
|                 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); | ||||
|             if (!item.shortcut_text().is_empty()) { | ||||
|                 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 height() const; | ||||
| 
 | ||||
|     int item_height() const { return 16; } | ||||
|     int item_height() const { return 20; } | ||||
|     int frame_thickness() const { return 3; } | ||||
|     int horizontal_padding() const { return left_padding() + right_padding(); } | ||||
|     int left_padding() const { return 14; } | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| #include "WSMenuBar.h" | ||||
| #include "WSMenu.h" | ||||
| #include "WSMenuItem.h" | ||||
| #include <LibDraw/GraphicsBitmap.h> | ||||
| 
 | ||||
| WSMenuBar::WSMenuBar(WSClientConnection& client, int menubar_id) | ||||
|     : m_client(client) | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| #include "WSMenuItem.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) | ||||
|     : m_menu(menu) | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| #include <AK/Function.h> | ||||
| #include <LibDraw/Rect.h> | ||||
| 
 | ||||
| class GraphicsBitmap; | ||||
| class WSMenu; | ||||
| 
 | ||||
| class WSMenuItem { | ||||
|  | @ -40,6 +41,9 @@ public: | |||
| 
 | ||||
|     unsigned identifier() const { return m_identifier; } | ||||
| 
 | ||||
|     const GraphicsBitmap* icon() const { return m_icon; } | ||||
|     void set_icon(const GraphicsBitmap* icon) { m_icon = icon; } | ||||
| 
 | ||||
| private: | ||||
|     WSMenu& m_menu; | ||||
|     Type m_type { None }; | ||||
|  | @ -50,4 +54,5 @@ private: | |||
|     String m_text; | ||||
|     String m_shortcut_text; | ||||
|     Rect m_rect; | ||||
|     RefPtr<GraphicsBitmap> m_icon; | ||||
| }; | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| #include <LibDraw/Font.h> | ||||
| #include <LibDraw/GraphicsBitmap.h> | ||||
| #include <LibDraw/StylePainter.h> | ||||
| #include <WindowServer/WSEvent.h> | ||||
| #include <WindowServer/WSScreen.h> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling