plusui-native-core 0.1.4 → 0.1.5

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 (54) hide show
  1. package/Core/CMakeLists.txt +190 -7
  2. package/Core/Features/App/app.cpp +129 -0
  3. package/Core/Features/App/app.ts +126 -0
  4. package/Core/Features/Browser/browser.cpp +181 -0
  5. package/Core/Features/Browser/browser.ts +182 -0
  6. package/Core/Features/Clipboard/clipboard.cpp +234 -0
  7. package/Core/Features/Clipboard/clipboard.ts +113 -0
  8. package/Core/Features/Display/display.cpp +209 -0
  9. package/Core/Features/Display/display.ts +104 -0
  10. package/Core/Features/Event/Events.ts +166 -0
  11. package/Core/Features/Event/events.cpp +200 -0
  12. package/Core/Features/Keyboard/keyboard.cpp +186 -0
  13. package/Core/Features/Keyboard/keyboard.ts +175 -0
  14. package/Core/Features/Menu/context-menu.css +293 -0
  15. package/Core/Features/Menu/menu.cpp +481 -0
  16. package/Core/Features/Menu/menu.ts +439 -0
  17. package/Core/Features/Tray/tray.cpp +310 -0
  18. package/Core/Features/Tray/tray.ts +68 -0
  19. package/Core/Features/WebGPU/webgpu.cpp +937 -0
  20. package/Core/Features/WebGPU/webgpu.ts +1013 -0
  21. package/Core/Features/WebView/webview.cpp +1052 -0
  22. package/Core/Features/WebView/webview.ts +510 -0
  23. package/Core/Features/Window/window.cpp +664 -0
  24. package/Core/Features/Window/window.ts +142 -0
  25. package/Core/Features/WindowManager/window_manager.cpp +341 -0
  26. package/Core/include/plusui/app.hpp +73 -0
  27. package/Core/include/plusui/browser.hpp +66 -0
  28. package/Core/include/plusui/clipboard.hpp +41 -0
  29. package/Core/include/plusui/events.hpp +58 -0
  30. package/Core/include/{keyboard.hpp → plusui/keyboard.hpp} +21 -44
  31. package/Core/include/plusui/menu.hpp +153 -0
  32. package/Core/include/plusui/tray.hpp +93 -0
  33. package/Core/include/plusui/webgpu.hpp +434 -0
  34. package/Core/include/plusui/webview.hpp +142 -0
  35. package/Core/include/plusui/window.hpp +111 -0
  36. package/Core/include/plusui/window_manager.hpp +57 -0
  37. package/Core/vendor/WebView2EnvironmentOptions.h +406 -0
  38. package/Core/vendor/stb_image.h +7988 -0
  39. package/Core/vendor/webview.h +618 -510
  40. package/Core/vendor/webview2.h +52079 -0
  41. package/README.md +19 -0
  42. package/package.json +12 -15
  43. package/Core/include/app.hpp +0 -121
  44. package/Core/include/menu.hpp +0 -79
  45. package/Core/include/tray.hpp +0 -81
  46. package/Core/include/window.hpp +0 -106
  47. package/Core/src/app.cpp +0 -311
  48. package/Core/src/display.cpp +0 -424
  49. package/Core/src/tray.cpp +0 -275
  50. package/Core/src/window.cpp +0 -528
  51. package/dist/index.d.ts +0 -205
  52. package/dist/index.js +0 -198
  53. package/src/index.ts +0 -574
  54. /package/Core/include/{display.hpp → plusui/display.hpp} +0 -0
