Skip to main content

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}