plusui-native-core 0.1.4 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/Core/CMakeLists.txt +190 -7
  2. package/Core/Features/App/app.cpp +129 -0
  3. package/Core/Features/App/app.ts +126 -0
  4. package/Core/Features/Browser/browser.cpp +181 -0
  5. package/Core/Features/Browser/browser.ts +182 -0
  6. package/Core/Features/Clipboard/clipboard.cpp +234 -0
  7. package/Core/Features/Clipboard/clipboard.ts +113 -0
  8. package/Core/Features/Display/display.cpp +209 -0
  9. package/Core/Features/Display/display.ts +104 -0
  10. package/Core/Features/Event/Events.ts +166 -0
  11. package/Core/Features/Event/events.cpp +200 -0
  12. package/Core/Features/Keyboard/keyboard.cpp +186 -0
  13. package/Core/Features/Keyboard/keyboard.ts +175 -0
  14. package/Core/Features/Menu/context-menu.css +293 -0
  15. package/Core/Features/Menu/menu.cpp +481 -0
  16. package/Core/Features/Menu/menu.ts +439 -0
  17. package/Core/Features/Tray/tray.cpp +310 -0
  18. package/Core/Features/Tray/tray.ts +68 -0
  19. package/Core/Features/WebGPU/webgpu.cpp +939 -0
  20. package/Core/Features/WebGPU/webgpu.ts +1013 -0
  21. package/Core/Features/WebView/webview.cpp +1052 -0
  22. package/Core/Features/WebView/webview.ts +510 -0
  23. package/Core/Features/Window/window.cpp +664 -0
  24. package/Core/Features/Window/window.ts +142 -0
  25. package/Core/Features/WindowManager/window_manager.cpp +341 -0
  26. package/Core/include/plusui/app.hpp +73 -0
  27. package/Core/include/plusui/browser.hpp +66 -0
  28. package/Core/include/plusui/clipboard.hpp +41 -0
  29. package/Core/include/plusui/events.hpp +58 -0
  30. package/Core/include/{keyboard.hpp → plusui/keyboard.hpp} +21 -44
  31. package/Core/include/plusui/menu.hpp +153 -0
  32. package/Core/include/plusui/tray.hpp +93 -0
  33. package/Core/include/plusui/webgpu.hpp +434 -0
  34. package/Core/include/plusui/webview.hpp +142 -0
  35. package/Core/include/plusui/window.hpp +111 -0
  36. package/Core/include/plusui/window_manager.hpp +57 -0
  37. package/Core/vendor/WebView2EnvironmentOptions.h +406 -0
  38. package/Core/vendor/stb_image.h +7988 -0
  39. package/Core/vendor/webview.h +618 -510
  40. package/Core/vendor/webview2.h +52079 -0
  41. package/README.md +19 -0
  42. package/package.json +12 -15
  43. package/Core/include/app.hpp +0 -121
  44. package/Core/include/menu.hpp +0 -79
  45. package/Core/include/tray.hpp +0 -81
  46. package/Core/include/window.hpp +0 -106
  47. package/Core/src/app.cpp +0 -311
  48. package/Core/src/display.cpp +0 -424
  49. package/Core/src/tray.cpp +0 -275
  50. package/Core/src/window.cpp +0 -528
  51. package/dist/index.d.ts +0 -205
  52. package/dist/index.js +0 -198
  53. package/src/index.ts +0 -574
  54. /package/Core/include/{display.hpp → plusui/display.hpp} +0 -0
@@ -5,11 +5,33 @@ set(CMAKE_CXX_STANDARD 20)
5
5
  set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
6
  set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
7
7
 
