aetna_core/scroll.rs
1//! App-side scroll request types.
2//!
3//! Apps push [`ScrollRequest`]s via [`crate::App::drain_scroll_requests`];
4//! the layout pass resolves each one against the matching scroll
5//! container's live viewport rect and writes the resulting offset into
6//! the scroll state so the same frame's render reflects the new position.
7//!
8//! Mirrors [`crate::toast`]: the App produces fire-and-forget descriptors
9//! and the runtime resolves them with state that's only fully known
10//! mid-frame.
11
12/// Where in the viewport a [`ScrollRequest::ToRow`] should land its
13/// target row.
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub enum ScrollAlignment {
16 /// Top of the row aligns with the top of the viewport.
17 Start,
18 /// Centre of the row aligns with the centre of the viewport.
19 Center,
20 /// Bottom of the row aligns with the bottom of the viewport.
21 End,
22 /// No-op if the row already fits entirely inside the viewport;
23 /// otherwise scroll the minimum amount that brings it into view
24 /// (i.e., align to the nearer edge).
25 Visible,
26}
27
28/// What the app produces from [`crate::App::drain_scroll_requests`].
29/// Two shapes today:
30///
31/// - [`ScrollRequest::ToRow`] — "scroll the virtual list keyed
32/// `list_key` so row `row` lands per `align`." Resolved during
33/// layout of the matching `virtual_list` / `virtual_list_dyn` using
34/// the live viewport and the row-height cache.
35/// - [`ScrollRequest::EnsureVisible`] — "scroll the nearest scroll
36/// container under the node keyed `container_key` so the content-
37/// space rect `y..y+h` is visible." Resolved during layout of the
38/// matching `scroll(...)` container; minimal-displacement (top edge
39/// if above viewport, bottom edge if below, no-op if already
40/// visible). Used by [`crate::widgets::text_area`] for
41/// caret-into-view on keyboard navigation, and available for any
42/// widget that needs to keep an inner anchor on screen.
43#[derive(Clone, Debug)]
44pub enum ScrollRequest {
45 /// Bring `row` of the virtual list keyed `list_key` into view per
46 /// `align`.
47 ToRow {
48 list_key: String,
49 row: usize,
50 align: ScrollAlignment,
51 },
52 /// Ensure the content-space rect at `y..y+h` is visible inside
53 /// the scroll container under the node keyed `container_key`.
54 /// `container_key` is the outer widget's key (e.g. the text_area's
55 /// key) — the resolver descends to find the nearest `Kind::Scroll`
56 /// inside that node.
57 EnsureVisible {
58 container_key: String,
59 y: f32,
60 h: f32,
61 },
62}
63
64impl ScrollRequest {
65 /// Construct a [`ScrollRequest::ToRow`]. Kept for source-compat
66 /// with callers that predate the enum — `ScrollRequest::new(...)`
67 /// has always meant "scroll a virtual list to a row."
68 pub fn new(list_key: impl Into<String>, row: usize, align: ScrollAlignment) -> Self {
69 ScrollRequest::ToRow {
70 list_key: list_key.into(),
71 row,
72 align,
73 }
74 }
75
76 /// Construct a [`ScrollRequest::EnsureVisible`] for the widget
77 /// keyed `container_key`, asking the resolver to keep
78 /// `y..y+h` (in the scroll container's content coordinates)
79 /// inside the viewport.
80 pub fn ensure_visible(container_key: impl Into<String>, y: f32, h: f32) -> Self {
81 ScrollRequest::EnsureVisible {
82 container_key: container_key.into(),
83 y,
84 h,
85 }
86 }
87}