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
Notes:
sideshowbarker
2024-07-19 12:30:28 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/1e604b79842
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
Reference in a new issue