plusui-native-core 0.1.103 → 0.1.105
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/Features/API/Connect_API.ts +20 -52
- package/Core/Features/API/filedrop-api.ts +12 -1
- package/Core/Features/API/index.ts +1 -1
- package/Core/Features/App/app.cpp +32 -32
- package/Core/Features/FileDrop/filedrop.ts +133 -44
- package/Core/Features/Window/webview.cpp +25 -8
- package/Core/Features/Window/webview.ts +1 -1
- package/Core/Features/Window/window.cpp +46 -21
- package/Core/include/plusui/api.hpp +2 -2
- package/Core/include/plusui/app.hpp +41 -41
- package/Core/include/plusui/window.hpp +38 -40
- package/package.json +1 -1
|
@@ -1,76 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* PlusUI Connect API —
|
|
2
|
+
* PlusUI Connect API — Custom Frontend ↔ Backend Communication
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* IMPORTANT: `connect` is for CUSTOM user-defined communication only!
|
|
7
|
-
* Built-in features (window, clipboard, app, etc.) use direct imports:
|
|
8
|
-
* import plusui from 'plusui';
|
|
9
|
-
* plusui.window.minimize();
|
|
10
|
-
* plusui.clipboard.setText('hello');
|
|
11
|
-
*
|
|
12
|
-
* For custom communication, use `connect.namespace.method()`:
|
|
4
|
+
* Use `connect` for YOUR custom communication channels only!
|
|
13
5
|
*
|
|
14
6
|
* import { connect } from 'plusui';
|
|
15
7
|
*
|
|
16
|
-
* // Request/Response
|
|
8
|
+
* // Request/Response
|
|
17
9
|
* const user = await connect.user.fetch(123);
|
|
18
10
|
*
|
|
19
|
-
* // Fire & Forget
|
|
11
|
+
* // Fire & Forget (one-way)
|
|
20
12
|
* connect.files.upload({ file: myFile });
|
|
21
13
|
*
|
|
22
|
-
* //
|
|
14
|
+
* // Subscribe to events
|
|
23
15
|
* connect.app.onNotify((msg) => console.log(msg));
|
|
24
16
|
*
|
|
25
|
-
* // Register
|
|
17
|
+
* // Register handler (backend calls frontend)
|
|
26
18
|
* connect.ui.handlePrompt = async (data) => {
|
|
27
19
|
* return await Dialog.confirm(data.msg);
|
|
28
20
|
* };
|
|
29
21
|
*
|
|
30
|
-
*
|
|
22
|
+
* Run `plusui connect` to generate typed bindings.
|
|
31
23
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* - `connect.x.y()` → FIRE (Simplex, one-way)
|
|
24
|
+
* BUILT-IN FEATURES use direct imports:
|
|
25
|
+
* import plusui from 'plusui';
|
|
26
|
+
* plusui.window.minimize();
|
|
27
|
+
* plusui.clipboard.setText('hello');
|
|
37
28
|
*/
|
|
38
29
|
|
|
39
30
|
import { connect, _client, createFeatureConnect } from '../Connection/connect';
|
|
40
31
|
import type { FeatureConnect, ConnectionKind, ConnectionEnvelope } from '../Connection/connect';
|
|
41
32
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
*
|
|
45
|
-
* Usage:
|
|
46
|
-
* connect.namespace.method(...args)
|
|
47
|
-
*
|
|
48
|
-
* The proxy routes to the correct wire format automatically:
|
|
49
|
-
* connect.user.fetch(123) → _client.call('user.fetch', 123)
|
|
50
|
-
* connect.app.onNotify(cb) → _client.on('app.onNotify', cb)
|
|
51
|
-
* connect.ui.handlePrompt = fn → _client.handle('ui.handlePrompt', fn)
|
|
52
|
-
* connect.system.minimize() → _client.fire('system.minimize', {})
|
|
53
|
-
*
|
|
54
|
-
* Works dynamically even before running `plusui connect`.
|
|
55
|
-
* After running `plusui connect`, you get IDE autocomplete.
|
|
56
|
-
*/
|
|
57
|
-
export { connect };
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* _client — Low-level connection client
|
|
61
|
-
*
|
|
62
|
-
* Direct access to the underlying client for advanced use cases.
|
|
63
|
-
* Prefer using `connect.namespace.method()` for normal communication.
|
|
64
|
-
*/
|
|
65
|
-
export { _client };
|
|
33
|
+
export { connect, _client, createFeatureConnect };
|
|
34
|
+
export type { FeatureConnect, ConnectionKind, ConnectionEnvelope };
|
|
66
35
|
|
|
67
36
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* Creates a connect API scoped to a feature namespace.
|
|
71
|
-
* Used internally by PlusUI features like window, clipboard, etc.
|
|
37
|
+
* Low-level client methods (use `connect` instead when possible)
|
|
72
38
|
*/
|
|
73
|
-
export
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
39
|
+
export type ClientAPI = {
|
|
40
|
+
call: <TOut = unknown, TIn = Record<string, unknown>>(name: string, payload: TIn) => Promise<TOut>;
|
|
41
|
+
fire: <TIn = Record<string, unknown>>(name: string, payload: TIn) => void;
|
|
42
|
+
on: <TData = unknown>(name: string, callback: (payload: TData) => void) => () => void;
|
|
43
|
+
handle: (name: string, handler: (...args: any[]) => any) => () => void;
|
|
44
|
+
};
|
|
@@ -1,2 +1,13 @@
|
|
|
1
|
+
export {
|
|
2
|
+
fileDrop,
|
|
3
|
+
createDropZone,
|
|
4
|
+
formatFileSize,
|
|
5
|
+
filterFilesByExtension,
|
|
6
|
+
filterFilesByMimeType,
|
|
7
|
+
isImageFile,
|
|
8
|
+
isVideoFile,
|
|
9
|
+
isAudioFile,
|
|
10
|
+
isTextFile
|
|
11
|
+
} from '../FileDrop/filedrop';
|
|
12
|
+
|
|
1
13
|
export type { FileInfo, DropZone } from '../FileDrop/filedrop';
|
|
2
|
-
export { fileDrop, formatFileSize, createDropZone } from '../FileDrop/filedrop';
|
|
@@ -10,7 +10,7 @@ export { router } from './router-api';
|
|
|
10
10
|
export { keyboard, KeyCode, KeyMod } from './keyboard-api';
|
|
11
11
|
export { tray } from './tray-api';
|
|
12
12
|
export { display } from './display-api';
|
|
13
|
-
export { fileDrop, formatFileSize, createDropZone } from './filedrop-api';
|
|
13
|
+
export { fileDrop, formatFileSize, createDropZone, filterFilesByExtension, filterFilesByMimeType, isImageFile, isVideoFile, isAudioFile, isTextFile } from './filedrop-api';
|
|
14
14
|
export { menu } from './menu-api';
|
|
15
15
|
export { gpu, GPUBufferUsage, GPUTextureUsage, GPUMapMode, GPUShaderStage, GPUColorWrite } from './webgpu-api';
|
|
16
16
|
|
|
@@ -77,38 +77,38 @@ App::Builder &App::Builder::transparent(bool transparent) {
|
|
|
77
77
|
App::Builder &App::Builder::decorations(bool decorations) {
|
|
78
78
|
config.decorations = decorations;
|
|
79
79
|
return *this;
|
|
80
|
-
}
|
|
81
|
-
App::Builder &App::Builder::skipTaskbar(bool skip) {
|
|
82
|
-
config.skipTaskbar = skip;
|
|
83
|
-
return *this;
|
|
84
|
-
}
|
|
85
|
-
App::Builder &App::Builder::scrollbars(bool show) {
|
|
86
|
-
config.scrollbars = show;
|
|
87
|
-
return *this;
|
|
88
|
-
}
|
|
89
|
-
App::Builder &App::Builder::
|
|
90
|
-
config.
|
|
91
|
-
return *this;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
Window 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
|
-
winConfig.
|
|
106
|
-
|
|
107
|
-
// WebView configuration (now part of WindowConfig)
|
|
108
|
-
winConfig.devtools = config.devtools;
|
|
109
|
-
winConfig.scrollbars = config.scrollbars;
|
|
110
|
-
winConfig.disableWebviewDragDrop =
|
|
111
|
-
config.
|
|
80
|
+
}
|
|
81
|
+
App::Builder &App::Builder::skipTaskbar(bool skip) {
|
|
82
|
+
config.skipTaskbar = skip;
|
|
83
|
+
return *this;
|
|
84
|
+
}
|
|
85
|
+
App::Builder &App::Builder::scrollbars(bool show) {
|
|
86
|
+
config.scrollbars = show;
|
|
87
|
+
return *this;
|
|
88
|
+
}
|
|
89
|
+
App::Builder &App::Builder::fileDrop(bool enable) {
|
|
90
|
+
config.fileDrop = enable;
|
|
91
|
+
return *this;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
Window 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
|
+
winConfig.fileDrop = config.fileDrop;
|
|
106
|
+
|
|
107
|
+
// WebView configuration (now part of WindowConfig)
|
|
108
|
+
winConfig.devtools = config.devtools;
|
|
109
|
+
winConfig.scrollbars = config.scrollbars;
|
|
110
|
+
winConfig.disableWebviewDragDrop =
|
|
111
|
+
config.fileDrop; // Auto-disable browser drag-drop when OS file drop enabled
|
|
112
112
|
|
|
113
113
|
// Create native window
|
|
114
114
|
auto nativeWinPtr = std::make_shared<Window>(Window::create(winConfig));
|
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PlusUI FileDrop API
|
|
3
|
+
*
|
|
4
|
+
* Simple API for handling OS file drops in your app.
|
|
5
|
+
* Enable in config: .fileDrop(true) - enabled by default
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import plusui from 'plusui';
|
|
9
|
+
* const { fileDrop } = plusui;
|
|
10
|
+
*
|
|
11
|
+
* // Create a drop zone
|
|
12
|
+
* const zone = fileDrop.createDropZone('myZone', element);
|
|
13
|
+
* zone.onFiles((files) => {
|
|
14
|
+
* console.log('Dropped:', files.map(f => f.name));
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* // Or use data-dropzone attribute
|
|
18
|
+
* // <div data-dropzone="upload"></div>
|
|
19
|
+
* fileDrop.onFiles('upload', (files) => { ... });
|
|
20
|
+
*/
|
|
21
|
+
|
|
1
22
|
export interface FileInfo {
|
|
2
23
|
path: string;
|
|
3
24
|
name: string;
|
|
@@ -5,9 +26,22 @@ export interface FileInfo {
|
|
|
5
26
|
size: number;
|
|
6
27
|
}
|
|
7
28
|
|
|
29
|
+
export interface DropZone {
|
|
30
|
+
name: string;
|
|
31
|
+
onFiles(callback: (files: FileInfo[]) => void): () => void;
|
|
32
|
+
onDragEnter(callback: () => void): () => void;
|
|
33
|
+
onDragLeave(callback: () => void): () => void;
|
|
34
|
+
element: HTMLElement | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface PendingCall {
|
|
38
|
+
resolve: (v: any) => void;
|
|
39
|
+
reject: (e: any) => void;
|
|
40
|
+
}
|
|
41
|
+
|
|
8
42
|
let callId = 0;
|
|
9
|
-
const pending = new Map<string,
|
|
10
|
-
const
|
|
43
|
+
const pending = new Map<string, PendingCall>();
|
|
44
|
+
const zones = new Map<string, DropZone>();
|
|
11
45
|
|
|
12
46
|
function getGlobal(): any {
|
|
13
47
|
if (typeof window !== 'undefined') return window;
|
|
@@ -25,18 +59,18 @@ g.__response__ = function(id: string, result: any, error?: any) {
|
|
|
25
59
|
}
|
|
26
60
|
};
|
|
27
61
|
|
|
28
|
-
// Called by C++ when files are dropped on a named zone
|
|
29
62
|
g.__plusui_fileDrop__ = function(zoneName: string, files: FileInfo[]) {
|
|
30
|
-
const zone =
|
|
31
|
-
if (zone
|
|
63
|
+
const zone = zones.get(zoneName);
|
|
64
|
+
if (zone) {
|
|
65
|
+
const cb = (zone as any).__onFiles;
|
|
66
|
+
if (cb) cb(files);
|
|
67
|
+
}
|
|
32
68
|
};
|
|
33
69
|
|
|
34
|
-
// Called by C++ when the drop point doesn't hit any named zone — deliver to
|
|
35
|
-
// all registered zones so a single-zone app always works regardless of DPI
|
|
36
|
-
// scaling or hit-test accuracy.
|
|
37
70
|
g.__plusui_fileDrop_default__ = function(files: FileInfo[]) {
|
|
38
|
-
for (const zone of
|
|
39
|
-
|
|
71
|
+
for (const zone of zones.values()) {
|
|
72
|
+
const cb = (zone as any).__onFiles;
|
|
73
|
+
if (cb) cb(files);
|
|
40
74
|
}
|
|
41
75
|
};
|
|
42
76
|
|
|
@@ -52,77 +86,132 @@ async function invoke<T>(method: string, params: any[] = []): Promise<T> {
|
|
|
52
86
|
g.chrome.webview.postMessage(payload);
|
|
53
87
|
} else {
|
|
54
88
|
pending.delete(id);
|
|
55
|
-
console.warn('[PlusUI]
|
|
89
|
+
console.warn('[PlusUI] Native bridge not ready');
|
|
56
90
|
resolve(null as T);
|
|
57
91
|
}
|
|
58
92
|
});
|
|
59
93
|
}
|
|
60
94
|
|
|
61
|
-
export
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (element) {
|
|
70
|
-
element.setAttribute('data-dropzone', name);
|
|
71
|
-
|
|
72
|
-
// Prevent browser default on drop (file navigation).
|
|
73
|
-
// Actual file delivery comes from C++ via WM_DROPFILES → __plusui_fileDrop__.
|
|
74
|
-
element.addEventListener('drop', (e: DragEvent) => {
|
|
95
|
+
export function createDropZone(name: string, element?: HTMLElement | null): DropZone {
|
|
96
|
+
const el = element || document.querySelector(`[data-dropzone="${name}"]`) as HTMLElement;
|
|
97
|
+
|
|
98
|
+
if (el) {
|
|
99
|
+
el.setAttribute('data-dropzone', name);
|
|
100
|
+
el.classList.add('plusui-dropzone');
|
|
101
|
+
|
|
102
|
+
el.addEventListener('drop', (e: DragEvent) => {
|
|
75
103
|
e.preventDefault();
|
|
76
|
-
|
|
77
|
-
|
|
104
|
+
el.classList.remove('dropzone-active');
|
|
105
|
+
el.classList.remove('filedrop-active');
|
|
78
106
|
});
|
|
79
107
|
|
|
80
|
-
|
|
108
|
+
el.addEventListener('dragleave', (e: DragEvent) => {
|
|
81
109
|
const related = e.relatedTarget as Node | null;
|
|
82
|
-
if (!related || !
|
|
83
|
-
|
|
110
|
+
if (!related || !el.contains(related)) {
|
|
111
|
+
el.classList.remove('dropzone-active');
|
|
112
|
+
el.classList.remove('filedrop-active');
|
|
84
113
|
}
|
|
85
114
|
});
|
|
86
115
|
|
|
87
|
-
|
|
116
|
+
el.addEventListener('dragenter', (e: DragEvent) => {
|
|
88
117
|
e.preventDefault();
|
|
89
|
-
|
|
118
|
+
el.classList.add('dropzone-active');
|
|
119
|
+
el.classList.add('filedrop-active');
|
|
90
120
|
if (e.dataTransfer) {
|
|
91
|
-
|
|
92
|
-
// rather than the browser's '+ Copy' cursor
|
|
93
|
-
try { e.dataTransfer.dropEffect = 'move'; } catch (_) {}
|
|
121
|
+
try { e.dataTransfer.dropEffect = 'copy'; } catch (_) {}
|
|
94
122
|
}
|
|
95
123
|
});
|
|
96
124
|
|
|
97
|
-
|
|
125
|
+
el.addEventListener('dragover', (e: DragEvent) => {
|
|
98
126
|
e.preventDefault();
|
|
99
127
|
if (e.dataTransfer) {
|
|
100
|
-
try { e.dataTransfer.dropEffect = '
|
|
128
|
+
try { e.dataTransfer.dropEffect = 'copy'; } catch (_) {}
|
|
101
129
|
}
|
|
102
130
|
});
|
|
103
131
|
}
|
|
104
132
|
|
|
105
|
-
|
|
106
|
-
|
|
133
|
+
const zone: DropZone = {
|
|
134
|
+
name,
|
|
135
|
+
element: el,
|
|
107
136
|
onFiles: (callback: (files: FileInfo[]) => void) => {
|
|
108
|
-
|
|
109
|
-
|
|
137
|
+
(zone as any).__onFiles = callback;
|
|
138
|
+
zones.set(name, zone);
|
|
139
|
+
return () => {
|
|
140
|
+
(zone as any).__onFiles = undefined;
|
|
141
|
+
zones.delete(name);
|
|
142
|
+
};
|
|
143
|
+
},
|
|
144
|
+
onDragEnter: (callback: () => void) => {
|
|
145
|
+
(zone as any).__onDragEnter = callback;
|
|
146
|
+
return () => { (zone as any).__onDragEnter = undefined; };
|
|
147
|
+
},
|
|
148
|
+
onDragLeave: (callback: () => void) => {
|
|
149
|
+
(zone as any).__onDragLeave = callback;
|
|
150
|
+
return () => { (zone as any).__onDragLeave = undefined; };
|
|
110
151
|
}
|
|
111
152
|
};
|
|
153
|
+
|
|
154
|
+
zones.set(name, zone);
|
|
155
|
+
return zone;
|
|
112
156
|
}
|
|
113
157
|
|
|
114
158
|
export const fileDrop = {
|
|
115
|
-
setEnabled: (enabled: boolean) => invoke<void>('fileDrop.setEnabled', [enabled]),
|
|
116
|
-
isEnabled: () => invoke<boolean>('fileDrop.isEnabled'),
|
|
117
159
|
createDropZone,
|
|
160
|
+
|
|
161
|
+
onFiles: (name: string, callback: (files: FileInfo[]) => void): (() => void) => {
|
|
162
|
+
const zone = zones.get(name);
|
|
163
|
+
if (zone) {
|
|
164
|
+
return zone.onFiles(callback);
|
|
165
|
+
}
|
|
166
|
+
const newZone = createDropZone(name);
|
|
167
|
+
return newZone.onFiles(callback);
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
setEnabled: (enabled: boolean): Promise<void> =>
|
|
171
|
+
invoke<void>('fileDrop.setEnabled', [enabled]),
|
|
172
|
+
|
|
173
|
+
isEnabled: (): Promise<boolean> =>
|
|
174
|
+
invoke<boolean>('fileDrop.isEnabled'),
|
|
175
|
+
|
|
176
|
+
startDrag: (filePaths: string[]): Promise<void> =>
|
|
177
|
+
invoke<void>('fileDrop.startDrag', [filePaths]),
|
|
118
178
|
};
|
|
119
179
|
|
|
120
180
|
export function formatFileSize(bytes: number): string {
|
|
121
181
|
if (bytes === 0) return '0 B';
|
|
122
182
|
const k = 1024;
|
|
123
|
-
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
183
|
+
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
124
184
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
125
185
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
|
|
126
186
|
}
|
|
127
187
|
|
|
188
|
+
export function filterFilesByExtension(files: FileInfo[], extensions: string[]): FileInfo[] {
|
|
189
|
+
const exts = extensions.map(e => e.toLowerCase().replace(/^\./, ''));
|
|
190
|
+
return files.filter(f => {
|
|
191
|
+
const ext = f.name.split('.').pop()?.toLowerCase() || '';
|
|
192
|
+
return exts.includes(ext);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function filterFilesByMimeType(files: FileInfo[], mimeTypes: string[]): FileInfo[] {
|
|
197
|
+
return files.filter(f => mimeTypes.includes(f.type));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function isImageFile(file: FileInfo): boolean {
|
|
201
|
+
return file.type.startsWith('image/');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function isVideoFile(file: FileInfo): boolean {
|
|
205
|
+
return file.type.startsWith('video/');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function isAudioFile(file: FileInfo): boolean {
|
|
209
|
+
return file.type.startsWith('audio/');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function isTextFile(file: FileInfo): boolean {
|
|
213
|
+
return file.type.startsWith('text/') || file.name.endsWith('.txt') ||
|
|
214
|
+
file.name.endsWith('.md') || file.name.endsWith('.json');
|
|
215
|
+
}
|
|
216
|
+
|
|
128
217
|
export default fileDrop;
|
|
@@ -167,13 +167,14 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
167
167
|
// while still showing visual feedback on drop zones.
|
|
168
168
|
// File delivery is handled by WM_DROPFILES in wndProc.
|
|
169
169
|
if (pImpl->config.disableWebviewDragDrop ||
|
|
170
|
-
pImpl->config.
|
|
170
|
+
pImpl->config.fileDrop) {
|
|
171
171
|
std::string disableDragDropScript = R"(
|
|
172
172
|
(function() {
|
|
173
173
|
if (window.__plusui_dropzone_init) return;
|
|
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,13 +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
|
-
|
|
223
|
+
dragDepth--;
|
|
224
|
+
if (dragDepth <= 0) {
|
|
225
|
+
dragDepth = 0;
|
|
226
|
+
updateActiveZone(null);
|
|
227
|
+
} else {
|
|
228
|
+
var zone = findDropZone(e);
|
|
229
|
+
updateActiveZone(zone);
|
|
230
|
+
}
|
|
217
231
|
}, true);
|
|
218
232
|
|
|
219
233
|
document.addEventListener('drop', function(e) {
|
|
220
234
|
e.preventDefault();
|
|
221
235
|
updateActiveZone(null);
|
|
236
|
+
dragDepth = 0;
|
|
222
237
|
}, true);
|
|
223
238
|
|
|
224
239
|
window.addEventListener('dragover', function(e) { e.preventDefault(); }, true);
|
|
@@ -679,7 +694,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
679
694
|
script = "(function() { delete window.__plusui_dropzone_init; })();";
|
|
680
695
|
} else {
|
|
681
696
|
// Re-install the dropzone blocker script
|
|
682
|
-
script = "(function() { if (window.__plusui_dropzone_init) return; window.__plusui_dropzone_init = true; var activeZone = null; var findDropZone = function(e) { var target = null; if (e && typeof e.clientX === 'number' && document.elementFromPoint) { target = document.elementFromPoint(e.clientX, e.clientY); } if (!target && e && e.target) target = e.target; if (!target || !target.closest) return null; return target.closest('[data-dropzone]'); }; var updateActiveZone = function(zone) { if (activeZone === zone) return; if (activeZone) activeZone.classList.remove('dropzone-active'); activeZone = zone; if (activeZone) activeZone.classList.add('dropzone-active'); }; document.addEventListener('dragenter', function(e) { e.preventDefault(); var zone = findDropZone(e); updateActiveZone(zone); if (e.dataTransfer) { try { e.dataTransfer.dropEffect = zone ? 'copy' : 'none'; } catch(_) {} } }, true); document.addEventListener('dragover', function(e) { e.preventDefault(); var zone = findDropZone(e); updateActiveZone(zone); if (e.dataTransfer) { try { e.dataTransfer.dropEffect = zone ? 'copy' : 'none'; } catch(_) {} } }, true); document.addEventListener('dragleave', function(e) { e.preventDefault(); var zone = findDropZone(e);
|
|
697
|
+
script = "(function() { if (window.__plusui_dropzone_init) return; window.__plusui_dropzone_init = true; var activeZone = null; var findDropZone = function(e) { var target = null; if (e && typeof e.clientX === 'number' && document.elementFromPoint) { target = document.elementFromPoint(e.clientX, e.clientY); } if (!target && e && e.target) target = e.target; if (!target || !target.closest) return null; return target.closest('[data-dropzone]'); }; var updateActiveZone = function(zone) { if (activeZone === zone) return; if (activeZone) activeZone.classList.remove('dropzone-active'); activeZone = zone; if (activeZone) activeZone.classList.add('dropzone-active'); }; document.addEventListener('dragenter', function(e) { e.preventDefault(); var zone = findDropZone(e); updateActiveZone(zone); if (e.dataTransfer) { try { e.dataTransfer.dropEffect = zone ? 'copy' : 'none'; } catch(_) {} } }, true); document.addEventListener('dragover', function(e) { e.preventDefault(); var zone = findDropZone(e); updateActiveZone(zone); if (e.dataTransfer) { try { e.dataTransfer.dropEffect = zone ? 'copy' : 'none'; } catch(_) {} } }, true); document.addEventListener('dragleave', function(e) { e.preventDefault(); var zone = findDropZone(e); if (e.relatedTarget && zone && !zone.contains(e.relatedTarget)) { updateActiveZone(null); } }, true); document.addEventListener('drop', function(e) { e.preventDefault(); updateActiveZone(null); }, true); window.addEventListener('dragover', function(e) { e.preventDefault(); }, true); window.addEventListener('drop', function(e) { e.preventDefault(); }, true); })();";
|
|
683
698
|
}
|
|
684
699
|
pImpl->webview->ExecuteScript(
|
|
685
700
|
std::wstring(script.begin(), script.end()).c_str(),
|
|
@@ -806,7 +821,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
806
821
|
// Block browser default drag-drop while allowing drop zone visual feedback.
|
|
807
822
|
// File delivery is handled natively by macOS drag APIs, not browser events.
|
|
808
823
|
if (win.pImpl->config.disableWebviewDragDrop ||
|
|
809
|
-
win.pImpl->config.
|
|
824
|
+
win.pImpl->config.fileDrop) {
|
|
810
825
|
NSString *disableDragDropScript = @"(function() {"
|
|
811
826
|
"if (window.__plusui_dropzone_init) return;"
|
|
812
827
|
"window.__plusui_dropzone_init = true;"
|
|
@@ -841,7 +856,9 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
841
856
|
"document.addEventListener('dragleave', function(e) {"
|
|
842
857
|
"e.preventDefault();"
|
|
843
858
|
"var zone = findDropZone(e);"
|
|
844
|
-
"
|
|
859
|
+
"if (e.relatedTarget && zone && !zone.contains(e.relatedTarget)) {"
|
|
860
|
+
"updateActiveZone(null);"
|
|
861
|
+
"}"
|
|
845
862
|
"}, true);"
|
|
846
863
|
"document.addEventListener('drop', function(e) {"
|
|
847
864
|
"e.preventDefault();"
|
|
@@ -20,7 +20,7 @@ export interface WebViewConfig {
|
|
|
20
20
|
allowRemoteContent?: boolean;
|
|
21
21
|
dataPath?: string;
|
|
22
22
|
cacheSize?: number; // MB
|
|
23
|
-
disableWebviewDragDrop?: boolean; // Disable
|
|
23
|
+
disableWebviewDragDrop?: boolean; // Disable browser HTML drag-drop (auto-set by WindowConfig.fileDrop)
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export type WindowId = number;
|
|
@@ -270,7 +270,7 @@ struct Window::Impl {
|
|
|
270
270
|
}
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
-
if (!targetImpl || !targetImpl->config.
|
|
273
|
+
if (!targetImpl || !targetImpl->config.fileDrop ||
|
|
274
274
|
!targetImpl->webview) {
|
|
275
275
|
DragFinish(hDrop);
|
|
276
276
|
return 0;
|
|
@@ -346,6 +346,10 @@ struct Window::Impl {
|
|
|
346
346
|
"var files=" +
|
|
347
347
|
filesJson +
|
|
348
348
|
";"
|
|
349
|
+
// Clear any leftover visual feedback from the drag
|
|
350
|
+
"document.querySelectorAll('.dropzone-active,.filedrop-active')"
|
|
351
|
+
".forEach(function(z){z.classList.remove('dropzone-active');"
|
|
352
|
+
"z.classList.remove('filedrop-active');});"
|
|
349
353
|
// Global event — always fires
|
|
350
354
|
"window.dispatchEvent(new "
|
|
351
355
|
"CustomEvent('plusui:fileDrop.filesDropped',"
|
|
@@ -504,7 +508,7 @@ Window Window::create(const WindowConfig &config) {
|
|
|
504
508
|
w.pImpl->state.width = config.width;
|
|
505
509
|
w.pImpl->state.height = config.height;
|
|
506
510
|
|
|
507
|
-
DragAcceptFiles(w.pImpl->hwnd, config.
|
|
511
|
+
DragAcceptFiles(w.pImpl->hwnd, config.fileDrop ? TRUE : FALSE);
|
|
508
512
|
|
|
509
513
|
if (config.center) {
|
|
510
514
|
RECT screen;
|
|
@@ -1069,7 +1073,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
1069
1073
|
|
|
1070
1074
|
// Pure native file-drop mode: when native FileDrop is enabled,
|
|
1071
1075
|
// fully disable browser/WebView drag-drop handling.
|
|
1072
|
-
if (win.pImpl->config.
|
|
1076
|
+
if (win.pImpl->config.fileDrop) {
|
|
1073
1077
|
win.pImpl->config.disableWebviewDragDrop = true;
|
|
1074
1078
|
}
|
|
1075
1079
|
|
|
@@ -1104,18 +1108,20 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
1104
1108
|
Window::Impl::embeddedWebviewByParent[parentHwnd] =
|
|
1105
1109
|
pImpl.get();
|
|
1106
1110
|
|
|
1107
|
-
//
|
|
1108
|
-
//
|
|
1109
|
-
//
|
|
1110
|
-
//
|
|
1111
|
-
//
|
|
1112
|
-
//
|
|
1111
|
+
// AllowExternalDrop must be TRUE so that drag-
|
|
1112
|
+
// related DOM events (dragenter, dragover, dragleave,
|
|
1113
|
+
// drop) fire inside the webview. Our injected JS
|
|
1114
|
+
// calls preventDefault() on all of them to stop the
|
|
1115
|
+
// browser from navigating to the dropped file.
|
|
1116
|
+
// The parent HWND still receives WM_DROPFILES via
|
|
1117
|
+
// DragAcceptFiles, which is where we extract file
|
|
1118
|
+
// metadata and push it into JavaScript.
|
|
1113
1119
|
ComPtr<ICoreWebView2Controller4> controller4;
|
|
1114
1120
|
if (controller &&
|
|
1115
1121
|
SUCCEEDED(controller->QueryInterface(
|
|
1116
1122
|
IID_PPV_ARGS(&controller4))) &&
|
|
1117
1123
|
controller4) {
|
|
1118
|
-
controller4->put_AllowExternalDrop(
|
|
1124
|
+
controller4->put_AllowExternalDrop(TRUE);
|
|
1119
1125
|
}
|
|
1120
1126
|
|
|
1121
1127
|
pImpl->nativeWebView = pImpl->webview.Get();
|
|
@@ -1157,6 +1163,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
1157
1163
|
window.__plusui_dropzone_init = true;
|
|
1158
1164
|
|
|
1159
1165
|
var activeZone = null;
|
|
1166
|
+
var dragDepth = 0;
|
|
1160
1167
|
|
|
1161
1168
|
var findDropZone = function(e) {
|
|
1162
1169
|
var target = null;
|
|
@@ -1170,19 +1177,28 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
1170
1177
|
|
|
1171
1178
|
var updateActiveZone = function(zone) {
|
|
1172
1179
|
if (activeZone === zone) return;
|
|
1173
|
-
if (activeZone)
|
|
1180
|
+
if (activeZone) {
|
|
1181
|
+
activeZone.classList.remove('dropzone-active');
|
|
1182
|
+
activeZone.classList.remove('filedrop-active');
|
|
1183
|
+
}
|
|
1174
1184
|
activeZone = zone;
|
|
1175
|
-
if (activeZone)
|
|
1185
|
+
if (activeZone) {
|
|
1186
|
+
activeZone.classList.add('dropzone-active');
|
|
1187
|
+
activeZone.classList.add('filedrop-active');
|
|
1188
|
+
}
|
|
1176
1189
|
};
|
|
1177
1190
|
|
|
1178
1191
|
// Always preventDefault to stop browser from navigating to file,
|
|
1179
|
-
// but show visual feedback when over a drop zone
|
|
1192
|
+
// but show visual feedback when over a drop zone.
|
|
1193
|
+
// dragDepth tracks nested dragenter/dragleave pairs so we know
|
|
1194
|
+
// when the drag truly leaves the window (depth returns to 0).
|
|
1180
1195
|
document.addEventListener('dragenter', function(e) {
|
|
1181
1196
|
e.preventDefault();
|
|
1197
|
+
dragDepth++;
|
|
1182
1198
|
var zone = findDropZone(e);
|
|
1183
1199
|
updateActiveZone(zone);
|
|
1184
1200
|
if (e.dataTransfer) {
|
|
1185
|
-
try { e.dataTransfer.dropEffect = zone ? '
|
|
1201
|
+
try { e.dataTransfer.dropEffect = zone ? 'copy' : 'none'; } catch(_) {}
|
|
1186
1202
|
}
|
|
1187
1203
|
}, true);
|
|
1188
1204
|
|
|
@@ -1191,18 +1207,25 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
1191
1207
|
var zone = findDropZone(e);
|
|
1192
1208
|
updateActiveZone(zone);
|
|
1193
1209
|
if (e.dataTransfer) {
|
|
1194
|
-
try { e.dataTransfer.dropEffect = zone ? '
|
|
1210
|
+
try { e.dataTransfer.dropEffect = zone ? 'copy' : 'none'; } catch(_) {}
|
|
1195
1211
|
}
|
|
1196
1212
|
}, true);
|
|
1197
1213
|
|
|
1198
1214
|
document.addEventListener('dragleave', function(e) {
|
|
1199
1215
|
e.preventDefault();
|
|
1200
|
-
|
|
1201
|
-
|
|
1216
|
+
dragDepth--;
|
|
1217
|
+
if (dragDepth <= 0) {
|
|
1218
|
+
dragDepth = 0;
|
|
1219
|
+
updateActiveZone(null);
|
|
1220
|
+
} else {
|
|
1221
|
+
var zone = findDropZone(e);
|
|
1222
|
+
updateActiveZone(zone);
|
|
1223
|
+
}
|
|
1202
1224
|
}, true);
|
|
1203
1225
|
|
|
1204
1226
|
document.addEventListener('drop', function(e) {
|
|
1205
1227
|
e.preventDefault();
|
|
1228
|
+
dragDepth = 0;
|
|
1206
1229
|
updateActiveZone(null);
|
|
1207
1230
|
}, true);
|
|
1208
1231
|
|
|
@@ -1619,7 +1642,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
1619
1642
|
msg.substr(p1 + 1, p2 - p1 - 1);
|
|
1620
1643
|
bool enabled = params.find("true") !=
|
|
1621
1644
|
std::string::npos;
|
|
1622
|
-
pImpl->config.
|
|
1645
|
+
pImpl->config.fileDrop = enabled;
|
|
1623
1646
|
|
|
1624
1647
|
HWND targetHwnd = nullptr;
|
|
1625
1648
|
if (pImpl->window) {
|
|
@@ -1652,7 +1675,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
1652
1675
|
}
|
|
1653
1676
|
success = true;
|
|
1654
1677
|
} else if (fileDropMethod == "isEnabled") {
|
|
1655
|
-
result = pImpl->config.
|
|
1678
|
+
result = pImpl->config.fileDrop
|
|
1656
1679
|
? "true"
|
|
1657
1680
|
: "false";
|
|
1658
1681
|
success = true;
|
|
@@ -1758,7 +1781,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
1758
1781
|
// Block browser default drag-drop while allowing drop zone visual feedback.
|
|
1759
1782
|
// File delivery is handled natively by macOS drag APIs, not browser events.
|
|
1760
1783
|
if (win.pImpl->config.disableWebviewDragDrop ||
|
|
1761
|
-
win.pImpl->config.
|
|
1784
|
+
win.pImpl->config.fileDrop) {
|
|
1762
1785
|
NSString *disableDragDropScript =
|
|
1763
1786
|
@"(function() {"
|
|
1764
1787
|
"if (window.__plusui_dropzone_init) return;"
|
|
@@ -1794,7 +1817,9 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
1794
1817
|
"document.addEventListener('dragleave', function(e) {"
|
|
1795
1818
|
"e.preventDefault();"
|
|
1796
1819
|
"var zone = findDropZone(e);"
|
|
1797
|
-
"
|
|
1820
|
+
"if (e.relatedTarget && zone && !zone.contains(e.relatedTarget)) {"
|
|
1821
|
+
"updateActiveZone(null);"
|
|
1822
|
+
"}"
|
|
1798
1823
|
"}, true);"
|
|
1799
1824
|
"document.addEventListener('drop', function(e) {"
|
|
1800
1825
|
"e.preventDefault();"
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
* .width(1200) .width(1200)
|
|
75
75
|
* .height(800) .height(800)
|
|
76
76
|
* .devtools(true) .devtools(true)
|
|
77
|
-
* .
|
|
77
|
+
* .fileDrop(true); .fileDrop(true)
|
|
78
78
|
* .build() ← returns Window
|
|
79
79
|
* TS: app.quit() C++: app.quit()
|
|
80
80
|
* TS: app.onReady(cb) C++: (start after build())
|
|
@@ -201,7 +201,7 @@
|
|
|
201
201
|
* .title("My App")
|
|
202
202
|
* .width(1200)
|
|
203
203
|
* .height(800)
|
|
204
|
-
* .
|
|
204
|
+
* .fileDrop(true)
|
|
205
205
|
* .build();
|
|
206
206
|
*
|
|
207
207
|
* MyApp connect;
|
|
@@ -25,47 +25,47 @@ private:
|
|
|
25
25
|
std::unique_ptr<Impl> pImpl;
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
class App::Builder {
|
|
29
|
-
public:
|
|
30
|
-
struct Config {
|
|
31
|
-
std::string title = "PlusUI App";
|
|
32
|
-
int width = 1200;
|
|
33
|
-
int height = 800;
|
|
34
|
-
bool resizable = true;
|
|
35
|
-
bool devtools = true;
|
|
36
|
-
std::string trayIconPath;
|
|
37
|
-
std::string trayTooltip;
|
|
38
|
-
bool alwaysOnTop = false;
|
|
39
|
-
bool centered = true;
|
|
40
|
-
bool transparent = false;
|
|
41
|
-
bool decorations = true;
|
|
42
|
-
bool skipTaskbar = false;
|
|
43
|
-
bool scrollbars = true;
|
|
44
|
-
bool
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
Builder();
|
|
48
|
-
|
|
49
|
-
Builder &title(const std::string &t);
|
|
50
|
-
Builder &width(int w);
|
|
51
|
-
Builder &height(int h);
|
|
52
|
-
Builder &resizable(bool r);
|
|
53
|
-
Builder &devtools(bool d);
|
|
54
|
-
Builder &trayIcon(const std::string &icon);
|
|
55
|
-
Builder &trayTooltip(const std::string &tooltip);
|
|
56
|
-
Builder &alwaysOnTop(bool top);
|
|
57
|
-
Builder ¢ered(bool center);
|
|
58
|
-
Builder &transparent(bool transparent);
|
|
59
|
-
Builder &decorations(bool decorations);
|
|
60
|
-
Builder &skipTaskbar(bool skip);
|
|
61
|
-
Builder &scrollbars(bool show);
|
|
62
|
-
Builder &
|
|
63
|
-
|
|
64
|
-
Window build();
|
|
65
|
-
|
|
66
|
-
private:
|
|
67
|
-
Config config;
|
|
68
|
-
};
|
|
28
|
+
class App::Builder {
|
|
29
|
+
public:
|
|
30
|
+
struct Config {
|
|
31
|
+
std::string title = "PlusUI App";
|
|
32
|
+
int width = 1200;
|
|
33
|
+
int height = 800;
|
|
34
|
+
bool resizable = true;
|
|
35
|
+
bool devtools = true;
|
|
36
|
+
std::string trayIconPath;
|
|
37
|
+
std::string trayTooltip;
|
|
38
|
+
bool alwaysOnTop = false;
|
|
39
|
+
bool centered = true;
|
|
40
|
+
bool transparent = false;
|
|
41
|
+
bool decorations = true;
|
|
42
|
+
bool skipTaskbar = false;
|
|
43
|
+
bool scrollbars = true;
|
|
44
|
+
bool fileDrop = true; // Enable OS file drop
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
Builder();
|
|
48
|
+
|
|
49
|
+
Builder &title(const std::string &t);
|
|
50
|
+
Builder &width(int w);
|
|
51
|
+
Builder &height(int h);
|
|
52
|
+
Builder &resizable(bool r);
|
|
53
|
+
Builder &devtools(bool d);
|
|
54
|
+
Builder &trayIcon(const std::string &icon);
|
|
55
|
+
Builder &trayTooltip(const std::string &tooltip);
|
|
56
|
+
Builder &alwaysOnTop(bool top);
|
|
57
|
+
Builder ¢ered(bool center);
|
|
58
|
+
Builder &transparent(bool transparent);
|
|
59
|
+
Builder &decorations(bool decorations);
|
|
60
|
+
Builder &skipTaskbar(bool skip);
|
|
61
|
+
Builder &scrollbars(bool show);
|
|
62
|
+
Builder &fileDrop(bool enable);
|
|
63
|
+
|
|
64
|
+
Window build();
|
|
65
|
+
|
|
66
|
+
private:
|
|
67
|
+
Config config;
|
|
68
|
+
};
|
|
69
69
|
|
|
70
70
|
App::Builder createApp();
|
|
71
71
|
|
|
@@ -11,46 +11,44 @@ namespace plusui {
|
|
|
11
11
|
|
|
12
12
|
class TrayManager;
|
|
13
13
|
|
|
14
|
-
struct WindowConfig {
|
|
15
|
-
// Window properties
|
|
16
|
-
std::string title = "PlusUI Window";
|
|
17
|
-
int x = -1;
|
|
18
|
-
int y = -1;
|
|
19
|
-
int width = 800;
|
|
20
|
-
int height = 600;
|
|
21
|
-
int minWidth = 100;
|
|
22
|
-
int minHeight = 100;
|
|
23
|
-
int maxWidth = -1;
|
|
24
|
-
int maxHeight = -1;
|
|
25
|
-
bool resizable = true;
|
|
26
|
-
bool minimizable = true;
|
|
27
|
-
bool maximizable = true;
|
|
28
|
-
bool closable = true;
|
|
29
|
-
bool alwaysOnTop = false;
|
|
30
|
-
bool center = true;
|
|
31
|
-
bool frame = true;
|
|
32
|
-
bool transparent = false;
|
|
33
|
-
bool decorations = true;
|
|
34
|
-
bool skipTaskbar = false;
|
|
35
|
-
double opacity = 1.0;
|
|
36
|
-
bool fullscreen = false;
|
|
37
|
-
bool
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// WebView properties
|
|
41
|
-
std::string userAgent = "";
|
|
42
|
-
bool devtools = true;
|
|
43
|
-
bool contextMenu = true;
|
|
44
|
-
bool javascript = true;
|
|
45
|
-
bool webSecurity = true;
|
|
46
|
-
bool allowFileAccess = false;
|
|
47
|
-
bool allowRemoteContent = true;
|
|
48
|
-
std::string dataPath = "";
|
|
49
|
-
int cacheSize = 100; // MB
|
|
50
|
-
bool scrollbars = true; // Show/hide scrollbars
|
|
51
|
-
|
|
52
|
-
true; // Disable webview drag-drop (usually set by enableFileDrop)
|
|
53
|
-
};
|
|
14
|
+
struct WindowConfig {
|
|
15
|
+
// Window properties
|
|
16
|
+
std::string title = "PlusUI Window";
|
|
17
|
+
int x = -1;
|
|
18
|
+
int y = -1;
|
|
19
|
+
int width = 800;
|
|
20
|
+
int height = 600;
|
|
21
|
+
int minWidth = 100;
|
|
22
|
+
int minHeight = 100;
|
|
23
|
+
int maxWidth = -1;
|
|
24
|
+
int maxHeight = -1;
|
|
25
|
+
bool resizable = true;
|
|
26
|
+
bool minimizable = true;
|
|
27
|
+
bool maximizable = true;
|
|
28
|
+
bool closable = true;
|
|
29
|
+
bool alwaysOnTop = false;
|
|
30
|
+
bool center = true;
|
|
31
|
+
bool frame = true;
|
|
32
|
+
bool transparent = false;
|
|
33
|
+
bool decorations = true;
|
|
34
|
+
bool skipTaskbar = false;
|
|
35
|
+
double opacity = 1.0;
|
|
36
|
+
bool fullscreen = false;
|
|
37
|
+
bool fileDrop = true; // Enable OS file drop (drag files from Explorer/Finder into window)
|
|
38
|
+
bool disableWebviewDragDrop = true; // Disable browser HTML drag-drop (auto-set by fileDrop)
|
|
39
|
+
|
|
40
|
+
// WebView properties
|
|
41
|
+
std::string userAgent = "";
|
|
42
|
+
bool devtools = true;
|
|
43
|
+
bool contextMenu = true;
|
|
44
|
+
bool javascript = true;
|
|
45
|
+
bool webSecurity = true;
|
|
46
|
+
bool allowFileAccess = false;
|
|
47
|
+
bool allowRemoteContent = true;
|
|
48
|
+
std::string dataPath = "";
|
|
49
|
+
int cacheSize = 100; // MB
|
|
50
|
+
bool scrollbars = true; // Show/hide scrollbars
|
|
51
|
+
};
|
|
54
52
|
|
|
55
53
|
struct WindowState {
|
|
56
54
|
int x = 0;
|