plusui-native-core 0.1.105 → 0.1.107

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/Core/.cache/clangd/index/api.hpp.016B34C8046EF490.idx +0 -0
  2. package/Core/.cache/clangd/index/app.cpp.1E6FAF043D496421.idx +0 -0
  3. package/Core/.cache/clangd/index/app.hpp.FA0E5D412C4E6148.idx +0 -0
  4. package/Core/.cache/clangd/index/browser.cpp.9461A2CAF129F1D9.idx +0 -0
  5. package/Core/.cache/clangd/index/browser.hpp.BE40AE80881B3107.idx +0 -0
  6. package/Core/.cache/clangd/index/clipboard.cpp.2399913537B2A7AD.idx +0 -0
  7. package/Core/.cache/clangd/index/clipboard.hpp.C1095DDACD7149E9.idx +0 -0
  8. package/Core/.cache/clangd/index/connect.cpp.518C66C7C28B30A9.idx +0 -0
  9. package/Core/.cache/clangd/index/connect.hpp.08E2F7CD13B78601.idx +0 -0
  10. package/Core/.cache/clangd/index/connection.hpp.849FAEF1523BF2C3.idx +0 -0
  11. package/Core/.cache/clangd/index/display.cpp.F6F6D932BF9F8D8E.idx +0 -0
  12. package/Core/.cache/clangd/index/display.hpp.0C1A9CAD11EE4404.idx +0 -0
  13. package/Core/.cache/clangd/index/filedrop.cpp.669B524B3C501C52.idx +0 -0
  14. package/Core/.cache/clangd/index/filedrop.hpp.48460099C3F35F2D.idx +0 -0
  15. package/Core/.cache/clangd/index/keyboard.cpp.DC6D34E4A4F798DD.idx +0 -0
  16. package/Core/.cache/clangd/index/keyboard.hpp.F016CB68D7DE5A46.idx +0 -0
  17. package/Core/.cache/clangd/index/keyboard_linux.cpp.B403FDCEA7A6CA53.idx +0 -0
  18. package/Core/.cache/clangd/index/menu.cpp.3059F08D8D2DF265.idx +0 -0
  19. package/Core/.cache/clangd/index/menu.hpp.8716DCCC573910D4.idx +0 -0
  20. package/Core/.cache/clangd/index/plusui.hpp.8CFCDFDC2E3F41DD.idx +0 -0
  21. package/Core/.cache/clangd/index/router.cpp.EAC9EAD34C59E573.idx +0 -0
  22. package/Core/.cache/clangd/index/router.hpp.2B06E2EE9998468D.idx +0 -0
  23. package/Core/.cache/clangd/index/stb_image.h.E26CF48FE089A0D3.idx +0 -0
  24. package/Core/.cache/clangd/index/tray.cpp.92F244E7E1D7F0EC.idx +0 -0
  25. package/Core/.cache/clangd/index/tray.hpp.0D881B0601BBBD25.idx +0 -0
  26. package/Core/.cache/clangd/index/webgpu.cpp.FC656FA8BE10FE15.idx +0 -0
  27. package/Core/.cache/clangd/index/webgpu.hpp.5AF1A5E9DF9E5AE0.idx +0 -0
  28. package/Core/.cache/clangd/index/window.cpp.191D8C9ADF874B22.idx +0 -0
  29. package/Core/.cache/clangd/index/window.hpp.B9811B43AA295697.idx +0 -0
  30. package/Core/.claude/settings.local.json +7 -0
  31. package/Core/CMakeLists.txt +1 -1
  32. package/Core/Features/API/app-api.ts +28 -28
  33. package/Core/Features/API/browser-api.ts +38 -38
  34. package/Core/Features/API/clipboard-api.ts +21 -21
  35. package/Core/Features/API/display-api.ts +33 -33
  36. package/Core/Features/API/keyboard-api.ts +33 -33
  37. package/Core/Features/API/menu-api.ts +39 -39
  38. package/Core/Features/API/router-api.ts +23 -23
  39. package/Core/Features/API/tray-api.ts +22 -22
  40. package/Core/Features/API/webgpu-api.ts +55 -55
  41. package/Core/Features/App/app.cpp +135 -102
  42. package/Core/Features/Browser/browser.cpp +227 -227
  43. package/Core/Features/Browser/browser.ts +161 -161
  44. package/Core/Features/Clipboard/clipboard.cpp +235 -235
  45. package/Core/Features/Display/display.cpp +212 -212
  46. package/Core/Features/FileDrop/filedrop.cpp +448 -324
  47. package/Core/Features/FileDrop/filedrop.css +421 -421
  48. package/Core/Features/FileDrop/filedrop.ts +0 -7
  49. package/Core/Features/Keyboard/keyboard_linux.cpp +4 -0
  50. package/Core/Features/Router/router.cpp +62 -62
  51. package/Core/Features/Router/router.ts +113 -113
  52. package/Core/Features/Tray/tray.cpp +437 -324
  53. package/Core/Features/WebGPU/webgpu.cpp +948 -948
  54. package/Core/Features/Window/webview.cpp +1009 -1009
  55. package/Core/Features/Window/webview.ts +516 -516
  56. package/Core/Features/Window/window.cpp +2240 -1986
  57. package/Core/include/plusui/api.hpp +237 -237
  58. package/Core/include/plusui/app.hpp +36 -34
  59. package/Core/include/plusui/browser.hpp +67 -67
  60. package/Core/include/plusui/clipboard.hpp +41 -41
  61. package/Core/include/plusui/connect.hpp +340 -340
  62. package/Core/include/plusui/connection.hpp +3 -3
  63. package/Core/include/plusui/display.hpp +90 -90
  64. package/Core/include/plusui/filedrop.hpp +92 -77
  65. package/Core/include/plusui/keyboard.hpp +112 -112
  66. package/Core/include/plusui/menu.hpp +153 -153
  67. package/Core/include/plusui/plusui +18 -18
  68. package/Core/include/plusui/router.hpp +42 -42
  69. package/Core/include/plusui/tray.hpp +94 -94
  70. package/Core/include/plusui/webgpu.hpp +434 -434
  71. package/Core/include/plusui/window.hpp +180 -177
  72. package/Core/scripts/generate-umbrella-header.mjs +77 -77
  73. package/Core/vendor/WebView2EnvironmentOptions.h +406 -406
  74. package/Core/vendor/webview.h +487 -487
  75. package/Core/vendor/webview2.h +52079 -52079
  76. package/README.md +19 -19
  77. package/package.json +1 -1
