Skip to content

dioxus-dnd 3.0.0 — multi-window desktop drag and drop

Choose a tag to compare

@kindintelligence kindintelligence released this 11 Jul 04:19
· 8 commits to main since this release
9a47a91

dioxus-dnd 3.0.0

Version 3.0 is the multi-window desktop release. It adds process-wide drag worlds for sibling Dioxus windows, promotes the verified desktop bridge into supported library API, hardens growing event types for future releases, and ships a complete concept-guide and API-reference documentation set.

The crate targets Dioxus 0.7 and requires Rust 1.85 or newer.

Multi-window desktop drag and drop

Enable the host integration explicitly:

dioxus-dnd = { version = "3.0.0", features = ["desktop"] }

Multi-window drags carry the original Rust payload between windows of one application. They do not serialize the payload and do not use HTML5 DataTransfer.

The model has two layers:

  • DndWorld<T> is always available in the dependency-free core. use_dnd_world::<T>() creates a process-lived world containing the shared DndContext<T> and joined-window table.
  • The desktop feature adds use_window_geometry_feed() and DragBridge<T>, backed by dioxus-desktop, Tao, a time-only Tokio runtime, tracing, and x11rb on Linux.

Create one world and pass it to each sibling VirtualDom with VirtualDom::with_root_context. In every window, call use_window_geometry_feed() above DndProvider<T> and render one DragBridge<T> inside the provider. Providers discover and join the world automatically; nested providers keep their normal shadowing behavior.

What is coordinated across windows

  • Window-qualified zone registration and hit testing.
  • Live Rust payload delivery into a zone owned by another VirtualDom.
  • Global-physical-pixel to per-window CSS-pixel conversion, including scale factors.
  • Exactly one presenting DragOverlay per frame.
  • Drop-settle animation in the receiving window, including when the origin window closes during settlement.
  • Modifier-aware Move, Copy, and Link effects at host-side release.
  • Window visibility, minimization, resize, scale-factor changes, focus order, close, and reopen.
  • Safe close ordering: the window that created the world may close first; surviving windows keep dragging.

The host-drive surface is public for non-Tao integrations: begin_from, track_global, drop_at_global, cancel_drag, update_modifiers, window lookup, geometry lookup, and active drag metadata.

Why DragBridge exists

Webviews do not provide one portable event shape outside their viewport. The bridge supplies the missing host-side observations while binding every poller and callback to the originating world/session generation. A delayed callback from drag N cannot drive drag N+1, duplicate reports are idempotent, and an event arriving after completion cannot resurrect a drag.

Bridging is pointer-aware:

  • Mouse and pen need host bridging when webview pointer capture is unavailable.
  • Touch is implicitly captured by the browser and is never bridged. This avoids double-driving a gesture with Windows' touch-synthesized mouse stream.
  • Keyboard drags stay on the normal keyboard path and cannot be hijacked by host pointer input.

Platform support and verified boundaries

Windows 11 / WebView2: verified. WebView2 consumes the window mouse messages Tao would otherwise see, so the Windows leg uses WM_INPUT device events for raw movement, button release, and modifier keys. It enables Tao's DeviceEventFilter::Never once per process. The bridge includes a warning tripwire if a future Tao/WebView2 update begins delivering the supposedly absent window mouse events, while leaving raw input as sole owner to prevent double delivery.

The Windows ARM64 runtime matrix covered cross-window mouse and injected-touch drags, generation-bound raw tracking, desktop-dead-space cancellation and immediate restart, live target resizing, minimized-window exclusion, hovered/origin window closure, close/reopen churn, touch/mouse interleaving, and clean process exit. The tested machine was single-monitor, so mixed-DPI behavior is covered by coordinate tests but has not yet been exercised on a physical mixed-DPI rig.

Linux X11: verified under WSLg forced to X11. The origin polls the global cursor, foreign-window events reconcile release, and an x11rb root-pointer query detects button release over desktop dead space. A transient query miss skips one sample instead of killing the bridge.

Linux Wayland: explicit graceful degradation. Tao's live event-loop backend decision is authoritative. Wayland exposes neither global window placement nor a global cursor, so global geometry and every cross-window bridge leg remain disabled. Local, per-window drag and drop continues normally.

macOS: implemented but not runtime-verified. The current policy uses the portable cursor-poller and release legs. Treat macOS multi-window support as provisional until it receives an AppKit/WKWebView runtime pass.

See the multi-window guide, API reference, platform evidence, and working two-window example.

Operational controls

Cross-window bridging can be disabled without removing local drag behavior:

  • Call DndWorld::set_bridging(false) and inspect it with bridging_enabled().
  • Set DIOXUS_DND_NO_BRIDGE=1 before process launch to start with bridging disabled.
  • cancel_drag() remains available while bridging is disabled as an emergency escape hatch.

With tracing at debug, bridge engagement identifies the active leg as cursor-poller, release, x11-deadspace, or raw-input.

Breaking changes and migration

Growing public event types are now #[non_exhaustive] so adding context in future releases does not repeatedly break downstream code.

Add wildcard arms when matching:

  • GestureEvent
  • FileRejection
  • DragMode
  • PointerKind

Use .. when destructuring:

  • CanvasDrop
  • SortEvent
  • MoveEvent
  • TreeDropEvent

Replace literal construction with:

  • SortEvent::new(from, to)
  • MoveEvent::new(item, from, to)
  • TreeDropEvent::new(payload, target, intent)

DropEffect, DropIntent, and DropOutcome remain exhaustive by design.

GestureEvent::Hold is also part of the 3.0 input vocabulary. Custom state-machine drivers must handle it or use a wildcard arm.

Other major additions

  • Typed JSON transport: TypedDragSource<T> and TypedDropZone<T> under the serde feature, with a text/plain fallback for non-typed consumers.
  • PointerKind is recorded for every pointer drag and exposes implicitly_captured() for safe host-bridge policy.
  • Runtime bridge diagnostics and a platform-model warning tripwire.
  • Scheduled compatibility canaries for the latest published Dioxus 0.7 patch and Dioxus git main cross-VirtualDom contracts.
  • A complete docs/ tree: 22 concept guides and 19 API references. API Markdown is included directly as module Rustdoc, packaged with the crate, and built on docs.rs with all features.
  • CI Markdown linting, semver reporting, and a guard preventing Tao/Wry implementation types from leaking into the public desktop API.

Verification

The release commit passed both local and GitHub gates:

  • Core plus serde tests: 103 unit tests, 28 multi-window tests, 2 cross-VirtualDom seam tests, 60 runtime tests, and 5 typed-transport tests.
  • Desktop-feature tests: 115 unit tests plus the same integration suites.
  • Strict Clippy across all targets with serde desktop and warnings denied.
  • Rustdoc with all features and warnings denied.
  • Markdown linting through pinned rumdl 0.2.30.
  • cargo-semver-checks against 2.4.0.
  • Packaged-crate build verification.

The complete change inventory is in the 3.0.0 changelog.