8
+ # ============================================================
9
+ # WebGPU (Optional - disabled by default)
10
+ # ============================================================
11
+ option(PLUSUI_ENABLE_WEBGPU "Enable WebGPU support via Dawn" OFF)
12
+
13
+ # Platform detection
14
+ if(WIN32)
15
+ add_definitions(-DPLUSUI_EDGE)
16
+ elseif(APPLE)
17
+ add_definitions(-DPLUSUI_COCOA)
18
+ else()
19
+ add_definitions(-DPLUSUI_GTK)
20
+ endif()
21
+
8
22
  add_library(plusui STATIC
9
- src/app.cpp
10
- src/window.cpp
11
- src/display.cpp
12
- src/tray.cpp
23
+ Features/App/app.cpp
24
+ Features/Window/window.cpp
25
+ Features/WindowManager/window_manager.cpp
26
+ Features/WebView/webview.cpp
27
+ Features/Browser/browser.cpp
28
+ Features/Display/display.cpp
29
+ Features/Tray/tray.cpp
30
+ Features/Clipboard/clipboard.cpp
31
+ Features/Menu/menu.cpp
32
+ Features/Keyboard/keyboard.cpp
33
+ Features/Event/events.cpp
34
+ Features/WebGPU/webgpu.cpp
13
35
  )
14
36
 
