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 row-targeted [`ScrollRequest`] should land
13/// its 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/// Three 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::ToRowKey`] — same operation, but targets the
36///   virtual-list row by stable row identity instead of current row
37///   index. Prefer this for `virtual_list_dyn` when the app already
38///   has message/thread/commit ids.
39/// - [`ScrollRequest::EnsureVisible`] — "scroll the nearest scroll
40///   container under the node keyed `container_key` so the content-
41///   space rect `y..y+h` is visible." Resolved during layout of the
42///   matching `scroll(...)` container; minimal-displacement (top edge
43///   if above viewport, bottom edge if below, no-op if already
44///   visible). Used by [`crate::widgets::text_area`] for
45///   caret-into-view on keyboard navigation, and available for any
46///   widget that needs to keep an inner anchor on screen.
47#[derive(Clone, Debug)]
48pub enum ScrollRequest {
49    /// Bring `row` of the virtual list keyed `list_key` into view per
50    /// `align`.
51    ToRow {
52        list_key: String,
53        row: usize,
54        align: ScrollAlignment,
55    },
56    /// Bring the row identified by `row_key` in the virtual list keyed
57    /// `list_key` into view per `align`.
58    ToRowKey {
59        list_key: String,
60        row_key: String,
61        align: ScrollAlignment,
62    },
63    /// Ensure the content-space rect at `y..y+h` is visible inside
64    /// the scroll container under the node keyed `container_key`.
65    /// `container_key` is the outer widget's key (e.g. the text_area's
66    /// key) — the resolver descends to find the nearest `Kind::Scroll`
67    /// inside that node.
68    EnsureVisible {
69        container_key: String,
70        y: f32,
71        h: f32,
72    },
73}
74
75impl ScrollRequest {
76    /// Construct a [`ScrollRequest::ToRow`]. Kept for source-compat
77    /// with callers that predate the enum — `ScrollRequest::new(...)`
78    /// has always meant "scroll a virtual list to a row."
79    pub fn new(list_key: impl Into<String>, row: usize, align: ScrollAlignment) -> Self {
80        ScrollRequest::ToRow {
81            list_key: list_key.into(),
82            row,
83            align,
84        }
85    }
86
87    /// Construct a [`ScrollRequest::ToRowKey`]. Dynamic virtual lists
88    /// resolve this against the same stable row identities passed to
89    /// [`crate::virtual_list_dyn`].
90    pub fn to_row_key(
91        list_key: impl Into<String>,
92        row_key: impl Into<String>,
93        align: ScrollAlignment,
94    ) -> Self {
95        ScrollRequest::ToRowKey {
96            list_key: list_key.into(),
97            row_key: row_key.into(),
98            align,
99        }
100    }
101
102    /// Construct a [`ScrollRequest::EnsureVisible`] for the widget
103    /// keyed `container_key`, asking the resolver to keep
104    /// `y..y+h` (in the scroll container's content coordinates)
105    /// inside the viewport.
106    pub fn ensure_visible(container_key: impl Into<String>, y: f32, h: f32) -> Self {
107        ScrollRequest::EnsureVisible {
108            container_key: container_key.into(),
109            y,
110            h,
111        }
112    }
113}