Skip to main content

Module world

Module world 

Source
Expand description

Multi-window drag worlds: one shared drag state spanning several windows of a desktop app, each window an independent VirtualDom.

Dioxus desktop polls every window’s VirtualDom on the main thread, and signal storage is thread-local rather than runtime-local, so a Signal (and therefore a DndContext) created in one window’s runtime can be read, written and subscribed from another’s - a write in window A re-renders window B through B’s own scheduler. DndWorld builds on exactly that: the payload crosses windows as a live Rust value, with no serialization and none of the platform roulette of native HTML5 drag-and-drop. (DataTransfer interop for drags that leave the app entirely stays in crate::external.)

§Coordinate spaces

Everything zone-shaped stays in client CSS pixels of its own window, exactly as in single-window use. The world adds one more space: global desktop physical pixels, in which windows are located and hit-tested. Each window’s WindowGeometry carries the conversion: the client area’s top-left in physical px (inner_position() on desktop), the window scale factor, and the client-area size in physical px. Conversion happens only at the world boundary.

§Wiring

With the desktop feature, render MultiWindowProvider<T> once in every window. It installs the geometry feed above the drag provider and the host bridge below it, so their required ordering is structural. Create sibling VDOMs with DndWorld::vdom so the world cannot be omitted; chain the app model and legitimate per-window context afterwards:

fn main_window() -> Element {
    let world = use_dnd_world::<Card>();
    let model = use_dnd_model(Model::new);
    let popup_model = model.clone();
    let open = move |_| {
        let dom = world.vdom(popup).with_root_context(popup_model.clone());
        dioxus::desktop::window().new_window(dom, Default::default());
    };
    rsx! {
        MultiWindowProvider::<Card> {
            button { onclick: open, "Open" }
            // zones, overlay, live region
        }
    }
}

fn popup() -> Element {
    let model = use_context::<Model>();
    rsx! { MultiWindowProvider::<Card> { /* ... */ } }
}

MultiWindowProvider warns once if it mounts without a world in context; that window otherwise falls back to isolated drag state.

Custom windowing hosts use the manual path: provide a WindowGeometry above DndProvider<T>, update it from host move/resize/focus events, and render the host bridge inside the provider after it joins. A provider that finds a world joins it instead of creating isolated state (nested providers keep today’s shadowing semantics: only a window’s outermost provider of T joins). Sample placement in global physical pixels and call WindowGeometry::set; call WindowGeometry::mark_focused on focus so overlapping windows resolve to the frontmost. Without geometry the world degrades gracefully: drags behave exactly as single-window drags (this is also the honest Wayland story, where a client can learn neither the cursor’s global position nor its own windows’ positions).

§Lifetimes: close windows in any order

A world’s own state (the shared context and the window table) is process-lived: it is created under an owner this module holds for the life of the app, not under any window’s scope. Whichever window created the world can close first and every other window keeps dragging - cross-window between the survivors, single-window when only one remains. Closing a joined window prunes it from the table and aborts an in-flight drag that originated there (its coordinate anchor is gone). The cost is a deliberate, bounded leak: a handful of signals per world, once per app.

Structs§

DndWorld
A drag world shared by several windows: one DndContext every joined provider re-provides, plus the window table cross-window hit-testing walks. Cheap to copy; seed sibling windows with Self::vdom.
JoinedWindow
A provider’s handle to the world it joined: the world, this window’s key, and this window’s geometry - everything the pointer path needs to think cross-window.
WindowGeometry
One window’s placement on the desktop, as reactive signals the host feeds. Copy handle; create one per window (the provider creates an inert one when none is in context) and keep it updated from your windowing layer. Missing placement or host ineligibility makes it inert: the window still drags internally, but cannot take part in cross-window hit-testing.
WindowKey
Identifies one joined window within a DndWorld. Process-unique.
WindowRecord
One window joined to a DndWorld: its geometry, its zone registry, and the per-window handles drop delivery needs.
ZoneLocation
A drop-zone identity qualified by the joined window that owns it.

Functions§

use_dnd_world
Create a DndWorld<T> (process-lived - see the module docs on lifetimes) and provide it in context, so providers in this window join it. Create sibling windows with DndWorld::vdom. Call it once, in any window.
use_joined_window
The enclosing provider’s world membership, if it joined a world - the handle desktop glue needs to bridge host-side input (see DndWorld::track_global / DndWorld::drop_at_global). Call it anywhere below the DndProvider.