Skip to main content

SceneModel

Struct SceneModel 

Source
pub struct SceneModel(/* private fields */);
Expand description

A shared, cloneable handle to a Scene.

Implementations§

Source§

impl SceneModel

Source

pub fn write_guard(&self) -> SceneWriteGuard<'_>

Open a write scope over the scene; see SceneWriteGuard.

The one door for a block of edits that must fan out together, and the door a &mut Scene escape hatch should be built on.

Source

pub fn flush_changes(&self)

Deliver every queued notification.

Called automatically when a write scope closes, so apps rarely need it. The exception is recovering from a caught panic: an unwinding SceneWriteGuard deliberately skips its fan-out, and a panicking observer stops the drain where it stood, so a catch_unwind boundary that intends to keep using the scene calls this once.

§When it does nothing

Where it is called from never makes it panic — this is the door a recovery path is told to knock on, so it cannot be one that only works from some of them. (An observer can still panic once the drain reaches it, and so can the runaway budgets below; both are the observer’s doing, not the call site’s.) It is a no-op, and says so rather than delivering half a batch, when:

  • nothing is queued (overwhelmingly the common case — every mutator that early-returned on an unchanged value, and every per-frame refresh_dynamic_bounds);
  • any borrow on the scene is outstanding. A write scope is the obvious one — it owns its batch and fans out when it closes, so flushing from inside it would deliver a half-finished mutation, and the old implementation’s borrow() simply panicked there. But a shared borrow disqualifies a flush just as firmly, because an observer is allowed to write and a write needs the cell exclusively: draining under a read-only policy closure (SceneView::focus_order, MagnetismConfig’s predicate) would panic the first observer that took the invitation. So the probe is try_borrow_mut, the only question whose answer is “the cell is completely free”;
  • a drain is already running, i.e. this call came from inside an observer. The outermost drain owns the queue and picks that observer’s changes up on its next round, which is what keeps delivery in emission order rather than depth-first.
§Ordering

FIFO. Observers see changes in the order they were emitted, including across a batch — so remove’s documented leaves-then-root order, and the interleaving of the item and logical-AT channels, both survive.

§Termination

The drain keeps going until the queue is empty, so an observer’s own writes are always delivered rather than silently dropped. Most cycles terminate on their own — Scene::set_local_pos and friends early-return when the value is unchanged, so a clamp-it-back-into-bounds observer settles in two rounds. (Snapping a gesture is not this channel’s job at all; see set_geometry_constraint.)

An observer that writes on every change with no equality guard never settles, and is stopped by a budget that panics with a diagnostic naming the item at fault.

What the budget counts is total observer-generated deliveries in one drain, and nothing else. The caller’s own batch — everything queued before the first observer ran — is free at any size, because it is finite by construction; so are cascade depth and the number of distinct subjects a cascade touches. A cap on rounds would cap how long a legitimate chain may be (a settling 300-link chain is 300 rounds); a cap per subject would cap how wide a graph may be, and scaling that cap with the scene makes the work a runaway gets to do scale with the scene too. A flat total is the one bound under which a runaway costs the same number of deliveries and the same peak memory whatever the model’s size, and the shapes this tier runs clear it by more than an order of magnitude. See CascadeBudget for the measurements.

Per-subject counts are still kept — they are what lets the panic name the culprit, which a round counter never could — but they are diagnostics, not a second trip condition.

The budget is not debug-only, and this is the one place the mechanism deliberately does not copy Signal::try_set: try_set recurses, so an unchecked feedback loop there exhausts the stack and aborts loudly by itself. This drain is an iterative loop, so an unchecked loop here is a frozen UI thread with no diagnostic and no core dump — the one failure mode a release build must not have. Trading a hang for a panic is not a close call.

The queue is left intact when a budget trips (dropping it would be the silent notification loss the whole mechanism exists to prevent), so a later flush with the same observer still installed panics again. That is the intended reading: the observer, not the flush, is what has to change.

§Unwind

An observer that panics costs exactly its own delivery. Each notification leaves the queue immediately before it is handed to the signal and is never parked in a local batch, so everything not yet delivered is still queued, in order, when the unwind passes through — and the next call here delivers it. (A local batch would have its untouched tail dropped by the unwind with nothing able to redeliver it: the scene would have advanced with no notification, permanently.)

§Coalescing

None. Repeated LocalPosChanged for one item are delivered once each. Two reasons: the advertised uses of this channel — persistence, validation, audit, mirroring to a data layer — are the ones that need every event; and collapsing two changes would require merging one’s old with the other’s new, a transaction semantic this crate does not define. A drag is not a reason to reconsider: each pointer sample is its own SceneModel call, hence its own one-element batch, exactly as before. If a transaction layer ever wants coalescing, SceneWriteGuard is already its boundary.

§Lifetime