15
37
  target_include_directories(plusui PUBLIC
@@ -18,16 +40,177 @@ target_include_directories(plusui PUBLIC
18
40
  $<INSTALL_INTERFACE:include>
19
41
  )
20
42
 
21
- target_compile_definitions(plusui PUBLIC WEBVIEW_HEADER)
43
+ # Define WEBVIEW_EDGE on Windows to enable WebView2 backend
44
+ if(WIN32)
45
+ target_compile_definitions(plusui PUBLIC WEBVIEW_EDGE)
46
+ endif()
22
47
  target_compile_options(plusui PRIVATE
23
- $<$<CXX_COMPILER_ID:MSVC>:/W4 /utf-8>
48
+ $<$<CXX_COMPILER_ID:MSVC>:/W4 /utf-8 /FS>
24
49
  $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra>
25
50
  )
26
51
 
27
52
  find_package(Threads REQUIRED)
53
+ target_link_libraries(plusui PRIVATE Threads::Threads)
54
+
55
+ # ============================================================
56
+ # WebGPU: Dawn Library (Optional)
57
+ # ============================================================
58
+ if(PLUSUI_ENABLE_WEBGPU)
59
+ include(FetchContent)
60
+
61
+ set(DAWN_FETCH_DEPENDENCIES ON)
62
+ set(DAWN_BUILD_SAMPLES OFF)
63
+ set(DAWN_BUILD_TESTS OFF)
64
+ if(WIN32)
65
+ set(DAWN_ENABLE_D3D12 ON)
66
+ set(DAWN_ENABLE_METAL OFF)
67
+ set(DAWN_ENABLE_VULKAN OFF)
68
+ set(DAWN_USE_X11 OFF)
69
+ elseif(APPLE)
70
+ set(DAWN_ENABLE_D3D12 OFF)
71
+ set(DAWN_ENABLE_METAL ON)
72
+ set(DAWN_ENABLE_VULKAN OFF)
73
+ set(DAWN_USE_X11 OFF)
74
+ else()
75
+ set(DAWN_ENABLE_D3D12 OFF)
76
+ set(DAWN_ENABLE_METAL OFF)
77
+ set(DAWN_ENABLE_VULKAN ON)
78
+ set(DAWN_USE_X11 ON)
79
+ endif()
80
+
81
+ FetchContent_Declare(
82
+ dawn
83
+ GIT_REPOSITORY https://dawn.googlesource.com/dawn
84
+ GIT_TAG d41e7df926f0d84ca64262cffa7f3a0ddc56c53c
85
+ GIT_SHALLOW TRUE
86
+ TIMEOUT 600
87
+ )
88
+
89
+ message(STATUS "Fetching Dawn WebGPU library...")
90
+ FetchContent_MakeAvailable(dawn)
91
+
92
+ target_link_libraries(plusui PUBLIC
93
+ dawncpp
94
+ dawn_native
95
+ dawn_proc
96
+ )
97
+
98
+ target_include_directories(plusui PUBLIC
99
+ ${dawn_SOURCE_DIR}/include
100
+ ${dawn_BINARY_DIR}/gen/include
101
+ )
102
+
103
+ target_compile_definitions(plusui PUBLIC PLUSUI_WEBGPU_ENABLED)
104
+ message(STATUS "WebGPU support enabled via Dawn")
105
+ else()
106
+ message(STATUS "WebGPU support disabled (set PLUSUI_ENABLE_WEBGPU=ON to enable)")
107
+ endif()
28
108
 
109
+ # ============================================================
110
+ # Windows: WebView2 SDK
111
+ # ============================================================
29
112
  if(WIN32)
30
- find_package(WebView2 QUIET)
113
+ include(FetchContent)
114
+
115
+ # Download Microsoft WebView2 SDK
116
+ FetchContent_Declare(
117
+ webview2
118
+ URL https://www.nuget.org/api/v2/package/Microsoft.Web.WebView2/1.0.2210.55
119
+ URL_HASH SHA256=482d0b55f78a9a90fe4a03e66b5ba5d0dc4b9b55dfe9e9e4e3f3e6e6e3e3e3e3e
120
+ DOWNLOAD_NO_EXTRACT FALSE
121
+ )
122
+
123
+ # Check if already downloaded, if not use alternative method
124
+ set(WEBVIEW2_DIR "${CMAKE_BINARY_DIR}/_deps/webview2")
125
+
126
+ if(NOT EXISTS "${WEBVIEW2_DIR}/build/native/include/WebView2.h")
127
+ message(STATUS "Downloading WebView2 SDK...")
128
+
129
+ # Create directory
130
+ file(MAKE_DIRECTORY "${WEBVIEW2_DIR}")
131
+
132
+ # Download the NuGet package
133
+ set(WEBVIEW2_NUGET_URL "https://www.nuget.org/api/v2/package/Microsoft.Web.WebView2/1.0.2210.55")
134
+ set(WEBVIEW2_ZIP "${WEBVIEW2_DIR}/webview2.zip")
135
+
136
+ if(NOT EXISTS "${WEBVIEW2_ZIP}")
137
+ file(DOWNLOAD
138
+ "${WEBVIEW2_NUGET_URL}"
139
+ "${WEBVIEW2_ZIP}"
140
+ SHOW_PROGRESS
141
+ STATUS DOWNLOAD_STATUS
142
+ )
143
+ list(GET DOWNLOAD_STATUS 0 STATUS_CODE)
144
+ if(NOT STATUS_CODE EQUAL 0)
145
+ message(WARNING "Failed to download WebView2 SDK. Trying alternative...")
146
+ # Try alternative URL
147
+ file(DOWNLOAD
148
+ "https://globalcdn.nuget.org/packages/microsoft.web.webview2.1.0.2210.55.nupkg"
149
+ "${WEBVIEW2_ZIP}"
150
+ SHOW_PROGRESS
151
+ )
152
+ endif()
153
+ endif()
154
+
155
+ # Extract the package
156
+ if(EXISTS "${WEBVIEW2_ZIP}" AND NOT EXISTS "${WEBVIEW2_DIR}/build")
157
+ message(STATUS "Extracting WebView2 SDK...")
158
+ execute_process(
159
+ COMMAND ${CMAKE_COMMAND} -E tar xzf "${WEBVIEW2_ZIP}"
160
+ WORKING_DIRECTORY "${WEBVIEW2_DIR}"
161
+ )
162
+ endif()
163
+ endif()
164
+
165
+ # Add WebView2 include directory
166
+ if(EXISTS "${WEBVIEW2_DIR}/build/native/include")
167
+ target_include_directories(plusui PUBLIC "${WEBVIEW2_DIR}/build/native/include")
168
+ message(STATUS "WebView2 SDK found at: ${WEBVIEW2_DIR}/build/native/include")
169
+ else()
170
+ message(WARNING "WebView2 SDK headers not found. Please install WebView2 SDK manually.")
171
+ endif()
172
+
173
+ # Link required Windows libraries
174
+ target_link_libraries(plusui PRIVATE
175
+ ole32
176
+ shell32
177
+ shlwapi
178
+ user32
179
+ version
180
+ advapi32
181
+ )
182
+
183
+ # Link WebView2 loader library
184
+ if(CMAKE_SIZEOF_VOID_P EQUAL 8)
185
+ set(WEBVIEW2_ARCH "x64")
186
+ else()
187
+ set(WEBVIEW2_ARCH "x86")
188
+ endif()
189
+
190
+ set(WEBVIEW2_LIB "${WEBVIEW2_DIR}/build/native/${WEBVIEW2_ARCH}/WebView2LoaderStatic.lib")
191
+ if(EXISTS "${WEBVIEW2_LIB}")
192
+ target_link_libraries(plusui PUBLIC "${WEBVIEW2_LIB}")
193
+ else()
194
+ # Try the loader DLL approach
195
+ set(WEBVIEW2_LOADER_LIB "${WEBVIEW2_DIR}/build/native/${WEBVIEW2_ARCH}/WebView2Loader.dll.lib")
196
+ if(EXISTS "${WEBVIEW2_LOADER_LIB}")
197
+ target_link_libraries(plusui PUBLIC "${WEBVIEW2_LOADER_LIB}")
198
+ endif()
199
+ endif()
200
+
201
+ elseif(APPLE)
202
+ # macOS: WebKit frameworks
203
+ find_library(WEBKIT_LIBRARY WebKit REQUIRED)
204
+ find_library(COCOA_LIBRARY Cocoa REQUIRED)
205
+ target_link_libraries(plusui PRIVATE ${WEBKIT_LIBRARY} ${COCOA_LIBRARY})
206
+
207
+ else()
208
+ # Linux: GTK + WebKitGTK
209
+ find_package(PkgConfig REQUIRED)
210
+ pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
211
+ pkg_check_modules(WEBKIT2 REQUIRED webkit2gtk-4.0)
212
+ target_include_directories(plusui PRIVATE ${GTK3_INCLUDE_DIRS} ${WEBKIT2_INCLUDE_DIRS})
213
+ target_link_libraries(plusui PRIVATE ${GTK3_LIBRARIES} ${WEBKIT2_LIBRARIES})
31
214
  endif()
32
215
 
33
216
  install(TARGETS plusui ARCHIVE DESTINATION lib)
@@ -0,0 +1,129 @@
1
+ #ifdef _WIN32
2
+ #define WIN32_LEAN_AND_MEAN
3
+ #include <windows.h>
4
+ #endif
5
+
6
+ #include <plusui/app.hpp>
7
+ #include <plusui/tray.hpp>
8
+ #include <plusui/webview.hpp>
9
+ #include <plusui/window.hpp>
10
+ #include <plusui/window_manager.hpp>
11
+
12
+ namespace plusui {
13
+
14
+ struct App::Impl {
15
+ bool running = false;
16
+ };
17
+
18
+ App::App() : pImpl(std::make_unique<Impl>()) {}
19
+ App::~App() = default;
20
+
21
+ void App::run() {
22
+ pImpl->running = true;
23
+ #ifdef _WIN32
24
+ MSG msg;
25
+ while (GetMessage(&msg, nullptr, 0, 0)) {
26
+ TranslateMessage(&msg);
27
+ DispatchMessage(&msg);
28
+ }
29
+ #endif
30
+ }
31
+
32
+ void App::quit() {
33
+ pImpl->running = false;
34
+ #ifdef _WIN32
35
+ PostQuitMessage(0);
36
+ #endif
37
+ }
38
+
39
+ App::Builder::Builder() = default;
40
+
41
+ App::Builder &App::Builder::title(const std::string &t) {
42
+ config.title = t;
43
+ return *this;
44
+ }
45
+ App::Builder &App::Builder::width(int w) {
46
+ config.width = w;
47
+ return *this;
48
+ }
49
+ App::Builder &App::Builder::height(int h) {
50
+ config.height = h;
51
+ return *this;
52
+ }
53
+ App::Builder &App::Builder::resizable(bool r) {
54
+ config.resizable = r;
55
+ return *this;
56
+ }
57
+ App::Builder &App::Builder::devtools(bool d) {
58
+ config.devtools = d;
59
+ return *this;
60
+ }
61
+ App::Builder &App::Builder::trayIcon(const std::string &icon) {
62
+ config.trayIconPath = icon;
63
+ return *this;
64
+ }
65
+ App::Builder &App::Builder::trayTooltip(const std::string &tooltip) {
66
+ config.trayTooltip = tooltip;
67
+ return *this;
68
+ }
69
+ App::Builder &App::Builder::alwaysOnTop(bool top) {
70
+ config.alwaysOnTop = top;
71
+ return *this;
72
+ }
73
+ App::Builder &App::Builder::centered(bool center) {
74
+ config.centered = center;
75
+ return *this;
76
+ }
77
+ App::Builder &App::Builder::transparent(bool transparent) {
78
+ config.transparent = transparent;
79
+ return *this;
80
+ }
81
+ App::Builder &App::Builder::decorations(bool decorations) {
82
+ config.decorations = decorations;
83
+ return *this;
84
+ }
85
+ App::Builder &App::Builder::skipTaskbar(bool skip) {
86
+ config.skipTaskbar = skip;
87
+ return *this;
88
+ }
89
+ App::Builder &App::Builder::scrollbars(bool show) {
90
+ config.scrollbars = show;
91
+ return *this;
92
+ }
93
+
94
+ WebView App::Builder::build() {
95
+ WindowConfig winConfig;
96
+ winConfig.title = config.title;
97
+ winConfig.width = config.width;
98
+ winConfig.height = config.height;
99
+ winConfig.resizable = config.resizable;
100
+ winConfig.alwaysOnTop = config.alwaysOnTop;
101
+ winConfig.center = config.centered;
102
+ winConfig.transparent = config.transparent;
103
+ winConfig.decorations = config.decorations;
104
+ winConfig.skipTaskbar = config.skipTaskbar;
105
+
106
+ auto win = std::make_shared<Window>(Window::create(winConfig));
107
+ win->show();
108
+
109
+ WebViewConfig webConfig;
110
+ webConfig.devtools = config.devtools;
111
+ webConfig.scrollbars = config.scrollbars;
112
+
113
+ WebView wv = WebView::create(win->nativeHandle(), webConfig);
114
+ wv.setWindow(win);
115
+
116
+ if (!config.trayIconPath.empty()) {
117
+ wv.tray().setIcon(config.trayIconPath);
118
+ wv.tray().setVisible(true);
119
+ if (!config.trayTooltip.empty()) {
120
+ wv.tray().setTooltip(config.trayTooltip);
121
+ }
122
+ }
123
+
124
+ return wv;
125
+ }
126
+
127
+ App::Builder createApp() { return App::Builder(); }
128
+
129
+ } // namespace plusui
@@ -0,0 +1,126 @@
1
+ export interface AppConfig {
2
+ title?: string;
3
+ width?: number;
4
+ height?: number;
5
+ resizable?: boolean;
6
+ devtools?: boolean;
7
+ trayIcon?: string;
8
+ trayTooltip?: string;
9
+ alwaysOnTop?: boolean;
10
+ centered?: boolean;
11
+ transparent?: boolean;
12
+ decorations?: boolean;
13
+ skipTaskbar?: boolean;
14
+ }
15
+
16
+ export interface InvokeOptions {
17
+ timeout?: number;
18
+ }
19
+
20
+ export class App {
21
+ private initialized = false;
22
+
23
+ constructor(
24
+ private config: AppConfig = {},
25
+ private invokeFn?: (name: string, args?: unknown[]) => Promise<unknown>
26
+ ) {
27
+ if (typeof window !== 'undefined') {
28
+ this.init();
29
+ }
30
+ }
31
+
32
+ private init(): void {
33
+ if (this.initialized) return;
34
+ this.initialized = true;
35
+
36
+ if (typeof window !== 'undefined') {
37
+ const win = window as Window & {
38
+ __plusui_listeners__?: Map<string, Set<(...args: unknown[]) => void>>;
39
+ };
40
+
41
+ win.__plusui_listeners__ = new Map();
42
+
43
+ window.addEventListener('message', (event: MessageEvent) => {
44
+ if (event.data?.type === '__plusui_event__') {
45
+ const { event: eventName, payload } = event.data;
46
+ const listeners = win.__plusui_listeners__?.get(eventName);
47
+ if (listeners) {
48
+ listeners.forEach(cb => cb(payload));
49
+ }
50
+ }
51
+ });
52
+ }
53
+ }
54
+
55
+ async invoke<T = unknown>(name: string, args?: unknown[], options: InvokeOptions = {}): Promise<T> {
56
+ const { timeout = 30000 } = options;
57
+
58
+ if (this.invokeFn) {
59
+ return this.invokeFn(name, args) as Promise<T>;
60
+ }
61
+
62
+ if (typeof window !== 'undefined') {
63
+ const win = window as Window & {
64
+ __plusui_invoke__?: (name: string, args?: unknown[]) => Promise<unknown>;
65
+ __invoke__?: (name: string, args?: unknown[]) => Promise<unknown>;
66
+ __native_invoke__?: (request: string) => void;
67
+ };
68
+ // Check for SDK bridge first (__invoke__ from plusui-native-core)
69
+ if (win.__invoke__) {
70
+ return win.__invoke__(name, args) as Promise<T>;
71
+ }
72
+ // Check for legacy __plusui_invoke__
73
+ if (win.__plusui_invoke__) {
74
+ return win.__plusui_invoke__(name, args) as Promise<T>;
75
+ }
76
+ }
77
+
78
+ return new Promise((_, reject) => {
79
+ setTimeout(() => reject(new Error(`Invocation ${name} timed out`)), timeout);
80
+ });
81
+ }
82
+
83
+ on(event: string, callback: (...args: unknown[]) => void): () => void {
84
+ if (typeof window === 'undefined') {
85
+ return () => { };
86
+ }
87
+
88
+ const win = window as Window & {
89
+ __plusui_listeners__?: Map<string, Set<(...args: unknown[]) => void>>;
90
+ };
91
+
92
+ if (!win.__plusui_listeners__) {
93
+ win.__plusui_listeners__ = new Map();
94
+ }
95
+
96
+ if (!win.__plusui_listeners__.has(event)) {
97
+ win.__plusui_listeners__.set(event, new Set());
98
+ }
99
+
100
+ win.__plusui_listeners__.get(event)!.add(callback);
101
+
102
+ return () => {
103
+ win.__plusui_listeners__.get(event)?.delete(callback);
104
+ };
105
+ }
106
+
107
+ once(event: string, callback: (...args: unknown[]) => void): void {
108
+ const unsubscribe = this.on(event, (...args) => {
109
+ unsubscribe();
110
+ callback(...args);
111
+ });
112
+ }
113
+
114
+ emit(event: string, payload?: unknown): void {
115
+ if (typeof window !== 'undefined') {
116
+ const customEvent = new CustomEvent(event, { detail: payload });
117
+ window.dispatchEvent(customEvent);
118
+ }
119
+ }
120
+ }
121
+
122
+ export function createApp(config?: AppConfig): App {
123
+ return new App(config);
124
+ }
125
+
126
+ export default App;
@@ -0,0 +1,181 @@
1
+ #include <iostream>
2
+ #include <plusui/browser.hpp>
3
+ #include <plusui/webview.hpp>
4
+
5
+ namespace plusui {
6
+
7
+ struct Browser::Impl {
8
+ WebView* webview = nullptr;
9
+ std::map<std::string, std::string> routes;
10
+ std::string currentRoute;
11
+ BrowserState state;
12
+
13
+ std::vector<NavigateCallback> navigateCallbacks;
14
+ std::vector<StateCallback> stateCallbacks;
15
+ std::vector<LoadCallback> loadStartCallbacks;
16
+ std::vector<LoadCallback> loadEndCallbacks;
17
+ std::vector<std::function<void(const std::string&)>> loadErrorCallbacks;
18
+
19
+ void updateState() {
20
+ if (webview) {
21
+ state.url = webview->getURL();
22
+ state.title = webview->getTitle();
23
+ state.canGoBack = webview->canGoBack();
24
+ state.canGoForward = webview->canGoForward();
25
+ state.isLoading = webview->isLoading();
26
+
27
+ for (auto& cb : stateCallbacks) {
28
+ cb(state);
29
+ }
30
+ }
31
+ }
32
+
33
+ void notifyNavigate(const std::string& url) {
34
+ state.url = url;
35
+ for (auto& cb : navigateCallbacks) {
36
+ cb(url);
37
+ }
38
+ updateState();
39
+ }
40
+ };
41
+
42
+ Browser::Browser() : pImpl(std::make_shared<Impl>()) {}
43
+
44
+ Browser::~Browser() = default;
45
+
46
+ Browser Browser::create(WebView* webview) {
47
+ Browser browser;
48
+ browser.pImpl->webview = webview;
49
+
50
+ if (webview) {
51
+ webview->onNavigationStart([impl = browser.pImpl](const std::string& url) {
52
+ impl->notifyNavigate(url);
53
+ return true;
54
+ });
55
+
56
+ webview->onLoadStart([impl = browser.pImpl]() {
57
+ impl->state.isLoading = true;
58
+ for (auto& cb : impl->loadStartCallbacks) {
59
+ cb();
60
+ }
61
+ });
62
+
63
+ webview->onLoadEnd([impl = browser.pImpl]() {
64
+ impl->state.isLoading = false;
65
+ impl->updateState();
66
+ for (auto& cb : impl->loadEndCallbacks) {
67
+ cb();
68
+ }
69
+ });
70
+
71
+ webview->onLoadError([impl = browser.pImpl](const std::string& error, int code) {
72
+ impl->state.isLoading = false;
73
+ for (auto& cb : impl->loadErrorCallbacks) {
74
+ cb(error);
75
+ }
76
+ });
77
+ }
78
+
79
+ return browser;
80
+ }
81
+
82
+ void Browser::navigate(const std::string& url) {
83
+ if (pImpl->webview) {
84
+ pImpl->webview->navigate(url);
85
+ pImpl->notifyNavigate(url);
86
+ }
87
+ }
88
+
89
+ void Browser::goBack() {
90
+ if (pImpl->webview && pImpl->webview->canGoBack()) {
91
+ pImpl->webview->goBack();
92
+ pImpl->updateState();
93
+ }
94
+ }
95
+
96
+ void Browser::goForward() {
97
+ if (pImpl->webview && pImpl->webview->canGoForward()) {
98
+ pImpl->webview->goForward();
99
+ pImpl->updateState();
100
+ }
101
+ }
102
+
103
+ void Browser::reload() {
104
+ if (pImpl->webview) {
105
+ pImpl->webview->reload();
106
+ }
107
+ }
108
+
109
+ void Browser::stop() {
110
+ if (pImpl->webview) {
111
+ pImpl->webview->stop();
112
+ }
113
+ }
114
+
115
+ std::string Browser::getURL() const {
116
+ return pImpl->webview ? pImpl->webview->getURL() : "";
117
+ }
118
+
119
+ std::string Browser::getTitle() const {
120
+ return pImpl->webview ? pImpl->webview->getTitle() : "";
121
+ }
122
+
123
+ BrowserState Browser::getState() const {
124
+ return pImpl->state;
125
+ }
126
+
127
+ bool Browser::canGoBack() const {
128
+ return pImpl->webview ? pImpl->webview->canGoBack() : false;
129
+ }
130
+
131
+ bool Browser::canGoForward() const {
132
+ return pImpl->webview ? pImpl->webview->canGoForward() : false;
133
+ }
134
+
135
+ bool Browser::isLoading() const {
136
+ return pImpl->state.isLoading;
137
+ }
138
+
139
+ void Browser::onNavigate(NavigateCallback callback) {
140
+ pImpl->navigateCallbacks.push_back(callback);
141
+ }
142
+
143
+ void Browser::onStateChange(StateCallback callback) {
144
+ pImpl->stateCallbacks.push_back(callback);
145
+ }
146
+
147
+ void Browser::onLoadStart(LoadCallback callback) {
148
+ pImpl->loadStartCallbacks.push_back(callback);
149
+ }
150
+
151
+ void Browser::onLoadEnd(LoadCallback callback) {
152
+ pImpl->loadEndCallbacks.push_back(callback);
153
+ }
154
+
155
+ void Browser::onLoadError(std::function<void(const std::string& error)> callback) {
156
+ pImpl->loadErrorCallbacks.push_back(callback);
157
+ }
158
+
159
+ void Browser::setRoutes(const std::map<std::string, std::string>& routes) {
160
+ pImpl->routes = routes;
161
+ }
162
+
163
+ void Browser::push(const std::string& route) {
164
+ pImpl->currentRoute = route;
165
+ auto it = pImpl->routes.find(route);
166
+ if (it != pImpl->routes.end()) {
167
+ navigate(it->second);
168
+ } else {
169
+ navigate(route);
170
+ }
171
+ }
172
+
173
+ void Browser::replace(const std::string& route) {
174
+ push(route);
175
+ }
176
+
177
+ std::string Browser::getCurrentRoute() const {
178
+ return pImpl->currentRoute;
179
+ }
180
+
181
+ } // namespace plusui