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/autotracking/root_view.rs
1use pathfinder_color::ColorU;
2use strato_ui::elements::DispatchEventResult;
3use strato_ui::fonts::FamilyId;
4use strato_ui::{
5 elements::{
6 Align, Border, ChildView, Container, CornerRadius, EventHandler, Flex, ParentElement,
7 Radius, Rect, Stack, Text,
8 },
9 AppContext, Element, Entity, ModelHandle, SingletonEntity, Tracked, TypedActionView, View,
10 ViewContext, ViewHandle,
11};
12 
13pub fn init(ctx: &mut AppContext) {
14 ctx.add_singleton_model(|_| Settings {
15 dark_mode: Tracked::new(false),
16 });
17}
18 
19pub struct RootView {
20 main: ViewHandle<MainView>,
21}
22 
23impl RootView {
24 pub fn new(ctx: &mut ViewContext<Self>) -> Self {
25 let main = ctx.add_typed_action_view(MainView::new);
26 
27 RootView { main }
28 }
29}
30 
31impl Entity for RootView {
32 type Event = ();
33}
34 
35impl View for RootView {
36 fn ui_name() -> &'static str {
37 "RootView"
38 }
39 
40 fn render(&self, app: &AppContext) -> Box<dyn Element> {
41 let dark_mode = *Settings::as_ref(app).dark_mode;
42 
43 Stack::new()
44 .with_child(
45 Rect::new()
46 .with_background_color(if dark_mode {
47 ColorU::black()
48 } else {
49 ColorU::white()
50 })
51 .finish(),
52 )
53 .with_child(ChildView::new(&self.main).finish())
54 .finish()
55 }
56}
57 
58struct Settings {
59 dark_mode: Tracked<bool>,
60}
61 
62impl Entity for Settings {
63 type Event = ();
64}
65 
66impl SingletonEntity for Settings {}
67 
68#[derive(Default)]
69struct Counter {
70 value: Tracked<isize>,
71}
72 
73impl Counter {
74 fn increment(&mut self) {
75 *self.value += 1;
76 }
77 
78 fn decrement(&mut self) {
79 *self.value -= 1;
80 }
81 
82 fn value(&self) -> isize {
83 *self.value
84 }
85}
86 
87impl Entity for Counter {
88 type Event = ();
89}
90 
91struct MainView {
92 model: ModelHandle<Counter>,
93 stored: Tracked<Option<isize>>,
94 font_family: FamilyId,
95}
96 
97#[derive(Clone, Copy, Debug)]
98enum MainViewAction {
99 Increment,
100 Decrement,
101 Save,
102 Restore,
103 ToggleDarkMode,
104}
105 
106impl MainView {
107 fn new(ctx: &mut ViewContext<Self>) -> Self {
108 let model = ctx.add_model(|_| Counter::default());
109 let font_family = strato_ui::fonts::Cache::handle(ctx)
110 .update(ctx, |cache, _| cache.load_system_font("Arial").unwrap());
111 MainView {
112 model,
113 stored: Tracked::new(None),
114 font_family,
115 }
116 }
117}
118 
119impl Entity for MainView {
120 type Event = ();
121}
122 
123impl TypedActionView for MainView {
124 type Action = MainViewAction;
125 
126 fn handle_action(&mut self, action: &Self::Action, ctx: &mut ViewContext<Self>) {
127 use MainViewAction::*;
128 
129 match action {
130 Increment => self.model.update(ctx, |model, _| model.increment()),
131 Decrement => self.model.update(ctx, |model, _| model.decrement()),
132 Save => {
133 let current = self.model.read(ctx, |model, _| model.value());
134 *self.stored = Some(current);
135 }
136 Restore => {
137 if let Some(stored) = self.stored.take() {
138 self.model.update(ctx, |model, _| *model.value = stored);
139 }
140 }
141 ToggleDarkMode => {
142 Settings::handle(ctx).update(ctx, |settings, _| {
143 *settings.dark_mode = !*settings.dark_mode;
144 });
145 }
146 }
147 }
148}
149 
150impl View for MainView {
151 fn ui_name() -> &'static str {
152 "MainView"
153 }
154 
155 fn render(&self, app: &AppContext) -> Box<dyn Element> {
156 let dark_mode = *Settings::as_ref(app).dark_mode;
157 let text_color = if dark_mode {
158 ColorU::white()
159 } else {
160 ColorU::black()
161 };
162 let counter = self.model.as_ref(app).value();
163 
164 Align::new(
165 Flex::column()
166 .with_child(
167 Align::new(
168 Flex::row()
169 .with_child(render_button(
170 Text::new_inline("-", self.font_family, 16.)
171 .with_color(text_color)
172 .finish(),
173 MainViewAction::Decrement,
174 ))
175 .with_child(
176 Text::new_inline(format!("{counter}"), self.font_family, 16.)
177 .with_color(text_color)
178 .finish(),
179 )
180 .with_child(render_button(
181 Text::new_inline("+", self.font_family, 16.)
182 .with_color(text_color)
183 .finish(),
184 MainViewAction::Increment,
185 ))
186 .finish(),
187 )
188 .finish(),
189 )
190 .with_child(
191 Align::new(
192 Flex::row()
193 .with_child(render_button(
194 Text::new_inline("Toggle Dark Mode", self.font_family, 16.)
195 .with_color(text_color)
196 .finish(),
197 MainViewAction::ToggleDarkMode,
198 ))
199 .with_child(if self.stored.is_some() {
200 render_button(
201 Text::new_inline("Restore", self.font_family, 16.)
202 .with_color(text_color)
203 .finish(),
204 MainViewAction::Restore,
205 )
206 } else {
207 render_button(
208 Text::new_inline("Save Value", self.font_family, 16.)
209 .with_color(text_color)
210 .finish(),
211 MainViewAction::Save,
212 )
213 })
214 .finish(),
215 )
216 .finish(),
217 )
218 .finish(),
219 )
220 .finish()
221 }
222}
223 
224fn render_button(inner: Box<dyn Element>, action: MainViewAction) -> Box<dyn Element> {
225 Container::new(
226 EventHandler::new(
227 Container::new(inner)
228 .with_corner_radius(CornerRadius::with_all(Radius::Pixels(4.)))
229 .with_border(Border::all(1.).with_border_color(ColorU::new(128, 128, 128, 255)))
230 .with_uniform_padding(4.)
231 .finish(),
232 )
233 .on_left_mouse_down(move |ctx, _, _| {
234 ctx.dispatch_typed_action(action);
235 DispatchEventResult::StopPropagation
236 })
237 .finish(),
238 )
239 .with_uniform_margin(4.)
240 .finish()
241}
242 
243impl TypedActionView for RootView {
244 type Action = ();
245}
246