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.
- package/Core/CMakeLists.txt +190 -7
- package/Core/Features/App/app.cpp +129 -0
- package/Core/Features/App/app.ts +126 -0
- package/Core/Features/Browser/browser.cpp +181 -0
- package/Core/Features/Browser/browser.ts +182 -0
- package/Core/Features/Clipboard/clipboard.cpp +234 -0
- package/Core/Features/Clipboard/clipboard.ts +113 -0
- package/Core/Features/Display/display.cpp +209 -0
- package/Core/Features/Display/display.ts +104 -0
- package/Core/Features/Event/Events.ts +166 -0
- package/Core/Features/Event/events.cpp +200 -0
- package/Core/Features/Keyboard/keyboard.cpp +186 -0
- package/Core/Features/Keyboard/keyboard.ts +175 -0
- package/Core/Features/Menu/context-menu.css +293 -0
- package/Core/Features/Menu/menu.cpp +481 -0
- package/Core/Features/Menu/menu.ts +439 -0
- package/Core/Features/Tray/tray.cpp +310 -0
- package/Core/Features/Tray/tray.ts +68 -0
- package/Core/Features/WebGPU/webgpu.cpp +937 -0
- package/Core/Features/WebGPU/webgpu.ts +1013 -0
- package/Core/Features/WebView/webview.cpp +1052 -0
- package/Core/Features/WebView/webview.ts +510 -0
- package/Core/Features/Window/window.cpp +664 -0
- package/Core/Features/Window/window.ts +142 -0
- package/Core/Features/WindowManager/window_manager.cpp +341 -0
- package/Core/include/plusui/app.hpp +73 -0
- package/Core/include/plusui/browser.hpp +66 -0
- package/Core/include/plusui/clipboard.hpp +41 -0
- package/Core/include/plusui/events.hpp +58 -0
- package/Core/include/{keyboard.hpp → plusui/keyboard.hpp} +21 -44
- package/Core/include/plusui/menu.hpp +153 -0
- package/Core/include/plusui/tray.hpp +93 -0
- package/Core/include/plusui/webgpu.hpp +434 -0
- package/Core/include/plusui/webview.hpp +142 -0
- package/Core/include/plusui/window.hpp +111 -0
- package/Core/include/plusui/window_manager.hpp +57 -0
- package/Core/vendor/WebView2EnvironmentOptions.h +406 -0
- package/Core/vendor/stb_image.h +7988 -0
- package/Core/vendor/webview.h +618 -510
- package/Core/vendor/webview2.h +52079 -0
- package/README.md +19 -0
- package/package.json +12 -15
- package/Core/include/app.hpp +0 -121
- package/Core/include/menu.hpp +0 -79
- package/Core/include/tray.hpp +0 -81
- package/Core/include/window.hpp +0 -106
- package/Core/src/app.cpp +0 -311
- package/Core/src/display.cpp +0 -424
- package/Core/src/tray.cpp +0 -275
- package/Core/src/window.cpp +0 -528
- package/dist/index.d.ts +0 -205
- package/dist/index.js +0 -198
- package/src/index.ts +0 -574
- /package/Core/include/{display.hpp → plusui/display.hpp} +0 -0
|
@@ -0,0 +1,1052 @@
|
|
|
1
|
+
#include <fstream>
|
|
2
|
+
#include <iostream>
|
|
3
|
+
#include <map>
|
|
4
|
+
#include <plusui/tray.hpp>
|
|
5
|
+
#include <plusui/webgpu.hpp>
|
|
6
|
+
#include <plusui/webview.hpp>
|
|
7
|
+
#include <plusui/window.hpp>
|
|
8
|
+
#include <plusui/window_manager.hpp>
|
|
9
|
+
#include <regex>
|
|
10
|
+
#include <sstream>
|
|
11
|
+
|
|
12
|
+
#ifdef _WIN32
|
|
13
|
+
#include <WebView2.h>
|
|
14
|
+
#include <windows.h>
|
|
15
|
+
#include <wrl.h>
|
|
16
|
+
using namespace Microsoft::WRL;
|
|
17
|
+
#elif defined(__APPLE__)
|
|
18
|
+
#import <WebKit/WebKit.h>
|
|
19
|
+
#import <objc/objc-runtime.h>
|
|
20
|
+
#else
|
|
21
|
+
#include <webkit2/webkit2.h>
|
|
22
|
+
#endif
|
|
23
|
+
|
|
24
|
+
namespace plusui {
|
|
25
|
+
|
|
26
|
+
struct WebView::Impl {
|
|
27
|
+
void *nativeWebView = nullptr;
|
|
28
|
+
WebViewConfig config;
|
|
29
|
+
std::string currentURL;
|
|
30
|
+
std::string currentTitle;
|
|
31
|
+
bool loading = false;
|
|
32
|
+
double zoom = 1.0;
|
|
33
|
+
bool ready = false;
|
|
34
|
+
std::vector<std::string> pendingScripts;
|
|
35
|
+
std::string pendingNavigation;
|
|
36
|
+
std::string pendingHTML;
|
|
37
|
+
std::string pendingFile;
|
|
38
|
+
|
|
39
|
+
std::map<std::string, JSCallback> bindings;
|
|
40
|
+
|
|
41
|
+
std::unique_ptr<TrayManager> trayManager;
|
|
42
|
+
std::unique_ptr<WindowManager> windowManager;
|
|
43
|
+
std::shared_ptr<Window> window; // Keep the window alive
|
|
44
|
+
std::map<std::string, std::function<void(const std::string &)>> events;
|
|
45
|
+
WebGPU webgpu; // WebGPU support
|
|
46
|
+
|
|
47
|
+
NavigationCallback navigationCallback;
|
|
48
|
+
LoadCallback loadStartCallback;
|
|
49
|
+
LoadCallback loadEndCallback;
|
|
50
|
+
LoadCallback navigationCompleteCallback;
|
|
51
|
+
ErrorCallback errorCallback;
|
|
52
|
+
ConsoleCallback consoleCallback;
|
|
53
|
+
|
|
54
|
+
#ifdef _WIN32
|
|
55
|
+
ComPtr<ICoreWebView2Controller> controller;
|
|
56
|
+
ComPtr<ICoreWebView2> webview;
|
|
57
|
+
#elif defined(__APPLE__)
|
|
58
|
+
WKWebView *wkWebView = nullptr;
|
|
59
|
+
#else
|
|
60
|
+
WebKitWebView *gtkWebView = nullptr;
|
|
61
|
+
#endif
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
WebView::WebView() : pImpl(std::shared_ptr<Impl>(new Impl())) {}
|
|
65
|
+
|
|
66
|
+
WebView::~WebView() = default;
|
|
67
|
+
|
|
68
|
+
WebView::WebView(WebView &&other) noexcept = default;
|
|
69
|
+
WebView &WebView::operator=(WebView &&other) noexcept = default;
|
|
70
|
+
|
|
71
|
+
WebView WebView::create(void *windowHandle, const WebViewConfig &config) {
|
|
72
|
+
WebView wv;
|
|
73
|
+
wv.pImpl->config = config;
|
|
74
|
+
|
|
75
|
+
#ifdef _WIN32
|
|
76
|
+
HWND hwnd = static_cast<HWND>(windowHandle);
|
|
77
|
+
|
|
78
|
+
// Create WebView2 environment and controller
|
|
79
|
+
auto pImpl = wv.pImpl;
|
|
80
|
+
CreateCoreWebView2EnvironmentWithOptions(
|
|
81
|
+
nullptr, nullptr, nullptr,
|
|
82
|
+
Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
|
|
83
|
+
[hwnd, pImpl](HRESULT result,
|
|
84
|
+
ICoreWebView2Environment *env) -> HRESULT {
|
|
85
|
+
if (FAILED(result) || !env)
|
|
86
|
+
return result;
|
|
87
|
+
env->CreateCoreWebView2Controller(
|
|
88
|
+
hwnd,
|
|
89
|
+
Callback<
|
|
90
|
+
ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
|
|
91
|
+
[pImpl](HRESULT result,
|
|
92
|
+
ICoreWebView2Controller *controller) -> HRESULT {
|
|
93
|
+
if (controller != nullptr) {
|
|
94
|
+
pImpl->controller = controller;
|
|
95
|
+
controller->get_CoreWebView2(&pImpl->webview);
|
|
96
|
+
|
|
97
|
+
RECT bounds;
|
|
98
|
+
HWND parentHwnd;
|
|
99
|
+
controller->get_ParentWindow(&parentHwnd);
|
|
100
|
+
GetClientRect(parentHwnd, &bounds);
|
|
101
|
+
controller->put_Bounds(bounds);
|
|
102
|
+
|
|
103
|
+
pImpl->nativeWebView = pImpl->webview.Get();
|
|
104
|
+
pImpl->ready = true;
|
|
105
|
+
|
|
106
|
+
// Inject bridge script that runs on EVERY document
|
|
107
|
+
// (survives navigation)
|
|
108
|
+
std::string bridgeScript = R"(
|
|
109
|
+
window.__native_invoke__ = function(request) {
|
|
110
|
+
if (window.chrome && window.chrome.webview) {
|
|
111
|
+
window.chrome.webview.postMessage(request);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
)";
|
|
115
|
+
pImpl->webview->AddScriptToExecuteOnDocumentCreated(
|
|
116
|
+
std::wstring(bridgeScript.begin(),
|
|
117
|
+
bridgeScript.end())
|
|
118
|
+
.c_str(),
|
|
119
|
+
nullptr);
|
|
120
|
+
|
|
121
|
+
// Inject scrollbar hiding CSS if disabled
|
|
122
|
+
if (!pImpl->config.scrollbars) {
|
|
123
|
+
std::string scrollbarScript = R"(
|
|
124
|
+
(function() {
|
|
125
|
+
var css = 'html, body { overflow: hidden !important; margin: 0 !important; padding: 0 !important; } ::-webkit-scrollbar { display: none !important; width: 0 !important; height: 0 !important; }';
|
|
126
|
+
var style = document.createElement('style');
|
|
127
|
+
style.type = 'text/css';
|
|
128
|
+
style.appendChild(document.createTextNode(css));
|
|
129
|
+
(document.head || document.documentElement).appendChild(style);
|
|
130
|
+
})();
|
|
131
|
+
)";
|
|
132
|
+
pImpl->webview->AddScriptToExecuteOnDocumentCreated(
|
|
133
|
+
std::wstring(scrollbarScript.begin(),
|
|
134
|
+
scrollbarScript.end())
|
|
135
|
+
.c_str(),
|
|
136
|
+
nullptr);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Set up WebMessageReceived handler for JS->C++ bridge
|
|
140
|
+
pImpl->webview->add_WebMessageReceived(
|
|
141
|
+
Callback<
|
|
142
|
+
ICoreWebView2WebMessageReceivedEventHandler>(
|
|
143
|
+
[pImpl](ICoreWebView2 *sender,
|
|
144
|
+
ICoreWebView2WebMessageReceivedEventArgs
|
|
145
|
+
*args) -> HRESULT {
|
|
146
|
+
LPWSTR message = nullptr;
|
|
147
|
+
args->TryGetWebMessageAsString(&message);
|
|
148
|
+
if (!message)
|
|
149
|
+
return S_OK;
|
|
150
|
+
std::wstring wmsg(message);
|
|
151
|
+
std::string msg(wmsg.begin(), wmsg.end());
|
|
152
|
+
CoTaskMemFree(message);
|
|
153
|
+
|
|
154
|
+
// Debug: log received message
|
|
155
|
+
std::cout << "[PlusUI] Received: " << msg
|
|
156
|
+
<< std::endl;
|
|
157
|
+
|
|
158
|
+
// Parse JSON-RPC: {"id":"...",
|
|
159
|
+
// "method":"window.minimize", "params":[...]}
|
|
160
|
+
std::string id, method;
|
|
161
|
+
std::string result = "null";
|
|
162
|
+
bool success = false;
|
|
163
|
+
|
|
164
|
+
// Simple JSON parsing
|
|
165
|
+
auto getId = [&msg]() {
|
|
166
|
+
size_t pos = msg.find("\"id\"");
|
|
167
|
+
if (pos == std::string::npos)
|
|
168
|
+
return std::string();
|
|
169
|
+
pos = msg.find(":", pos);
|
|
170
|
+
if (pos == std::string::npos)
|
|
171
|
+
return std::string();
|
|
172
|
+
size_t start = msg.find("\"", pos);
|
|
173
|
+
if (start == std::string::npos)
|
|
174
|
+
return std::string();
|
|
175
|
+
size_t end = msg.find("\"", start + 1);
|
|
176
|
+
if (end == std::string::npos)
|
|
177
|
+
return std::string();
|
|
178
|
+
return msg.substr(start + 1,
|
|
179
|
+
end - start - 1);
|
|
180
|
+
};
|
|
181
|
+
auto getMethod = [&msg]() {
|
|
182
|
+
size_t pos = msg.find("\"method\"");
|
|
183
|
+
if (pos == std::string::npos)
|
|
184
|
+
return std::string();
|
|
185
|
+
pos = msg.find(":", pos);
|
|
186
|
+
if (pos == std::string::npos)
|
|
187
|
+
return std::string();
|
|
188
|
+
size_t start = msg.find("\"", pos);
|
|
189
|
+
if (start == std::string::npos)
|
|
190
|
+
return std::string();
|
|
191
|
+
size_t end = msg.find("\"", start + 1);
|
|
192
|
+
if (end == std::string::npos)
|
|
193
|
+
return std::string();
|
|
194
|
+
return msg.substr(start + 1,
|
|
195
|
+
end - start - 1);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
id = getId();
|
|
199
|
+
method = getMethod();
|
|
200
|
+
|
|
201
|
+
// Route to handlers
|
|
202
|
+
if (method.find("window.") == 0) {
|
|
203
|
+
std::string winMethod = method.substr(7);
|
|
204
|
+
if (winMethod == "minimize") {
|
|
205
|
+
pImpl->windowManager->minimize();
|
|
206
|
+
success = true;
|
|
207
|
+
} else if (winMethod == "maximize") {
|
|
208
|
+
pImpl->windowManager->maximize();
|
|
209
|
+
success = true;
|
|
210
|
+
} else if (winMethod == "restore") {
|
|
211
|
+
pImpl->windowManager->restore();
|
|
212
|
+
success = true;
|
|
213
|
+
} else if (winMethod == "close") {
|
|
214
|
+
pImpl->windowManager->close();
|
|
215
|
+
success = true;
|
|
216
|
+
} else if (winMethod == "show") {
|
|
217
|
+
pImpl->windowManager->show();
|
|
218
|
+
success = true;
|
|
219
|
+
} else if (winMethod == "hide") {
|
|
220
|
+
pImpl->windowManager->hide();
|
|
221
|
+
success = true;
|
|
222
|
+
} else if (winMethod == "getSize") {
|
|
223
|
+
auto size =
|
|
224
|
+
pImpl->windowManager->getSize();
|
|
225
|
+
result = "{\"width\":" +
|
|
226
|
+
std::to_string(size.width) +
|
|
227
|
+
",\"height\":" +
|
|
228
|
+
std::to_string(size.height) +
|
|
229
|
+
"}";
|
|
230
|
+
success = true;
|
|
231
|
+
} else if (winMethod == "getPosition") {
|
|
232
|
+
auto pos =
|
|
233
|
+
pImpl->windowManager->getPosition();
|
|
234
|
+
result =
|
|
235
|
+
"{\"x\":" + std::to_string(pos.x) +
|
|
236
|
+
",\"y\":" + std::to_string(pos.y) +
|
|
237
|
+
"}";
|
|
238
|
+
success = true;
|
|
239
|
+
} else if (winMethod == "setSize") {
|
|
240
|
+
// Parse params [width, height]
|
|
241
|
+
size_t p1 = msg.find("[");
|
|
242
|
+
size_t p2 = msg.find("]");
|
|
243
|
+
if (p1 != std::string::npos &&
|
|
244
|
+
p2 != std::string::npos) {
|
|
245
|
+
std::string params =
|
|
246
|
+
msg.substr(p1 + 1, p2 - p1 - 1);
|
|
247
|
+
int w = 0, h = 0;
|
|
248
|
+
sscanf(params.c_str(), "%d, %d", &w,
|
|
249
|
+
&h);
|
|
250
|
+
pImpl->windowManager->setSize(w, h);
|
|
251
|
+
}
|
|
252
|
+
success = true;
|
|
253
|
+
} else if (winMethod == "setPosition") {
|
|
254
|
+
size_t p1 = msg.find("[");
|
|
255
|
+
size_t p2 = msg.find("]");
|
|
256
|
+
if (p1 != std::string::npos &&
|
|
257
|
+
p2 != std::string::npos) {
|
|
258
|
+
std::string params =
|
|
259
|
+
msg.substr(p1 + 1, p2 - p1 - 1);
|
|
260
|
+
int x = 0, y = 0;
|
|
261
|
+
sscanf(params.c_str(), "%d, %d", &x,
|
|
262
|
+
&y);
|
|
263
|
+
pImpl->windowManager->setPosition(x, y);
|
|
264
|
+
}
|
|
265
|
+
success = true;
|
|
266
|
+
} else if (winMethod == "setTitle") {
|
|
267
|
+
size_t p1 = msg.find("[\"");
|
|
268
|
+
size_t p2 = msg.find("\"]");
|
|
269
|
+
if (p1 != std::string::npos &&
|
|
270
|
+
p2 != std::string::npos) {
|
|
271
|
+
std::string title =
|
|
272
|
+
msg.substr(p1 + 2, p2 - p1 - 2);
|
|
273
|
+
pImpl->windowManager->setTitle(title);
|
|
274
|
+
}
|
|
275
|
+
success = true;
|
|
276
|
+
} else if (winMethod == "setFullscreen") {
|
|
277
|
+
size_t p1 = msg.find("[");
|
|
278
|
+
size_t p2 = msg.find("]");
|
|
279
|
+
if (p1 != std::string::npos &&
|
|
280
|
+
p2 != std::string::npos) {
|
|
281
|
+
std::string params =
|
|
282
|
+
msg.substr(p1 + 1, p2 - p1 - 1);
|
|
283
|
+
pImpl->windowManager->setFullscreen(
|
|
284
|
+
params.find("true") !=
|
|
285
|
+
std::string::npos);
|
|
286
|
+
}
|
|
287
|
+
success = true;
|
|
288
|
+
} else if (winMethod == "setAlwaysOnTop") {
|
|
289
|
+
size_t p1 = msg.find("[");
|
|
290
|
+
size_t p2 = msg.find("]");
|
|
291
|
+
if (p1 != std::string::npos &&
|
|
292
|
+
p2 != std::string::npos) {
|
|
293
|
+
std::string params =
|
|
294
|
+
msg.substr(p1 + 1, p2 - p1 - 1);
|
|
295
|
+
pImpl->windowManager->setAlwaysOnTop(
|
|
296
|
+
params.find("true") !=
|
|
297
|
+
std::string::npos);
|
|
298
|
+
}
|
|
299
|
+
success = true;
|
|
300
|
+
} else if (winMethod == "setResizable") {
|
|
301
|
+
size_t p1 = msg.find("[");
|
|
302
|
+
size_t p2 = msg.find("]");
|
|
303
|
+
if (p1 != std::string::npos &&
|
|
304
|
+
p2 != std::string::npos) {
|
|
305
|
+
std::string params =
|
|
306
|
+
msg.substr(p1 + 1, p2 - p1 - 1);
|
|
307
|
+
pImpl->windowManager->setResizable(
|
|
308
|
+
params.find("true") !=
|
|
309
|
+
std::string::npos);
|
|
310
|
+
}
|
|
311
|
+
success = true;
|
|
312
|
+
} else if (winMethod == "isMaximized") {
|
|
313
|
+
result =
|
|
314
|
+
pImpl->windowManager->isMaximized()
|
|
315
|
+
? "true"
|
|
316
|
+
: "false";
|
|
317
|
+
success = true;
|
|
318
|
+
} else if (winMethod == "isMinimized") {
|
|
319
|
+
result =
|
|
320
|
+
pImpl->windowManager->isMinimized()
|
|
321
|
+
? "true"
|
|
322
|
+
: "false";
|
|
323
|
+
success = true;
|
|
324
|
+
} else if (winMethod == "isVisible") {
|
|
325
|
+
result = pImpl->windowManager->isVisible()
|
|
326
|
+
? "true"
|
|
327
|
+
: "false";
|
|
328
|
+
success = true;
|
|
329
|
+
} else if (winMethod == "center") {
|
|
330
|
+
pImpl->windowManager->setCenter();
|
|
331
|
+
success = true;
|
|
332
|
+
}
|
|
333
|
+
} else if (method.find("tray.") == 0) {
|
|
334
|
+
std::string trayMethod = method.substr(5);
|
|
335
|
+
if (trayMethod == "setIcon") {
|
|
336
|
+
// TODO: implement
|
|
337
|
+
success = true;
|
|
338
|
+
} else if (trayMethod == "setTooltip") {
|
|
339
|
+
// TODO: implement
|
|
340
|
+
success = true;
|
|
341
|
+
} else if (trayMethod == "setVisible") {
|
|
342
|
+
// TODO: implement
|
|
343
|
+
success = true;
|
|
344
|
+
}
|
|
345
|
+
} else if (method.find("browser.") == 0) {
|
|
346
|
+
std::string browserMethod =
|
|
347
|
+
method.substr(8);
|
|
348
|
+
if (browserMethod == "navigate") {
|
|
349
|
+
size_t p1 = msg.find("[\"");
|
|
350
|
+
size_t p2 = msg.find("\"]");
|
|
351
|
+
if (p1 != std::string::npos &&
|
|
352
|
+
p2 != std::string::npos) {
|
|
353
|
+
std::string url =
|
|
354
|
+
msg.substr(p1 + 2, p2 - p1 - 2);
|
|
355
|
+
pImpl->webview->Navigate(
|
|
356
|
+
std::wstring(url.begin(), url.end())
|
|
357
|
+
.c_str());
|
|
358
|
+
}
|
|
359
|
+
success = true;
|
|
360
|
+
} else if (browserMethod == "goBack") {
|
|
361
|
+
pImpl->webview->GoBack();
|
|
362
|
+
success = true;
|
|
363
|
+
} else if (browserMethod == "goForward") {
|
|
364
|
+
pImpl->webview->GoForward();
|
|
365
|
+
success = true;
|
|
366
|
+
} else if (browserMethod == "reload") {
|
|
367
|
+
pImpl->webview->Reload();
|
|
368
|
+
success = true;
|
|
369
|
+
} else if (browserMethod == "stop") {
|
|
370
|
+
pImpl->webview->Stop();
|
|
371
|
+
success = true;
|
|
372
|
+
} else if (browserMethod == "getUrl") {
|
|
373
|
+
result = "\"" + pImpl->currentURL + "\"";
|
|
374
|
+
success = true;
|
|
375
|
+
} else if (browserMethod == "getTitle") {
|
|
376
|
+
result =
|
|
377
|
+
"\"" + pImpl->currentTitle + "\"";
|
|
378
|
+
success = true;
|
|
379
|
+
} else if (browserMethod == "canGoBack") {
|
|
380
|
+
BOOL canBack;
|
|
381
|
+
pImpl->webview->get_CanGoBack(&canBack);
|
|
382
|
+
result = canBack ? "true" : "false";
|
|
383
|
+
success = true;
|
|
384
|
+
} else if (browserMethod ==
|
|
385
|
+
"canGoForward") {
|
|
386
|
+
BOOL canForward;
|
|
387
|
+
pImpl->webview->get_CanGoForward(
|
|
388
|
+
&canForward);
|
|
389
|
+
result = canForward ? "true" : "false";
|
|
390
|
+
success = true;
|
|
391
|
+
}
|
|
392
|
+
} else if (method.find("app.") == 0) {
|
|
393
|
+
std::string appMethod = method.substr(4);
|
|
394
|
+
if (appMethod == "quit") {
|
|
395
|
+
pImpl->windowManager->close();
|
|
396
|
+
PostQuitMessage(0);
|
|
397
|
+
success = true;
|
|
398
|
+
}
|
|
399
|
+
} else if (method.find("display.") == 0) {
|
|
400
|
+
std::string displayMethod =
|
|
401
|
+
method.substr(8);
|
|
402
|
+
if (displayMethod == "getAll") {
|
|
403
|
+
result =
|
|
404
|
+
"[{\"id\":1,\"name\":\"Primary\","
|
|
405
|
+
"\"x\":0,\"y\":0,\"width\":1920,"
|
|
406
|
+
"\"height\":1080,\"scale\":1,"
|
|
407
|
+
"\"isPrimary\":true}]";
|
|
408
|
+
success = true;
|
|
409
|
+
} else if (displayMethod == "getPrimary") {
|
|
410
|
+
result = "{\"id\":1,\"name\":\"Primary\","
|
|
411
|
+
"\"x\":0,\"y\":0,\"width\":1920,"
|
|
412
|
+
"\"height\":1080,\"scale\":1,"
|
|
413
|
+
"\"isPrimary\":true}";
|
|
414
|
+
success = true;
|
|
415
|
+
} else if (displayMethod == "getCurrent") {
|
|
416
|
+
result = "{\"id\":1,\"name\":\"Primary\","
|
|
417
|
+
"\"x\":0,\"y\":0,\"width\":1920,"
|
|
418
|
+
"\"height\":1080,\"scale\":1,"
|
|
419
|
+
"\"isPrimary\":true}";
|
|
420
|
+
success = true;
|
|
421
|
+
}
|
|
422
|
+
} else if (method.find("clipboard.") == 0) {
|
|
423
|
+
std::string clipMethod = method.substr(10);
|
|
424
|
+
if (clipMethod == "writeText") {
|
|
425
|
+
size_t p1 = msg.find("[\"");
|
|
426
|
+
size_t p2 = msg.find("\"]");
|
|
427
|
+
if (p1 != std::string::npos &&
|
|
428
|
+
p2 != std::string::npos) {
|
|
429
|
+
// TODO: implement clipboard write
|
|
430
|
+
}
|
|
431
|
+
success = true;
|
|
432
|
+
} else if (clipMethod == "readText") {
|
|
433
|
+
result = "\"\"";
|
|
434
|
+
success = true;
|
|
435
|
+
} else if (clipMethod == "clear") {
|
|
436
|
+
success = true;
|
|
437
|
+
}
|
|
438
|
+
} else if (method.find("keyboard.") == 0) {
|
|
439
|
+
// Keyboard API routing
|
|
440
|
+
std::string kbMethod = method.substr(9);
|
|
441
|
+
if (kbMethod == "isKeyPressed") {
|
|
442
|
+
result = "false";
|
|
443
|
+
success = true;
|
|
444
|
+
} else if (kbMethod == "registerShortcut") {
|
|
445
|
+
// TODO: integrate with ShortcutManager
|
|
446
|
+
result = "true";
|
|
447
|
+
success = true;
|
|
448
|
+
} else if (kbMethod ==
|
|
449
|
+
"unregisterShortcut") {
|
|
450
|
+
result = "true";
|
|
451
|
+
success = true;
|
|
452
|
+
} else if (kbMethod == "clearShortcuts") {
|
|
453
|
+
success = true;
|
|
454
|
+
} else if (kbMethod == "setAutoRepeat") {
|
|
455
|
+
success = true;
|
|
456
|
+
} else if (kbMethod == "getAutoRepeat") {
|
|
457
|
+
result = "true";
|
|
458
|
+
success = true;
|
|
459
|
+
} else {
|
|
460
|
+
std::cout << "[PlusUI] Unknown keyboard "
|
|
461
|
+
"method: "
|
|
462
|
+
<< kbMethod << std::endl;
|
|
463
|
+
}
|
|
464
|
+
} else if (method.find("menu.") == 0) {
|
|
465
|
+
// Menu API routing
|
|
466
|
+
std::string menuMethod = method.substr(5);
|
|
467
|
+
if (menuMethod == "create") {
|
|
468
|
+
// TODO: integrate with
|
|
469
|
+
// MenuBindings/ContextMenuManager
|
|
470
|
+
result = "\"menu_" + id + "\"";
|
|
471
|
+
success = true;
|
|
472
|
+
} else if (menuMethod == "popup") {
|
|
473
|
+
success = true;
|
|
474
|
+
} else if (menuMethod == "popupAtCursor") {
|
|
475
|
+
success = true;
|
|
476
|
+
} else if (menuMethod == "close") {
|
|
477
|
+
success = true;
|
|
478
|
+
} else if (menuMethod == "destroy") {
|
|
479
|
+
success = true;
|
|
480
|
+
} else if (menuMethod ==
|
|
481
|
+
"setApplicationMenu") {
|
|
482
|
+
// TODO: integrate with MenuBar
|
|
483
|
+
success = true;
|
|
484
|
+
} else if (menuMethod ==
|
|
485
|
+
"getApplicationMenu") {
|
|
486
|
+
result = "[]";
|
|
487
|
+
success = true;
|
|
488
|
+
} else if (menuMethod ==
|
|
489
|
+
"appendToMenuBar") {
|
|
490
|
+
success = true;
|
|
491
|
+
} else {
|
|
492
|
+
std::cout
|
|
493
|
+
<< "[PlusUI] Unknown menu method: "
|
|
494
|
+
<< menuMethod << std::endl;
|
|
495
|
+
}
|
|
496
|
+
} else if (method.find("webgpu.") == 0) {
|
|
497
|
+
// WebGPU API routing
|
|
498
|
+
std::string webgpuMethod = method.substr(
|
|
499
|
+
7); // Remove "webgpu." prefix
|
|
500
|
+
|
|
501
|
+
if (webgpuMethod == "requestAdapter") {
|
|
502
|
+
result = pImpl->webgpu.requestAdapter(
|
|
503
|
+
msg.substr(msg.find("["),
|
|
504
|
+
msg.rfind("]") -
|
|
505
|
+
msg.find("[") + 1));
|
|
506
|
+
success = true;
|
|
507
|
+
} else if (webgpuMethod ==
|
|
508
|
+
"requestDevice") {
|
|
509
|
+
// Extract adapterId and descriptor from
|
|
510
|
+
// params
|
|
511
|
+
size_t p1 = msg.find("[");
|
|
512
|
+
size_t p2 = msg.find("]");
|
|
513
|
+
if (p1 != std::string::npos &&
|
|
514
|
+
p2 != std::string::npos) {
|
|
515
|
+
std::string params =
|
|
516
|
+
msg.substr(p1 + 1, p2 - p1 - 1);
|
|
517
|
+
// TODO: Parse adapterId and descriptor
|
|
518
|
+
// properly
|
|
519
|
+
}
|
|
520
|
+
success = true;
|
|
521
|
+
} else if (webgpuMethod == "createBuffer") {
|
|
522
|
+
success = true;
|
|
523
|
+
} else if (webgpuMethod ==
|
|
524
|
+
"createTexture") {
|
|
525
|
+
success = true;
|
|
526
|
+
} else if (webgpuMethod ==
|
|
527
|
+
"createShaderModule") {
|
|
528
|
+
success = true;
|
|
529
|
+
} else if (webgpuMethod ==
|
|
530
|
+
"createRenderPipeline") {
|
|
531
|
+
success = true;
|
|
532
|
+
} else if (webgpuMethod ==
|
|
533
|
+
"createCommandEncoder") {
|
|
534
|
+
success = true;
|
|
535
|
+
} else if (webgpuMethod ==
|
|
536
|
+
"submitCommands") {
|
|
537
|
+
success = true;
|
|
538
|
+
} else {
|
|
539
|
+
// Unknown WebGPU method
|
|
540
|
+
std::cout
|
|
541
|
+
<< "[PlusUI] Unknown WebGPU method: "
|
|
542
|
+
<< webgpuMethod << std::endl;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Send response back to JS (matches
|
|
547
|
+
// plusui-native-core SDK bridge)
|
|
548
|
+
std::string response =
|
|
549
|
+
"window.__response__(\"" + id + "\", " +
|
|
550
|
+
result + ");";
|
|
551
|
+
pImpl->webview->ExecuteScript(
|
|
552
|
+
std::wstring(response.begin(),
|
|
553
|
+
response.end())
|
|
554
|
+
.c_str(),
|
|
555
|
+
nullptr);
|
|
556
|
+
|
|
557
|
+
return S_OK;
|
|
558
|
+
})
|
|
559
|
+
.Get(),
|
|
560
|
+
nullptr);
|
|
561
|
+
|
|
562
|
+
// Process pending scripts
|
|
563
|
+
for (const auto &script : pImpl->pendingScripts) {
|
|
564
|
+
pImpl->webview->ExecuteScript(
|
|
565
|
+
std::wstring(script.begin(), script.end())
|
|
566
|
+
.c_str(),
|
|
567
|
+
nullptr);
|
|
568
|
+
}
|
|
569
|
+
pImpl->pendingScripts.clear();
|
|
570
|
+
|
|
571
|
+
// Process pending navigation
|
|
572
|
+
if (!pImpl->pendingNavigation.empty()) {
|
|
573
|
+
pImpl->webview->Navigate(
|
|
574
|
+
std::wstring(pImpl->pendingNavigation.begin(),
|
|
575
|
+
pImpl->pendingNavigation.end())
|
|
576
|
+
.c_str());
|
|
577
|
+
pImpl->pendingNavigation.clear();
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Process pending HTML
|
|
581
|
+
if (!pImpl->pendingHTML.empty()) {
|
|
582
|
+
pImpl->webview->NavigateToString(
|
|
583
|
+
std::wstring(pImpl->pendingHTML.begin(),
|
|
584
|
+
pImpl->pendingHTML.end())
|
|
585
|
+
.c_str());
|
|
586
|
+
pImpl->pendingHTML.clear();
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Process pending File
|
|
590
|
+
if (!pImpl->pendingFile.empty()) {
|
|
591
|
+
// For now, loadFile is implemented via navigate in
|
|
592
|
+
// some versions or direct file reading. We'll handle
|
|
593
|
+
// it via navigate for simplicity if it's already
|
|
594
|
+
// implemented that way.
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
return S_OK;
|
|
598
|
+
})
|
|
599
|
+
.Get());
|
|
600
|
+
return S_OK;
|
|
601
|
+
})
|
|
602
|
+
.Get());
|
|
603
|
+
|
|
604
|
+
#elif defined(__APPLE__)
|
|
605
|
+
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
|
|
606
|
+
config.preferences.javaScriptEnabled = YES;
|
|
607
|
+
|
|
608
|
+
if (wv.pImpl->config.devtools) {
|
|
609
|
+
[config.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
NSView *parentView = (__bridge NSView *)windowHandle;
|
|
613
|
+
wv.pImpl->wkWebView =
|
|
614
|
+
[[WKWebView alloc] initWithFrame:parentView.bounds configuration:config];
|
|
615
|
+
[parentView addSubview:wv.pImpl->wkWebView];
|
|
616
|
+
|
|
617
|
+
wv.pImpl->nativeWebView = (__bridge void *)wv.pImpl->wkWebView;
|
|
618
|
+
|
|
619
|
+
#endif
|
|
620
|
+
|
|
621
|
+
wv.pImpl->windowManager = std::make_unique<WindowManager>(windowHandle);
|
|
622
|
+
wv.pImpl->trayManager = std::make_unique<TrayManager>();
|
|
623
|
+
wv.pImpl->trayManager->setWindowHandle(windowHandle);
|
|
624
|
+
|
|
625
|
+
return wv;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
TrayManager &WebView::tray() { return *pImpl->trayManager; }
|
|
629
|
+
WindowManager &WebView::window() { return *pImpl->windowManager; }
|
|
630
|
+
|
|
631
|
+
void WebView::setWindow(std::shared_ptr<Window> win) {
|
|
632
|
+
pImpl->window = win;
|
|
633
|
+
if (!win)
|
|
634
|
+
return;
|
|
635
|
+
|
|
636
|
+
std::weak_ptr<Impl> weak_pImpl = pImpl;
|
|
637
|
+
win->onResize([weak_pImpl](int w, int h) {
|
|
638
|
+
if (auto pImpl = weak_pImpl.lock()) {
|
|
639
|
+
#ifdef _WIN32
|
|
640
|
+
if (pImpl->controller) {
|
|
641
|
+
RECT bounds = {0, 0, w, h};
|
|
642
|
+
pImpl->controller->put_Bounds(bounds);
|
|
643
|
+
}
|
|
644
|
+
#elif defined(__APPLE__)
|
|
645
|
+
if (pImpl->wkWebView) {
|
|
646
|
+
NSView *view = (__bridge NSView *)pImpl->wkWebView;
|
|
647
|
+
NSView *parent = [view superview];
|
|
648
|
+
if (parent) {
|
|
649
|
+
[view setFrame:[parent bounds]];
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
#else
|
|
653
|
+
if (pImpl->gtkWebView) {
|
|
654
|
+
// GTK usually handles this if added to a container with expand=TRUE
|
|
655
|
+
// but we can ensure it here if it's a fixed layout parent
|
|
656
|
+
gtk_widget_set_size_request(GTK_WIDGET(pImpl->gtkWebView), w, h);
|
|
657
|
+
}
|
|
658
|
+
#endif
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
// Trigger initial resize
|
|
663
|
+
int w, h;
|
|
664
|
+
win->getSize(w, h);
|
|
665
|
+
#ifdef _WIN32
|
|
666
|
+
if (pImpl->controller) {
|
|
667
|
+
RECT bounds = {0, 0, w, h};
|
|
668
|
+
pImpl->controller->put_Bounds(bounds);
|
|
669
|
+
}
|
|
670
|
+
#endif
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
void WebView::on(const std::string &event,
|
|
674
|
+
std::function<void(const std::string &)> callback) {
|
|
675
|
+
pImpl->events[event] = callback;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
void WebView::emit(const std::string &event, const std::string &data) {
|
|
679
|
+
std::string js = "window.dispatchEvent(new CustomEvent('" + event +
|
|
680
|
+
"', {detail: " + data + "}))";
|
|
681
|
+
executeScript(js);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
void WebView::navigate(const std::string &url) {
|
|
685
|
+
pImpl->currentURL = url;
|
|
686
|
+
pImpl->loading = true;
|
|
687
|
+
|
|
688
|
+
#ifdef _WIN32
|
|
689
|
+
if (pImpl->ready && pImpl->webview) {
|
|
690
|
+
pImpl->webview->Navigate(std::wstring(url.begin(), url.end()).c_str());
|
|
691
|
+
} else {
|
|
692
|
+
pImpl->pendingNavigation = url;
|
|
693
|
+
}
|
|
694
|
+
#elif defined(__APPLE__)
|
|
695
|
+
if (pImpl->wkWebView) {
|
|
696
|
+
NSURL *nsurl =
|
|
697
|
+
[NSURL URLWithString:[NSString stringWithUTF8String:url.c_str()]];
|
|
698
|
+
NSURLRequest *request = [NSURLRequest requestWithURL:nsurl];
|
|
699
|
+
[pImpl->wkWebView loadRequest:request];
|
|
700
|
+
}
|
|
701
|
+
#else
|
|
702
|
+
if (pImpl->gtkWebView) {
|
|
703
|
+
webkit_web_view_load_uri(pImpl->gtkWebView, url.c_str());
|
|
704
|
+
}
|
|
705
|
+
#endif
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
void WebView::loadHTML(const std::string &html) {
|
|
709
|
+
loadHTML(html, "about:blank");
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
void WebView::loadHTML(const std::string &html, const std::string &baseURL) {
|
|
713
|
+
pImpl->loading = true;
|
|
714
|
+
|
|
715
|
+
#ifdef _WIN32
|
|
716
|
+
if (pImpl->ready && pImpl->webview) {
|
|
717
|
+
pImpl->webview->NavigateToString(
|
|
718
|
+
std::wstring(html.begin(), html.end()).c_str());
|
|
719
|
+
} else {
|
|
720
|
+
pImpl->pendingHTML = html;
|
|
721
|
+
}
|
|
722
|
+
#elif defined(__APPLE__)
|
|
723
|
+
if (pImpl->wkWebView) {
|
|
724
|
+
NSString *htmlString = [NSString stringWithUTF8String:html.c_str()];
|
|
725
|
+
NSURL *base =
|
|
726
|
+
[NSURL URLWithString:[NSString stringWithUTF8String:baseURL.c_str()]];
|
|
727
|
+
[pImpl->wkWebView loadHTMLString:htmlString baseURL:base];
|
|
728
|
+
}
|
|
729
|
+
#else
|
|
730
|
+
if (pImpl->gtkWebView) {
|
|
731
|
+
webkit_web_view_load_html(pImpl->gtkWebView, html.c_str(), baseURL.c_str());
|
|
732
|
+
}
|
|
733
|
+
#endif
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
void WebView::loadFile(const std::string &filePath) {
|
|
737
|
+
std::ifstream file(filePath);
|
|
738
|
+
if (!file) {
|
|
739
|
+
std::cerr << "PlusUI: Failed to open file: " << filePath << std::endl;
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
std::stringstream buffer;
|
|
744
|
+
buffer << file.rdbuf();
|
|
745
|
+
|
|
746
|
+
// Use file:// URL as base for relative paths
|
|
747
|
+
std::string baseURL =
|
|
748
|
+
"file://" + filePath.substr(0, filePath.find_last_of("/\\") + 1);
|
|
749
|
+
loadHTML(buffer.str(), baseURL);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
void WebView::reload() {
|
|
753
|
+
#ifdef _WIN32
|
|
754
|
+
if (pImpl->webview) {
|
|
755
|
+
pImpl->webview->Reload();
|
|
756
|
+
}
|
|
757
|
+
#elif defined(__APPLE__)
|
|
758
|
+
if (pImpl->wkWebView) {
|
|
759
|
+
[pImpl->wkWebView reload];
|
|
760
|
+
}
|
|
761
|
+
#else
|
|
762
|
+
if (pImpl->gtkWebView) {
|
|
763
|
+
webkit_web_view_reload(pImpl->gtkWebView);
|
|
764
|
+
}
|
|
765
|
+
#endif
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
void WebView::stop() {
|
|
769
|
+
#ifdef _WIN32
|
|
770
|
+
if (pImpl->webview) {
|
|
771
|
+
pImpl->webview->Stop();
|
|
772
|
+
}
|
|
773
|
+
#elif defined(__APPLE__)
|
|
774
|
+
if (pImpl->wkWebView) {
|
|
775
|
+
[pImpl->wkWebView stopLoading];
|
|
776
|
+
}
|
|
777
|
+
#else
|
|
778
|
+
if (pImpl->gtkWebView) {
|
|
779
|
+
webkit_web_view_stop_loading(pImpl->gtkWebView);
|
|
780
|
+
}
|
|
781
|
+
#endif
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
void WebView::goBack() {
|
|
785
|
+
#ifdef _WIN32
|
|
786
|
+
if (pImpl->webview) {
|
|
787
|
+
pImpl->webview->GoBack();
|
|
788
|
+
}
|
|
789
|
+
#elif defined(__APPLE__)
|
|
790
|
+
if (pImpl->wkWebView) {
|
|
791
|
+
[pImpl->wkWebView goBack];
|
|
792
|
+
}
|
|
793
|
+
#else
|
|
794
|
+
if (pImpl->gtkWebView) {
|
|
795
|
+
webkit_web_view_go_back(pImpl->gtkWebView);
|
|
796
|
+
}
|
|
797
|
+
#endif
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
void WebView::goForward() {
|
|
801
|
+
#ifdef _WIN32
|
|
802
|
+
if (pImpl->webview) {
|
|
803
|
+
pImpl->webview->GoForward();
|
|
804
|
+
}
|
|
805
|
+
#elif defined(__APPLE__)
|
|
806
|
+
if (pImpl->wkWebView) {
|
|
807
|
+
[pImpl->wkWebView goForward];
|
|
808
|
+
}
|
|
809
|
+
#else
|
|
810
|
+
if (pImpl->gtkWebView) {
|
|
811
|
+
webkit_web_view_go_forward(pImpl->gtkWebView);
|
|
812
|
+
}
|
|
813
|
+
#endif
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
bool WebView::canGoBack() const {
|
|
817
|
+
#ifdef _WIN32
|
|
818
|
+
if (pImpl->webview) {
|
|
819
|
+
BOOL canGoBack;
|
|
820
|
+
pImpl->webview->get_CanGoBack(&canGoBack);
|
|
821
|
+
return canGoBack;
|
|
822
|
+
}
|
|
823
|
+
#elif defined(__APPLE__)
|
|
824
|
+
if (pImpl->wkWebView) {
|
|
825
|
+
return [pImpl->wkWebView canGoBack];
|
|
826
|
+
}
|
|
827
|
+
#else
|
|
828
|
+
if (pImpl->gtkWebView) {
|
|
829
|
+
return webkit_web_view_can_go_back(pImpl->gtkWebView);
|
|
830
|
+
}
|
|
831
|
+
#endif
|
|
832
|
+
return false;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
bool WebView::canGoForward() const {
|
|
836
|
+
#ifdef _WIN32
|
|
837
|
+
if (pImpl->webview) {
|
|
838
|
+
BOOL canGoForward;
|
|
839
|
+
pImpl->webview->get_CanGoForward(&canGoForward);
|
|
840
|
+
return canGoForward;
|
|
841
|
+
}
|
|
842
|
+
#elif defined(__APPLE__)
|
|
843
|
+
if (pImpl->wkWebView) {
|
|
844
|
+
return [pImpl->wkWebView canGoForward];
|
|
845
|
+
}
|
|
846
|
+
#else
|
|
847
|
+
if (pImpl->gtkWebView) {
|
|
848
|
+
return webkit_web_view_can_go_forward(pImpl->gtkWebView);
|
|
849
|
+
}
|
|
850
|
+
#endif
|
|
851
|
+
return false;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
void WebView::executeScript(const std::string &script) {
|
|
855
|
+
#ifdef _WIN32
|
|
856
|
+
if (pImpl->ready && pImpl->webview) {
|
|
857
|
+
pImpl->webview->ExecuteScript(
|
|
858
|
+
std::wstring(script.begin(), script.end()).c_str(), nullptr);
|
|
859
|
+
} else {
|
|
860
|
+
pImpl->pendingScripts.push_back(script);
|
|
861
|
+
}
|
|
862
|
+
#elif defined(__APPLE__)
|
|
863
|
+
if (pImpl->wkWebView) {
|
|
864
|
+
NSString *js = [NSString stringWithUTF8String:script.c_str()];
|
|
865
|
+
[pImpl->wkWebView evaluateJavaScript:js completionHandler:nil];
|
|
866
|
+
}
|
|
867
|
+
#else
|
|
868
|
+
if (pImpl->gtkWebView) {
|
|
869
|
+
webkit_web_view_run_javascript(pImpl->gtkWebView, script.c_str(), nullptr,
|
|
870
|
+
nullptr, nullptr);
|
|
871
|
+
}
|
|
872
|
+
#endif
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
void WebView::executeScript(const std::string &script,
|
|
876
|
+
std::function<void(const std::string &)> callback) {
|
|
877
|
+
#ifdef _WIN32
|
|
878
|
+
if (pImpl->webview) {
|
|
879
|
+
pImpl->webview->ExecuteScript(
|
|
880
|
+
std::wstring(script.begin(), script.end()).c_str(),
|
|
881
|
+
Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
|
|
882
|
+
[callback](HRESULT error, LPCWSTR result) -> HRESULT {
|
|
883
|
+
if (result && callback) {
|
|
884
|
+
std::wstring wstr(result);
|
|
885
|
+
callback(std::string(wstr.begin(), wstr.end()));
|
|
886
|
+
}
|
|
887
|
+
return S_OK;
|
|
888
|
+
})
|
|
889
|
+
.Get());
|
|
890
|
+
}
|
|
891
|
+
#elif defined(__APPLE__)
|
|
892
|
+
if (pImpl->wkWebView) {
|
|
893
|
+
NSString *js = [NSString stringWithUTF8String:script.c_str()];
|
|
894
|
+
[pImpl->wkWebView evaluateJavaScript:js
|
|
895
|
+
completionHandler:^(id result, NSError *error) {
|
|
896
|
+
if (result && callback) {
|
|
897
|
+
NSString *resultStr =
|
|
898
|
+
[NSString stringWithFormat:@"%@", result];
|
|
899
|
+
callback([resultStr UTF8String]);
|
|
900
|
+
}
|
|
901
|
+
}];
|
|
902
|
+
}
|
|
903
|
+
#else
|
|
904
|
+
if (pImpl->gtkWebView) {
|
|
905
|
+
// GTK WebKit callback handling would go here
|
|
906
|
+
webkit_web_view_run_javascript(pImpl->gtkWebView, script.c_str(), nullptr,
|
|
907
|
+
nullptr, nullptr);
|
|
908
|
+
}
|
|
909
|
+
#endif
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
void WebView::bind(const std::string &name, JSCallback callback) {
|
|
913
|
+
pImpl->bindings[name] = callback;
|
|
914
|
+
|
|
915
|
+
// Inject bridge code to expose function to JavaScript
|
|
916
|
+
std::string bridgeScript = R"(
|
|
917
|
+
window.)" + name + R"( = function(...args) {
|
|
918
|
+
return window.plusui.invoke('webview.)" +
|
|
919
|
+
name + R"(', args);
|
|
920
|
+
};
|
|
921
|
+
)";
|
|
922
|
+
|
|
923
|
+
executeScript(bridgeScript);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
void WebView::unbind(const std::string &name) {
|
|
927
|
+
pImpl->bindings.erase(name);
|
|
928
|
+
|
|
929
|
+
std::string script = "delete window." + name + ";";
|
|
930
|
+
executeScript(script);
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
void WebView::openDevTools() {
|
|
934
|
+
#ifdef _WIN32
|
|
935
|
+
if (pImpl->webview) {
|
|
936
|
+
pImpl->webview->OpenDevToolsWindow();
|
|
937
|
+
}
|
|
938
|
+
#elif defined(__APPLE__)
|
|
939
|
+
// macOS: Dev tools open in Safari's Web Inspector
|
|
940
|
+
#else
|
|
941
|
+
if (pImpl->gtkWebView) {
|
|
942
|
+
WebKitWebInspector *inspector =
|
|
943
|
+
webkit_web_view_get_inspector(pImpl->gtkWebView);
|
|
944
|
+
webkit_web_inspector_show(inspector);
|
|
945
|
+
}
|
|
946
|
+
#endif
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
void WebView::closeDevTools() {
|
|
950
|
+
#ifdef _WIN32
|
|
951
|
+
// WebView2 doesn't have explicit close
|
|
952
|
+
#elif defined(__APPLE__)
|
|
953
|
+
// macOS: Handled by Web Inspector
|
|
954
|
+
#else
|
|
955
|
+
if (pImpl->gtkWebView) {
|
|
956
|
+
WebKitWebInspector *inspector =
|
|
957
|
+
webkit_web_view_get_inspector(pImpl->gtkWebView);
|
|
958
|
+
webkit_web_inspector_close(inspector);
|
|
959
|
+
}
|
|
960
|
+
#endif
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
void WebView::setUserAgent(const std::string &userAgent) {
|
|
964
|
+
pImpl->config.userAgent = userAgent;
|
|
965
|
+
|
|
966
|
+
#ifdef _WIN32
|
|
967
|
+
if (pImpl->webview) {
|
|
968
|
+
ComPtr<ICoreWebView2Settings> settings;
|
|
969
|
+
pImpl->webview->get_Settings(&settings);
|
|
970
|
+
// WebView2 user agent requires settings2 interface
|
|
971
|
+
}
|
|
972
|
+
#elif defined(__APPLE__)
|
|
973
|
+
if (pImpl->wkWebView) {
|
|
974
|
+
[pImpl->wkWebView
|
|
975
|
+
setCustomUserAgent:[NSString stringWithUTF8String:userAgent.c_str()]];
|
|
976
|
+
}
|
|
977
|
+
#else
|
|
978
|
+
if (pImpl->gtkWebView) {
|
|
979
|
+
WebKitSettings *settings = webkit_web_view_get_settings(pImpl->gtkWebView);
|
|
980
|
+
webkit_settings_set_user_agent(settings, userAgent.c_str());
|
|
981
|
+
}
|
|
982
|
+
#endif
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
std::string WebView::getUserAgent() const { return pImpl->config.userAgent; }
|
|
986
|
+
|
|
987
|
+
void WebView::setZoom(double factor) {
|
|
988
|
+
pImpl->zoom = factor;
|
|
989
|
+
|
|
990
|
+
#ifdef _WIN32
|
|
991
|
+
if (pImpl->controller) {
|
|
992
|
+
pImpl->controller->put_ZoomFactor(factor);
|
|
993
|
+
}
|
|
994
|
+
#elif defined(__APPLE__)
|
|
995
|
+
if (pImpl->wkWebView) {
|
|
996
|
+
[pImpl->wkWebView setPageZoom:factor];
|
|
997
|
+
}
|
|
998
|
+
#else
|
|
999
|
+
if (pImpl->gtkWebView) {
|
|
1000
|
+
webkit_web_view_set_zoom_level(pImpl->gtkWebView, factor);
|
|
1001
|
+
}
|
|
1002
|
+
#endif
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
double WebView::getZoom() const { return pImpl->zoom; }
|
|
1006
|
+
|
|
1007
|
+
void WebView::injectCSS(const std::string &css) {
|
|
1008
|
+
std::string script = R"(
|
|
1009
|
+
(function() {
|
|
1010
|
+
const style = document.createElement('style');
|
|
1011
|
+
style.textContent = `)" +
|
|
1012
|
+
css + R"(`;
|
|
1013
|
+
document.head.appendChild(style);
|
|
1014
|
+
})();
|
|
1015
|
+
)";
|
|
1016
|
+
|
|
1017
|
+
executeScript(script);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
void WebView::onNavigationStart(NavigationCallback callback) {
|
|
1021
|
+
pImpl->navigationCallback = callback;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
void WebView::onNavigationComplete(LoadCallback callback) {
|
|
1025
|
+
pImpl->navigationCompleteCallback = callback;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
void WebView::onLoadStart(LoadCallback callback) {
|
|
1029
|
+
pImpl->loadStartCallback = callback;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
void WebView::onLoadEnd(LoadCallback callback) {
|
|
1033
|
+
pImpl->loadEndCallback = callback;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
void WebView::onLoadError(ErrorCallback callback) {
|
|
1037
|
+
pImpl->errorCallback = callback;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
void WebView::onConsoleMessage(ConsoleCallback callback) {
|
|
1041
|
+
pImpl->consoleCallback = callback;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
bool WebView::isLoading() const { return pImpl->loading; }
|
|
1045
|
+
|
|
1046
|
+
std::string WebView::getURL() const { return pImpl->currentURL; }
|
|
1047
|
+
|
|
1048
|
+
std::string WebView::getTitle() const { return pImpl->currentTitle; }
|
|
1049
|
+
|
|
1050
|
+
void *WebView::nativeHandle() const { return pImpl->nativeWebView; }
|
|
1051
|
+
|
|
1052
|
+
} // namespace plusui
|