mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
7.1 KiB
7.1 KiB
Drag-and-Drop Analysis (Windows Focus)
Overview
- Wails v3 supports two drag-and-drop surfaces when
EnableDragAndDropis set: a native window-level file drop channel and HTML drag/drop within the embedded webview. You can see the end-to-end flow inv3/pkg/application/webview_window_windows.go(native bridge) andv3/internal/runtime/desktop/@wailsio/runtime/src/window.ts(runtime dispatcher). - Native drops rely on a custom
IDropTargetimplementation that gathers files/coordinates from Win32, pushes them through the Wails event system, and fan out asevents.Common.WindowDropZoneFilesDroppedwith enriched context (DropZoneDetails, attribute map, coordinates). - Pure HTML drag/drop (e.g.
v3/examples/html-dnd-api) is handled entirely by the browser layer; Wails only needs to stay out of the way so standard DOM APIs operate normally.
Native/Go Pipeline
- When a window is created with
EnableDragAndDrop, line ~1948 ofv3/pkg/application/webview_window_windows.goinstantiatesw32.NewDropTarget()and (optionally) callschromium.AllowExternalDrag(false)to disable WebView2’s built-in file handling. EnumChildWindowsregisters the COM drop target against every current child HWND. The callbacks (OnEnter,OnOver,OnLeave,OnDrop) emit Windows-specific events (events.Windows.WindowDragEnteretc.) viaw.parent.emit, so listeners can react even before files are delivered.DropTarget.Drop(seev3/pkg/w32/idroptarget.go:69-140) extracts filenames from theIDataObject, then hands control back to the window impl. Coordinates arrive as screen pixels (POINT), soOnDropconverts to window-relative coordinates and then callsconvertWindowToWebviewCoordinates(lines ~1908-1990). FinallyInitiateFrontendDropProcessing(inv3/pkg/application/webview_window.go:1484-1515) formats a JS call:window.wails.Window.HandlePlatformFileDrop([...], x, y).MessageProcessorcaseWindowDropZoneDropped(v3/pkg/application/messageprocessor_window.go:430-488) decodes the payload that the runtime posts back, wraps it inDropZoneDetails, and pushes it through the bufferedwindowDragAndDropBuffer.App.handleDragAndDropMessagepicks it up and forwards toWebviewWindow.HandleDragAndDropMessage, which attaches dropped files + drop-zone metadata to theWindowEventContext.- The consumer API is the
events.Common.WindowDropZoneFilesDroppedevent. The drag-n-drop example shows how to subscribe (v3/examples/drag-n-drop/main.go:109-158) and propagate to the frontend via custom events.
Runtime/JS Behaviour
@wailsio/runtime/src/window.ts:538-680controls the frontend side.HandlePlatformFileDroplocates a drop target usingdocument.elementFromPointandclosest([data-wails-dropzone]); if nothing qualifies it returns early, so native drops that miss a registered dropzone never reach Go.- The runtime maintains hover styling by tracking
dragenter/over/leaveondocument.documentElementand togglingwails-dropzone-hover. This is how the example achieves live highlighting. - A legacy helper still exists:
System.HandlePlatformFileDropin@wailsio/runtime/src/system.ts:159-184marshals a different payload and calls method idApplicationFilesDroppedWithContext, but there is no matching handler inmessageprocessor_application.go. That means any codepath that invokes it would receive an HTTP 400/500. - The window runtime bundles
drag.ts, which manages--wails-draggableregions so window dragging/resizing does not swallow pointer events. Developers must ensure dropzones are not also marked draggable.
Example Insights
v3/examples/drag-n-drop/assets/index.htmlannotates folders withdata-wails-dropzoneand demonstrates how attributes flow through to Go (DropZoneDetails.Attributes). It also shows that the frontend expectsdropX/dropYin CSS pixels, which helps when validating coordinate transforms.v3/examples/html-dnd-apiconfirms standard HTML DnD works without the native bridge; it is a useful regression test when tweakingdrag.tsso pointer suppression does not break DOM events.
Potential Bug Hotspots
- Window-level drops ignored -
window.ts:554-569bails if no dropzone is discovered, so the documentedWindowFilesDroppedevent never fires. Users expecting “drop anywhere” support lose file payloads entirely. - Coordinate scaling -
convertWindowToWebviewCoordinates(webview_window_windows.go:1908-1994) computes offsets using physical pixels but never converts to WebView2 DIPs. On mixed-DPI or >100% scaling setups,elementFromPointwill query the wrong DOM position. - Drop target lifecycle -
EnumChildWindowsruns only once during initialisation. WebView2 can spawn newChrome_RenderWidgetHostHWNDinstances on navigation or GPU process resets, leaving them unregistered and breaking drops until the app restarts. - OLE cleanup -
DropTarget.Dropnever callsw32.DragFinishafterDragQueryFile. Windows docs recommend doing so to release HDROP resources; skipping it risks leaks on repeated drops. - Stale runtime API -
System.HandlePlatformFileDropreferences a non-existent backend method (messageprocessor_application.golacks case 100). Any future JS that follows the generated docs will fail at runtime. - Backpressure risk -
windowDragAndDropBuffer(channel size 5) blocks the HTTP handler if event consumers stall. Heavy processing in listeners could cause the runtime call to hang and, on Windows, freeze the drag cursor until the fetch resolves.
Improvement Opportunities
- Emit a fallback
WindowFilesDroppedevent directly fromDropTarget.OnDropwhenHandlePlatformFileDropdeclines the payload, preserving drop-anywhere behaviour. - Introduce DPI-aware coordinate conversion (use
globalApplication.Screen.PhysicalToDipPoint) before invokingelementFromPoint. - Re-run
RegisterDragDropwhenever a new WebView child window appears (CoreWebView2FrameCreated/NewBrowserVersionAvailablecallbacks) and on navigation completions. - Either wire up the
ApplicationFilesDroppedWithContextmethod or remove theSystem.HandlePlatformFileDropexport to avoid misleading integrators. - Add integration tests that exercise drops on high-DPI displays and across multiple monitors using the drag-n-drop example as a harness.
- Consider surfacing drop-target state (e.g., active element id) via diagnostic logging so Windows reports can be correlated without attaching a debugger.
Open Questions
- Do we need to respect WebView2’s native
AllowExternalDrop(true)whenEnableDragAndDropis disabled, or should we expose both behaviours concurrently? - How should conflicting CSS states (
--wails-draggablevsdata-wails-dropzone) be resolved? Current logic leaves it up to the developer, but documenting or enforcing precedence could prevent accidental suppression. - Can we guarantee that
elementFromPointis safe when overlays or transparent windows are involved, or do we need hit-testing improvements usingelementsFromPoint? - Would it be safer to move drop processing entirely to Go (dispatching both window-wide and targeted events) and keep JS solely for hover styling?