Queued notifications live in the Scene. Dropping the last SceneModel handle drops the scene and discards anything still queued — which can only be a batch abandoned by an unwind, and by then there is no scene left to observe.

Source

pub fn deliver_records(&self)

Hand every committed transaction to the edit sink, with the scene unborrowed.

Called automatically wherever a transaction closes — the end of a write scope, the drop of a SceneTransaction. Apps rarely need it; the exception is the same as flush_changes’, a catch_unwind recovery path.

A no-op when nothing is queued, when the scene is borrowed at all (the sink is allowed to write, and a write needs the cell exclusively), when a delivery is already running, and while the change fan-out is mid-drain.

That last one is the ordering this seam promises: an observer that writes the scene commits its own transaction from inside the drain, and delivering from there would hand the edit sink a transaction some views had not reconciled from yet. The records wait, and the drain’s own caller delivers them when it settles.

Source

pub fn transaction( &self, source: ChangeSource, history: HistoryMode, ) -> SceneTransaction<'_>

Group every edit until the returned guard drops into one transaction, stamped with a ChangeSource and a HistoryMode.

Each SceneChange emitted inside it carries the same TxnId and the same stamp, and — when an edit sink is installed — the whole group arrives as one SceneTransactionRecord once the guard drops.

§Nesting joins

An inner transaction adds no boundary: it joins the open one and its stamp is ignored, so an observer opening its own transaction inside a framework-opened gesture cannot split that gesture into two undo steps.

§Why the guard is safe to hold

Every mutator on this type borrows the scene for the length of one call and releases it at the semicolon. The guard is held by the caller, outside any borrow, so its Drop runs with the RefCell free — which is what lets the edit sink read the scene and write it back.

A transaction is a synchronous scope. Holding one across a frame means its edits are never delivered and its salvage accumulates; a debug assertion in SceneView’s build catches the common case.

let model = SceneModel::new();
let a = model.add_item(RectItem::new(Rect::new(0.0, 0.0, 10.0, 10.0)), Point::ZERO);
let b = model.add_item(RectItem::new(Rect::new(0.0, 0.0, 10.0, 10.0)), Point::ZERO);
{
    let _txn = model.transaction(ChangeSource::User, HistoryMode::Record);
    model.set_local_pos(a, Point::new(10.0, 0.0));
    model.set_local_pos(b, Point::new(20.0, 0.0));
} // one transaction, two edits
Source

pub fn user_edit(&self) -> SceneTransaction<'_>

transaction(ChangeSource::User, HistoryMode::Record) — one user-visible edit.

Source

