plusui-native-core 0.1.4 → 0.1.7

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 +939 -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,481 @@
1
+ #include <iostream>
2
+ #include <plusui/menu.hpp>
3
+ #include <plusui/webview.hpp>
4
+ #include <sstream>
5
+
6
+ #ifdef _WIN32
7
+ #include <windows.h>
8
+ #endif
9
+
10
+ namespace plusui {
11
+ namespace bindings {
12
+
13
+ // JSON parsing helpers (simple implementation)
14
+ static std::string trim(const std::string &str) {
15
+ size_t first = str.find_first_not_of(" \t\n\r");
16
+ if (first == std::string::npos)
17
+ return "";
18
+ size_t last = str.find_last_not_of(" \t\n\r");
19
+ return str.substr(first, last - first + 1);
20
+ }
21
+
22
+ static std::string extractJsonString(const std::string &json,
23
+ const std::string &key) {
24
+ std::string searchKey = "\"" + key + "\"";
25
+ size_t pos = json.find(searchKey);
26
+ if (pos == std::string::npos)
27
+ return "";
28
+
29
+ pos = json.find(":", pos);
30
+ if (pos == std::string::npos)
31
+ return "";
32
+
33
+ pos = json.find("\"", pos);
34
+ if (pos == std::string::npos)
35
+ return "";
36
+
37
+ size_t start = pos + 1;
38
+ size_t end = json.find("\"", start);
39
+ if (end == std::string::npos)
40
+ return "";
41
+
42
+ return json.substr(start, end - start);
43
+ }
44
+
45
+ static bool extractJsonBool(const std::string &json, const std::string &key,
46
+ bool defaultVal = false) {
47
+ std::string searchKey = "\"" + key + "\"";
48
+ size_t pos = json.find(searchKey);
49
+ if (pos == std::string::npos)
50
+ return defaultVal;
51
+
52
+ pos = json.find(":", pos);
53
+ if (pos == std::string::npos)
54
+ return defaultVal;
55
+
56
+ std::string rest = json.substr(pos + 1);
57
+ rest = trim(rest);
58
+
59
+ if (rest.find("true") == 0)
60
+ return true;
61
+ if (rest.find("false") == 0)
62
+ return false;
63
+ return defaultVal;
64
+ }
65
+
66
+ void MenuBindings::registerBindings(WebView &webview) {
67
+ // Binding: menu.create
68
+ webview.bind("plusui_menu_create",
69
+ [this](const std::string &args) -> std::string {
70
+ auto items = parseMenuItems(args);
71
+ std::string menuId = MenuManager::instance().createMenu(items);
72
+ return "\"" + menuId + "\"";
73
+ });
74
+
75
+ // Binding: menu.popup
76
+ webview.bind("plusui_menu_popup", [](const std::string &args) -> std::string {
77
+ // Parse: [menuId, x, y]
78
+ std::string menuId = extractJsonString(args, "menuId");
79
+
80
+ // Get x, y from args (simplified parsing)
81
+ int x = 0, y = 0;
82
+ size_t xPos = args.find("\"x\"");
83
+ size_t yPos = args.find("\"y\"");
84
+
85
+ if (xPos != std::string::npos) {
86
+ size_t colonPos = args.find(":", xPos);
87
+ if (colonPos != std::string::npos) {
88
+ x = std::stoi(args.substr(colonPos + 1));
89
+ }
90
+ }
91
+ if (yPos != std::string::npos) {
92
+ size_t colonPos = args.find(":", yPos);
93
+ if (colonPos != std::string::npos) {
94
+ y = std::stoi(args.substr(colonPos + 1));
95
+ }
96
+ }
97
+
98
+ Menu *menu = MenuManager::instance().getMenu(menuId);
99
+ if (menu) {
100
+ menu->popup(x, y);
101
+ }
102
+ return "null";
103
+ });
104
+
105
+ // Binding: menu.setApplicationMenu
106
+ webview.bind("plusui_menu_setAppMenu",
107
+ [this](const std::string &args) -> std::string {
108
+ auto items = parseMenuItems(args);
109
+ MenuBarData data;
110
+ data.items = items;
111
+ MenuBar::instance().setMenu(data);
112
+ return "null";
113
+ });
114
+
115
+ // Binding: menu.destroy
116
+ webview.bind("plusui_menu_destroy",
117
+ [](const std::string &args) -> std::string {
118
+ std::string menuId = extractJsonString(args, "menuId");
119
+ MenuManager::instance().destroyMenu(menuId);
120
+ return "null";
121
+ });
122
+
123
+ // Binding: menu.close
124
+ webview.bind("plusui_menu_close", [](const std::string &args) -> std::string {
125
+ std::string menuId = extractJsonString(args, "menuId");
126
+ Menu *menu = MenuManager::instance().getMenu(menuId);
127
+ if (menu) {
128
+ menu->close();
129
+ }
130
+ return "null";
131
+ });
132
+
133
+ // Inject JavaScript bridge for context menu handling
134
+ webview.eval(R"(
135
+ // PlusUI Context Menu Bridge
136
+ window.__plusui_contextMenu = {
137
+ enabled: false,
138
+ items: [],
139
+
140
+ enable: function(items) {
141
+ this.enabled = true;
142
+ this.items = items || [];
143
+ },
144
+
145
+ disable: function() {
146
+ this.enabled = false;
147
+ },
148
+
149
+ setItems: function(items) {
150
+ this.items = items;
151
+ }
152
+ };
153
+
154
+ // Override right-click if custom context menu is enabled
155
+ document.addEventListener('contextmenu', function(e) {
156
+ if (window.__plusui_contextMenu.enabled) {
157
+ e.preventDefault();
158
+
159
+ // Get element info for context-aware menus
160
+ const target = e.target;
161
+ const selector = target.tagName.toLowerCase() +
162
+ (target.id ? '#' + target.id : '') +
163
+ (target.className ? '.' + target.className.split(' ').join('.') : '');
164
+
165
+ // Notify C++ to show native context menu
166
+ if (typeof plusui_menu_showContext === 'function') {
167
+ plusui_menu_showContext(JSON.stringify({
168
+ x: e.screenX,
169
+ y: e.screenY,
170
+ clientX: e.clientX,
171
+ clientY: e.clientY,
172
+ selector: selector,
173
+ tagName: target.tagName,
174
+ isEditable: target.isContentEditable ||
175
+ target.tagName === 'INPUT' ||
176
+ target.tagName === 'TEXTAREA',
177
+ hasSelection: window.getSelection().toString().length > 0
178
+ }));
179
+ }
180
+
181
+ return false;
182
+ }
183
+ });
184
+ )");
185
+
186
+ // Binding: Show context menu
187
+ webview.bind("plusui_menu_showContext",
188
+ [this](const std::string &args) -> std::string {
189
+ if (!contextMenuEnabled_ || contextMenuItems_.empty()) {
190
+ return "null";
191
+ }
192
+
193
+ int x = 0, y = 0;
194
+ size_t xPos = args.find("\"x\"");
195
+ size_t yPos = args.find("\"y\"");
196
+
197
+ if (xPos != std::string::npos) {
198
+ size_t colonPos = args.find(":", xPos);
199
+ if (colonPos != std::string::npos) {
200
+ x = std::stoi(args.substr(colonPos + 1));
201
+ }
202
+ }
203
+ if (yPos != std::string::npos) {
204
+ size_t colonPos = args.find(":", yPos);
205
+ if (colonPos != std::string::npos) {
206
+ y = std::stoi(args.substr(colonPos + 1));
207
+ }
208
+ }
209
+
210
+ // Create and show context menu
211
+ if (currentContextMenuId_.empty()) {
212
+ currentContextMenuId_ =
213
+ MenuManager::instance().createMenu(contextMenuItems_);
214
+ }
215
+
216
+ Menu *menu =
217
+ MenuManager::instance().getMenu(currentContextMenuId_);
218
+ if (menu) {
219
+ menu->popup(x, y);
220
+ }
221
+
222
+ return "null";
223
+ });
224
+
225
+ // Binding: Enable custom context menu
226
+ webview.bind("plusui_menu_enableContext",
227
+ [this, &webview](const std::string &args) -> std::string {
228
+ contextMenuEnabled_ = true;
229
+ webview.eval("window.__plusui_contextMenu.enabled = true;");
230
+ return "null";
231
+ });
232
+
233
+ // Binding: Disable custom context menu
234
+ webview.bind("plusui_menu_disableContext",
235
+ [this, &webview](const std::string &args) -> std::string {
236
+ contextMenuEnabled_ = false;
237
+ webview.eval("window.__plusui_contextMenu.enabled = false;");
238
+ return "null";
239
+ });
240
+ }
241
+
242
+ void MenuBindings::setItemClickCallback(
243
+ std::function<void(const std::string &, const std::string &)> callback) {
244
+ itemClickCallback_ = callback;
245
+ }
246
+
247
+ void MenuBindings::setContextMenu(const std::vector<MenuItem> &items) {
248
+ contextMenuItems_ = items;
249
+
250
+ // Destroy old context menu if exists
251
+ if (!currentContextMenuId_.empty()) {
252
+ MenuManager::instance().destroyMenu(currentContextMenuId_);
253
+ currentContextMenuId_.clear();
254
+ }
255
+ }
256
+
257
+ void MenuBindings::enableContextMenu(bool enable) {
258
+ contextMenuEnabled_ = enable;
259
+ }
260
+
261
+ void MenuBindings::showContextMenuAtCursor() {
262
+ #ifdef _WIN32
263
+ POINT pt;
264
+ GetCursorPos(&pt);
265
+
266
+ if (!currentContextMenuId_.empty()) {
267
+ Menu *menu = MenuManager::instance().getMenu(currentContextMenuId_);
268
+ if (menu) {
269
+ menu->popup(pt.x, pt.y);
270
+ }
271
+ }
272
+ #endif
273
+ }
274
+
275
+ std::vector<MenuItem> MenuBindings::parseMenuItems(const std::string &json) {
276
+ std::vector<MenuItem> items;
277
+
278
+ // Simple JSON array parsing
279
+ // Format: [{"id":"...", "label":"...", ...}, ...]
280
+ size_t pos = json.find('[');
281
+ if (pos == std::string::npos)
282
+ return items;
283
+
284
+ size_t end = json.rfind(']');
285
+ if (end == std::string::npos)
286
+ return items;
287
+
288
+ std::string content = json.substr(pos + 1, end - pos - 1);
289
+
290
+ // Find each object
291
+ int braceCount = 0;
292
+ size_t objStart = std::string::npos;
293
+
294
+ for (size_t i = 0; i < content.size(); i++) {
295
+ char c = content[i];
296
+ if (c == '{') {
297
+ if (braceCount == 0) {
298
+ objStart = i;
299
+ }
300
+ braceCount++;
301
+ } else if (c == '}') {
302
+ braceCount--;
303
+ if (braceCount == 0 && objStart != std::string::npos) {
304
+ std::string objStr = content.substr(objStart, i - objStart + 1);
305
+
306
+ MenuItem item;
307
+ item.id = extractJsonString(objStr, "id");
308
+ item.label = extractJsonString(objStr, "label");
309
+ item.accelerator = extractJsonString(objStr, "accelerator");
310
+ item.icon = extractJsonString(objStr, "icon");
311
+ item.enabled = extractJsonBool(objStr, "enabled", true);
312
+ item.checked = extractJsonBool(objStr, "checked", false);
313
+
314
+ std::string typeStr = extractJsonString(objStr, "type");
315
+ if (typeStr == "separator") {
316
+ item.type = MenuItemType::Separator;
317
+ } else if (typeStr == "checkbox") {
318
+ item.type = MenuItemType::Checkbox;
319
+ } else if (typeStr == "radio") {
320
+ item.type = MenuItemType::Radio;
321
+ } else if (typeStr == "submenu") {
322
+ item.type = MenuItemType::Submenu;
323
+ } else {
324
+ item.type = MenuItemType::Normal;
325
+ }
326
+
327
+ // Parse submenu recursively
328
+ size_t subPos = objStr.find("\"submenu\"");
329
+ if (subPos != std::string::npos) {
330
+ size_t subStart = objStr.find('[', subPos);
331
+ if (subStart != std::string::npos) {
332
+ int bracketCount = 1;
333
+ size_t subEnd = subStart + 1;
334
+ while (subEnd < objStr.size() && bracketCount > 0) {
335
+ if (objStr[subEnd] == '[')
336
+ bracketCount++;
337
+ else if (objStr[subEnd] == ']')
338
+ bracketCount--;
339
+ subEnd++;
340
+ }
341
+ std::string subJson = objStr.substr(subStart, subEnd - subStart);
342
+ item.submenu = parseMenuItems(subJson);
343
+ }
344
+ }
345
+
346
+ items.push_back(item);
347
+ objStart = std::string::npos;
348
+ }
349
+ }
350
+ }
351
+
352
+ return items;
353
+ }
354
+
355
+ std::string MenuBindings::menuItemToJson(const MenuItem &item) {
356
+ std::ostringstream ss;
357
+ ss << "{";
358
+ ss << "\"id\":\"" << item.id << "\",";
359
+ ss << "\"label\":\"" << item.label << "\",";
360
+ ss << "\"enabled\":" << (item.enabled ? "true" : "false") << ",";
361
+ ss << "\"checked\":" << (item.checked ? "true" : "false");
362
+
363
+ if (!item.accelerator.empty()) {
364
+ ss << ",\"accelerator\":\"" << item.accelerator << "\"";
365
+ }
366
+ if (!item.icon.empty()) {
367
+ ss << ",\"icon\":\"" << item.icon << "\"";
368
+ }
369
+
370
+ std::string typeStr;
371
+ switch (item.type) {
372
+ case MenuItemType::Separator:
373
+ typeStr = "separator";
374
+ break;
375
+ case MenuItemType::Checkbox:
376
+ typeStr = "checkbox";
377
+ break;
378
+ case MenuItemType::Radio:
379
+ typeStr = "radio";
380
+ break;
381
+ case MenuItemType::Submenu:
382
+ typeStr = "submenu";
383
+ break;
384
+ default:
385
+ typeStr = "normal";
386
+ break;
387
+ }
388
+ ss << ",\"type\":\"" << typeStr << "\"";
389
+
390
+ if (!item.submenu.empty()) {
391
+ ss << ",\"submenu\":[";
392
+ for (size_t i = 0; i < item.submenu.size(); i++) {
393
+ if (i > 0)
394
+ ss << ",";
395
+ ss << menuItemToJson(item.submenu[i]);
396
+ }
397
+ ss << "]";
398
+ }
399
+
400
+ ss << "}";
401
+ return ss.str();
402
+ }
403
+
404
+ // ContextMenuManager implementation
405
+
406
+ ContextMenuManager &ContextMenuManager::instance() {
407
+ static ContextMenuManager inst;
408
+ return inst;
409
+ }
410
+
411
+ void ContextMenuManager::setDefaultMenu(const std::vector<MenuItem> &items) {
412
+ defaultMenu_ = items;
413
+ }
414
+
415
+ void ContextMenuManager::setMenuForSelector(
416
+ const std::string &selector, const std::vector<MenuItem> &items) {
417
+ selectorMenus_[selector] = items;
418
+ }
419
+
420
+ void ContextMenuManager::attachToWebView(WebView &webview) {
421
+ // Inject context menu override script
422
+ webview.eval(R"(
423
+ (function() {
424
+ document.addEventListener('contextmenu', function(e) {
425
+ // Check if we should use custom context menu
426
+ if (window.__plusui_useCustomContext) {
427
+ e.preventDefault();
428
+
429
+ const info = {
430
+ x: e.screenX,
431
+ y: e.screenY,
432
+ target: e.target.tagName,
433
+ id: e.target.id,
434
+ className: e.target.className,
435
+ isInput: e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA',
436
+ isEditable: e.target.isContentEditable,
437
+ selectedText: window.getSelection().toString()
438
+ };
439
+
440
+ if (window.plusui && window.plusui.menu) {
441
+ window.plusui.menu._showContextMenu(info);
442
+ }
443
+ }
444
+ });
445
+ })();
446
+ )");
447
+ }
448
+
449
+ void ContextMenuManager::handleRightClick(int x, int y,
450
+ const std::string &targetSelector) {
451
+ // Find the appropriate menu for this selector
452
+ std::vector<MenuItem> *menuItems = &defaultMenu_;
453
+
454
+ for (const auto &pair : selectorMenus_) {
455
+ if (targetSelector.find(pair.first) != std::string::npos) {
456
+ menuItems = const_cast<std::vector<MenuItem> *>(&pair.second);
457
+ break;
458
+ }
459
+ }
460
+
461
+ if (menuItems->empty())
462
+ return;
463
+
464
+ // Create and show menu
465
+ std::string menuId = MenuManager::instance().createMenu(*menuItems);
466
+ Menu *menu = MenuManager::instance().getMenu(menuId);
467
+ if (menu) {
468
+ menu->popup(x, y);
469
+ }
470
+
471
+ // Cleanup after menu closes (delayed)
472
+ // In production, this should be handled by menu close event
473
+ }
474
+
475
+ void ContextMenuManager::onItemClick(
476
+ std::function<void(const std::string &, const std::string &)> callback) {
477
+ clickCallback_ = callback;
478
+ }
479
+
480
+ } // namespace bindings
481
+ } // namespace plusui