plusui-native-core 0.1.104 → 0.1.106
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/.claude/settings.local.json +7 -0
- package/Core/CMakeLists.txt +1 -1
- package/Core/Features/API/Connect_API.ts +20 -52
- package/Core/Features/API/app-api.ts +28 -28
- package/Core/Features/API/browser-api.ts +38 -38
- package/Core/Features/API/clipboard-api.ts +21 -21
- package/Core/Features/API/display-api.ts +33 -33
- package/Core/Features/API/keyboard-api.ts +33 -33
- package/Core/Features/API/menu-api.ts +39 -39
- package/Core/Features/API/router-api.ts +23 -23
- package/Core/Features/API/tray-api.ts +22 -22
- package/Core/Features/API/webgpu-api.ts +55 -55
- package/Core/Features/App/app.cpp +128 -102
- package/Core/Features/Browser/browser.cpp +227 -227
- package/Core/Features/Browser/browser.ts +161 -161
- package/Core/Features/Clipboard/clipboard.cpp +235 -235
- package/Core/Features/Display/display.cpp +212 -212
- package/Core/Features/FileDrop/filedrop.cpp +448 -324
- package/Core/Features/FileDrop/filedrop.css +421 -421
- package/Core/Features/FileDrop/filedrop.ts +5 -9
- package/Core/Features/Keyboard/keyboard_linux.cpp +4 -0
- package/Core/Features/Router/router.cpp +62 -62
- package/Core/Features/Router/router.ts +113 -113
- package/Core/Features/Tray/tray.cpp +328 -324
- package/Core/Features/WebGPU/webgpu.cpp +948 -948
- package/Core/Features/Window/webview.cpp +1026 -1014
- package/Core/Features/Window/webview.ts +516 -516
- package/Core/Features/Window/window.cpp +2265 -1988
- package/Core/include/plusui/api.hpp +237 -237
- package/Core/include/plusui/app.hpp +33 -33
- package/Core/include/plusui/browser.hpp +67 -67
- package/Core/include/plusui/clipboard.hpp +41 -41
- package/Core/include/plusui/connect.hpp +340 -340
- package/Core/include/plusui/connection.hpp +3 -3
- package/Core/include/plusui/display.hpp +90 -90
- package/Core/include/plusui/filedrop.hpp +92 -77
- package/Core/include/plusui/keyboard.hpp +112 -112
- package/Core/include/plusui/menu.hpp +153 -153
- package/Core/include/plusui/plusui +18 -18
- package/Core/include/plusui/router.hpp +42 -42
- package/Core/include/plusui/tray.hpp +94 -94
- package/Core/include/plusui/webgpu.hpp +434 -434
- package/Core/include/plusui/window.hpp +180 -177
- package/Core/scripts/generate-umbrella-header.mjs +77 -77
- package/Core/vendor/WebView2EnvironmentOptions.h +406 -406
- package/Core/vendor/webview.h +487 -487
- package/Core/vendor/webview2.h +52079 -52079
- package/README.md +19 -19
- package/package.json +1 -1
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
#include <fstream>
|
|
2
|
-
#include <functional>
|
|
3
|
-
#include <iostream>
|
|
4
|
-
#include <map>
|
|
5
|
-
#include <plusui/tray.hpp>
|
|
6
|
-
#include <plusui/webgpu.hpp>
|
|
7
|
-
#include <plusui/window.hpp>
|
|
8
|
-
#include <regex>
|
|
9
|
-
#include <sstream>
|
|
10
|
-
|
|
1
|
+
#include <fstream>
|
|
2
|
+
#include <functional>
|
|
3
|
+
#include <iostream>
|
|
4
|
+
#include <map>
|
|
5
|
+
#include <plusui/tray.hpp>
|
|
6
|
+
#include <plusui/webgpu.hpp>
|
|
7
|
+
#include <plusui/window.hpp>
|
|
8
|
+
#include <regex>
|
|
9
|
+
#include <sstream>
|
|
10
|
+
|
|
11
11
|
#ifdef _WIN32
|
|
12
12
|
#pragma warning(push)
|
|
13
13
|
#pragma warning(disable: 4996) // Disable sscanf deprecation warning
|
|
@@ -26,30 +26,30 @@ using namespace Microsoft::WRL;
|
|
|
26
26
|
#include <gtk/gtk.h>
|
|
27
27
|
#include <webkit2/webkit2.h>
|
|
28
28
|
#endif
|
|
29
|
-
|
|
30
|
-
namespace plusui {
|
|
31
|
-
|
|
32
|
-
namespace {
|
|
33
|
-
constexpr char kHideScrollbarsScript[] = R"(
|
|
34
|
-
(function() {
|
|
35
|
-
var css = "::-webkit-scrollbar{display:none!important;width:0!important;height:0!important;}::-webkit-scrollbar-track{background:transparent!important;}::-webkit-scrollbar-thumb{background:transparent!important;}*{-ms-overflow-style:none!important;scrollbar-width:none!important;scrollbar-color:transparent transparent!important;}";
|
|
36
|
-
var styleId = "__plusui_hide_scrollbars";
|
|
37
|
-
var ensureStyle = function() {
|
|
38
|
-
if (document.getElementById(styleId)) return;
|
|
39
|
-
var style = document.createElement("style");
|
|
40
|
-
style.id = styleId;
|
|
41
|
-
style.type = "text/css";
|
|
42
|
-
style.appendChild(document.createTextNode(css));
|
|
43
|
-
var container = document.head || document.documentElement || document.body;
|
|
44
|
-
if (container) {
|
|
45
|
-
container.appendChild(style);
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
ensureStyle();
|
|
49
|
-
document.addEventListener("DOMContentLoaded", ensureStyle);
|
|
50
|
-
window.addEventListener("load", ensureStyle);
|
|
51
|
-
})();
|
|
52
|
-
)";
|
|
29
|
+
|
|
30
|
+
namespace plusui {
|
|
31
|
+
|
|
32
|
+
namespace {
|
|
33
|
+
constexpr char kHideScrollbarsScript[] = R"(
|
|
34
|
+
(function() {
|
|
35
|
+
var css = "::-webkit-scrollbar{display:none!important;width:0!important;height:0!important;}::-webkit-scrollbar-track{background:transparent!important;}::-webkit-scrollbar-thumb{background:transparent!important;}*{-ms-overflow-style:none!important;scrollbar-width:none!important;scrollbar-color:transparent transparent!important;}";
|
|
36
|
+
var styleId = "__plusui_hide_scrollbars";
|
|
37
|
+
var ensureStyle = function() {
|
|
38
|
+
if (document.getElementById(styleId)) return;
|
|
39
|
+
var style = document.createElement("style");
|
|
40
|
+
style.id = styleId;
|
|
41
|
+
style.type = "text/css";
|
|
42
|
+
style.appendChild(document.createTextNode(css));
|
|
43
|
+
var container = document.head || document.documentElement || document.body;
|
|
44
|
+
if (container) {
|
|
45
|
+
container.appendChild(style);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
ensureStyle();
|
|
49
|
+
document.addEventListener("DOMContentLoaded", ensureStyle);
|
|
50
|
+
window.addEventListener("load", ensureStyle);
|
|
51
|
+
})();
|
|
52
|
+
)";
|
|
53
53
|
// Build the JS snippet that dispatches a plusui:keyboard:keydown/keyup CustomEvent.
|
|
54
54
|
// keyCode — PlusUI KeyCode value
|
|
55
55
|
// scancode — raw scan/keycode from the platform
|
|
@@ -89,79 +89,79 @@ static std::string buildShortcutScript(const std::string& id) {
|
|
|
89
89
|
|
|
90
90
|
// Note: Window::Impl is defined in window.cpp
|
|
91
91
|
// This file only provides implementations for webview-specific methods
|
|
92
|
-
|
|
93
|
-
Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
94
|
-
Window win;
|
|
95
|
-
win.pImpl->config = config;
|
|
96
|
-
|
|
97
|
-
#ifdef _WIN32
|
|
98
|
-
HWND hwnd = static_cast<HWND>(windowHandle);
|
|
99
|
-
|
|
100
|
-
// Create WebView2 environment and controller
|
|
101
|
-
auto pImpl = win.pImpl;
|
|
102
|
-
CreateCoreWebView2EnvironmentWithOptions(
|
|
103
|
-
nullptr, nullptr, nullptr,
|
|
104
|
-
Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
|
|
105
|
-
[hwnd, pImpl](HRESULT result,
|
|
106
|
-
ICoreWebView2Environment *env) -> HRESULT {
|
|
107
|
-
if (FAILED(result) || !env)
|
|
108
|
-
return result;
|
|
109
|
-
env->CreateCoreWebView2Controller(
|
|
110
|
-
hwnd,
|
|
111
|
-
Callback<
|
|
112
|
-
ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
|
|
113
|
-
[pImpl](HRESULT result,
|
|
114
|
-
ICoreWebView2Controller *controller) -> HRESULT {
|
|
115
|
-
(void)result; // Suppress unused warning
|
|
116
|
-
if (controller != nullptr) {
|
|
117
|
-
pImpl->controller = controller;
|
|
118
|
-
controller->get_CoreWebView2(&pImpl->webview);
|
|
119
|
-
|
|
120
|
-
RECT bounds;
|
|
121
|
-
HWND parentHwnd;
|
|
122
|
-
controller->get_ParentWindow(&parentHwnd);
|
|
123
|
-
GetClientRect(parentHwnd, &bounds);
|
|
124
|
-
controller->put_Bounds(bounds);
|
|
125
|
-
|
|
126
|
-
pImpl->nativeWebView = pImpl->webview.Get();
|
|
127
|
-
pImpl->ready = true;
|
|
128
|
-
|
|
129
|
-
// Disable scrollbars at the control level if configured
|
|
130
|
-
if (!pImpl->config.scrollbars) {
|
|
131
|
-
ComPtr<ICoreWebView2Settings> settings;
|
|
132
|
-
pImpl->webview->get_Settings(&settings);
|
|
133
|
-
if (settings) {
|
|
134
|
-
// Attempt to disable scrollbars if this API is available
|
|
135
|
-
// Note: This may not be available in all WebView2 versions
|
|
136
|
-
// The CSS injection below will serve as fallback
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Inject bridge script that runs on EVERY document
|
|
141
|
-
// (survives navigation)
|
|
142
|
-
std::string bridgeScript = R"(
|
|
143
|
-
window.__native_invoke__ = function(request) {
|
|
144
|
-
if (window.chrome && window.chrome.webview) {
|
|
145
|
-
window.chrome.webview.postMessage(request);
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
)";
|
|
149
|
-
pImpl->webview->AddScriptToExecuteOnDocumentCreated(
|
|
150
|
-
std::wstring(bridgeScript.begin(),
|
|
151
|
-
bridgeScript.end())
|
|
152
|
-
.c_str(),
|
|
153
|
-
nullptr);
|
|
154
|
-
|
|
155
|
-
// Disable scrollbars if configured
|
|
156
|
-
if (!pImpl->config.scrollbars) {
|
|
157
|
-
std::string scrollbarScript = kHideScrollbarsScript;
|
|
158
|
-
pImpl->webview->AddScriptToExecuteOnDocumentCreated(
|
|
159
|
-
std::wstring(scrollbarScript.begin(),
|
|
160
|
-
scrollbarScript.end())
|
|
161
|
-
.c_str(),
|
|
162
|
-
nullptr);
|
|
163
|
-
}
|
|
164
|
-
|
|
92
|
+
|
|
93
|
+
Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
94
|
+
Window win;
|
|
95
|
+
win.pImpl->config = config;
|
|
96
|
+
|
|
97
|
+
#ifdef _WIN32
|
|
98
|
+
HWND hwnd = static_cast<HWND>(windowHandle);
|
|
99
|
+
|
|
100
|
+
// Create WebView2 environment and controller
|
|
101
|
+
auto pImpl = win.pImpl;
|
|
102
|
+
CreateCoreWebView2EnvironmentWithOptions(
|
|
103
|
+
nullptr, nullptr, nullptr,
|
|
104
|
+
Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
|
|
105
|
+
[hwnd, pImpl](HRESULT result,
|
|
106
|
+
ICoreWebView2Environment *env) -> HRESULT {
|
|
107
|
+
if (FAILED(result) || !env)
|
|
108
|
+
return result;
|
|
109
|
+
env->CreateCoreWebView2Controller(
|
|
110
|
+
hwnd,
|
|
111
|
+
Callback<
|
|
112
|
+
ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
|
|
113
|
+
[pImpl](HRESULT result,
|
|
114
|
+
ICoreWebView2Controller *controller) -> HRESULT {
|
|
115
|
+
(void)result; // Suppress unused warning
|
|
116
|
+
if (controller != nullptr) {
|
|
117
|
+
pImpl->controller = controller;
|
|
118
|
+
controller->get_CoreWebView2(&pImpl->webview);
|
|
119
|
+
|
|
120
|
+
RECT bounds;
|
|
121
|
+
HWND parentHwnd;
|
|
122
|
+
controller->get_ParentWindow(&parentHwnd);
|
|
123
|
+
GetClientRect(parentHwnd, &bounds);
|
|
124
|
+
controller->put_Bounds(bounds);
|
|
125
|
+
|
|
126
|
+
pImpl->nativeWebView = pImpl->webview.Get();
|
|
127
|
+
pImpl->ready = true;
|
|
128
|
+
|
|
129
|
+
// Disable scrollbars at the control level if configured
|
|
130
|
+
if (!pImpl->config.scrollbars) {
|
|
131
|
+
ComPtr<ICoreWebView2Settings> settings;
|
|
132
|
+
pImpl->webview->get_Settings(&settings);
|
|
133
|
+
if (settings) {
|
|
134
|
+
// Attempt to disable scrollbars if this API is available
|
|
135
|
+
// Note: This may not be available in all WebView2 versions
|
|
136
|
+
// The CSS injection below will serve as fallback
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Inject bridge script that runs on EVERY document
|
|
141
|
+
// (survives navigation)
|
|
142
|
+
std::string bridgeScript = R"(
|
|
143
|
+
window.__native_invoke__ = function(request) {
|
|
144
|
+
if (window.chrome && window.chrome.webview) {
|
|
145
|
+
window.chrome.webview.postMessage(request);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
)";
|
|
149
|
+
pImpl->webview->AddScriptToExecuteOnDocumentCreated(
|
|
150
|
+
std::wstring(bridgeScript.begin(),
|
|
151
|
+
bridgeScript.end())
|
|
152
|
+
.c_str(),
|
|
153
|
+
nullptr);
|
|
154
|
+
|
|
155
|
+
// Disable scrollbars if configured
|
|
156
|
+
if (!pImpl->config.scrollbars) {
|
|
157
|
+
std::string scrollbarScript = kHideScrollbarsScript;
|
|
158
|
+
pImpl->webview->AddScriptToExecuteOnDocumentCreated(
|
|
159
|
+
std::wstring(scrollbarScript.begin(),
|
|
160
|
+
scrollbarScript.end())
|
|
161
|
+
.c_str(),
|
|
162
|
+
nullptr);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
165
|
// Block browser default drag-drop behavior (prevents
|
|
166
166
|
// the browser from navigating to the dropped file)
|
|
167
167
|
// while still showing visual feedback on drop zones.
|
|
@@ -174,6 +174,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
174
174
|
window.__plusui_dropzone_init = true;
|
|
175
175
|
|
|
176
176
|
var activeZone = null;
|
|
177
|
+
var dragDepth = 0;
|
|
177
178
|
|
|
178
179
|
var findDropZone = function(e) {
|
|
179
180
|
var target = null;
|
|
@@ -187,13 +188,20 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
187
188
|
|
|
188
189
|
var updateActiveZone = function(zone) {
|
|
189
190
|
if (activeZone === zone) return;
|
|
190
|
-
if (activeZone)
|
|
191
|
+
if (activeZone) {
|
|
192
|
+
activeZone.classList.remove('dropzone-active');
|
|
193
|
+
activeZone.classList.remove('filedrop-active');
|
|
194
|
+
}
|
|
191
195
|
activeZone = zone;
|
|
192
|
-
if (activeZone)
|
|
196
|
+
if (activeZone) {
|
|
197
|
+
activeZone.classList.add('dropzone-active');
|
|
198
|
+
activeZone.classList.add('filedrop-active');
|
|
199
|
+
}
|
|
193
200
|
};
|
|
194
201
|
|
|
195
202
|
document.addEventListener('dragenter', function(e) {
|
|
196
203
|
e.preventDefault();
|
|
204
|
+
dragDepth++;
|
|
197
205
|
var zone = findDropZone(e);
|
|
198
206
|
updateActiveZone(zone);
|
|
199
207
|
if (e.dataTransfer) {
|
|
@@ -212,16 +220,20 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
212
220
|
|
|
213
221
|
document.addEventListener('dragleave', function(e) {
|
|
214
222
|
e.preventDefault();
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
223
|
+
dragDepth--;
|
|
224
|
+
if (dragDepth <= 0) {
|
|
225
|
+
dragDepth = 0;
|
|
218
226
|
updateActiveZone(null);
|
|
227
|
+
} else {
|
|
228
|
+
var zone = findDropZone(e);
|
|
229
|
+
updateActiveZone(zone);
|
|
219
230
|
}
|
|
220
231
|
}, true);
|
|
221
232
|
|
|
222
233
|
document.addEventListener('drop', function(e) {
|
|
223
234
|
e.preventDefault();
|
|
224
235
|
updateActiveZone(null);
|
|
236
|
+
dragDepth = 0;
|
|
225
237
|
}, true);
|
|
226
238
|
|
|
227
239
|
window.addEventListener('dragover', function(e) { e.preventDefault(); }, true);
|
|
@@ -234,314 +246,314 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
234
246
|
.c_str(),
|
|
235
247
|
nullptr);
|
|
236
248
|
}
|
|
237
|
-
|
|
238
|
-
// Set up WebMessageReceived handler for JS->C++ bridge
|
|
239
|
-
pImpl->webview->add_WebMessageReceived(
|
|
240
|
-
Callback<
|
|
241
|
-
ICoreWebView2WebMessageReceivedEventHandler>(
|
|
242
|
-
[pImpl](ICoreWebView2 *sender,
|
|
243
|
-
ICoreWebView2WebMessageReceivedEventArgs
|
|
244
|
-
*args) -> HRESULT {
|
|
245
|
-
(void)sender; // Suppress unused warning
|
|
246
|
-
LPWSTR message = nullptr;
|
|
247
|
-
args->TryGetWebMessageAsString(&message);
|
|
248
|
-
if (!message)
|
|
249
|
-
return S_OK;
|
|
250
|
-
std::wstring wmsg(message);
|
|
251
|
-
#pragma warning(push)
|
|
252
|
-
#pragma warning(disable: 4244) // Suppress wchar_t to char conversion warning
|
|
253
|
-
std::string msg(wmsg.begin(), wmsg.end());
|
|
254
|
-
#pragma warning(pop)
|
|
255
|
-
CoTaskMemFree(message);
|
|
256
|
-
|
|
257
|
-
// Debug: log received message
|
|
258
|
-
std::cout << "[PlusUI] Received: " << msg
|
|
259
|
-
<< std::endl;
|
|
260
|
-
|
|
261
|
-
// Parse JSON-RPC: {"id":"...",
|
|
262
|
-
// "method":"window.minimize", "params":[...]}
|
|
263
|
-
std::string id, method;
|
|
264
|
-
std::string result = "null";
|
|
265
|
-
bool success = false;
|
|
266
|
-
|
|
267
|
-
// Simple JSON parsing
|
|
268
|
-
auto getId = [&msg]() {
|
|
269
|
-
size_t pos = msg.find("\"id\"");
|
|
270
|
-
if (pos == std::string::npos)
|
|
271
|
-
return std::string();
|
|
272
|
-
pos = msg.find(":", pos);
|
|
273
|
-
if (pos == std::string::npos)
|
|
274
|
-
return std::string();
|
|
275
|
-
size_t start = msg.find("\"", pos);
|
|
276
|
-
if (start == std::string::npos)
|
|
277
|
-
return std::string();
|
|
278
|
-
size_t end = msg.find("\"", start + 1);
|
|
279
|
-
if (end == std::string::npos)
|
|
280
|
-
return std::string();
|
|
281
|
-
return msg.substr(start + 1,
|
|
282
|
-
end - start - 1);
|
|
283
|
-
};
|
|
284
|
-
auto getMethod = [&msg]() {
|
|
285
|
-
size_t pos = msg.find("\"method\"");
|
|
286
|
-
if (pos == std::string::npos)
|
|
287
|
-
return std::string();
|
|
288
|
-
pos = msg.find(":", pos);
|
|
289
|
-
if (pos == std::string::npos)
|
|
290
|
-
return std::string();
|
|
291
|
-
size_t start = msg.find("\"", pos);
|
|
292
|
-
if (start == std::string::npos)
|
|
293
|
-
return std::string();
|
|
294
|
-
size_t end = msg.find("\"", start + 1);
|
|
295
|
-
if (end == std::string::npos)
|
|
296
|
-
return std::string();
|
|
297
|
-
return msg.substr(start + 1,
|
|
298
|
-
end - start - 1);
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
id = getId();
|
|
302
|
-
method = getMethod();
|
|
303
|
-
|
|
304
|
-
// Route to handlers
|
|
305
|
-
if (method.find("window.") == 0) {
|
|
306
|
-
std::string winMethod = method.substr(7);
|
|
307
|
-
if (winMethod == "minimize") {
|
|
308
|
-
if (pImpl->window) pImpl->window->minimize();
|
|
309
|
-
success = true;
|
|
310
|
-
} else if (winMethod == "maximize") {
|
|
311
|
-
if (pImpl->window) pImpl->window->maximize();
|
|
312
|
-
success = true;
|
|
313
|
-
} else if (winMethod == "restore") {
|
|
314
|
-
if (pImpl->window) pImpl->window->restore();
|
|
315
|
-
success = true;
|
|
316
|
-
} else if (winMethod == "close") {
|
|
317
|
-
if (pImpl->window) pImpl->window->close();
|
|
318
|
-
success = true;
|
|
319
|
-
} else if (winMethod == "show") {
|
|
320
|
-
if (pImpl->window) pImpl->window->show();
|
|
321
|
-
success = true;
|
|
322
|
-
} else if (winMethod == "hide") {
|
|
323
|
-
if (pImpl->window) pImpl->window->hide();
|
|
324
|
-
success = true;
|
|
325
|
-
} else if (winMethod == "getSize") {
|
|
326
|
-
if (pImpl->window) {
|
|
327
|
-
int w, h;
|
|
328
|
-
pImpl->window->getSize(w, h);
|
|
329
|
-
result = "{\"width\":" +
|
|
330
|
-
std::to_string(w) +
|
|
331
|
-
",\"height\":" +
|
|
332
|
-
std::to_string(h) +
|
|
333
|
-
"}";
|
|
334
|
-
}
|
|
335
|
-
success = true;
|
|
336
|
-
} else if (winMethod == "getPosition") {
|
|
337
|
-
if (pImpl->window) {
|
|
338
|
-
int x, y;
|
|
339
|
-
pImpl->window->getPosition(x, y);
|
|
340
|
-
result =
|
|
341
|
-
"{\"x\":" + std::to_string(x) +
|
|
342
|
-
",\"y\":" + std::to_string(y) +
|
|
343
|
-
"}";
|
|
344
|
-
}
|
|
345
|
-
success = true;
|
|
346
|
-
} else if (winMethod == "setSize") {
|
|
347
|
-
// Parse params [width, height]
|
|
348
|
-
size_t p1 = msg.find("[");
|
|
349
|
-
size_t p2 = msg.find("]");
|
|
350
|
-
if (p1 != std::string::npos &&
|
|
351
|
-
p2 != std::string::npos) {
|
|
352
|
-
std::string params =
|
|
353
|
-
msg.substr(p1 + 1, p2 - p1 - 1);
|
|
354
|
-
int w = 0, h = 0;
|
|
355
|
-
sscanf(params.c_str(), "%d, %d", &w,
|
|
356
|
-
&h);
|
|
357
|
-
if (pImpl->window) pImpl->window->setSize(w, h);
|
|
358
|
-
}
|
|
359
|
-
success = true;
|
|
360
|
-
} else if (winMethod == "setPosition") {
|
|
361
|
-
size_t p1 = msg.find("[");
|
|
362
|
-
size_t p2 = msg.find("]");
|
|
363
|
-
if (p1 != std::string::npos &&
|
|
364
|
-
p2 != std::string::npos) {
|
|
365
|
-
std::string params =
|
|
366
|
-
msg.substr(p1 + 1, p2 - p1 - 1);
|
|
367
|
-
int x = 0, y = 0;
|
|
368
|
-
sscanf(params.c_str(), "%d, %d", &x,
|
|
369
|
-
&y);
|
|
370
|
-
if (pImpl->window) pImpl->window->setPosition(x, y);
|
|
371
|
-
}
|
|
372
|
-
success = true;
|
|
373
|
-
} else if (winMethod == "setTitle") {
|
|
374
|
-
size_t p1 = msg.find("[\"");
|
|
375
|
-
size_t p2 = msg.find("\"]");
|
|
376
|
-
if (p1 != std::string::npos &&
|
|
377
|
-
p2 != std::string::npos) {
|
|
378
|
-
std::string title =
|
|
379
|
-
msg.substr(p1 + 2, p2 - p1 - 2);
|
|
380
|
-
if (pImpl->window) pImpl->window->setTitle(title);
|
|
381
|
-
}
|
|
382
|
-
success = true;
|
|
383
|
-
} else if (winMethod == "setFullscreen") {
|
|
384
|
-
size_t p1 = msg.find("[");
|
|
385
|
-
size_t p2 = msg.find("]");
|
|
386
|
-
if (p1 != std::string::npos &&
|
|
387
|
-
p2 != std::string::npos) {
|
|
388
|
-
std::string params =
|
|
389
|
-
msg.substr(p1 + 1, p2 - p1 - 1);
|
|
390
|
-
if (pImpl->window) pImpl->window->setFullscreen(
|
|
391
|
-
params.find("true") !=
|
|
392
|
-
std::string::npos);
|
|
393
|
-
}
|
|
394
|
-
success = true;
|
|
395
|
-
} else if (winMethod == "setAlwaysOnTop") {
|
|
396
|
-
size_t p1 = msg.find("[");
|
|
397
|
-
size_t p2 = msg.find("]");
|
|
398
|
-
if (p1 != std::string::npos &&
|
|
399
|
-
p2 != std::string::npos) {
|
|
400
|
-
std::string params =
|
|
401
|
-
msg.substr(p1 + 1, p2 - p1 - 1);
|
|
402
|
-
if (pImpl->window) pImpl->window->setAlwaysOnTop(
|
|
403
|
-
params.find("true") !=
|
|
404
|
-
std::string::npos);
|
|
405
|
-
}
|
|
406
|
-
success = true;
|
|
407
|
-
} else if (winMethod == "setResizable") {
|
|
408
|
-
size_t p1 = msg.find("[");
|
|
409
|
-
size_t p2 = msg.find("]");
|
|
410
|
-
if (p1 != std::string::npos &&
|
|
411
|
-
p2 != std::string::npos) {
|
|
412
|
-
std::string params =
|
|
413
|
-
msg.substr(p1 + 1, p2 - p1 - 1);
|
|
414
|
-
if (pImpl->window) pImpl->window->setResizable(
|
|
415
|
-
params.find("true") !=
|
|
416
|
-
std::string::npos);
|
|
417
|
-
}
|
|
418
|
-
success = true;
|
|
419
|
-
} else if (winMethod == "isMaximized") {
|
|
420
|
-
result =
|
|
421
|
-
(pImpl->window && pImpl->window->isMaximized())
|
|
422
|
-
? "true"
|
|
423
|
-
: "false";
|
|
424
|
-
success = true;
|
|
425
|
-
} else if (winMethod == "isMinimized") {
|
|
426
|
-
result =
|
|
427
|
-
(pImpl->window && pImpl->window->isMinimized())
|
|
428
|
-
? "true"
|
|
429
|
-
: "false";
|
|
430
|
-
success = true;
|
|
431
|
-
} else if (winMethod == "isVisible") {
|
|
432
|
-
result = (pImpl->window && pImpl->window->isVisible())
|
|
433
|
-
? "true"
|
|
434
|
-
: "false";
|
|
435
|
-
success = true;
|
|
436
|
-
} else if (winMethod == "center") {
|
|
437
|
-
if (pImpl->window) pImpl->window->center();
|
|
438
|
-
success = true;
|
|
439
|
-
}
|
|
440
|
-
} else if (method.find("tray.") == 0) {
|
|
441
|
-
std::string trayMethod = method.substr(5);
|
|
442
|
-
if (trayMethod == "setIcon") {
|
|
443
|
-
// TODO: implement
|
|
444
|
-
success = true;
|
|
445
|
-
} else if (trayMethod == "setTooltip") {
|
|
446
|
-
// TODO: implement
|
|
447
|
-
success = true;
|
|
448
|
-
} else if (trayMethod == "setVisible") {
|
|
449
|
-
// TODO: implement
|
|
450
|
-
success = true;
|
|
451
|
-
}
|
|
452
|
-
} else if (method.find("browser.") == 0) {
|
|
453
|
-
std::string browserMethod =
|
|
454
|
-
method.substr(8);
|
|
455
|
-
if (browserMethod == "navigate") {
|
|
456
|
-
size_t p1 = msg.find("[\"");
|
|
457
|
-
size_t p2 = msg.find("\"]");
|
|
458
|
-
if (p1 != std::string::npos &&
|
|
459
|
-
p2 != std::string::npos) {
|
|
460
|
-
std::string url =
|
|
461
|
-
msg.substr(p1 + 2, p2 - p1 - 2);
|
|
462
|
-
pImpl->webview->Navigate(
|
|
463
|
-
std::wstring(url.begin(), url.end())
|
|
464
|
-
.c_str());
|
|
465
|
-
}
|
|
466
|
-
success = true;
|
|
467
|
-
} else if (browserMethod == "goBack") {
|
|
468
|
-
pImpl->webview->GoBack();
|
|
469
|
-
success = true;
|
|
470
|
-
} else if (browserMethod == "goForward") {
|
|
471
|
-
pImpl->webview->GoForward();
|
|
472
|
-
success = true;
|
|
473
|
-
} else if (browserMethod == "reload") {
|
|
474
|
-
pImpl->webview->Reload();
|
|
475
|
-
success = true;
|
|
476
|
-
} else if (browserMethod == "stop") {
|
|
477
|
-
pImpl->webview->Stop();
|
|
478
|
-
success = true;
|
|
479
|
-
} else if (browserMethod == "getUrl") {
|
|
480
|
-
result = "\"" + pImpl->currentURL + "\"";
|
|
481
|
-
success = true;
|
|
482
|
-
} else if (browserMethod == "getTitle") {
|
|
483
|
-
result =
|
|
484
|
-
"\"" + pImpl->currentTitle + "\"";
|
|
485
|
-
success = true;
|
|
486
|
-
} else if (browserMethod == "canGoBack") {
|
|
487
|
-
BOOL canBack;
|
|
488
|
-
pImpl->webview->get_CanGoBack(&canBack);
|
|
489
|
-
result = canBack ? "true" : "false";
|
|
490
|
-
success = true;
|
|
491
|
-
} else if (browserMethod ==
|
|
492
|
-
"canGoForward") {
|
|
493
|
-
BOOL canForward;
|
|
494
|
-
pImpl->webview->get_CanGoForward(
|
|
495
|
-
&canForward);
|
|
496
|
-
result = canForward ? "true" : "false";
|
|
497
|
-
success = true;
|
|
498
|
-
}
|
|
499
|
-
} else if (method.find("app.") == 0) {
|
|
500
|
-
std::string appMethod = method.substr(4);
|
|
501
|
-
if (appMethod == "quit") {
|
|
502
|
-
if (pImpl->window) pImpl->window->close();
|
|
503
|
-
PostQuitMessage(0);
|
|
504
|
-
success = true;
|
|
505
|
-
}
|
|
506
|
-
} else if (method.find("display.") == 0) {
|
|
507
|
-
std::string displayMethod =
|
|
508
|
-
method.substr(8);
|
|
509
|
-
if (displayMethod == "getAll") {
|
|
510
|
-
result =
|
|
511
|
-
"[{\"id\":1,\"name\":\"Primary\","
|
|
512
|
-
"\"x\":0,\"y\":0,\"width\":1920,"
|
|
513
|
-
"\"height\":1080,\"scale\":1,"
|
|
514
|
-
"\"isPrimary\":true}]";
|
|
515
|
-
success = true;
|
|
516
|
-
} else if (displayMethod == "getPrimary") {
|
|
517
|
-
result = "{\"id\":1,\"name\":\"Primary\","
|
|
518
|
-
"\"x\":0,\"y\":0,\"width\":1920,"
|
|
519
|
-
"\"height\":1080,\"scale\":1,"
|
|
520
|
-
"\"isPrimary\":true}";
|
|
521
|
-
success = true;
|
|
522
|
-
} else if (displayMethod == "getCurrent") {
|
|
523
|
-
result = "{\"id\":1,\"name\":\"Primary\","
|
|
524
|
-
"\"x\":0,\"y\":0,\"width\":1920,"
|
|
525
|
-
"\"height\":1080,\"scale\":1,"
|
|
526
|
-
"\"isPrimary\":true}";
|
|
527
|
-
success = true;
|
|
528
|
-
}
|
|
529
|
-
} else if (method.find("clipboard.") == 0) {
|
|
530
|
-
std::string clipMethod = method.substr(10);
|
|
531
|
-
if (clipMethod == "writeText") {
|
|
532
|
-
size_t p1 = msg.find("[\"");
|
|
533
|
-
size_t p2 = msg.find("\"]");
|
|
534
|
-
if (p1 != std::string::npos &&
|
|
535
|
-
p2 != std::string::npos) {
|
|
536
|
-
// TODO: implement clipboard write
|
|
537
|
-
}
|
|
538
|
-
success = true;
|
|
539
|
-
} else if (clipMethod == "readText") {
|
|
540
|
-
result = "\"\"";
|
|
541
|
-
success = true;
|
|
542
|
-
} else if (clipMethod == "clear") {
|
|
543
|
-
success = true;
|
|
544
|
-
}
|
|
249
|
+
|
|
250
|
+
// Set up WebMessageReceived handler for JS->C++ bridge
|
|
251
|
+
pImpl->webview->add_WebMessageReceived(
|
|
252
|
+
Callback<
|
|
253
|
+
ICoreWebView2WebMessageReceivedEventHandler>(
|
|
254
|
+
[pImpl](ICoreWebView2 *sender,
|
|
255
|
+
ICoreWebView2WebMessageReceivedEventArgs
|
|
256
|
+
*args) -> HRESULT {
|
|
257
|
+
(void)sender; // Suppress unused warning
|
|
258
|
+
LPWSTR message = nullptr;
|
|
259
|
+
args->TryGetWebMessageAsString(&message);
|
|
260
|
+
if (!message)
|
|
261
|
+
return S_OK;
|
|
262
|
+
std::wstring wmsg(message);
|
|
263
|
+
#pragma warning(push)
|
|
264
|
+
#pragma warning(disable: 4244) // Suppress wchar_t to char conversion warning
|
|
265
|
+
std::string msg(wmsg.begin(), wmsg.end());
|
|
266
|
+
#pragma warning(pop)
|
|
267
|
+
CoTaskMemFree(message);
|
|
268
|
+
|
|
269
|
+
// Debug: log received message
|
|
270
|
+
std::cout << "[PlusUI] Received: " << msg
|
|
271
|
+
<< std::endl;
|
|
272
|
+
|
|
273
|
+
// Parse JSON-RPC: {"id":"...",
|
|
274
|
+
// "method":"window.minimize", "params":[...]}
|
|
275
|
+
std::string id, method;
|
|
276
|
+
std::string result = "null";
|
|
277
|
+
bool success = false;
|
|
278
|
+
|
|
279
|
+
// Simple JSON parsing
|
|
280
|
+
auto getId = [&msg]() {
|
|
281
|
+
size_t pos = msg.find("\"id\"");
|
|
282
|
+
if (pos == std::string::npos)
|
|
283
|
+
return std::string();
|
|
284
|
+
pos = msg.find(":", pos);
|
|
285
|
+
if (pos == std::string::npos)
|
|
286
|
+
return std::string();
|
|
287
|
+
size_t start = msg.find("\"", pos);
|
|
288
|
+
if (start == std::string::npos)
|
|
289
|
+
return std::string();
|
|
290
|
+
size_t end = msg.find("\"", start + 1);
|
|
291
|
+
if (end == std::string::npos)
|
|
292
|
+
return std::string();
|
|
293
|
+
return msg.substr(start + 1,
|
|
294
|
+
end - start - 1);
|
|
295
|
+
};
|
|
296
|
+
auto getMethod = [&msg]() {
|
|
297
|
+
size_t pos = msg.find("\"method\"");
|
|
298
|
+
if (pos == std::string::npos)
|
|
299
|
+
return std::string();
|
|
300
|
+
pos = msg.find(":", pos);
|
|
301
|
+
if (pos == std::string::npos)
|
|
302
|
+
return std::string();
|
|
303
|
+
size_t start = msg.find("\"", pos);
|
|
304
|
+
if (start == std::string::npos)
|
|
305
|
+
return std::string();
|
|
306
|
+
size_t end = msg.find("\"", start + 1);
|
|
307
|
+
if (end == std::string::npos)
|
|
308
|
+
return std::string();
|
|
309
|
+
return msg.substr(start + 1,
|
|
310
|
+
end - start - 1);
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
id = getId();
|
|
314
|
+
method = getMethod();
|
|
315
|
+
|
|
316
|
+
// Route to handlers
|
|
317
|
+
if (method.find("window.") == 0) {
|
|
318
|
+
std::string winMethod = method.substr(7);
|
|
319
|
+
if (winMethod == "minimize") {
|
|
320
|
+
if (pImpl->window) pImpl->window->minimize();
|
|
321
|
+
success = true;
|
|
322
|
+
} else if (winMethod == "maximize") {
|
|
323
|
+
if (pImpl->window) pImpl->window->maximize();
|
|
324
|
+
success = true;
|
|
325
|
+
} else if (winMethod == "restore") {
|
|
326
|
+
if (pImpl->window) pImpl->window->restore();
|
|
327
|
+
success = true;
|
|
328
|
+
} else if (winMethod == "close") {
|
|
329
|
+
if (pImpl->window) pImpl->window->close();
|
|
330
|
+
success = true;
|
|
331
|
+
} else if (winMethod == "show") {
|
|
332
|
+
if (pImpl->window) pImpl->window->show();
|
|
333
|
+
success = true;
|
|
334
|
+
} else if (winMethod == "hide") {
|
|
335
|
+
if (pImpl->window) pImpl->window->hide();
|
|
336
|
+
success = true;
|
|
337
|
+
} else if (winMethod == "getSize") {
|
|
338
|
+
if (pImpl->window) {
|
|
339
|
+
int w, h;
|
|
340
|
+
pImpl->window->getSize(w, h);
|
|
341
|
+
result = "{\"width\":" +
|
|
342
|
+
std::to_string(w) +
|
|
343
|
+
",\"height\":" +
|
|
344
|
+
std::to_string(h) +
|
|
345
|
+
"}";
|
|
346
|
+
}
|
|
347
|
+
success = true;
|
|
348
|
+
} else if (winMethod == "getPosition") {
|
|
349
|
+
if (pImpl->window) {
|
|
350
|
+
int x, y;
|
|
351
|
+
pImpl->window->getPosition(x, y);
|
|
352
|
+
result =
|
|
353
|
+
"{\"x\":" + std::to_string(x) +
|
|
354
|
+
",\"y\":" + std::to_string(y) +
|
|
355
|
+
"}";
|
|
356
|
+
}
|
|
357
|
+
success = true;
|
|
358
|
+
} else if (winMethod == "setSize") {
|
|
359
|
+
// Parse params [width, height]
|
|
360
|
+
size_t p1 = msg.find("[");
|
|
361
|
+
size_t p2 = msg.find("]");
|
|
362
|
+
if (p1 != std::string::npos &&
|
|
363
|
+
p2 != std::string::npos) {
|
|
364
|
+
std::string params =
|
|
365
|
+
msg.substr(p1 + 1, p2 - p1 - 1);
|
|
366
|
+
int w = 0, h = 0;
|
|
367
|
+
sscanf(params.c_str(), "%d, %d", &w,
|
|
368
|
+
&h);
|
|
369
|
+
if (pImpl->window) pImpl->window->setSize(w, h);
|
|
370
|
+
}
|
|
371
|
+
success = true;
|
|
372
|
+
} else if (winMethod == "setPosition") {
|
|
373
|
+
size_t p1 = msg.find("[");
|
|
374
|
+
size_t p2 = msg.find("]");
|
|
375
|
+
if (p1 != std::string::npos &&
|
|
376
|
+
p2 != std::string::npos) {
|
|
377
|
+
std::string params =
|
|
378
|
+
msg.substr(p1 + 1, p2 - p1 - 1);
|
|
379
|
+
int x = 0, y = 0;
|
|
380
|
+
sscanf(params.c_str(), "%d, %d", &x,
|
|
381
|
+
&y);
|
|
382
|
+
if (pImpl->window) pImpl->window->setPosition(x, y);
|
|
383
|
+
}
|
|
384
|
+
success = true;
|
|
385
|
+
} else if (winMethod == "setTitle") {
|
|
386
|
+
size_t p1 = msg.find("[\"");
|
|
387
|
+
size_t p2 = msg.find("\"]");
|
|
388
|
+
if (p1 != std::string::npos &&
|
|
389
|
+
p2 != std::string::npos) {
|
|
390
|
+
std::string title =
|
|
391
|
+
msg.substr(p1 + 2, p2 - p1 - 2);
|
|
392
|
+
if (pImpl->window) pImpl->window->setTitle(title);
|
|
393
|
+
}
|
|
394
|
+
success = true;
|
|
395
|
+
} else if (winMethod == "setFullscreen") {
|
|
396
|
+
size_t p1 = msg.find("[");
|
|
397
|
+
size_t p2 = msg.find("]");
|
|
398
|
+
if (p1 != std::string::npos &&
|
|
399
|
+
p2 != std::string::npos) {
|
|
400
|
+
std::string params =
|
|
401
|
+
msg.substr(p1 + 1, p2 - p1 - 1);
|
|
402
|
+
if (pImpl->window) pImpl->window->setFullscreen(
|
|
403
|
+
params.find("true") !=
|
|
404
|
+
std::string::npos);
|
|
405
|
+
}
|
|
406
|
+
success = true;
|
|
407
|
+
} else if (winMethod == "setAlwaysOnTop") {
|
|
408
|
+
size_t p1 = msg.find("[");
|
|
409
|
+
size_t p2 = msg.find("]");
|
|
410
|
+
if (p1 != std::string::npos &&
|
|
411
|
+
p2 != std::string::npos) {
|
|
412
|
+
std::string params =
|
|
413
|
+
msg.substr(p1 + 1, p2 - p1 - 1);
|
|
414
|
+
if (pImpl->window) pImpl->window->setAlwaysOnTop(
|
|
415
|
+
params.find("true") !=
|
|
416
|
+
std::string::npos);
|
|
417
|
+
}
|
|
418
|
+
success = true;
|
|
419
|
+
} else if (winMethod == "setResizable") {
|
|
420
|
+
size_t p1 = msg.find("[");
|
|
421
|
+
size_t p2 = msg.find("]");
|
|
422
|
+
if (p1 != std::string::npos &&
|
|
423
|
+
p2 != std::string::npos) {
|
|
424
|
+
std::string params =
|
|
425
|
+
msg.substr(p1 + 1, p2 - p1 - 1);
|
|
426
|
+
if (pImpl->window) pImpl->window->setResizable(
|
|
427
|
+
params.find("true") !=
|
|
428
|
+
std::string::npos);
|
|
429
|
+
}
|
|
430
|
+
success = true;
|
|
431
|
+
} else if (winMethod == "isMaximized") {
|
|
432
|
+
result =
|
|
433
|
+
(pImpl->window && pImpl->window->isMaximized())
|
|
434
|
+
? "true"
|
|
435
|
+
: "false";
|
|
436
|
+
success = true;
|
|
437
|
+
} else if (winMethod == "isMinimized") {
|
|
438
|
+
result =
|
|
439
|
+
(pImpl->window && pImpl->window->isMinimized())
|
|
440
|
+
? "true"
|
|
441
|
+
: "false";
|
|
442
|
+
success = true;
|
|
443
|
+
} else if (winMethod == "isVisible") {
|
|
444
|
+
result = (pImpl->window && pImpl->window->isVisible())
|
|
445
|
+
? "true"
|
|
446
|
+
: "false";
|
|
447
|
+
success = true;
|
|
448
|
+
} else if (winMethod == "center") {
|
|
449
|
+
if (pImpl->window) pImpl->window->center();
|
|
450
|
+
success = true;
|
|
451
|
+
}
|
|
452
|
+
} else if (method.find("tray.") == 0) {
|
|
453
|
+
std::string trayMethod = method.substr(5);
|
|
454
|
+
if (trayMethod == "setIcon") {
|
|
455
|
+
// TODO: implement
|
|
456
|
+
success = true;
|
|
457
|
+
} else if (trayMethod == "setTooltip") {
|
|
458
|
+
// TODO: implement
|
|
459
|
+
success = true;
|
|
460
|
+
} else if (trayMethod == "setVisible") {
|
|
461
|
+
// TODO: implement
|
|
462
|
+
success = true;
|
|
463
|
+
}
|
|
464
|
+
} else if (method.find("browser.") == 0) {
|
|
465
|
+
std::string browserMethod =
|
|
466
|
+
method.substr(8);
|
|
467
|
+
if (browserMethod == "navigate") {
|
|
468
|
+
size_t p1 = msg.find("[\"");
|
|
469
|
+
size_t p2 = msg.find("\"]");
|
|
470
|
+
if (p1 != std::string::npos &&
|
|
471
|
+
p2 != std::string::npos) {
|
|
472
|
+
std::string url =
|
|
473
|
+
msg.substr(p1 + 2, p2 - p1 - 2);
|
|
474
|
+
pImpl->webview->Navigate(
|
|
475
|
+
std::wstring(url.begin(), url.end())
|
|
476
|
+
.c_str());
|
|
477
|
+
}
|
|
478
|
+
success = true;
|
|
479
|
+
} else if (browserMethod == "goBack") {
|
|
480
|
+
pImpl->webview->GoBack();
|
|
481
|
+
success = true;
|
|
482
|
+
} else if (browserMethod == "goForward") {
|
|
483
|
+
pImpl->webview->GoForward();
|
|
484
|
+
success = true;
|
|
485
|
+
} else if (browserMethod == "reload") {
|
|
486
|
+
pImpl->webview->Reload();
|
|
487
|
+
success = true;
|
|
488
|
+
} else if (browserMethod == "stop") {
|
|
489
|
+
pImpl->webview->Stop();
|
|
490
|
+
success = true;
|
|
491
|
+
} else if (browserMethod == "getUrl") {
|
|
492
|
+
result = "\"" + pImpl->currentURL + "\"";
|
|
493
|
+
success = true;
|
|
494
|
+
} else if (browserMethod == "getTitle") {
|
|
495
|
+
result =
|
|
496
|
+
"\"" + pImpl->currentTitle + "\"";
|
|
497
|
+
success = true;
|
|
498
|
+
} else if (browserMethod == "canGoBack") {
|
|
499
|
+
BOOL canBack;
|
|
500
|
+
pImpl->webview->get_CanGoBack(&canBack);
|
|
501
|
+
result = canBack ? "true" : "false";
|
|
502
|
+
success = true;
|
|
503
|
+
} else if (browserMethod ==
|
|
504
|
+
"canGoForward") {
|
|
505
|
+
BOOL canForward;
|
|
506
|
+
pImpl->webview->get_CanGoForward(
|
|
507
|
+
&canForward);
|
|
508
|
+
result = canForward ? "true" : "false";
|
|
509
|
+
success = true;
|
|
510
|
+
}
|
|
511
|
+
} else if (method.find("app.") == 0) {
|
|
512
|
+
std::string appMethod = method.substr(4);
|
|
513
|
+
if (appMethod == "quit") {
|
|
514
|
+
if (pImpl->window) pImpl->window->close();
|
|
515
|
+
PostQuitMessage(0);
|
|
516
|
+
success = true;
|
|
517
|
+
}
|
|
518
|
+
} else if (method.find("display.") == 0) {
|
|
519
|
+
std::string displayMethod =
|
|
520
|
+
method.substr(8);
|
|
521
|
+
if (displayMethod == "getAll") {
|
|
522
|
+
result =
|
|
523
|
+
"[{\"id\":1,\"name\":\"Primary\","
|
|
524
|
+
"\"x\":0,\"y\":0,\"width\":1920,"
|
|
525
|
+
"\"height\":1080,\"scale\":1,"
|
|
526
|
+
"\"isPrimary\":true}]";
|
|
527
|
+
success = true;
|
|
528
|
+
} else if (displayMethod == "getPrimary") {
|
|
529
|
+
result = "{\"id\":1,\"name\":\"Primary\","
|
|
530
|
+
"\"x\":0,\"y\":0,\"width\":1920,"
|
|
531
|
+
"\"height\":1080,\"scale\":1,"
|
|
532
|
+
"\"isPrimary\":true}";
|
|
533
|
+
success = true;
|
|
534
|
+
} else if (displayMethod == "getCurrent") {
|
|
535
|
+
result = "{\"id\":1,\"name\":\"Primary\","
|
|
536
|
+
"\"x\":0,\"y\":0,\"width\":1920,"
|
|
537
|
+
"\"height\":1080,\"scale\":1,"
|
|
538
|
+
"\"isPrimary\":true}";
|
|
539
|
+
success = true;
|
|
540
|
+
}
|
|
541
|
+
} else if (method.find("clipboard.") == 0) {
|
|
542
|
+
std::string clipMethod = method.substr(10);
|
|
543
|
+
if (clipMethod == "writeText") {
|
|
544
|
+
size_t p1 = msg.find("[\"");
|
|
545
|
+
size_t p2 = msg.find("\"]");
|
|
546
|
+
if (p1 != std::string::npos &&
|
|
547
|
+
p2 != std::string::npos) {
|
|
548
|
+
// TODO: implement clipboard write
|
|
549
|
+
}
|
|
550
|
+
success = true;
|
|
551
|
+
} else if (clipMethod == "readText") {
|
|
552
|
+
result = "\"\"";
|
|
553
|
+
success = true;
|
|
554
|
+
} else if (clipMethod == "clear") {
|
|
555
|
+
success = true;
|
|
556
|
+
}
|
|
545
557
|
} else if (method.find("keyboard.") == 0) {
|
|
546
558
|
// Keyboard API routing
|
|
547
559
|
std::string kbMethod = method.substr(9);
|
|
@@ -634,38 +646,38 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
634
646
|
"method: "
|
|
635
647
|
<< kbMethod << std::endl;
|
|
636
648
|
}
|
|
637
|
-
} else if (method.find("menu.") == 0) {
|
|
638
|
-
// Menu API routing
|
|
639
|
-
std::string menuMethod = method.substr(5);
|
|
640
|
-
if (menuMethod == "create") {
|
|
641
|
-
// TODO: integrate with
|
|
642
|
-
// MenuBindings/ContextMenuManager
|
|
643
|
-
result = "\"menu_" + id + "\"";
|
|
644
|
-
success = true;
|
|
645
|
-
} else if (menuMethod == "popup") {
|
|
646
|
-
success = true;
|
|
647
|
-
} else if (menuMethod == "popupAtCursor") {
|
|
648
|
-
success = true;
|
|
649
|
-
} else if (menuMethod == "close") {
|
|
650
|
-
success = true;
|
|
651
|
-
} else if (menuMethod == "destroy") {
|
|
652
|
-
success = true;
|
|
653
|
-
} else if (menuMethod ==
|
|
654
|
-
"setApplicationMenu") {
|
|
655
|
-
// TODO: integrate with MenuBar
|
|
656
|
-
success = true;
|
|
657
|
-
} else if (menuMethod ==
|
|
658
|
-
"getApplicationMenu") {
|
|
659
|
-
result = "[]";
|
|
660
|
-
success = true;
|
|
661
|
-
} else if (menuMethod ==
|
|
662
|
-
"appendToMenuBar") {
|
|
663
|
-
success = true;
|
|
664
|
-
} else {
|
|
665
|
-
std::cout
|
|
666
|
-
<< "[PlusUI] Unknown menu method: "
|
|
667
|
-
<< menuMethod << std::endl;
|
|
668
|
-
}
|
|
649
|
+
} else if (method.find("menu.") == 0) {
|
|
650
|
+
// Menu API routing
|
|
651
|
+
std::string menuMethod = method.substr(5);
|
|
652
|
+
if (menuMethod == "create") {
|
|
653
|
+
// TODO: integrate with
|
|
654
|
+
// MenuBindings/ContextMenuManager
|
|
655
|
+
result = "\"menu_" + id + "\"";
|
|
656
|
+
success = true;
|
|
657
|
+
} else if (menuMethod == "popup") {
|
|
658
|
+
success = true;
|
|
659
|
+
} else if (menuMethod == "popupAtCursor") {
|
|
660
|
+
success = true;
|
|
661
|
+
} else if (menuMethod == "close") {
|
|
662
|
+
success = true;
|
|
663
|
+
} else if (menuMethod == "destroy") {
|
|
664
|
+
success = true;
|
|
665
|
+
} else if (menuMethod ==
|
|
666
|
+
"setApplicationMenu") {
|
|
667
|
+
// TODO: integrate with MenuBar
|
|
668
|
+
success = true;
|
|
669
|
+
} else if (menuMethod ==
|
|
670
|
+
"getApplicationMenu") {
|
|
671
|
+
result = "[]";
|
|
672
|
+
success = true;
|
|
673
|
+
} else if (menuMethod ==
|
|
674
|
+
"appendToMenuBar") {
|
|
675
|
+
success = true;
|
|
676
|
+
} else {
|
|
677
|
+
std::cout
|
|
678
|
+
<< "[PlusUI] Unknown menu method: "
|
|
679
|
+
<< menuMethod << std::endl;
|
|
680
|
+
}
|
|
669
681
|
} else if (method.find("webview.") == 0) {
|
|
670
682
|
// WebView API routing
|
|
671
683
|
std::string webviewMethod = method.substr(8);
|
|
@@ -690,122 +702,122 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
690
702
|
}
|
|
691
703
|
success = true;
|
|
692
704
|
}
|
|
693
|
-
} else if (method.find("webgpu.") == 0) {
|
|
694
|
-
// WebGPU API routing
|
|
695
|
-
std::string webgpuMethod = method.substr(
|
|
696
|
-
7); // Remove "webgpu." prefix
|
|
697
|
-
|
|
698
|
-
if (webgpuMethod == "requestAdapter") {
|
|
699
|
-
result = pImpl->webgpu.requestAdapter(
|
|
700
|
-
msg.substr(msg.find("["),
|
|
701
|
-
msg.rfind("]") -
|
|
702
|
-
msg.find("[") + 1));
|
|
703
|
-
success = true;
|
|
704
|
-
} else if (webgpuMethod ==
|
|
705
|
-
"requestDevice") {
|
|
706
|
-
// Extract adapterId and descriptor from
|
|
707
|
-
// params
|
|
708
|
-
size_t p1 = msg.find("[");
|
|
709
|
-
size_t p2 = msg.find("]");
|
|
710
|
-
if (p1 != std::string::npos &&
|
|
711
|
-
p2 != std::string::npos) {
|
|
712
|
-
std::string params =
|
|
713
|
-
msg.substr(p1 + 1, p2 - p1 - 1);
|
|
714
|
-
// TODO: Parse adapterId and descriptor
|
|
715
|
-
// properly
|
|
716
|
-
}
|
|
717
|
-
success = true;
|
|
718
|
-
} else if (webgpuMethod == "createBuffer") {
|
|
719
|
-
success = true;
|
|
720
|
-
} else if (webgpuMethod ==
|
|
721
|
-
"createTexture") {
|
|
722
|
-
success = true;
|
|
723
|
-
} else if (webgpuMethod ==
|
|
724
|
-
"createShaderModule") {
|
|
725
|
-
success = true;
|
|
726
|
-
} else if (webgpuMethod ==
|
|
727
|
-
"createRenderPipeline") {
|
|
728
|
-
success = true;
|
|
729
|
-
} else if (webgpuMethod ==
|
|
730
|
-
"createCommandEncoder") {
|
|
731
|
-
success = true;
|
|
732
|
-
} else if (webgpuMethod ==
|
|
733
|
-
"submitCommands") {
|
|
734
|
-
success = true;
|
|
735
|
-
} else {
|
|
736
|
-
// Unknown WebGPU method
|
|
737
|
-
std::cout
|
|
738
|
-
<< "[PlusUI] Unknown WebGPU method: "
|
|
739
|
-
<< webgpuMethod << std::endl;
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
// Send response back to JS (matches
|
|
744
|
-
// plusui-native-core SDK bridge)
|
|
745
|
-
std::string response =
|
|
746
|
-
"window.__response__(\"" + id + "\", " +
|
|
747
|
-
result + ");";
|
|
748
|
-
pImpl->webview->ExecuteScript(
|
|
749
|
-
std::wstring(response.begin(),
|
|
750
|
-
response.end())
|
|
751
|
-
.c_str(),
|
|
752
|
-
nullptr);
|
|
753
|
-
|
|
754
|
-
return S_OK;
|
|
755
|
-
})
|
|
756
|
-
.Get(),
|
|
757
|
-
nullptr);
|
|
758
|
-
|
|
759
|
-
// Process pending scripts
|
|
760
|
-
for (const auto &script : pImpl->pendingScripts) {
|
|
761
|
-
pImpl->webview->ExecuteScript(
|
|
762
|
-
std::wstring(script.begin(), script.end())
|
|
763
|
-
.c_str(),
|
|
764
|
-
nullptr);
|
|
765
|
-
}
|
|
766
|
-
pImpl->pendingScripts.clear();
|
|
767
|
-
|
|
768
|
-
// Process pending navigation
|
|
769
|
-
if (!pImpl->pendingNavigation.empty()) {
|
|
770
|
-
pImpl->webview->Navigate(
|
|
771
|
-
std::wstring(pImpl->pendingNavigation.begin(),
|
|
772
|
-
pImpl->pendingNavigation.end())
|
|
773
|
-
.c_str());
|
|
774
|
-
pImpl->pendingNavigation.clear();
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
// Process pending HTML
|
|
778
|
-
if (!pImpl->pendingHTML.empty()) {
|
|
779
|
-
pImpl->webview->NavigateToString(
|
|
780
|
-
std::wstring(pImpl->pendingHTML.begin(),
|
|
781
|
-
pImpl->pendingHTML.end())
|
|
782
|
-
.c_str());
|
|
783
|
-
pImpl->pendingHTML.clear();
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
// Process pending File
|
|
787
|
-
if (!pImpl->pendingFile.empty()) {
|
|
788
|
-
// For now, loadFile is implemented via navigate in
|
|
789
|
-
// some versions or direct file reading. We'll handle
|
|
790
|
-
// it via navigate for simplicity if it's already
|
|
791
|
-
// implemented that way.
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
return S_OK;
|
|
795
|
-
})
|
|
796
|
-
.Get());
|
|
797
|
-
return S_OK;
|
|
798
|
-
})
|
|
799
|
-
.Get());
|
|
800
|
-
|
|
801
|
-
#elif defined(__APPLE__)
|
|
802
|
-
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
|
|
803
|
-
config.preferences.javaScriptEnabled = YES;
|
|
804
|
-
|
|
805
|
-
if (win.pImpl->config.devtools) {
|
|
806
|
-
[config.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
|
|
807
|
-
}
|
|
808
|
-
|
|
705
|
+
} else if (method.find("webgpu.") == 0) {
|
|
706
|
+
// WebGPU API routing
|
|
707
|
+
std::string webgpuMethod = method.substr(
|
|
708
|
+
7); // Remove "webgpu." prefix
|
|
709
|
+
|
|
710
|
+
if (webgpuMethod == "requestAdapter") {
|
|
711
|
+
result = pImpl->webgpu.requestAdapter(
|
|
712
|
+
msg.substr(msg.find("["),
|
|
713
|
+
msg.rfind("]") -
|
|
714
|
+
msg.find("[") + 1));
|
|
715
|
+
success = true;
|
|
716
|
+
} else if (webgpuMethod ==
|
|
717
|
+
"requestDevice") {
|
|
718
|
+
// Extract adapterId and descriptor from
|
|
719
|
+
// params
|
|
720
|
+
size_t p1 = msg.find("[");
|
|
721
|
+
size_t p2 = msg.find("]");
|
|
722
|
+
if (p1 != std::string::npos &&
|
|
723
|
+
p2 != std::string::npos) {
|
|
724
|
+
std::string params =
|
|
725
|
+
msg.substr(p1 + 1, p2 - p1 - 1);
|
|
726
|
+
// TODO: Parse adapterId and descriptor
|
|
727
|
+
// properly
|
|
728
|
+
}
|
|
729
|
+
success = true;
|
|
730
|
+
} else if (webgpuMethod == "createBuffer") {
|
|
731
|
+
success = true;
|
|
732
|
+
} else if (webgpuMethod ==
|
|
733
|
+
"createTexture") {
|
|
734
|
+
success = true;
|
|
735
|
+
} else if (webgpuMethod ==
|
|
736
|
+
"createShaderModule") {
|
|
737
|
+
success = true;
|
|
738
|
+
} else if (webgpuMethod ==
|
|
739
|
+
"createRenderPipeline") {
|
|
740
|
+
success = true;
|
|
741
|
+
} else if (webgpuMethod ==
|
|
742
|
+
"createCommandEncoder") {
|
|
743
|
+
success = true;
|
|
744
|
+
} else if (webgpuMethod ==
|
|
745
|
+
"submitCommands") {
|
|
746
|
+
success = true;
|
|
747
|
+
} else {
|
|
748
|
+
// Unknown WebGPU method
|
|
749
|
+
std::cout
|
|
750
|
+
<< "[PlusUI] Unknown WebGPU method: "
|
|
751
|
+
<< webgpuMethod << std::endl;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// Send response back to JS (matches
|
|
756
|
+
// plusui-native-core SDK bridge)
|
|
757
|
+
std::string response =
|
|
758
|
+
"window.__response__(\"" + id + "\", " +
|
|
759
|
+
result + ");";
|
|
760
|
+
pImpl->webview->ExecuteScript(
|
|
761
|
+
std::wstring(response.begin(),
|
|
762
|
+
response.end())
|
|
763
|
+
.c_str(),
|
|
764
|
+
nullptr);
|
|
765
|
+
|
|
766
|
+
return S_OK;
|
|
767
|
+
})
|
|
768
|
+
.Get(),
|
|
769
|
+
nullptr);
|
|
770
|
+
|
|
771
|
+
// Process pending scripts
|
|
772
|
+
for (const auto &script : pImpl->pendingScripts) {
|
|
773
|
+
pImpl->webview->ExecuteScript(
|
|
774
|
+
std::wstring(script.begin(), script.end())
|
|
775
|
+
.c_str(),
|
|
776
|
+
nullptr);
|
|
777
|
+
}
|
|
778
|
+
pImpl->pendingScripts.clear();
|
|
779
|
+
|
|
780
|
+
// Process pending navigation
|
|
781
|
+
if (!pImpl->pendingNavigation.empty()) {
|
|
782
|
+
pImpl->webview->Navigate(
|
|
783
|
+
std::wstring(pImpl->pendingNavigation.begin(),
|
|
784
|
+
pImpl->pendingNavigation.end())
|
|
785
|
+
.c_str());
|
|
786
|
+
pImpl->pendingNavigation.clear();
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// Process pending HTML
|
|
790
|
+
if (!pImpl->pendingHTML.empty()) {
|
|
791
|
+
pImpl->webview->NavigateToString(
|
|
792
|
+
std::wstring(pImpl->pendingHTML.begin(),
|
|
793
|
+
pImpl->pendingHTML.end())
|
|
794
|
+
.c_str());
|
|
795
|
+
pImpl->pendingHTML.clear();
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// Process pending File
|
|
799
|
+
if (!pImpl->pendingFile.empty()) {
|
|
800
|
+
// For now, loadFile is implemented via navigate in
|
|
801
|
+
// some versions or direct file reading. We'll handle
|
|
802
|
+
// it via navigate for simplicity if it's already
|
|
803
|
+
// implemented that way.
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
return S_OK;
|
|
807
|
+
})
|
|
808
|
+
.Get());
|
|
809
|
+
return S_OK;
|
|
810
|
+
})
|
|
811
|
+
.Get());
|
|
812
|
+
|
|
813
|
+
#elif defined(__APPLE__)
|
|
814
|
+
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
|
|
815
|
+
config.preferences.javaScriptEnabled = YES;
|
|
816
|
+
|
|
817
|
+
if (win.pImpl->config.devtools) {
|
|
818
|
+
[config.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
|
|
819
|
+
}
|
|
820
|
+
|
|
809
821
|
// Block browser default drag-drop while allowing drop zone visual feedback.
|
|
810
822
|
// File delivery is handled natively by macOS drag APIs, not browser events.
|
|
811
823
|
if (win.pImpl->config.disableWebviewDragDrop ||
|
|
@@ -862,18 +874,18 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
862
874
|
forMainFrameOnly:NO];
|
|
863
875
|
[config.userContentController addUserScript:userScript];
|
|
864
876
|
}
|
|
865
|
-
|
|
866
|
-
// Hide scrollbars if disabled
|
|
867
|
-
if (!win.pImpl->config.scrollbars) {
|
|
868
|
-
NSString *scrollbarScript = [NSString stringWithUTF8String:kHideScrollbarsScript];
|
|
869
|
-
|
|
870
|
-
WKUserScript *scrollScript = [[WKUserScript alloc]
|
|
871
|
-
initWithSource:scrollbarScript
|
|
872
|
-
injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
|
|
873
|
-
forMainFrameOnly:NO];
|
|
874
|
-
[config.userContentController addUserScript:scrollScript];
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
+
|
|
878
|
+
// Hide scrollbars if disabled
|
|
879
|
+
if (!win.pImpl->config.scrollbars) {
|
|
880
|
+
NSString *scrollbarScript = [NSString stringWithUTF8String:kHideScrollbarsScript];
|
|
881
|
+
|
|
882
|
+
WKUserScript *scrollScript = [[WKUserScript alloc]
|
|
883
|
+
initWithSource:scrollbarScript
|
|
884
|
+
injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
|
|
885
|
+
forMainFrameOnly:NO];
|
|
886
|
+
[config.userContentController addUserScript:scrollScript];
|
|
887
|
+
}
|
|
888
|
+
|
|
877
889
|
NSView *parentView = (__bridge NSView *)windowHandle;
|
|
878
890
|
win.pImpl->wkWebView =
|
|
879
891
|
[[WKWebView alloc] initWithFrame:parentView.bounds configuration:config];
|
|
@@ -963,406 +975,406 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
963
975
|
}
|
|
964
976
|
|
|
965
977
|
#endif
|
|
966
|
-
|
|
967
|
-
win.pImpl->trayManager = std::make_unique<TrayManager>();
|
|
968
|
-
win.pImpl->trayManager->setWindowHandle(windowHandle);
|
|
969
|
-
|
|
970
|
-
return win;
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
TrayManager &Window::tray() { return *pImpl->trayManager; }
|
|
974
|
-
|
|
975
|
-
void Window::setWindow(std::shared_ptr<Window> win) {
|
|
976
|
-
pImpl->window = win;
|
|
977
|
-
if (!win)
|
|
978
|
-
return;
|
|
979
|
-
|
|
980
|
-
std::weak_ptr<Impl> weak_pImpl = pImpl;
|
|
981
|
-
win->onResize([weak_pImpl](int w, int h) {
|
|
982
|
-
if (auto pImpl = weak_pImpl.lock()) {
|
|
983
|
-
#ifdef _WIN32
|
|
984
|
-
if (pImpl->controller) {
|
|
985
|
-
RECT bounds = {0, 0, w, h};
|
|
986
|
-
pImpl->controller->put_Bounds(bounds);
|
|
987
|
-
}
|
|
988
|
-
#elif defined(__APPLE__)
|
|
989
|
-
if (pImpl->wkWebView) {
|
|
990
|
-
NSView *view = (__bridge NSView *)pImpl->wkWebView;
|
|
991
|
-
NSView *parent = [view superview];
|
|
992
|
-
if (parent) {
|
|
993
|
-
[view setFrame:[parent bounds]];
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
#else
|
|
997
|
-
if (pImpl->gtkWebView) {
|
|
998
|
-
// GTK usually handles this if added to a container with expand=TRUE
|
|
999
|
-
// but we can ensure it here if it's a fixed layout parent
|
|
1000
|
-
gtk_widget_set_size_request(GTK_WIDGET(pImpl->gtkWebView), w, h);
|
|
1001
|
-
}
|
|
1002
|
-
#endif
|
|
1003
|
-
}
|
|
1004
|
-
});
|
|
1005
|
-
|
|
1006
|
-
// Trigger initial resize
|
|
1007
|
-
int w, h;
|
|
1008
|
-
win->getSize(w, h);
|
|
1009
|
-
#ifdef _WIN32
|
|
1010
|
-
if (pImpl->controller) {
|
|
1011
|
-
RECT bounds = {0, 0, w, h};
|
|
1012
|
-
pImpl->controller->put_Bounds(bounds);
|
|
1013
|
-
}
|
|
1014
|
-
#endif
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
void Window::on(const std::string &event,
|
|
1018
|
-
std::function<void(const std::string &)> callback) {
|
|
1019
|
-
pImpl->events[event] = callback;
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
void Window::emit(const std::string &event, const std::string &data) {
|
|
1023
|
-
std::string js = "window.dispatchEvent(new CustomEvent('" + event +
|
|
1024
|
-
"', {detail: " + data + "}))";
|
|
1025
|
-
executeScript(js);
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
void Window::navigate(const std::string &url) {
|
|
1029
|
-
pImpl->currentURL = url;
|
|
1030
|
-
pImpl->loading = true;
|
|
1031
|
-
|
|
1032
|
-
#ifdef _WIN32
|
|
1033
|
-
if (pImpl->ready && pImpl->webview) {
|
|
1034
|
-
pImpl->webview->Navigate(std::wstring(url.begin(), url.end()).c_str());
|
|
1035
|
-
} else {
|
|
1036
|
-
pImpl->pendingNavigation = url;
|
|
1037
|
-
}
|
|
1038
|
-
#elif defined(__APPLE__)
|
|
1039
|
-
if (pImpl->wkWebView) {
|
|
1040
|
-
NSURL *nsurl =
|
|
1041
|
-
[NSURL URLWithString:[NSString stringWithUTF8String:url.c_str()]];
|
|
1042
|
-
NSURLRequest *request = [NSURLRequest requestWithURL:nsurl];
|
|
1043
|
-
[pImpl->wkWebView loadRequest:request];
|
|
1044
|
-
}
|
|
1045
|
-
#else
|
|
1046
|
-
if (pImpl->gtkWebView) {
|
|
1047
|
-
webkit_web_view_load_uri(pImpl->gtkWebView, url.c_str());
|
|
1048
|
-
}
|
|
1049
|
-
#endif
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
void Window::loadHTML(const std::string &html) {
|
|
1053
|
-
loadHTML(html, "about:blank");
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
void Window::loadHTML(const std::string &html, const std::string &baseURL) {
|
|
1057
|
-
pImpl->loading = true;
|
|
1058
|
-
|
|
1059
|
-
#ifdef _WIN32
|
|
1060
|
-
(void)baseURL; // Not used on Windows
|
|
1061
|
-
if (pImpl->ready && pImpl->webview) {
|
|
1062
|
-
pImpl->webview->NavigateToString(
|
|
1063
|
-
std::wstring(html.begin(), html.end()).c_str());
|
|
1064
|
-
} else {
|
|
1065
|
-
pImpl->pendingHTML = html;
|
|
1066
|
-
}
|
|
1067
|
-
#elif defined(__APPLE__)
|
|
1068
|
-
if (pImpl->wkWebView) {
|
|
1069
|
-
NSString *htmlString = [NSString stringWithUTF8String:html.c_str()];
|
|
1070
|
-
NSURL *base =
|
|
1071
|
-
[NSURL URLWithString:[NSString stringWithUTF8String:baseURL.c_str()]];
|
|
1072
|
-
[pImpl->wkWebView loadHTMLString:htmlString baseURL:base];
|
|
1073
|
-
}
|
|
1074
|
-
#else
|
|
1075
|
-
if (pImpl->gtkWebView) {
|
|
1076
|
-
webkit_web_view_load_html(pImpl->gtkWebView, html.c_str(), baseURL.c_str());
|
|
1077
|
-
}
|
|
1078
|
-
#endif
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
void Window::loadFile(const std::string &filePath) {
|
|
1082
|
-
std::ifstream file(filePath);
|
|
1083
|
-
if (!file) {
|
|
1084
|
-
std::cerr << "PlusUI: Failed to open file: " << filePath << std::endl;
|
|
1085
|
-
return;
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
std::stringstream buffer;
|
|
1089
|
-
buffer << file.rdbuf();
|
|
1090
|
-
|
|
1091
|
-
// Use file:// URL as base for relative paths
|
|
1092
|
-
std::string baseURL =
|
|
1093
|
-
"file://" + filePath.substr(0, filePath.find_last_of("/\\") + 1);
|
|
1094
|
-
loadHTML(buffer.str(), baseURL);
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
void Window::reload() {
|
|
1098
|
-
#ifdef _WIN32
|
|
1099
|
-
if (pImpl->webview) {
|
|
1100
|
-
pImpl->webview->Reload();
|
|
1101
|
-
}
|
|
1102
|
-
#elif defined(__APPLE__)
|
|
1103
|
-
if (pImpl->wkWebView) {
|
|
1104
|
-
[pImpl->wkWebView reload];
|
|
1105
|
-
}
|
|
1106
|
-
#else
|
|
1107
|
-
if (pImpl->gtkWebView) {
|
|
1108
|
-
webkit_web_view_reload(pImpl->gtkWebView);
|
|
1109
|
-
}
|
|
1110
|
-
#endif
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
void Window::stop() {
|
|
1114
|
-
#ifdef _WIN32
|
|
1115
|
-
if (pImpl->webview) {
|
|
1116
|
-
pImpl->webview->Stop();
|
|
1117
|
-
}
|
|
1118
|
-
#elif defined(__APPLE__)
|
|
1119
|
-
if (pImpl->wkWebView) {
|
|
1120
|
-
[pImpl->wkWebView stopLoading];
|
|
1121
|
-
}
|
|
1122
|
-
#else
|
|
1123
|
-
if (pImpl->gtkWebView) {
|
|
1124
|
-
webkit_web_view_stop_loading(pImpl->gtkWebView);
|
|
1125
|
-
}
|
|
1126
|
-
#endif
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
void Window::goBack() {
|
|
1130
|
-
#ifdef _WIN32
|
|
1131
|
-
if (pImpl->webview) {
|
|
1132
|
-
pImpl->webview->GoBack();
|
|
1133
|
-
}
|
|
1134
|
-
#elif defined(__APPLE__)
|
|
1135
|
-
if (pImpl->wkWebView) {
|
|
1136
|
-
[pImpl->wkWebView goBack];
|
|
1137
|
-
}
|
|
1138
|
-
#else
|
|
1139
|
-
if (pImpl->gtkWebView) {
|
|
1140
|
-
webkit_web_view_go_back(pImpl->gtkWebView);
|
|
1141
|
-
}
|
|
1142
|
-
#endif
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
void Window::goForward() {
|
|
1146
|
-
#ifdef _WIN32
|
|
1147
|
-
if (pImpl->webview) {
|
|
1148
|
-
pImpl->webview->GoForward();
|
|
1149
|
-
}
|
|
1150
|
-
#elif defined(__APPLE__)
|
|
1151
|
-
if (pImpl->wkWebView) {
|
|
1152
|
-
[pImpl->wkWebView goForward];
|
|
1153
|
-
}
|
|
1154
|
-
#else
|
|
1155
|
-
if (pImpl->gtkWebView) {
|
|
1156
|
-
webkit_web_view_go_forward(pImpl->gtkWebView);
|
|
1157
|
-
}
|
|
1158
|
-
#endif
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
bool Window::canGoBack() const {
|
|
1162
|
-
#ifdef _WIN32
|
|
1163
|
-
if (pImpl->webview) {
|
|
1164
|
-
BOOL canGoBack;
|
|
1165
|
-
pImpl->webview->get_CanGoBack(&canGoBack);
|
|
1166
|
-
return canGoBack;
|
|
1167
|
-
}
|
|
1168
|
-
#elif defined(__APPLE__)
|
|
1169
|
-
if (pImpl->wkWebView) {
|
|
1170
|
-
return [pImpl->wkWebView canGoBack];
|
|
1171
|
-
}
|
|
1172
|
-
#else
|
|
1173
|
-
if (pImpl->gtkWebView) {
|
|
1174
|
-
return webkit_web_view_can_go_back(pImpl->gtkWebView);
|
|
1175
|
-
}
|
|
1176
|
-
#endif
|
|
1177
|
-
return false;
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
bool Window::canGoForward() const {
|
|
1181
|
-
#ifdef _WIN32
|
|
1182
|
-
if (pImpl->webview) {
|
|
1183
|
-
BOOL canGoForward;
|
|
1184
|
-
pImpl->webview->get_CanGoForward(&canGoForward);
|
|
1185
|
-
return canGoForward;
|
|
1186
|
-
}
|
|
1187
|
-
#elif defined(__APPLE__)
|
|
1188
|
-
if (pImpl->wkWebView) {
|
|
1189
|
-
return [pImpl->wkWebView canGoForward];
|
|
1190
|
-
}
|
|
1191
|
-
#else
|
|
1192
|
-
if (pImpl->gtkWebView) {
|
|
1193
|
-
return webkit_web_view_can_go_forward(pImpl->gtkWebView);
|
|
1194
|
-
}
|
|
1195
|
-
#endif
|
|
1196
|
-
return false;
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
void Window::executeScript(const std::string &script) {
|
|
1200
|
-
#ifdef _WIN32
|
|
1201
|
-
if (pImpl->ready && pImpl->webview) {
|
|
1202
|
-
pImpl->webview->ExecuteScript(
|
|
1203
|
-
std::wstring(script.begin(), script.end()).c_str(), nullptr);
|
|
1204
|
-
} else {
|
|
1205
|
-
pImpl->pendingScripts.push_back(script);
|
|
1206
|
-
}
|
|
1207
|
-
#elif defined(__APPLE__)
|
|
1208
|
-
if (pImpl->wkWebView) {
|
|
1209
|
-
NSString *js = [NSString stringWithUTF8String:script.c_str()];
|
|
1210
|
-
[pImpl->wkWebView evaluateJavaScript:js completionHandler:nil];
|
|
1211
|
-
}
|
|
1212
|
-
#else
|
|
1213
|
-
if (pImpl->gtkWebView) {
|
|
1214
|
-
webkit_web_view_run_javascript(pImpl->gtkWebView, script.c_str(), nullptr,
|
|
1215
|
-
nullptr, nullptr);
|
|
1216
|
-
}
|
|
1217
|
-
#endif
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
void Window::executeScript(const std::string &script,
|
|
1221
|
-
std::function<void(const std::string &)> callback) {
|
|
1222
|
-
#ifdef _WIN32
|
|
1223
|
-
if (pImpl->webview) {
|
|
1224
|
-
pImpl->webview->ExecuteScript(
|
|
1225
|
-
std::wstring(script.begin(), script.end()).c_str(),
|
|
1226
|
-
Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
|
|
1227
|
-
[callback](HRESULT error, LPCWSTR result) -> HRESULT {
|
|
1228
|
-
(void)error; // Suppress unused warning
|
|
1229
|
-
if (result && callback) {
|
|
1230
|
-
std::wstring wstr(result);
|
|
1231
|
-
callback(std::string(wstr.begin(), wstr.end()));
|
|
1232
|
-
}
|
|
1233
|
-
return S_OK;
|
|
1234
|
-
})
|
|
1235
|
-
.Get());
|
|
1236
|
-
}
|
|
1237
|
-
#elif defined(__APPLE__)
|
|
1238
|
-
if (pImpl->wkWebView) {
|
|
1239
|
-
NSString *js = [NSString stringWithUTF8String:script.c_str()];
|
|
1240
|
-
[pImpl->wkWebView evaluateJavaScript:js
|
|
1241
|
-
completionHandler:^(id result, NSError *error) {
|
|
1242
|
-
if (result && callback) {
|
|
1243
|
-
NSString *resultStr =
|
|
1244
|
-
[NSString stringWithFormat:@"%@", result];
|
|
1245
|
-
callback([resultStr UTF8String]);
|
|
1246
|
-
}
|
|
1247
|
-
}];
|
|
1248
|
-
}
|
|
1249
|
-
#else
|
|
1250
|
-
if (pImpl->gtkWebView) {
|
|
1251
|
-
// GTK WebKit callback handling would go here
|
|
1252
|
-
webkit_web_view_run_javascript(pImpl->gtkWebView, script.c_str(), nullptr,
|
|
1253
|
-
nullptr, nullptr);
|
|
1254
|
-
}
|
|
1255
|
-
#endif
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
void Window::bind(const std::string &name, JSCallback callback) {
|
|
1259
|
-
pImpl->bindings[name] = callback;
|
|
1260
|
-
|
|
1261
|
-
// Inject bridge code to expose function to JavaScript
|
|
1262
|
-
std::string bridgeScript = R"(
|
|
1263
|
-
window.)" + name + R"( = function(...args) {
|
|
1264
|
-
return window.plusui.invoke('webview.)" +
|
|
1265
|
-
name + R"(', args);
|
|
1266
|
-
};
|
|
1267
|
-
)";
|
|
1268
|
-
|
|
1269
|
-
executeScript(bridgeScript);
|
|
1270
|
-
}
|
|
1271
|
-
|
|
1272
|
-
void Window::unbind(const std::string &name) {
|
|
1273
|
-
pImpl->bindings.erase(name);
|
|
1274
|
-
|
|
1275
|
-
std::string script = "delete window." + name + ";";
|
|
1276
|
-
executeScript(script);
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
void Window::openDevTools() {
|
|
1280
|
-
#ifdef _WIN32
|
|
1281
|
-
if (pImpl->webview) {
|
|
1282
|
-
pImpl->webview->OpenDevToolsWindow();
|
|
1283
|
-
}
|
|
1284
|
-
#elif defined(__APPLE__)
|
|
1285
|
-
// macOS: Dev tools open in Safari's Web Inspector
|
|
1286
|
-
#else
|
|
1287
|
-
if (pImpl->gtkWebView) {
|
|
1288
|
-
WebKitWebInspector *inspector =
|
|
1289
|
-
webkit_web_view_get_inspector(pImpl->gtkWebView);
|
|
1290
|
-
webkit_web_inspector_show(inspector);
|
|
1291
|
-
}
|
|
1292
|
-
#endif
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
|
-
void Window::closeDevTools() {
|
|
1296
|
-
#ifdef _WIN32
|
|
1297
|
-
// WebView2 doesn't have explicit close
|
|
1298
|
-
#elif defined(__APPLE__)
|
|
1299
|
-
// macOS: Handled by Web Inspector
|
|
1300
|
-
#else
|
|
1301
|
-
if (pImpl->gtkWebView) {
|
|
1302
|
-
WebKitWebInspector *inspector =
|
|
1303
|
-
webkit_web_view_get_inspector(pImpl->gtkWebView);
|
|
1304
|
-
webkit_web_inspector_close(inspector);
|
|
1305
|
-
}
|
|
1306
|
-
#endif
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
void Window::setUserAgent(const std::string &userAgent) {
|
|
1310
|
-
pImpl->userAgent = userAgent;
|
|
1311
|
-
|
|
1312
|
-
#ifdef _WIN32
|
|
1313
|
-
if (pImpl->webview) {
|
|
1314
|
-
ComPtr<ICoreWebView2Settings> settings;
|
|
1315
|
-
pImpl->webview->get_Settings(&settings);
|
|
1316
|
-
// WebView2 user agent requires settings2 interface
|
|
1317
|
-
}
|
|
1318
|
-
#elif defined(__APPLE__)
|
|
1319
|
-
if (pImpl->wkWebView) {
|
|
1320
|
-
[pImpl->wkWebView
|
|
1321
|
-
setCustomUserAgent:[NSString stringWithUTF8String:userAgent.c_str()]];
|
|
1322
|
-
}
|
|
1323
|
-
#else
|
|
1324
|
-
if (pImpl->gtkWebView) {
|
|
1325
|
-
WebKitSettings *settings = webkit_web_view_get_settings(pImpl->gtkWebView);
|
|
1326
|
-
webkit_settings_set_user_agent(settings, userAgent.c_str());
|
|
1327
|
-
}
|
|
1328
|
-
#endif
|
|
1329
|
-
}
|
|
1330
|
-
|
|
1331
|
-
std::string Window::getUserAgent() const { return pImpl->userAgent; }
|
|
1332
|
-
|
|
1333
|
-
void Window::setZoom(double factor) {
|
|
1334
|
-
pImpl->zoom = factor;
|
|
1335
|
-
|
|
1336
|
-
#ifdef _WIN32
|
|
1337
|
-
if (pImpl->controller) {
|
|
1338
|
-
pImpl->controller->put_ZoomFactor(factor);
|
|
1339
|
-
}
|
|
1340
|
-
#elif defined(__APPLE__)
|
|
1341
|
-
if (pImpl->wkWebView) {
|
|
1342
|
-
[pImpl->wkWebView setPageZoom:factor];
|
|
1343
|
-
}
|
|
1344
|
-
#else
|
|
1345
|
-
if (pImpl->gtkWebView) {
|
|
1346
|
-
webkit_web_view_set_zoom_level(pImpl->gtkWebView, factor);
|
|
1347
|
-
}
|
|
1348
|
-
#endif
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
double Window::getZoom() const { return pImpl->zoom; }
|
|
1352
|
-
|
|
1353
|
-
void Window::injectCSS(const std::string &css) {
|
|
1354
|
-
std::string script = R"(
|
|
1355
|
-
(function() {
|
|
1356
|
-
const style = document.createElement('style');
|
|
1357
|
-
style.textContent = `)" +
|
|
1358
|
-
css + R"(`;
|
|
1359
|
-
document.head.appendChild(style);
|
|
1360
|
-
})();
|
|
1361
|
-
)";
|
|
1362
|
-
|
|
1363
|
-
executeScript(script);
|
|
1364
|
-
}
|
|
1365
|
-
|
|
978
|
+
|
|
979
|
+
win.pImpl->trayManager = std::make_unique<TrayManager>();
|
|
980
|
+
win.pImpl->trayManager->setWindowHandle(windowHandle);
|
|
981
|
+
|
|
982
|
+
return win;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
TrayManager &Window::tray() { return *pImpl->trayManager; }
|
|
986
|
+
|
|
987
|
+
void Window::setWindow(std::shared_ptr<Window> win) {
|
|
988
|
+
pImpl->window = win;
|
|
989
|
+
if (!win)
|
|
990
|
+
return;
|
|
991
|
+
|
|
992
|
+
std::weak_ptr<Impl> weak_pImpl = pImpl;
|
|
993
|
+
win->onResize([weak_pImpl](int w, int h) {
|
|
994
|
+
if (auto pImpl = weak_pImpl.lock()) {
|
|
995
|
+
#ifdef _WIN32
|
|
996
|
+
if (pImpl->controller) {
|
|
997
|
+
RECT bounds = {0, 0, w, h};
|
|
998
|
+
pImpl->controller->put_Bounds(bounds);
|
|
999
|
+
}
|
|
1000
|
+
#elif defined(__APPLE__)
|
|
1001
|
+
if (pImpl->wkWebView) {
|
|
1002
|
+
NSView *view = (__bridge NSView *)pImpl->wkWebView;
|
|
1003
|
+
NSView *parent = [view superview];
|
|
1004
|
+
if (parent) {
|
|
1005
|
+
[view setFrame:[parent bounds]];
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
#else
|
|
1009
|
+
if (pImpl->gtkWebView) {
|
|
1010
|
+
// GTK usually handles this if added to a container with expand=TRUE
|
|
1011
|
+
// but we can ensure it here if it's a fixed layout parent
|
|
1012
|
+
gtk_widget_set_size_request(GTK_WIDGET(pImpl->gtkWebView), w, h);
|
|
1013
|
+
}
|
|
1014
|
+
#endif
|
|
1015
|
+
}
|
|
1016
|
+
});
|
|
1017
|
+
|
|
1018
|
+
// Trigger initial resize
|
|
1019
|
+
int w, h;
|
|
1020
|
+
win->getSize(w, h);
|
|
1021
|
+
#ifdef _WIN32
|
|
1022
|
+
if (pImpl->controller) {
|
|
1023
|
+
RECT bounds = {0, 0, w, h};
|
|
1024
|
+
pImpl->controller->put_Bounds(bounds);
|
|
1025
|
+
}
|
|
1026
|
+
#endif
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
void Window::on(const std::string &event,
|
|
1030
|
+
std::function<void(const std::string &)> callback) {
|
|
1031
|
+
pImpl->events[event] = callback;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
void Window::emit(const std::string &event, const std::string &data) {
|
|
1035
|
+
std::string js = "window.dispatchEvent(new CustomEvent('" + event +
|
|
1036
|
+
"', {detail: " + data + "}))";
|
|
1037
|
+
executeScript(js);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
void Window::navigate(const std::string &url) {
|
|
1041
|
+
pImpl->currentURL = url;
|
|
1042
|
+
pImpl->loading = true;
|
|
1043
|
+
|
|
1044
|
+
#ifdef _WIN32
|
|
1045
|
+
if (pImpl->ready && pImpl->webview) {
|
|
1046
|
+
pImpl->webview->Navigate(std::wstring(url.begin(), url.end()).c_str());
|
|
1047
|
+
} else {
|
|
1048
|
+
pImpl->pendingNavigation = url;
|
|
1049
|
+
}
|
|
1050
|
+
#elif defined(__APPLE__)
|
|
1051
|
+
if (pImpl->wkWebView) {
|
|
1052
|
+
NSURL *nsurl =
|
|
1053
|
+
[NSURL URLWithString:[NSString stringWithUTF8String:url.c_str()]];
|
|
1054
|
+
NSURLRequest *request = [NSURLRequest requestWithURL:nsurl];
|
|
1055
|
+
[pImpl->wkWebView loadRequest:request];
|
|
1056
|
+
}
|
|
1057
|
+
#else
|
|
1058
|
+
if (pImpl->gtkWebView) {
|
|
1059
|
+
webkit_web_view_load_uri(pImpl->gtkWebView, url.c_str());
|
|
1060
|
+
}
|
|
1061
|
+
#endif
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
void Window::loadHTML(const std::string &html) {
|
|
1065
|
+
loadHTML(html, "about:blank");
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
void Window::loadHTML(const std::string &html, const std::string &baseURL) {
|
|
1069
|
+
pImpl->loading = true;
|
|
1070
|
+
|
|
1071
|
+
#ifdef _WIN32
|
|
1072
|
+
(void)baseURL; // Not used on Windows
|
|
1073
|
+
if (pImpl->ready && pImpl->webview) {
|
|
1074
|
+
pImpl->webview->NavigateToString(
|
|
1075
|
+
std::wstring(html.begin(), html.end()).c_str());
|
|
1076
|
+
} else {
|
|
1077
|
+
pImpl->pendingHTML = html;
|
|
1078
|
+
}
|
|
1079
|
+
#elif defined(__APPLE__)
|
|
1080
|
+
if (pImpl->wkWebView) {
|
|
1081
|
+
NSString *htmlString = [NSString stringWithUTF8String:html.c_str()];
|
|
1082
|
+
NSURL *base =
|
|
1083
|
+
[NSURL URLWithString:[NSString stringWithUTF8String:baseURL.c_str()]];
|
|
1084
|
+
[pImpl->wkWebView loadHTMLString:htmlString baseURL:base];
|
|
1085
|
+
}
|
|
1086
|
+
#else
|
|
1087
|
+
if (pImpl->gtkWebView) {
|
|
1088
|
+
webkit_web_view_load_html(pImpl->gtkWebView, html.c_str(), baseURL.c_str());
|
|
1089
|
+
}
|
|
1090
|
+
#endif
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
void Window::loadFile(const std::string &filePath) {
|
|
1094
|
+
std::ifstream file(filePath);
|
|
1095
|
+
if (!file) {
|
|
1096
|
+
std::cerr << "PlusUI: Failed to open file: " << filePath << std::endl;
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
std::stringstream buffer;
|
|
1101
|
+
buffer << file.rdbuf();
|
|
1102
|
+
|
|
1103
|
+
// Use file:// URL as base for relative paths
|
|
1104
|
+
std::string baseURL =
|
|
1105
|
+
"file://" + filePath.substr(0, filePath.find_last_of("/\\") + 1);
|
|
1106
|
+
loadHTML(buffer.str(), baseURL);
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
void Window::reload() {
|
|
1110
|
+
#ifdef _WIN32
|
|
1111
|
+
if (pImpl->webview) {
|
|
1112
|
+
pImpl->webview->Reload();
|
|
1113
|
+
}
|
|
1114
|
+
#elif defined(__APPLE__)
|
|
1115
|
+
if (pImpl->wkWebView) {
|
|
1116
|
+
[pImpl->wkWebView reload];
|
|
1117
|
+
}
|
|
1118
|
+
#else
|
|
1119
|
+
if (pImpl->gtkWebView) {
|
|
1120
|
+
webkit_web_view_reload(pImpl->gtkWebView);
|
|
1121
|
+
}
|
|
1122
|
+
#endif
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
void Window::stop() {
|
|
1126
|
+
#ifdef _WIN32
|
|
1127
|
+
if (pImpl->webview) {
|
|
1128
|
+
pImpl->webview->Stop();
|
|
1129
|
+
}
|
|
1130
|
+
#elif defined(__APPLE__)
|
|
1131
|
+
if (pImpl->wkWebView) {
|
|
1132
|
+
[pImpl->wkWebView stopLoading];
|
|
1133
|
+
}
|
|
1134
|
+
#else
|
|
1135
|
+
if (pImpl->gtkWebView) {
|
|
1136
|
+
webkit_web_view_stop_loading(pImpl->gtkWebView);
|
|
1137
|
+
}
|
|
1138
|
+
#endif
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
void Window::goBack() {
|
|
1142
|
+
#ifdef _WIN32
|
|
1143
|
+
if (pImpl->webview) {
|
|
1144
|
+
pImpl->webview->GoBack();
|
|
1145
|
+
}
|
|
1146
|
+
#elif defined(__APPLE__)
|
|
1147
|
+
if (pImpl->wkWebView) {
|
|
1148
|
+
[pImpl->wkWebView goBack];
|
|
1149
|
+
}
|
|
1150
|
+
#else
|
|
1151
|
+
if (pImpl->gtkWebView) {
|
|
1152
|
+
webkit_web_view_go_back(pImpl->gtkWebView);
|
|
1153
|
+
}
|
|
1154
|
+
#endif
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
void Window::goForward() {
|
|
1158
|
+
#ifdef _WIN32
|
|
1159
|
+
if (pImpl->webview) {
|
|
1160
|
+
pImpl->webview->GoForward();
|
|
1161
|
+
}
|
|
1162
|
+
#elif defined(__APPLE__)
|
|
1163
|
+
if (pImpl->wkWebView) {
|
|
1164
|
+
[pImpl->wkWebView goForward];
|
|
1165
|
+
}
|
|
1166
|
+
#else
|
|
1167
|
+
if (pImpl->gtkWebView) {
|
|
1168
|
+
webkit_web_view_go_forward(pImpl->gtkWebView);
|
|
1169
|
+
}
|
|
1170
|
+
#endif
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
bool Window::canGoBack() const {
|
|
1174
|
+
#ifdef _WIN32
|
|
1175
|
+
if (pImpl->webview) {
|
|
1176
|
+
BOOL canGoBack;
|
|
1177
|
+
pImpl->webview->get_CanGoBack(&canGoBack);
|
|
1178
|
+
return canGoBack;
|
|
1179
|
+
}
|
|
1180
|
+
#elif defined(__APPLE__)
|
|
1181
|
+
if (pImpl->wkWebView) {
|
|
1182
|
+
return [pImpl->wkWebView canGoBack];
|
|
1183
|
+
}
|
|
1184
|
+
#else
|
|
1185
|
+
if (pImpl->gtkWebView) {
|
|
1186
|
+
return webkit_web_view_can_go_back(pImpl->gtkWebView);
|
|
1187
|
+
}
|
|
1188
|
+
#endif
|
|
1189
|
+
return false;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
bool Window::canGoForward() const {
|
|
1193
|
+
#ifdef _WIN32
|
|
1194
|
+
if (pImpl->webview) {
|
|
1195
|
+
BOOL canGoForward;
|
|
1196
|
+
pImpl->webview->get_CanGoForward(&canGoForward);
|
|
1197
|
+
return canGoForward;
|
|
1198
|
+
}
|
|
1199
|
+
#elif defined(__APPLE__)
|
|
1200
|
+
if (pImpl->wkWebView) {
|
|
1201
|
+
return [pImpl->wkWebView canGoForward];
|
|
1202
|
+
}
|
|
1203
|
+
#else
|
|
1204
|
+
if (pImpl->gtkWebView) {
|
|
1205
|
+
return webkit_web_view_can_go_forward(pImpl->gtkWebView);
|
|
1206
|
+
}
|
|
1207
|
+
#endif
|
|
1208
|
+
return false;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
void Window::executeScript(const std::string &script) {
|
|
1212
|
+
#ifdef _WIN32
|
|
1213
|
+
if (pImpl->ready && pImpl->webview) {
|
|
1214
|
+
pImpl->webview->ExecuteScript(
|
|
1215
|
+
std::wstring(script.begin(), script.end()).c_str(), nullptr);
|
|
1216
|
+
} else {
|
|
1217
|
+
pImpl->pendingScripts.push_back(script);
|
|
1218
|
+
}
|
|
1219
|
+
#elif defined(__APPLE__)
|
|
1220
|
+
if (pImpl->wkWebView) {
|
|
1221
|
+
NSString *js = [NSString stringWithUTF8String:script.c_str()];
|
|
1222
|
+
[pImpl->wkWebView evaluateJavaScript:js completionHandler:nil];
|
|
1223
|
+
}
|
|
1224
|
+
#else
|
|
1225
|
+
if (pImpl->gtkWebView) {
|
|
1226
|
+
webkit_web_view_run_javascript(pImpl->gtkWebView, script.c_str(), nullptr,
|
|
1227
|
+
nullptr, nullptr);
|
|
1228
|
+
}
|
|
1229
|
+
#endif
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
void Window::executeScript(const std::string &script,
|
|
1233
|
+
std::function<void(const std::string &)> callback) {
|
|
1234
|
+
#ifdef _WIN32
|
|
1235
|
+
if (pImpl->webview) {
|
|
1236
|
+
pImpl->webview->ExecuteScript(
|
|
1237
|
+
std::wstring(script.begin(), script.end()).c_str(),
|
|
1238
|
+
Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
|
|
1239
|
+
[callback](HRESULT error, LPCWSTR result) -> HRESULT {
|
|
1240
|
+
(void)error; // Suppress unused warning
|
|
1241
|
+
if (result && callback) {
|
|
1242
|
+
std::wstring wstr(result);
|
|
1243
|
+
callback(std::string(wstr.begin(), wstr.end()));
|
|
1244
|
+
}
|
|
1245
|
+
return S_OK;
|
|
1246
|
+
})
|
|
1247
|
+
.Get());
|
|
1248
|
+
}
|
|
1249
|
+
#elif defined(__APPLE__)
|
|
1250
|
+
if (pImpl->wkWebView) {
|
|
1251
|
+
NSString *js = [NSString stringWithUTF8String:script.c_str()];
|
|
1252
|
+
[pImpl->wkWebView evaluateJavaScript:js
|
|
1253
|
+
completionHandler:^(id result, NSError *error) {
|
|
1254
|
+
if (result && callback) {
|
|
1255
|
+
NSString *resultStr =
|
|
1256
|
+
[NSString stringWithFormat:@"%@", result];
|
|
1257
|
+
callback([resultStr UTF8String]);
|
|
1258
|
+
}
|
|
1259
|
+
}];
|
|
1260
|
+
}
|
|
1261
|
+
#else
|
|
1262
|
+
if (pImpl->gtkWebView) {
|
|
1263
|
+
// GTK WebKit callback handling would go here
|
|
1264
|
+
webkit_web_view_run_javascript(pImpl->gtkWebView, script.c_str(), nullptr,
|
|
1265
|
+
nullptr, nullptr);
|
|
1266
|
+
}
|
|
1267
|
+
#endif
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
void Window::bind(const std::string &name, JSCallback callback) {
|
|
1271
|
+
pImpl->bindings[name] = callback;
|
|
1272
|
+
|
|
1273
|
+
// Inject bridge code to expose function to JavaScript
|
|
1274
|
+
std::string bridgeScript = R"(
|
|
1275
|
+
window.)" + name + R"( = function(...args) {
|
|
1276
|
+
return window.plusui.invoke('webview.)" +
|
|
1277
|
+
name + R"(', args);
|
|
1278
|
+
};
|
|
1279
|
+
)";
|
|
1280
|
+
|
|
1281
|
+
executeScript(bridgeScript);
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
void Window::unbind(const std::string &name) {
|
|
1285
|
+
pImpl->bindings.erase(name);
|
|
1286
|
+
|
|
1287
|
+
std::string script = "delete window." + name + ";";
|
|
1288
|
+
executeScript(script);
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
void Window::openDevTools() {
|
|
1292
|
+
#ifdef _WIN32
|
|
1293
|
+
if (pImpl->webview) {
|
|
1294
|
+
pImpl->webview->OpenDevToolsWindow();
|
|
1295
|
+
}
|
|
1296
|
+
#elif defined(__APPLE__)
|
|
1297
|
+
// macOS: Dev tools open in Safari's Web Inspector
|
|
1298
|
+
#else
|
|
1299
|
+
if (pImpl->gtkWebView) {
|
|
1300
|
+
WebKitWebInspector *inspector =
|
|
1301
|
+
webkit_web_view_get_inspector(pImpl->gtkWebView);
|
|
1302
|
+
webkit_web_inspector_show(inspector);
|
|
1303
|
+
}
|
|
1304
|
+
#endif
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
void Window::closeDevTools() {
|
|
1308
|
+
#ifdef _WIN32
|
|
1309
|
+
// WebView2 doesn't have explicit close
|
|
1310
|
+
#elif defined(__APPLE__)
|
|
1311
|
+
// macOS: Handled by Web Inspector
|
|
1312
|
+
#else
|
|
1313
|
+
if (pImpl->gtkWebView) {
|
|
1314
|
+
WebKitWebInspector *inspector =
|
|
1315
|
+
webkit_web_view_get_inspector(pImpl->gtkWebView);
|
|
1316
|
+
webkit_web_inspector_close(inspector);
|
|
1317
|
+
}
|
|
1318
|
+
#endif
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
void Window::setUserAgent(const std::string &userAgent) {
|
|
1322
|
+
pImpl->userAgent = userAgent;
|
|
1323
|
+
|
|
1324
|
+
#ifdef _WIN32
|
|
1325
|
+
if (pImpl->webview) {
|
|
1326
|
+
ComPtr<ICoreWebView2Settings> settings;
|
|
1327
|
+
pImpl->webview->get_Settings(&settings);
|
|
1328
|
+
// WebView2 user agent requires settings2 interface
|
|
1329
|
+
}
|
|
1330
|
+
#elif defined(__APPLE__)
|
|
1331
|
+
if (pImpl->wkWebView) {
|
|
1332
|
+
[pImpl->wkWebView
|
|
1333
|
+
setCustomUserAgent:[NSString stringWithUTF8String:userAgent.c_str()]];
|
|
1334
|
+
}
|
|
1335
|
+
#else
|
|
1336
|
+
if (pImpl->gtkWebView) {
|
|
1337
|
+
WebKitSettings *settings = webkit_web_view_get_settings(pImpl->gtkWebView);
|
|
1338
|
+
webkit_settings_set_user_agent(settings, userAgent.c_str());
|
|
1339
|
+
}
|
|
1340
|
+
#endif
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
std::string Window::getUserAgent() const { return pImpl->userAgent; }
|
|
1344
|
+
|
|
1345
|
+
void Window::setZoom(double factor) {
|
|
1346
|
+
pImpl->zoom = factor;
|
|
1347
|
+
|
|
1348
|
+
#ifdef _WIN32
|
|
1349
|
+
if (pImpl->controller) {
|
|
1350
|
+
pImpl->controller->put_ZoomFactor(factor);
|
|
1351
|
+
}
|
|
1352
|
+
#elif defined(__APPLE__)
|
|
1353
|
+
if (pImpl->wkWebView) {
|
|
1354
|
+
[pImpl->wkWebView setPageZoom:factor];
|
|
1355
|
+
}
|
|
1356
|
+
#else
|
|
1357
|
+
if (pImpl->gtkWebView) {
|
|
1358
|
+
webkit_web_view_set_zoom_level(pImpl->gtkWebView, factor);
|
|
1359
|
+
}
|
|
1360
|
+
#endif
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
double Window::getZoom() const { return pImpl->zoom; }
|
|
1364
|
+
|
|
1365
|
+
void Window::injectCSS(const std::string &css) {
|
|
1366
|
+
std::string script = R"(
|
|
1367
|
+
(function() {
|
|
1368
|
+
const style = document.createElement('style');
|
|
1369
|
+
style.textContent = `)" +
|
|
1370
|
+
css + R"(`;
|
|
1371
|
+
document.head.appendChild(style);
|
|
1372
|
+
})();
|
|
1373
|
+
)";
|
|
1374
|
+
|
|
1375
|
+
executeScript(script);
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1366
1378
|
void Window::setWebviewDragDropEnabled(bool enabled) {
|
|
1367
1379
|
if (enabled) {
|
|
1368
1380
|
executeScript(R"(
|
|
@@ -1430,37 +1442,37 @@ void Window::setWebviewDragDropEnabled(bool enabled) {
|
|
|
1430
1442
|
)");
|
|
1431
1443
|
}
|
|
1432
1444
|
}
|
|
1433
|
-
|
|
1434
|
-
void Window::onNavigationStart(NavigationCallback callback) {
|
|
1435
|
-
pImpl->navigationCallback = callback;
|
|
1436
|
-
}
|
|
1437
|
-
|
|
1438
|
-
void Window::onNavigationComplete(LoadCallback callback) {
|
|
1439
|
-
pImpl->navigationCompleteCallback = callback;
|
|
1440
|
-
}
|
|
1441
|
-
|
|
1442
|
-
void Window::onLoadStart(LoadCallback callback) {
|
|
1443
|
-
pImpl->loadStartCallback = callback;
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
|
-
void Window::onLoadEnd(LoadCallback callback) {
|
|
1447
|
-
pImpl->loadEndCallback = callback;
|
|
1448
|
-
}
|
|
1449
|
-
|
|
1450
|
-
void Window::onLoadError(ErrorCallback callback) {
|
|
1451
|
-
pImpl->errorCallback = callback;
|
|
1452
|
-
}
|
|
1453
|
-
|
|
1454
|
-
void Window::onConsoleMessage(ConsoleCallback callback) {
|
|
1455
|
-
pImpl->consoleCallback = callback;
|
|
1456
|
-
}
|
|
1457
|
-
|
|
1458
|
-
bool Window::isLoading() const { return pImpl->loading; }
|
|
1459
|
-
|
|
1460
|
-
std::string Window::getURL() const { return pImpl->currentURL; }
|
|
1461
|
-
|
|
1462
|
-
std::string Window::getTitle() const { return pImpl->currentTitle; }
|
|
1463
|
-
|
|
1464
|
-
void *Window::nativeHandle() const { return pImpl->nativeWebView; }
|
|
1465
|
-
|
|
1466
|
-
} // namespace plusui
|
|
1445
|
+
|
|
1446
|
+
void Window::onNavigationStart(NavigationCallback callback) {
|
|
1447
|
+
pImpl->navigationCallback = callback;
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
void Window::onNavigationComplete(LoadCallback callback) {
|
|
1451
|
+
pImpl->navigationCompleteCallback = callback;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
void Window::onLoadStart(LoadCallback callback) {
|
|
1455
|
+
pImpl->loadStartCallback = callback;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
void Window::onLoadEnd(LoadCallback callback) {
|
|
1459
|
+
pImpl->loadEndCallback = callback;
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
void Window::onLoadError(ErrorCallback callback) {
|
|
1463
|
+
pImpl->errorCallback = callback;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
void Window::onConsoleMessage(ConsoleCallback callback) {
|
|
1467
|
+
pImpl->consoleCallback = callback;
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
bool Window::isLoading() const { return pImpl->loading; }
|
|
1471
|
+
|
|
1472
|
+
std::string Window::getURL() const { return pImpl->currentURL; }
|
|
1473
|
+
|
|
1474
|
+
std::string Window::getTitle() const { return pImpl->currentTitle; }
|
|
1475
|
+
|
|
1476
|
+
void *Window::nativeHandle() const { return pImpl->nativeWebView; }
|
|
1477
|
+
|
|
1478
|
+
} // namespace plusui
|