@@ -0,0 +1,664 @@
1
+ #include <iostream>
2
+ #include <map>
3
+ #include <plusui/display.hpp>
4
+ #include <plusui/window.hpp>
5
+
6
+ #ifdef _WIN32
7
+ #include <windows.h>
8
+ #elif defined(__APPLE__)
9
+ #include <Cocoa/Cocoa.h>
10
+ #include <objc/objc-runtime.h>
11
+ #else
12
+ #include <gtk/gtk.h>
13
+ #endif
14
+
15
+ #include <stb_image.h>
16
+
17
+ namespace plusui {
18
+
19
+ struct Window::Impl {
20
+ void *nativeWindow = nullptr;
21
+ WindowConfig config;
22
+ WindowState state;
23
+
24
+ std::vector<MoveCallback> moveCallbacks;
25
+ std::vector<ResizeCallback> resizeCallbacks;
26
+ std::vector<CloseCallback> closeCallbacks;
27
+ std::vector<FocusCallback> focusCallbacks;
28
+ std::vector<StateCallback> stateCallbacks;
29
+
30
+ #ifdef _WIN32
31
+ HWND hwnd = nullptr;
32
+ WNDPROC originalProc = nullptr;
33
+
34
+ static LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
35
+ Impl *impl = nullptr;
36
+ if (msg == WM_NCCREATE) {
37
+ auto cs = (LPCREATESTRUCT)lp;
38
+ impl = (Impl *)cs->lpCreateParams;
39
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)impl);
40
+ } else {
41
+ impl = (Impl *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
42
+ }
43
+
44
+ if (impl) {
45
+ switch (msg) {
46
+ case WM_MOVE: {
47
+ int x = (int)(short)LOWORD(lp);
48
+ int y = (int)(short)HIWORD(lp);
49
+ impl->state.x = x;
50
+ impl->state.y = y;
51
+ for (auto &cb : impl->moveCallbacks)
52
+ cb(x, y);
53
+ break;
54
+ }
55
+ case WM_SIZE: {
56
+ int width = LOWORD(lp);
57
+ int height = HIWORD(lp);
58
+ impl->state.width = width;
59
+ impl->state.height = height;
60
+ for (auto &cb : impl->resizeCallbacks)
61
+ cb(width, height);
62
+ break;
63
+ }
64
+ case WM_CLOSE:
65
+ for (auto &cb : impl->closeCallbacks)
66
+ cb();
67
+ break;
68
+ case WM_SETFOCUS:
69
+ impl->state.isFocused = true;
70
+ for (auto &cb : impl->focusCallbacks)
71
+ cb(true);
72
+ break;
73
+ case WM_KILLFOCUS:
74
+ impl->state.isFocused = false;
75
+ for (auto &cb : impl->focusCallbacks)
76
+ cb(false);
77
+ break;
78
+ }
79
+ }
80
+ return DefWindowProc(hwnd, msg, wp, lp);
81
+ }
82
+ #endif
83
+ };
84
+
85
+ Window::Window() : pImpl(std::shared_ptr<Impl>(new Impl())) {}
86
+
87
+ Window::~Window() = default;
88
+
89
+ Window Window::create(const WindowConfig &config) {
90
+ Window w;
91
+ w.pImpl->config = config;
92
+
93
+ #ifdef _WIN32
94
+ WNDCLASSEXW wc = {};
95
+ wc.cbSize = sizeof(WNDCLASSEXW);
96
+ wc.lpfnWndProc = Impl::wndProc;
97
+ wc.hInstance = GetModuleHandle(nullptr);
98
+ wc.lpszClassName = L"PLUSUI_WINDOW";
99
+ wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
100
+ wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
101
+ RegisterClassExW(&wc);
102
+
103
+ DWORD style = 0;
104
+ DWORD exStyle = 0;
105
+
106
+ if (config.decorations) {
107
+ style = WS_OVERLAPPEDWINDOW;
108
+ if (!config.resizable)
109
+ style &= ~WS_THICKFRAME;
110
+ if (!config.minimizable)
111
+ style &= ~WS_MINIMIZEBOX;
112
+ if (!config.closable)
113
+ style &= ~WS_SYSMENU;
114
+ } else {
115
+ // Frameless window
116
+ style = WS_POPUP;
117
+ if (config.resizable)
118
+ style |= WS_THICKFRAME;
119
+ }
120
+
121
+ if (config.transparent) {
122
+ exStyle = WS_EX_LAYERED;
123
+ }
124
+
125
+ if (config.skipTaskbar) {
126
+ exStyle |= WS_EX_TOOLWINDOW;
127
+ }
128
+
129
+ std::wstring wideTitle(config.title.begin(), config.title.end());
130
+ w.pImpl->hwnd = CreateWindowExW(
131
+ exStyle, L"PLUSUI_WINDOW", wideTitle.c_str(), style,
132
+ config.x >= 0 ? config.x : CW_USEDEFAULT,
133
+ config.y >= 0 ? config.y : CW_USEDEFAULT, config.width, config.height,
134
+ nullptr, nullptr, GetModuleHandle(nullptr), w.pImpl.get());
135
+
136
+ w.pImpl->nativeWindow = (void *)w.pImpl->hwnd;
137
+ w.pImpl->state.width = config.width;
138
+ w.pImpl->state.height = config.height;
139
+
140
+ if (config.center) {
141
+ RECT screen;
142
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &screen, 0);
143
+ int x = (screen.right - config.width) / 2;
144
+ int y = (screen.bottom - config.height) / 2;
145
+ SetWindowPos(w.pImpl->hwnd, nullptr, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
146
+ }
147
+
148
+ if (config.alwaysOnTop) {
149
+ SetWindowPos(w.pImpl->hwnd, HWND_TOPMOST, 0, 0, 0, 0,
150
+ SWP_NOMOVE | SWP_NOSIZE);
151
+ }
152
+
153
+ #elif defined(__APPLE__)
154
+ NSWindow *nswin = [[NSWindow alloc]
155
+ initWithContentRect:NSMakeRect(config.x >= 0 ? config.x : 100,
156
+ config.y >= 0 ? config.y : 100,
157
+ config.width, config.height)
158
+ styleMask:(config.resizable ? NSWindowStyleMaskResizable : 0) |
159
+ NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
160
+ (config.minimizable ? NSWindowStyleMaskMiniaturizable
161
+ : 0)backing:NSBackingStoreBuffered
162
+ defer:NO];
163
+
164
+ [nswin setTitle:[NSString stringWithUTF8String:config.title.c_str()]];
165
+ [nswin setReleasedWhenClosed:NO];
166
+
167
+ if (config.center) {
168
+ [nswin center];
169
+ }
170
+
171
+ if (config.alwaysOnTop) {
172
+ [nswin setLevel:NSFloatingWindowLevel];
173
+ }
174
+
175
+ w.pImpl->nativeWindow = (__bridge void *)nswin;
176
+
177
+ #else
178
+ gtk_init_check(0, nullptr);
179
+
180
+ GtkWindow *gtkwin = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
181
+ gtk_window_set_title(gtkwin, config.title.c_str());
182
+ gtk_window_set_default_size(gtkwin, config.width, config.height);
183
+
184
+ if (config.x >= 0 && config.y >= 0) {
185
+ gtk_window_move(gtkwin, config.x, config.y);
186
+ } else if (config.center) {
187
+ gtk_window_set_position(gtkwin, GTK_WIN_POS_CENTER);
188
+ }
189
+
190
+ if (!config.resizable) {
191
+ gtk_window_set_resizable(gtkwin, FALSE);
192
+ }
193
+
194
+ if (config.alwaysOnTop) {
195
+ gtk_window_set_keep_above(gtkwin, TRUE);
196
+ }
197
+
198
+ w.pImpl->nativeWindow = (void *)gtkwin;
199
+ #endif
200
+
201
+ return w;
202
+ }
203
+
204
+ void Window::setTitle(const std::string &title) {
205
+ pImpl->config.title = title;
206
+ #ifdef _WIN32
207
+ if (pImpl->hwnd) {
208
+ std::wstring wideTitle(title.begin(), title.end());
209
+ SetWindowTextW(pImpl->hwnd, wideTitle.c_str());
210
+ }
211
+ #elif defined(__APPLE__)
212
+ if (pImpl->nativeWindow) {
213
+ NSWindow *nswin = (__bridge NSWindow *)pImpl->nativeWindow;
214
+ [nswin setTitle:[NSString stringWithUTF8String:title.c_str()]];
215
+ }
216
+ #else
217
+ if (pImpl->nativeWindow) {
218
+ gtk_window_set_title(GTK_WINDOW(pImpl->nativeWindow), title.c_str());
219
+ }
220
+ #endif
221
+ }
222
+
223
+ std::string Window::getTitle() const { return pImpl->config.title; }
224
+
225
+ void Window::setSize(int width, int height) {
226
+ pImpl->config.width = width;
227
+ pImpl->config.height = height;
228
+ #ifdef _WIN32
229
+ if (pImpl->hwnd)
230
+ SetWindowPos(pImpl->hwnd, nullptr, 0, 0, width, height,
231
+ SWP_NOZORDER | SWP_NOMOVE);
232
+ #elif defined(__APPLE__)
233
+ if (pImpl->nativeWindow) {
234
+ NSWindow *nswin = (__bridge NSWindow *)pImpl->nativeWindow;
235
+ [nswin setContentSize:NSMakeSize(width, height)];
236
+ }
237
+ #else
238
+ if (pImpl->nativeWindow) {
239
+ gtk_window_resize(GTK_WINDOW(pImpl->nativeWindow), width, height);
240
+ }
241
+ #endif
242
+ }
243
+
244
+ void Window::getSize(int &width, int &height) const {
245
+ width = pImpl->state.width;
246
+ height = pImpl->state.height;
247
+ }
248
+
249
+ void Window::setMinSize(int minWidth, int minHeight) {
250
+ pImpl->config.minWidth = minWidth;
251
+ pImpl->config.minHeight = minHeight;
252
+ #ifdef _WIN32
253
+ if (pImpl->hwnd) {
254
+ SetWindowLongPtr(pImpl->hwnd, GWL_STYLE,
255
+ GetWindowLong(pImpl->hwnd, GWL_STYLE) | WS_THICKFRAME);
256
+ }
257
+ #endif
258
+ }
259
+
260
+ void Window::setMaxSize(int maxWidth, int maxHeight) {
261
+ pImpl->config.maxWidth = maxWidth;
262
+ pImpl->config.maxHeight = maxHeight;
263
+ }
264
+
265
+ void Window::setPosition(int x, int y) {
266
+ pImpl->config.x = x;
267
+ pImpl->config.y = y;
268
+ #ifdef _WIN32
269
+ if (pImpl->hwnd)
270
+ SetWindowPos(pImpl->hwnd, nullptr, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
271
+ #elif defined(__APPLE__)
272
+ if (pImpl->nativeWindow) {
273
+ NSWindow *nswin = (__bridge NSWindow *)pImpl->nativeWindow;
274
+ [nswin setFrameOrigin:NSMakePoint(x, y)];
275
+ }
276
+ #else
277
+ if (pImpl->nativeWindow) {
278
+ gtk_window_move(GTK_WINDOW(pImpl->nativeWindow), x, y);
279
+ }
280
+ #endif
281
+ }
282
+
283
+ void Window::getPosition(int &x, int &y) const {
284
+ x = pImpl->state.x;
285
+ y = pImpl->state.y;
286
+ }
287
+
288
+ void Window::center() {
289
+ #ifdef _WIN32
290
+ if (pImpl->hwnd) {
291
+ RECT rc, screen;
292
+ GetWindowRect(pImpl->hwnd, &rc);
293
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &screen, 0);
294
+ int x = (screen.right - (rc.right - rc.left)) / 2;
295
+ int y = (screen.bottom - (rc.bottom - rc.top)) / 2;
296
+ SetWindowPos(pImpl->hwnd, nullptr, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
297
+ }
298
+ #elif defined(__APPLE__)
299
+ if (pImpl->nativeWindow) {
300
+ NSWindow *nswin = (__bridge NSWindow *)pImpl->nativeWindow;
301
+ [nswin center];
302
+ }
303
+ #else
304
+ if (pImpl->nativeWindow) {
305
+ gtk_window_set_position(GTK_WINDOW(pImpl->nativeWindow),
306
+ GTK_WIN_POS_CENTER);
307
+ }
308
+ #endif
309
+ }
310
+
311
+ void Window::setFullscreen(bool enabled) {
312
+ bool wasFullscreen = pImpl->state.isFullscreen;
313
+ pImpl->config.fullscreen = enabled;
314
+ pImpl->state.isFullscreen = enabled;
315
+ pImpl->state.isHidden = false;
316
+
317
+ #ifdef _WIN32
318
+ if (pImpl->hwnd) {
319
+ if (enabled) {
320
+ SetWindowLongPtr(pImpl->hwnd, GWL_STYLE,
321
+ GetWindowLong(pImpl->hwnd, GWL_STYLE) &
322
+ ~WS_OVERLAPPEDWINDOW);
323
+ SetWindowPos(pImpl->hwnd, HWND_TOP, 0, 0, GetSystemMetrics(SM_CXSCREEN),
324
+ GetSystemMetrics(SM_CYSCREEN), SWP_SHOWWINDOW);
325
+ } else {
326
+ SetWindowLongPtr(pImpl->hwnd, GWL_STYLE,
327
+ GetWindowLong(pImpl->hwnd, GWL_STYLE) |
328
+ WS_OVERLAPPEDWINDOW);
329
+ SetWindowPos(pImpl->hwnd, nullptr, 0, 0, 0, 0,
330
+ SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
331
+ }
332
+ }
333
+ #elif defined(__APPLE__)
334
+ if (pImpl->nativeWindow) {
335
+ NSWindow *nswin = (__bridge NSWindow *)pImpl->nativeWindow;
336
+ if (enabled) {
337
+ [nswin toggleFullScreen:nil];
338
+ }
339
+ }
340
+ #else
341
+ if (pImpl->nativeWindow) {
342
+ if (enabled) {
343
+ gtk_window_fullscreen(GTK_WINDOW(pImpl->nativeWindow));
344
+ } else {
345
+ gtk_window_unfullscreen(GTK_WINDOW(pImpl->nativeWindow));
346
+ }
347
+ }
348
+ #endif
349
+
350
+ if (wasFullscreen != enabled) {
351
+ for (auto &cb : pImpl->stateCallbacks)
352
+ cb(pImpl->state);
353
+ }
354
+ }
355
+
356
+ bool Window::isFullscreen() const { return pImpl->state.isFullscreen; }
357
+
358
+ void Window::minimize() {
359
+ bool wasMinimized = pImpl->state.isMinimized;
360
+ pImpl->state.isMinimized = true;
361
+ pImpl->state.isHidden = false;
362
+
363
+ #ifdef _WIN32
364
+ if (pImpl->hwnd)
365
+ ShowWindow(pImpl->hwnd, SW_MINIMIZE);
366
+ #elif defined(__APPLE__)
367
+ if (pImpl->nativeWindow) {
368
+ NSWindow *nswin = (__bridge NSWindow *)pImpl->nativeWindow;
369
+ [nswin miniaturize:nil];
370
+ }
371
+ #else
372
+ if (pImpl->nativeWindow) {
373
+ gtk_window_iconify(GTK_WINDOW(pImpl->nativeWindow));
374
+ }
375
+ #endif
376
+
377
+ if (!wasMinimized) {
378
+ for (auto &cb : pImpl->stateCallbacks)
379
+ cb(pImpl->state);
380
+ }
381
+ }
382
+
383
+ void Window::maximize() {
384
+ bool wasMaximized = pImpl->state.isMaximized;
385
+ pImpl->state.isMaximized = true;
386
+ pImpl->state.isHidden = false;
387
+
388
+ #ifdef _WIN32
389
+ if (pImpl->hwnd)
390
+ ShowWindow(pImpl->hwnd, SW_MAXIMIZE);
391
+ #elif defined(__APPLE__)
392
+ if (pImpl->nativeWindow) {
393
+ NSWindow *nswin = (__bridge NSWindow *)pImpl->nativeWindow;
394
+ [nswin zoom:nil];
395
+ }
396
+ #else
397
+ if (pImpl->nativeWindow) {
398
+ gtk_window_maximize(GTK_WINDOW(pImpl->nativeWindow));
399
+ }
400
+ #endif
401
+
402
+ if (!wasMaximized) {
403
+ for (auto &cb : pImpl->stateCallbacks)
404
+ cb(pImpl->state);
405
+ }
406
+ }
407
+
408
+ void Window::restore() {
409
+ bool wasMinimized = pImpl->state.isMinimized;
410
+ bool wasMaximized = pImpl->state.isMaximized;
411
+ pImpl->state.isMinimized = false;
412
+ pImpl->state.isMaximized = false;
413
+ pImpl->state.isHidden = false;
414
+
415
+ #ifdef _WIN32
416
+ if (pImpl->hwnd)
417
+ ShowWindow(pImpl->hwnd, SW_RESTORE);
418
+ #elif defined(__APPLE__)
419
+ if (pImpl->nativeWindow) {
420
+ NSWindow *nswin = (__bridge NSWindow *)pImpl->nativeWindow;
421
+ [nswin deminiaturize:nil];
422
+ }
423
+ #else
424
+ if (pImpl->nativeWindow) {
425
+ gtk_window_unmaximize(GTK_WINDOW(pImpl->nativeWindow));
426
+ gtk_window_deiconify(GTK_WINDOW(pImpl->nativeWindow));
427
+ }
428
+ #endif
429
+
430
+ if (wasMinimized || wasMaximized) {
431
+ for (auto &cb : pImpl->stateCallbacks)
432
+ cb(pImpl->state);
433
+ }
434
+ }
435
+
436
+ bool Window::isMaximized() const { return pImpl->state.isMaximized; }
437
+
438
+ bool Window::isMinimized() const { return pImpl->state.isMinimized; }
439
+
440
+ void Window::show() {
441
+ bool wasHidden = pImpl->state.isHidden;
442
+ pImpl->state.isVisible = true;
443
+ pImpl->state.isHidden = false;
444
+
445
+ #ifdef _WIN32
446
+ if (pImpl->hwnd)
447
+ ShowWindow(pImpl->hwnd, SW_SHOW);
448
+ #elif defined(__APPLE__)
449
+ if (pImpl->nativeWindow) {
450
+ NSWindow *nswin = (__bridge NSWindow *)pImpl->nativeWindow;
451
+ [nswin orderFront:nil];
452
+ }
453
+ #else
454
+ if (pImpl->nativeWindow) {
455
+ gtk_widget_show(GTK_WIDGET(pImpl->nativeWindow));
456
+ }
457
+ #endif
458
+
459
+ // Fire state change event if hidden state changed
460
+ if (wasHidden) {
461
+ for (auto &cb : pImpl->stateCallbacks)
462
+ cb(pImpl->state);
463
+ }
464
+ }
465
+
466
+ void Window::hide() {
467
+ bool wasVisible = pImpl->state.isVisible;
468
+ pImpl->state.isVisible = false;
469
+ pImpl->state.isHidden = true;
470
+
471
+ #ifdef _WIN32
472
+ if (pImpl->hwnd)
473
+ ShowWindow(pImpl->hwnd, SW_HIDE);
474
+ #elif defined(__APPLE__)
475
+ if (pImpl->nativeWindow) {
476
+ NSWindow *nswin = (__bridge NSWindow *)pImpl->nativeWindow;
477
+ [nswin orderOut:nil];
478
+ }
479
+ #else
480
+ if (pImpl->nativeWindow) {
481
+ gtk_widget_hide(GTK_WIDGET(pImpl->nativeWindow));
482
+ }
483
+ #endif
484
+
485
+ // Fire state change event if visibility changed
486
+ if (wasVisible) {
487
+ for (auto &cb : pImpl->stateCallbacks)
488
+ cb(pImpl->state);
489
+ }
490
+ }
491
+
492
+ bool Window::isVisible() const { return pImpl->state.isVisible; }
493
+
494
+ bool Window::isHidden() const { return pImpl->state.isHidden; }
495
+
496
+ void Window::focus() {
497
+ #ifdef _WIN32
498
+ if (pImpl->hwnd)
499
+ SetFocus(pImpl->hwnd);
500
+ #elif defined(__APPLE__)
501
+ if (pImpl->nativeWindow) {
502
+ NSWindow *nswin = (__bridge NSWindow *)pImpl->nativeWindow;
503
+ [nswin makeKeyAndOrderFront:nil];
504
+ }
505
+ #else
506
+ if (pImpl->nativeWindow) {
507
+ gtk_window_present(GTK_WINDOW(pImpl->nativeWindow));
508
+ }
509
+ #endif
510
+ }
511
+
512
+ bool Window::isFocused() const { return pImpl->state.isFocused; }
513
+
514
+ void Window::setAlwaysOnTop(bool enabled) {
515
+ pImpl->config.alwaysOnTop = enabled;
516
+ #ifdef _WIN32
517
+ if (pImpl->hwnd) {
518
+ SetWindowPos(pImpl->hwnd, enabled ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0,
519
+ 0, SWP_NOMOVE | SWP_NOSIZE);
520
+ }
521
+ #elif defined(__APPLE__)
522
+ if (pImpl->nativeWindow) {
523
+ NSWindow *nswin = (__bridge NSWindow *)pImpl->nativeWindow;
524
+ [nswin setLevel:enabled ? NSFloatingWindowLevel : NSNormalWindowLevel];
525
+ }
526
+ #else
527
+ if (pImpl->nativeWindow) {
528
+ gtk_window_set_keep_above(GTK_WINDOW(pImpl->nativeWindow), enabled);
529
+ }
530
+ #endif
531
+ }
532
+
533
+ void Window::setResizable(bool enabled) {
534
+ pImpl->config.resizable = enabled;
535
+ #ifdef _WIN32
536
+ if (pImpl->hwnd) {
537
+ SetWindowLongPtr(
538
+ pImpl->hwnd, GWL_STYLE,
539
+ enabled ? GetWindowLong(pImpl->hwnd, GWL_STYLE) | WS_THICKFRAME
540
+ : GetWindowLong(pImpl->hwnd, GWL_STYLE) & ~WS_THICKFRAME);
541
+ }
542
+ #elif defined(__APPLE__)
543
+ // Handled in create
544
+ #else
545
+ if (pImpl->nativeWindow) {
546
+ gtk_window_set_resizable(GTK_WINDOW(pImpl->nativeWindow), enabled);
547
+ }
548
+ #endif
549
+ }
550
+
551
+ void Window::setDecorations(bool enabled) {
552
+ #ifdef _WIN32
553
+ if (pImpl->hwnd) {
554
+ SetWindowLongPtr(pImpl->hwnd, GWL_STYLE,
555
+ enabled
556
+ ? GetWindowLong(pImpl->hwnd, GWL_STYLE) | WS_CAPTION
557
+ : GetWindowLong(pImpl->hwnd, GWL_STYLE) & ~WS_CAPTION);
558
+ }
559
+ #elif defined(__APPLE__)
560
+ if (pImpl->nativeWindow) {
561
+ NSWindow *nswin = (__bridge NSWindow *)pImpl->nativeWindow;
562
+ [nswin setStyleMask:enabled ? [nswin styleMask] | NSWindowStyleMaskTitled
563
+ : [nswin styleMask] & ~NSWindowStyleMaskTitled];
564
+ }
565
+ #else
566
+ if (pImpl->nativeWindow) {
567
+ // GTK handles this differently
568
+ }
569
+ #endif
570
+ }
571
+
572
+ void Window::setSkipTaskbar(bool enabled) {
573
+ #ifdef _WIN32
574
+ if (pImpl->hwnd) {
575
+ SetWindowLongPtr(
576
+ pImpl->hwnd, GWL_EXSTYLE,
577
+ enabled ? GetWindowLong(pImpl->hwnd, GWL_EXSTYLE) | WS_EX_TOOLWINDOW
578
+ : GetWindowLong(pImpl->hwnd, GWL_EXSTYLE) & ~WS_EX_TOOLWINDOW);
579
+ }
580
+ #endif
581
+ }
582
+
583
+ void Window::setOpacity(double opacity) {
584
+ pImpl->config.opacity = opacity;
585
+ #ifdef _WIN32
586
+ if (pImpl->hwnd) {
587
+ BYTE alpha = static_cast<BYTE>(opacity * 255);
588
+ SetWindowLongPtr(pImpl->hwnd, GWL_EXSTYLE,
589
+ GetWindowLong(pImpl->hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
590
+ SetLayeredWindowAttributes(pImpl->hwnd, 0, alpha, LWA_ALPHA);
591
+ }
592
+ #endif
593
+ }
594
+
595
+ void Window::setIconFromMemory(const unsigned char *data, size_t size) {
596
+ #ifdef _WIN32
597
+ if (pImpl->hwnd) {
598
+ int width, height, channels;
599
+ unsigned char *pixels =
600
+ stbi_load_from_memory(data, (int)size, &width, &height, &channels, 4);
601
+ if (pixels) {
602
+ BITMAPINFO bmi = {};
603
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
604
+ bmi.bmiHeader.biWidth = width;
605
+ bmi.bmiHeader.biHeight = -height;
606
+ bmi.bmiHeader.biPlanes = 1;
607
+ bmi.bmiHeader.biBitCount = 32;
608
+ bmi.bmiHeader.biCompression = BI_RGB;
609
+
610
+ void *bits = nullptr;
611
+ HBITMAP hbm =
612
+ CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, &bits, nullptr, 0);
613
+ if (hbm && bits) {
614
+ // Convert RGBA to BGRA
615
+ for (int i = 0; i < width * height; ++i) {
616
+ ((uint8_t *)bits)[i * 4 + 0] = pixels[i * 4 + 2];
617
+ ((uint8_t *)bits)[i * 4 + 1] = pixels[i * 4 + 1];
618
+ ((uint8_t *)bits)[i * 4 + 2] = pixels[i * 4 + 0];
619
+ ((uint8_t *)bits)[i * 4 + 3] = pixels[i * 4 + 3];
620
+ }
621
+
622
+ ICONINFO ii = {};
623
+ ii.fIcon = TRUE;
624
+ ii.hbmColor = hbm;
625
+ ii.hbmMask = hbm;
626
+
627
+ HICON hIcon = CreateIconIndirect(&ii);
628
+ if (hIcon) {
629
+ SendMessage(pImpl->hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
630
+ SendMessage(pImpl->hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
631
+ }
632
+ DeleteObject(hbm);
633
+ }
634
+ stbi_image_free(pixels);
635
+ }
636
+ }
637
+ #endif
638
+ }
639
+
640
+ WindowState Window::getState() const { return pImpl->state; }
641
+
642
+ void Window::onMove(MoveCallback callback) {
643
+ pImpl->moveCallbacks.push_back(callback);
644
+ }
645
+
646
+ void Window::onResize(ResizeCallback callback) {
647
+ pImpl->resizeCallbacks.push_back(callback);
648
+ }
649
+
650
+ void Window::onClose(CloseCallback callback) {
651
+ pImpl->closeCallbacks.push_back(callback);
652
+ }
653
+
654
+ void Window::onFocus(FocusCallback callback) {
655
+ pImpl->focusCallbacks.push_back(callback);
656
+ }
657
+
658
+ void Window::onStateChange(StateCallback callback) {
659
+ pImpl->stateCallbacks.push_back(callback);
660
+ }
661
+
662
+ void *Window::nativeHandle() const { return pImpl->nativeWindow; }
663
+
664
+ } // namespace plusui