Skip to main content

DragCoordinator

Struct DragCoordinator 

Source
pub struct DragCoordinator { /* private fields */ }
Expand description

A cloneable handle onto one drag scope’s session state: sources arm/begin a drag and report its pointer with update_pointer, which resolves the hovered target against the registry (set_hovered overrides it), and it ends in drop + complete_drop or cancel. Every clone shares one state. See the module docs for the transition rules.

use frust_widgets::drag::{DragCoordinator, DragPhase};
use kurbo::{Point, Rect};

let drag = DragCoordinator::new();
let source = drag.new_source_id();
let target = drag.new_target_id();
drag.register_target(target);
drag.set_target_bounds(target, Rect::new(50.0, 0.0, 150.0, 100.0));

drag.arm(source, Point::new(10.0, 10.0));
drag.begin(42_u32);
// Resolution: the pointer is inside the target's reported bounds.
drag.update_pointer(Point::new(80.0, 40.0));
assert_eq!(drag.state().session().and_then(|s| s.hovered), Some(target));
assert_eq!(drag.drop(), Some(target));

// The target claims the payload, then completes the drop.
assert_eq!(drag.take_payload::<u32>().as_deref(), Some(&42));
drag.complete_drop();
assert_eq!(drag.phase(), DragPhase::Idle);

Implementations§

Source§

impl DragCoordinator

Source

pub fn new() -> DragCoordinator

A coordinator with no session in flight.

Source

pub fn new_source_id(&self) -> DragSourceId

Allocate a source id, unique within this coordinator.

Source

pub fn new_target_id(&self) -> DragTargetId

Allocate a target id, unique within this coordinator.

Source

pub fn state(&self) -> DragState

A snapshot of the current state (payload excluded).

Source

pub fn phase(&self) -> DragPhase

The current phase — never DragPhase::Cancelled, which is transient.

Source

pub fn arm(&self, source: DragSourceId, press: Point) -> bool

source was pressed at press: Idle → Armed.

Re-arming while Armed, or while an unclaimed drop is still Dropping, cancels that first (→ Cancelled → Idle, the drop target hearing Leave), then arms. Arming while Dragging is ignored with a debug-build log — the session in flight owns the pointer. Returns whether the coordinator is now armed by this call.

Source

pub fn lift(&self, source: DragSourceId) -> bool

source is lifted by keyboard, with no pointer: Idle → Armed, marked as a keyboard session so the begin that follows carries DragSession::keyboard into Dragging — see the module docs. Same re-arm/ignore rules as arm: re-arming while Armed (or while an unclaimed drop is still Dropping) cancels that first; lifting while Dragging is ignored (debug-build log). Returns whether the coordinator is now armed by this call.

Source

pub fn move_to_next_target(&self)

Move the hovered target to the next one registered after it (registration/tree order, wrapping to the first) — the keyboard ArrowRight/ArrowDown drive for a lifted session. Only targets pointer resolution would consider take part: those accepting the session’s payload type and visible in the latest paint pass. Starts at the first of them when nothing is hovered yet. Raises the same Leave/Enter pair pointer resolution does. A no-op outside Dragging or with no such targets — see the module docs.

Reaches only a target visible in the latest paint pass, and nothing scrolls an off-screen one into view on the keyboard’s behalf — a list taller than its viewport cycles only across its currently visible rows (drag-keyboard-cycle-visible-targets-only, docs/LIMITATIONS.md).

Source

pub fn move_to_previous_target(&self)

The mirror of move_to_next_target — the keyboard ArrowLeft/ArrowUp drive, wrapping to the last eligible target. Carries the same visible-targets-only reach.

Source

pub fn begin<T>(&self, payload: T) -> bool
where T: Any,

The armed source starts dragging payload: Armed → Dragging, with the pointer at the press point and nothing hovered.

begin without an arm is an error and is ignored (debug-build log), as is begin while Dropping. A second begin while Dragging cancels the first session (a hovered target hearing Leave) and restarts from the same source with this payload — re-armed at the first session’s press point, then dragging from its current pointer and ghost offset, so subscribers hear the full Dragging → Cancelled → Idle → Armed → Dragging walk. An external session has no source to restart from: it is cancelled and this begin ignored. Returns whether a session is now dragging this payload.

Source

pub fn begin_external(&self, paths: Vec<PathBuf>, pointer: Point)

An OS file drag entered at pointer: Idle → Dragging with DragKind::ExternalFiles, no source, and paths as the payload (a Vec<PathBuf>, so payload_is::<Vec<PathBuf>>() holds). Any session already in flight — armed, dragging or dropping — is cancelled first.

Source

pub fn set_external_payload(&self, paths: Vec<PathBuf>)

Replace the held Vec<PathBuf> payload of an in-flight DragKind::ExternalFiles session with paths.

