plusui-native-core 0.1.34 → 0.1.36

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.
@@ -24,7 +24,6 @@ add_library(plusui STATIC
24
24
  Features/Bindings/CustomBindings/custom_bindings.cpp
25
25
  Features/App/app.cpp
26
26
  Features/Window/window.cpp
27
- # Features/Window/webview.cpp # TODO: Resolve duplicate symbol definitions with window.cpp
28
27
  Features/Browser/browser.cpp
29
28
  Features/Display/display.cpp
30
29
  Features/Tray/tray.cpp
@@ -105,24 +105,29 @@ Window App::Builder::build() {
105
105
  winConfig.decorations = config.decorations;
106
106
  winConfig.skipTaskbar = config.skipTaskbar;
107
107
  winConfig.enableFileDrop = config.enableFileDrop;
108
-
108
+
109
109
  // WebView configuration (now part of WindowConfig)
110
110
  winConfig.devtools = config.devtools;
111
111
  winConfig.scrollbars = config.scrollbars;
112
112
  winConfig.disableWebviewDragDrop = config.enableFileDrop; // Auto-disable webview drag when FileDrop enabled
113
113
 
114
- Window win = Window::create(winConfig);
115
- win.show();
114
+ // Create native window
115
+ auto nativeWinPtr = std::make_shared<Window>(Window::create(winConfig));
116
+ nativeWinPtr->show();
117
+
118
+ // Create embedded webview window inside the native window
119
+ Window webviewWin = Window::create(nativeWinPtr->nativeHandle(), winConfig);
120
+ webviewWin.setWindow(nativeWinPtr);
116
121
 
117
122
  if (!config.trayIconPath.empty()) {
118
- win.tray().setIcon(config.trayIconPath);
119
- win.tray().setVisible(true);
123
+ nativeWinPtr->tray().setIcon(config.trayIconPath);
124
+ nativeWinPtr->tray().setVisible(true);
120
125
  if (!config.trayTooltip.empty()) {
121
- win.tray().setTooltip(config.trayTooltip);
126
+ nativeWinPtr->tray().setTooltip(config.trayTooltip);
122
127
  }
123
128
  }
124
129
 
125
- return win;
130
+ return webviewWin;
126
131
  }
127
132
 
128
133
  App::Builder createApp() { return App::Builder(); }
@@ -25,50 +25,8 @@ using namespace Microsoft::WRL;
25
25
 
26
26
  namespace plusui {
27
27
 
28
- struct Window::Impl {
29
- void *nativeWebView = nullptr;
30
- WindowConfig config;
31
- std::string currentURL;
32
- std::string currentTitle;
33
- std::shared_ptr<Window> window; // Keep the native window alive
34
- std::string userAgent; // Advanced webview setting
35
- bool loading = false;
36
- double zoom = 1.0;
37
- bool ready = false;
38
- std::vector<std::string> pendingScripts;
39
- std::string pendingNavigation;
40
- std::string pendingHTML;
41
- std::string pendingFile;
42
-
43
- std::map<std::string, JSCallback> bindings;
44
-
45
- std::unique_ptr<TrayManager> trayManager;
46
- std::map<std::string, std::function<void(const std::string &)>> events;
47
- WebGPU webgpu; // WebGPU support
48
-
49
- NavigationCallback navigationCallback;
50
- LoadCallback loadStartCallback;
51
- LoadCallback loadEndCallback;
52
- LoadCallback navigationCompleteCallback;
53
- ErrorCallback errorCallback;
54
- ConsoleCallback consoleCallback;
55
-
56
- #ifdef _WIN32
57
- ComPtr<ICoreWebView2Controller> controller;
58
- ComPtr<ICoreWebView2> webview;
59
- #elif defined(__APPLE__)
60
- WKWebView *wkWebView = nullptr;
61
- #else
62
- WebKitWebView *gtkWebView = nullptr;
63
- #endif
64
- };
65
-
66
- Window::Window() : pImpl(std::shared_ptr<Impl>(new Impl())) {}
67
-
68
- Window::~Window() = default;
69
-
70
- Window::Window(Window &&other) noexcept = default;
71
- Window &Window::operator=(Window &&other) noexcept = default;
28
+ // Note: Window::Impl is defined in window.cpp
29
+ // This file only provides implementations for webview-specific methods
72
30
 
73
31
  Window Window::create(void *windowHandle, const WindowConfig &config) {
74
32
  Window win;
@@ -121,15 +79,38 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
121
79
  .c_str(),
122
80
  nullptr);
123
81
 
124
- // Inject scrollbar hiding CSS if disabled
82
+ // Hide scrollbars if disabled via CSS injection
125
83
  if (!pImpl->config.scrollbars) {
84
+ // First, inject a style that will execute immediately on document creation
126
85
  std::string scrollbarScript = R"(
127
86
  (function() {
128
- var css = 'html, body { overflow: hidden !important; margin: 0 !important; padding: 0 !important; } ::-webkit-scrollbar { display: none !important; width: 0 !important; height: 0 !important; }';
87
+ // Apply CSS directly to documentElement immediately
88
+ if (document.documentElement) {
89
+ document.documentElement.style.overflow = 'hidden';
90
+ document.documentElement.style.margin = '0';
91
+ document.documentElement.style.padding = '0';
92
+ }
93
+
94
+ // Create and inject style element
129
95
  var style = document.createElement('style');
130
96
  style.type = 'text/css';
131
- style.appendChild(document.createTextNode(css));
132
- (document.head || document.documentElement).appendChild(style);
97
+ style.textContent = 'html { overflow: hidden !important; margin: 0 !important; padding: 0 !important; } body { overflow: hidden !important; margin: 0 !important; padding: 0 !important; } ::-webkit-scrollbar { display: none !important; width: 0 !important; height: 0 !important; }';
98
+
99
+ function attachStyle() {
100
+ try {
101
+ if (document.head) {
102
+ document.head.insertBefore(style, document.head.firstChild);
103
+ } else if (document.documentElement) {
104
+ document.documentElement.insertBefore(style, document.documentElement.firstChild);
105
+ } else {
106
+ setTimeout(attachStyle, 10);
107
+ }
108
+ } catch (e) {
109
+ // Ignore errors
110
+ }
111
+ }
112
+
113
+ attachStyle();
133
114
  })();
134
115
  )";
135
116
  pImpl->webview->AddScriptToExecuteOnDocumentCreated(
@@ -1,15 +1,29 @@
1
+ #include <fstream>
2
+ #include <functional>
1
3
  #include <iostream>
2
4
  #include <map>
3
5
  #include <plusui/display.hpp>
6
+ #include <plusui/tray.hpp>
7
+ #include <plusui/webgpu.hpp>
4
8
  #include <plusui/window.hpp>
9
+ #include <regex>
10
+ #include <sstream>
5
11
 
6
12
  #ifdef _WIN32
13
+ #pragma warning(push)
14
+ #pragma warning(disable: 4996)
15
+ #include <WebView2.h>
7
16
  #include <windows.h>
17
+ #include <wrl.h>
18
+ using namespace Microsoft::WRL;
19
+ #pragma warning(pop)
8
20
  #elif defined(__APPLE__)
9
21
  #include <Cocoa/Cocoa.h>
10
22
  #include <objc/objc-runtime.h>
23
+ #import <WebKit/WebKit.h>
11
24
  #else
12
25
  #include <gtk/gtk.h>
26
+ #include <webkit2/webkit2.h>
13
27
  #endif
14
28
 
15
29
  #include <stb_image.h>
@@ -18,9 +32,33 @@ namespace plusui {
18
32
 
19
33
  struct Window::Impl {
20
34
  void *nativeWindow = nullptr;
35
+ void *nativeWebView = nullptr;
21
36
  WindowConfig config;
22
37
  WindowState state;
23
38
 
39
+ // WebView-specific members
40
+ std::string currentURL;
41
+ std::string currentTitle;
42
+ std::shared_ptr<Window> window;
43
+ std::string userAgent;
44
+ bool loading = false;
45
+ double zoom = 1.0;
46
+ bool ready = false;
47
+ std::vector<std::string> pendingScripts;
48
+ std::string pendingNavigation;
49
+ std::string pendingHTML;
50
+ std::string pendingFile;
51
+ std::map<std::string, JSCallback> bindings;
52
+ std::unique_ptr<TrayManager> trayManager;
53
+ std::map<std::string, std::function<void(const std::string &)>> events;
54
+ WebGPU webgpu;
55
+ NavigationCallback navigationCallback;
56
+ LoadCallback loadStartCallback;
57
+ LoadCallback loadEndCallback;
58
+ LoadCallback navigationCompleteCallback;
59
+ ErrorCallback errorCallback;
60
+ ConsoleCallback consoleCallback;
61
+
24
62
  std::vector<MoveCallback> moveCallbacks;
25
63
  std::vector<ResizeCallback> resizeCallbacks;
26
64
  std::vector<CloseCallback> closeCallbacks;
@@ -30,6 +68,8 @@ struct Window::Impl {
30
68
  #ifdef _WIN32
31
69
  HWND hwnd = nullptr;
32
70
  WNDPROC originalProc = nullptr;
71
+ ComPtr<ICoreWebView2Controller> controller;
72
+ ComPtr<ICoreWebView2> webview;
33
73
 
34
74
  static LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
35
75
  Impl *impl = nullptr;
@@ -79,6 +119,10 @@ struct Window::Impl {
79
119
  }
80
120
  return DefWindowProc(hwnd, msg, wp, lp);
81
121
  }
122
+ #elif defined(__APPLE__)
123
+ WKWebView *wkWebView = nullptr;
124
+ #else
125
+ WebKitWebView *gtkWebView = nullptr;
82
126
  #endif
83
127
  };
84
128
 
@@ -86,6 +130,9 @@ Window::Window() : pImpl(std::shared_ptr<Impl>(new Impl())) {}
86
130
 
87
131
  Window::~Window() = default;
88
132
 
133
+ Window::Window(Window &&other) noexcept = default;
134
+ Window &Window::operator=(Window &&other) noexcept = default;
135
+
89
136
  Window Window::create(const WindowConfig &config) {
90
137
  Window w;
91
138
  w.pImpl->config = config;
@@ -198,6 +245,18 @@ Window Window::create(const WindowConfig &config) {
198
245
  w.pImpl->nativeWindow = (void *)gtkwin;
199
246
  #endif
200
247
 
248
+ // Initialize tray manager for native windows
249
+ w.pImpl->trayManager = std::make_unique<TrayManager>();
250
+ #ifdef _WIN32
251
+ if (w.pImpl->hwnd) {
252
+ w.pImpl->trayManager->setWindowHandle((void *)w.pImpl->hwnd);
253
+ }
254
+ #elif defined(__APPLE__)
255
+ w.pImpl->trayManager->setWindowHandle(w.pImpl->nativeWindow);
256
+ #else
257
+ w.pImpl->trayManager->setWindowHandle(w.pImpl->nativeWindow);
258
+ #endif
259
+
201
260
  return w;
202
261
  }
203
262
 
@@ -220,7 +279,10 @@ void Window::setTitle(const std::string &title) {
220
279
  #endif
221
280
  }
222
281
 
223
- std::string Window::getTitle() const { return pImpl->config.title; }
282
+ std::string Window::getTitle() const {
283
+ if (!pImpl->currentTitle.empty()) return pImpl->currentTitle;
284
+ return pImpl->config.title;
285
+ }
224
286
 
225
287
  void Window::setSize(int width, int height) {
226
288
  pImpl->config.width = width;
@@ -677,4 +739,979 @@ void Window::onStateChange(StateCallback callback) {
677
739
 
678
740
  void *Window::nativeHandle() const { return pImpl->nativeWindow; }
679
741
 
742
+ // WebView-specific implementations
743
+ Window Window::create(void *windowHandle, const WindowConfig &config) {
744
+ Window win;
745
+ win.pImpl->config = config;
746
+
747
+ #ifdef _WIN32
748
+ HWND hwnd = static_cast<HWND>(windowHandle);
749
+
750
+ // Create WebView2 environment and controller
751
+ auto pImpl = win.pImpl;
752
+ CreateCoreWebView2EnvironmentWithOptions(
753
+ nullptr, nullptr, nullptr,
754
+ Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
755
+ [hwnd, pImpl](HRESULT result,
756
+ ICoreWebView2Environment *env) -> HRESULT {
757
+ if (FAILED(result) || !env)
758
+ return result;
759
+ env->CreateCoreWebView2Controller(
760
+ hwnd,
761
+ Callback<
762
+ ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
763
+ [pImpl](HRESULT result,
764
+ ICoreWebView2Controller *controller) -> HRESULT {
765
+ (void)result; // Suppress unused warning
766
+ if (controller != nullptr) {
767
+ pImpl->controller = controller;
768
+ controller->get_CoreWebView2(&pImpl->webview);
769
+
770
+ RECT bounds;
771
+ HWND parentHwnd;
772
+ controller->get_ParentWindow(&parentHwnd);
773
+ GetClientRect(parentHwnd, &bounds);
774
+ controller->put_Bounds(bounds);
775
+
776
+ pImpl->nativeWebView = pImpl->webview.Get();
777
+ pImpl->ready = true;
778
+
779
+ // Inject bridge script that runs on EVERY document
780
+ // (survives navigation)
781
+ std::string bridgeScript = R"(
782
+ window.__native_invoke__ = function(request) {
783
+ if (window.chrome && window.chrome.webview) {
784
+ window.chrome.webview.postMessage(request);
785
+ }
786
+ };
787
+ )";
788
+ pImpl->webview->AddScriptToExecuteOnDocumentCreated(
789
+ std::wstring(bridgeScript.begin(),
790
+ bridgeScript.end())
791
+ .c_str(),
792
+ nullptr);
793
+
794
+ // Inject scrollbar hiding CSS if disabled
795
+ if (!pImpl->config.scrollbars) {
796
+ std::string scrollbarScript = R"(
797
+ (function() {
798
+ var css = 'html, body { overflow: hidden !important; margin: 0 !important; padding: 0 !important; } ::-webkit-scrollbar { display: none !important; width: 0 !important; height: 0 !important; }';
799
+ var style = document.createElement('style');
800
+ style.type = 'text/css';
801
+ style.appendChild(document.createTextNode(css));
802
+ (document.head || document.documentElement).appendChild(style);
803
+ })();
804
+ )";
805
+ pImpl->webview->AddScriptToExecuteOnDocumentCreated(
806
+ std::wstring(scrollbarScript.begin(),
807
+ scrollbarScript.end())
808
+ .c_str(),
809
+ nullptr);
810
+ }
811
+
812
+ // Disable webview drag & drop behavior if requested
813
+ // This prevents files from being loaded in the browser
814
+ // and allows the native FileDrop API to handle them instead
815
+ if (pImpl->config.disableWebviewDragDrop) {
816
+ std::string disableDragDropScript = R"(
817
+ (function() {
818
+ // Prevent default drag and drop behavior on document/window
819
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(function(eventName) {
820
+ document.addEventListener(eventName, function(e) {
821
+ // Only prevent default on document.body or document.documentElement
822
+ // to allow custom drop zones to still work
823
+ if (e.target === document.body || e.target === document.documentElement || e.target === document) {
824
+ e.preventDefault();
825
+ e.stopPropagation();
826
+ }
827
+ }, true);
828
+
829
+ window.addEventListener(eventName, function(e) {
830
+ e.preventDefault();
831
+ e.stopPropagation();
832
+ }, true);
833
+ });
834
+ })();
835
+ )";
836
+ pImpl->webview->AddScriptToExecuteOnDocumentCreated(
837
+ std::wstring(disableDragDropScript.begin(),
838
+ disableDragDropScript.end())
839
+ .c_str(),
840
+ nullptr);
841
+ }
842
+
843
+ // Set up WebMessageReceived handler for JS->C++ bridge
844
+ pImpl->webview->add_WebMessageReceived(
845
+ Callback<
846
+ ICoreWebView2WebMessageReceivedEventHandler>(
847
+ [pImpl](ICoreWebView2 *sender,
848
+ ICoreWebView2WebMessageReceivedEventArgs
849
+ *args) -> HRESULT {
850
+ (void)sender; // Suppress unused warning
851
+ LPWSTR message = nullptr;
852
+ args->TryGetWebMessageAsString(&message);
853
+ if (!message)
854
+ return S_OK;
855
+ std::wstring wmsg(message);
856
+ #pragma warning(push)
857
+ #pragma warning(disable: 4244) // Suppress wchar_t to char conversion warning
858
+ std::string msg(wmsg.begin(), wmsg.end());
859
+ #pragma warning(pop)
860
+ CoTaskMemFree(message);
861
+
862
+ // Debug: log received message
863
+ std::cout << "[PlusUI] Received: " << msg
864
+ << std::endl;
865
+
866
+ // Parse JSON-RPC: {"id":"...",
867
+ // "method":"window.minimize", "params":[...]}
868
+ std::string id, method;
869
+ std::string result = "null";
870
+ bool success = false;
871
+
872
+ // Simple JSON parsing
873
+ auto getId = [&msg]() {
874
+ size_t pos = msg.find("\"id\"");
875
+ if (pos == std::string::npos)
876
+ return std::string();
877
+ pos = msg.find(":", pos);
878
+ if (pos == std::string::npos)
879
+ return std::string();
880
+ size_t start = msg.find("\"", pos);
881
+ if (start == std::string::npos)
882
+ return std::string();
883
+ size_t end = msg.find("\"", start + 1);
884
+ if (end == std::string::npos)
885
+ return std::string();
886
+ return msg.substr(start + 1,
887
+ end - start - 1);
888
+ };
889
+ auto getMethod = [&msg]() {
890
+ size_t pos = msg.find("\"method\"");
891
+ if (pos == std::string::npos)
892
+ return std::string();
893
+ pos = msg.find(":", pos);
894
+ if (pos == std::string::npos)
895
+ return std::string();
896
+ size_t start = msg.find("\"", pos);
897
+ if (start == std::string::npos)
898
+ return std::string();
899
+ size_t end = msg.find("\"", start + 1);
900
+ if (end == std::string::npos)
901
+ return std::string();
902
+ return msg.substr(start + 1,
903
+ end - start - 1);
904
+ };
905
+
906
+ id = getId();
907
+ method = getMethod();
908
+
909
+ // Route to handlers
910
+ if (method.find("window.") == 0) {
911
+ std::string winMethod = method.substr(7);
912
+ if (winMethod == "minimize") {
913
+ if (pImpl->window) pImpl->window->minimize();
914
+ success = true;
915
+ } else if (winMethod == "maximize") {
916
+ if (pImpl->window) pImpl->window->maximize();
917
+ success = true;
918
+ } else if (winMethod == "restore") {
919
+ if (pImpl->window) pImpl->window->restore();
920
+ success = true;
921
+ } else if (winMethod == "close") {
922
+ if (pImpl->window) pImpl->window->close();
923
+ success = true;
924
+ } else if (winMethod == "show") {
925
+ if (pImpl->window) pImpl->window->show();
926
+ success = true;
927
+ } else if (winMethod == "hide") {
928
+ if (pImpl->window) pImpl->window->hide();
929
+ success = true;
930
+ } else if (winMethod == "getSize") {
931
+ if (pImpl->window) {
932
+ int w, h;
933
+ pImpl->window->getSize(w, h);
934
+ result = "{\"width\":" +
935
+ std::to_string(w) +
936
+ ",\"height\":" +
937
+ std::to_string(h) +
938
+ "}";
939
+ }
940
+ success = true;
941
+ } else if (winMethod == "getPosition") {
942
+ if (pImpl->window) {
943
+ int x, y;
944
+ pImpl->window->getPosition(x, y);
945
+ result =
946
+ "{\"x\":" + std::to_string(x) +
947
+ ",\"y\":" + std::to_string(y) +
948
+ "}";
949
+ }
950
+ success = true;
951
+ } else if (winMethod == "setSize") {
952
+ // Parse params [width, height]
953
+ size_t p1 = msg.find("[");
954
+ size_t p2 = msg.find("]");
955
+ if (p1 != std::string::npos &&
956
+ p2 != std::string::npos) {
957
+ std::string params =
958
+ msg.substr(p1 + 1, p2 - p1 - 1);
959
+ int w = 0, h = 0;
960
+ sscanf(params.c_str(), "%d, %d", &w,
961
+ &h);
962
+ if (pImpl->window) pImpl->window->setSize(w, h);
963
+ }
964
+ success = true;
965
+ } else if (winMethod == "setPosition") {
966
+ size_t p1 = msg.find("[");
967
+ size_t p2 = msg.find("]");
968
+ if (p1 != std::string::npos &&
969
+ p2 != std::string::npos) {
970
+ std::string params =
971
+ msg.substr(p1 + 1, p2 - p1 - 1);
972
+ int x = 0, y = 0;
973
+ sscanf(params.c_str(), "%d, %d", &x,
974
+ &y);
975
+ if (pImpl->window) pImpl->window->setPosition(x, y);
976
+ }
977
+ success = true;
978
+ } else if (winMethod == "setTitle") {
979
+ size_t p1 = msg.find("[\"");
980
+ size_t p2 = msg.find("\"]");
981
+ if (p1 != std::string::npos &&
982
+ p2 != std::string::npos) {
983
+ std::string title =
984
+ msg.substr(p1 + 2, p2 - p1 - 2);
985
+ if (pImpl->window) pImpl->window->setTitle(title);
986
+ }
987
+ success = true;
988
+ } else if (winMethod == "setFullscreen") {
989
+ size_t p1 = msg.find("[");
990
+ size_t p2 = msg.find("]");
991
+ if (p1 != std::string::npos &&
992
+ p2 != std::string::npos) {
993
+ std::string params =
994
+ msg.substr(p1 + 1, p2 - p1 - 1);
995
+ if (pImpl->window) pImpl->window->setFullscreen(
996
+ params.find("true") !=
997
+ std::string::npos);
998
+ }
999
+ success = true;
1000
+ } else if (winMethod == "setAlwaysOnTop") {
1001
+ size_t p1 = msg.find("[");
1002
+ size_t p2 = msg.find("]");
1003
+ if (p1 != std::string::npos &&
1004
+ p2 != std::string::npos) {
1005
+ std::string params =
1006
+ msg.substr(p1 + 1, p2 - p1 - 1);
1007
+ if (pImpl->window) pImpl->window->setAlwaysOnTop(
1008
+ params.find("true") !=
1009
+ std::string::npos);
1010
+ }
1011
+ success = true;
1012
+ } else if (winMethod == "setResizable") {
1013
+ size_t p1 = msg.find("[");
1014
+ size_t p2 = msg.find("]");
1015
+ if (p1 != std::string::npos &&
1016
+ p2 != std::string::npos) {
1017
+ std::string params =
1018
+ msg.substr(p1 + 1, p2 - p1 - 1);
1019
+ if (pImpl->window) pImpl->window->setResizable(
1020
+ params.find("true") !=
1021
+ std::string::npos);
1022
+ }
1023
+ success = true;
1024
+ } else if (winMethod == "isMaximized") {
1025
+ result =
1026
+ (pImpl->window && pImpl->window->isMaximized())
1027
+ ? "true"
1028
+ : "false";
1029
+ success = true;
1030
+ } else if (winMethod == "isMinimized") {
1031
+ result =
1032
+ (pImpl->window && pImpl->window->isMinimized())
1033
+ ? "true"
1034
+ : "false";
1035
+ success = true;
1036
+ } else if (winMethod == "isVisible") {
1037
+ result = (pImpl->window && pImpl->window->isVisible())
1038
+ ? "true"
1039
+ : "false";
1040
+ success = true;
1041
+ } else if (winMethod == "center") {
1042
+ if (pImpl->window) pImpl->window->center();
1043
+ success = true;
1044
+ }
1045
+ } else if (method.find("browser.") == 0) {
1046
+ std::string browserMethod =
1047
+ method.substr(8);
1048
+ if (browserMethod == "navigate") {
1049
+ size_t p1 = msg.find("[\"");
1050
+ size_t p2 = msg.find("\"]");
1051
+ if (p1 != std::string::npos &&
1052
+ p2 != std::string::npos) {
1053
+ std::string url =
1054
+ msg.substr(p1 + 2, p2 - p1 - 2);
1055
+ pImpl->webview->Navigate(
1056
+ std::wstring(url.begin(), url.end())
1057
+ .c_str());
1058
+ }
1059
+ success = true;
1060
+ } else if (browserMethod == "goBack") {
1061
+ pImpl->webview->GoBack();
1062
+ success = true;
1063
+ } else if (browserMethod == "goForward") {
1064
+ pImpl->webview->GoForward();
1065
+ success = true;
1066
+ } else if (browserMethod == "reload") {
1067
+ pImpl->webview->Reload();
1068
+ success = true;
1069
+ } else if (browserMethod == "stop") {
1070
+ pImpl->webview->Stop();
1071
+ success = true;
1072
+ } else if (browserMethod == "getUrl") {
1073
+ result = "\"" + pImpl->currentURL + "\"";
1074
+ success = true;
1075
+ } else if (browserMethod == "getTitle") {
1076
+ result =
1077
+ "\"" + pImpl->currentTitle + "\"";
1078
+ success = true;
1079
+ } else if (browserMethod == "canGoBack") {
1080
+ BOOL canBack;
1081
+ pImpl->webview->get_CanGoBack(&canBack);
1082
+ result = canBack ? "true" : "false";
1083
+ success = true;
1084
+ } else if (browserMethod ==
1085
+ "canGoForward") {
1086
+ BOOL canForward;
1087
+ pImpl->webview->get_CanGoForward(
1088
+ &canForward);
1089
+ result = canForward ? "true" : "false";
1090
+ success = true;
1091
+ }
1092
+ }
1093
+
1094
+ // Send response back to JS (matches
1095
+ // plusui-native-core SDK bridge)
1096
+ std::string response =
1097
+ "window.__response__(\"" + id + "\", " +
1098
+ result + ");";
1099
+ pImpl->webview->ExecuteScript(
1100
+ std::wstring(response.begin(),
1101
+ response.end())
1102
+ .c_str(),
1103
+ nullptr);
1104
+
1105
+ return S_OK;
1106
+ })
1107
+ .Get(),
1108
+ nullptr);
1109
+
1110
+ // Process pending scripts
1111
+ for (const auto &script : pImpl->pendingScripts) {
1112
+ pImpl->webview->ExecuteScript(
1113
+ std::wstring(script.begin(), script.end())
1114
+ .c_str(),
1115
+ nullptr);
1116
+ }
1117
+ pImpl->pendingScripts.clear();
1118
+
1119
+ // Process pending navigation
1120
+ if (!pImpl->pendingNavigation.empty()) {
1121
+ pImpl->webview->Navigate(
1122
+ std::wstring(pImpl->pendingNavigation.begin(),
1123
+ pImpl->pendingNavigation.end())
1124
+ .c_str());
1125
+ pImpl->pendingNavigation.clear();
1126
+ }
1127
+
1128
+ // Process pending HTML
1129
+ if (!pImpl->pendingHTML.empty()) {
1130
+ pImpl->webview->NavigateToString(
1131
+ std::wstring(pImpl->pendingHTML.begin(),
1132
+ pImpl->pendingHTML.end())
1133
+ .c_str());
1134
+ pImpl->pendingHTML.clear();
1135
+ }
1136
+
1137
+ // Process pending File
1138
+ if (!pImpl->pendingFile.empty()) {
1139
+ // For now, loadFile is implemented via navigate in
1140
+ // some versions or direct file reading. We'll handle
1141
+ // it via navigate for simplicity if it's already
1142
+ // implemented that way.
1143
+ }
1144
+ }
1145
+ return S_OK;
1146
+ })
1147
+ .Get());
1148
+ return S_OK;
1149
+ })
1150
+ .Get());
1151
+
1152
+ #elif defined(__APPLE__)
1153
+ WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
1154
+ config.preferences.javaScriptEnabled = YES;
1155
+
1156
+ if (win.pImpl->config.devtools) {
1157
+ [config.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
1158
+ }
1159
+
1160
+ // Disable webview drag & drop behavior to allow native FileDrop API
1161
+ if (win.pImpl->config.disableWebviewDragDrop) {
1162
+ NSString *disableDragDropScript = @"(function() {"
1163
+ "['dragenter', 'dragover', 'dragleave', 'drop'].forEach(function(eventName) {"
1164
+ "document.addEventListener(eventName, function(e) {"
1165
+ "if (e.target === document.body || e.target === document.documentElement || e.target === document) {"
1166
+ "e.preventDefault();"
1167
+ "e.stopPropagation();"
1168
+ "}"
1169
+ "}, true);"
1170
+ "window.addEventListener(eventName, function(e) {"
1171
+ "e.preventDefault();"
1172
+ "e.stopPropagation();"
1173
+ "}, true);"
1174
+ "});"
1175
+ "})();";
1176
+
1177
+ WKUserScript *userScript = [[WKUserScript alloc]
1178
+ initWithSource:disableDragDropScript
1179
+ injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
1180
+ forMainFrameOnly:NO];
1181
+ [config.userContentController addUserScript:userScript];
1182
+ }
1183
+
1184
+ // Hide scrollbars if disabled
1185
+ if (!win.pImpl->config.scrollbars) {
1186
+ NSString *scrollbarScript = @"(function() {"
1187
+ "var css = 'html, body { overflow: hidden !important; margin: 0 !important; padding: 0 !important; } ::-webkit-scrollbar { display: none !important; width: 0 !important; height: 0 !important; }';"
1188
+ "var style = document.createElement('style');"
1189
+ "style.type = 'text/css';"
1190
+ "style.appendChild(document.createTextNode(css));"
1191
+ "(document.head || document.documentElement).appendChild(style);"
1192
+ "})();";
1193
+
1194
+ WKUserScript *scrollScript = [[WKUserScript alloc]
1195
+ initWithSource:scrollbarScript
1196
+ injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
1197
+ forMainFrameOnly:NO];
1198
+ [config.userContentController addUserScript:scrollScript];
1199
+ }
1200
+
1201
+ NSView *parentView = (__bridge NSView *)windowHandle;
1202
+ win.pImpl->wkWebView =
1203
+ [[WKWebView alloc] initWithFrame:parentView.bounds configuration:config];
1204
+ [parentView addSubview:win.pImpl->wkWebView];
1205
+
1206
+ win.pImpl->nativeWebView = (__bridge void *)win.pImpl->wkWebView;
1207
+
1208
+ #endif
1209
+
1210
+ win.pImpl->trayManager = std::make_unique<TrayManager>();
1211
+ win.pImpl->trayManager->setWindowHandle(windowHandle);
1212
+
1213
+ return win;
1214
+ }
1215
+
1216
+ TrayManager &Window::tray() { return *pImpl->trayManager; }
1217
+
1218
+ void Window::setWindow(std::shared_ptr<Window> win) {
1219
+ pImpl->window = win;
1220
+ if (!win)
1221
+ return;
1222
+
1223
+ std::weak_ptr<Impl> weak_pImpl = pImpl;
1224
+ win->onResize([weak_pImpl](int w, int h) {
1225
+ if (auto pImpl = weak_pImpl.lock()) {
1226
+ #ifdef _WIN32
1227
+ if (pImpl->controller) {
1228
+ RECT bounds = {0, 0, w, h};
1229
+ pImpl->controller->put_Bounds(bounds);
1230
+ }
1231
+ #elif defined(__APPLE__)
1232
+ if (pImpl->wkWebView) {
1233
+ NSView *view = (__bridge NSView *)pImpl->wkWebView;
1234
+ NSView *parent = [view superview];
1235
+ if (parent) {
1236
+ [view setFrame:[parent bounds]];
1237
+ }
1238
+ }
1239
+ #else
1240
+ if (pImpl->gtkWebView) {
1241
+ // GTK usually handles this if added to a container with expand=TRUE
1242
+ // but we can ensure it here if it's a fixed layout parent
1243
+ gtk_widget_set_size_request(GTK_WIDGET(pImpl->gtkWebView), w, h);
1244
+ }
1245
+ #endif
1246
+ }
1247
+ });
1248
+
1249
+ // Trigger initial resize
1250
+ int w, h;
1251
+ win->getSize(w, h);
1252
+ #ifdef _WIN32
1253
+ if (pImpl->controller) {
1254
+ RECT bounds = {0, 0, w, h};
1255
+ pImpl->controller->put_Bounds(bounds);
1256
+ }
1257
+ #endif
1258
+ }
1259
+
1260
+ void Window::on(const std::string &event,
1261
+ std::function<void(const std::string &)> callback) {
1262
+ pImpl->events[event] = callback;
1263
+ }
1264
+
1265
+ void Window::emit(const std::string &event, const std::string &data) {
1266
+ std::string js = "window.dispatchEvent(new CustomEvent('" + event +
1267
+ "', {detail: " + data + "}))";
1268
+ executeScript(js);
1269
+ }
1270
+
1271
+ void Window::navigate(const std::string &url) {
1272
+ pImpl->currentURL = url;
1273
+ pImpl->loading = true;
1274
+
1275
+ #ifdef _WIN32
1276
+ if (pImpl->ready && pImpl->webview) {
1277
+ pImpl->webview->Navigate(std::wstring(url.begin(), url.end()).c_str());
1278
+ } else {
1279
+ pImpl->pendingNavigation = url;
1280
+ }
1281
+ #elif defined(__APPLE__)
1282
+ if (pImpl->wkWebView) {
1283
+ NSURL *nsurl =
1284
+ [NSURL URLWithString:[NSString stringWithUTF8String:url.c_str()]];
1285
+ NSURLRequest *request = [NSURLRequest requestWithURL:nsurl];
1286
+ [pImpl->wkWebView loadRequest:request];
1287
+ }
1288
+ #else
1289
+ if (pImpl->gtkWebView) {
1290
+ webkit_web_view_load_uri(pImpl->gtkWebView, url.c_str());
1291
+ }
1292
+ #endif
1293
+
1294
+ // Apply scrollbar hiding CSS if configured
1295
+ if (!pImpl->config.scrollbars) {
1296
+ std::string hideScrollbars = R"(
1297
+ (function() {
1298
+ document.documentElement.style.overflow = 'hidden';
1299
+ document.documentElement.style.margin = '0';
1300
+ document.documentElement.style.padding = '0';
1301
+ document.body.style.overflow = 'hidden';
1302
+ document.body.style.margin = '0';
1303
+ document.body.style.padding = '0';
1304
+ })();
1305
+ )";
1306
+ // Schedule this to run after a short delay to ensure DOM is ready
1307
+ #ifdef _WIN32
1308
+ if (pImpl->ready && pImpl->webview) {
1309
+ pImpl->webview->ExecuteScript(
1310
+ std::wstring(hideScrollbars.begin(), hideScrollbars.end()).c_str(),
1311
+ nullptr);
1312
+ }
1313
+ #elif defined(__APPLE__)
1314
+ if (pImpl->wkWebView) {
1315
+ NSString *js = [NSString stringWithUTF8String:hideScrollbars.c_str()];
1316
+ [pImpl->wkWebView evaluateJavaScript:js completionHandler:nil];
1317
+ }
1318
+ #else
1319
+ if (pImpl->gtkWebView) {
1320
+ webkit_web_view_run_javascript(pImpl->gtkWebView, hideScrollbars.c_str(),
1321
+ nullptr, nullptr, nullptr);
1322
+ }
1323
+ #endif
1324
+ }
1325
+ }
1326
+
1327
+ void Window::loadURL(const std::string &url) {
1328
+ navigate(url);
1329
+ }
1330
+
1331
+ void Window::loadHTML(const std::string &html) {
1332
+ loadHTML(html, "about:blank");
1333
+ }
1334
+
1335
+ void Window::loadHTML(const std::string &html, const std::string &baseURL) {
1336
+ pImpl->loading = true;
1337
+
1338
+ #ifdef _WIN32
1339
+ (void)baseURL; // Not used on Windows
1340
+ if (pImpl->ready && pImpl->webview) {
1341
+ pImpl->webview->NavigateToString(
1342
+ std::wstring(html.begin(), html.end()).c_str());
1343
+ } else {
1344
+ pImpl->pendingHTML = html;
1345
+ }
1346
+ #elif defined(__APPLE__)
1347
+ if (pImpl->wkWebView) {
1348
+ NSString *htmlString = [NSString stringWithUTF8String:html.c_str()];
1349
+ NSURL *base =
1350
+ [NSURL URLWithString:[NSString stringWithUTF8String:baseURL.c_str()]];
1351
+ [pImpl->wkWebView loadHTMLString:htmlString baseURL:base];
1352
+ }
1353
+ #else
1354
+ if (pImpl->gtkWebView) {
1355
+ webkit_web_view_load_html(pImpl->gtkWebView, html.c_str(), baseURL.c_str());
1356
+ }
1357
+ #endif
1358
+ }
1359
+
1360
+ void Window::loadFile(const std::string &filePath) {
1361
+ std::ifstream file(filePath);
1362
+ if (!file) {
1363
+ std::cerr << "PlusUI: Failed to open file: " << filePath << std::endl;
1364
+ return;
1365
+ }
1366
+
1367
+ std::stringstream buffer;
1368
+ buffer << file.rdbuf();
1369
+
1370
+ // Use file:// URL as base for relative paths
1371
+ std::string baseURL =
1372
+ "file://" + filePath.substr(0, filePath.find_last_of("/\\") + 1);
1373
+ loadHTML(buffer.str(), baseURL);
1374
+ }
1375
+
1376
+ void Window::reload() {
1377
+ #ifdef _WIN32
1378
+ if (pImpl->webview) {
1379
+ pImpl->webview->Reload();
1380
+ }
1381
+ #elif defined(__APPLE__)
1382
+ if (pImpl->wkWebView) {
1383
+ [pImpl->wkWebView reload];
1384
+ }
1385
+ #else
1386
+ if (pImpl->gtkWebView) {
1387
+ webkit_web_view_reload(pImpl->gtkWebView);
1388
+ }
1389
+ #endif
1390
+ }
1391
+
1392
+ void Window::stop() {
1393
+ #ifdef _WIN32
1394
+ if (pImpl->webview) {
1395
+ pImpl->webview->Stop();
1396
+ }
1397
+ #elif defined(__APPLE__)
1398
+ if (pImpl->wkWebView) {
1399
+ [pImpl->wkWebView stopLoading];
1400
+ }
1401
+ #else
1402
+ if (pImpl->gtkWebView) {
1403
+ webkit_web_view_stop_loading(pImpl->gtkWebView);
1404
+ }
1405
+ #endif
1406
+ }
1407
+
1408
+ void Window::goBack() {
1409
+ #ifdef _WIN32
1410
+ if (pImpl->webview) {
1411
+ pImpl->webview->GoBack();
1412
+ }
1413
+ #elif defined(__APPLE__)
1414
+ if (pImpl->wkWebView) {
1415
+ [pImpl->wkWebView goBack];
1416
+ }
1417
+ #else
1418
+ if (pImpl->gtkWebView) {
1419
+ webkit_web_view_go_back(pImpl->gtkWebView);
1420
+ }
1421
+ #endif
1422
+ }
1423
+
1424
+ void Window::goForward() {
1425
+ #ifdef _WIN32
1426
+ if (pImpl->webview) {
1427
+ pImpl->webview->GoForward();
1428
+ }
1429
+ #elif defined(__APPLE__)
1430
+ if (pImpl->wkWebView) {
1431
+ [pImpl->wkWebView goForward];
1432
+ }
1433
+ #else
1434
+ if (pImpl->gtkWebView) {
1435
+ webkit_web_view_go_forward(pImpl->gtkWebView);
1436
+ }
1437
+ #endif
1438
+ }
1439
+
1440
+ bool Window::canGoBack() const {
1441
+ #ifdef _WIN32
1442
+ if (pImpl->webview) {
1443
+ BOOL canGoBack;
1444
+ pImpl->webview->get_CanGoBack(&canGoBack);
1445
+ return canGoBack;
1446
+ }
1447
+ #elif defined(__APPLE__)
1448
+ if (pImpl->wkWebView) {
1449
+ return [pImpl->wkWebView canGoBack];
1450
+ }
1451
+ #else
1452
+ if (pImpl->gtkWebView) {
1453
+ return webkit_web_view_can_go_back(pImpl->gtkWebView);
1454
+ }
1455
+ #endif
1456
+ return false;
1457
+ }
1458
+
1459
+ bool Window::canGoForward() const {
1460
+ #ifdef _WIN32
1461
+ if (pImpl->webview) {
1462
+ BOOL canGoForward;
1463
+ pImpl->webview->get_CanGoForward(&canGoForward);
1464
+ return canGoForward;
1465
+ }
1466
+ #elif defined(__APPLE__)
1467
+ if (pImpl->wkWebView) {
1468
+ return [pImpl->wkWebView canGoForward];
1469
+ }
1470
+ #else
1471
+ if (pImpl->gtkWebView) {
1472
+ return webkit_web_view_can_go_forward(pImpl->gtkWebView);
1473
+ }
1474
+ #endif
1475
+ return false;
1476
+ }
1477
+
1478
+ void Window::executeScript(const std::string &script) {
1479
+ #ifdef _WIN32
1480
+ if (pImpl->ready && pImpl->webview) {
1481
+ pImpl->webview->ExecuteScript(
1482
+ std::wstring(script.begin(), script.end()).c_str(), nullptr);
1483
+ } else {
1484
+ pImpl->pendingScripts.push_back(script);
1485
+ }
1486
+ #elif defined(__APPLE__)
1487
+ if (pImpl->wkWebView) {
1488
+ NSString *js = [NSString stringWithUTF8String:script.c_str()];
1489
+ [pImpl->wkWebView evaluateJavaScript:js completionHandler:nil];
1490
+ }
1491
+ #else
1492
+ if (pImpl->gtkWebView) {
1493
+ webkit_web_view_run_javascript(pImpl->gtkWebView, script.c_str(), nullptr,
1494
+ nullptr, nullptr);
1495
+ }
1496
+ #endif
1497
+ }
1498
+
1499
+ void Window::executeScript(const std::string &script,
1500
+ std::function<void(const std::string &)> callback) {
1501
+ #ifdef _WIN32
1502
+ if (pImpl->webview) {
1503
+ pImpl->webview->ExecuteScript(
1504
+ std::wstring(script.begin(), script.end()).c_str(),
1505
+ Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
1506
+ [callback](HRESULT error, LPCWSTR result) -> HRESULT {
1507
+ (void)error; // Suppress unused warning
1508
+ if (result && callback) {
1509
+ std::wstring wstr(result);
1510
+ callback(std::string(wstr.begin(), wstr.end()));
1511
+ }
1512
+ return S_OK;
1513
+ })
1514
+ .Get());
1515
+ }
1516
+ #elif defined(__APPLE__)
1517
+ if (pImpl->wkWebView) {
1518
+ NSString *js = [NSString stringWithUTF8String:script.c_str()];
1519
+ [pImpl->wkWebView evaluateJavaScript:js
1520
+ completionHandler:^(id result, NSError *error) {
1521
+ if (result && callback) {
1522
+ NSString *resultStr =
1523
+ [NSString stringWithFormat:@"%@", result];
1524
+ callback([resultStr UTF8String]);
1525
+ }
1526
+ }];
1527
+ }
1528
+ #else
1529
+ if (pImpl->gtkWebView) {
1530
+ // GTK WebKit callback handling would go here
1531
+ webkit_web_view_run_javascript(pImpl->gtkWebView, script.c_str(), nullptr,
1532
+ nullptr, nullptr);
1533
+ }
1534
+ #endif
1535
+ }
1536
+
1537
+ void Window::bind(const std::string &name, JSCallback callback) {
1538
+ pImpl->bindings[name] = callback;
1539
+
1540
+ // Inject bridge code to expose function to JavaScript
1541
+ std::string bridgeScript = R"(
1542
+ window.)" + name + R"( = function(...args) {
1543
+ return window.plusui.invoke('webview.)" +
1544
+ name + R"(', args);
1545
+ };
1546
+ )";
1547
+
1548
+ executeScript(bridgeScript);
1549
+ }
1550
+
1551
+ void Window::unbind(const std::string &name) {
1552
+ pImpl->bindings.erase(name);
1553
+
1554
+ std::string script = "delete window." + name + ";";
1555
+ executeScript(script);
1556
+ }
1557
+
1558
+ void Window::openDevTools() {
1559
+ #ifdef _WIN32
1560
+ if (pImpl->webview) {
1561
+ pImpl->webview->OpenDevToolsWindow();
1562
+ }
1563
+ #elif defined(__APPLE__)
1564
+ // macOS: Dev tools open in Safari's Web Inspector
1565
+ #else
1566
+ if (pImpl->gtkWebView) {
1567
+ WebKitWebInspector *inspector =
1568
+ webkit_web_view_get_inspector(pImpl->gtkWebView);
1569
+ webkit_web_inspector_show(inspector);
1570
+ }
1571
+ #endif
1572
+ }
1573
+
1574
+ void Window::closeDevTools() {
1575
+ #ifdef _WIN32
1576
+ // WebView2 doesn't have explicit close
1577
+ #elif defined(__APPLE__)
1578
+ // macOS: Handled by Web Inspector
1579
+ #else
1580
+ if (pImpl->gtkWebView) {
1581
+ WebKitWebInspector *inspector =
1582
+ webkit_web_view_get_inspector(pImpl->gtkWebView);
1583
+ webkit_web_inspector_close(inspector);
1584
+ }
1585
+ #endif
1586
+ }
1587
+
1588
+ void Window::setUserAgent(const std::string &userAgent) {
1589
+ pImpl->userAgent = userAgent;
1590
+
1591
+ #ifdef _WIN32
1592
+ if (pImpl->webview) {
1593
+ ComPtr<ICoreWebView2Settings> settings;
1594
+ pImpl->webview->get_Settings(&settings);
1595
+ // WebView2 user agent requires settings2 interface
1596
+ }
1597
+ #elif defined(__APPLE__)
1598
+ if (pImpl->wkWebView) {
1599
+ [pImpl->wkWebView
1600
+ setCustomUserAgent:[NSString stringWithUTF8String:userAgent.c_str()]];
1601
+ }
1602
+ #else
1603
+ if (pImpl->gtkWebView) {
1604
+ WebKitSettings *settings = webkit_web_view_get_settings(pImpl->gtkWebView);
1605
+ webkit_settings_set_user_agent(settings, userAgent.c_str());
1606
+ }
1607
+ #endif
1608
+ }
1609
+
1610
+ std::string Window::getUserAgent() const { return pImpl->userAgent; }
1611
+
1612
+ void Window::setZoom(double factor) {
1613
+ pImpl->zoom = factor;
1614
+
1615
+ #ifdef _WIN32
1616
+ if (pImpl->controller) {
1617
+ pImpl->controller->put_ZoomFactor(factor);
1618
+ }
1619
+ #elif defined(__APPLE__)
1620
+ if (pImpl->wkWebView) {
1621
+ [pImpl->wkWebView setPageZoom:factor];
1622
+ }
1623
+ #else
1624
+ if (pImpl->gtkWebView) {
1625
+ webkit_web_view_set_zoom_level(pImpl->gtkWebView, factor);
1626
+ }
1627
+ #endif
1628
+ }
1629
+
1630
+ double Window::getZoom() const { return pImpl->zoom; }
1631
+
1632
+ void Window::injectCSS(const std::string &css) {
1633
+ std::string script = R"(
1634
+ (function() {
1635
+ const style = document.createElement('style');
1636
+ style.id = 'plusui-injected-css-' + Date.now();
1637
+ style.textContent = `)" +
1638
+ css + R"(`;
1639
+ if (document.head) {
1640
+ document.head.appendChild(style);
1641
+ } else {
1642
+ document.documentElement.appendChild(style);
1643
+ }
1644
+ })();
1645
+ )";
1646
+
1647
+ executeScript(script);
1648
+ }
1649
+
1650
+ void Window::setWebviewDragDropEnabled(bool enabled) {
1651
+ std::string script;
1652
+
1653
+ if (enabled) {
1654
+ // Enable webview drag-drop by removing our prevention handlers
1655
+ script = R"(
1656
+ (function() {
1657
+ if (window.__plusui_dragDropDisabled) {
1658
+ delete window.__plusui_dragDropDisabled;
1659
+ }
1660
+ })();
1661
+ )";
1662
+ } else {
1663
+ // Disable webview drag-drop by preventing default behavior
1664
+ script = R"(
1665
+ (function() {
1666
+ if (window.__plusui_dragDropDisabled) return; // Already disabled
1667
+ window.__plusui_dragDropDisabled = true;
1668
+
1669
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(function(eventName) {
1670
+ document.addEventListener(eventName, function(e) {
1671
+ if (e.target === document.body || e.target === document.documentElement || e.target === document) {
1672
+ e.preventDefault();
1673
+ e.stopPropagation();
1674
+ }
1675
+ }, true);
1676
+
1677
+ window.addEventListener(eventName, function(e) {
1678
+ e.preventDefault();
1679
+ e.stopPropagation();
1680
+ }, true);
1681
+ });
1682
+ })();
1683
+ )";
1684
+ }
1685
+
1686
+ executeScript(script);
1687
+ }
1688
+
1689
+ void Window::onNavigationStart(NavigationCallback callback) {
1690
+ pImpl->navigationCallback = callback;
1691
+ }
1692
+
1693
+ void Window::onNavigationComplete(LoadCallback callback) {
1694
+ pImpl->navigationCompleteCallback = callback;
1695
+ }
1696
+
1697
+ void Window::onLoadStart(LoadCallback callback) {
1698
+ pImpl->loadStartCallback = callback;
1699
+ }
1700
+
1701
+ void Window::onLoadEnd(LoadCallback callback) {
1702
+ pImpl->loadEndCallback = callback;
1703
+ }
1704
+
1705
+ void Window::onLoadError(ErrorCallback callback) {
1706
+ pImpl->errorCallback = callback;
1707
+ }
1708
+
1709
+ void Window::onConsoleMessage(ConsoleCallback callback) {
1710
+ pImpl->consoleCallback = callback;
1711
+ }
1712
+
1713
+ bool Window::isLoading() const { return pImpl->loading; }
1714
+
1715
+ std::string Window::getURL() const { return pImpl->currentURL; }
1716
+
680
1717
  } // namespace plusui
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plusui-native-core",
3
- "version": "0.1.34",
3
+ "version": "0.1.36",
4
4
  "description": "PlusUI Core framework (frontend + backend implementations)",
5
5
  "type": "module",
6
6
  "files": [