Skip to content

Commit 49e58d5

Browse files
feat(ui, menu): support svg icon
1 parent bfd042e commit 49e58d5

20 files changed

+179
-99
lines changed

scripts/bindgen/quickjs-types.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
declare module "mshell" {
22
export function println(...args: any[]);
3+
type size_t = number;
34
}
45

56
declare module "qjs:os" {
Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11

2-
#include "shell.h"
2+
#include "contextmenu.h"
3+
#include "../utils.h"
34
#include "menu_widget.h"
45
#include "ui.h"
5-
#include "../utils.h"
66
#include <algorithm>
77
#include <codecvt>
88

9+
910
#include "menu_render.h"
1011

1112
#include <consoleapi.h>
@@ -49,8 +50,9 @@ std::wstring strip_extra_infos(std::wstring_view str) {
4950

5051
menu menu::construct_with_hmenu(HMENU hMenu, HWND hWnd, bool is_top) {
5152
menu m;
52-
SendMessageW(hWnd, WM_INITMENUPOPUP, reinterpret_cast<WPARAM>(hMenu), 0xFFFFFFFF);
53-
53+
SendMessageW(hWnd, WM_INITMENUPOPUP, reinterpret_cast<WPARAM>(hMenu),
54+
0xFFFFFFFF);
55+
5456
for (int i = 0; i < GetMenuItemCount(hMenu); i++) {
5557
menu_item item;
5658
wchar_t buffer[256];
@@ -72,7 +74,8 @@ menu menu::construct_with_hmenu(HMENU hMenu, HWND hWnd, bool is_top) {
7274

7375
if (info.hSubMenu) {
7476
item.submenu = [=](std::shared_ptr<menu_widget> mw) {
75-
mw->init_from_data(menu::construct_with_hmenu(info.hSubMenu, hWnd, false));
77+
mw->init_from_data(
78+
menu::construct_with_hmenu(info.hSubMenu, hWnd, false));
7679
};
7780
} else {
7881
item.action = [=]() mutable {
@@ -89,12 +92,12 @@ menu menu::construct_with_hmenu(HMENU hMenu, HWND hWnd, bool is_top) {
8992
}
9093

9194
if (info.fType & MFT_BITMAP) {
92-
item.icon_bitmap = info.hbmpItem;
95+
item.icon_bitmap = (size_t)info.hbmpItem;
9396
} else if (info.hbmpChecked || info.hbmpUnchecked) {
9497
if (info.fState & MFS_CHECKED)
95-
item.icon_bitmap = info.hbmpChecked;
98+
item.icon_bitmap = (size_t)info.hbmpChecked;
9699
else
97-
item.icon_bitmap = info.hbmpUnchecked;
100+
item.icon_bitmap = (size_t)info.hbmpUnchecked;
98101
}
99102

100103
if (info.dwItemData) {
@@ -127,7 +130,7 @@ menu menu::construct_with_hmenu(HMENU hMenu, HWND hWnd, bool is_top) {
127130
bitmap.bmBitsPixel == 16 || bitmap.bmBitsPixel == 8) &&
128131
bitmap.bmWidth >= 4 && bitmap.bmWidth <= 64 &&
129132
bitmap.bmHeight >= 4 && bitmap.bmHeight <= 64) {
130-
item.icon_bitmap = result;
133+
item.icon_bitmap = (size_t)result;
131134
break;
132135
}
133136
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ struct menu_item {
3232
std::optional<std::function<void()>> action;
3333
std::optional<std::function<void(std::shared_ptr<menu_widget>)>> submenu;
3434
bool checked = false;
35-
std::optional<HBITMAP> icon_bitmap;
35+
std::optional<size_t> icon_bitmap;
36+
std::optional<std::string> icon_svg;
37+
bool icon_updated = false;
3638
};
3739
} // namespace mb_shell

src/shell/contextmenu/menu_render.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#pragma once
22
#include "../utils.h"
3-
#include "shell.h"
3+
#include "contextmenu.h"
44
#include "ui.h"
55
#include <optional>
66

src/shell/contextmenu/menu_widget.cc

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
#include "menu_widget.h"
22
#include "../utils.h"
33
#include "animator.h"
4+
#include "contextmenu.h"
45
#include "hbitmap_utils.h"
56
#include "menu_render.h"
67
#include "nanovg.h"
7-
#include "shell.h"
88
#include "ui.h"
99
#include <iostream>
1010
#include <print>
@@ -32,16 +32,16 @@ void mb_shell::menu_item_widget::render(ui::nanovg_context ctx) {
3232
ctx.fillRoundedRect(*x + margin, *y, *width - margin * 2, *height,
3333
roundcorner);
3434

35-
if (item.icon_bitmap.has_value()) {
35+
if (item.icon_bitmap.has_value() || item.icon_svg.has_value()) {
36+
if (!icon_img || item.icon_updated)
37+
reload_icon_img(ctx);
38+
item.icon_updated = false;
39+
3640
auto paintY = floor(*y + (*height - icon_width) / 2);
37-
if (!icon_img_bmp) {
38-
icon_img_bmp = ui::LoadBitmapImage(ctx, item.icon_bitmap.value());
39-
}
40-
4141
auto paint =
4242
nvgImagePattern(ctx.ctx, *x + icon_padding + margin + ctx.offset_x,
4343
paintY + ctx.offset_y, icon_width, icon_width, 0,
44-
icon_img_bmp->id, *opacity / 255.f);
44+
icon_img->id, *opacity / 255.f);
4545

4646
ctx.beginPath();
4747
ctx.rect(*x + icon_padding + margin, paintY, icon_width, icon_width);
@@ -579,7 +579,8 @@ void mb_shell::menu_widget::init_from_data(menu menu_data) {
579579
void mb_shell::menu_widget::update_icon_width() {
580580
bool has_icon = std::ranges::any_of(children, [](auto &item) {
581581
auto i = item->template downcast<menu_item_widget>()->item;
582-
return i.icon_bitmap.has_value() || i.type == menu_item::type::toggle;
582+
return i.icon_bitmap.has_value() || i.icon_svg.has_value() ||
583+
i.type == menu_item::type::toggle;
583584
});
584585

585586
for (auto &item : children) {
@@ -590,4 +591,14 @@ void mb_shell::menu_widget::update_icon_width() {
590591
mi->icon_width = 16;
591592
}
592593
}
593-
};
594+
};
595+
void mb_shell::menu_item_widget::reload_icon_img(ui::nanovg_context ctx) {
596+
if (item.icon_bitmap)
597+
icon_img = ui::LoadBitmapImage(ctx, (HBITMAP)item.icon_bitmap.value());
598+
else if (item.icon_svg) {
599+
std::string copy = item.icon_svg.value();
600+
icon_img = ctx.imageFromSVG(nsvgParse(copy.data(), "px", 96));
601+
} else {
602+
icon_img = std::nullopt;
603+
}
604+
}

src/shell/contextmenu/menu_widget.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#include "animator.h"
33
#include "extra_widgets.h"
44
#include "nanovg_wrapper.h"
5-
#include "shell.h"
5+
#include "contextmenu.h"
66
#include "ui.h"
77
#include "widget.h"
88
#include <algorithm>
@@ -24,7 +24,7 @@ struct menu_item_widget : public ui::widget {
2424
menu_item_widget(menu_item item, menu_widget *parent_menu);
2525
void reset_appear_animation(float delay);
2626

27-
std::optional<ui::NVGImage> icon_img_bmp{};
27+
std::optional<ui::NVGImage> icon_img{};
2828

2929
std::shared_ptr<menu_widget> submenu_wid = nullptr;
3030
float show_submenu_timer = 0.f;
@@ -34,6 +34,8 @@ struct menu_item_widget : public ui::widget {
3434
void update(ui::update_context &ctx) override;
3535
float measure_width(ui::update_context &ctx) override;
3636
bool check_hit(const ui::update_context &ctx) override;
37+
38+
void reload_icon_img(ui::nanovg_context ctx);
3739
};
3840

3941
enum class popup_direction {

src/shell/entry.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
#include "./contextmenu/menu_render.h"
1313
#include "./contextmenu/menu_widget.h"
14-
#include "./contextmenu/shell.h"
14+
#include "./contextmenu/contextmenu.h"
1515

1616
#include <chrono>
1717
#include <codecvt>

src/shell/script/binding_qjs.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,9 @@ template <> struct qjs::js_traits<mb_shell::js::js_menu_data> {
314314

315315
obj.action = js_traits<std::optional<std::function<void (mb_shell::js::js_menu_action_event_data)>>>::unwrap(ctx, JS_GetPropertyStr(ctx, v, "action"));
316316

317-
obj.icon_path = js_traits<std::optional<std::string>>::unwrap(ctx, JS_GetPropertyStr(ctx, v, "icon_path"));
317+
obj.icon_svg = js_traits<std::optional<std::string>>::unwrap(ctx, JS_GetPropertyStr(ctx, v, "icon_svg"));
318+
319+
obj.icon_bitmap = js_traits<std::optional<size_t>>::unwrap(ctx, JS_GetPropertyStr(ctx, v, "icon_bitmap"));
318320

319321
return obj;
320322
}
@@ -330,7 +332,9 @@ template <> struct qjs::js_traits<mb_shell::js::js_menu_data> {
330332

331333
JS_SetPropertyStr(ctx, obj, "action", js_traits<std::optional<std::function<void (mb_shell::js::js_menu_action_event_data)>>>::wrap(ctx, val.action));
332334

333-
JS_SetPropertyStr(ctx, obj, "icon_path", js_traits<std::optional<std::string>>::wrap(ctx, val.icon_path));
335+
JS_SetPropertyStr(ctx, obj, "icon_svg", js_traits<std::optional<std::string>>::wrap(ctx, val.icon_svg));
336+
337+
JS_SetPropertyStr(ctx, obj, "icon_bitmap", js_traits<std::optional<size_t>>::wrap(ctx, val.icon_bitmap));
334338

335339
return obj;
336340
}
@@ -343,7 +347,8 @@ template<> struct js_bind<mb_shell::js::js_menu_data> {
343347
.fun<&mb_shell::js::js_menu_data::name>("name")
344348
.fun<&mb_shell::js::js_menu_data::submenu>("submenu")
345349
.fun<&mb_shell::js::js_menu_data::action>("action")
346-
.fun<&mb_shell::js::js_menu_data::icon_path>("icon_path")
350+
.fun<&mb_shell::js::js_menu_data::icon_svg>("icon_svg")
351+
.fun<&mb_shell::js::js_menu_data::icon_bitmap>("icon_bitmap")
347352
;
348353
}
349354

src/shell/script/binding_types.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,16 @@ static void to_menu_item(menu_item &data, const js_menu_data &js_data) {
116116
}
117117
};
118118
}
119+
120+
if (js_data.icon_bitmap) {
121+
data.icon_bitmap = js_data.icon_bitmap.value();
122+
data.icon_updated = true;
123+
}
124+
125+
if (js_data.icon_svg) {
126+
data.icon_svg = js_data.icon_svg.value();
127+
data.icon_updated = true;
128+
}
119129
}
120130

121131
void menu_item_controller::set_data(js_menu_data data) {
@@ -179,6 +189,20 @@ js_menu_data menu_item_controller::data() {
179189
};
180190
}
181191

192+
if (item->item.submenu) {
193+
data.submenu = [item](std::shared_ptr<menu_controller> ctl) {
194+
item->item.submenu.value()(ctl->$menu.lock());
195+
};
196+
}
197+
198+
if (item->item.icon_bitmap) {
199+
data.icon_bitmap = item->item.icon_bitmap.value();
200+
}
201+
202+
if (item->item.icon_svg) {
203+
data.icon_svg = item->item.icon_svg.value();
204+
}
205+
182206
return data;
183207
}
184208
std::shared_ptr<menu_item_controller> menu_controller::get_item(int index) {

src/shell/script/binding_types.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ export class js_menu_data {
9595
name?: string | undefined
9696
submenu?: ((arg1: menu_controller) => void) | undefined
9797
action?: ((arg1: js_menu_action_event_data) => void) | undefined
98-
icon_path?: string | undefined
98+
icon_svg?: string | undefined
99+
icon_bitmap?: size_t | undefined
99100

100101
}
101102

@@ -170,6 +171,7 @@ export class subproc {
170171
}
171172
declare module "mshell" {
172173
export function println(...args: any[]);
174+
type size_t = number;
173175
}
174176

175177
declare module "qjs:os" {

0 commit comments

Comments
 (0)