A hover-phase file-drag notification carries no paths of its own (the platform source delivers one file per hover with no “this is everything” signal), so a caller that opens the session on hover via begin_external starts it with an empty list and calls this once the real list is known — typically right before drop, when the platform’s drop notification finally carries every path. Notifies nothing: this replaces payload data, not phase or hover state, so no subscriber needs telling, and the target that eventually take_payloads it sees exactly paths. A silent no-op unless Dragging with DragKind::ExternalFiles — in particular, never while Dropping (too late: the target may have already taken the old payload).

Source

pub fn update_pointer(&self, pointer: Point)

The dragged pointer is at pointer (window space): record it, then resolve the hovered target under it — the latest-registered target whose reported bounds contain it (target_at), or none. A hover change is reported as Leave (the old target) then Enter (the new one), and a changed pointer then as a DragStateChange::Move, so a newly entered target’s first Move is already inside it. Called with an unchanged pointer it still re-resolves, which is how bounds that moved under a stationary pointer are picked up (see also resolve_hover). A silent no-op unless Dragging.

Resolution reads the registry only: it never hit-tests the widget tree (the ghost floats in a transparent overlay pod above it), and a hover set explicitly with set_hovered stands only until the next call.

A silent no-op for a keyboard session (DragSession::keyboard) — nothing drives a real pointer during one, and a stray call must not fight the hover move_to_next_target/ move_to_previous_target last set (see the module docs).

Source

pub fn resolve_hover(&self)

Re-resolve the hovered target under the current pointer against the registry’s current bounds, without moving the pointer — for a pass that moved targets under a stationary pointer (an auto-scrolled list re-reporting its rows’ bounds). Raises the same Leave/Enter pair update_pointer does, and nothing when the hover is unchanged. A silent no-op unless Dragging.

Unlike update_pointer, this one tolerates a target stamped with the pass immediately before the current one: super::auto_scroll’s zone calls it right after its own child paints, which can be before a later sibling sharing this coordinator gets its own turn to repaint in the same frame (a later column, a Stack layer above, an overlay). Without that one-pass tolerance, the sibling’s still-current bounds would read as culled for the moment between the two, flipping the hover away and back for no visible reason. A target silent for a whole pass on top of that one is excluded exactly as update_pointer excludes it — the tolerance never masks a target that has genuinely stopped painting. update_pointer/target_at and keyboard cycling stay strict to the latest pass; a pointer or keyboard event is never dispatched mid-paint, so they never race a sibling’s own turn.

Source

pub fn set_ghost_offset(&self, offset: Vec2)

Set where the ghost’s origin sits relative to the pointer (see DragSession::ghost_origin). Notifies nothing — a source sets it right after begin, before the ghost first paints. A silent no-op unless Dragging.

Source

pub fn set_hovered(&self, target: Option<DragTargetId>)

Override the hovered target with target, bypassing resolution: the old one (if any) hears Leave, then the new one (if any) hears Enter. Nothing when unchanged; a silent no-op unless Dragging. The override stands until the next update_pointer or resolve_hover re-resolves against the registry.

Source

pub fn register_target(&self, id: DragTargetId)

Register an untyped drop target for bounds tracking and resolution: it accepts a session of any payload type. A target that accepts one payload type registers with register_target_for instead, so resolution and keyboard cycling skip it for every other type.

The target is stamped after every target already registered — a later registration wins an overlap in target_at. Idempotent: re-registering an already-registered target is a no-op and keeps its original stamp and accepted type.

Source

pub fn register_target_for<T>(&self, id: DragTargetId)
where T: Any,

Register a drop target that accepts payloads of type T only: target_at, pointer resolution and move_to_next_target/ move_to_previous_target consider it only while the session in flight carries a T (an DragKind::ExternalFiles session carries a Vec<PathBuf>). Otherwise exactly register_target, same stamping and idempotence.

Source

pub fn unregister_target(&self, id: DragTargetId)

Unregister a drop target. A hovered target is un-hovered and hears Leave; the pending target of an unclaimed drop ends the session instead — exactly a cancel: one Leave, then Dropping → Cancelled → Idle, the payload discarded — since no one is left to claim it. A silent no-op for an unregistered target.

Source

pub fn set_target_bounds(&self, id: DragTargetId, bounds: Rect)

Record the window-space bounds of a registered target, unclocked — see report_target_bounds, which a painted target uses instead. The report is stamped into the current paint pass, opening a new one when this target already reported in the current pass. Updates persist across drag sessions. Ignored (with a debug-build log) if the target is not registered.

Source

pub fn report_target_bounds( &self, id: DragTargetId, bounds: Option<Rect>, frame: FrameTime, )

