plusui-native-core 0.1.47 → 0.1.49

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.
@@ -240,17 +240,49 @@ struct Window::Impl {
240
240
 
241
241
  DragFinish(hDrop);
242
242
 
243
- std::string eventScript =
244
- "window.dispatchEvent(new CustomEvent('plusui:fileDrop.filesDropped',"
245
- " { detail: { files: " +
246
- filesJson + " } }));";
247
-
248
- // Also emit to backend event API so C++ listeners can react.
249
- event::emit("fileDrop.filesDropped", filesJson);
243
+ POINT dropPoint = {0, 0};
244
+ DragQueryPoint(hDrop, &dropPoint);
245
+
246
+ std::string zoneHitScript =
247
+ "(function(){try{"
248
+ "var el=document.elementFromPoint(" +
249
+ std::to_string(dropPoint.x) + "," + std::to_string(dropPoint.y) +
250
+ ");"
251
+ "if(!el) return false;"
252
+ "if(!el.closest) return false;"
253
+ "return !!el.closest('[data-dropzone=\\\"true\\\"], .filedrop-zone, .dropzone');"
254
+ "}catch(_){return false;}})();";
250
255
 
251
256
  targetImpl->webview->ExecuteScript(
252
- std::wstring(eventScript.begin(), eventScript.end()).c_str(),
253
- nullptr);
257
+ std::wstring(zoneHitScript.begin(), zoneHitScript.end()).c_str(),
258
+ Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
259
+ [targetImpl, filesJson](HRESULT errorCode,
260
+ LPCWSTR resultJson) -> HRESULT {
261
+ if (FAILED(errorCode) || !targetImpl || !targetImpl->webview)
262
+ return S_OK;
263
+
264
+ bool overDropZone = false;
265
+ if (resultJson) {
266
+ std::wstring wres(resultJson);
267
+ overDropZone = (wres.find(L"true") != std::wstring::npos);
268
+ }
269
+ if (!overDropZone)
270
+ return S_OK;
271
+
272
+ std::string eventScript =
273
+ "window.dispatchEvent(new CustomEvent('plusui:fileDrop.filesDropped',"
274
+ " { detail: { files: " +
275
+ filesJson + " } }));";
276
+
277
+ event::emit("fileDrop.filesDropped", filesJson);
278
+
279
+ targetImpl->webview->ExecuteScript(
280
+ std::wstring(eventScript.begin(), eventScript.end())
281
+ .c_str(),
282
+ nullptr);
283
+ return S_OK;
284
+ })
285
+ .Get());
254
286
  return 0;
255
287
  }
256
288
  case WM_SETFOCUS:
@@ -935,6 +967,18 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
935
967
  Window::Impl::embeddedWebviewByParent[parentHwnd] =
936
968
  pImpl.get();
937
969
 
970
+ ComPtr<ICoreWebView2Controller4> controller4;
971
+ if (controller &&
972
+ SUCCEEDED(controller->QueryInterface(
973
+ IID_PPV_ARGS(&controller4))) &&
974
+ controller4) {
975
+ bool allowExternalDrop =
976
+ !(pImpl->config.enableFileDrop ||
977
+ pImpl->config.disableWebviewDragDrop);
978
+ controller4->put_AllowExternalDrop(
979
+ allowExternalDrop ? TRUE : FALSE);
980
+ }
981
+
938
982
  pImpl->nativeWebView = pImpl->webview.Get();
939
983
  pImpl->ready = true;
940
984
 
@@ -972,12 +1016,33 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
972
1016
  if (window.__plusui_nativeFileDropOnly) return;
973
1017
  window.__plusui_nativeFileDropOnly = true;
974
1018
 
