Browse Source

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 :^)
Andreas Kling 5 năm trước cách đây
mục cha
commit
1e604b7

+ 1 - 0
Libraries/LibGUI/GAction.h

@@ -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;
 

+ 16 - 0
Libraries/LibGUI/GMenu.cpp

@@ -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));

+ 1 - 0
Servers/WindowServer/WSAPITypes.h

@@ -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;

+ 14 - 2
Servers/WindowServer/WSClientConnection.cpp

@@ -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;

+ 4 - 1
Servers/WindowServer/WSEvent.h

@@ -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 {

+ 16 - 7
Servers/WindowServer/WSMenu.cpp

@@ -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);

+ 1 - 1
Servers/WindowServer/WSMenu.h

@@ -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 - 0
Servers/WindowServer/WSMenuBar.cpp

@@ -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 - 0
Servers/WindowServer/WSMenuItem.cpp

@@ -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)

+ 5 - 0
Servers/WindowServer/WSMenuItem.h

@@ -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 - 0
Servers/WindowServer/WSWindowSwitcher.cpp

@@ -1,4 +1,5 @@
 #include <LibDraw/Font.h>
+#include <LibDraw/GraphicsBitmap.h>
 #include <LibDraw/StylePainter.h>
 #include <WindowServer/WSEvent.h>
 #include <WindowServer/WSScreen.h>