Skip to main content

UiState

Struct UiState 

Source
pub struct UiState {
    pub pointer_pos: Option<(f32, f32)>,
    pub hovered: Option<UiTarget>,
    pub pressed: Option<UiTarget>,
    pub focused: Option<UiTarget>,
    pub focus_visible: bool,
    pub current_selection: Selection,
    pub modifiers: KeyModifiers,
    /* private fields */
}
Expand description

Internal UI state — interaction trackers + the side maps the library writes during layout / state-apply / animation-tick passes. Owned by the renderer; the host doesn’t interact with this directly.

The side maps replace the per-node bookkeeping fields that used to live on El (computed rect, interaction state, envelope amounts). Keying is by El::computed_id, the path-shaped string assigned by the layout pass.

Fields§

§pointer_pos: Option<(f32, f32)>

Last known pointer position in logical pixels. None until the pointer enters the window.

§hovered: Option<UiTarget>§pressed: Option<UiTarget>§focused: Option<UiTarget>§focus_visible: bool

Whether the focused element should display its focus ring. Tracks the web platform’s :focus-visible heuristic: keyboard focus (Tab, arrow-nav) raises the flag; pointer-down clears it. Widgets where the ring belongs even on click — text inputs and text areas, where the ring communicates “this surface is now active” beyond the caret alone — opt back in via crate::tree::El::always_show_focus_ring.

§current_selection: Selection

Mirror of the application’s current crate::selection::Selection. Set by the host runner once per frame from crate::event::App::selection; read by the painter to draw highlight bands and by the selection manager to know what’s currently active when extending a drag.

§modifiers: KeyModifiers

Last known keyboard modifier mask. Updated by the host runner from winit’s ModifiersChanged; pointer events stamp this value into their UiEvent.modifiers so widgets that need to detect Shift+click / Ctrl+drag can read it without separate plumbing.

Implementations§

Source§

impl UiState

Source

pub fn envelope(&self, id: &str, kind: EnvelopeKind) -> f32

Current eased state envelope amount in [0, 1] for (id, kind). Missing entries read as 0.0.

Source

pub fn tick_visual_animations(&mut self, root: &mut El, now: Instant) -> bool

Walk the laid-out tree, retarget per-(node, prop) animations to the values implied by each node’s current state, step them forward to now, and write back: app-driven props mutate the El’s fill / text_color / stroke / opacity / translate / scale (so the next rebuild reads the eased value); state envelopes are written to the envelope side map for draw_ops to modulate visuals from.

Returns true if any animation is still in flight; the host should request another redraw next frame.

Source

pub fn set_animation_mode(&mut self, mode: AnimationMode)

Switch animation pacing. The default is AnimationMode::Live; headless render binaries flip to AnimationMode::Settled so a single-frame snapshot reflects the post-animation visual without depending on integrator timing.

Source

pub fn animation_mode(&self) -> AnimationMode

Current animation pacing. Backends read this to gate time-driven shader uniforms (e.g. frame.time) so headless fixtures stay byte-identical regardless of when they ran.

Source

pub fn has_animations_in_flight(&self) -> bool

Whether any visual animation is still moving. The host’s runner uses this (via the renderer’s PrepareResult) to keep the redraw loop ticking only while there’s motion.

Source

pub fn debug_summary(&self) -> String

One-line summary of interactive state for diagnostic logging. Format: hov=<key|->|press=<key|->|focus=<key|->|env={...}|in_flight=N. Keep terse — this is intended for per-frame console.log.

Source§

impl UiState

Source

pub fn cursor(&self, root: &El) -> Cursor

Resolved pointer cursor for the current frame.

Picks the cursor in this order:

  1. If Self::pressed is set: a. If the press target itself declares .cursor_pressed(...), use that — drives the slider’s Grab → Grabbing transition and the more general “press has its own affordance” idiom. Does not inherit: an ancestor’s cursor_pressed doesn’t apply to a descendant press target. b. Otherwise walk from the press target up to root for the first explicit .cursor(...). Press capture wins so a button drag that wanders onto a text region doesn’t flicker the cursor mid-press.
  2. Else if the pointer is over a text-link run (hovered_link is Some), use Cursor::Pointer. Link runs aren’t keyed hit-test targets, so this branch sits parallel to the keyed-hover lookup. Beats any .cursor(Cursor::Text) declared on a containing paragraph — link affordance is more specific than panel affordance.
  3. Else if Self::hovered is set, walk from the hovered target up to root for the first explicit declaration — so a panel that sets .cursor(Move) once propagates to children that don’t override.
  4. Else Cursor::Default.

