Skip to main content

scroll_list/
scroll_list.rs

1//! Scroll fixture for the scroll substrate.
2//!
3//! Demonstrates: a vertical scroll viewport with content taller than
4//! its visible rect, content clipped by the viewport, and a non-zero
5//! scroll offset reflected in the laid-out tree (children translated up,
6//! topmost rows clipped by the scissor).
7//!
8//! Run: `cargo run -p aetna-core --example scroll_list`
9
10use aetna_core::prelude::*;
11// This headless artifact example seeds scroll state before rendering,
12// so it opts into the explicit advanced state/layout modules.
13use aetna_core::{UiState, layout};
14
15fn scroll_list_fixture() -> El {
16    let rows: Vec<El> = (0..20)
17        .map(|i| {
18            row([
19                badge(format!("#{i}")).info(),
20                text(format!("Notification {i}")).bold(),
21                spacer(),
22                text(format!("{}m ago", i + 1)).muted(),
23            ])
24            .gap(tokens::SPACE_2)
25            .height(Size::Fixed(44.0))
26            .padding(Sides::xy(tokens::SPACE_3, tokens::SPACE_2))
27        })
28        .collect();
29
30    let list = scroll(rows)
31        .key("notifications")
32        .height(Size::Fixed(420.0))
33        .padding(tokens::SPACE_2);
34
35    column([
36        h2("Notifications"),
37        text("Roll the wheel inside the panel to scroll. The content is taller than the viewport.")
38            .muted(),
39        list,
40    ])
41    .gap(tokens::SPACE_4)
42    .padding(tokens::SPACE_7)
43}
44
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}
73
74/// Walk the tree (with `computed_id`s already assigned) and return the
75/// first node tagged with `key`'s computed_id.
76fn find_id(node: &El, key: &str) -> Option<String> {
77    if node.key.as_deref() == Some(key) {
78        return Some(node.computed_id.clone());
79    }
80    node.children.iter().find_map(|c| find_id(c, key))
81}