StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.
| 1 | use strato_core::inspector::{inspector, InspectorConfig}; |
| 2 | use strato_core::types::Color; |
| 3 | use strato_platform::{application::Application, window::WindowBuilder}; |
| 4 | use strato_widgets::{ |
| 5 | input::{InputType, TextInput}, |
| 6 | layout::{CrossAxisAlignment, MainAxisAlignment}, |
| 7 | text::FontWeight, |
| 8 | Button, ButtonStyle, Column, Container, Dropdown, Flex, InspectorOverlay, Row, Text, Widget, |
| 9 | }; |
| 10 | use tracing::{info, warn}; |
| 11 | use tracing_subscriber::prelude::*; |
| 12 | |
| 13 | // Theme Colors |
| 14 | fn theme_bg() -> Color { |
| 15 | Color::rgba(0.09, 0.09, 0.11, 1.0) |
| 16 | } // #17171C |
| 17 | fn theme_surface() -> Color { |
| 18 | Color::rgba(0.13, 0.13, 0.16, 1.0) |
| 19 | } // #212129 |
| 20 | fn theme_surface_hover() -> Color { |
| 21 | Color::rgba(0.16, 0.16, 0.20, 1.0) |
| 22 | } // #292933 |
| 23 | fn theme_accent() -> Color { |
| 24 | Color::rgba(0.39, 0.35, 0.88, 1.0) |
| 25 | } // #6459E1 |
| 26 | fn theme_text_primary() -> Color { |
| 27 | Color::rgba(0.95, 0.95, 0.97, 1.0) |
| 28 | } |
| 29 | fn theme_text_secondary() -> Color { |
| 30 | Color::rgba(0.60, 0.60, 0.65, 1.0) |
| 31 | } |
| 32 | const BORDER_RADIUS: f32 = 12.0; |
| 33 | |
| 34 | fn main() -> anyhow::Result<()> { |
| 35 | // Setup logging to file |
| 36 | let file_appender = tracing_appender::rolling::daily("logs", "comprehensive_test.log"); |
| 37 | let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender); |
| 38 | |
| 39 | tracing_subscriber::registry() |
| 40 | .with( |
| 41 | tracing_subscriber::fmt::layer() |
| 42 | .with_writer(non_blocking) |
| 43 | .with_ansi(false), |
| 44 | ) |
| 45 | .with(tracing_subscriber::fmt::layer().with_writer(std::io::stdout)) |
| 46 | .init(); |
| 47 | |
| 48 | info!("Starting Comprehensive Test Example - Revolutionized UI"); |
| 49 | |
| 50 | inspector().configure(InspectorConfig { |
| 51 | enabled: true, |
| 52 | ..Default::default() |
| 53 | }); |
| 54 | |
| 55 | // Create application |
| 56 | let window_builder = WindowBuilder::new() |
| 57 | .with_title("StratoUI Dashboard") |
| 58 | .with_size(1280.0, 900.0) |
| 59 | .resizable(true); |
| 60 | |
| 61 | let mut app = Application::new("StratoUI Dashboard", window_builder); |
| 62 | |
| 63 | // Create UI structure |
| 64 | let main_container = InspectorOverlay::new( |
| 65 | Container::new().background(theme_bg()).padding(30.0).child( |
| 66 | Column::new() |
| 67 | .spacing(30.0) |
| 68 | .main_axis_alignment(MainAxisAlignment::Start) |
| 69 | .cross_axis_alignment(CrossAxisAlignment::Stretch) |
| 70 | .children(vec![ |
| 71 | create_header(), |
| 72 | // Main Content Grid (simulated with Row/Column) |
| 73 | Box::new( |
| 74 | Row::new() |
| 75 | .spacing(30.0) |
| 76 | .cross_axis_alignment(CrossAxisAlignment::Start) |
| 77 | .children(vec![ |
| 78 | // Left Column (Interactive) |
| 79 | Box::new(Flex::new(create_interactive_section())) |
| 80 | as Box<dyn Widget>, |
| 81 | // Right Column (Layouts/Cards) |
| 82 | Box::new(Flex::new(create_layout_section())) as Box<dyn Widget>, |
| 83 | ]), |
| 84 | ) as Box<dyn Widget>, |
| 85 | ]), |
| 86 | ), |
| 87 | ); |
| 88 | |
| 89 | app.set_root(Box::new(main_container)); |
| 90 | |
| 91 | info!("Application initialized, entering event loop"); |
| 92 | app.run() |
| 93 | } |
| 94 | |
| 95 | fn create_header() -> Box<dyn Widget> { |
| 96 | Box::new( |
| 97 | Container::new() |
| 98 | .background(theme_surface()) |
| 99 | .padding(25.0) |
| 100 | .border_radius(BORDER_RADIUS) |
| 101 | .child( |
| 102 | Row::new() |
| 103 | .main_axis_alignment(MainAxisAlignment::SpaceBetween) |
| 104 | .cross_axis_alignment(CrossAxisAlignment::Center) |
| 105 | .children(vec![ |
| 106 | Box::new(Column::new().spacing(5.0).children(vec![ |
| 107 | Box::new( |
| 108 | Text::new("StratoUI Dashboard") |
| 109 | .font_size(28.0) |
| 110 | .font_weight(FontWeight::Bold) |
| 111 | .color(theme_text_primary()) |
| 112 | ) as Box<dyn Widget>, |
| 113 | Box::new( |
| 114 | Text::new("Next-gen Rust Native UI Framework") |
| 115 | .font_size(14.0) |
| 116 | .color(theme_text_secondary()) |
| 117 | ) as Box<dyn Widget>, |
| 118 | ])) as Box<dyn Widget>, |
| 119 | Box::new(Row::new().spacing(15.0).children(vec![ |
| 120 | Box::new( |
| 121 | Button::new("Documentation") |
| 122 | .outline() |
| 123 | .on_click(|| info!("Docs clicked")) |
| 124 | ) as Box<dyn Widget>, |
| 125 | Box::new( |
| 126 | Button::new("New Project") |
| 127 | .primary() |
| 128 | .on_click(|| info!("New Project clicked")) |
| 129 | ) as Box<dyn Widget>, |
| 130 | ])) as Box<dyn Widget>, |
| 131 | ]), |
| 132 | ), |
| 133 | ) |
| 134 | } |
| 135 | |
| 136 | fn create_interactive_section() -> Box<dyn Widget> { |
| 137 | Box::new(Column::new().spacing(20.0).children(vec![ |
| 138 | create_section_title("Controls & Inputs"), |
| 139 | Container::new() |
| 140 | .background(theme_surface()) |
| 141 | .padding(20.0) |
| 142 | .border_radius(BORDER_RADIUS) |
| 143 | .child( |
| 144 | Column::new() |
| 145 | .spacing(25.0) |
| 146 | .cross_axis_alignment(CrossAxisAlignment::Stretch) |
| 147 | .children(vec![ |
| 148 | // Buttons Group |
| 149 | Box::new( |
| 150 | Column::new().spacing(10.0).children(vec![ |
| 151 | Box::new( |
| 152 | Text::new("Buttons") |
| 153 | .font_size(14.0) |
| 154 | .color(theme_text_secondary()), |
| 155 | ) as Box<dyn Widget>, |
| 156 | Box::new( |
| 157 | Row::new().spacing(10.0).children(vec![ |
| 158 | Box::new( |
| 159 | Flex::new(Box::new( |
| 160 | Button::new("Primary").primary(), |
| 161 | )) |
| 162 | .flex(1.0), |
| 163 | ) |
| 164 | as Box<dyn Widget>, |
| 165 | Box::new( |
| 166 | Flex::new(Box::new( |
| 167 | Button::new("Secondary").secondary(), |
| 168 | )) |
| 169 | .flex(1.0), |
| 170 | ) |
| 171 | as Box<dyn Widget>, |
| 172 | Box::new( |
| 173 | Flex::new(Box::new(Button::new("Danger").danger())) |
| 174 | .flex(1.0), |
| 175 | ) |
| 176 | as Box<dyn Widget>, |
| 177 | ]), |
| 178 | ) as Box<dyn Widget>, |
| 179 | ]), |
| 180 | ) as Box<dyn Widget>, |
| 181 | // Dropdown Group |
| 182 | Box::new( |
| 183 | Column::new().spacing(10.0).children(vec![ |
| 184 | Box::new( |
| 185 | Text::new("Selection") |
| 186 | .font_size(14.0) |
| 187 | .color(theme_text_secondary()), |
| 188 | ) as Box<dyn Widget>, |
| 189 | Box::new( |
| 190 | Dropdown::new() |
| 191 | .add_value("Development".to_string()) |
| 192 | .add_value("Staging".to_string()) |
| 193 | .add_value("Production".to_string()) |
| 194 | .placeholder("Select Environment".to_string()), |
| 195 | ) as Box<dyn Widget>, |
| 196 | ]), |
| 197 | ) as Box<dyn Widget>, |
| 198 | // Inputs Group |
| 199 | Box::new( |
| 200 | Column::new().spacing(15.0).children(vec![ |
| 201 | Box::new( |
| 202 | Text::new("Authentication") |
| 203 | .font_size(14.0) |
| 204 | .color(theme_text_secondary()), |
| 205 | ) as Box<dyn Widget>, |
| 206 | Box::new(TextInput::new().placeholder("Username or Email")) |
| 207 | as Box<dyn Widget>, |
| 208 | Box::new( |
| 209 | TextInput::new() |
| 210 | .placeholder("Password") |
| 211 | .input_type(InputType::Password), |
| 212 | ) as Box<dyn Widget>, |
| 213 | ]), |
| 214 | ) as Box<dyn Widget>, |
| 215 | ]), |
| 216 | ) |
| 217 | .into_boxed(), |
| 218 | ])) |
| 219 | } |
| 220 | |
| 221 | fn create_layout_section() -> Box<dyn Widget> { |
| 222 | Box::new(Column::new().spacing(20.0).children(vec![ |
| 223 | create_section_title("Project Status"), |
| 224 | // Stats Row |
| 225 | Box::new(Row::new().spacing(20.0).children(vec![ |
| 226 | create_stat_card("Active Users", "12.5k", "+15%", theme_accent()), |
| 227 | create_stat_card("Server Load", "42%", "-5%", Color::rgba(0.2, 0.8, 0.4, 1.0)), |
| 228 | create_stat_card("Errors", "0.01%", "-2%", Color::rgba(0.9, 0.3, 0.3, 1.0)), |
| 229 | ])) as Box<dyn Widget>, |
| 230 | // Detailed Cards |
| 231 | Box::new( |
| 232 | Container::new() |
| 233 | .background(theme_surface()) |
| 234 | .padding(20.0) |
| 235 | .border_radius(BORDER_RADIUS) |
| 236 | .child( |
| 237 | Column::new().spacing(15.0).children(vec![ |
| 238 | Box::new( |
| 239 | Text::new("Recent Activity") |
| 240 | .font_size(16.0) |
| 241 | .font_weight(FontWeight::SemiBold) |
| 242 | .color(theme_text_primary()), |
| 243 | ) as Box<dyn Widget>, |
| 244 | create_activity_item("Deployment #1024", "Successful", "2 mins ago"), |
| 245 | create_activity_item("Database Backup", "Processing", "15 mins ago"), |
| 246 | create_activity_item("User Registration", "New User", "1 hour ago"), |
| 247 | ]), |
| 248 | ), |
| 249 | ) as Box<dyn Widget>, |
| 250 | ])) |
| 251 | } |
| 252 | |
| 253 | fn create_section_title(title: &str) -> Box<dyn Widget> { |
| 254 | Box::new( |
| 255 | Text::new(title) |
| 256 | .font_size(18.0) |
| 257 | .font_weight(FontWeight::SemiBold) |
| 258 | .color(theme_text_primary()), |
| 259 | ) |
| 260 | } |
| 261 | |
| 262 | fn create_stat_card(label: &str, value: &str, trend: &str, trend_color: Color) -> Box<dyn Widget> { |
| 263 | Box::new(Flex::new(Box::new( |
| 264 | Container::new() |
| 265 | .background(theme_surface()) |
| 266 | .padding(20.0) |
| 267 | .border_radius(BORDER_RADIUS) |
| 268 | .child(Column::new().spacing(10.0).children(vec![ |
| 269 | Box::new( |
| 270 | Text::new(label) |
| 271 | .font_size(14.0) |
| 272 | .color(theme_text_secondary()), |
| 273 | ) as Box<dyn Widget>, |
| 274 | Box::new( |
| 275 | Text::new(value) |
| 276 | .font_size(24.0) |
| 277 | .font_weight(FontWeight::Bold) |
| 278 | .color(theme_text_primary()), |
| 279 | ) as Box<dyn Widget>, |
| 280 | Box::new(Text::new(trend).font_size(12.0).color(trend_color)) |
| 281 | as Box<dyn Widget>, |
| 282 | ])), |
| 283 | ))) |
| 284 | } |
| 285 | |
| 286 | fn create_activity_item(title: &str, status: &str, time: &str) -> Box<dyn Widget> { |
| 287 | Box::new( |
| 288 | Container::new() |
| 289 | .background(theme_surface_hover()) |
| 290 | .padding(12.0) |
| 291 | .border_radius(8.0) |
| 292 | .child( |
| 293 | Row::new() |
| 294 | .main_axis_alignment(MainAxisAlignment::SpaceBetween) |
| 295 | .cross_axis_alignment(CrossAxisAlignment::Center) |
| 296 | .children(vec![ |
| 297 | Box::new(Column::new().spacing(4.0).children(vec![ |
| 298 | Box::new(Text::new(title).font_size(14.0).color(theme_text_primary())) |
| 299 | as Box<dyn Widget>, |
| 300 | Box::new(Text::new(status).font_size(12.0).color(theme_accent())) |
| 301 | as Box<dyn Widget>, |
| 302 | ])) as Box<dyn Widget>, |
| 303 | Box::new( |
| 304 | Text::new(time) |
| 305 | .font_size(12.0) |
| 306 | .color(theme_text_secondary()), |
| 307 | ) as Box<dyn Widget>, |
| 308 | ]), |
| 309 | ), |
| 310 | ) |
| 311 | } |
| 312 | |
| 313 | // Extension trait helper to box widgets easily |
| 314 | trait WidgetExt { |
| 315 | fn into_boxed(self) -> Box<dyn Widget>; |
| 316 | } |
| 317 | |
| 318 | impl<T: Widget + 'static> WidgetExt for T { |
| 319 | fn into_boxed(self) -> Box<dyn Widget> { |
| 320 | Box::new(self) |
| 321 | } |
| 322 | } |
| 323 |