@@ -1,324 +1,437 @@
1
- #ifdef _WIN32
2
- #ifndef _WIN32_WINNT
3
- #define _WIN32_WINNT 0x0601
4
- #endif
5
- #include <windows.h>
6
-
7
- #ifndef EXTERN_C
8
- #ifdef __cplusplus
9
- #define EXTERN_C extern "C"
10
- #else
11
- #define EXTERN_C extern
12
- #endif
13
- #endif
14
-
15
- #include <shellapi.h>
16
- #include <shlobj.h>
17
- #pragma comment(lib, "shell32.lib")
18
- #endif
19
-
20
- #include <algorithm>
21
- #include <iostream>
22
- #include <plusui/tray.hpp>
23
- #include <string>
24
- #include <vector>
25
-
26
- #define STB_IMAGE_IMPLEMENTATION
27
- #include <stb_image.h>
28
-
29
- #ifdef __APPLE__
30
- #include <Cocoa/Cocoa.h>
31
- #elif !defined(_WIN32)
32
- #include <gdk/gdk.h>
33
- #include <gtk/gtk.h>
34
- #endif
35
-
36
- namespace plusui {
37
-
38
- struct TrayIcon::Impl {
39
- int id = 0;
40
- std::string tooltip;
41
- std::string iconPath;
42
- bool visible = true;
43
-
44
- std::function<void(int, int)> clickCallback;
45
- std::function<void(int, int)> rightClickCallback;
46
- std::function<void()> doubleClickCallback;
47
- std::function<void(const std::string &)> menuItemCallback;
48
- std::vector<TrayMenuItem> menuItems;
49
-
50
- #ifdef _WIN32
51
- NOTIFYICONDATAW nid = {};
52
- HWND hwnd = nullptr;
53
- #endif
54
- };
55
-
56
- TrayIcon::TrayIcon() : pImpl(std::make_unique<Impl>()) {}
57
- TrayIcon::~TrayIcon() { dispose(); }
58
-
59
- TrayIcon::TrayIcon(TrayIcon &&other) noexcept = default;
60
- TrayIcon &TrayIcon::operator=(TrayIcon &&other) noexcept = default;
61
-
62
- TrayIcon TrayIcon::create(const std::string &tooltip,
63
- const std::string &iconPath) {
64
- TrayIcon icon;
65
- icon.pImpl->tooltip = tooltip;
66
- icon.pImpl->iconPath = iconPath;
67
-
68
- #ifdef _WIN32
69
- icon.pImpl->nid.uID = 1;
70
- icon.pImpl->nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
71
- icon.pImpl->nid.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
72
- icon.pImpl->nid.uCallbackMessage = WM_APP + 1; // Example message
73
-
74
- if (!tooltip.empty()) {
75
- std::wstring wtooltip(tooltip.begin(), tooltip.end());
76
- wcsncpy_s(icon.pImpl->nid.szTip, _countof(icon.pImpl->nid.szTip),
77
- wtooltip.c_str(), _TRUNCATE);
78
- }
79
-
80
- // Note: nim_add should be called after hwnd is set
81
- #endif
82
-
83
- return icon;
84
- }
85
-
86
- void TrayIcon::show() {
87
- pImpl->visible = true;
88
- #ifdef _WIN32
89
- if (pImpl->hwnd) {
90
- pImpl->nid.hWnd = pImpl->hwnd;
91
- Shell_NotifyIconW(NIM_ADD, &pImpl->nid);
92
- }
93
- #endif
94
- }
95
-
96
- void TrayIcon::hide() {
97
- pImpl->visible = false;
98
- #ifdef _WIN32
99
- Shell_NotifyIconW(NIM_DELETE, &pImpl->nid);
100
- #endif
101
- }
102
-
103
- void TrayIcon::setTooltip(const std::string &tooltip) {
104
- pImpl->tooltip = tooltip;
105
- #ifdef _WIN32
106
- if (!tooltip.empty()) {
107
- std::wstring wtooltip(tooltip.begin(), tooltip.end());
108
- wcsncpy_s(pImpl->nid.szTip, _countof(pImpl->nid.szTip), wtooltip.c_str(),
109
- _TRUNCATE);
110
- Shell_NotifyIconW(NIM_MODIFY, &pImpl->nid);
111
- }
112
- #endif
113
- }
114
-
115
- void TrayIcon::setIcon(const std::string &iconPath) {
116
- pImpl->iconPath = iconPath;
117
- #ifdef _WIN32
118
- if (!iconPath.empty()) {
119
- std::wstring wpath(iconPath.begin(), iconPath.end());
120
- pImpl->nid.hIcon = (HICON)LoadImageW(nullptr, wpath.c_str(), IMAGE_ICON, 16,
121
- 16, LR_LOADFROMFILE);
122
- Shell_NotifyIconW(NIM_MODIFY, &pImpl->nid);
123
- }
124
- #endif
125
- }
126
-
127
- void TrayIcon::setIconFromData(const std::vector<uint8_t> &data, int width,
128
- int height) {
129
- #ifdef _WIN32
130
- if (!data.empty()) {
131
- BITMAPINFO bmi = {};
132
- bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
133
- bmi.bmiHeader.biWidth = width;
134
- bmi.bmiHeader.biHeight = -height;
135
- bmi.bmiHeader.biPlanes = 1;
136
- bmi.bmiHeader.biBitCount = 32;
137
- bmi.bmiHeader.biCompression = BI_RGB;
138
-
139
- void *bits = nullptr;
140
- HBITMAP hbm =
141
- CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, &bits, nullptr, 0);
142
- if (hbm && bits && data.size() >= (size_t)width * height * 4) {
143
- memcpy(bits, data.data(), width * height * 4);
144
-
145
- ICONINFO ii = {};
146
- ii.fIcon = TRUE;
147
- ii.hbmColor = hbm;
148
- ii.hbmMask = hbm;
149
-
150
- HICON hIcon = CreateIconIndirect(&ii);
151
- if (hIcon) {
152
- pImpl->nid.hIcon = hIcon;
153
- Shell_NotifyIconW(NIM_MODIFY, &pImpl->nid);
154
- DestroyIcon(hIcon);
155
- }
156
- DeleteObject(hbm);
157
- }
158
- }
159
- #endif
160
- }
161
-
162
- void TrayIcon::setIconFromMemory(const unsigned char *data, size_t size) {
163
- int width, height, channels;
164
- unsigned char *pixels =
165
- stbi_load_from_memory(data, (int)size, &width, &height, &channels, 4);
166
- if (pixels) {
167
- std::vector<uint8_t> pixelVec(pixels, pixels + (width * height * 4));
168
-
169
- #ifdef _WIN32
170
- // Windows expects BGRA, stb_image returns RGBA
171
- for (int i = 0; i < width * height; ++i) {
172
- uint8_t r = pixelVec[i * 4 + 0];
173
- uint8_t b = pixelVec[i * 4 + 2];
174
- pixelVec[i * 4 + 0] = b;
175
- pixelVec[i * 4 + 2] = r;
176
- }
177
- setIconFromData(pixelVec, width, height);
178
- #elif defined(__APPLE__)
179
- // Implementation for macOS (To be implemented)
180
- #else
181
- // Implementation for Linux (GTK) (To be implemented)
182
- #endif
183
-
184
- stbi_image_free(pixels);
185
- }
186
- }
187
-
188
- // Methods removed: show, hide (duplicate definitions)
189
-
190
- bool TrayIcon::isVisible() const { return pImpl->visible; }
191
-
192
- void TrayIcon::setMenu(const std::vector<TrayMenuItem> &items) {
193
- pImpl->menuItems = items;
194
- #ifdef _WIN32
195
- HMENU hMenu = CreatePopupMenu();
196
- for (const auto &item : items) {
197
- if (item.separator) {
198
- AppendMenuW(hMenu, MF_SEPARATOR, 0, nullptr);
199
- } else {
200
- std::wstring label(item.label.begin(), item.label.end());
201
- UINT flags = MF_STRING;
202
- if (!item.enabled)
203
- flags |= MF_DISABLED;
204
- if (item.checked)
205
- flags |= MF_CHECKED;
206
- AppendMenuW(hMenu, flags, (UINT_PTR)item.id.c_str(), label.c_str());
207
- }
208
- }
209
- #endif
210
- }
211
-
212
- void TrayIcon::setContextMenu(const std::vector<TrayMenuItem> &items) {
213
- setMenu(items);
214
- }
215
-
216
- void TrayIcon::onClick(std::function<void(int, int)> callback) {
217
- pImpl->clickCallback = callback;
218
- }
219
-
220
- void TrayIcon::onRightClick(std::function<void(int, int)> callback) {
221
- pImpl->rightClickCallback = callback;
222
- }
223
-
224
- void TrayIcon::onDoubleClick(std::function<void()> callback) {
225
- pImpl->doubleClickCallback = callback;
226
- }
227
-
228
- void TrayIcon::onMenuItemClick(
229
- std::function<void(const std::string &)> callback) {
230
- pImpl->menuItemCallback = callback;
231
- }
232
-
233
- void TrayIcon::dispose() {
234
- #ifdef _WIN32
235
- Shell_NotifyIconW(NIM_DELETE, &pImpl->nid);
236
- #endif
237
- }
238
-
239
- struct TrayManager::Impl {
240
- HWND hwnd = nullptr;
241
- std::unique_ptr<TrayIcon> mainIcon;
242
- std::vector<TrayIcon *> icons;
243
- std::function<void(TrayIcon &)> onCreatedCallback;
244
- std::function<void(int)> onDestroyedCallback;
245
- std::string currentIconPath;
246
- std::string currentTooltip;
247
- bool currentVisible = false;
248
- };
249
-
250
- TrayManager::TrayManager() : pImpl(std::make_unique<Impl>()) {}
251
- TrayManager::~TrayManager() = default;
252
-
253
- TrayManager &TrayManager::instance() {
254
- static TrayManager inst;
255
- return inst;
256
- }
257
-
258
- void TrayManager::setWindowHandle(void *hwnd) {
259
- #ifdef _WIN32
260
- pImpl->hwnd = (HWND)hwnd;
261
- if (pImpl->mainIcon) {
262
- pImpl->mainIcon->pImpl->hwnd = pImpl->hwnd;
263
- }
264
- #endif
265
- }
266
-
267
- void TrayManager::setIcon(const std::string &iconPath) {
268
- pImpl->currentIconPath = iconPath;
269
- if (pImpl->mainIcon) {
270
- pImpl->mainIcon->setIcon(iconPath);
271
- } else {
272
- pImpl->mainIcon =
273
- std::make_unique<TrayIcon>(TrayIcon::create("", iconPath));
274
- #ifdef _WIN32
275
- pImpl->mainIcon->pImpl->nid.cbSize = sizeof(NOTIFYICONDATAW);
276
- pImpl->mainIcon->pImpl->hwnd = pImpl->hwnd;
277
- #endif
278
- }
279
- }
280
-
281
- void TrayManager::setTooltip(const std::string &tooltip) {
282
- pImpl->currentTooltip = tooltip;
283
- if (pImpl->mainIcon) {
284
- pImpl->mainIcon->setTooltip(tooltip);
285
- }
286
- }
287
-
288
- void TrayManager::setVisible(bool visible) {
289
- pImpl->currentVisible = visible;
290
- if (pImpl->mainIcon) {
291
- if (visible)
292
- pImpl->mainIcon->show();
293
- else
294
- pImpl->mainIcon->hide();
295
- }
296
- }
297
-
298
- std::vector<TrayIcon *> TrayManager::getIcons() { return pImpl->icons; }
299
-
300
- void TrayManager::onTrayCreated(std::function<void(TrayIcon &)> callback) {
301
- pImpl->onCreatedCallback = callback;
302
- }
303
-
304
- void TrayManager::onTrayDestroyed(std::function<void(int)> callback) {
305
- pImpl->onDestroyedCallback = callback;
306
- }
307
-
308
- void TrayManager::refresh() {}
309
-
310
- void TrayManager::setIconFromMemory(const unsigned char *data, size_t size) {
311
- if (pImpl->mainIcon) {
312
- pImpl->mainIcon->setIconFromMemory(data, size);
313
- } else {
314
- // Create the icon if it doesn't exist
315
- pImpl->mainIcon = std::make_unique<TrayIcon>(TrayIcon::create("", ""));
316
- #ifdef _WIN32
317
- pImpl->mainIcon->pImpl->nid.cbSize = sizeof(NOTIFYICONDATAW);
318
- pImpl->mainIcon->pImpl->hwnd = pImpl->hwnd;
319
- #endif
320
- pImpl->mainIcon->setIconFromMemory(data, size);
321
- }
322
- }
323
-
324
- } // namespace plusui
1
+ #ifdef _WIN32
2
+ #ifndef _WIN32_WINNT
3
+ #define _WIN32_WINNT 0x0601
4
+ #endif
5
+ #include <windows.h>
6
+
7
+ #ifndef EXTERN_C
8
+ #ifdef __cplusplus
9
+ #define EXTERN_C extern "C"
10
+ #else
11
+ #define EXTERN_C extern
12
+ #endif
13
+ #endif
14
+
15
+ #include <shellapi.h>
16
+ #include <shlobj.h>
17
+ #pragma comment(lib, "shell32.lib")
18
+ #endif
19
+
20
+ #include <algorithm>
21
+ #include <iostream>
22
+ #include <plusui/tray.hpp>
23
+ #include <string>
24
+ #include <vector>
25
+
26
+ #define STB_IMAGE_IMPLEMENTATION
27
+ #include <stb_image.h>
28
+
29
+ #ifdef __APPLE__
30
+ #import <Cocoa/Cocoa.h>
31
+ #import <AppKit/AppKit.h>
32
+ #elif !defined(_WIN32)
33
+ #include <gdk/gdk.h>
34
+ #include <gtk/gtk.h>
35
+ #endif
36
+
37
+ namespace plusui {
38
+
39
+ struct TrayIcon::Impl {
40
+ int id = 0;
41
+ std::string tooltip;
42
+ std::string iconPath;
43
+ bool visible = true;
44
+
45
+ std::function<void(int, int)> clickCallback;
46
+ std::function<void(int, int)> rightClickCallback;
47
+ std::function<void()> doubleClickCallback;
48
+ std::function<void(const std::string &)> menuItemCallback;
49
+ std::vector<TrayMenuItem> menuItems;
50
+
51
+ #ifdef _WIN32
52
+ NOTIFYICONDATAW nid = {};
53
+ HWND hwnd = nullptr;
54
+ #elif defined(__APPLE__)
55
+ NSStatusItem* statusItem = nullptr;
56
+ NSMenu* menu = nullptr;
57
+ #else
58
+ GtkStatusIcon* statusIcon = nullptr;
59
+ GtkMenu* menu = nullptr;
60
+ #endif
61
+ };
62
+
63
+ TrayIcon::TrayIcon() : pImpl(std::make_unique<Impl>()) {}
64
+ TrayIcon::~TrayIcon() { dispose(); }
65
+
66
+ TrayIcon::TrayIcon(TrayIcon &&other) noexcept = default;
67
+ TrayIcon &TrayIcon::operator=(TrayIcon &&other) noexcept = default;
68
+
69
+ TrayIcon TrayIcon::create(const std::string &tooltip,
70
+ const std::string &iconPath) {
71
+ TrayIcon icon;
72
+ icon.pImpl->tooltip = tooltip;
73
+ icon.pImpl->iconPath = iconPath;
74
+
75
+ #ifdef _WIN32
76
+ icon.pImpl->nid.uID = 1;
77
+ icon.pImpl->nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
78
+ icon.pImpl->nid.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
79
+ icon.pImpl->nid.uCallbackMessage = WM_APP + 1;
80
+
81
+ if (!tooltip.empty()) {
82
+ std::wstring wtooltip(tooltip.begin(), tooltip.end());
83
+ wcsncpy_s(icon.pImpl->nid.szTip, _countof(icon.pImpl->nid.szTip),
84
+ wtooltip.c_str(), _TRUNCATE);
85
+ }
86
+ #elif defined(__APPLE__)
87
+ icon.pImpl->statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
88
+ if (icon.pImpl->statusItem) {
89
+ if (!iconPath.empty()) {
90
+ NSImage* nsImage = [[NSImage alloc] initWithContentsOfFile:[NSString stringWithUTF8String:iconPath.c_str()]];
91
+ if (nsImage) {
92
+ [nsImage setSize:NSMakeSize(18, 18)];
93
+ icon.pImpl->statusItem.button.image = nsImage;
94
+ }
95
+ }
96
+ if (!tooltip.empty()) {
97
+ icon.pImpl->statusItem.button.toolTip = [NSString stringWithUTF8String:tooltip.c_str()];
98
+ }
99
+ icon.pImpl->menu = [[NSMenu alloc] init];
100
+ icon.pImpl->statusItem.menu = icon.pImpl->menu;
101
+ }
102
+ #else
103
+ icon.pImpl->statusIcon = gtk_status_icon_new();
104
+ if (icon.pImpl->statusIcon) {
105
+ gtk_status_icon_set_title(icon.pImpl->statusIcon, tooltip.empty() ? "PlusUI App" : tooltip.c_str());
106
+ if (!tooltip.empty()) {
107
+ gtk_status_icon_set_tooltip_text(icon.pImpl->statusIcon, tooltip.c_str());
108
+ }
109
+ if (!iconPath.empty()) {
110
+ gtk_status_icon_set_from_file(icon.pImpl->statusIcon, iconPath.c_str());
111
+ } else {
112
+ gtk_status_icon_set_from_icon_name(icon.pImpl->statusIcon, "application-x-executable");
113
+ }
114
+ icon.pImpl->menu = GTK_MENU(gtk_menu_new());
115
+ gtk_status_icon_set_visible(icon.pImpl->statusIcon, TRUE);
116
+ }
117
+ #endif
118
+
119
+ return icon;
120
+ }
121
+
122
+ void TrayIcon::show() {
123
+ pImpl->visible = true;
124
+ #ifdef _WIN32
125
+ if (pImpl->hwnd) {
126
+ pImpl->nid.hWnd = pImpl->hwnd;
127
+ Shell_NotifyIconW(NIM_ADD, &pImpl->nid);
128
+ }
129
+ #elif defined(__APPLE__)
130
+ if (pImpl->statusItem) {
131
+ pImpl->statusItem.button.hidden = NO;
132
+ }
133
+ #else
134
+ if (pImpl->statusIcon) {
135
+ gtk_status_icon_set_visible(pImpl->statusIcon, TRUE);
136
+ }
137
+ #endif
138
+ }
139
+
140
+ void TrayIcon::hide() {
141
+ pImpl->visible = false;
142
+ #ifdef _WIN32
143
+ Shell_NotifyIconW(NIM_DELETE, &pImpl->nid);
144
+ #elif defined(__APPLE__)
145
+ if (pImpl->statusItem) {
146
+ pImpl->statusItem.button.hidden = YES;
147
+ }
148
+ #else
149
+ if (pImpl->statusIcon) {
150
+ gtk_status_icon_set_visible(pImpl->statusIcon, FALSE);
151
+ }
152
+ #endif
153
+ }
154
+
155
+ void TrayIcon::setTooltip(const std::string &tooltip) {
156
+ pImpl->tooltip = tooltip;
157
+ #ifdef _WIN32
158
+ if (!tooltip.empty()) {
159
+ std::wstring wtooltip(tooltip.begin(), tooltip.end());
160
+ wcsncpy_s(pImpl->nid.szTip, _countof(pImpl->nid.szTip), wtooltip.c_str(),
161
+ _TRUNCATE);
162
+ Shell_NotifyIconW(NIM_MODIFY, &pImpl->nid);
163
+ }
164
+ #elif defined(__APPLE__)
165
+ if (pImpl->statusItem && !tooltip.empty()) {
166
+ pImpl->statusItem.button.toolTip = [NSString stringWithUTF8String:tooltip.c_str()];
167
+ }
168
+ #else
169
+ if (pImpl->statusIcon && !tooltip.empty()) {
170
+ gtk_status_icon_set_tooltip_text(pImpl->statusIcon, tooltip.c_str());
171
+ }
172
+ #endif
173
+ }
174
+
175
+ void TrayIcon::setIcon(const std::string &iconPath) {
176
+ pImpl->iconPath = iconPath;
177
+ #ifdef _WIN32
178
+ if (!iconPath.empty()) {
179
+ std::wstring wpath(iconPath.begin(), iconPath.end());
180
+ pImpl->nid.hIcon = (HICON)LoadImageW(nullptr, wpath.c_str(), IMAGE_ICON, 16,
181
+ 16, LR_LOADFROMFILE);
182
+ Shell_NotifyIconW(NIM_MODIFY, &pImpl->nid);
183
+ }
184
+ #elif defined(__APPLE__)
185
+ if (pImpl->statusItem && !iconPath.empty()) {
186
+ NSImage* nsImage = [[NSImage alloc] initWithContentsOfFile:[NSString stringWithUTF8String:iconPath.c_str()]];
187
+ if (nsImage) {
188
+ [nsImage setSize:NSMakeSize(18, 18)];
189
+ pImpl->statusItem.button.image = nsImage;
190
+ }
191
+ }
192
+ #else
193
+ if (pImpl->statusIcon && !iconPath.empty()) {
194
+ gtk_status_icon_set_from_file(pImpl->statusIcon, iconPath.c_str());
195
+ }
196
+ #endif
197
+ }
198
+
199
+ void TrayIcon::setIconFromData(const std::vector<uint8_t> &data, int width,
200
+ int height) {
201
+ #ifdef _WIN32
202
+ if (!data.empty()) {
203
+ BITMAPINFO bmi = {};
204
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
205
+ bmi.bmiHeader.biWidth = width;
206
+ bmi.bmiHeader.biHeight = -height;
207
+ bmi.bmiHeader.biPlanes = 1;
208
+ bmi.bmiHeader.biBitCount = 32;
209
+ bmi.bmiHeader.biCompression = BI_RGB;
210
+
211
+ void *bits = nullptr;
212
+ HBITMAP hbm =
213
+ CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, &bits, nullptr, 0);
214
+ if (hbm && bits && data.size() >= (size_t)width * height * 4) {
215
+ memcpy(bits, data.data(), width * height * 4);
216
+
217
+ ICONINFO ii = {};
218
+ ii.fIcon = TRUE;
219
+ ii.hbmColor = hbm;
220
+ ii.hbmMask = hbm;
221
+
222
+ HICON hIcon = CreateIconIndirect(&ii);
223
+ if (hIcon) {
224
+ pImpl->nid.hIcon = hIcon;
225
+ Shell_NotifyIconW(NIM_MODIFY, &pImpl->nid);
226
+ DestroyIcon(hIcon);
227
+ }
228
+ DeleteObject(hbm);
229
+ }
230
+ }
231
+ #endif
232
+ }
233
+
234
+ void TrayIcon::setIconFromMemory(const unsigned char *data, size_t size) {
235
+ int width, height, channels;
236
+ unsigned char *pixels =
237
+ stbi_load_from_memory(data, (int)size, &width, &height, &channels, 4);
238
+ if (pixels) {
239
+ std::vector<uint8_t> pixelVec(pixels, pixels + (width * height * 4));
240
+
241
+ #ifdef _WIN32
242
+ for (int i = 0; i < width * height; ++i) {
243
+ uint8_t r = pixelVec[i * 4 + 0];
244
+ uint8_t b = pixelVec[i * 4 + 2];
245
+ pixelVec[i * 4 + 0] = b;
246
+ pixelVec[i * 4 + 2] = r;
247
+ }
248
+ setIconFromData(pixelVec, width, height);
249
+ #elif defined(__APPLE__)
250
+ if (pImpl->statusItem) {
251
+ NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc]
252
+ initWithBitmapDataPlanes:&pixels
253
+ pixelsWide:width
254
+ pixelsHigh:height
255
+ bitsPerSample:8
256
+ samplesPerPixel:4
257
+ hasAlpha:YES
258
+ isPlanar:NO
259
+ colorSpaceName:NSCalibratedRGBColorSpace
260
+ bytesPerRow:width * 4
261
+ bitsPerPixel:32];
262
+ NSImage* nsImage = [[NSImage alloc] initWithSize:NSMakeSize(width, height)];
263
+ [nsImage addRepresentation:bitmap];
264
+ [nsImage setSize:NSMakeSize(18, 18)];
265
+ pImpl->statusItem.button.image = nsImage;
266
+ }
267
+ #else
268
+ if (pImpl->statusIcon) {
269
+ GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data(
270
+ pixels, GDK_COLORSPACE_RGB, TRUE, 8, width, height,
271
+ width * 4, nullptr, nullptr);
272
+ if (pixbuf) {
273
+ gtk_status_icon_set_from_pixbuf(pImpl->statusIcon, pixbuf);
274
+ g_object_unref(pixbuf);
275
+ }
276
+ }
277
+ #endif
278
+
279
+ stbi_image_free(pixels);
280
+ }
281
+ }
282
+
283
+ // Methods removed: show, hide (duplicate definitions)
284
+
285
+ bool TrayIcon::isVisible() const { return pImpl->visible; }
286
+
287
+ void TrayIcon::setMenu(const std::vector<TrayMenuItem> &items) {
288
+ pImpl->menuItems = items;
289
+ #ifdef _WIN32
290
+ HMENU hMenu = CreatePopupMenu();
291
+ for (const auto &item : items) {
292
+ if (item.separator) {
293
+ AppendMenuW(hMenu, MF_SEPARATOR, 0, nullptr);
294
+ } else {
295
+ std::wstring label(item.label.begin(), item.label.end());
296
+ UINT flags = MF_STRING;
297
+ if (!item.enabled)
298
+ flags |= MF_DISABLED;
299
+ if (item.checked)
300
+ flags |= MF_CHECKED;
301
+ AppendMenuW(hMenu, flags, (UINT_PTR)item.id.c_str(), label.c_str());
302
+ }
303
+ }
304
+ #endif
305
+ }
306
+
307
+ void TrayIcon::setContextMenu(const std::vector<TrayMenuItem> &items) {
308
+ setMenu(items);
309
+ }
310
+
311
+ void TrayIcon::onClick(std::function<void(int, int)> callback) {
312
+ pImpl->clickCallback = callback;
313
+ }
314
+
315
+ void TrayIcon::onRightClick(std::function<void(int, int)> callback) {
316
+ pImpl->rightClickCallback = callback;
317
+ }
318
+
319
+ void TrayIcon::onDoubleClick(std::function<void()> callback) {
320
+ pImpl->doubleClickCallback = callback;
321
+ }
322
+
323
+ void TrayIcon::onMenuItemClick(
324
+ std::function<void(const std::string &)> callback) {
325
+ pImpl->menuItemCallback = callback;
326
+ }
327
+
328
+ void TrayIcon::dispose() {
329
+ #ifdef _WIN32
330
+ Shell_NotifyIconW(NIM_DELETE, &pImpl->nid);
331
+ #elif defined(__APPLE__)
332
+ if (pImpl->statusItem) {
333
+ [[NSStatusBar systemStatusBar] removeStatusItem:pImpl->statusItem];
334
+ pImpl->statusItem = nil;
335
+ }
336
+ #else
337
+ if (pImpl->statusIcon) {
338
+ gtk_widget_destroy(GTK_WIDGET(pImpl->statusIcon));
339
+ pImpl->statusIcon = nullptr;
340
+ }
341
+ #endif
342
+ }
343
+
344
+ struct TrayManager::Impl {
345
+ #ifdef _WIN32
346
+ HWND hwnd = nullptr;
347
+ #else
348
+ void* hwnd = nullptr;
349
+ #endif
350
+ std::unique_ptr<TrayIcon> mainIcon;
351
+ std::vector<TrayIcon *> icons;
352
+ std::function<void(TrayIcon &)> onCreatedCallback;
353
+ std::function<void(int)> onDestroyedCallback;
354
+ std::string currentIconPath;
355
+ std::string currentTooltip;
356
+ bool currentVisible = false;
357
+ };
358
+
359
+ TrayManager::TrayManager() : pImpl(std::make_unique<Impl>()) {}
360
+ TrayManager::~TrayManager() = default;
361
+
362
+ TrayManager &TrayManager::instance() {
363
+ static TrayManager inst;
364
+ return inst;
365
+ }
366
+
367
+ void TrayManager::setWindowHandle(void *hwnd) {
368
+ #ifdef _WIN32
369
+ pImpl->hwnd = (HWND)hwnd;
370
+ if (pImpl->mainIcon) {
371
+ pImpl->mainIcon->pImpl->hwnd = pImpl->hwnd;
372
+ }
373
+ #endif
374
+ }
375
+
376
+ void TrayManager::setIcon(const std::string &iconPath) {
377
+ pImpl->currentIconPath = iconPath;
378
+ if (pImpl->mainIcon) {
379
+ pImpl->mainIcon->setIcon(iconPath);
380
+ } else {
381
+ pImpl->mainIcon =
382
+ std::make_unique<TrayIcon>(TrayIcon::create("", iconPath));
383
+ #ifdef _WIN32
384
+ pImpl->mainIcon->pImpl->nid.cbSize = sizeof(NOTIFYICONDATAW);
385
+ pImpl->mainIcon->pImpl->hwnd = pImpl->hwnd;
386
+ #endif
387
+ }
388
+ }
389
+
390
+ void TrayManager::setTooltip(const std::string &tooltip) {
391
+ pImpl->currentTooltip = tooltip;
392
+ if (pImpl->mainIcon) {
393
+ pImpl->mainIcon->setTooltip(tooltip);
394
+ }
395
+ }
396
+
397
+ void TrayManager::setVisible(bool visible) {
398
+ pImpl->currentVisible = visible;
399
+ if (pImpl->mainIcon) {
400
+ if (visible)
401
+ pImpl->mainIcon->show();
402
+ else
403
+ pImpl->mainIcon->hide();
404
+ }
405
+ }
406
+
407
+ std::vector<TrayIcon *> TrayManager::getIcons() { return pImpl->icons; }
408
+
409
+ void TrayManager::onTrayCreated(std::function<void(TrayIcon &)> callback) {
410
+ pImpl->onCreatedCallback = callback;
411
+ }
412
+
413
+ void TrayManager::onTrayDestroyed(std::function<void(int)> callback) {
414
+ pImpl->onDestroyedCallback = callback;
415
+ }
416
+
417
+ void TrayManager::refresh() {}
418
+
419
+ void TrayManager::setIconFromMemory(const unsigned char *data, size_t size) {
420
+ if (pImpl->mainIcon) {
421
+ pImpl->mainIcon->setIconFromMemory(data, size);
422
+ } else {
423
+ pImpl->mainIcon = std::make_unique<TrayIcon>(TrayIcon::create("", ""));
424
+ #ifdef _WIN32
425
+ pImpl->mainIcon->pImpl->nid.cbSize = sizeof(NOTIFYICONDATAW);
426
+ pImpl->mainIcon->pImpl->hwnd = pImpl->hwnd;
427
+ #endif
428
+ pImpl->icons.push_back(pImpl->mainIcon.get());
429
+ if (pImpl->onCreatedCallback) {
430
+ pImpl->onCreatedCallback(*pImpl->mainIcon);
431
+ }
432
+ pImpl->mainIcon->setIconFromMemory(data, size);
433
+ pImpl->mainIcon->show();
434
+ }
435
+ }
436
+
437
+ } // namespace plusui