pub fn set_edit_sink( &self, sink: impl FnMut(SceneTransactionRecord) + 'static, ) -> Option<Box<dyn FnMut(SceneTransactionRecord)>>

Install the edit sink, returning whatever was there.

Invoked once per committed transaction that changed anything, with the scene unborrowed — so the sink may read the scene and write it back. Its own writes are ordinary transactions: they produce their own records and are delivered to it on a later round of the same loop, which is what keeps the history it is feeding in agreement with the scene.

Exactly one. A scene’s edit history has one owner; a second consumer composes inside the first. Owning is why: the sink is handed the removed items themselves, and two owners of one Box is not a thing.

With no sink installed, nothing is recorded and a removal drops its salvage — today’s behaviour at today’s cost, for an app that wants no history.

§This is not where snap-to-grid goes

The sink runs after the write. Snapping a gesture belongs in set_geometry_constraint, which runs before it and is consulted by every gesture route — so the frame the last preview drew is the frame the commit writes. Use the sink for what happens once an edit is real: recording it, persisting it, marking the document dirty, mirroring it to a peer.

§Panics

If called from inside the sink itself.

Source

pub fn clear_edit_sink(&self) -> Option<Box<dyn FnMut(SceneTransactionRecord)>>

Remove the edit sink, returning it. The scene stops recording.

Source

pub fn transaction_signal(&self) -> Signal<TxnId>

Fires once per committed transaction, after the edit sink, with the scene still unborrowed.

The per-gesture counterpart of item_change_signal: one notification for a whole logical edit rather than one per intermediate write. This is where a document-dirty flag, a debounced save or a validation pass belongs — they want to run once when the user lets go, not six times while the item is moving.

Source

pub fn open_transaction_depth(&self) -> u32

How many transaction scopes are currently open on this scene — zero between mutations.

A transaction is a synchronous scope: holding one across a frame means its edits never reach the sink and its salvage accumulates. There is no way to enforce that at the type level (nothing stops a guard being stashed in a struct field), so this is the door an assertion knocks on. SceneView asserts it is zero at the top of every build in debug, which catches the common case.

Source

pub fn cascade_budget(&self) -> CascadeBudget

The runaway-detection budget for this scene’s change fan-out — how much work observers may generate from one batch before the drain declares the cascade non-terminating and panics.

See CascadeBudget for the rule and its default.

Source

pub fn set_cascade_budget(&self, budget: CascadeBudget)

Replace the runaway-detection budget for this scene’s change fan-out.

The mechanism is in the framework; this is where the policy lives. The default — 100 000 observer-generated deliveries per drain — is more than an order of magnitude above the legitimate shapes this tier runs, but it cannot tell a guarded cascade that is genuinely enormous from an unguarded write-back: the two look identical from the queue’s side. An app whose graph genuinely settles past the default therefore has a supported answer here rather than a crash:

use teksilo_scene::{CascadeBudget, SceneModel};

let model = SceneModel::new();
model.set_cascade_budget(CascadeBudget::new(1_000_000));
assert_eq!(model.cascade_budget().total, 1_000_000);

Raising it to silence a cycle that does not settle only postpones the freeze it is there to prevent — the drain is still unbounded in time, just later. Check the write is guarded first.

A budget of zero is clamped to the smallest usable one rather than stored: it would forbid the reactive write-back this channel exists for.

Takes effect on the next drain: a drain already running read its budget when it began, so an observer cannot enlarge its own.

Source

pub fn new() -> Self

A handle to a fresh empty scene with the default spatial index.

Source

pub fn with_index(index: Box<dyn SpatialIndex>) -> Self

A handle to a fresh scene with a custom SpatialIndex.

Source

pub fn from_scene(scene: Scene) -> Self

Wrap an existing Scene in a handle. Used by SceneView::new for the single-view path.

Source

pub fn handle_count(&self) -> usize

Number of distinct handles to this scene (1 = unshared).

Source

pub fn downgrade(&self) -> WeakSceneModel

A non-owning handle to the same scene.

For a closure the scene itself owns. The geometry constraint is the one in this crate: the scene holds it, so a closure holding a SceneModel back closes a reference cycle and the whole scene — items, heavyweight payloads, journal, spatial index — is never dropped. A constraint normally needs no handle at all (it is handed ProposedChange::scene, which is the whole read surface); this is for the case where a policy object holds a model for its other work and is also installed as the closure.

let model = SceneModel::new();
let weak = model.downgrade();
assert!(weak.upgrade().is_some());
drop(model);
assert!(weak.upgrade().is_none());
Source

pub fn add_widget<W: Widget + 'static>(&self, widget: W, rect: Rect) -> ItemId

Single-view heavyweight widget (the one-shot Once path). The first view to build drains it; a second view sharing this model produces no child for it. For multi-view, use add_widget_item.

Source

pub fn add_widget_item<P: 'static>(&self, payload: P, rect: Rect) -> ItemId

Multi-view heavyweight item: store a typed payload; each view builds its own widget instance from it via its delegate. Returns the ItemId.

Source

pub fn set_payload<P: 'static>(&self, id: ItemId, payload: P)

Replace the payload of a Delegated heavyweight item; every view rebuilds that item’s widget on the next pass.

§Panics

Panics if id is unknown, refers to a single-view add_widget (Once) entry, or refers to a lightweight item.

Source

pub fn payload(&self, id: ItemId) -> Option<Rc<dyn Any>>

The current type-erased payload of a Delegated item, if any.

Source

pub fn add_item<I: SceneItem + 'static>( &self, item: I, local_pos: Point, ) -> ItemId

Add a lightweight SceneItem at local_pos.

Source

pub fn add_item_dynamic<I: SceneItem + 'static>( &self, item: I, local_pos: Point, ) -> ItemId

Add a lightweight item with signal-driven (dynamic) bounds.

Source

pub fn add_boxed_item( &self, item: Box<dyn SceneItem>, local_pos: Point, ) -> ItemId

Add an already-boxed lightweight item at local_pos. The boxed-dyn counterpart of add_item, used by SceneListAdapter.

Source

pub fn set_local_pos(&self, id: ItemId, local_pos: Point)

Move id to local_pos in its parent’s coordinate space; notifies all views.

Source

pub fn set_local_bounds(&self, id: ItemId, local_bounds: Rect)

Replace the local bounding rect of id; notifies all views.

Idempotent, and an item whose box is derived from its geometry fits itself to the rectangle rather than adopting it — see Scene::set_local_bounds for both contracts, which matter here because this is the form an app drives per frame.

Source

pub fn set_transform(&self, id: ItemId, transform: Transform2D)

Set an additional local-to-parent transform (rotation, scale) on id; notifies all views.

Source

pub fn size_policy(&self, id: ItemId) -> SizePolicy

Which axes of id’s box its widget decides. See SizePolicy.

Source

pub fn set_size_policy(&self, id: ItemId, policy: SizePolicy) -> bool

Hand one or both axes of id’s box to its widget, on every view of this model. See SizePolicy and Scene::set_size_policy — including for the note that it is refused for a lightweight entry.

Source

pub fn set_measured_size(&self, id: ItemId, size: Size) -> bool

Write a size derived from content rather than authored by the user; notifies all views. See Scene::set_measured_size for which of the two geometry doors this is and when to reach for the other one.

Source

pub fn set_geometry_constraint( &self, f: impl Fn(&ProposedChange<'_>) -> ChangeVerdict + 'static, )

Install the document’s standing geometry rule — snap-to-grid, axis lock, page-bounds clamp — consulted before every user-driven gesture applies anything, on every view attached to this model.

One per scene. Programmatic mutators (set_local_pos and friends) are never constrained: a document load must land where it says.

The closure is handed a ProposedChange — the scene read-only, the roots the gesture moves, and the gesture’s start and proposed frames in scene coordinates — and returns a ChangeVerdict. It may read the scene and may not write it; a write panics naming this hook.

use teksilo_canvas::{Point, Rect};
use teksilo_scene::{ChangeVerdict, RectItem, SceneModel};

let model = SceneModel::new();
let card = model.add_item(
    RectItem::new(Rect::new(0.0, 0.0, 80.0, 50.0)),
    Point::new(4.0, 4.0),
);

let grid = 25.0;
let page = Rect::new(0.0, 0.0, 1000.0, 700.0);
model.set_geometry_constraint(move |c| {
    // An explicit magnet outranks the standing rule.
    if c.magnet_snapped {
        return ChangeVerdict::Accept;
    }
    let mut f = c.proposed;
    // The frame is the item's own box in scene coordinates, so this
    // snaps what the user can see — no grab offset to carry.
    f.rect.x = (f.rect.x / grid).round() * grid;
    f.rect.y = (f.rect.y / grid).round() * grid;
    f.rect.x = f.rect.x.clamp(page.x, page.right() - f.rect.width);
    f.rect.y = f.rect.y.clamp(page.y, page.bottom() - f.rect.height);
    ChangeVerdict::Adjust(f)
});

// Programmatic writes are exact — the constraint does not see them.
model.set_local_pos(card, Point::new(7.0, 9.0));
assert_eq!(model.local_pos(card), Some(Point::new(7.0, 9.0)));
Source

pub fn clear_geometry_constraint(&self)

Remove the geometry constraint. See Scene::clear_geometry_constraint.

Source

pub fn has_geometry_constraint(&self) -> bool

Whether a geometry constraint is installed.

Source

pub fn constrain_frame( &self, items: &[ItemId], op: TransformOp, start: TransformFrame, proposed: TransformFrame, source: TransformSource, ) -> TransformFrame

Run the installed constraint over a proposed frame and return the frame to apply. Returns proposed unchanged when none is installed.

The general door for an app that drives its own gesture — a heavyweight card’s on_drag, a custom resize grip, a layout command. The SceneView’s own routes call exactly this, so an app-driven gesture and a framework-driven one obey the same rule.

start is the frame the gesture began from (transform_frame at press time), and it must stay fixed for the gesture’s life — that is what lets a constraint measure total travel, and what keeps the preview and the commit in agreement.

§Panics

Twice, each naming what to do instead:

  • From inside a constraint that is already running on this scene — the constraint is the answer, and asking for another one would recurse without end.
  • While a write scope is open on this model — a SceneWriteGuard, any mutator, or an observer running inside one. Asking the constraint reads the scene, and an open write scope holds it exclusively. Another shared borrow is fine: two reads coexist, which is what lets a constraint read the scene it is deciding about.
Source

pub fn constrain_move( &self, items: &[ItemId], start: TransformFrame, translation: Vec2, source: TransformSource, ) -> Vec2

Run the installed constraint over a proposed move and return the scene-space translation to apply. Returns translation unchanged when none is installed.

The door a heavyweight card’s own drag handler uses, and the reason the constraint lives on the model rather than on a view — a card built by a per-view delegate holds a SceneModel clone and never a &SceneView:

// At the press: the frame the gesture starts from.
let start = model.transform_frame(&[card]).expect("card resolves");
// On each sample: the raw travel, constrained.
let travel = Vec2::new(31.0, 12.0);
let applied = model.constrain_move(&[card], start, travel, TransformSource::Pointer);
assert_eq!(applied, Vec2::new(25.0, 0.0));

Only the frame’s origin is read back, because a translation is all a move can express: a constraint that also resizes or rotates the frame here has that part of its answer ignored. Use constrain_frame plus apply_transform_delta for a gesture that changes extent or orientation.

§Panics

The same two as constrain_frame: from inside a running constraint, and while a write scope is open on this model.

Source

pub fn apply_transform_delta( &self, roots: &[ItemId], delta: &TransformDelta, ) -> usize

Apply one transform delta to roots as a single operation; notifies all views. See Scene::apply_transform_delta.

Source

pub fn selection_roots(&self, ids: &[ItemId]) -> Vec<ItemId>

ids pruned to its roots. See Scene::selection_roots.

Source

pub fn transformable_roots( &self, ids: &[ItemId], op: TransformOp, ) -> Vec<ItemId>

The roots of ids eligible for op. See Scene::transformable_roots.

Source

pub fn transform_frame(&self, roots: &[ItemId]) -> Option<TransformFrame>

The selection frame enclosing roots. See Scene::transform_frame.

Source

pub fn scene_rotation(&self, id: ItemId) -> Option<f32>

The item’s own rotation in scene space, in radians. See Scene::scene_rotation.

Source

pub fn set_flags(&self, id: ItemId, flags: ItemFlags)

Replace the complete ItemFlags bitset for id; notifies all views.

Source

pub fn set_flag(&self, id: ItemId, flag: ItemFlags, on: bool)

Set or clear a single ItemFlags bit on id; notifies all views.

Source

pub fn set_visible(&self, id: ItemId, visible: bool)

Show or hide id (also hides its descendants); notifies all views.

Source

pub fn set_opacity(&self, id: ItemId, opacity: f32)

Set the paint opacity of id (0.0 = transparent, 1.0 = opaque); notifies all views.

Source

pub fn set_item_fill(&self, id: ItemId, fill: impl Into<ColorProp>)

Replace a lightweight item’s fill colour live; every view repaints (no relayout/rebuild). Accepts a plain Color, a theme role, a Signal<Color>, or a Signal<Role>. See Scene::set_item_fill for the reactive-colour contract.

Source

pub fn clear_item_fill(&self, id: ItemId)

Clear a lightweight item’s fill; every view repaints.

Source

pub fn set_item_stroke( &self, id: ItemId, color: impl Into<ColorProp>, style: StrokeStyle, )

Replace a lightweight item’s stroke (colour + [StrokeStyle]) live; every view repaints (no relayout/rebuild).

Source

pub fn clear_item_stroke(&self, id: ItemId)

Clear a lightweight item’s stroke; every view repaints.

Source

pub fn set_z(&self, id: ItemId, z: f32)

Set the z-order of id within its layer; higher values paint on top.

Source

pub fn bring_to_front(&self, id: ItemId)

Give id the highest z-value in its layer so it paints on top of all siblings.

Source

pub fn send_to_back(&self, id: ItemId)

Give id the lowest z-value in its layer so it paints beneath all siblings.

Source

pub fn set_layer(&self, id: ItemId, layer: SceneLayer)

Move id to a different SceneLayer (Under, Interleaved, Over); notifies all views.

Source

pub fn set_item_parent(&self, child: ItemId, parent: Option<ItemId>)

Re-parent child under parent (or under the scene root when None); notifies all views.

Source

pub fn remove(&self, id: ItemId)

Remove an item and its descendants. Drops any Delegated payload Rc and cleans the item’s a11y mappings; alive logical children re-root.

Source

pub fn take(&self, id: ItemId) -> Vec<RemovedItem>

Remove id and its descendants, handing the salvage back — item box, handlers, magnets, logical-AT decorations, each at its original ItemId. See Scene::take.

Source

pub fn restore(&self, salvage: RemovedItem) -> Result<ItemId, RestoreError>

Put one salvaged item back at its original id. See Scene::restore.

Source

pub fn restore_all( &self, salvage: Vec<RemovedItem>, ) -> Result<Vec<ItemId>, RestoreError>

Put a whole take result back, roots first. See Scene::restore_all.

Source

pub fn replace_item( &self, id: ItemId, item: Box<dyn SceneItem>, ) -> Result<Box<dyn SceneItem>, ReplaceRejected>

Swap the lightweight item box at id, keeping the entry and the id. Returns the box that was there. See Scene::replace_item.

Source

pub fn placement(&self, id: ItemId) -> Option<Placement>

Where id sits, as one value: parent, z, position, transform.

Source

pub fn set_placement(&self, id: ItemId, placement: Placement)

Write parent, z, position and transform together, as one change. See Scene::set_placement.

Source

pub fn reparent_keeping_scene_pos(&self, id: ItemId, parent: Option<ItemId>)

Reparent id while holding it visually still. See Scene::reparent_keeping_scene_pos.

Source

pub fn z_between(&self, below: ItemId, above: ItemId) -> Option<f32>

A z strictly between two items’, or None when there is no room left at that locus. See Scene::z_between.

Source

pub fn orphan(&self, id: ItemId)

Promote an item’s children to the scene root.

Source

pub fn set_item_handlers( &self, id: ItemId, handlers: Option<SceneItemHandlerSet>, )

Replace the SceneItemHandlerSet of id, or clear it with None.

Source

pub fn with_handlers_mut<R>( &self, id: ItemId, f: impl FnOnce(&mut SceneItemHandlerSet) -> R, ) -> Option<R>

Mutate an item’s handler set through a closure (avoids returning a borrow guard tied to the RefMut).

Source

pub fn add_magnet(&self, item: ItemId, magnet: Magnet) -> MagnetId

Attach a Magnet to item; see Scene::add_magnet.

Source

pub fn remove_magnet(&self, magnet: MagnetId)

Remove a magnet by id; see Scene::remove_magnet.

Source

pub fn clear_magnets(&self, item: ItemId)

Remove every magnet on item; see Scene::clear_magnets.

Source

pub fn set_magnet_local_pos(&self, magnet: MagnetId, local_pos: Point)

Move a magnet in its item’s local frame; see Scene::set_magnet_local_pos.

Source

pub fn set_magnet_enabled(&self, magnet: MagnetId, enabled: bool)

Enable or disable a magnet; see Scene::set_magnet_enabled.

Source

pub fn magnet_ids_of(&self, item: ItemId) -> Vec<MagnetId>

Ids of every magnet on item; see Scene::magnet_ids_of.

Source

pub fn magnet_owner(&self, magnet: MagnetId) -> Option<ItemId>

The owning item of a magnet; see Scene::magnet_owner.

Source

pub fn magnet_scene_pos(&self, magnet: MagnetId) -> Option<Point>

A magnet’s scene position; see Scene::magnet_scene_pos.

Source

pub fn magnet(&self, magnet: MagnetId) -> Option<MagnetRef>

Resolve a magnet to a MagnetRef snapshot; see Scene::magnet.

Source

pub fn compute_item_snap( &self, dragged: ItemId, drag_delta: Vec2, capture_radius: f32, predicate: &dyn Fn(&MagnetRef, &MagnetRef) -> MagnetVerdict, ) -> Option<MagnetSnap>

Best item-drag snap; see Scene::compute_item_snap. A shared (read-only) borrow is held while the predicate runs over owned candidate snapshots, so the predicate may read but must not mutate the model.

Source

pub fn compute_port_snap( &self, source: MagnetId, cursor_scene: Point, capture_radius: f32, predicate: &dyn Fn(&MagnetRef, &MagnetRef) -> MagnetVerdict, ) -> Option<(MagnetRef, Option<Rc<dyn Any>>)>

Best port-drag snap; see Scene::compute_port_snap.

Source

pub fn nearest_magnet(&self, scene_pt: Point, radius: f32) -> Option<MagnetId>

Nearest enabled magnet within radius; see Scene::nearest_magnet.

Source

pub fn set_scene_rect(&self, rect: Option<Rect>)

Set the logical extent of the scene (used for scroll-bar sizing); None = unbounded.

Source

pub fn pan_axes(&self, axes: PanAxes)

Restrict panning to horizontal, vertical, or both axes; updates pan_axes_signal.

Source

pub fn zoomable(&self, on: bool)

Enable or disable pinch/scroll zoom; updates zoomable_signal.

Source

pub fn set_pan_bounds(&self, bounds: Option<Rect>)

Clamp the camera pan to bounds (scene coordinates); None = no limit; updates pan_bounds_signal.

Source

pub fn set_zoom_range(&self, range: Option<RangeInclusive<f32>>)

Restrict the zoom factor to range; None = no limit; updates zoom_range_signal.

Source

pub fn add_a11y_group(&self, builder: A11yGroupBuilder) -> A11yGroupId

Register a logical AT group (landmark / rotor category container); returns its stable A11yGroupId.

Source

pub fn remove_a11y_group(&self, id: A11yGroupId)

Remove a previously registered AT group; triggers an a11y_change_signal bump.

Source

pub fn set_a11y_parent(&self, child: A11yNode, parent: Option<A11yNode>)

Re-parent child in the AT tree, overriding the default visual parent; None re-attaches under the scene root.

Source

pub fn add_a11y_relation( &self, from: A11yNode, kind: A11yRelation, to: A11yNode, )

Declare a cross-node AT relationship (controls, describes, labels) from from to to.

Source

pub fn set_a11y_live(&self, node: A11yNode, live: Live)

Mark node as a live region (Polite or Assertive) so assistive tech announces changes to it.

Source

pub fn set_a11y_landmark(&self, node: A11yNode, role: Role)

Assign a landmark role to node (e.g. Role::Region, Role::Main) for rotor navigation.

Source

pub fn set_a11y_categories(&self, node: A11yNode, categories: &[A11yCategory])

Register node under the given rotor A11yCategory slices so it appears in category-filtered navigation.

Source

pub fn refresh_dynamic_bounds(&self) -> bool

Re-read signal-driven bounds for add_item_dynamic entries; returns true if any changed.

Source

pub fn item_change_signal(&self) -> Signal<SceneChange>

Reactive signal fired on every structural scene change; all views observe this to reconcile.

Source

pub fn a11y_change_signal(&self) -> Signal<u64>

Reactive monotonic counter bumped on every AT-structure change; views re-walk accessibility on any increment.

Source

pub fn mutation_version(&self) -> u64

Monotonic counter incremented on every mutation; useful for cache invalidation without observing a signal.

Source

pub fn structural_version(&self) -> u64

mutation_version with the per-frame churn of refresh_dynamic_bounds excluded — the version to gate an expensive rebuild on. See Scene::structural_version.

Source

pub fn pan_axes_signal(&self) -> Signal<PanAxes>

Reactive current PanAxes restriction; updated by pan_axes.

Source

pub fn pan_bounds_signal(&self) -> Signal<Option<Rect>>

Reactive camera-pan clamp bounds; updated by set_pan_bounds.

Source

pub fn zoom_range_signal(&self) -> Signal<Option<RangeInclusive<f32>>>

Reactive zoom-factor clamp range; updated by set_zoom_range.

Source

pub fn zoomable_signal(&self) -> Signal<bool>

Reactive zoom-enabled flag; updated by zoomable.

Source

pub fn len(&self) -> usize

Total number of items in the scene (lightweight + heavyweight).

Source

pub fn is_empty(&self) -> bool

Returns true when the scene contains no items.

Source

pub fn ids(&self) -> Vec<ItemId>

All ItemIds currently in the scene, in insertion order.

Source

pub fn local_pos(&self, id: ItemId) -> Option<Point>

The local position of id in its parent’s coordinate space; None if id is unknown.

Source

pub fn local_bounds(&self, id: ItemId) -> Option<Rect>

The local bounding rect of id; None if id is unknown.

Source

pub fn transform(&self, id: ItemId) -> Option<Transform2D>

The additional local-to-parent transform of id (beyond position); None if none is set.

Source

pub fn scene_transform(&self, id: ItemId) -> Transform2D

The full local-to-scene transform for id (parent chain composed); identity if id is unknown.

Source

pub fn scene_pos(&self, id: ItemId) -> Option<Point>

The origin of id mapped into scene coordinates; None if id is unknown.

Source

pub fn scene_rect(&self, id: ItemId) -> Option<Rect>

The bounding rect of id in scene coordinates (local bounds transformed by the parent chain); None if unknown.

Source

pub fn flags(&self, id: ItemId) -> Option<ItemFlags>

The ItemFlags bitset of id; None if id is unknown.

Source

pub fn is_effectively_visible(&self, id: ItemId) -> bool

Returns true if id and all of its ancestors are visible.

Source

pub fn opacity(&self, id: ItemId) -> Option<f32>

The own opacity of id (ignoring ancestors); None if id is unknown.

Source

pub fn effective_opacity(&self, id: ItemId) -> f32

Accumulated opacity for id (own × each ancestor’s opacity).

Source

pub fn z(&self, id: ItemId) -> Option<f32>

The z-order value of id within its layer; None if id is unknown.

Source

pub fn layer(&self, id: ItemId) -> Option<SceneLayer>

The SceneLayer of id; None if id is unknown.

Source

pub fn parent_of(&self, id: ItemId) -> Option<ItemId>

The direct parent of id, or None if it is a root item (or unknown).

Source

pub fn is_descendant_of(&self, id: ItemId, ancestor: ItemId) -> bool

Returns true if id is anywhere in ancestor’s subtree.

Source

pub fn scene_rect_extent(&self) -> Option<Rect>

The logical extent set via set_scene_rect; None = unbounded.

Source

pub fn current_pan_axes(&self) -> PanAxes

The current pan-axis restriction without subscribing to its signal.

Source

pub fn is_zoomable(&self) -> bool

Returns true if zoom is currently enabled (snapshot; use zoomable_signal for reactivity).

Source

pub fn current_pan_bounds(&self) -> Option<Rect>

Current pan-clamp bounds without subscribing to its signal.

Source

pub fn current_zoom_range(&self) -> Option<RangeInclusive<f32>>

Current zoom-factor clamp range without subscribing to its signal.

Source

pub fn items_in_rect(&self, scene_rect: Rect) -> Vec<ItemId>

All items whose bounding rects overlap scene_rect (spatial-index query).

Source

pub fn items_in_region( &self, region: &SceneRegion, mode: ItemSelectionMode, view_scale: f32, ) -> Vec<ItemId>

Items matching region under mode; see Scene::items_in_region.

Screen-anchored (IGNORES_TRANSFORMATIONS) items are skipped, for the same reason SceneModel::item_at skips them: a scene-space region cannot place them.

Source

pub fn item_shape(&self, id: ItemId) -> Option<ItemShape>

An entry’s geometry in local coordinates; see Scene::item_shape.

Source

pub fn item_region(&self, id: ItemId) -> Option<SceneRegion>

An entry’s own shape as a scene-space region; see Scene::item_region.

Source

pub fn item_contains( &self, id: ItemId, scene_pt: Point, view_scale: f32, ) -> bool

Whether id’s shape contains scene_pt; see Scene::item_contains.

Source

pub fn paint_key(&self, id: ItemId) -> Option<PaintKey>

Where id sits in this scene’s single paint order; see Scene::paint_key.

Defined for both tiers, so it is the value to compare when an app needs to ask “which of these two is on top?” without re-deriving the band/z/insertion rule.

Source

pub fn is_hit_testable(&self, id: ItemId) -> bool

Whether id takes part in pointer hit-testing — visible along its whole ancestor chain AND enabled; see Scene::is_hit_testable.

The public form of the filter every picker here applies, so an app can ask why one of its items is not answering.

Source

pub fn item_at(&self, scene_pt: Point) -> Option<ItemId>

The topmost lightweight item whose shape contains scene_pt, at unit view scale; see Scene::item_at for the full contract.

“Topmost” is Scene::paint_key order, so an Over-band item beats a higher-z Under one and an equal-z tie goes to the later-inserted entry. Hidden and disabled entries are excluded, and heavyweight widget entries and screen-anchored (IGNORES_TRANSFORMATIONS) items are skipped — see SceneModel::item_at_in_view for the query that places the latter.

Source

pub fn item_at_scaled(&self, scene_pt: Point, view_scale: f32) -> Option<ItemId>

Scene::item_at at an explicit view zoom. The zoom reaches exactly one thing: a cosmetic stroke band’s width in scene units.

Source

pub fn items_at(&self, scene_pt: Point) -> Vec<ItemId>

All lightweight items whose shape contains scene_pt, topmost-first in Scene::paint_key order. Same tier, flag and IGNORES_TRANSFORMATIONS rules as SceneModel::item_at.

Source

pub fn items_at_scaled(&self, scene_pt: Point, view_scale: f32) -> Vec<ItemId>

Scene::items_at at an explicit view zoom.

Source

pub fn item_at_in_view( &self, screen_pt: Point, view_transform: Transform2D, ) -> Option<ItemId>

The topmost item under a screen point, resolving both hit spaces; see Scene::item_at_in_view.

Source

pub fn colliding_items(&self, id: ItemId) -> Vec<ItemId>

Items overlapping id’s shape, excluding id; see Scene::colliding_items.

Source

pub fn colliding_items_with( &self, id: ItemId, mode: ItemSelectionMode, ) -> Vec<ItemId>

Scene::colliding_items under an explicit mode.

Source

pub fn items_along_path(&self, path: &Path) -> Vec<ItemId>

Items lying along path; see Scene::items_along_path. (This was unreachable from a SceneModel before — the facade never forwarded it.)

Source

pub fn items_along_path_with( &self, path: &Path, stroke_width: f32, mode: ItemSelectionMode, ) -> Vec<ItemId>

Scene::items_along_path with an explicit stroke width and mode.

Source

pub fn a11y_parent_of(&self, child: A11yNode) -> Option<A11yNode>

The AT-tree parent of child as set by set_a11y_parent; None = visual default.

Source

pub fn a11y_relations(&self) -> Vec<(A11yNode, A11yRelation, A11yNode)>

Every declared AT relation, in declaration order.

Source

pub fn a11y_live_of(&self, node: A11yNode) -> Option<Live>

A node’s declared live-region politeness; None when it is not one.

Source

pub fn a11y_landmark_of(&self, node: A11yNode) -> Option<Role>

A node’s declared landmark role; None when it is not a landmark.

Source

pub fn a11y_categories_of(&self, node: A11yNode) -> Vec<A11yCategory>

A node’s declared rotor / quick-nav categories.

Trait Implementations§

Source§

impl Clone for SceneModel

Source§

fn clone(&self) -> Self

Produce a second handle to the same scene (cheap Rc clone).

1.0.0 (const: unstable) · Source§

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

Performs copy-assignment from source. Read more
Source§

impl Debug for SceneModel

Source§

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

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

impl Default for SceneModel

Source§

fn default() -> Self

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
§

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

§

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
§

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

§

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.
§

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.
§

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.
§

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.
§

impl<T> Downcast<T> for T

§

fn downcast(&self) -> &T

§

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.

§

impl<T> Instrument for T

§

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

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

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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

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

§

type NoneType = T

§

fn null_value() -> T

The none-equivalent value.
§

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

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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 = Infallible

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

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

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.
§

impl<T> Upcast<T> for T

§

fn upcast(&self) -> Option<&T>

§

impl<T> WithSubscriber for T

§

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
§

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

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