pub struct ChildPod { /* private fields */ }Expand description
A container’s owned child: a boxed widget plus the layout geometry and capture bookkeeping the container maintains for it.
Containers own their children directly as ChildPods (a Vec for
Flex/Stack, named fields for Padding/Align) rather than as arena nodes — the
WidgetTree arena stays single-root. This is a
deliberate divergence from the masonry “everything in the arena” model:
it needs zero global-id plumbing and no
disjoint-borrow gymnastics for the container features this crate ships. Arena-backed children
(for damage tracking / a11y global access) are deferred to a later phase.
origin/size are in the container’s local coordinate space;
ChildPod::event_child translates events into the child’s local space and
ChildPod::paint_child offsets the child’s paint origin accordingly.
Because the arena cannot see into a container, the pod also carries what
read-only tooling needs to describe the element it wraps —
type_name, debug_label,
inspect_id, plus the geometry above — and
Widget::visit_children is how a walk reaches it.
Implementations§
Source§impl ChildPod
impl ChildPod
Sourcepub const INSPECT_ID_BASE: u64
pub const INSPECT_ID_BASE: u64
Where ChildPod::inspect_id’s allocator starts.
Pod ids and arena WidgetIds share one
namespace in an inspect snapshot, and the arena’s are allocated from zero
upward by BuildCtx::alloc_id. Starting the pod allocator at 2^48 keeps
the two apart for any tree an app could plausibly build (the arena would
have to allocate 281 trillion ids to reach it) without threading a shared
counter through a read-only walk.
Sourcepub fn new(widget: Box<dyn Widget>) -> Self
pub fn new(widget: Box<dyn Widget>) -> Self
Wrap a freshly built child widget at the origin, with zero size until its first layout.
Sourcepub fn type_name(&self) -> &'static str
pub fn type_name(&self) -> &'static str
The wrapped widget’s concrete type name, asked of the live widget
(Widget::type_name) rather than recorded at build time — so a
rebuild that swapped the child’s type can never leave a stale name here,
and the double box build_child stores resolves through both layers.
Diagnostic only: type_name’s output is not a stable contract across
compiler versions, so never parse or match on it.
Sourcepub fn debug_label(&self) -> Option<&str>
pub fn debug_label(&self) -> Option<&str>
The human name attached for tooling, if any. None by default.
Sourcepub fn set_debug_label(&mut self, label: impl Into<Cow<'static, str>>)
pub fn set_debug_label(&mut self, label: impl Into<Cow<'static, str>>)
Attach a human name for tooling (an inspector shows it beside the type name). Purely descriptive — nothing in the build/layout/paint/event path reads it.
Sourcepub fn clear_debug_label(&mut self)
pub fn clear_debug_label(&mut self)
Drop any attached debug label.
Sourcepub fn inspect_id(&self) -> WidgetId
pub fn inspect_id(&self) -> WidgetId
This pod’s tooling id, assigning one on the first call and reusing it thereafter — so the id a devtools client holds keeps naming the same pod across frames, and across a keyed reorder that relocates the pod.
Behind &self (interior mutability) because the whole introspection
seam is read-only; ids come from INSPECT_ID_BASE
upward and never collide with an arena WidgetId.
Sourcepub fn widget_mut(&mut self) -> &mut dyn Widget
pub fn widget_mut(&mut self) -> &mut dyn Widget
Mutable access to the boxed child widget (e.g. to downcast during a container’s own rebuild).
Marks the pod layout-dirty, conservatively: a caller holding the
widget mutably may change state its layout depends on, and nothing tells
the layout skip whether it did (see ChildPod::layout_child). A
hand-rolled reconciler that rebuilds a child’s own sub-pods through this
is thereby covered without knowing about the skip. A reconciler that
marks the pod itself from the rebuild’s
ChangeFlags uses
ChildPod::widget_mut_for_rebuild instead, so a rebuild that changed
nothing layout-visible leaves the pod clean.
Sourcepub fn widget_mut_for_rebuild(&mut self) -> &mut dyn Widget
pub fn widget_mut_for_rebuild(&mut self) -> &mut dyn Widget
Mutable access to the boxed child widget without marking the pod
layout-dirty — for a reconciler that rebuilds the child through it and
then marks the pod itself with ChildPod::mark_layout_dirty when the
rebuild returns ChangeFlags::LAYOUT
(frust-widgets’ rebuild_child/rebuild_children and
crate::component::ComponentView do). Taking this accessor is taking
on that duty: a layout-affecting change made through it and not marked
lays out stale.
Sourcepub fn set_widget(&mut self, widget: Box<dyn Widget>)
pub fn set_widget(&mut self, widget: Box<dyn Widget>)
Replace the boxed child widget (used when a type-changing rebuild swaps the underlying widget). The fresh widget has never been laid out, so the pod is marked layout-dirty.
Sourcepub fn is_layout_dirty(&self) -> bool
pub fn is_layout_dirty(&self) -> bool
Whether the next ChildPod::layout_child must lay the child out even
under unchanged constraints (see that method for the rule). Read-only;
for tooling and tests (crate::tree::WidgetTree::inspect_layout_dirty).
Sourcepub fn mark_layout_dirty(&mut self)
pub fn mark_layout_dirty(&mut self)
Mark this pod layout-dirty: its next ChildPod::layout_child calls
Widget::layout whatever its constraints.
The reconcilers call this when a child’s rebuild returns
ChangeFlags::LAYOUT. It marks this
pod only — a dirty pod is reached only if every pod above it is laid
out too, so a caller outside a rebuild that reports LAYOUT upward must
also dirty its own path (by calling PaintCtx::request_layout or
EventCtx::request_layout).
Sourcepub fn is_paint_dirty(&self) -> bool
pub fn is_paint_dirty(&self) -> bool
Whether the pod’s subtree must be painted afresh (see
PaintCtx::subtree_clean). Read-only; for tooling and tests
(crate::tree::WidgetTree::inspect_paint_dirty).
Sourcepub fn mark_paint_dirty(&mut self)
pub fn mark_paint_dirty(&mut self)
Mark this pod paint-dirty only: layout is untouched. A reconciler calls
this when a child’s rebuild returns
ChangeFlags::PAINT (a LAYOUT rebuild
already implies it through ChildPod::mark_layout_dirty). Like that
method it marks this pod only; the pods above are marked by their own
reconcilers from the same flags.
Sourcepub fn size(&self) -> Size
pub fn size(&self) -> Size
The child’s resolved size (valid after ChildPod::layout_child).
Sourcepub fn set_origin(&mut self, origin: Point)
pub fn set_origin(&mut self, origin: Point)
Place the child at origin within the container’s coordinate space.
Sourcepub fn transform(&self) -> Option<Affine>
pub fn transform(&self) -> Option<Affine>
The child’s transform, if any (see ChildPod::set_transform).
Sourcepub fn set_transform(&mut self, transform: Option<Affine>)
pub fn set_transform(&mut self, transform: Option<Affine>)
Place the child under an arbitrary transform, or clear it with None.
The transform is applied in the child’s own local space, before the
origin offset: a child-local point p lands at
origin + transform * p in the container’s space. So
Affine::scale_about(2.0, center) zooms the child about its own local
center, and a pan/zoom container can leave origin at zero and drive
the whole placement through the transform.
While one is set:
ChildPod::paint_childwraps the child’s paint inPaintScene::push_transform/PaintScene::pop_transform, and maps an ancestor’s visible rect back into the child’s frame (the bounding box of its inverse image), so a culling descendant still culls against what is really on screen;ChildPod::containsmaps the point through the inverse before the bounds test, so it hits exactly what paint drew — a rotated child hits along its rotated edges, not its axis-aligned bounding box (the same test ascrate::hit::point_in_transformed_rect);ChildPod::event_childmaps pointer, scroll and scale positions through the inverse (InputEvent::transformed), so the child sees local coordinates exactly as an untransformed child does;ChildPod::semantics_childreports the subtree at the axis-aligned bounding box of the transformed child rect. This is an approximation (v1): the pod’s frame becomes that box, and descendants are offset from its corner unscaled and unrotated.
A transform with no inverse (a zero scale, a collapsed axis, a non-finite
coefficient — see crate::hit::checked_inverse) draws nothing and hits
nothing: contains is false and paint skips the subtree. An event that
still reaches the child through a recorded capture or focus path is
delivered translated by -origin only, so a capture can always release.
Out of v1’s reach: rects a descendant reports in window space from
paint (platform-view frames, input shields, hero rects, overlay anchors)
are not mapped through the transform.
Sourcepub fn is_active(&self) -> bool
pub fn is_active(&self) -> bool
Whether this child currently holds the recorded active (captured) path.
Sourcepub fn set_active(&mut self, active: bool)
pub fn set_active(&mut self, active: bool)
Set (or clear) the recorded active path — the container clears this on
Up/Cancel when capture auto-releases.
The link is keyed on the gesture’s claimant. While the root is
delivering a non-claimant contact down a live capture’s path (rule (c)
of InputEvent::PointerContact’s
multi-contact contract), a clear is refused: that contact’s Up/Cancel
reaches every container on the path, and a container clearing its link on
it would strand the claimant’s own follow-ups. Outside such a pass — every
single-pointer dispatch, a rebuild, a container’s own teardown — a clear
takes effect as always.
A container that clears the link to take the gesture over from its
child (rather than because the gesture ended) should release it through
EventCtx::release_captured_child instead, which also ends the
gesture’s contact opt-in when the released subtree held it.
Sourcepub fn is_focused(&self) -> bool
pub fn is_focused(&self) -> bool
Whether this child has a recorded focus path at all — not whether that record belongs to the session the root currently holds.
The raw flag, kept for the things that legitimately want it: a paint-time
culling exemption, a blur sweep clearing whatever it finds, a container
asking “did I ever record a link here”. Deciding where a focus-routed
event goes, or whether a widget may speak for the focus session, needs
ChildPod::holds_live_focus instead — a link this flag reports can be
one a moved session left behind, and there is no pass that visits an
abandoned branch to clear it.
Sourcepub fn set_focused(&mut self, focused: bool)
pub fn set_focused(&mut self, focused: bool)
Record (or drop) this child’s focus path against the live session.
Setting it stamps the session standing on this thread
(set_live_focus_session) beside the flag, so the link says which session
it belongs to rather than merely that one existed; clearing it leaves the
stamp alone, which costs nothing because the flag gates every read.
Containers clear it on blur-on-outside-tap and set it when a child
requests focus; the flag is maintained automatically by
ChildPod::event_child on a focus_requested/focus_released bubble.
Sourcepub fn focus_epoch(&self) -> u64
pub fn focus_epoch(&self) -> u64
The focus epoch this pod’s recorded link was stamped with — the focus
counterpart of ChildPod::hover_epoch, and just as much an identity
rather than an ordering or a boolean.
Diagnostic only, for the same reason the hover stamp’s accessor is: a
stamp is never cleared, only stranded by the next session, so a non-zero
value means “a claim passed through here once”, never “focused”. Ask
ChildPod::holds_live_focus, which is the comparison this exists for.
Sourcepub fn holds_live_focus(&self) -> bool
pub fn holds_live_focus(&self) -> bool
Whether this child holds a focus link on the session the root currently has — the read every routing and provenance decision wants.
true only while the flag is set and the stamp names the live session
(set_live_focus_session). Two branches can both carry a set flag — an
overlay pod’s claim reaches no container’s blur sweep, so the branch it
superseded keeps its own record — and this is what tells them apart
without either branch having to be visited.
A dispatch driven with no root at all reads (0, 0) on both sides, so a
pod that claimed focus during such a dispatch answers true: with no
session to be stale relative to, the flag is all there is.
Sourcepub fn retire_stale_focus_link(&mut self) -> bool
pub fn retire_stale_focus_link(&mut self) -> bool
Drop a recorded focus link that the live session has already stranded, reporting whether one was dropped.
The retirement seam a RenderRoot needs and
cannot otherwise have. A pod floated by the overlay portal lives off the
tree, behind its owner’s Rc, and the root borrows it for exactly the
length of the paint pass that floats it — so the one moment the root can
act on such a link is that pass, and the one thing it can honestly say
about it is what the stamp already decides. Calling this keeps the raw
flag and the stamp telling the same story, which is what the consumers of
ChildPod::is_focused that cannot consult an epoch depend on.
Deliberately not a bare set_focused(false) at the call site: the
condition is the whole contract, and a pod whose link is live must never
be retired by a pass that merely walked past it.
Sourcepub fn hover_epoch(&self) -> u64
pub fn hover_epoch(&self) -> u64
The hover epoch this pod’s recorded hover link belongs to — the hover
counterpart of ChildPod::is_focused, reported as a stamp rather than a
flag because “is it hovered?” is only answerable against the live epoch
(0 means no claim has ever passed through this pod).
There is no setter: the stamp is maintained solely by
ChildPod::event_child from a claim bubble, so a container cannot record
or clear a hover link by hand — which is what keeps at most one path
hovered. See the crate::event module docs.
§A bare stamp answers nothing
The returned u64 is an identity, not an ordering and not a boolean. It
means something only compared for equality against the live epoch, and
that comparison is the pipeline’s own: the live epoch rides the running
context (PaintCtx::hover_epoch/EventCtx::hover_epoch), both
crate-private, and the comparison is already ANDed with the ancestor chain
by paint_child/event_child before any widget sees it. Treating a
non-zero stamp as “hovered”, or ordering two pods’ stamps, reads reasonable
and is wrong: a stamp is never cleared, only stranded by the next epoch
advance, so a pod the pointer left an hour ago still carries a non-zero
one, and the counter wraps.
Read PaintCtx::is_hovered instead (authoritative), or
EventCtx::is_hovered for the state as of the previous pass; between
them they answer “is this widget or its subtree hovered” — which is the
question a widget actually has. A container asking the narrower “which of
my children” answers it with the same hit test its own claim rides, not
from here. This accessor is diagnostic — tooling, tests, and the debug
dump — the way ChildPod::type_name is.
Sourcepub fn layout_child(
&mut self,
ctx: &mut LayoutCtx<'_>,
bc: &BoxConstraints,
) -> Size
pub fn layout_child( &mut self, ctx: &mut LayoutCtx<'_>, bc: &BoxConstraints, ) -> Size
Lay the child out under bc, recording and returning its chosen size —
or, when nothing it depends on can have changed, return the size it
recorded last time without calling Widget::layout.
§The skip rule
The child is laid out again unless all of these hold:
- the pass is not forced (
LayoutCtx::is_forced— a context built outside a root, or a root withset_layout_skip(false)); - the pod is clean (
ChildPod::is_layout_dirty); bcequals the constraints the last layout ran with;- the root’s layout epoch (
LayoutCtx::layout_epoch) equals the one the last layout ran under — a root bumps it for a theme swap, an inset change and a window resize; - the window insets the context carries equal last time’s (an ancestor’s
scoped
LayoutCtx::with_window_insetsoverride counts).
A pod is dirtied — and, because each source travels the pod chain, so is every pod between it and the root — by:
- a rebuild that returned
ChangeFlags::LAYOUTfor it (the reconcilers callChildPod::mark_layout_dirty; a PAINT-only rebuild does not dirty layout), or a widget swap; - a paint in which the child (or anything below it) called
PaintCtx::request_layoutor any frame request — conservatively, an animating subtree is laid out again on the next pass — and the one paint after the last such request, so the frame an animation lands on (the first to request nothing) is laid out too; - an event the child consumed (
EventResult::Handled), asked a redraw for, or calledEventCtx::request_layouton; - mutable access through
ChildPod::widget_mut(conservatively — a reconciler marking from flags usesChildPod::widget_mut_for_rebuild); - being freshly built.
A layout that runs records bc, the epoch and the insets, and clears the
dirty flag. Widgets keep their side of the contract on
Widget::layout: no layout-affecting state may change except through
one of the sources above.
Sourcepub fn layout_sliver_child(
&mut self,
ctx: &mut LayoutCtx<'_>,
sc: &SliverConstraints,
) -> Option<SliverGeometry>
pub fn layout_sliver_child( &mut self, ctx: &mut LayoutCtx<'_>, sc: &SliverConstraints, ) -> Option<SliverGeometry>
Lay the child out as a sliver under sc, recording and returning its
geometry — or None, touching nothing, when the child does not speak the
sliver protocol (Widget::as_sliver); the caller then lays it out as a
box, typically through an adapter.
On a sliver the pod records the geometry (ChildPod::sliver_geometry)
and sizes itself to what was painted: (cross_axis_extent, paint_extent)
in axis terms (ScrollAxis::size), so
ChildPod::contains, ChildPod::paint_child and
ChildPod::semantics_child all see the painted extent. The caller
positions the pod with ChildPod::set_origin as for a box child.
§The skip rule
Exactly ChildPod::layout_child’s, with sc in place of the box
constraints: the stored geometry is returned without calling
SliverWidget::layout_sliver when the pass is not forced, the pod is
clean, sc equals the sliver constraints the last layout ran with, and
the layout epoch and window insets are unchanged. The dirty sources are
the same ones. A box layout in between clears the recorded sliver
constraints (and the reverse), so switching protocol always lays out.
One addition: a geometry carrying a
scroll_offset_correction
leaves the pod dirty, because a correction asks the viewport to lay out
again and must never be replayed from the cache.
In debug builds sc and the reported geometry are checked with
SliverConstraints::debug_assert_valid and
SliverGeometry::debug_assert_valid.
Sourcepub fn sliver_geometry(&self) -> Option<SliverGeometry>
pub fn sliver_geometry(&self) -> Option<SliverGeometry>
The geometry the child reported from its last sliver layout
(ChildPod::layout_sliver_child) — None before one has run, or once
the pod has been laid out as a box since.
Sourcepub fn is_sliver(&mut self) -> bool
pub fn is_sliver(&mut self) -> bool
Whether the child speaks the sliver protocol (Widget::as_sliver).
Takes &mut self only because as_sliver does; unlike
ChildPod::widget_mut it does not mark the pod layout-dirty. What a
transparent wrapper holding its child in a pod asks to decide its own
Widget::as_sliver answer.
Sourcepub fn paint_child(
&mut self,
ctx: &mut PaintCtx<'_>,
scene: &mut dyn PaintScene,
)
pub fn paint_child( &mut self, ctx: &mut PaintCtx<'_>, scene: &mut dyn PaintScene, )
Paint the child, offsetting its paint origin by the container’s origin
(ctx.origin()) so the child draws at its absolute position.
Bubbles the child’s animation-continuation request (PaintCtx::needs_frame)
back into the parent ctx, mirroring ChildPod::event_child’s absorb of
the child’s redraw/capture flags — so a nested flinging widget keeps the
whole tree’s frames coming.
A pod with a transform additionally wraps
that paint in PaintScene::push_transform/PaintScene::pop_transform
(and skips it entirely when the transform has no inverse); an
untransformed pod pushes nothing.
Sourcepub fn semantics_child(&self, ctx: &mut SemanticsCtx)
pub fn semantics_child(&self, ctx: &mut SemanticsCtx)
Collect the child’s semantics, translating the current absolute origin
into the child’s space exactly like ChildPod::paint_child offsets its
paint origin (child_origin = ctx.origin() + self.origin).
A container’s Widget::semantics calls this for each of its
ChildPods so their nodes attach under the container’s node (or, for a
transparent container, under whatever encloses it — see
crate::semantics).
A pod with a transform reports its subtree at
the axis-aligned bounding box of the transformed child rect — an
approximation: descendants are offset from that box’s corner, unscaled
and unrotated.
Sourcepub fn event_child(
&mut self,
ctx: &mut EventCtx<'_>,
event: &InputEvent,
) -> EventResult
pub fn event_child( &mut self, ctx: &mut EventCtx<'_>, event: &InputEvent, ) -> EventResult
Route an event into the child, translating its position into the child’s
local space and folding the child’s redraw/capture flags back into ctx.
If the child captured the pointer, this records the active path
(ChildPod::is_active); the container clears it on Up/Cancel.
Containers should not call this directly gated on an ad-hoc
contains() check — that drops a captured gesture the instant it moves
outside the child’s bounds. Route through frust-widgets’
route_event/route_event_single helpers instead, which check
ChildPod::is_active first and forward unconditionally to a captured
child.
A pod with a transform maps positions
through the inverse of its local→container mapping instead
(InputEvent::transformed); everything else about the dispatch —
capture, contact and focus bookkeeping, hover — is identical.
§Another contact walks the active path forward-only
A non-claimant contact the root delivers down a live capture (rule (c) of
InputEvent::PointerContact’s contract) is meant for the widget that
opted in with EventCtx::capture_contacts — the captor — and for
nothing above it. The containers between the root and the captor must
not run their own pointer handling on it: a scroll view that saw a second
finger’s Down as its own would re-arm its drag from the wrong finger and
take the gesture away from the captor on the claimant’s next move.
A pod cannot reach into its child widget’s own pods, so the walk travels
on the one route every container already provides without running its
pointer machinery: the broadcast-first rule. While the root walks such a
contact, each container on the path is handed an inert carrier (an
InputEvent::Overlay addressed to a key no overlay owner holds, which
every widget ignores and every container forwards to its children before
anything else), and the real event rides beside it, re-based into each
pod’s space on the way down. This method then decides, per pod:
- Off the recorded active path: nothing — the child widget is not called at all.
- The captor (the child itself opted in on the capture that made this pod active): the child receives the real event, translated as usual, with the walk closed, so it — and whatever it routes below itself — handles the contact the ordinary way. The walk ends here: a widget below the captor that also opted in hears the contact only if the captor forwards it.
- On the path, above the captor: the child receives the carrier, so
its own handler runs but none of its gesture, capture, focus, blur or
hit-test logic does; the walk continues into its children. The result
reported upward is the captor’s, not the carrier’s
Ignored. If the carrier never reaches a pod on the path (a container whose captured child is not one of its broadcast targets — an overlay owner whose captured pod is a floated surface), this child alone is handed the real event the ordinary way instead.
Everything this method folds back into ctx — redraw, capture, focus,
IME — still bubbles from the captor through every pod on the way back
up. No hover, cursor or blur bookkeeping moves: the root opens no hover
pass for a non-claimant contact, and no container above the captor runs
the blur sweep it would run on a Down. Every other dispatch, including
the claimant’s own events, takes exactly the path described above this
section.
Sourcepub fn contains(&self, point: Point) -> bool
pub fn contains(&self, point: Point) -> bool
Whether point (in the container’s coordinate space) lies within this
child’s bounds — the container’s hit test.
This is only the initial hit test (deciding which child a fresh
Down/first contact goes to). Once a child has captured the pointer
(ChildPod::is_active), subsequent events must bypass this check and
go straight to the captured child regardless of where the point now
falls — see frust-widgets’ route_event/route_event_single.
A pod with a transform maps point back
through the transform first, so the test agrees with what paint drew; a
transform with no inverse hits nothing.
Trait Implementations§
Source§impl Drop for ChildPod
impl Drop for ChildPod
Source§fn drop(&mut self)
fn drop(&mut self)
Report a live hover link severed by the pod’s own removal, so
RenderRoot::rebuild ends the hover
before the frame ends.
The epoch mechanism strands a stale stamp on every hover pass, but a pass
is exactly what a removed widget no longer gets: a rebuild that drops the
claimant leaves the root’s mirror standing (is_hover_active() keeps
reporting a link nothing holds) and leaves every surviving ancestor of the
dead claimant reading hovered off its own still-matching stamp, until some
later Move happens to re-derive — which never arrives on a pointer the
user has stopped moving. This destructor is the hover analog of the
focus-orphan mark, and lives here rather than in the reconcilers because
the stamp has no setter for a container to cooperate through; see
crate::event::mark_hover_orphaned for the full rationale and the
“only when the link was live” invariant this comparison enforces.
The comparison is against the published (root, epoch) pair, not the epoch
alone: per-root counters collide, so an unqualified match would let a pod
of one root end another root’s live hover (see hover_root).
Costs one predictable branch on a u64 field per pod dropped; the
thread-local read happens only for the pod chain that has actually held a
claim at some point.