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
examples/modern_dashboard/src/main.rs
1use strato_core::inspector::{inspector, InspectorConfig};
2use strato_core::types::Color;
3use strato_platform::{application::ApplicationBuilder, window::WindowBuilder};
4use strato_widgets::{
5 container::Container,
6 image::Image,
7 layout::{Column, Flex, Row},
8 prelude::*,
9 text::Text,
10 top_bar::TopBar,
11 InspectorOverlay,
12};
13 
14fn main() {
15 // We don't need registry for direct widget construction
16 inspector().configure(InspectorConfig {
17 enabled: true,
18 ..Default::default()
19 });
20 
21 let root_widget = InspectorOverlay::new(build_ui());
22 
23 ApplicationBuilder::new()
24 .window(
25 WindowBuilder::new()
26 .with_title("Modern Dashboard")
27 .with_size(1200.0, 800.0)
28 .resizable(true)
29 .transparent(true) // Enable glassmorphism support
30 .decorations(true), // Restore native window controls
31 )
32 .run(root_widget);
33}
34 
35// --- Theme Colors ---
36// Catppuccin-inspired Logic format (0.0-1.0)
37// --- Theme Colors (Refined) ---
38fn col_bg() -> Color {
39 Color::rgba(0.13, 0.13, 0.18, 0.90)
40} // Semi-transparent background
41fn col_sidebar() -> Color {
42 Color::rgba(0.18, 0.18, 0.25, 0.95)
43}
44fn col_card_bg() -> Color {
45 Color::rgba(0.20, 0.20, 0.28, 0.8)
46} // Glassy cards
47fn col_text() -> Color {
48 Color::rgba(0.95, 0.95, 0.95, 1.0)
49}
50fn col_text_dim() -> Color {
51 Color::rgba(0.70, 0.70, 0.80, 1.0)
52}
53fn col_subtext() -> Color {
54 Color::rgba(0.60, 0.60, 0.70, 1.0)
55}
56fn col_accent() -> Color {
57 Color::rgba(0.12, 0.45, 0.98, 1.0)
58}
59fn col_header() -> Color {
60 Color::rgba(0.50, 0.50, 0.60, 1.0)
61}
62 
63fn build_ui() -> Container {
64 Container::new()
65 .background(Color::TRANSPARENT)
66 .child(Row::new()
67 .children(vec![
68 // Sidebar (Fixed Width, Full Height)
69 Box::new(Container::new()
70 .width(260.0)
71 .height(800.0)
72 .background(col_sidebar())
73 .padding(0.0)
74 .border_radius(12.0)
75 .margin(10.0)
76 .child(Column::new()
77 .spacing(2.0)
78 .children(vec![
79 // Window Controls Spacer
80 Box::new(Container::new().height(20.0).child(Text::new(""))),
81 
82 // User Profile
83 Box::new(Container::new()
84 .padding(16.0)
85 .margin(0.0)
86 .child(Row::new()
87 .spacing(12.0)
88 .children(vec![
89 // Avatar
90 Box::new(Container::new()
91 .width(32.0).height(32.0).border_radius(4.0).background(Color::WHITE)
92 .child(Image::from_url("https://avatars.githubusercontent.com/u/109359355?v=4")
93 .border_radius(4.0)
94 )),
95 Box::new(Text::new("SeregonWar").color(col_text()).size(14.0))
96 ])
97 )
98 ),
99 
100 // TIMELINES
101 section_header("TIMELINES"),
102 sidebar_item("Classic Timeline", "📅", false),
103 sidebar_item("Local", "👥", false),
104 sidebar_item("Federated", "🌐", false),
105 
106 // ACCOUNT
107 Box::new(Container::new().height(20.0).child(Text::new(""))),
108 section_header("ACCOUNT"),
109 sidebar_item("Your Posts", "📝", false),
110 sidebar_item("Followers", "👥", false),
111 sidebar_item("Following", "👤", true),
112 sidebar_item("Bookmarks", "🔖", false),
113 sidebar_item("Favorites", "⭐️", false),
114 
115 // Spacer (Pushes content down)
116 Box::new(Flex::new(
117 Box::new(Container::new().height(1.0).child(Text::new("")))
118 ).flex(1.0)),
119 
120 // Theme Switcher (At bottom)
121 Box::new(Container::new()
122 .background(Color::rgba(1.0, 1.0, 1.0, 0.05))
123 .padding(10.0)
124 .border_radius(6.0)
125 .width(240.0)
126 .child(Row::new()
127 .spacing(12.0)
128 .children(vec![
129 Box::new(Text::new("sun").size(14.0)),
130 Box::new(Text::new("Toggle Theme").color(col_text_dim()).size(14.0))
131 ])
132 )
133 .on_click(|| {
134 println!("Theme toggle clicked - Dynamic switching pending");
135 })
136 )
137 ])
138 )
139 ),
140 
141 // Main Content Area
142 Box::new(Container::new()
143 .padding(0.0)
144 .width(900.0)
145 .child(Column::new()
146 .spacing(0.0)
147 .children(vec![
148 // TopBar
149 Box::new(TopBar::new("Following".to_string())
150 .with_background(col_bg())
151 .height(60.0)),
152 
153 // Feed
154 Box::new(Container::new()
155 .padding(24.0)
156 .background(col_bg()) // Main bg
157 .border_radius(12.0)
158 .margin(10.0)
159 .child(Column::new()
160 .spacing(16.0)
161 .children(vec![
162 card_item(
163 "NDR",
164 "@NDR",
165 "Moin! Hier gibt's pro Tag 3-5 Nachrichten aus Hamburg...",
166 "146 Posts 7 Following 5k Followers",
167 "https://avatars.githubusercontent.com/u/1?v=4"
168 ),
169 card_item(
170 "tagesschau",
171 "@tagesschau",
172 "Hier trötet die tagesschau Nachrichten von https://www.tagesschau.de/",
173 "683 Posts 4 Following 20k Followers",
174 "https://avatars.githubusercontent.com/u/2?v=4"
175 ),
176 card_item(
177 "Simon Willison",
178 "@simon",
179 "Open source developer building tools to help journalists...",
180 "3k Posts 1k Following 17k Followers",
181 "https://avatars.githubusercontent.com/u/3?v=4"
182 ),
183 ])
184 )
185 )
186 ])
187 )
188 )
189 ])
190 )
191}
192 
193// --- Components ---
194 
195fn section_header(title: &str) -> Box<dyn Widget> {
196 Box::new(
197 Container::new()
198 .padding(16.0) // Left padding alignment
199 .margin(0.0)
200 .child(Text::new(title).color(col_header()).size(11.0)),
201 )
202}
203 
204fn sidebar_item(label: &str, icon: &str, active: bool) -> Box<dyn Widget> {
205 let bg_color = if active {
206 col_accent()
207 } else {
208 Color::TRANSPARENT
209 };
210 let text_color = if active { Color::WHITE } else { col_subtext() };
211 let label_clone = label.to_string();
212 
213 Box::new(
214 Container::new()
215 .background(bg_color)
216 .margin(0.0)
217 .padding(10.0)
218 .border_radius(6.0) // Rounded active item
219 .width(240.0) // Slight inset from full width
220 .child(Row::new().spacing(12.0).children(vec![
221 Box::new(Text::new(icon).size(14.0)), // Smaller icons
222 Box::new(Text::new(label).color(text_color).size(14.0)),
223 ]))
224 .on_click(move || {
225 println!("Clicked sidebar item: {}", label_clone);
226 }),
227 )
228}
229 
230fn card_item(
231 name: &str,
232 handle: &str,
233 content: &str,
234 stats: &str,
235 avatar_url: &str,
236) -> Box<dyn Widget> {
237 Box::new(
238 Container::new()
239 .background(col_card_bg())
240 .padding(16.0)
241 .border_radius(10.0) // Smooth card rounding
242 .width(600.0) // Fixed card width for specific look
243 .child(Column::new().spacing(12.0).children(vec![
244 // Header Row
245 Box::new(Row::new()
246 .spacing(12.0)
247 .children(vec![
248 // Avatar
249 Box::new(Container::new()
250 .width(44.0).height(44.0).border_radius(4.0)
251 .child(Image::from_url(avatar_url).border_radius(4.0))
252 ),
253 // Name & Handle
254 Box::new(Column::new()
255 .spacing(2.0)
256 .children(vec![
257 Box::new(Text::new(name).color(col_text()).size(15.0)),
258 Box::new(Text::new(handle).color(col_subtext()).size(13.0))
259 ])
260 ),
261 Box::new(Container::new().width(180.0).child(Text::new(""))), // Flex Spacer
262 // Button
263 Box::new(Container::new()
264 .background(col_accent())
265 .border_radius(14.0) // Pill shape
266 .padding(6.0)
267 .width(60.0)
268 .child(Container::new()
269 // Centering hack (via padding)
270 .padding(0.0).margin(0.0)
271 .child(Text::new("Open").color(Color::WHITE).size(12.0))
272 )
273 .on_click(|| {
274 println!("Clicked Open button");
275 })
276 )
277 ])
278 ),
279 // Content Body
280 Box::new(Container::new()
281 .padding(0.0)
282 .child(Text::new(content).color(col_subtext()).size(14.0))
283 ),
284 // Footer Stats
285 Box::new(Container::new()
286 .padding(0.0)
287 .child(Text::new(stats).color(col_header()).size(12.0))
288 )
289 ])),
290 )
291}
292