Magnet
Magnetism: typed snap-and-connect between anchor points on scene items.
Public types
| Kind | Name |
|---|---|
struct | MagnetId — Opaque identifier for a Magnet inside a Scene |
enum | MagnetRole — The direction a magnet faces in a connection |
struct | Magnet — A magnetism anchor attached to a scene item |
struct | MagnetRef — An owned, borrow-free snapshot of one magnet, handed to the accept/reject predicate and carried in a MagnetConnection |
enum | MagnetVerdict — The result of running the accept/reject predicate on a candidate magnet pair |
struct | MagnetConnection — A formed connection between two magnets, delivered to the consumer's on_connect handler on release (mouse) or confirm (keyboard) |
struct | MagnetSnap — The chosen snap when a dragged item's magnet aligns onto another item's magnet |
enum | MarkerVisibility — When the SceneView paints magnet markers |
enum | MagnetVisualState — The visual state of a magnet as the feedback renderer sees it |
struct | MagnetMarker — One magnet's render data, handed to the feedback renderer |
struct | MagnetFeedback — Everything the magnetism feedback renderer needs for one frame, in scene coordinates (the canvas is already in the view-transform scope) |
struct | MagnetismConfig — Per-view magnetism configuration, installed via SceneView::magnetism |
Public functions
Magnet
| Returns | Function |
|---|---|
| Constructors | |
Self | new(local_pos: Point) |
| Builder methods | |
Self | role(role: MagnetRole) |
Self | payload<P: 'static>(payload: P) |
Self | payload_rc(payload: Rc<dyn Any>) |
Self | label(label: impl Into<LocalizedString>) |
Self | enabled(on: bool) |
MagnetId
| Returns | Function |
|---|---|
| Methods | |
u64 | as_u64() |
MagnetRef
| Returns | Function |
|---|---|
| Constructors | |
Self | new(id: MagnetId, item: ItemId, role: MagnetRole, payload: Option<Rc<dyn Any>>, scene_pos: Point) |
| Methods | |
Option<&P> | payload_as<P: 'static>() |
MagnetVerdict
| Returns | Function |
|---|---|
| Constructors | |
Self | accept() |
Self | accept_with<P: 'static>(payload: P) |
| Methods | |
bool | is_accept() |
MagnetConnection
| Returns | Function |
|---|---|
| Constructors | |
Self | new(from: MagnetRef, to: MagnetRef, payload: Option<Rc<dyn Any>>) |
| Methods | |
Option<&P> | payload_as<P: 'static>() |
MagnetSnap
| Returns | Function |
|---|---|
| Constructors | |
Self | new(from: MagnetId, to: MagnetId, snap_vector: Vec2, payload: Option<Rc<dyn Any>>, distance: f32) |
MagnetMarker
| Returns | Function |
|---|---|
| Constructors | |
Self | new(id: MagnetId, scene_pos: Point, role: MagnetRole, state: MagnetVisualState) |
MagnetFeedback
| Returns | Function |
|---|---|
| Constructors | |
Self | new(zoom: f32, markers: Vec<MagnetMarker>, connector: Option<(Point, Point, bool)>) |
MagnetismConfig
| Returns | Function |
|---|---|
| Constructors | |
Self | new(predicate: impl Fn(&MagnetRef, &MagnetRef) -> MagnetVerdict + 'static) |
| Builder methods | |
Self | on_connect(f: impl Fn(&MagnetConnection, &mut EventContext) + 'static) |
Self | capture_px(px: f32) |
Self | markers(markers: MarkerVisibility) |
Self | feedback(f: impl Fn(&mut Canvas, &PaintContext, &MagnetFeedback) + 'static) |
Self | connect_key(key: Key) |
Self | enabled(on: impl Into<Prop<bool>>) |
| Methods | |
Signal<bool> | enabled_signal() |
bool | is_enabled() |
Detailed description
A magnet is a local point on an item (relative to the item's
anchor, like a child point), carrying a type-erased payload
('static, downcastable) and a directional MagnetRole. An item
can carry several. During an interaction the scene broad-phases
nearby magnets, runs an accept/reject predicate per
candidate pair, snaps so the closest accepting pair aligns, and on
release a connection event carries the payloads to the consumer.
Mechanism in scene, policy in the consumer
This module and Scene own the mechanism: magnet
geometry, broad-phase, snap math, and the connection result. They do
not own policy — which magnet types are compatible, what a
connection means, or whether a connection persists. Compatibility is
decided entirely by the predicate the consumer supplies to
Scene::compute_item_snap /
Scene::compute_port_snap; the
meaning of a formed connection is decided by the consumer's
on_connect handler. No widget-tree or designer concept (slot,
category, insertion index) leaks into this API; those live in the
payloads and the predicate.
MagnetRole is generic node-graph / diagram vocabulary used by the
scene only for default feedback (which end is the source) and for
ordering the keyboard connect flow. It is advisory: the predicate is
always authoritative on whether two magnets may connect.
Example — two items connected by a typed magnet pair
#![allow(unused)] fn main() { use teksilo_scene::{Scene, RectItem, Magnet, MagnetRole, MagnetRef, MagnetVerdict}; use teksilo_canvas::{Point, Rect, Vec2}; // A predicate that accepts Source → Target pairs on different items. fn source_to_target(a: &MagnetRef, b: &MagnetRef) -> MagnetVerdict { if a.item == b.item { return MagnetVerdict::Reject; } match (a.role, b.role) { (MagnetRole::Source, MagnetRole::Target) | (MagnetRole::Target, MagnetRole::Source) => MagnetVerdict::accept(), _ => MagnetVerdict::Reject, } } let mut scene = Scene::new(); // Dragged item with a Source magnet at its local origin. let dragged = scene.add_item( RectItem::new(Rect::new(0.0, 0.0, 10.0, 10.0)), Point::ZERO, ); scene.add_magnet(dragged, Magnet::new(Point::ZERO).role(MagnetRole::Source)); // Target item 100 px to the right with a Target magnet at its local origin. let target = scene.add_item( RectItem::new(Rect::new(0.0, 0.0, 10.0, 10.0)), Point::new(100.0, 0.0), ); scene.add_magnet(target, Magnet::new(Point::ZERO).role(MagnetRole::Target)); // The dragged item is 5 px away from snapping; capture radius 20 px. if let Some(snap) = scene.compute_item_snap(dragged, Vec2::new(95.0, 0.0), 20.0, &source_to_target) { // snap_vector carries the dragged item exactly onto the target magnet. assert!((snap.snap_vector.x - 5.0).abs() < 1e-3); } }
API reference
📖 Full rustdoc API for this module
pub struct MagnetId
Import: use teksilo_scene::MagnetId; — add teksilo-scene to [dependencies]: teksilo does not re-export it.
Opaque identifier for a Magnet inside a Scene.
Globally unique within a process, minted by MagnetId::next. Stable
across the magnet's lifetime; removing a magnet (or its owning item)
retires its id permanently — ids are never reused.
#![allow(unused)] fn main() { pub struct MagnetId(pub(crate) u64); }
Methods
pub fn as_u64(self) -> u64
Raw numeric value, used by AccessKit's synthetic-NodeId derivation.
pub enum MagnetRole
Import: use teksilo_scene::MagnetRole; — add teksilo-scene to [dependencies]: teksilo does not re-export it.
The direction a magnet faces in a connection.
Advisory only — the scene uses it for default feedback (arrow
direction, which end starts the keyboard flow), but the
accept/reject predicate is always the authority on compatibility. A
node-graph output port is a Source, an input
port is a Target; a snap point that can be
either end is Bidirectional.
#![allow(unused)] fn main() { pub enum MagnetRole { /* variants */ } }
Variants
Source— Originates a connection (e.g. a node-graph output port).Target— Receives a connection (e.g. a node-graph input port).Bidirectional— Can be either end of a connection.
pub struct Magnet
Import: use teksilo_scene::Magnet; — add teksilo-scene to [dependencies]: teksilo does not re-export it.
A magnetism anchor attached to a scene item.
Built fluently and handed to
SceneModel::add_magnet. Carries a
local-frame position, a MagnetRole, an optional type-erased
payload, an enabled flag, and an optional accessibility label.
#![allow(unused)] fn main() { pub struct Magnet { /* fields */ } }
Methods
pub fn new(local_pos: Point) -> Self
A magnet at local_pos in the owning item's local frame, role
Bidirectional, no payload, enabled.
pub fn role(mut self, role: MagnetRole) -> Self (default: MagnetRole::Bidirectional)
Set the connection direction (advisory — see MagnetRole).
pub fn payload<P: 'static>(mut self, payload: P) -> Self
Attach a type-erased payload the predicate and the connection
event can downcast. Cheap to carry around (held in an Rc).
pub fn payload_rc(mut self, payload: Rc<dyn Any>) -> Self (default: None)
Attach an already-Rc-wrapped payload (use when several magnets
share one payload object).
pub fn label(mut self, label: impl Into<LocalizedString>) -> Self
The accessibility name announced for this magnet's synthetic AT node. Defaults to a generic role-based label when unset.
pub fn enabled(mut self, on: bool) -> Self
Disabled magnets are skipped by broad-phase, feedback, the keyboard cycle, and AT emission. Enabled by default.
pub struct MagnetRef
Import: use teksilo_scene::MagnetRef; — add teksilo-scene to [dependencies]: teksilo does not re-export it.
An owned, borrow-free snapshot of one magnet, handed to the
accept/reject predicate and carried in a MagnetConnection.
The payload is an Rc clone, so a snapshot can outlive the borrow
taken to collect candidates. The predicate inspects these snapshots
while a shared (read-only) scene borrow is held — it may read the
model but must not mutate it. The on_connect handler, by contrast,
runs after every borrow is dropped and may freely mutate the model
(add an edge item, reparent, fire an intent).
#[non_exhaustive]: the crate hands this to consumer code — it is what a
predicate inspects — the same reason MagnetVisualState carries it. Build
one with new.
#![allow(unused)] fn main() { pub struct MagnetRef { /* fields */ } }
Fields
id:MagnetId— The magnet's id.item:ItemId— The item the magnet is attached to.role:MagnetRole— The magnet's advisory direction.payload:Option<Rc<dyn Any>>— The magnet's payload, if any (anRcclone of the stored one).scene_pos:Point— The magnet's current position in scene coordinates.
Methods
pub fn new( id: MagnetId, item: ItemId, role: MagnetRole, payload: Option<Rc<dyn Any>>, scene_pos: Point, ) -> Self
A snapshot stated field by field — the constructor
#[non_exhaustive] takes the place of a struct literal for.
The scene builds its own; this is for a consumer testing its predicate.
pub fn payload_as<P: 'static>(&self) -> Option<&P>
Borrow the payload downcast to P, or None if absent or a
different type. The ergonomic way to read a typed payload inside
a predicate.
pub enum MagnetVerdict
Import: use teksilo_scene::MagnetVerdict; — add teksilo-scene to [dependencies]: teksilo does not re-export it.
The result of running the accept/reject predicate on a candidate magnet pair. "Both payloads in, reject or accept-with-payload out."
#![allow(unused)] fn main() { pub enum MagnetVerdict { /* variants */ } }
Variants
Reject— The pair may not connect; the scene skips it.Accept(Option<Rc<dyn Any>>)— The pair may connect. The optional payload is attached to the resultingMagnetConnection(e.g. a derived edge descriptor).
Methods
pub fn accept() -> Self
Accept with no extra connection payload.
pub fn accept_with<P: 'static>(payload: P) -> Self
Accept and attach a typed connection payload.
pub fn is_accept(&self) -> bool
Whether this verdict accepts the pair.
pub struct MagnetConnection
Import: use teksilo_scene::MagnetConnection; — add teksilo-scene to [dependencies]: teksilo does not re-export it.
A formed connection between two magnets, delivered to the consumer's
on_connect handler on release (mouse) or confirm (keyboard).
from is the magnet that initiated the connection (the dragged
item's magnet, the grabbed port, or the keyboard-activated source);
to is the magnet it connected onto. payload is whatever the
predicate's MagnetVerdict::Accept carried.
#[non_exhaustive]: the crate hands this to consumer code — it is the
argument of on_connect. Build one with new.
#![allow(unused)] fn main() { pub struct MagnetConnection { /* fields */ } }
Fields
from:MagnetRef— The initiating magnet.to:MagnetRef— The receiving magnet.payload:Option<Rc<dyn Any>>— The connection payload from the accepting verdict, if any.
Methods
pub fn new(from: MagnetRef, to: MagnetRef, payload: Option<Rc<dyn Any>>) -> Self
A connection stated field by field — the constructor
#[non_exhaustive] takes the place of a struct literal for.
The scene builds its own; this is for a consumer testing on_connect.
pub fn payload_as<P: 'static>(&self) -> Option<&P>
Borrow the connection payload downcast to P.
pub struct MagnetSnap
Import: use teksilo_scene::MagnetSnap; — add teksilo-scene to [dependencies]: teksilo does not re-export it.
The chosen snap when a dragged item's magnet aligns onto another
item's magnet. Returned by
Scene::compute_item_snap.
A heavyweight consumer that drives its own drag uses snap_vector to
place the item so from lands on to, and resolves from / to
via Scene::magnet to build the connection
for its own on_connect.
#[non_exhaustive]: the crate hands this to consumer code — a
heavyweight consumer driving its own drag reads it. Build one with
new.
#![allow(unused)] fn main() { pub struct MagnetSnap { /* fields */ } }
Fields
from:MagnetId— The dragged item's magnet that is snapping.to:MagnetId— The stationary magnet it snaps onto.snap_vector:Vec2— Add this to the drag delta (or the item's position) sofrom's scene position coincides withto's.payload:Option<Rc<dyn Any>>— The accepting verdict's payload, if any.distance:f32— Scene-space distance between the pair before snapping (the tie-break used to pick the closest accepting pair).
Methods
pub fn new( from: MagnetId, to: MagnetId, snap_vector: Vec2, payload: Option<Rc<dyn Any>>, distance: f32, ) -> Self
A snap stated field by field — the constructor
#[non_exhaustive] takes the place of a struct literal for.
The scene computes its own; this is for a consumer's own tests.
pub enum MarkerVisibility
Import: use teksilo_scene::MarkerVisibility; — add teksilo-scene to [dependencies]: teksilo does not re-export it.
When the SceneView paints magnet markers.
#![allow(unused)] fn main() { pub enum MarkerVisibility { /* variants */ } }
Variants
Always— Always draw a marker for every enabled magnet (busy, but the clearest discoverability — good for a dedicated editor).DuringInteraction— Draw markers only while an interaction is in progress (an item drag, a port drag, or keyboard connect mode). The default — keeps an idle scene clean.Never— Never draw markers (the consumer paints its own via the feedback hook, or wants no visual at all).
pub enum MagnetVisualState
Import: use teksilo_scene::MagnetVisualState; — add teksilo-scene to [dependencies]: teksilo does not re-export it.
The visual state of a magnet as the feedback renderer sees it.
#[non_exhaustive]: the crate hands this to consumer code — a custom
MagnetFeedback must match it — and the set grows with every interaction
state magnetism learns. A renderer's wildcard arm then keeps meaning "draw
it plainly" instead of failing to compile.
#![allow(unused)] fn main() { pub enum MagnetVisualState { /* variants */ } }
Variants
Idle— A normal, idle magnet.Candidate— A magnet the current interaction could connect to (it passes the predicate against the active source).Snapped— The magnet the active interaction is currently snapped onto.Focused— The keyboard-focused magnet (connect mode).PendingSource— The keyboard-activated source magnet awaiting a target.
pub struct MagnetMarker
Import: use teksilo_scene::MagnetMarker; — add teksilo-scene to [dependencies]: teksilo does not re-export it.
One magnet's render data, handed to the feedback renderer.
#[non_exhaustive]: the crate hands this to consumer code — it is what a
custom MagnetismConfig::feedback draws, and it is the struct
MagnetVisualState lives inside, which already says so for itself. Build
one with new.
#![allow(unused)] fn main() { pub struct MagnetMarker { /* fields */ } }
Fields
id:MagnetId— The magnet's id.scene_pos:Point— Its current scene position.role:MagnetRole— Its advisory role.state:MagnetVisualState— Its visual state for this frame.
Methods
pub fn new(id: MagnetId, scene_pos: Point, role: MagnetRole, state: MagnetVisualState) -> Self
A marker stated field by field — the constructor
#[non_exhaustive] takes the place of a struct literal for.
pub struct MagnetFeedback
Import: use teksilo_scene::MagnetFeedback; — add teksilo-scene to [dependencies]: teksilo does not re-export it.
Everything the magnetism feedback renderer needs for one frame, in
scene coordinates (the canvas is already in the view-transform
scope). The built-in renderer draws markers plus a connector; a
custom MagnetismConfig::feedback closure receives the same data.
#[non_exhaustive]: the crate hands this to consumer code — it is the
argument of a custom feedback renderer, and the set grows with every cue
magnetism learns to draw. Build one with new.
#![allow(unused)] fn main() { pub struct MagnetFeedback { /* fields */ } }
Fields
zoom:f32— The view's current geometric zoom, so the renderer can size constant-pixel chrome aspixels / zoomin scene units.markers:Vec<MagnetMarker>— Eligible magnets to mark, with their per-frame state.connector:Option<(Point, Point, bool)>— A connector to draw between two scene points, if an interaction is forming one: the active item-drag snap pair, the port-drag wire (source to snapped target or cursor), or the keyboard preview (pending source to focused candidate).truein the second field marks an accepted connector (drawn solid / highlighted) versus a tentative one (the free port-drag wire).
Methods
pub fn new( zoom: f32, markers: Vec<MagnetMarker>, connector: Option<(Point, Point, bool)>, ) -> Self
A frame of feedback stated field by field — the constructor
#[non_exhaustive] takes the place of a struct literal for.
pub struct MagnetismConfig
Import: use teksilo_scene::MagnetismConfig; — add teksilo-scene to [dependencies]: teksilo does not re-export it.
Per-view magnetism configuration, installed via
SceneView::magnetism.
Holds the consumer's policy — the accept/reject predicate and the
on_connect handler — plus presentation knobs. The scene supplies
the mechanism (snap math, broad-phase, feedback rendering, the
connection event); this config is where the consumer plugs its
policy in.
#![allow(unused)] fn main() { pub struct MagnetismConfig { /* fields */ } }
Methods
pub fn new(predicate: impl Fn(&MagnetRef, &MagnetRef) -> MagnetVerdict + 'static) -> Self
A config with the given accept/reject predicate and defaults:
14 px capture radius, markers during interaction, the built-in
feedback renderer, m to toggle keyboard connect mode, enabled.
Install an on_connect handler to actually do something on
connect.
pub fn on_connect( mut self, f: impl Fn(&MagnetConnection, &mut EventContext) + 'static, ) -> Self
The handler invoked when a connection is formed (mouse release or
keyboard confirm). Runs with a live EventContext and no scene
borrow held, so it may mutate the model (add an edge item,
reparent), call scene.add_a11y_relation, or fire an intent.
pub fn capture_px(mut self, px: f32) -> Self
Capture and grab radius in screen pixels (converted to scene units by dividing by the live zoom, so snapping feels consistent at any zoom). Default 14.
pub fn markers(mut self, markers: MarkerVisibility) -> Self
When magnet markers are painted. Default
MarkerVisibility::DuringInteraction.
pub fn feedback( mut self, f: impl Fn(&mut Canvas, &PaintContext, &MagnetFeedback) + 'static, ) -> Self
Replace the built-in feedback renderer with a custom one. The closure paints in scene coordinates (the canvas already has the view transform pushed).
pub fn connect_key(mut self, key: Key) -> Self
The key that toggles keyboard connect mode while the SceneView is
focused. Default m.
pub fn enabled(mut self, on: impl Into<Prop<bool>>) -> Self
Set the enabled state, statically or reactively (an app-owned signal drives enabled/disabled from e.g. a toolbar toggle).
pub fn enabled_signal(&self) -> Signal<bool>
The reactive enabled signal, for a toolbar to read or bind.
pub fn is_enabled(&self) -> bool
Whether magnetism is currently enabled.