plusui-native-core 0.1.103 → 0.1.104
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/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 +128 -42
- package/Core/Features/Window/webview.cpp +10 -5
- package/Core/Features/Window/webview.ts +1 -1
- package/Core/Features/Window/window.cpp +9 -7
- 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,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,49 +86,40 @@ 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
|
-
element.classList.remove('dropzone-active');
|
|
104
|
+
el.classList.remove('dropzone-active');
|
|
78
105
|
});
|
|
79
106
|
|
|
80
|
-
|
|
107
|
+
el.addEventListener('dragleave', (e: DragEvent) => {
|
|
81
108
|
const related = e.relatedTarget as Node | null;
|
|
82
|
-
if (!related || !
|
|
83
|
-
|
|
109
|
+
if (!related || !el.contains(related)) {
|
|
110
|
+
el.classList.remove('dropzone-active');
|
|
84
111
|
}
|
|
85
112
|
});
|
|
86
113
|
|
|
87
|
-
|
|
114
|
+
el.addEventListener('dragenter', (e: DragEvent) => {
|
|
88
115
|
e.preventDefault();
|
|
89
|
-
|
|
116
|
+
el.classList.add('dropzone-active');
|
|
90
117
|
if (e.dataTransfer) {
|
|
91
|
-
// 'move' matches the native OS dropzone cursor (arrow + file icon)
|
|
92
|
-
// rather than the browser's '+ Copy' cursor
|
|
93
118
|
try { e.dataTransfer.dropEffect = 'move'; } catch (_) {}
|
|
94
119
|
}
|
|
95
120
|
});
|
|
96
121
|
|
|
97
|
-
|
|
122
|
+
el.addEventListener('dragover', (e: DragEvent) => {
|
|
98
123
|
e.preventDefault();
|
|
99
124
|
if (e.dataTransfer) {
|
|
100
125
|
try { e.dataTransfer.dropEffect = 'move'; } catch (_) {}
|
|
@@ -102,27 +127,88 @@ export function createDropZone(name: string, el?: HTMLElement | null): DropZone
|
|
|
102
127
|
});
|
|
103
128
|
}
|
|
104
129
|
|
|
105
|
-
|
|
106
|
-
|
|
130
|
+
const zone: DropZone = {
|
|
131
|
+
name,
|
|
132
|
+
element: el,
|
|
107
133
|
onFiles: (callback: (files: FileInfo[]) => void) => {
|
|
108
|
-
|
|
109
|
-
|
|
134
|
+
(zone as any).__onFiles = callback;
|
|
135
|
+
zones.set(name, zone);
|
|
136
|
+
return () => {
|
|
137
|
+
(zone as any).__onFiles = undefined;
|
|
138
|
+
zones.delete(name);
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
onDragEnter: (callback: () => void) => {
|
|
142
|
+
(zone as any).__onDragEnter = callback;
|
|
143
|
+
return () => { (zone as any).__onDragEnter = undefined; };
|
|
144
|
+
},
|
|
145
|
+
onDragLeave: (callback: () => void) => {
|
|
146
|
+
(zone as any).__onDragLeave = callback;
|
|
147
|
+
return () => { (zone as any).__onDragLeave = undefined; };
|
|
110
148
|
}
|
|
111
149
|
};
|
|
150
|
+
|
|
151
|
+
zones.set(name, zone);
|
|
152
|
+
return zone;
|
|
112
153
|
}
|
|
113
154
|
|
|
114
155
|
export const fileDrop = {
|
|
115
|
-
setEnabled: (enabled: boolean) => invoke<void>('fileDrop.setEnabled', [enabled]),
|
|
116
|
-
isEnabled: () => invoke<boolean>('fileDrop.isEnabled'),
|
|
117
156
|
createDropZone,
|
|
157
|
+
|
|
158
|
+
onFiles: (name: string, callback: (files: FileInfo[]) => void): (() => void) => {
|
|
159
|
+
const zone = zones.get(name);
|
|
160
|
+
if (zone) {
|
|
161
|
+
return zone.onFiles(callback);
|
|
162
|
+
}
|
|
163
|
+
const newZone = createDropZone(name);
|
|
164
|
+
return newZone.onFiles(callback);
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
setEnabled: (enabled: boolean): Promise<void> =>
|
|
168
|
+
invoke<void>('fileDrop.setEnabled', [enabled]),
|
|
169
|
+
|
|
170
|
+
isEnabled: (): Promise<boolean> =>
|
|
171
|
+
invoke<boolean>('fileDrop.isEnabled'),
|
|
172
|
+
|
|
173
|
+
startDrag: (filePaths: string[]): Promise<void> =>
|
|
174
|
+
invoke<void>('fileDrop.startDrag', [filePaths]),
|
|
118
175
|
};
|
|
119
176
|
|
|
120
177
|
export function formatFileSize(bytes: number): string {
|
|
121
178
|
if (bytes === 0) return '0 B';
|
|
122
179
|
const k = 1024;
|
|
123
|
-
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
180
|
+
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
124
181
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
125
182
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
|
|
126
183
|
}
|
|
127
184
|
|
|
185
|
+
export function filterFilesByExtension(files: FileInfo[], extensions: string[]): FileInfo[] {
|
|
186
|
+
const exts = extensions.map(e => e.toLowerCase().replace(/^\./, ''));
|
|
187
|
+
return files.filter(f => {
|
|
188
|
+
const ext = f.name.split('.').pop()?.toLowerCase() || '';
|
|
189
|
+
return exts.includes(ext);
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function filterFilesByMimeType(files: FileInfo[], mimeTypes: string[]): FileInfo[] {
|
|
194
|
+
return files.filter(f => mimeTypes.includes(f.type));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function isImageFile(file: FileInfo): boolean {
|
|
198
|
+
return file.type.startsWith('image/');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function isVideoFile(file: FileInfo): boolean {
|
|
202
|
+
return file.type.startsWith('video/');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function isAudioFile(file: FileInfo): boolean {
|
|
206
|
+
return file.type.startsWith('audio/');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function isTextFile(file: FileInfo): boolean {
|
|
210
|
+
return file.type.startsWith('text/') || file.name.endsWith('.txt') ||
|
|
211
|
+
file.name.endsWith('.md') || file.name.endsWith('.json');
|
|
212
|
+
}
|
|
213
|
+
|
|
128
214
|
export default fileDrop;
|
|
@@ -167,7 +167,7 @@ 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;
|
|
@@ -213,7 +213,10 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
213
213
|
document.addEventListener('dragleave', function(e) {
|
|
214
214
|
e.preventDefault();
|
|
215
215
|
var zone = findDropZone(e);
|
|
216
|
-
|
|
216
|
+
// Only remove if actually leaving the zone (relatedTarget is outside)
|
|
217
|
+
if (e.relatedTarget && zone && !zone.contains(e.relatedTarget)) {
|
|
218
|
+
updateActiveZone(null);
|
|
219
|
+
}
|
|
217
220
|
}, true);
|
|
218
221
|
|
|
219
222
|
document.addEventListener('drop', function(e) {
|
|
@@ -679,7 +682,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
679
682
|
script = "(function() { delete window.__plusui_dropzone_init; })();";
|
|
680
683
|
} else {
|
|
681
684
|
// 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);
|
|
685
|
+
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
686
|
}
|
|
684
687
|
pImpl->webview->ExecuteScript(
|
|
685
688
|
std::wstring(script.begin(), script.end()).c_str(),
|
|
@@ -806,7 +809,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
806
809
|
// Block browser default drag-drop while allowing drop zone visual feedback.
|
|
807
810
|
// File delivery is handled natively by macOS drag APIs, not browser events.
|
|
808
811
|
if (win.pImpl->config.disableWebviewDragDrop ||
|
|
809
|
-
win.pImpl->config.
|
|
812
|
+
win.pImpl->config.fileDrop) {
|
|
810
813
|
NSString *disableDragDropScript = @"(function() {"
|
|
811
814
|
"if (window.__plusui_dropzone_init) return;"
|
|
812
815
|
"window.__plusui_dropzone_init = true;"
|
|
@@ -841,7 +844,9 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
841
844
|
"document.addEventListener('dragleave', function(e) {"
|
|
842
845
|
"e.preventDefault();"
|
|
843
846
|
"var zone = findDropZone(e);"
|
|
844
|
-
"
|
|
847
|
+
"if (e.relatedTarget && zone && !zone.contains(e.relatedTarget)) {"
|
|
848
|
+
"updateActiveZone(null);"
|
|
849
|
+
"}"
|
|
845
850
|
"}, true);"
|
|
846
851
|
"document.addEventListener('drop', function(e) {"
|
|
847
852
|
"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;
|
|
@@ -504,7 +504,7 @@ Window Window::create(const WindowConfig &config) {
|
|
|
504
504
|
w.pImpl->state.width = config.width;
|
|
505
505
|
w.pImpl->state.height = config.height;
|
|
506
506
|
|
|
507
|
-
DragAcceptFiles(w.pImpl->hwnd, config.
|
|
507
|
+
DragAcceptFiles(w.pImpl->hwnd, config.fileDrop ? TRUE : FALSE);
|
|
508
508
|
|
|
509
509
|
if (config.center) {
|
|
510
510
|
RECT screen;
|
|
@@ -1069,7 +1069,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
1069
1069
|
|
|
1070
1070
|
// Pure native file-drop mode: when native FileDrop is enabled,
|
|
1071
1071
|
// fully disable browser/WebView drag-drop handling.
|
|
1072
|
-
if (win.pImpl->config.
|
|
1072
|
+
if (win.pImpl->config.fileDrop) {
|
|
1073
1073
|
win.pImpl->config.disableWebviewDragDrop = true;
|
|
1074
1074
|
}
|
|
1075
1075
|
|
|
@@ -1619,7 +1619,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
1619
1619
|
msg.substr(p1 + 1, p2 - p1 - 1);
|
|
1620
1620
|
bool enabled = params.find("true") !=
|
|
1621
1621
|
std::string::npos;
|
|
1622
|
-
pImpl->config.
|
|
1622
|
+
pImpl->config.fileDrop = enabled;
|
|
1623
1623
|
|
|
1624
1624
|
HWND targetHwnd = nullptr;
|
|
1625
1625
|
if (pImpl->window) {
|
|
@@ -1652,7 +1652,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
1652
1652
|
}
|
|
1653
1653
|
success = true;
|
|
1654
1654
|
} else if (fileDropMethod == "isEnabled") {
|
|
1655
|
-
result = pImpl->config.
|
|
1655
|
+
result = pImpl->config.fileDrop
|
|
1656
1656
|
? "true"
|
|
1657
1657
|
: "false";
|
|
1658
1658
|
success = true;
|
|
@@ -1758,7 +1758,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
1758
1758
|
// Block browser default drag-drop while allowing drop zone visual feedback.
|
|
1759
1759
|
// File delivery is handled natively by macOS drag APIs, not browser events.
|
|
1760
1760
|
if (win.pImpl->config.disableWebviewDragDrop ||
|
|
1761
|
-
win.pImpl->config.
|
|
1761
|
+
win.pImpl->config.fileDrop) {
|
|
1762
1762
|
NSString *disableDragDropScript =
|
|
1763
1763
|
@"(function() {"
|
|
1764
1764
|
"if (window.__plusui_dropzone_init) return;"
|
|
@@ -1794,7 +1794,9 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
|
|
|
1794
1794
|
"document.addEventListener('dragleave', function(e) {"
|
|
1795
1795
|
"e.preventDefault();"
|
|
1796
1796
|
"var zone = findDropZone(e);"
|
|
1797
|
-
"
|
|
1797
|
+
"if (e.relatedTarget && zone && !zone.contains(e.relatedTarget)) {"
|
|
1798
|
+
"updateActiveZone(null);"
|
|
1799
|
+
"}"
|
|
1798
1800
|
"}, true);"
|
|
1799
1801
|
"document.addEventListener('drop', function(e) {"
|
|
1800
1802
|
"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;
|