pub struct EventContext<'ops> { /* private fields */ }Expand description
Context available during event handling.
Implementations§
Source§impl<'ops> EventContext<'ops>
impl<'ops> EventContext<'ops>
Sourcepub fn is_pressed(&self) -> bool
pub fn is_pressed(&self) -> bool
Whether the pointer being dispatched holds a press whose visual is showing — inside its tap boundary and past any press-feedback delay.
The framework already drives the pressed node’s own
pressed_signal from the same
state; this is for a handler that has to branch on the press rather
than paint it. false outside a press.
Sourcepub fn press_is_inside(&self) -> bool
pub fn press_is_inside(&self) -> bool
Whether the pointer being dispatched holds a press that has not left
its tap boundary. Unlike is_pressed this is still
true during the press-feedback delay: the press is real, only its
visual is being withheld.
Sourcepub fn press_pending(&self) -> bool
pub fn press_pending(&self) -> bool
Whether the pointer being dispatched holds a press whose feedback delay has not elapsed — the press is inside a pan claimant and the framework is waiting to see whether it becomes a scroll.
Sourcepub fn pointer(&self) -> PointerInfo
pub fn pointer(&self) -> PointerInfo
The pointer that produced the event being handled.
Two dispatches have a pointer without having a sample, and both report
it: a gesture the timer recognised — a hold — reports the contact that
held, and a drag-and-drop handler (on_drag_hover / on_drag_tick /
on_drag_leave / on_drop) reports the pointer that started the
drag, which is what makes it right inside a tick fired from a layout
pass or an OS drag phase delivered from a platform thread. Outside any
pointer, scroll, gesture or drag dispatch — an assistive-technology
action, a hand-constructed test context — this is the mouse at the tree
epoch, which is the same answer every such handler got before pointers
were distinguishable.
Sourcepub fn pointer_kind(&self) -> PointerKind
pub fn pointer_kind(&self) -> PointerKind
What kind of device is pointing: mouse, finger, stylus.
The one question most handlers actually need — it is what decides whether a hover affordance is reachable, whether a target needs slop, and which gesture profile governs.
Sourcepub fn pointer_position(&self) -> Option<Point>
pub fn pointer_position(&self) -> Option<Point>
Where the pointer was, in window-logical coordinates, when the event being handled was produced.
None for an event that carries no position — a keyboard-driven
scroll, a wheel notch (which routes by hover rather than by position),
anything dispatched outside a pointer sample. Distinct from
tree_pointer_position, which reports
where the pointer is at this instant regardless of what is being
dispatched.
Sourcepub fn coalesced(&self) -> &[CoalescedSample]
pub fn coalesced(&self) -> &[CoalescedSample]
The positions the OS batched into the packet being dispatched, oldest
first and excluding the packet’s own
(pointer_position, which is the newest).
A backend whose device outruns the window’s message rate hands over one
sample per message and puts the intermediate positions here, each with
the PointerAxes it was sampled at and
its own EventTime. A surface that must
see every position a stylus produced — an ink tool is the case this
exists for — iterates these and then handles
pointer_position, and is correct whether or
not the backend batches, because a backend that does not returns an
empty slice.
Window-logical coordinates, like
WidgetEvent::Scroll’s window_position and
for the same reason: a batch has no single widget to localise against.
A handler working in its own space converts at the use site.
Empty on every dispatch that is not a pointer sample — a gesture the timer recognised, a drag-and-drop tick, an assistive-technology action. Those batched nothing, and reporting whichever sample arrived last would attribute its positions to a gesture that did not produce them.
Sourcepub fn scroll_phase(&self) -> ScrollPhase
pub fn scroll_phase(&self) -> ScrollPhase
Where in a continuous scroll gesture the event being handled sits.
ScrollPhase::Discrete — a
self-contained wheel notch — for everything that is not a phased
gesture, which is every scroll Teksilo produced before the touch
programme.
Sourcepub fn scroll_source(&self) -> ScrollSource
pub fn scroll_source(&self) -> ScrollSource
What produced the scroll being handled: a notched wheel, a precision trackpad, a synthesised touch pan, or the app itself.
Sourcepub fn touch_action(&self) -> TouchAction
pub fn touch_action(&self) -> TouchAction
The TouchAction governing the gesture being handled.
The value is frozen at press for the whole gesture’s lifetime: the
router computes it once, from WidgetTree::effective_touch_action of
the pressed target, and stores it on that pointer’s
PointerSequence, so a handler never
re-reads a subtree that may have rebuilt mid-gesture.
TouchAction::AUTO — the neutral value — outside a press, and for a
hand-constructed context. A mouse never consults this at all. See
crate::pointer::touch_action.
Sourcepub fn layout_direction(&self) -> LayoutDirection
pub fn layout_direction(&self) -> LayoutDirection
Layout direction of the hosting tree at dispatch time.
Sourcepub fn is_rtl(&self) -> bool
pub fn is_rtl(&self) -> bool
Whether the hosting tree is laid out right-to-left. Mirrors
LayoutContext::is_rtl
for the event-dispatch side.
Sourcepub fn focused(&self) -> Option<WidgetId>
pub fn focused(&self) -> Option<WidgetId>
The widget that held focus when this event batch began.
A snapshot, not a live read: it answers what the tree’s focus was at
dispatch time, so a handler that has already called
request_focus still sees the old
value. That is the useful reading for a handler deciding whether to
act on the focused widget.
None when nothing is focused, and also for an EventContext built
outside WidgetTree::make_event_context, which is what a hand-made
test context is. Treat it as None-safe, like the other snapshots.
The reason this exists: a widget-scoped shortcut fires before the
focused widget sees the key, so a container that binds a key which its
own children also handle has no other way to yield to them.
MessageBox is the case that asked for it, where Enter is bound to the
default button and must not answer for the button the user has actually
tabbed to.
Sourcepub fn drag_is_external(&self) -> bool
pub fn drag_is_external(&self) -> bool
Whether a drag is currently in flight that was started by an external
(OS) drag-and-drop (files / text / URLs from another application),
rather than by an in-app start_drag. Useful in on_drag_leave /
on_drag_tick handlers, which don’t receive the payload directly;
in on_drag_hover / on_drop prefer payload.is_external().
Sourcepub fn app_state<T: 'static>(&self) -> Option<&T>
pub fn app_state<T: 'static>(&self) -> Option<&T>
Look up an application-scoped value by type. Mirrors
BuildContext::app_state. Returns None when the handler was
invoked without a registry (hand-constructed EventContext in
tests, or when no value of that type was registered).
Sourcepub fn poster(&self) -> Option<&Arc<dyn AppEventPoster>>
pub fn poster(&self) -> Option<&Arc<dyn AppEventPoster>>
Borrow the AppEventPoster installed
by the framework. Used by integrations that need to post
typed payloads back to the UI loop from a worker thread
(teksilo_platform::file_dialog’s RfdAsyncBackend, future
async-result features). Returns None for hand-constructed
EventContexts in tests.
Sourcepub fn request_frame(&mut self)
pub fn request_frame(&mut self)
Ask the tree to pump one more frame after this handler returns.
Use from event handlers that kick off per-frame work (pending
document events to drain, drag-select auto-scroll, caret blink
restart on focus). See WidgetTree::request_frame for the
draw-when-needed contract.
Sourcepub fn send_intent(&mut self, intent: impl Into<Intent>)
pub fn send_intent(&mut self, intent: impl Into<Intent>)
Dispatch an Intent as if the source
widget pressed its keyboard shortcut. The framework walks
source-widget → root after the current handler returns,
invoking any matching Action it
finds. Unmatched intents are silently dropped.
The intent’s source is overridden by the dispatcher’s
current handler-source label (current_source) when one is
active. This is how the framework distinguishes
IntentSource::Handler (button taps, generic on_tap) from
IntentSource::Menu, IntentSource::Accessibility, etc.
Programmatic callers outside any handler pass through with
IntentSource::Programmatic (the default).
Sourcepub fn with_intent_source<R>(
&mut self,
source: IntentSource,
f: impl FnOnce(&mut Self) -> R,
) -> R
pub fn with_intent_source<R>( &mut self, source: IntentSource, f: impl FnOnce(&mut Self) -> R, ) -> R
Run a closure with the given IntentSource active. Any
ctx.send_intent(...) issued from within the closure will
be tagged with this source instead of the dispatcher’s
default (Handler / Shortcut / Accessibility).
The previous source is restored after the closure returns. Panic during the closure unwinds the dispatcher’s whole frame, so the EventContext is destroyed before the next dispatch — no need for a panic-safe drop guard.
Used by framework widgets that want a more specific source
label than the default — MenuItem wraps its activation
handler to emit IntentSource::Menu, etc.
Sourcepub fn begin_key_capture(
&mut self,
callback: impl FnOnce(KeyStroke, &mut ShortcutRegistry, &mut EventContext<'_>) + 'static,
) -> CaptureHandle
pub fn begin_key_capture( &mut self, callback: impl FnOnce(KeyStroke, &mut ShortcutRegistry, &mut EventContext<'_>) + 'static, ) -> CaptureHandle
Arm a one-shot key-capture callback, returning a
CaptureHandle whose Drop
cancels the capture if it hasn’t fired yet. The next KeyDown
bypasses shortcut resolution and invokes the callback with:
- the captured
KeyStroke - mutable access to the registry (rebinds in-place)
- a mutable
EventContext(emit commands, send intents, dismiss overlays, …)
The handle must be stored somewhere with an appropriate lifetime (typically in the calling widget’s state) or the capture will be cancelled immediately when the returned handle drops at end of scope.
Sourcepub fn cancel_key_capture(&mut self)
pub fn cancel_key_capture(&mut self)
Cancel any key capture armed earlier in this handler or via
WidgetTree::begin_key_capture before the handler ran.
Sourcepub fn rebind_shortcut_primary(
&mut self,
id: impl Into<String>,
keystroke: Option<KeyStroke>,
)
pub fn rebind_shortcut_primary( &mut self, id: impl Into<String>, keystroke: Option<KeyStroke>, )
Queue a deferred rebind of the primary keystroke for the
registered shortcut with the given id. Applied by the tree
after the current handler returns. Use None to explicitly
unbind the slot.
Sourcepub fn rebind_shortcut_secondary(
&mut self,
id: impl Into<String>,
keystroke: Option<KeyStroke>,
)
pub fn rebind_shortcut_secondary( &mut self, id: impl Into<String>, keystroke: Option<KeyStroke>, )
Queue a deferred rebind of the secondary keystroke for the registered shortcut with the given id.
Sourcepub fn clear_shortcut_override(&mut self, id: impl Into<String>)
pub fn clear_shortcut_override(&mut self, id: impl Into<String>)
Queue a deferred clear of any user override for the given shortcut id, restoring its declared defaults.
Sourcepub fn close_window(&mut self)
pub fn close_window(&mut self)
Request that the application close the window this tree belongs to. Drained by the app event loop after the handler returns. Typical use: title-bar close button handlers.
This is a guarded close: if the window declared a close guard
via
WindowConfig::on_close_requested
or can_close, that
guard runs first and may veto the close. To skip the guard (e.g.
from the confirmation dialog the guard itself opened), use
close_window_forced.
Sourcepub fn close_window_forced(&mut self)
pub fn close_window_forced(&mut self)
Request that the application close this tree’s window
unconditionally, bypassing any close guard declared via
WindowConfig::on_close_requested
/ can_close.
This is the second half of the veto-then-reissue pattern: the
guard returns CloseResponse::Veto
and opens a confirmation dialog; the dialog’s “close anyway”
button calls close_window_forced so the window actually closes
without re-triggering the guard.
Sourcepub fn tree_pointer_position(&self) -> Option<Point>
pub fn tree_pointer_position(&self) -> Option<Point>
The WindowState for the window
hosting this handler. None only for handlers run outside
of an app (hand-constructed EventContext in tests).
Cursor position at the moment this handler was invoked. None
when no PointerMove has reached the tree yet, or when the
context was constructed without a tree-side snapshot (e.g.
hand-built EventContexts in tests). Used by the safe-triangle
submenu hover gate.
Sourcepub fn press_claimed_by_interactive_child(&self) -> bool
pub fn press_claimed_by_interactive_child(&self) -> bool
True when the in-flight pointer press’s hit target is a strict
descendant of THIS handler’s widget that carries its own tap gesture
(chevron, checkbox, inline button). A row/container that selects on
press should early-return EventResponse::Ignored when this is set, so
the press belongs to the inner control, not the row. Only meaningful
inside on_pointer_event handlers for PointerDown/PointerUp.
Sourcepub fn dispatch_target(&self) -> Option<WidgetId>
pub fn dispatch_target(&self) -> Option<WidgetId>
The node this dispatch is addressed to — the innermost node the
arena’s hit walk accepted for this pointer sample, with
hit_transparent,
Widget::hit_shape,
Widget::accepts_child_hit
and event_pass_through all already applied.
The point of it is the preview pass. on_pointer_event fires on
every strict ancestor of the target during preview and on the target
itself during the bubble, so an ancestor handler cannot otherwise tell
“a descendant won the walk” from “I am the one that was hit”. A widget
that owns a second picking system over the same area needs exactly that
distinction, to know whether to yield: comparing this against its own id
reads the arena’s verdict instead of re-deriving it from a rectangle,
which is what keeps the two pickers from answering in different orders.
None for a context made outside pointer dispatch (a gesture timer, a
key-capture callback, an async completion). Treat None as “no verdict
available”, not as “not the target”.
Sourcepub fn overlay_bounds_for_content(&self, content_id: WidgetId) -> Option<Rect>
pub fn overlay_bounds_for_content(&self, content_id: WidgetId) -> Option<Rect>
Look up the bounds rect of an open overlay by its root content
widget id. Returns None when no such overlay is currently
active. The snapshot is taken once per dispatch; mid-handler
show_overlay calls will not appear here. Used by the
safe-triangle submenu hover gate.
Sourcepub fn overlay_safe_region_armed(&self, content_id: WidgetId) -> bool
pub fn overlay_safe_region_armed(&self, content_id: WidgetId) -> bool
Whether a safe-triangle traversal toward the overlay rooted at
content_id is still live — i.e. whether the user may still be on
their way to that submenu.
A widget whose hover would otherwise tear the overlay down (a
sibling menu row switching the selection) asks this first and
stands aside while it is true, leaving the dismissal to the
overlay’s own pointer-leave grace — which tests the cone on every
sample and closes the overlay one delay after the pointer stops
heading there.
This is deliberately the armed window, not a point-in-cone test. A sibling row’s hover fires exactly once, at the instant the pointer crosses onto it — a pixel or two from the apex, where the cone is a needle — so answering “is this one sample inside the cone” made a single quantized step final, and any departure steeper than the cone (which is most of them, for a wide menu with a short submenu) killed the submenu the moment the pointer left the trigger row. Whether this sample is inside the cone is the framework’s question, asked continuously; the widget’s question is only whether to get out of the way.
false when no region is armed and when its budget is spent.
Arm the region with
arm_overlay_safe_region.
pub fn window(&self) -> Option<&WindowState>
Sourcepub fn window_active(&self) -> bool
pub fn window_active(&self) -> bool
Whether the host window is currently active (focused AND not occluded) — the occlusion-aware companion to
self.window().map(|w| w.focused()) (raw OS focus). Snapshotted at
context construction. Matches BuildContext::window_active.
Sourcepub fn open_window(&mut self, config: WindowConfig) -> TeksiloWindowId
pub fn open_window(&mut self, config: WindowConfig) -> TeksiloWindowId
Open a new window, creating the winit-level surface
synchronously. The returned id is immediately valid for
focus_window,
window_state, and
find_window.
Panics when called from a handler on a standalone WidgetTree
(no app context) — tests should not invoke this method.
Sourcepub fn find_window(&self, string_id: &str) -> Option<TeksiloWindowId>
pub fn find_window(&self, string_id: &str) -> Option<TeksiloWindowId>
Find a window by the string id assigned via
WindowConfig::id. Returns
None if no open window carries that id.
Sourcepub fn window_state(&self, id: TeksiloWindowId) -> Option<WindowState>
pub fn window_state(&self, id: TeksiloWindowId) -> Option<WindowState>
Read the WindowState for a
specific window.
Sourcepub fn windows(&self) -> Vec<WindowState>
pub fn windows(&self) -> Vec<WindowState>
Snapshot of every live window’s state.
Sourcepub fn focus_window(&mut self, id: TeksiloWindowId)
pub fn focus_window(&mut self, id: TeksiloWindowId)
Raise a window to the front and give it keyboard focus.
Sourcepub fn request_activation_token(
&mut self,
id: TeksiloWindowId,
cb: Box<dyn FnOnce(Option<String>)>,
)
pub fn request_activation_token( &mut self, id: TeksiloWindowId, cb: Box<dyn FnOnce(Option<String>)>, )
Request an xdg-activation token for id (see
WindowOps::request_activation_token).
cb fires once with the token string, or None where the platform can’t
provide one — used to hand a token to a child process (“open in new
window”) or an IPC peer that will raise itself on Wayland.
Sourcepub fn request_activation_token_self(
&mut self,
cb: Box<dyn FnOnce(Option<String>)>,
)
pub fn request_activation_token_self( &mut self, cb: Box<dyn FnOnce(Option<String>)>, )
Request an activation token for the current window (see
WindowOps::request_activation_token_self).
Use this from a widget handler to mint a token from this focused window
to hand to another window or process — it works mid-dispatch, unlike the
id-based variant.
Sourcepub fn close_window_by_id(&mut self, id: TeksiloWindowId)
pub fn close_window_by_id(&mut self, id: TeksiloWindowId)
Close a specific window by id. Equivalent to
close_window when id is the current
window’s id.
Sourcepub fn set_ime_cursor_area(&mut self, area: Rect)
pub fn set_ime_cursor_area(&mut self, area: Rect)
Report the focused text widget’s caret rectangle (window-logical pixels) so the platform can position the OS IME candidate window at the insertion point. Text-editing widgets call this whenever the caret moves. No-op outside a dispatch / on a standalone tree.
Sourcepub fn parent_window_handle(&self) -> Option<ParentHandle>
pub fn parent_window_handle(&self) -> Option<ParentHandle>
Resolve the platform parent handle of the window currently
dispatching the event. Used by native-dialog integrations
(teksilo_platform::file_dialog) to parent OS dialogs to the
originating Teksilo window.
Returns None when called from a standalone WidgetTree (no
app-level WindowOps sink), or when the platform refuses to
surface a handle (rare; mostly during teardown).
Sourcepub fn set_cursor(&mut self, cursor: CursorIcon)
pub fn set_cursor(&mut self, cursor: CursorIcon)
Request a cursor icon change.
This is an override: it outranks the cursor the hovered node
declared with WidgetBuilder::cursor, and it outlives the dispatch
that set it. The tree’s cursor moves only when something writes to it,
and the node-declared cursor is written on PointerEnter /
PointerLeave alone — so a handler that sets a cursor while the
pointer is inside a node owns the cursor until the pointer leaves that
node, or until the handler gives it back with
release_cursor.
Sourcepub fn release_cursor(&mut self)
pub fn release_cursor(&mut self)
Withdraw this handler’s cursor override, so the cursor the hovered node declared applies again.
The cursor counterpart of returning EventResponse::Ignored: it says
“I have nothing to say about the cursor”, which is not the same as
saying CursorIcon::Default and not the same as staying silent.
Silence is only safe for a handler that has never spoken. A handler which re-decides the cursor on every move — a scene arbitrating a lightweight item’s cursor against the card underneath it, a chart’s overlay marks, a terminal’s link layer — reaches points where it has no answer while the pointer is still inside the same node. No hover transition fires there, so nothing re-applies the node’s declared cursor, and going quiet leaves the handler’s last word standing. This is how it takes that word back.
Restores exactly what the PointerEnter walk resolved for the current
hover chain (or CursorIcon::Default if that chain declared none),
so it can never disagree with the declared-cursor mechanism it defers
to. Calling it when nothing was overridden is a no-op.
Sourcepub fn set_dormant(&mut self, id: WidgetId)
pub fn set_dormant(&mut self, id: WidgetId)
Set a widget subtree as dormant (preserves state, releases rendering).
Sourcepub fn destroy(&mut self, id: WidgetId)
pub fn destroy(&mut self, id: WidgetId)
Destroy a widget subtree (removes from arena entirely, state is gone).
Sourcepub fn with_widget_mut<W: 'static>(
&mut self,
id: WidgetId,
dirty: BindingLevel,
f: impl FnOnce(&mut W) + 'static,
)
pub fn with_widget_mut<W: 'static>( &mut self, id: WidgetId, dirty: BindingLevel, f: impl FnOnce(&mut W) + 'static, )
Imperatively mutate a mounted widget by id, downcasting to the
concrete type W.
The mutation is deferred: the closure runs after the handler
returns, inside apply_tree_mutations, where the framework holds
&mut arena access (a handler cannot re-borrow the arena to reach
another node, so this is the only safe channel — the same model as
destroy). After the closure runs, the target is
dirty-marked at dirty so the mutation takes visual effect.
The target widget must override Widget::as_any_mut to return
Some(self). If the id is gone or is not a W, the closure is a
no-op in release and a debug_assert failure in debug — it never
silently mutates the wrong widget.
Use it for per-view state a handler can’t otherwise reach — e.g.
SceneView::ensure_visible(...) (camera) after the view is mounted:
ctx.with_widget_mut::<SceneView>(view_id, BindingLevel::Relayout, |v| {
v.ensure_visible(card_rect, 40.0);
});For scene content, prefer the shared SceneModel handle (view.model())
— its mutators are &self, so a handler holding a clone can drive the
scene directly and every attached view reconciles, no with_widget_mut
needed.
Sourcepub fn row_space_activate(
&mut self,
row: WidgetId,
fallback: Rc<dyn Fn(&mut EventContext<'_>)>,
)
pub fn row_space_activate( &mut self, row: WidgetId, fallback: Rc<dyn Fn(&mut EventContext<'_>)>, )
Re-run one widget’s build() during this handler’s drain, before
overlays are shown and before focus requests are applied — rather than
dirty-marking it for the next layout pass, which is what every other
rebuild trigger does.
It exists for one shape:
DeferredSubtree content
that a handler is about to depend on. Opening a popover activates its
content, shows an overlay anchored to it, and moves focus into it — all
three inside the same drain (see collect_from_ctx). A deferred panel
marked for rebuild would not exist yet at any of those points: the
overlay would be measured against an empty node and
first_focusable_descendant would find nothing to focus, so the popover
would open in the wrong place and swallow the keyboard. Materializing
here closes that window, and makes deferred content behave exactly like
the eagerly-built content it replaces.
Cheap to call redundantly: a DeferredSubtree that is already
materialized returns its existing child, so a second open costs one
build() of the host and nothing below it.
Not a general “rebuild this widget now” door — reach for
with_widget_mut or a Rebuild binding for
ordinary reactive updates, which are correctly served by the next
layout pass.
Space on a data view’s focused row: activate the row’s published
keyboard toggle — the checkbox StandardListItem embeds, most often —
or run fallback when the row publishes none.
A row’s controls are out of the Tab order, so this is the only keyboard
route to them; fallback is what Space means on a row without one,
which for the data views is “toggle the selection”.
pub fn materialize_now(&mut self, id: WidgetId)
Sourcepub fn request_accessibility_update(&mut self)
pub fn request_accessibility_update(&mut self)
Request that the AccessKit tree be re-walked after this handler
returns. Use after a mutation that changes the accessibility tree
shape in a way the framework doesn’t already detect (relayout
alone no longer re-walks AT; only events that change the AT tree
— focus, overlays, locale/shortcut rebinds — set the dirty flag).
The companion BuildContext::request_accessibility_update covers
the build-time path.
Sourcepub fn request_soft_keyboard(&mut self)
pub fn request_soft_keyboard(&mut self)
Ask the platform to raise its on-screen keyboard.
For the case the desktop convention has no answer for: a finger landing in a text field, where there is no physical keyboard and no focus change the accessibility layer would notice on its own.
The request is honoured only where it can do no harm. Where the
platform’s keyboard follows the framework’s IME-allowance reconcile
(SoftKeyboardSupport::ViaAccessibility),
the request resolves to nothing — always, not merely while a composition
happens to be live. That reconcile is the request, and the only thing
an explicit ask could add is a re-assertion of IME allowance, which is
what cancels a composition mid-word. Nothing on this path calls
set_ime_allowed, and that is what makes placing a caret with a finger
mid-composition safe. Where the framework has no keyboard request to
send at all the request is dropped; ask
soft_keyboard_support first if the
widget needs to offer a fallback.
Sourcepub fn dismiss_soft_keyboard(&mut self)
pub fn dismiss_soft_keyboard(&mut self)
Ask the platform to dismiss its on-screen keyboard.
Only a platform reporting
SoftKeyboardSupport::Explicit
can honour this; elsewhere there is no dismiss request to send, and a
keyboard that rose on the IME enable goes away on the matching disable
when focus leaves the text surface.
Sourcepub fn soft_keyboard_support(&self) -> SoftKeyboardSupport
pub fn soft_keyboard_support(&self) -> SoftKeyboardSupport
What the host platform can do about an on-screen keyboard.
SoftKeyboardSupport::None
on a standalone tree and on every platform the framework has no keyboard
request to send on — which, on the desktop, is most of them.
Sourcepub fn announce(&mut self, message: impl Into<String>)
pub fn announce(&mut self, message: impl Into<String>)
Speak message to the screen reader, politely.
For anything the user needs told that is not the name of a widget: a completed action, a new count, the result of an undo, a row that moved. Sighted users read those off the screen; a screen-reader user is told only what the framework says out loud.
ctx.announce(tr!(event_added(title = title.clone())));Takes impl Into<String>, so tr!(…) works directly.
LocalizedString is deliberately not the parameter type: an
announcement is an event, not a label, and
re-resolving it on a later language switch would re-speak it. See
crate::announcer.
Do not pair this with a toast on the same path. Toast is already a
correct live region, so doing both says everything twice.
Sourcepub fn announce_with(
&mut self,
message: impl Into<String>,
politeness: Politeness,
)
pub fn announce_with( &mut self, message: impl Into<String>, politeness: Politeness, )
Speak message to the screen reader at the given urgency.
Politeness::Assertive
interrupts whatever is being spoken. Reserve it for something the user
must not miss and cannot recover by re-reading the screen — a failure, a
refusal, a destructive result. Everything else is
Polite, which is what
announce uses.
Sourcepub fn show_overlay(&mut self, request: OverlayRequest)
pub fn show_overlay(&mut self, request: OverlayRequest)
Show an overlay (tooltip, menu, popover).
Sourcepub fn show_overlay_in_band(
&mut self,
request: OverlayRequest,
band: OverlayBand,
)
pub fn show_overlay_in_band( &mut self, request: OverlayRequest, band: OverlayBand, )
Show an overlay in an explicit z-band.
show_overlay is this with
Standard. The other band is
for the touch text affordances, which must render above the editor’s
clips_children ancestor, below every menu, and outside the
outside-press dismissal that every caret-moving tap would otherwise
trigger. Their lifetime is the controller’s — see
TouchSelection::dismiss.
Showing content that is already up is a no-op, so a host may call this on every raise without tracking whether it has.
Sourcepub fn update_overlay_placement_by_content(
&mut self,
content_id: WidgetId,
placement: OverlayPlacement,
)
pub fn update_overlay_placement_by_content( &mut self, content_id: WidgetId, placement: OverlayPlacement, )
Re-place the currently-shown overlay whose content root is content_id.
Content-keyed for the same reason
dismiss_overlay_by_content is:
show_overlay returns nothing, so a handler
cannot learn the OverlayId it created. A
no-op when no overlay is showing that content.
Sourcepub fn show_overlay_with_reveal(
&mut self,
request: OverlayRequest,
progress: Signal<f32>,
duration: Duration,
)
pub fn show_overlay_with_reveal( &mut self, request: OverlayRequest, progress: Signal<f32>, duration: Duration, )
Show an overlay whose reveal/dismiss is animated by a caller-owned progress signal.
progress must be an animated Signal<f32> (created with
Signal::new_animated or
BuildContext::animated_signal).
The framework shows the overlay, tweens progress 0 → 1 over
duration, and on any dismiss path tweens it 1 → 0 while
deferring the overlay’s removal (and its content’s dormancy)
until the roll-back completes — the same window the fade path
uses, but with no opacity applied. The caller binds progress
to whatever paints the reveal (e.g. an
Unroll width), and is responsible for
resetting it to 0.0 before the show if a prior reveal left it
at 1.0.
Under prefers-reduced-motion, skip this and use
show_overlay with the progress pinned at
1.0 so there is no tween and dismissal is immediate.
Sourcepub fn show_overlay_for(&mut self, request: OverlayRequest, duration: Duration)
pub fn show_overlay_for(&mut self, request: OverlayRequest, duration: Duration)
Show an overlay that dismisses automatically after duration.
Sourcepub fn dismiss_overlay(&mut self, id: OverlayId)
pub fn dismiss_overlay(&mut self, id: OverlayId)
Dismiss an overlay by ID.
Sourcepub fn dismiss_overlay_by_content(&mut self, content_id: WidgetId)
pub fn dismiss_overlay_by_content(&mut self, content_id: WidgetId)
Dismiss the currently-shown overlay whose content root is
content_id, if one is active. No-op when no overlay is showing
that content. Use this to dismiss an overlay you can only name by
its content widget — the symmetric companion to
cancel_delayed_overlay, which
cancels a pending delayed show for the same content. Together
they let a caller fully retract a reusable tooltip surface
(shown or pending) without tracking the OverlayId.
Sourcepub fn pause_overlay_auto_dismiss(&mut self, id: OverlayId)
pub fn pause_overlay_auto_dismiss(&mut self, id: OverlayId)
Queue a request to pause an overlay’s auto_dismiss_after
timer. Drained by the framework after this handler returns —
equivalent to calling
OverlayManager::pause_auto_dismiss
at the next safe point. Idempotent.
Used by ToastHost for hover-pause: on pointer-enter the
host queues pause_overlay_auto_dismiss(id) for every live
toast; on pointer-leave it queues resume_overlay_auto_dismiss.
Sourcepub fn resume_overlay_auto_dismiss(&mut self, id: OverlayId)
pub fn resume_overlay_auto_dismiss(&mut self, id: OverlayId)
Queue a request to resume an overlay’s auto_dismiss_after
timer paused via
pause_overlay_auto_dismiss.
Idempotent on un-paused overlays.
Sourcepub fn dismiss_all_overlays(&mut self)
pub fn dismiss_all_overlays(&mut self)
Dismiss all active overlays (e.g., after a menu item is activated).
Sourcepub fn dismiss_self_overlay_chain(&mut self)
pub fn dismiss_self_overlay_chain(&mut self)
Dismiss the source widget’s containing overlay and any ancestor
overlays in the chain that are menu-like (anything that isn’t a
Role::Tooltip, Role::Dialog, or Role::AlertDialog),
preserving an outer composite tooltip or modal hosting the
popover. Use for menu / dropdown item activation that wants to
close the menu cascade without disturbing the host surface.
Sourcepub fn dismiss_all_except_hosts(&mut self)
pub fn dismiss_all_except_hosts(&mut self)
Dismiss every overlay whose content is not a host surface
(Role::Tooltip, Role::Dialog, Role::AlertDialog),
preserving an outer composite tooltip or modal hosting the
trigger. Use for popover triggers and pre-show cleanup that
want to close stale popovers / menus without taking a hosting
surface with them.
Sourcepub fn dismiss_top_overlay(&mut self)
pub fn dismiss_top_overlay(&mut self)
Dismiss the topmost overlay only (e.g., closing a submenu while keeping the parent menu open).
Sourcepub fn dismiss_child_overlays(&mut self)
pub fn dismiss_child_overlays(&mut self)
Dismiss descendant overlays of the source widget’s containing overlay. Useful for closing sibling submenu branches while keeping the current parent menu open.
Sourcepub fn dismiss_child_overlays_except(&mut self, content_id: WidgetId)
pub fn dismiss_child_overlays_except(&mut self, content_id: WidgetId)
Dismiss descendant overlays of the source widget’s containing overlay,
preserving the subtree rooted at content_id if it is already open.
Sourcepub fn request_idle_callback(
&mut self,
callback: impl FnOnce(IdleDeadline) + 'static,
)
pub fn request_idle_callback( &mut self, callback: impl FnOnce(IdleDeadline) + 'static, )
Request an idle callback to be run during the next idle period. Use this for incremental work that takes 5-50ms — too short for a background thread, too long for a single frame.
Sourcepub fn present_modal(&mut self, request: ModalRequest)
pub fn present_modal(&mut self, request: ModalRequest)
Request framework-owned modal presentation.
The widget tree records the request together with the originating
widget, and the application layer can later resolve Auto into a
concrete presentation backend.
Sourcepub fn open_modal(&mut self, request: ModalRequest) -> Option<TeksiloWindowId>
pub fn open_modal(&mut self, request: ModalRequest) -> Option<TeksiloWindowId>
Synchronously open a modal as a native window — the single
unified path for native-window modals. Callers that don’t
care whether the modal lands in-tree or in a native window
use present_modal, which routes
ModalPresentation::Auto through the framework’s picker.
Returns the new window’s id, or None when called outside a
dispatch context (standalone trees). The window’s parent is
the current window; focus target and title / size from the
request are honored.
Only ModalContent::Deferred is supported here — an
ExistingWidget id wouldn’t make sense in a fresh tree.
Sourcepub fn dismiss_modal(&mut self)
pub fn dismiss_modal(&mut self)
Dismiss the current framework-owned modal presentation.
Sourcepub fn show_overlay_after(&mut self, request: OverlayRequest, delay: Duration)
pub fn show_overlay_after(&mut self, request: OverlayRequest, delay: Duration)
Show an overlay after a delay. The widget tree checks pending delayed
overlays during layout() and shows them once the delay elapses.
Use this for submenu hover-open delays.
The content widget should already be added to the tree (typically dormant). It will be activated automatically when the delay elapses.
Sourcepub fn show_overlay_after_with_focus(
&mut self,
request: OverlayRequest,
delay: Duration,
focus_target: WidgetId,
)
pub fn show_overlay_after_with_focus( &mut self, request: OverlayRequest, delay: Duration, focus_target: WidgetId, )
Show an overlay after a delay and move focus when it opens.
Sourcepub fn show_overlay_after_replacing_siblings(
&mut self,
request: OverlayRequest,
delay: Duration,
focus_target: WidgetId,
)
pub fn show_overlay_after_replacing_siblings( &mut self, request: OverlayRequest, delay: Duration, focus_target: WidgetId, )
Show an overlay after a delay, move focus when it opens, and dismiss the anchor’s sibling overlays at that moment rather than when the request was made.
This is the hover-switch between two submenu triggers in the same menu. Dismissing eagerly at hover-enter closes the submenu the user is still walking toward as soon as the pointer crosses a neighbouring trigger; deferring the dismissal to the moment the new submenu actually opens means a pointer merely passing through costs nothing, and one that settles gets the swap on the same frame — no window with two submenus on screen.
Sourcepub fn request_repaint(&mut self, id: WidgetId)
pub fn request_repaint(&mut self, id: WidgetId)
Request a repaint on a specific widget. Use this when an event handler on one widget changes state that affects a different widget’s appearance (e.g., keyboard navigation highlighting items in an overlay).
Sourcepub fn synthetic_click(&mut self, id: WidgetId)
pub fn synthetic_click(&mut self, id: WidgetId)
Programmatically click a widget (synthetic PointerDown + PointerUp at its center). Use this for keyboard activation of a child widget, e.g., Enter on a keyboard-focused menu item.
Sourcepub fn request_focus(&mut self, id: WidgetId)
pub fn request_focus(&mut self, id: WidgetId)
Transfer focus to a specific widget. Use this when opening overlay content (menus, dialogs) that should receive keyboard events.
Sourcepub fn request_focus_into(&mut self, id: WidgetId)
pub fn request_focus_into(&mut self, id: WidgetId)
Move focus into the content of id: focus its first focusable
descendant in tab order. Unlike request_focus,
this does not fall back to focusing id itself when the subtree has
no focusable descendant — it is a no-op in that case, so an empty region
never traps focus on a non-interactive container.
Use this for “dive into this region” gestures, e.g. pressing Enter on a focused tab header to move focus into the tab’s content panel. A panel with focusable content lands on its first control; a panel that opted into focusability itself (no inner controls) lands on the panel; a bare panel with neither leaves focus where it was.
Sourcepub fn ensure_visible(&mut self, rect: Rect)
pub fn ensure_visible(&mut self, rect: Rect)
Scroll the given rectangle into view inside every enclosing scroll container, walking outward from the widget whose handler is running.
rect is in absolute tree (window) coordinates — the same space
the arena stores widget bounds in. After the handler returns, the
framework walks the current widget’s ancestors and, for each
clips_children scroll container whose viewport does not already
fully contain rect, dispatches
WidgetEvent::ScrollIntoView
so the container adjusts its offset. Nested scroll areas each get a
turn (outermost included), exactly like the focus-driven path.
Unlike the automatic focus follow — which can only reveal a focused
widget’s own bounds — this lets a widget reveal an arbitrary interior
rectangle it computed itself: a text caret, a virtualized list/table
row (which is not a distinct focusable node), or a scrolled-off tab
header. The widget remains responsible for scrolling its own interior
viewport; ensure_visible handles the enclosing containers. It is a
no-op when there is no scroll container above the widget, or when every
container already shows the rect.
See ensure_visible_with_margin to
keep breathing room around the target.
Sourcepub fn ensure_visible_from(&mut self, owner: WidgetId, rect: Rect)
pub fn ensure_visible_from(&mut self, owner: WidgetId, rect: Rect)
ensure_visible, for a rect that belongs to
another widget.
The framework walks owner’s ancestors rather than the handling widget’s.
That distinction is the whole of it, and getting it wrong fails silently:
a find banner’s Next button sits beside the scrolling page, not inside
it, so a reveal walked from the button climbs out through the banner and
never meets the scroll container the match is in. The match is selected,
the counter moves, and the viewport does not follow.
The same reasoning ensure_widget_visible
already records for the id-based form; this is its rect-based twin, for a
target that is an interior span rather than a mounted child.
rect is in absolute tree (window) coordinates.
Sourcepub fn ensure_visible_with_margin(&mut self, rect: Rect, margin: f32)
pub fn ensure_visible_with_margin(&mut self, rect: Rect, margin: f32)
Like ensure_visible, but keeps margin
logical pixels of breathing room around rect on every edge, so the
target does not sit flush against the viewport boundary (the caret at
the bottom line, the selected row at the fold). rect is in absolute
tree (window) coordinates.
Sourcepub fn ensure_visible_aligned(
&mut self,
rect: Rect,
fraction: f32,
motion: ScrollMotion,
)
pub fn ensure_visible_aligned( &mut self, rect: Rect, fraction: f32, motion: ScrollMotion, )
Pin rect at fraction of the way down the innermost enclosing
scroll container — 0.0 flush with the top, 0.5 centred, 1.0 flush
with the bottom — instead of merely revealing it.
The difference from ensure_visible is that this
scrolls even when the target is already visible. That is what makes
it usable for typewriter scrolling: a caret that only moved the view once
it fell off the edge would not be pinned to anything.
Only the innermost clipping ancestor aligns; any further ancestors out fall back to a minimal reveal, since an outer container’s job is to bring the inner viewport on screen, not to align a rectangle it does not own.
fraction is clamped to 0.0..=1.0. The container additionally clamps
to its own scroll range, so a target near the start or end of the content
lands as close to fraction as the range permits — see the scroll
container’s scroll_past_end for buying range past the content’s end so
the last line can still reach the pin.
rect is in absolute tree (window) coordinates.
Sourcepub fn ensure_visible_aligned_from(
&mut self,
owner: WidgetId,
rect: Rect,
fraction: f32,
motion: ScrollMotion,
)
pub fn ensure_visible_aligned_from( &mut self, owner: WidgetId, rect: Rect, fraction: f32, motion: ScrollMotion, )
ensure_visible_aligned, for a rect that
belongs to another widget — see ensure_visible_from
for why the distinction exists and how it fails when it is missed.
Sourcepub fn ensure_widget_visible(&mut self, id: WidgetId)
pub fn ensure_widget_visible(&mut self, id: WidgetId)
Scroll a specific mounted widget into view inside every enclosing
scroll container — the id-based companion to
ensure_visible.
The framework resolves id to its current absolute bounds after the
handler returns and walks that widget’s ancestors (never id
itself), dispatching
WidgetEvent::ScrollIntoView
to each clips_children container that doesn’t already show it.
Use this when the target you want revealed is a real, non-virtualized
child whose bounds the arena already knows — a selected radio tile, a
tab header — so you don’t have to compute a rect. For a target that has
no distinct node (a text caret) or that may not be realized (a
virtualized list/table row), use ensure_visible
with an analytic rect instead. No-op if id is not currently mounted.
Sourcepub fn ensure_widget_visible_with_margin(&mut self, id: WidgetId, margin: f32)
pub fn ensure_widget_visible_with_margin(&mut self, id: WidgetId, margin: f32)
Like ensure_widget_visible, but keeps
margin logical pixels of breathing room around the widget.
Sourcepub fn show_highlight_tooltip(&mut self, id: WidgetId)
pub fn show_highlight_tooltip(&mut self, id: WidgetId)
Surface the tooltip of a keyboard-highlighted item immediately (no
dwell), dismissing the previously-highlighted item’s tooltip. Used by
MenuList on arrow-key navigation so a menu item’s rich/composite
tooltip is reachable by keyboard — real focus stays on the menu panel,
so this is keyed on the item id rather than on focus. Pass the item’s
own widget id; a tooltip-less item simply dismisses the previous one.
Sourcepub fn cancel_delayed_overlay(&mut self, content_id: WidgetId)
pub fn cancel_delayed_overlay(&mut self, content_id: WidgetId)
Cancel a pending delayed overlay by its content widget ID. Call this when the hover ends before the delay elapses.
Sourcepub fn arm_overlay_safe_region(&mut self, content_id: WidgetId)
pub fn arm_overlay_safe_region(&mut self, content_id: WidgetId)
Arm the “safe triangle” of the open overlay rooted at
content_id, with its apex at the current pointer position.
Call this from the anchor’s hover-leave handler: the pointer is
then exactly at the point the diagonal toward the overlay
starts. While the pointer sits inside the triangle spanned by
that apex and the overlay’s near edge, the overlay’s
pointer-leave grace is held off; leaving the triangle starts the
grace and re-entering it cancels the grace again, so a wobble
mid-diagonal costs nothing. Throughout — cone or no cone, until
the pointer arrives or the framework’s budget runs out —
overlay_safe_region_armed
reports true so sibling widgets stand aside and let that one
re-evaluated grace own the dismissal.
No-ops when the overlay is not open (a submenu whose hover-open delay was cancelled before it ever showed) or when no pointer position is known.
Sourcepub fn set_drag_activation(&mut self, activation: DragActivation)
pub fn set_drag_activation(&mut self, activation: DragActivation)
Choose, from this press’s PointerDown handler, when this node’s own
drag may begin — overriding its declared
DragActivation for this press alone.
.drag_activation(..) is a node property, decided at build time. That is
the right grain when a node’s on_drag means one thing. It is the wrong
grain when one handler means several: a scene viewport’s single on_drag
is its marquee and its item grab and its magnet port drag, and which
of the three a press is cannot be known until the press has been
hit-tested. This is the per-press door — the press handler has already
done that hit test, so it can say “this one landed on an item, arm it
immediately” while leaving an empty-space press to defer to the pan.
Stashed on the pointer’s sequence, not written back onto the node, so
it dies with the press that chose it. That matters here more than
hygiene usually does: on_pointer_event previews root-first over every
strict ancestor of the press target, so a node that answers from it also
answers for presses an interactive descendant owns, and a node write
would leave the declaration changed for the next press.
Read by the enrolment walk, which runs immediately after the press dispatch. Called from anything but a press handler it is inert for the press in flight — there is no enrolment left to read it — and applies to nothing else.
Last writer wins: answering twice on one press means the second answer.
Sourcepub fn capture_pointer(&mut self)
pub fn capture_pointer(&mut self)
Capture the pointer this handler is serving: its subsequent
PointerMove and PointerUp are routed to this widget regardless of
hit test, until the capture is released.
Capture is per pointer. Two fingers pressing two widgets hold two
independent captures, and each is released only by its own Up or
Cancel — so a second contact lifting can no longer steal the first
one’s stream. A mouse call site is unaffected: there is one mouse, and
this captures it.
Also an arbitration act. Taking the pointer from an undecided
PointerSequence enrols this widget
as a MemberRole::RawDrag
competitor, and for a precise pointer with no eligible pan competitor
it decides the sequence outright — which is what makes the splitter
handle, the dock resize handle and the table column grip (all of which
answer Ignored from on_pointer_event and work from PointerMove
with no recognizer at all) first-class competitors rather than widgets
the arbitration cannot see.
Sourcepub fn capture_pointer_id(&mut self, pointer: PointerId)
pub fn capture_pointer_id(&mut self, pointer: PointerId)
Capture a named pointer, for a handler driving a pointer other than the one whose sample it is serving.
Sourcepub fn claim_gesture(&mut self)
pub fn claim_gesture(&mut self)
Claim the pointer sequence for the widget whose handler is running: arbitration ends, every other competitor is cancelled.
The explicit form of what a recognizer does when it recognizes. Use it from an application recognizer that decides by its own rules.
Sourcepub fn reject_gesture(&mut self)
pub fn reject_gesture(&mut self)
Withdraw the widget whose handler is running from the sequence. It can no longer win this press; its peers carry on.
Sourcepub fn hold_gesture(&mut self)
pub fn hold_gesture(&mut self)
Defer this widget’s own decision without withdrawing: no peer may win while a member is holding.
The framework never holds. This exists for an application
recognizer awaiting an answer it does not have yet (a hit test against
an off-thread model, a network round trip). The hold auto-releases at
GestureProfile::max_hold
— 250 ms — so a holder that never answers cannot strand the press.
Sourcepub fn release_gesture(&mut self)
pub fn release_gesture(&mut self)
End this widget’s hold, putting it back in the running.
Sourcepub fn cancel_pointer_sequence(&mut self, reason: CancelReason)
pub fn cancel_pointer_sequence(&mut self, reason: CancelReason)
Revoke the whole interaction of the pointer this handler is serving,
for reason.
The widget’s own way into the cancel funnel, for a widget that knows
the interaction can no longer mean anything — the document under a text
drag was reloaded, the row being reordered was deleted by a peer. Every
competitor is cancelled, the capture is given back, and a
PointerCancel is
delivered, all after this handler returns: a cancel taken inline
would unwind the very sample the handler is standing on.
Distinct from reject_gesture, which withdraws
only this widget and lets its peers carry on with a pointer that is
still perfectly alive.
Sourcepub fn release_pointer(&mut self)
pub fn release_pointer(&mut self)
Release the capture of the pointer this handler is serving. Its events resume normal hit-test dispatch.
Sourcepub fn owns_pointer(&self) -> bool
pub fn owns_pointer(&self) -> bool
Whether the widget whose handler is running already holds the capture of the pointer it is serving.
true also immediately after a capture_pointer
in the same handler, even though the tree does not apply the request
until the handler returns — asking “do I own this pointer?” after
claiming it must not answer no.
Sourcepub fn start_drag(&mut self, source_widget: WidgetId, payload: DragPayload)
pub fn start_drag(&mut self, source_widget: WidgetId, payload: DragPayload)
Start a drag-and-drop operation from the given source widget.
The payload carries the data being dragged. During the drag:
PointerMoveevents update the drag position and fireon_drag_hoveron widgets under the pointer that have drop handlersPointerUpfireson_dropon the target widget (if any)Escapecancels the drag
Sourcepub fn start_drag_with_preview(
&mut self,
source_widget: WidgetId,
payload: DragPayload,
preview: Box<dyn Widget>,
)
pub fn start_drag_with_preview( &mut self, source_widget: WidgetId, payload: DragPayload, preview: Box<dyn Widget>, )
Start a drag-and-drop with a preview widget that follows the pointer.
Sourcepub fn cancel_drag(&mut self)
pub fn cancel_drag(&mut self)
Cancel the active drag-and-drop session (if any).
Sourcepub fn set_theme(&mut self, theme: Theme)
pub fn set_theme(&mut self, theme: Theme)
Replace the tree-level theme. Composite widgets are rebuilt so any derived values they captured at build time pick up the new tokens, and all widgets are marked dirty for repaint.
An explicit theme also turns off OS-following: the app’s theme mode is reset to manual, so a later OS light/dark change won’t override the chosen theme.
Sourcepub fn follow_system_theme(&mut self)
pub fn follow_system_theme(&mut self)
Switch the application to follow the OS theme (native / system mode): the app adopts the OS’s colours and tracks OS light/dark changes at runtime. On platforms without OS-colour support it falls back to following the built-in light/dark presets.
This is the counterpart to set_theme: calling
set_theme pins a fixed theme (manual mode), while this resumes
OS-following. Parameterless by design, so widgets need not reference
the app-layer theme-mode enum.
Sourcepub fn set_locale(&mut self, locale: impl Into<String>)
pub fn set_locale(&mut self, locale: impl Into<String>)
Replace the tree-level locale identifier. Composite widgets are rebuilt so any tr! lookups picked up at build time are re-evaluated against the new locale.
Sourcepub fn set_text_scale(&mut self, factor: f32)
pub fn set_text_scale(&mut self, factor: f32)
Set the user-controlled global text-scale factor (1.0 = 100 %).
The change is applied app-wide (every window) after the handler returns,
mirroring set_theme / set_locale.
All text grows uniformly without a rebuild. Persist the value through
ctx.settings() (e.g. teksilo_settings::TEXT_SCALE_KEY) so it survives
a restart — the TextScaleControl widget does both for you.