damascene_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// Lock in full per-item documentation for this module (issue #73).
13#![warn(missing_docs)]
14
15/// Where in the viewport a row-targeted [`ScrollRequest`] should land
16/// its target row.
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub enum ScrollAlignment {
19 /// Top of the row aligns with the top of the viewport.
20 Start,
21 /// Centre of the row aligns with the centre of the viewport.
22 Center,
23 /// Bottom of the row aligns with the bottom of the viewport.
24 End,
25 /// No-op if the row already fits entirely inside the viewport;
26 /// otherwise scroll the minimum amount that brings it into view
27 /// (i.e., align to the nearer edge).
28 Visible,
29}
30
31/// What the app produces from [`crate::App::drain_scroll_requests`].
32/// Three shapes today:
33///
34/// - [`ScrollRequest::ToRow`] — "scroll the virtual list keyed
35/// `list_key` so row `row` lands per `align`." Resolved during
36/// layout of the matching `virtual_list` / `virtual_list_dyn` using
37/// the live viewport and the row-height cache.
38/// - [`ScrollRequest::ToRowKey`] — same operation, but targets the
39/// virtual-list row by stable row identity instead of current row
40/// index. Prefer this for `virtual_list_dyn` when the app already
41/// has message/thread/commit ids.
42/// - [`ScrollRequest::EnsureVisible`] — "scroll the nearest scroll
43/// container under the node keyed `container_key` so the content-
44/// space rect `y..y+h` is visible." Resolved during layout of the
45/// matching `scroll(...)` container; minimal-displacement (top edge
46/// if above viewport, bottom edge if below, no-op if already
47/// visible). Used by [`crate::widgets::text_area`] for
48/// caret-into-view on keyboard navigation, and available for any
49/// widget that needs to keep an inner anchor on screen.
50#[derive(Clone, Debug)]
51pub enum ScrollRequest {
52 /// Bring `row` of the virtual list keyed `list_key` into view per
53 /// `align`.
54 ToRow {
55 /// `.key(...)` of the target virtual list.
56 list_key: String,
57 /// Zero-based row index. Out-of-range indices are ignored.
58 row: usize,
59 /// Where in the viewport the row should land.
60 align: ScrollAlignment,
61 },
62 /// Bring the row identified by `row_key` in the virtual list keyed
63 /// `list_key` into view per `align`.
64 ToRowKey {
65 /// `.key(...)` of the target virtual list.
66 list_key: String,
67 /// Stable row identity, as produced by the list's `row_key`
68 /// function. Unknown keys are ignored.
69 row_key: String,
70 /// Where in the viewport the row should land.
71 align: ScrollAlignment,
72 },
73 /// Ensure the content-space rect at `y..y+h` is visible inside
74 /// the scroll container under the node keyed `container_key`.
75 /// `container_key` is the outer widget's key (e.g. the text_area's
76 /// key) — the resolver descends to find the nearest `Kind::Scroll`
77 /// inside that node.
78 EnsureVisible {
79 /// `.key(...)` of the widget containing the scroll container.
80 container_key: String,
81 /// Top of the target rect, in the scroll container's content
82 /// coordinates (logical px, 0 = top of content).
83 y: f32,
84 /// Height of the target rect in logical pixels.
85 h: f32,
86 },
87}
88
89impl ScrollRequest {
90 /// Construct a [`ScrollRequest::ToRow`]. Kept for source-compat
91 /// with callers that predate the enum — `ScrollRequest::new(...)`
92 /// has always meant "scroll a virtual list to a row."
93 pub fn new(list_key: impl Into<String>, row: usize, align: ScrollAlignment) -> Self {
94 ScrollRequest::ToRow {
95 list_key: list_key.into(),
96 row,
97 align,
98 }
99 }
100
101 /// Construct a [`ScrollRequest::ToRowKey`]. Dynamic virtual lists
102 /// resolve this against the same stable row identities passed to
103 /// [`crate::virtual_list_dyn`].
104 pub fn to_row_key(
105 list_key: impl Into<String>,
106 row_key: impl Into<String>,
107 align: ScrollAlignment,
108 ) -> Self {
109 ScrollRequest::ToRowKey {
110 list_key: list_key.into(),
111 row_key: row_key.into(),
112 align,
113 }
114 }
115
116 /// Construct a [`ScrollRequest::EnsureVisible`] for the widget
117 /// keyed `container_key`, asking the resolver to keep
118 /// `y..y+h` (in the scroll container's content coordinates)
119 /// inside the viewport.
120 pub fn ensure_visible(container_key: impl Into<String>, y: f32, h: f32) -> Self {
121 ScrollRequest::EnsureVisible {
122 container_key: container_key.into(),
123 y,
124 h,
125 }
126 }
127}