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
impl DragCoordinator
Sourcepub fn new() -> DragCoordinator
pub fn new() -> DragCoordinator
A coordinator with no session in flight.
Sourcepub fn new_source_id(&self) -> DragSourceId
pub fn new_source_id(&self) -> DragSourceId
Allocate a source id, unique within this coordinator.
Sourcepub fn new_target_id(&self) -> DragTargetId
pub fn new_target_id(&self) -> DragTargetId
Allocate a target id, unique within this coordinator.
Sourcepub fn phase(&self) -> DragPhase
pub fn phase(&self) -> DragPhase
The current phase — never DragPhase::Cancelled, which is transient.
Sourcepub fn arm(&self, source: DragSourceId, press: Point) -> bool
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.
Sourcepub fn lift(&self, source: DragSourceId) -> bool
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.
Sourcepub fn move_to_next_target(&self)
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).
Sourcepub fn move_to_previous_target(&self)
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.
Sourcepub fn begin<T>(&self, payload: T) -> boolwhere
T: Any,
pub fn begin<T>(&self, payload: T) -> boolwhere
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.
Sourcepub fn begin_external(&self, paths: Vec<PathBuf>, pointer: Point)
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.
Sourcepub fn set_external_payload(&self, paths: Vec<PathBuf>)
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).
Sourcepub fn update_pointer(&self, pointer: Point)
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).
Sourcepub fn resolve_hover(&self)
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.
Sourcepub fn set_ghost_offset(&self, offset: Vec2)
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.
Sourcepub fn set_hovered(&self, target: Option<DragTargetId>)
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.
Sourcepub fn register_target(&self, id: DragTargetId)
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.
Sourcepub fn register_target_for<T>(&self, id: DragTargetId)where
T: Any,
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.
Sourcepub fn unregister_target(&self, id: DragTargetId)
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.
Sourcepub fn set_target_bounds(&self, id: DragTargetId, bounds: Rect)
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.
Sourcepub fn report_target_bounds(
&self,
id: DragTargetId,
bounds: Option<Rect>,
frame: FrameTime,
)
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.
Sourcepub fn target_bounds(&self, id: DragTargetId) -> Option<Rect>
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.
Sourcepub fn target_at(&self, point: Point) -> Option<DragTargetId>
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.
Sourcepub fn is_external(&self) -> bool
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.
Sourcepub fn registered_targets(&self) -> Vec<DragTargetId>
pub fn registered_targets(&self) -> Vec<DragTargetId>
All registered drop targets, in registration order.
Sourcepub fn drop(&self) -> Option<DragTargetId>
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.
Sourcepub fn complete_drop(&self)
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.
Sourcepub fn cancel(&self)
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.
Sourcepub fn end_external(&self) -> bool
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.
Sourcepub fn payload_is<T>(&self) -> boolwhere
T: Any,
pub fn payload_is<T>(&self) -> boolwhere
T: Any,
Whether a payload of type T is held (Dragging/Dropping, not yet
taken).
Sourcepub fn payload_ref<T>(&self) -> Option<Ref<'_, T>>where
T: Any,
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.
Sourcepub fn with_payload<T, R>(&self, f: impl FnOnce(&T) -> R) -> Option<R>where
T: Any,
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.
Sourcepub fn take_payload<T>(&self) -> Option<Box<T>>where
T: Any,
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.
Sourcepub fn subscribe<F>(&self, listener: F) -> DragSubscriptionwhere
F: Fn(DragStateChange) + 'static,
pub fn subscribe<F>(&self, listener: F) -> DragSubscriptionwhere
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
impl Clone for DragCoordinator
Source§fn clone(&self) -> DragCoordinator
fn clone(&self) -> DragCoordinator
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for DragCoordinator
impl Debug for DragCoordinator
Source§impl Default for DragCoordinator
impl Default for DragCoordinator
Source§fn default() -> DragCoordinator
fn default() -> DragCoordinator
Auto Trait Implementations§
impl !RefUnwindSafe for DragCoordinator
impl !Send for DragCoordinator
impl !Sync for DragCoordinator
impl !UnwindSafe for DragCoordinator
impl Freeze for DragCoordinator
impl Unpin for DragCoordinator
impl UnsafeUnpin for DragCoordinator
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
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>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
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)
fn as_any(&self) -> &(dyn Any + 'static)
&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)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.