pub trait AppTree {
Show 27 methods
// Required methods
fn rebuild(&mut self);
fn take_change_flags(&mut self) -> ChangeFlags;
fn has_pending_change_flags(&self) -> bool;
fn is_pointer_captured(&self) -> bool;
fn is_focus_active(&self) -> bool;
fn focus_ime_generation(&self) -> u64;
fn focus_epoch(&self) -> u64;
fn layout(&mut self, logical: Size, text_ctx: &mut dyn Any);
fn paint(
&mut self,
scene: &mut dyn PaintScene,
frame_time: FrameTime,
) -> PaintOutcome;
fn event(&mut self, event: &InputEvent) -> EventOutcome;
fn ime_apply(&mut self, state: EditingState) -> EventOutcome;
fn ime_state(&self) -> Option<ImeState>;
fn take_clipboard_write(&mut self) -> Option<String>;
fn take_paste_request(&mut self) -> bool;
fn set_theme(&mut self, theme: Box<dyn Any>);
fn semantics(&mut self) -> SemanticsUpdate;
fn semantics_generation(&self) -> u64;
fn semantics_if_changed(
&mut self,
last_seen: u64,
) -> Option<SemanticsUpdate>;
fn perform_accessibility_action(
&mut self,
node_id: u64,
action: Action,
) -> EventOutcome;
// Provided methods
fn selection_toolbar(&self) -> Option<SelectionToolbarRequest> { ... }
fn selection_toolbar_generation(&self) -> u64 { ... }
fn set_insets(&mut self, _insets: WindowInsets) { ... }
fn set_presented_frames(&mut self, _presented: u64) { ... }
fn set_surface_translucent(&mut self, _translucent: bool) { ... }
fn platform_view_frames(&self) -> &[PlatformViewFrame] { ... }
fn input_shields(&self) -> &[Rect] { ... }
fn take_retired_platform_views(&mut self) -> Vec<u64> { ... }
}Expand description
Type-erased app tree: the one seam that lets a shell’s native handle stay
non-generic while still driving a concrete State/app_logic/View.
Mirrors the desktop facade’s erasure approach (a stored generic behind a
non-generic driver): a platform shell’s generated extern entry points can’t
be generic, so the shell’s app-binding macro instantiates new_boxed_app
with the app’s types and stores the result as a Box<dyn AppTree> inside the
handle.
Required Methods§
Sourcefn take_change_flags(&mut self) -> ChangeFlags
fn take_change_flags(&mut self) -> ChangeFlags
Take (and clear) the layout/paint dirtiness accumulated since the last
call (delegates to RenderRoot::take_change_flags).
The frame-gate seam: a shell drives the layout-skip
decision off this — run AppTree::rebuild, then run AppTree::layout
only if the drained flags ChangeFlags::needs_layout (or it’s the
first frame, or the surface resized), then always AppTree::paint.
The set_theme ⇒ LAYOUT|PAINT contract keeps Text’s layout-baked
theme color correct across a bare theme swap (see
crate::frame_gate’s module docs). Draining is the caller’s commitment
to act on the flags this frame; use
AppTree::has_pending_change_flags to peek without draining when
gathering crate::FrameInputs for a frame that may be skipped.
Sourcefn has_pending_change_flags(&self) -> bool
fn has_pending_change_flags(&self) -> bool
Non-draining peek at whether any layout/paint dirtiness is pending
(delegates to RenderRoot::has_pending_change_flags).
The frame gate reads this as its change_flags_pending
crate::FrameInputs entry before deciding, so a frame it skips
leaves the flags intact for the next frame that runs to drain via
AppTree::take_change_flags.
Sourcefn is_pointer_captured(&self) -> bool
fn is_pointer_captured(&self) -> bool
Whether a captured pointer gesture is currently in flight (delegates to
RenderRoot::is_pointer_captured).
A frame-gate input: a mid-drag captured widget may
track/animate the pointer, so the mobile shells feed this into
crate::FrameInputs::pointer_capture_active to keep producing frames
while a gesture is live rather than skipping it.
Sourcefn is_focus_active(&self) -> bool
fn is_focus_active(&self) -> bool
Whether some widget in the tree currently holds keyboard/IME focus
(delegates to RenderRoot::is_focus_active).
A level read, used by a shell that needs the current state (an IME
reconcile, a caret decision). It is deliberately not what the mobile
frame gate consults any more — a focus session that lasts forces a frame
forever — see AppTree::focus_ime_generation.
Sourcefn focus_ime_generation(&self) -> u64
fn focus_ime_generation(&self) -> u64
The focus/IME session generation, bumped on every actual change of the
root’s focus flag or published IME surface (delegates to
RenderRoot::focus_ime_generation).
The frame gate’s edge input: a shell caches the value it last saw and
feeds last != now into
crate::FrameInputs::focus_or_ime_changed, so a focus/IME transition
forces exactly one frame while a steady focus session (a caret blinking
in an otherwise-idle field) leaves the gate free to skip or pace. The
same cheap compare-a-generation shape as
AppTree::semantics_generation.
Deliberately not defaulted, unlike AppTree::set_insets and the
other additive methods below: any constant default (0 included) would
report “nothing ever changed” and silently strand a focus transition,
against the frame gate’s default-to-run rule.
This is not the session’s identity, and the distinction is the whole
reason AppTree::focus_epoch sits beside it — read that one’s doc
before using this counter to decide whether focus is still where it was.
Sourcefn focus_epoch(&self) -> u64
fn focus_epoch(&self) -> u64
The live focus session’s identity, advanced once per honoured focus
claim and once per session release (delegates to
RenderRoot::focus_epoch).
Two adjacent generation counters invite exactly one mistake, so: the neighbour above counts changes to the published surface, this one counts sessions, and neither substitutes for the other.
- Focus moving from one field to another moves this one and can leave
the neighbour completely still. Claiming focus while some field is
already focused writes
trueovertrue, andImeStateis{active, editing, caret, content_type}— it names no widget, so two fields can publish equal surfaces, and a field that takes focus and publishes nothing leaves the previous field’s surface standing. - An edit, a caret move, or the field being repositioned under the user moves the neighbour and leaves this one still. The session is the same session throughout.
So a shell that must run a frame or re-sync the platform IME reads the neighbour; a shell binding an answer it will receive later to the session that asked for it — an off-thread clipboard read, say — reads this one and compares it again on arrival.
Never 0, whatever a shell’s own FFI layer may use 0 to mean: the
counter is built at 1 and steps past 0 on wrap.
Not defaulted, for a sharper reason than its neighbour’s: a constant default would report “still the same session” forever, so every stale answer would compare equal and be accepted. A wrong default here delivers text into the wrong field rather than costing a frame.
Sourcefn layout(&mut self, logical: Size, text_ctx: &mut dyn Any)
fn layout(&mut self, logical: Size, text_ctx: &mut dyn Any)
Lay the tree out against a logical (density-independent) size, threading
the shell-owned TextContext down type-erased.
Sourcefn paint(
&mut self,
scene: &mut dyn PaintScene,
frame_time: FrameTime,
) -> PaintOutcome
fn paint( &mut self, scene: &mut dyn PaintScene, frame_time: FrameTime, ) -> PaintOutcome
Paint the tree into a scene builder at the shell-provided frame_time.
frame_time is the shell’s shared monotonic clock for this frame, threaded
through to every animating widget as frust_core::widget::PaintCtx::frame_time
(time enters from the shell, never Instant::now() inside the
framework).
Returns a PaintOutcome whose needs_frame is set when a widget
advanced animation state during paint and wants another frame, the
framework’s animation seam. The desktop shell honors it with window.request_redraw();
the mobile shells’ continuous Choreographer/CADisplayLink loops already
produce the next frame and may ignore it.
Sourcefn event(&mut self, event: &InputEvent) -> EventOutcome
fn event(&mut self, event: &InputEvent) -> EventOutcome
Deliver one platform input event to the retained tree.
Delegates to RenderRoot::event, threading the erased State the same
way AppTree::rebuild does. The returned EventOutcome carries
needs_redraw, which the shell honours by scheduling a frame: the desktop
shell calls window.request_redraw(), while the mobile shells’ continuous
Choreographer/CADisplayLink loops already produce the next frame. The
event pass itself never rebuilds or repaints (see RenderRoot::event).
Sourcefn ime_apply(&mut self, state: EditingState) -> EventOutcome
fn ime_apply(&mut self, state: EditingState) -> EventOutcome
Apply a whole editing state pushed by the platform IME (the mobile
state-sync path), routed to the focused widget as an
InputEvent::Ime(ImeEvent::ApplyEditingState).
state’s selection/composing indices are UTF-16 code-unit based (the
platform-native unit); the focused widget / frust-text converts them
to Rust byte offsets. Returns the same EventOutcome as AppTree::event.
Sourcefn ime_state(&self) -> Option<ImeState>
fn ime_state(&self) -> Option<ImeState>
The IME surface the focused widget published, for the shell to drive the
platform input method. Delegates to RenderRoot::ime_state; None when
nothing is focused or no IME surface was published.
Sourcefn take_clipboard_write(&mut self) -> Option<String>
fn take_clipboard_write(&mut self) -> Option<String>
Take (and clear) the text a widget asked to put on the host clipboard
(delegates to RenderRoot::take_clipboard_write).
The shell half of the clipboard channel: a focused editable answers a
copy/cut by writing its selection into the pass’s clipboard slot
(frust_core::EventCtx::write_clipboard), and the shell — the only side
with a host clipboard to talk to — drains it here immediately after
every AppTree::event/AppTree::ime_apply, beside
AppTree::ime_state. None means no widget copied, and a shell with no
clipboard wired yet may simply not call this.
Destructive, unlike the level reads around it: a clipboard write is an edge, so a caller that drains and drops the result loses that write.
Sourcefn take_paste_request(&mut self) -> bool
fn take_paste_request(&mut self) -> bool
Take (and clear) whether a widget asked the shell to read the host
clipboard back to it (delegates to RenderRoot::take_paste_request).
The inverse direction, drained in the same place: on true the shell reads
its host clipboard and dispatches
InputEvent::EditCommand(EditCommand::Paste(text))
back through AppTree::event — a new dispatch, since the read may be
asynchronous. That answer is focus-routed and therefore self-cancelling: if
focus moved or was released while the read was in flight it reaches no
widget and is dropped, so the shell never has to track who asked.
Destructive, for AppTree::take_clipboard_write’s reason.
Sourcefn set_theme(&mut self, theme: Box<dyn Any>)
fn set_theme(&mut self, theme: Box<dyn Any>)
Store the app’s active theme, threaded into every subsequent
layout/paint pass (delegates to RenderRoot::set_theme).
The theme is type-erased (Box<dyn Any>) so frust-shell-common
stays free of a frust-theme dependency (it compiles everywhere with
no unsafe/FFI — see docs/ARCHITECTURE.md). The concrete Theme is
boxed by the shell that owns the appearance state; each mobile shell
wires this into its own appearance-change handling. Re-boxing on a
live appearance change replaces the stored theme.
Sourcefn semantics(&mut self) -> SemanticsUpdate
fn semantics(&mut self) -> SemanticsUpdate
Collect the accessibility tree for the current frame,
for a shell to push into its platform accesskit_* adapter. Delegates to
RenderRoot::semantics; must run after AppTree::layout so node
bounds are valid.
Sourcefn semantics_generation(&self) -> u64
fn semantics_generation(&self) -> u64
The current semantics generation, bumped whenever a rebuild/theme swap
could have changed the tree (delegates to
RenderRoot::semantics_generation). A shell compares it to skip
re-pushing an unchanged tree — see AppTree::semantics_if_changed.
Sourcefn semantics_if_changed(&mut self, last_seen: u64) -> Option<SemanticsUpdate>
fn semantics_if_changed(&mut self, last_seen: u64) -> Option<SemanticsUpdate>
Pull a fresh SemanticsUpdate only if the tree may have changed since
generation last_seen (delegates to RenderRoot::semantics_if_changed),
so a shell’s adapter push runs only when something changed.
Sourcefn perform_accessibility_action(
&mut self,
node_id: u64,
action: Action,
) -> EventOutcome
fn perform_accessibility_action( &mut self, node_id: u64, action: Action, ) -> EventOutcome
Perform a platform accessibility action delivered by the shell’s
accesskit_* adapter (an ActionRequest), threading the erased State
the same way AppTree::event does (delegates to
RenderRoot::perform_accessibility_action).
node_id is the raw accesskit id the adapter reported; action is the
requested accesskit::Action. Returns the same EventOutcome as
AppTree::event — its needs_redraw tells the shell whether to schedule
a frame. An unknown node or unmodelled action is a benign no-op.
Provided Methods§
Sourcefn selection_toolbar(&self) -> Option<SelectionToolbarRequest>
fn selection_toolbar(&self) -> Option<SelectionToolbarRequest>
The selection-toolbar request the focused field published during the most
recent AppTree::paint (delegates to
RenderRoot::selection_toolbar); None when no field has a selection
worth a toolbar.
The shell half of the platform edit-menu route: on a host with a
system edit menu (iOS UIEditMenuInteraction), a shell reads this beside
AppTree::ime_state and presents the host menu at the request’s anchor
rect, converting the logical rect to the platform’s own units itself. A
level, not an edge — pair it with
AppTree::selection_toolbar_generation to notice changes cheaply.
A field drawing its own toolbar publishes this too (one code path for
both routes), so a shell must gate on
frust_core::selection_toolbar::selection_toolbar_policy(), not on the
presence of a request.
Some means a field is FOCUSED, not that it has a selection. The verbs
are a level a platform responder chain reads at any moment, so they are
published on every paint of a focused field; None means no field is
focused. Whether a menu should be presented is the request’s own flag,
not the presence of the request.
Defaulted to None like the getters below, so an AppTree impl
that predates this channel still compiles and reads an empty one.
Sourcefn selection_toolbar_generation(&self) -> u64
fn selection_toolbar_generation(&self) -> u64
A monotonically-increasing generation bumped when the menu-significant
part of AppTree::selection_toolbar actually changes — the present flag
and the verb set, its clearing included — and NOT when the anchor alone
moves (delegates to RenderRoot::selection_toolbar_generation).
The anchor exclusion is deliberate: the anchor is recomputed every painted frame and follows a dragging selection, so moving the generation with it would ask the host to re-present its menu on every touch sample. Re-read the anchor as a level for the menu’s target rect; do not treat it as a reason to present.
The AppTree::focus_ime_generation contract one channel over: a shell
caches the last value it acted on and re-presents the host menu only when
it moves, which is what keeps a standing selection — republished every
frame it stands — from re-presenting the menu on every vsync.
Defaulted to 0, the same additive shape as its neighbour above.
Sourcefn set_insets(&mut self, _insets: WindowInsets)
fn set_insets(&mut self, _insets: WindowInsets)
Store the window’s insets (WindowInsets), threaded into every
subsequent layout/paint pass (delegates to RenderRoot::set_insets).
A shell reads the platform’s per-edge occlusion (Android WindowInsets,
iOS safeAreaInsets + keyboard frame), converts device px to logical
px at the FFI boundary (see
crate::ffi_support::logical_insets), and pushes
the result here; a SafeArea widget then insets by
WindowInsets::padding. WindowInsets is a concrete core-owned type
(only f64 scalars), so this needs no Box<dyn Any> erasure — unlike
AppTree::set_theme.
Defaulted to a no-op so existing AppTree impls compile unchanged;
the concrete tree overrides it to forward to RenderRoot::set_insets.
The set_insets ⇒ LAYOUT | PAINT dirty contract keeps a SafeArea’s
layout-time inset resolution correct under the mobile layout-skip gate,
exactly like set_theme (see crate::frame_gate’s module docs).
Sourcefn set_presented_frames(&mut self, _presented: u64)
fn set_presented_frames(&mut self, _presented: u64)
Store the shell’s running count of frames the render thread has actually
presented, threaded into every subsequent paint pass (delegates to
RenderRoot::set_presented_frames).
A shell loads the atomic its render side increments (once per presented
frame) and pushes it here once per UI frame, before AppTree::paint, so
a widget measuring FPS reports the presented rate rather than its own
paint cadence (which, under the render-thread split, runs faster).
Unlike AppTree::set_theme/AppTree::set_insets, this dirties
nothing — RenderRoot::set_presented_frames marks no ChangeFlags,
so a monotonically ticking counter never forces a relayout and — the
subtle one — never keeps the mobile frame_gate’s
pending-flags input perpetually true, so the menu still idles.
Defaulted to a no-op so existing
AppTree impls compile unchanged; the concrete tree overrides it.
Sourcefn set_surface_translucent(&mut self, _translucent: bool)
fn set_surface_translucent(&mut self, _translucent: bool)
Store whether the shell’s GPU surface is translucent (alpha-channel,
“Mode B”), threaded into every subsequent paint pass (delegates to
RenderRoot::set_surface_translucent).
A shell pushes the surface’s resolved translucency here — what
frust_render::SurfaceRenderer::surface_resolved_translucent reports
after an install, NOT the crate::SurfaceModeWatcher request latch
(a translucency request the platform refuses must
degrade to the opaque Mode A contract, or the punch presents black
rectangles). Both mobile shells re-read it every frame, so a
render-thread fallback downgrades within one frame. The platform-view
hole-punch then clears each slot’s rect on a genuinely translucent
surface so an opaque app backdrop doesn’t seal the hole (see
RenderRoot::set_surface_translucent). Desktop leaves the default
(opaque). Defaulted to a no-op so existing AppTree impls compile
unchanged; the concrete tree overrides it — mirrors
AppTree::set_insets’s default-no-op precedent.
Sourcefn platform_view_frames(&self) -> &[PlatformViewFrame]
fn platform_view_frames(&self) -> &[PlatformViewFrame]
The PlatformViewFrames the tree published during the most recent
paint pass (delegates to
RenderRoot::platform_view_frames). A shell’s peek-getter path feeds
this into a crate::platform_view::PlatformViewState’s
ingest after each
RUN frame’s paint (never on a gate-Skip, per that method’s
skip-safety contract).
Defaulted to an empty slice so existing AppTree impls compile
unchanged; the concrete tree overrides it — mirrors
AppTree::set_insets’s default-no-op precedent.
Sourcefn input_shields(&self) -> &[Rect]
fn input_shields(&self) -> &[Rect]
The z-shield rects the tree reported during the most recent paint pass
(delegates to RenderRoot::input_shields) — the
second argument of the same
ingest call
AppTree::platform_view_frames feeds.
Defaulted to an empty slice like the frames getter above, so an
AppTree impl that predates the shield channel still compiles (and
simply ships no auto-collected shields).
Sourcefn take_retired_platform_views(&mut self) -> Vec<u64>
fn take_retired_platform_views(&mut self) -> Vec<u64>
Drain the slot ids whose platform_view widgets were torn down since the
last call (delegates to
RenderRoot::take_retired_platform_views).
A shell calls this right after AppTree::rebuild and retires each id
in its PlatformViewState, so
a disposed slot’s native view goes away on the next frame instead of
waiting out the differ’s missing-streak heuristic. Draining is
destructive — an id is reported exactly once.
Defaulted to an empty Vec, mirroring the two getters above.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".