1019
+ var isOverDropZone = function(e) {
1020
+ if (!e) return false;
1021
+ var target = null;
1022
+ if (typeof e.clientX === 'number' && typeof e.clientY === 'number' && document.elementFromPoint) {
1023
+ target = document.elementFromPoint(e.clientX, e.clientY);
1024
+ }
1025
+ if (!target && e.target && e.target.nodeType === 1) {
1026
+ target = e.target;
1027
+ }
1028
+ if (!target || !target.closest) return false;
1029
+ return !!target.closest('[data-dropzone="true"], .filedrop-zone, .dropzone');
1030
+ };
1031
+
1032
+ var zoneActive = false;
1033
+ var emitZoneState = function(next) {
1034
+ if (next === zoneActive) return;
1035
+ zoneActive = next;
1036
+ window.dispatchEvent(new CustomEvent(next ? 'plusui:fileDrop.dragEnter' : 'plusui:fileDrop.dragLeave', { detail: {} }));
1037
+ };
1038
+
975
1039
  var block = function(e) {
976
1040
  if (!e) return false;
977
- if (e.type === 'dragenter') {
978
- window.dispatchEvent(new CustomEvent('plusui:fileDrop.dragEnter', { detail: {} }));
979
- } else if (e.type === 'dragleave') {
980
- window.dispatchEvent(new CustomEvent('plusui:fileDrop.dragLeave', { detail: {} }));
1041
+ var overDropZone = isOverDropZone(e);
1042
+ if (e.type === 'drop') {
1043
+ emitZoneState(false);
1044
+ } else {
1045
+ emitZoneState(overDropZone);
981
1046
  }
982
1047
  e.preventDefault();
983
1048
  e.stopPropagation();
@@ -985,7 +1050,7 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
985
1050
  e.stopImmediatePropagation();
986
1051
  }
987
1052
  if (e.dataTransfer) {
988
- try { e.dataTransfer.dropEffect = 'none'; } catch (_) {}
1053
+ try { e.dataTransfer.dropEffect = overDropZone ? 'copy' : 'none'; } catch (_) {}
989
1054
  }
990
1055
  return false;
991
1056
  };
@@ -1283,6 +1348,22 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
1283
1348
  enabled ? TRUE
1284
1349
  : FALSE);
1285
1350
  }
1351
+
1352
+ if (pImpl->controller) {
1353
+ ComPtr<ICoreWebView2Controller4>
1354
+ controller4;
1355
+ if (SUCCEEDED(pImpl->controller.As(
1356
+ &controller4)) &&
1357
+ controller4) {
1358
+ bool allowExternalDrop =
1359
+ !(pImpl->config.enableFileDrop ||
1360
+ pImpl->config
1361
+ .disableWebviewDragDrop);
1362
+ controller4->put_AllowExternalDrop(
1363
+ allowExternalDrop ? TRUE
1364
+ : FALSE);
1365
+ }
1366
+ }
1286
1367
  }
1287
1368
  success = true;