Record what a registered target painted at frame frame: its visible window-space bounds, already clipped to its ancestors’ visible region — None when clipped to nothing, which leaves it resolvable nowhere. Every report is stamped with a paint pass: a report whose frame differs from the one that opened the current pass opens a new pass, and only targets that reported in the latest pass resolve or cycle, so a target that stopped painting (culled, or scrolled out of a culling container) cannot resolve against stale bounds. A FrameTime::ZERO frame (no shell clock) falls back to set_target_bounds’s unclocked stamping. See the module docs for the pass contract and its limit. Ignored (with a debug-build log) if the target is not registered.

Source

pub fn target_bounds(&self, id: DragTargetId) -> Option<Rect>

Query the latest reported window-space bounds of a registered target, or None if the target is not registered, has not reported yet, or last reported itself clipped to nothing. Reads the record as-is: it does not check that the report belongs to the latest paint pass.

Source

pub fn target_at(&self, point: Point) -> Option<DragTargetId>

The registered target whose reported bounds contain point (window space); where several overlap, the one registered last wins — see the module docs for why registration order stands in for paint order. Only targets that reported visible bounds in the latest paint pass are considered, and — while a session is in flight — only those accepting its payload type (register_target_for). Returns None if no such target contains the point.

Source

pub fn is_external(&self) -> bool

Whether the session in flight (Dragging/Dropping) is an OS file drag (DragKind::ExternalFiles). false with no session in flight.

Source

pub fn registered_targets(&self) -> Vec<DragTargetId>

All registered drop targets, in registration order.

Source

pub fn drop(&self) -> Option<DragTargetId>

Release the drag: over a hovered target, Dragging → Dropping (the target hears no Leave — it is the drop target) and the target is returned; with nothing hovered, or from Armed, exactly cancel and None. A no-op (None) from Idle or an already-Dropping session.

Source

pub fn complete_drop(&self)

The drop target finished: Dropping → Idle, discarding the payload if it was never taken. A silent no-op unless Dropping.

Source

pub fn cancel(&self)

Abandon whatever is in flight: from any non-Idle phase, a hovered target (or an unclaimed drop’s target) hears Leave, then → Cancelled → Idle; the payload is discarded. A no-op from Idle.

Source

pub fn end_external(&self) -> bool

The OS file drag is over: end a DragKind::ExternalFiles session still Dragging, exactly as cancel would (a hovered target hears Leave, then → Cancelled → Idle), and report whether one was ended.

Idempotent and narrow, so every drop target can call it on the same end-of-drag broadcast and exactly one call does anything: a no-op (false) from Idle, from Armed, for any internal session (pointer or keyboard — an OS drag ending says nothing about it), and for an external session already Dropping — that one belongs to the target it was released over, which claims or cancels it on its own poll.

Source

pub fn payload_is<T>(&self) -> bool
where T: Any,

Whether a payload of type T is held (Dragging/Dropping, not yet taken).

Source

pub fn payload_ref<T>(&self) -> Option<Ref<'_, T>>
where T: Any,

Borrow the held payload as a T, or None (no session, payload taken, or a different type).

The returned guard borrows the coordinator’s shared state: calling any method that mutates the coordinator (on this or any clone) while it is alive panics on the borrow. Prefer with_payload when the read can be scoped to a closure.

Source

pub fn with_payload<T, R>(&self, f: impl FnOnce(&T) -> R) -> Option<R>
where T: Any,

Run f over the held payload as a T, returning its result, or None (no session, payload taken, or a different type). The shared state stays borrowed only for the duration of f, which must not mutate the coordinator.

Source

pub fn take_payload<T>(&self) -> Option<Box<T>>
where T: Any,

Move the held payload out as a T — only while Dragging or Dropping, and only when it is a T; otherwise None and the payload stays put. A second take returns None.

Source

pub fn subscribe<F>(&self, listener: F) -> DragSubscription
where F: Fn(DragStateChange) + 'static,

Hear every DragStateChange, in order, until the returned guard is dropped. Listeners run in subscription order with the coordinator’s state released, so a listener may read or drive the coordinator; see the module docs for the ordering guarantee under re-entrancy.

Trait Implementations§

Source§

impl Clone for DragCoordinator

Source§

fn clone(&self) -> DragCoordinator

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for DragCoordinator

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for DragCoordinator

Source§

fn default() -> DragCoordinator

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> NoneValue for T
where T: Default,

Source§

type NoneType = T

Source§

fn null_value() -> T

The none-equivalent value.
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T, S> SimdFrom<T, S> for T
where S: Simd,

Source§

fn simd_from(_simd: S, value: T) -> T

Source§

impl<F, T, S> SimdInto<T, S> for F
where T: SimdFrom<F, S>, S: Simd,

Source§

fn simd_into(self, simd: S) -> T

Source§

impl<T> StorageAccess<T> for T

Source§

fn as_borrowed(&self) -> &T

Borrows the value.
Source§

fn into_taken(self) -> T

Takes the value.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more