Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-renderer/examples/list/root_view.rs
1use strato_ui::fonts::FamilyId;
2use strato_ui::SingletonEntity as _;
3use strato_ui::{
4 elements::{
5 Border, ConstrainedBox, Container, Fill, Flex, List, ListState, MainAxisSize,
6 ParentElement, Rect, ScrollStateHandle, Scrollable, ScrollableElement, ScrollbarWidth,
7 Stack, Text,
8 },
9 AppContext, Element, Entity, TypedActionView, View, ViewContext,
10};
11 
12use std::sync::{Arc, Mutex};
13use strato_ui::color::ColorU;
14 
15pub struct RootView {
16 font_family: FamilyId,
17 list_state: ListState<()>,
18 scroll_state: ScrollStateHandle,
19}
20 
21impl RootView {
22 pub fn new(ctx: &mut ViewContext<Self>) -> Self {
23 let font_family = strato_ui::fonts::Cache::handle(ctx)
24 .update(ctx, |cache, _| cache.load_system_font("Arial").unwrap());
25 
26 let list_state = ListState::new(move |i, _scroll_offset, _app| {
27 println!(" 📦 Creating element for item {i}"); // This should only appear for visible items!
28 Self::make_list_item(i, font_family).finish()
29 });
30 let scroll_state = Arc::new(Mutex::new(Default::default()));
31 
32 // Add many items to demonstrate viewporting - only visible ones should be rendered
33 println!("Creating List with 1000 items...");
34 for _ in 0..=1000 {
35 list_state.add_item();
36 }
37 println!(
38 "✅ All 1000 items added to list state. Now only visible ones should be rendered."
39 );
40 
41 RootView {
42 font_family,
43 list_state,
44 scroll_state,
45 }
46 }
47 
48 fn make_list_item(index: usize, font_family: FamilyId) -> Container {
49 // Alternate colors to make it easy to see which items are rendered
50 let bg_color = if index.is_multiple_of(2) {
51 ColorU::new(240, 240, 240, 255) // Light gray
52 } else {
53 ColorU::new(255, 255, 255, 255) // White
54 };
55 
56 let border_color = if index.is_multiple_of(10) {
57 ColorU::new(255, 0, 0, 255) // Red border for every 10th item
58 } else {
59 ColorU::new(200, 200, 200, 255) // Light gray border
60 };
61 
62 let height = 50. * (index + 1) as f32;
63 
64 Container::new(
65 ConstrainedBox::new(
66 Flex::row()
67 .with_child(
68 Text::new_inline(format!("Item #{index}"), font_family, 16.)
69 .with_color(ColorU::black())
70 .finish(),
71 )
72 .with_child(
73 Container::new(
74 ConstrainedBox::new(
75 Text::new_inline(
76 if index.is_multiple_of(10) {
77 " (MILESTONE)".to_string()
78 } else {
79 format!(" - Height: {height}px")
80 },
81 font_family,
82 14.,
83 )
84 .with_color(ColorU::black())
85 .finish(),
86 )
87 .with_width(200.)
88 .finish(),
89 )
90 .finish(),
91 )
92 .with_main_axis_size(MainAxisSize::Max)
93 .finish(),
94 )
95 .with_width(600.)
96 .with_height(height)
97 .finish(),
98 )
99 .with_background_color(bg_color)
100 .with_border(Border::all(1.).with_border_color(border_color))
101 }
102 
103 fn make_instructions(&self) -> Box<dyn Element> {
104 Container::new(
105 ConstrainedBox::new(
106 Flex::column()
107 .with_child(
108 Text::new_inline(
109 "List Demo - 1000 Items".to_string(),
110 self.font_family,
111 20.,
112 )
113 .with_color(ColorU::black())
114 .finish(),
115 )
116 .with_child(
117 Text::new_inline(
118 "This demonstrates viewporting: only visible items are rendered!"
119 .to_string(),
120 self.font_family,
121 14.,
122 )
123 .with_color(ColorU::black())
124 .finish(),
125 )
126 .with_child(
127 Text::new_inline(
128 "🔍 Check the console output to see which items are being rendered."
129 .to_string(),
130 self.font_family,
131 14.,
132 )
133 .with_color(ColorU::black())
134 .finish(),
135 )
136 .with_child(
137 Text::new_inline(
138 "🟥 Red borders mark milestone items (every 10th).".to_string(),
139 self.font_family,
140 14.,
141 )
142 .with_color(ColorU::black())
143 .finish(),
144 )
145 .with_child(
146 Text::new_inline(
147 "📏 Viewport height: 400px.".to_string(),
148 self.font_family,
149 12.,
150 )
151 .with_color(ColorU::black())
152 .finish(),
153 )
154 .finish(),
155 )
156 .with_height(140.)
157 .finish(),
158 )
159 .with_background_color(ColorU::new(230, 230, 255, 255))
160 .with_border(Border::all(2.).with_border_color(ColorU::new(100, 100, 200, 255)))
161 .finish()
162 }
163}
164 
165impl Entity for RootView {
166 type Event = ();
167}
168 
169impl View for RootView {
170 fn ui_name() -> &'static str {
171 "ListRootView"
172 }
173 
174 fn render(&self, _: &AppContext) -> Box<dyn Element> {
175 Stack::new()
176 .with_child(Rect::new().with_background_color(ColorU::white()).finish())
177 .with_child(
178 Flex::column()
179 .with_child(self.make_instructions())
180 .with_child(
181 Container::new(
182 ConstrainedBox::new(
183 Scrollable::vertical(
184 self.scroll_state.clone(),
185 List::new(self.list_state.clone()).finish_scrollable(),
186 ScrollbarWidth::Auto,
187 Fill::Solid(ColorU::new(150, 150, 150, 255)), // Non-active thumb
188 Fill::Solid(ColorU::new(100, 100, 100, 255)), // Active thumb
189 Fill::Solid(ColorU::new(240, 240, 240, 255)), // Track background
190 )
191 .finish(),
192 )
193 .with_height(400.) // Constrain the viewport height
194 .finish(),
195 )
196 .with_background_color(ColorU::new(250, 250, 250, 255))
197 .with_border(Border::all(2.).with_border_color(ColorU::black()))
198 .finish(),
199 )
200 .finish(),
201 )
202 .finish()
203 }
204}
205 
206impl TypedActionView for RootView {
207 type Action = ();
208}
209