Disabled state isn’t auto-mapped to Cursor::NotAllowed; widgets that want that affordance branch in their build closure.

Source§

impl UiState

Source

pub fn sync_focus_order(&mut self, root: &El)

Source

pub fn set_focus(&mut self, target: Option<UiTarget>)

Source

pub fn push_focus_requests(&mut self, keys: Vec<String>)

Queue programmatic focus requests by key. Each entry is resolved once per prepare_layout, after the focus order has been rebuilt: matching keys focus the corresponding node; unmatched keys are dropped silently. Hosts call this once per frame from crate::event::App::drain_focus_requests; apps that own a Runner can also push directly for tests.

Source

pub fn drain_focus_requests(&mut self)

Drain the queued focus requests, resolving each by key against the current focus order. The last successfully-resolved key wins. Called by prepare_layout after sync_popover_focus so explicit requests override popover auto-focus.

Source

pub fn set_focus_visible(&mut self, visible: bool)

Set whether the current focus should display its focus ring. The runtime calls this from input-handling paths: pointer-down clears it (false), Tab and arrow-nav raise it (true). Apps that move focus programmatically can also flip it explicitly, e.g. force the ring on after restoring focus from an off-screen menu close. See UiState::focus_visible.

Source

pub fn focus_next(&mut self) -> Option<&UiTarget>

Source

pub fn focus_prev(&mut self) -> Option<&UiTarget>

Source§

impl UiState

Source

pub fn node_state(&self, id: &str) -> InteractionState

Resolved interaction state for id. Returns InteractionState::Default when no tracker matches.

Source

pub fn apply_to_state(&mut self)

Rebuild the resolved per-node interaction-state side map from the current focused/pressed/hovered trackers. Press wins over Hover on a same-node match; Hover wins over Focus on a same-node match (so a keyboard-auto-focused menu item still gets its hover-lighten when the cursor is over it). Focus applies on its own when the node isn’t pressed or hovered.

Press is gated on the pointer being currently over the originally-pressed target — drag the cursor off and the press visual decays, drag back on and it returns. Mirrors the HTML / Tailwind :active behaviour: the visual reflects “would release-here activate?”, not “was pointer_down captured?”. Drag events still route to pressed regardless of pointer position (see runtime::pointer_moved); this gating only affects the visual envelope.

Source§

impl UiState

Source

pub fn set_hotkeys(&mut self, hotkeys: Vec<(KeyChord, String)>)

Replace the hotkey registry. Called by the host runner from App::hotkeys() once per build cycle.

Source

pub fn set_modifiers(&mut self, modifiers: KeyModifiers)

Update the tracked modifier mask. Hosts call this from their platform’s “modifiers changed” hook (e.g. winit’s WindowEvent::ModifiersChanged); the value is stamped into UiEvent.modifiers for every subsequent pointer event so widgets can detect Shift+click / Ctrl+drag without needing a per-call modifier parameter.

Source

pub fn try_hotkey( &self, key: &UiKey, modifiers: KeyModifiers, repeat: bool, ) -> Option<UiEvent>

Match key + modifiers against the registered hotkey chords. Returns a Hotkey event if any registered chord matches; the event.key is the chord’s registered name. Used by both the library-default path and the capture-keys path (hotkeys always win over a widget’s raw key capture).

Source

pub fn key_down_raw( &self, key: UiKey, modifiers: KeyModifiers, repeat: bool, ) -> Option<UiEvent>

Build a raw KeyDown event routed to the focused target, bypassing the library’s Tab/Enter/Escape interpretation. Used by the runner when the focused node has capture_keys=true. Returns None if no node is focused.

Source

pub fn key_down( &mut self, key: UiKey, modifiers: KeyModifiers, repeat: bool, ) -> Option<UiEvent>

Source§

impl UiState

Source

pub fn rect(&self, id: &str) -> Rect

Look up the layout-assigned rect for id; returns a zero rect when id is unknown (pre-layout, or not in the laid-out tree).

Source

pub fn rect_of_key(&self, root: &El, key: &str) -> Option<Rect>

Look up the layout-assigned rect for an app-supplied element key. Returns None when the key is absent from root or layout has not written a rect for that node yet.

Source

pub fn target_of_key(&self, root: &El, key: &str) -> Option<UiTarget>

Build a UiTarget for an app-supplied element key using the current layout rect. Useful for hosts that need to anchor native overlays or forward events into externally painted regions.

