Skip to main content

Module drop_target

Module drop_target 

Source
Expand description

DropTarget — a transparent wrapping drop container.

Where DropZone is a standalone “drop files here” placeholder with its own label / icon / Browse button, DropTarget is a wrapping container: it turns any existing widget subtree into a drop target without replacing its visual identity. The wrapped child fills the bounds and is always visible; the widget adds a reactive highlight border + tint while a drag hovers and, if a hint slot is set, fades in a centered popup card (“Drop your image here”).

It reacts to both internal drags (typed [DragPayload]) and external (OS) drops (files / text / URIs), through the framework’s normal drag pipeline (on_drag_hover / on_drag_leave / on_drop).

// Wrap a panel; accept image files; show a hint while hovering.
DropTarget::new()
    .child(my_panel)
    .hint(TextWidget::new(lit!("Drop your image here")))
    .accept_external_extensions(["png", "jpg", "jpeg"])
    .on_drop(|payload, _pos, _ctx| { import(payload.files()); true });

// Typed internal drag — recovers the value even after an OS round-trip
// or across windows (the framework's typed re-entry).
DropTarget::new()
    .child(project_card)
    .on_drop_typed::<ProjectRef>(|project, _pos, ctx| {
        ctx.send_intent(AppIntent::Link(project));
        true
    });

§Multi-zone drops

Beyond the single whole-bounds target, a DropTarget can expose up to five independently enable-able DropRegions — Center / Top / Bottom / Leading / Trailing — each with its own optional hint, and route the drop by which zone the pointer released over. This is the VS Code-style “drop on the centre to add, drop on an edge to split” affordance (DockingLayout computes the same five zones by hand). Declare regions with DropTarget::region; the side zones share one DropTarget::zone_size_factor (0.1..=1.0, the fraction of the axis each edge strip occupies — 0.2 is the default fifth, 0.5 bisects) so you size them to the context. Route with DropTarget::on_region_drop (or observe DropTarget::active_region_signal).

DropTarget::new()
    .child(editor_pane)
    .zone_size_factor(0.25)
    .region(DropRegion::Center,   |z| z.hint(TextWidget::new(lit!("Add as tab"))))
    .region(DropRegion::Leading,  |z| z.hint(TextWidget::new(lit!("Split left"))))
    .region(DropRegion::Trailing, |z| z.hint(TextWidget::new(lit!("Split right"))))
    .on_region_drop(|region, payload, _pos, ctx| { route(region, payload); true });

Declaring any region switches the target to exactly the declared regions; declaring none keeps the Center-only whole-bounds default (.hint(w) is sugar for .region(DropRegion::Center, |z| z.hint(w))). Leading / Trailing map to left / right — the framework surfaces no writing direction on the layout context yet, so RTL mirroring is a follow-up.

Each zone can be reactively enabled with z.enabled(signal) (default true): a bound Signal<bool> disables the zone live — no rebuild — and its strip then falls through to the next-priority enabled zone (or Center, or rejects). A drop landing in a middle covered by no enabled zone is rejected; on_region_drop therefore only ever receives an enabled region.

§Styling

The per-zone highlight overlay + hint chrome is a Tier-3 DropTargetStyle; the default RecipeDropTargetStyle paints the active zone (centre → frame only, so the wrapped content shows through; an edge strip → translucent fill + accent frame) and a full-bounds error border on reject. Override per-call with DropTarget::style or theme-wide via theme.style_slots.drop_target.

§Accessibility

The wrapper is a Role::Group. Live is intentionally not set on the group (that would announce every change to the wrapped child); instead the recipe scopes Live::Polite to each hint card so a screen reader announces the active zone’s hint appearing. Each hint is gated by visible_when, so a non-active zone’s hint leaves the AT tree entirely.

§Keyboard accessibility is the caller’s responsibility

An OS drag cannot be initiated from the keyboard, and — unlike DropZone, which ships a keyboard-operable Browse… button as its WCAG 2.1.1 equivalent — DropTarget adds no keyboard affordance of its own. That is by design: DropTarget wraps existing content that is expected to already offer a keyboard path to the same outcome (e.g. a card you can drop a project onto or open with a context-menu “Link…” command). The drop is an enhancement, not the sole path.

If you use DropTarget for an action that has no other affordance, you must add a keyboard equivalent yourself (a button, menu item, or shortcut) — otherwise the action is unreachable for keyboard-only users, and entirely unavailable on platforms with no external-DnD backend (e.g. X11, where OS drag-and-drop is a no-op). DropZone is the better choice when the drop is the primary action.

Structs§

DropRegionSpec
Per-region configuration for a multi-zone DropTarget: an optional hint plus a reactive enabled flag. Kept as a struct so more per-zone knobs can land without a signature churn.
DropTarget
A transparent container that turns its child into a drop target. See the module docs.