1288
1369
  } else if (fileDropMethod == "isEnabled") {
@@ -1370,14 +1451,28 @@ Window Window::create(void *windowHandle, const WindowConfig &config) {
1370
1451
  NSString *disableDragDropScript = @"(function() {"
1371
1452
  "if (window.__plusui_nativeFileDropOnly) return;"
1372
1453
  "window.__plusui_nativeFileDropOnly = true;"
1454
+ "var isOverDropZone = function(e) {"
1455
+ "if (!e) return false;"
1456
+ "var target = null;"
1457
+ "if (typeof e.clientX === 'number' && typeof e.clientY === 'number' && document.elementFromPoint) { target = document.elementFromPoint(e.clientX, e.clientY); }"
1458
+ "if (!target && e.target && e.target.nodeType === 1) { target = e.target; }"
1459
+ "if (!target || !target.closest) return false;"
1460
+ "return !!target.closest('[data-dropzone=\\\"true\\\"], .filedrop-zone, .dropzone');"
1461
+ "};"
1462
+ "var zoneActive = false;"
1463
+ "var emitZoneState = function(next) {"
1464
+ "if (next === zoneActive) return;"
1465
+ "zoneActive = next;"
1466
+ "window.dispatchEvent(new CustomEvent(next ? 'plusui:fileDrop.dragEnter' : 'plusui:fileDrop.dragLeave', { detail: {} }));"
1467
+ "};"
1373
1468
  "var block = function(e) {"
1374
1469
  "if (!e) return false;"
1375
- "if (e.type === 'dragenter') { window.dispatchEvent(new CustomEvent('plusui:fileDrop.dragEnter', { detail: {} })); }"
1376
- "else if (e.type === 'dragleave') { window.dispatchEvent(new CustomEvent('plusui:fileDrop.dragLeave', { detail: {} })); }"
1470
+ "var overDropZone = isOverDropZone(e);"
1471
+ "if (e.type === 'drop') { emitZoneState(false); } else { emitZoneState(overDropZone); }"
1377
1472
  "e.preventDefault();"
1378
1473
  "e.stopPropagation();"
1379
1474
  "if (typeof e.stopImmediatePropagation === 'function') e.stopImmediatePropagation();"
1380
- "if (e.dataTransfer) { try { e.dataTransfer.dropEffect = 'none'; } catch (_) {} }"
1475
+ "if (e.dataTransfer) { try { e.dataTransfer.dropEffect = overDropZone ? 'copy' : 'none'; } catch (_) {} }"
1381
1476
  "return false;"
1382
1477
  "};"
1383
1478
  "window.__plusui_dragDropEvents = ['dragenter','dragover','dragleave','drop'];"
@@ -1852,12 +1947,33 @@ void Window::setWebviewDragDropEnabled(bool enabled) {
1852
1947
  window.__plusui_nativeFileDropOnly = true;
1853
1948
  window.__plusui_dragDropDisabled = true;
1854
1949
 
1950
+ var isOverDropZone = function(e) {
1951
+ if (!e) return false;
1952
+ var target = null;
1953
+ if (typeof e.clientX === 'number' && typeof e.clientY === 'number' && document.elementFromPoint) {
1954
+ target = document.elementFromPoint(e.clientX, e.clientY);
1955
+ }
1956
+ if (!target && e.target && e.target.nodeType === 1) {
1957
+ target = e.target;
1958
+ }
1959
+ if (!target || !target.closest) return false;
1960
+ return !!target.closest('[data-dropzone="true"], .filedrop-zone, .dropzone');
1961
+ };
1962
+
1963
+ var zoneActive = false;
1964
+ var emitZoneState = function(next) {
1965
+ if (next === zoneActive) return;
1966
+ zoneActive = next;
1967
+ window.dispatchEvent(new CustomEvent(next ? 'plusui:fileDrop.dragEnter' : 'plusui:fileDrop.dragLeave', { detail: {} }));
1968
+ };
1969
+
1855
1970
  var block = function(e) {
1856
1971
  if (!e) return false;
1857
- if (e.type === 'dragenter') {
1858
- window.dispatchEvent(new CustomEvent('plusui:fileDrop.dragEnter', { detail: {} }));
1859
- } else if (e.type === 'dragleave') {
1860
- window.dispatchEvent(new CustomEvent('plusui:fileDrop.dragLeave', { detail: {} }));
1972
+ var overDropZone = isOverDropZone(e);
1973
+ if (e.type === 'drop') {
1974
+ emitZoneState(false);
1975
+ } else {
1976
+ emitZoneState(overDropZone);
1861
1977
  }
1862
1978
  e.preventDefault();
1863
1979
  e.stopPropagation();
@@ -1865,7 +1981,7 @@ void Window::setWebviewDragDropEnabled(bool enabled) {
1865
1981
  e.stopImmediatePropagation();
1866
1982
  }
1867
1983
  if (e.dataTransfer) {
1868
- try { e.dataTransfer.dropEffect = 'none'; } catch (_) {}
1984
+ try { e.dataTransfer.dropEffect = overDropZone ? 'copy' : 'none'; } catch (_) {}
1869
1985
  }
1870
1986
  return false;
1871
1987
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plusui-native-core",
3
- "version": "0.1.47",
3
+ "version": "0.1.49",
4
4
  "description": "PlusUI Core framework (frontend + backend implementations)",
5
5
  "type": "module",
6
6
  "files": [