Source

pub fn hovered_key(&self) -> Option<&str>

The keyed leaf currently under the pointer, or None when nothing is hovered. Mirrors pointer hit-test target data, but is read-only and stable across rebuilds so apps can branch the build output on “what is hovered right now.”

Returns the leaf — the deepest keyed hit-test target. Use Self::is_hovering_within for subtree-aware queries (“is anything inside this row hovered?”), which matches the semantics of crate::tree::El::hover_alpha.

Source

pub fn is_hovering_within(&self, key: &str) -> bool

True iff key’s node — or any descendant of it — is the current hover target. Subtree-aware: a focusable card with keyed icon-buttons inside it reports true whether the cursor is on the card body or on one of the buttons. Same predicate El::hover_alpha uses to drive its declarative reveal, exposed for app-side reads.

Reads the underlying tracker, not the eased subtree envelope — the boolean flips immediately on hit-test identity change. For reactions tied to the eased animation, drive visuals through hover_alpha instead.

Returns false when key isn’t in the current tree (pre- layout, or the keyed node was removed in a recent build).

Source§

impl UiState

Source

pub fn set_scroll_offset(&mut self, id: impl Into<String>, value: f32)

Seed or read the persistent scroll offset for id. Use this to pre-position a scroll viewport before crate::layout::layout runs (call crate::layout::assign_ids first to populate the node’s computed_id).

Examples found in repository?
examples/virtual_list.rs (line 75)
68fn main() -> std::io::Result<()> {
69    let mut root = fixture();
70    layout::assign_ids(&mut root);
71    let list_id = find_id(&root, "entries").expect("virtual_list id");
72
73    let mut ui_state = UiState::new();
74    // Scroll so the realized window is near row 5000.
75    ui_state.set_scroll_offset(list_id, 5000.0 * ROW_HEIGHT);
76
77    let viewport = Rect::new(0.0, 0.0, 540.0, 540.0);
78    let bundle = render_bundle_with(&mut root, &mut ui_state, viewport);
79
80    let out_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("out");
81    let written = write_bundle(&bundle, &out_dir, "virtual_list")?;
82    for p in &written {
83        println!("wrote {}", p.display());
84    }
85
86    if !bundle.lint.findings.is_empty() {
87        eprintln!("\nlint findings ({}):", bundle.lint.findings.len());
88        eprint!("{}", bundle.lint.text());
89    }
90
91    Ok(())
92}
More examples
Hide additional examples
examples/scroll_list.rs (line 55)
45fn main() -> std::io::Result<()> {
46    // Scroll part-way down so the artifact actually shows the offset
47    // applied — the top rows clip and middle rows fill the viewport.
48    // Side-map architecture: we assign_ids first to populate the
49    // scroll node's computed_id, seed UiState by id, then call
50    // render_bundle_with so the layout pass sees the offset.
51    let mut root = scroll_list_fixture();
52    layout::assign_ids(&mut root);
53    let scroll_id = find_id(&root, "notifications").expect("scroll node id");
54    let mut ui_state = UiState::new();
55    ui_state.set_scroll_offset(scroll_id, 220.0);
56
57    let viewport = Rect::new(0.0, 0.0, 720.0, 600.0);
58    let bundle = render_bundle_with(&mut root, &mut ui_state, viewport);
59
60    let out_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("out");
61    let written = write_bundle(&bundle, &out_dir, "scroll_list")?;
62    for p in &written {
63        println!("wrote {}", p.display());
64    }
65
66    if !bundle.lint.findings.is_empty() {
67        eprintln!("\nlint findings ({}):", bundle.lint.findings.len());
68        eprint!("{}", bundle.lint.text());
69    }
70
71    Ok(())
72}
Source

pub fn scroll_offset(&self, id: &str) -> f32

Read the current scroll offset for id. Defaults to 0.0.

Source

pub fn push_scroll_requests(&mut self, requests: Vec<ScrollRequest>)

Queue programmatic scroll-to-row requests targeting virtual lists by key. Each request is consumed during layout of the matching list — viewport height and row heights are only known then, especially for virtual_list_dyn where unmeasured rows use the configured estimate. Hosts call this once per frame from crate::event::App::drain_scroll_requests; apps that own a Runner can also push directly for tests.

Source

pub fn clear_pending_scroll_requests(&mut self)

Drop any scroll requests still queued after the layout pass completed. Called by prepare_layout so requests targeting a list that wasn’t in the tree this frame don’t silently fire against a re-mounted list with the same key on a later frame.

Source

pub fn scrollbar_tracks(&self) -> impl Iterator<Item = (&str, &Rect)>

Iterate (scroll_node_id, track_rect) for every scrollable whose visible scrollbar is currently active. Hosts use this to drive cursor changes (e.g., a vertical-resize cursor over the thumb), to drive screenshot tools, or to test interaction flows. The map is rebuilt every layout pass.

Source

pub fn thumb_at(&self, x: f32, y: f32) -> Option<(String, Rect, Rect)>

Look up the scrollable whose track rect contains (x, y), returning its computed_id, the track rect, and the visible thumb rect. Returns None if no track is currently visible at that point. The track rect is wider than the visible thumb (Fitts’s law) and spans the full viewport height so callers can branch on whether y lands inside the thumb (grab) or above/below (click-to-page).

Source

pub fn pointer_wheel(&mut self, root: &El, point: (f32, f32), dy: f32) -> bool

Increment the scroll offset for the deepest scrollable container containing point. Returns true if any scrollable was hit and updated (host can use this to decide whether to request a redraw).

Source§

impl UiState

Source

pub fn sync_selection_order(&mut self, root: &El)

Walk the laid-out tree and rebuild the selectable-text order. Same shape as Self::sync_focus_order but filters for selectable keyed leaves instead of focusable ones. Should run on every frame post-layout, before the selection manager processes pointer events.

Source

pub fn selection_order(&self) -> &[UiTarget]

Read access to the current document-order list of selectable leaves. Mainly for tests; the selection manager uses internal access.

Source§

impl UiState

Source

pub fn push_toast(&mut self, spec: ToastSpec, now: Instant)

Queue a toast for the next frame. Stamps an id (monotonic) and computes the expires_at deadline from now + spec.ttl. The runtime re-walks the queue each frame and drops expired entries before synthesizing the toast layer.

Examples found in repository?
examples/toast.rs (line 68)
60fn main() -> std::io::Result<()> {
61    let viewport = Rect::new(0.0, 0.0, 720.0, 360.0);
62    // Seed the runtime's toast queue directly so the bundle dump
63    // shows the synthesized layer. In a live app the host calls
64    // `runner.push_toasts(app.drain_toasts())` once per frame.
65    let mut state = UiState::new();
66    let now = Instant::now();
67    let long_ttl = Duration::from_secs(60);
68    state.push_toast(ToastSpec::success("Settings saved").with_ttl(long_ttl), now);
69    state.push_toast(
70        ToastSpec::warning("Battery low — connect charger").with_ttl(long_ttl),
71        now,
72    );
73    state.push_toast(
74        ToastSpec::error("Failed to reach update server").with_ttl(long_ttl),
75        now,
76    );
77    state.push_toast(
78        ToastSpec::info("New version available").with_ttl(long_ttl),
79        now,
80    );
81
82    let mut tree = fixture();
83    assign_ids(&mut tree);
84    let _ = synthesize_toasts(&mut tree, &mut state, now);
85    let bundle = render_bundle_with(&mut tree, &mut state, viewport);
86
87    let out_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("out");
88    let written = write_bundle(&bundle, &out_dir, "toast")?;
89    for p in &written {
90        println!("wrote {}", p.display());
91    }
92
93    if !bundle.lint.findings.is_empty() {
94        eprintln!("\nlint findings ({}):", bundle.lint.findings.len());
95        eprint!("{}", bundle.lint.text());
96    }
97    Ok(())
98}
Source

pub fn dismiss_toast(&mut self, id: u64)

Remove the toast with the given id. Used by the runtime when the user clicks a toast-dismiss-{id} button; apps that want to programmatically cancel a toast can call this directly via the Runner::dismiss_toast host accessor.

Source

pub fn toasts(&self) -> &[Toast]

Read-only view of the current toast queue (post-expiry). Used by hosts that want to drive cursor / accessibility state from the visible stack.

Source§

impl UiState

Source

pub fn widget_state<T: WidgetState>(&self, id: &str) -> Option<&T>

Look up the widget state of type T for id. Returns None if no entry exists or the entry was inserted as a different type.

Source

pub fn widget_state_mut<T: WidgetState + Default>(&mut self, id: &str) -> &mut T

Get a mutable reference to the widget state of type T for id, inserting T::default() if no entry exists. Use this in the build closure of a stateful widget so the first call after the node enters the tree produces a fresh state, and every subsequent call returns the live one.

Source

pub fn clear_widget_state<T: WidgetState>(&mut self, id: &str)

Drop the widget state of type T for id, if any.

Source

pub fn widget_state_summary(&self, id: &str) -> Vec<(&'static str, String)>

Iterate (id, type_name, debug_summary) for every live widget state. Used by the tree dump to surface per-widget state in the agent loop’s view.

Source§

impl UiState

Source

pub fn new() -> Self

Examples found in repository?
examples/virtual_list.rs (line 73)
68fn main() -> std::io::Result<()> {
69    let mut root = fixture();
70    layout::assign_ids(&mut root);
71    let list_id = find_id(&root, "entries").expect("virtual_list id");
72
73    let mut ui_state = UiState::new();
74    // Scroll so the realized window is near row 5000.
75    ui_state.set_scroll_offset(list_id, 5000.0 * ROW_HEIGHT);
76
77    let viewport = Rect::new(0.0, 0.0, 540.0, 540.0);
78    let bundle = render_bundle_with(&mut root, &mut ui_state, viewport);
79
80    let out_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("out");
81    let written = write_bundle(&bundle, &out_dir, "virtual_list")?;
82    for p in &written {
83        println!("wrote {}", p.display());
84    }
85
86    if !bundle.lint.findings.is_empty() {
87        eprintln!("\nlint findings ({}):", bundle.lint.findings.len());
88        eprint!("{}", bundle.lint.text());
89    }
90
91    Ok(())
92}
More examples
Hide additional examples
examples/scroll_list.rs (line 54)
45fn main() -> std::io::Result<()> {
46    // Scroll part-way down so the artifact actually shows the offset
47    // applied — the top rows clip and middle rows fill the viewport.
48    // Side-map architecture: we assign_ids first to populate the
49    // scroll node's computed_id, seed UiState by id, then call
50    // render_bundle_with so the layout pass sees the offset.
51    let mut root = scroll_list_fixture();
52    layout::assign_ids(&mut root);
53    let scroll_id = find_id(&root, "notifications").expect("scroll node id");
54    let mut ui_state = UiState::new();
55    ui_state.set_scroll_offset(scroll_id, 220.0);
56
57    let viewport = Rect::new(0.0, 0.0, 720.0, 600.0);
58    let bundle = render_bundle_with(&mut root, &mut ui_state, viewport);
59
60    let out_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("out");
61    let written = write_bundle(&bundle, &out_dir, "scroll_list")?;
62    for p in &written {
63        println!("wrote {}", p.display());
64    }
65
66    if !bundle.lint.findings.is_empty() {
67        eprintln!("\nlint findings ({}):", bundle.lint.findings.len());
68        eprint!("{}", bundle.lint.text());
69    }
70
71    Ok(())
72}
examples/toast.rs (line 65)
60fn main() -> std::io::Result<()> {
61    let viewport = Rect::new(0.0, 0.0, 720.0, 360.0);
62    // Seed the runtime's toast queue directly so the bundle dump
63    // shows the synthesized layer. In a live app the host calls
64    // `runner.push_toasts(app.drain_toasts())` once per frame.
65    let mut state = UiState::new();
66    let now = Instant::now();
67    let long_ttl = Duration::from_secs(60);
68    state.push_toast(ToastSpec::success("Settings saved").with_ttl(long_ttl), now);
69    state.push_toast(
70        ToastSpec::warning("Battery low — connect charger").with_ttl(long_ttl),
71        now,
72    );
73    state.push_toast(
74        ToastSpec::error("Failed to reach update server").with_ttl(long_ttl),
75        now,
76    );
77    state.push_toast(
78        ToastSpec::info("New version available").with_ttl(long_ttl),
79        now,
80    );
81
82    let mut tree = fixture();
83    assign_ids(&mut tree);
84    let _ = synthesize_toasts(&mut tree, &mut state, now);
85    let bundle = render_bundle_with(&mut tree, &mut state, viewport);
86
87    let out_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("out");
88    let written = write_bundle(&bundle, &out_dir, "toast")?;
89    for p in &written {
90        println!("wrote {}", p.display());
91    }
92
93    if !bundle.lint.findings.is_empty() {
94        eprintln!("\nlint findings ({}):", bundle.lint.findings.len());
95        eprint!("{}", bundle.lint.text());
96    }
97    Ok(())
98}

Trait Implementations§

Source§

impl Debug for UiState

Source§

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

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

impl Default for UiState

Source§

fn default() -> UiState

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

Source§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Source§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
Source§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
Source§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

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

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.