From a6fa38bb557be1db9429017082ac5a34cee23d9f Mon Sep 17 00:00:00 2001 From: Seregon <109359355+seregonwar@users.noreply.github.com> Date: Wed, 17 Dec 2025 22:33:34 +0100 Subject: [PATCH] Add inspector overlay to examples --- examples/advanced_renderer/src/main.rs | 126 +++--- examples/animated_showcase/src/main.rs | 66 ++-- examples/calculator/src/main.rs | 321 ++++++++------- examples/complex_demo/src/main.rs | 221 ++++++----- examples/comprehensive_test/src/main.rs | 366 +++++++++--------- examples/counter/src/main.rs | 58 +-- examples/custom_init/src/main.rs | 13 +- examples/hello_world/src/main.rs | 69 ++-- examples/macro_showcase/src/main.rs | 15 +- examples/modern_dashboard/src/app.rs | 6 +- .../src/components/activity_list.rs | 6 +- .../src/components/animated_chart.rs | 10 +- .../src/components/sidebar.rs | 6 +- .../src/components/stats_card.rs | 4 +- examples/modern_dashboard/src/main.rs | 157 ++++---- .../modern_dashboard/src/views/dashboard.rs | 4 +- 16 files changed, 799 insertions(+), 649 deletions(-) diff --git a/examples/advanced_renderer/src/main.rs b/examples/advanced_renderer/src/main.rs index 118930e..98cdbf3 100644 --- a/examples/advanced_renderer/src/main.rs +++ b/examples/advanced_renderer/src/main.rs @@ -1,16 +1,15 @@ use anyhow::Result; +use std::sync::Arc; use strato_renderer::{ - IntegratedRenderer, RendererBuilder, RendererConfig, - AllocationStrategy, RenderContext, + AllocationStrategy, IntegratedRenderer, RenderContext, RendererBuilder, RendererConfig, }; +use tracing::{error, info}; use wgpu::*; use winit::{ event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::{Window, WindowBuilder}, }; -use std::sync::Arc; -use tracing::{info, error}; /// Advanced renderer example demonstrating the complete wgpu system struct AdvancedRendererExample { @@ -18,12 +17,12 @@ struct AdvancedRendererExample { surface: Surface<'static>, surface_config: SurfaceConfiguration, renderer: IntegratedRenderer, - + // Example resources vertex_buffer: Option, index_buffer: Option, render_pipeline: Option, - + // State frame_count: u64, } @@ -31,15 +30,15 @@ struct AdvancedRendererExample { impl AdvancedRendererExample { async fn new(window: Arc) -> Result { info!("Initializing advanced renderer example"); - + // Create surface let instance = Instance::new(InstanceDescriptor { backends: Backends::PRIMARY, ..Default::default() }); - + let surface = instance.create_surface(window.clone())?; - + // Create renderer with performance configuration let mut renderer = RendererBuilder::new() .with_instance(instance) @@ -52,9 +51,10 @@ impl AdvancedRendererExample { .with_validation(cfg!(debug_assertions)) .build() .await?; - + // Configure surface - let adapter = renderer.get_active_adapter() + let adapter = renderer + .get_active_adapter() .expect("Current adapter not found"); let size = window.inner_size(); @@ -68,16 +68,16 @@ impl AdvancedRendererExample { view_formats: vec![], desired_maximum_frame_latency: 2, }; - + surface.configure(&renderer.device().device, &surface_config); - + // Initialize renderer renderer.initialize().await?; - + info!("Renderer initialized successfully"); info!("GPU: {}", renderer.get_device_info().device_name); // info!("Backend: {:?}", renderer.get_device_info().backend); - + let mut example = Self { window, surface, @@ -88,36 +88,36 @@ impl AdvancedRendererExample { render_pipeline: None, frame_count: 0, }; - + // Create example resources example.create_resources().await?; - + Ok(example) } - + async fn create_resources(&mut self) -> Result<()> { info!("Creating example resources"); - + // Create vertex buffer let vertices: &[f32] = &[ // Triangle vertices (position + color) -0.5, -0.5, 1.0, 0.0, 0.0, // Bottom left - Red - 0.5, -0.5, 0.0, 1.0, 0.0, // Bottom right - Green - 0.0, 0.5, 0.0, 0.0, 1.0, // Top - Blue + 0.5, -0.5, 0.0, 1.0, 0.0, // Bottom right - Green + 0.0, 0.5, 0.0, 0.0, 1.0, // Top - Blue ]; - + let vertex_buffer = self.renderer.create_buffer( (vertices.len() * std::mem::size_of::()) as u64, BufferUsages::VERTEX | BufferUsages::COPY_DST, )?; - + // Create index buffer let indices: &[u16] = &[0, 1, 2]; let index_buffer = self.renderer.create_buffer( (indices.len() * std::mem::size_of::()) as u64, BufferUsages::INDEX | BufferUsages::COPY_DST, )?; - + // Load shader let path = std::path::PathBuf::from("examples/advanced_renderer/shaders/triangle.wgsl"); let stage = strato_renderer::shader::ShaderStage::Vertex; @@ -126,9 +126,9 @@ impl AdvancedRendererExample { features: vec![], optimization_level: 0, }; - + let shader = self.renderer.load_shader(&path, stage, variant)?; - + // Create render pipeline let render_pipeline_desc = RenderPipelineDescriptor { label: Some("Triangle Pipeline"), @@ -181,17 +181,21 @@ impl AdvancedRendererExample { }, multiview: None, }; - - let render_pipeline = self.renderer.device().device.create_render_pipeline(&render_pipeline_desc); + + let render_pipeline = self + .renderer + .device() + .device + .create_render_pipeline(&render_pipeline_desc); self.render_pipeline = Some(render_pipeline); - + self.vertex_buffer = Some(vertex_buffer); self.index_buffer = Some(index_buffer); - + info!("Resources created successfully"); Ok(()) } - + fn render(&mut self) -> Result<()> { // Resolve resources first to ensure they live long enough for the render pass let vertex_buffer_res = if let Some(handle) = self.vertex_buffer { @@ -199,7 +203,7 @@ impl AdvancedRendererExample { } else { None }; - + let index_buffer_res = if let Some(handle) = self.index_buffer { self.renderer.get_buffer(handle) } else { @@ -208,11 +212,13 @@ impl AdvancedRendererExample { // Begin frame let mut render_context = self.renderer.begin_frame()?; - + // Get surface texture let output = self.surface.get_current_texture()?; - let view = output.texture.create_view(&TextureViewDescriptor::default()); - + let view = output + .texture + .create_view(&TextureViewDescriptor::default()); + // Begin render pass let mut render_pass = render_context.begin_render_pass(&RenderPassDescriptor { label: Some("Main Render Pass"), @@ -233,53 +239,53 @@ impl AdvancedRendererExample { occlusion_query_set: None, timestamp_writes: None, }); - + // Draw triangle - if let (Some(pipeline), Some(vb), Some(ib)) = ( - &self.render_pipeline, - &vertex_buffer_res, - &index_buffer_res, - ) { + if let (Some(pipeline), Some(vb), Some(ib)) = + (&self.render_pipeline, &vertex_buffer_res, &index_buffer_res) + { render_pass.set_pipeline(pipeline); render_pass.set_vertex_buffer(0, vb.slice(..)); render_pass.set_index_buffer(ib.slice(..), IndexFormat::Uint16); render_pass.draw_indexed(0..3, 0, 0..1); } - + drop(render_pass); render_context.end_render_pass(); - + // End frame self.renderer.end_frame(render_context)?; - + // Present output.present(); - + self.frame_count += 1; - + // Print stats every 60 frames if self.frame_count % 60 == 0 { let stats = self.renderer.get_stats(); - info!("Frame {}: {:.2}ms avg, {}MB memory, {} resources", + info!( + "Frame {}: {:.2}ms avg, {}MB memory, {} resources", stats.frame_count, stats.average_frame_time * 1000.0, stats.memory_usage / (1024 * 1024), stats.active_resources ); - + if let Some(report) = self.renderer.get_performance_report() { info!("Performance report: {:#?}", report); } } - + Ok(()) } - + fn resize(&mut self, new_size: winit::dpi::PhysicalSize) -> Result<()> { if new_size.width > 0 && new_size.height > 0 { self.surface_config.width = new_size.width; self.surface_config.height = new_size.height; - self.surface.configure(&self.renderer.device().device, &self.surface_config); + self.surface + .configure(&self.renderer.device().device, &self.surface_config); self.renderer.resize((new_size.width, new_size.height))?; } Ok(()) @@ -289,26 +295,26 @@ impl AdvancedRendererExample { async fn run() -> Result<()> { // Initialize tracing tracing_subscriber::fmt::init(); - + info!("Starting advanced renderer example"); - + // Create event loop and window let event_loop = EventLoop::new()?; let window = Arc::new( WindowBuilder::new() .with_title("Advanced wgpu Renderer Example") .with_inner_size(winit::dpi::LogicalSize::new(800, 600)) - .build(&event_loop)? + .build(&event_loop)?, ); - + // Create example let mut example = AdvancedRendererExample::new(window.clone()).await?; - + info!("Starting event loop"); - + event_loop.run(move |event, elwt| { elwt.set_control_flow(ControlFlow::Poll); - + match event { Event::WindowEvent { ref event, @@ -336,7 +342,7 @@ async fn run() -> Result<()> { _ => {} } })?; - + Ok(()) } @@ -346,4 +352,4 @@ async fn main() { error!("Application error: {}", e); std::process::exit(1); } -} \ No newline at end of file +} diff --git a/examples/animated_showcase/src/main.rs b/examples/animated_showcase/src/main.rs index f7d3dc1..e6c204a 100644 --- a/examples/animated_showcase/src/main.rs +++ b/examples/animated_showcase/src/main.rs @@ -1,15 +1,22 @@ +use std::time::Duration; +use strato_core::inspector::{inspector, InspectorConfig}; +use strato_core::state::Signal; +use strato_core::types::Color; use strato_platform::{ApplicationBuilder, WindowBuilder}; +use strato_widgets::animation::{Curve, KeyframeAnimation, Parallel, Timeline, Tween}; use strato_widgets::prelude::*; use strato_widgets::scroll_view::ScrollView; -use strato_widgets::animation::{Timeline, Parallel, KeyframeAnimation, Tween, Curve}; -use strato_core::types::Color; -use strato_core::state::Signal; -use std::time::Duration; +use strato_widgets::InspectorOverlay; #[tokio::main] async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt::init(); + inspector().configure(InspectorConfig { + enabled: true, + ..Default::default() + }); + // Create state for animation targets let box_color = Signal::new(Color::RED); let box_width = Signal::new(100.0); @@ -17,53 +24,56 @@ async fn main() -> anyhow::Result<()> { // Create a ScrollView content let mut list = Column::new().spacing(10.0); for i in 0..20 { - list = list.child( - Box::new(Container::new() + list = list.child(Box::new( + Container::new() .padding(20.0) - .background(if i % 2 == 0 { Color::rgba(0.2, 0.2, 0.2, 1.0) } else { Color::rgba(0.3, 0.3, 0.3, 1.0) }) - .child(Text::new(format!("Scroll Item {}", i)).color(Color::WHITE))) - ); + .background(if i % 2 == 0 { + Color::rgba(0.2, 0.2, 0.2, 1.0) + } else { + Color::rgba(0.3, 0.3, 0.3, 1.0) + }) + .child(Text::new(format!("Scroll Item {}", i)).color(Color::WHITE)), + )); } let scroll_view = ScrollView::new(list); // Setup animation let mut timeline = Timeline::new(); - + // Animation 1: Change color Red -> Blue let color_anim = KeyframeAnimation::new( Duration::from_secs(2), Tween::new(Color::RED, Color::BLUE), - box_color.clone() - ).with_curve(Curve::EaseInOut); + box_color.clone(), + ) + .with_curve(Curve::EaseInOut); // Animation 2: Change width 100 -> 300 let width_anim = KeyframeAnimation::new( Duration::from_secs(2), Tween::new(100.0, 300.0), - box_width.clone() - ).with_curve(Curve::EaseOut); + box_width.clone(), + ) + .with_curve(Curve::EaseOut); // Run parallel let parallel = Parallel::new(vec![Box::new(color_anim), Box::new(width_anim)]); - + timeline.add(parallel); timeline.play(); ApplicationBuilder::new() .title("Animated Showcase") .window(WindowBuilder::new().with_size(1024.0, 768.0)) - .run(Container::new() - .child( - Row::new() - .child(Box::new(scroll_view)) - .child( - Box::new(Container::new() - .width(300.0) - .background(Color::BLACK) - .child(Text::new("Animation Placeholder"))) - - ) - ) - ); + .run(InspectorOverlay::new( + Container::new().child( + Row::new().child(Box::new(scroll_view)).child(Box::new( + Container::new() + .width(300.0) + .background(Color::BLACK) + .child(Text::new("Animation Placeholder")), + )), + ), + )); } diff --git a/examples/calculator/src/main.rs b/examples/calculator/src/main.rs index 3840aef..fdc1373 100644 --- a/examples/calculator/src/main.rs +++ b/examples/calculator/src/main.rs @@ -1,21 +1,17 @@ -use strato_sdk::prelude::*; -use strato_widgets::{ - - text::TextAlign, - Flex, -}; +use std::any::Any; +use std::sync::{Arc, Mutex}; +use std::time::Duration; +use strato_core::event::{Event, EventResult, MouseEvent}; +use strato_core::inspector::{inspector, InspectorConfig}; +use strato_core::state::Signal; +use strato_core::types::{Color, Point, Rect, Transform}; use strato_platform::{ - ApplicationBuilder, - WindowBuilder, init::{InitBuilder, InitConfig}, + ApplicationBuilder, WindowBuilder, }; -use strato_core::event::{Event, EventResult, MouseEvent}; -use strato_core::types::{Point, Rect, Transform, Color}; -use strato_core::state::Signal; +use strato_sdk::prelude::*; use strato_widgets::animation::{AnimationController, Curve}; -use std::sync::{Arc, Mutex}; -use std::time::Duration; -use std::any::Any; +use strato_widgets::{text::TextAlign, Flex, InspectorOverlay}; #[derive(Clone, Debug)] enum Operation { @@ -28,7 +24,7 @@ enum Operation { #[derive(Clone, Debug)] struct CalculatorState { display: Signal, - expression: Signal, // Shows full operation like "12 + 5" + expression: Signal, // Shows full operation like "12 + 5" history: Signal>, // Stores past calculations current_value: f64, previous_value: f64, @@ -63,12 +59,12 @@ impl CalculatorState { self.display.update(|s| s.push_str(&digit.to_string())); } } - + // Update expression // If we just finished an operation (equals), start over unless an operator was pressed // For simplicity in this step, we just append to expression if it's part of the current number // A more robust approach updates expression based entirely on state - + self.current_value = self.display.get().parse().unwrap_or(0.0); } @@ -111,42 +107,48 @@ impl CalculatorState { } else if result.fract() == 0.0 && result.abs() < 1e10 { format!("{}", result as i64) } else { - format!("{:.8}", result).trim_end_matches('0').trim_end_matches('.').to_string() + format!("{:.8}", result) + .trim_end_matches('0') + .trim_end_matches('.') + .to_string() }; self.display.set(display_val.clone()); - + // If equals was pressed (next_operation is None) if next_operation.is_none() { - let op_str = match op { + let op_str = match op { Operation::Add => "+", Operation::Subtract => "-", Operation::Multiply => "×", Operation::Divide => "÷", - }; - let history_entry = format!("{} {} {} = {}", - self.format_number(self.previous_value), - op_str, + }; + let history_entry = format!( + "{} {} {} = {}", + self.format_number(self.previous_value), + op_str, self.format_number(self.current_value), // This is actually the 2nd operand before result, but we overwrote it. Needs fix separately or simplified logic. display_val - ); - // Correction: We overwrote current_value with result. We should have stored 2nd operand. - // For now let's just push " = result" logic or simplify. - // Let's implement a better input tracking. - - // IMPROVED LOGIC: - // We need to track the operands better for the history string. - // But strictly following the plan: track history. - - // Let's construct history string properly. - // We'll rely on expression updates in a dedicated manner in a future step if needed, - // but here let's set expression to empty or result. - self.expression.set("".to_string()); - - // Add to history - self.history.update(|h| { - h.push(history_entry); - if h.len() > 5 { h.remove(0); } // Keep last 5 - }); + ); + // Correction: We overwrote current_value with result. We should have stored 2nd operand. + // For now let's just push " = result" logic or simplify. + // Let's implement a better input tracking. + + // IMPROVED LOGIC: + // We need to track the operands better for the history string. + // But strictly following the plan: track history. + + // Let's construct history string properly. + // We'll rely on expression updates in a dedicated manner in a future step if needed, + // but here let's set expression to empty or result. + self.expression.set("".to_string()); + + // Add to history + self.history.update(|h| { + h.push(history_entry); + if h.len() > 5 { + h.remove(0); + } // Keep last 5 + }); } } @@ -159,14 +161,15 @@ impl CalculatorState { }; // Update expression display: "Result + " let current_display = self.display.get(); - self.expression.set(format!("{} {}", current_display, op_symbol)); + self.expression + .set(format!("{} {}", current_display, op_symbol)); } self.waiting_for_operand = true; self.operation = next_operation; self.previous_value = self.current_value; } - + fn format_number(&self, num: f64) -> String { if num.fract() == 0.0 { format!("{}", num as i64) @@ -185,22 +188,29 @@ fn main() -> anyhow::Result<()> { ..Default::default() }; - InitBuilder::new() - .with_config(config) - .init_all()?; + InitBuilder::new().with_config(config).init_all()?; + + inspector().configure(InspectorConfig { + enabled: true, + ..Default::default() + }); println!("Calculator - StratoUI initialized with font optimizations!"); - + // Build and run the application ApplicationBuilder::new() .title("StratoUI Calculator") - .window(WindowBuilder::new().with_size(320.0, 480.0).resizable(false)) - .run(build_calculator_ui()) + .window( + WindowBuilder::new() + .with_size(320.0, 480.0) + .resizable(false), + ) + .run(InspectorOverlay::new(build_calculator_ui())) } fn build_calculator_ui() -> impl Widget { let state = Arc::new(Mutex::new(CalculatorState::default())); - + Container::new() .background(Color::rgb(0.0, 0.0, 0.0)) // Pure black background .padding(15.0) // Increased padding @@ -210,39 +220,38 @@ fn build_calculator_ui() -> impl Widget { .children(vec![ // Display Box::new(create_display(state.clone())), - // Button grid (main block) Box::new(create_button_grid(state.clone())), - // Button grid (bottom row with span) Box::new(create_bottom_row(state.clone())), - ]) + ]), ) } fn create_display(state: Arc>) -> impl Widget { let (display_signal, expression_signal, history_signal) = { let state = state.lock().unwrap(); - (state.display.clone(), state.expression.clone(), state.history.clone()) + ( + state.display.clone(), + state.expression.clone(), + state.history.clone(), + ) }; - + // We Wrap the Container in a Flex to ensure it takes full width of the parent Column/Container // effectively pushing the alignment to the far right. Flex::new(Box::new( Container::new() .background(Color::BLACK) // Match main background - // .padding(15.0) // padding handled by parent or here? + // .padding(15.0) // padding handled by parent or here? // Parent has 15.0 padding. This container is inside that. // If we want text to align to the very right edge of this container, // we need this container to be as wide as possible. // The Flex below with .flex(1.0) on the *Container* itself (if Container implemented FlexItem traits directly) // or we use Flex widget. .height(150.0) - .child( - Column::new() - .spacing(4.0) - .children(vec![ - // History + .child(Column::new().spacing(4.0).children(vec![ + // History Box::new( Text::new("") .bind(history_signal.map(|h| h.join("\n"))) @@ -251,9 +260,9 @@ fn create_display(state: Arc>) -> impl Widget { .align(TextAlign::Right) ), Box::new(Flex::new(Box::new( - Container::new().height(10.0) + Container::new().height(10.0) )).flex(1.0)), - // Current Expression + // Current Expression Box::new( Text::new("") .bind(expression_signal) @@ -269,30 +278,41 @@ fn create_display(state: Arc>) -> impl Widget { .color(Color::rgb(1.0, 1.0, 1.0)) .align(TextAlign::Right) ), - ]) - ) - )).flex(1.0) + ])), + )) + .flex(1.0) } fn create_button_grid(state: Arc>) -> impl Widget { let buttons = vec![ // Row 1 - ("C", ButtonType::Clear), ("±", ButtonType::PlusMinus), ("%", ButtonType::Percent), ("÷", ButtonType::Operation(Operation::Divide)), + ("C", ButtonType::Clear), + ("±", ButtonType::PlusMinus), + ("%", ButtonType::Percent), + ("÷", ButtonType::Operation(Operation::Divide)), // Row 2 - ("7", ButtonType::Digit(7)), ("8", ButtonType::Digit(8)), ("9", ButtonType::Digit(9)), ("×", ButtonType::Operation(Operation::Multiply)), + ("7", ButtonType::Digit(7)), + ("8", ButtonType::Digit(8)), + ("9", ButtonType::Digit(9)), + ("×", ButtonType::Operation(Operation::Multiply)), // Row 3 - ("4", ButtonType::Digit(4)), ("5", ButtonType::Digit(5)), ("6", ButtonType::Digit(6)), ("-", ButtonType::Operation(Operation::Subtract)), + ("4", ButtonType::Digit(4)), + ("5", ButtonType::Digit(5)), + ("6", ButtonType::Digit(6)), + ("-", ButtonType::Operation(Operation::Subtract)), // Row 4 - ("1", ButtonType::Digit(1)), ("2", ButtonType::Digit(2)), ("3", ButtonType::Digit(3)), ("+", ButtonType::Operation(Operation::Add)), + ("1", ButtonType::Digit(1)), + ("2", ButtonType::Digit(2)), + ("3", ButtonType::Digit(3)), + ("+", ButtonType::Operation(Operation::Add)), ]; - let grid_items: Vec> = buttons.into_iter().map(|(text, btn_type)| { - Box::new(AnimatedButton::new( - text, - btn_type, - state.clone() - )) as Box - }).collect(); + let grid_items: Vec> = buttons + .into_iter() + .map(|(text, btn_type)| { + Box::new(AnimatedButton::new(text, btn_type, state.clone())) as Box + }) + .collect(); Grid::new() .columns(vec![ @@ -306,26 +326,36 @@ fn create_button_grid(state: Arc>) -> impl Widget { .children(grid_items) } - fn create_bottom_row(state: Arc>) -> impl Widget { - Row::new() - .spacing(8.0) - .children(vec![ - // Zero button - Spans 2 columns worth (50%) - Box::new(Flex::new( - Box::new(AnimatedButton::new("0", ButtonType::Digit(0), state.clone())) - ).flex(2.0)), - - // Decimal - Box::new(Flex::new( - Box::new(AnimatedButton::new(".", ButtonType::Decimal, state.clone())) - ).flex(1.0)), - - // Equals - Box::new(Flex::new( - Box::new(AnimatedButton::new("=", ButtonType::Equals, state.clone())) - ).flex(1.0)), - ]) + Row::new().spacing(8.0).children(vec![ + // Zero button - Spans 2 columns worth (50%) + Box::new( + Flex::new(Box::new(AnimatedButton::new( + "0", + ButtonType::Digit(0), + state.clone(), + ))) + .flex(2.0), + ), + // Decimal + Box::new( + Flex::new(Box::new(AnimatedButton::new( + ".", + ButtonType::Decimal, + state.clone(), + ))) + .flex(1.0), + ), + // Equals + Box::new( + Flex::new(Box::new(AnimatedButton::new( + "=", + ButtonType::Equals, + state.clone(), + ))) + .flex(1.0), + ), + ]) } #[derive(Clone, Debug)] @@ -353,9 +383,9 @@ struct AnimatedButton { impl AnimatedButton { fn new(text: &str, button_type: ButtonType, state: Arc>) -> Self { - let controller = AnimationController::new(Duration::from_millis(100)) - .with_curve(Curve::EaseOut); - + let controller = + AnimationController::new(Duration::from_millis(100)).with_curve(Curve::EaseOut); + Self { id: strato_widgets::widget::generate_id(), text: text.to_string(), @@ -373,40 +403,56 @@ impl Widget for AnimatedButton { self.id } - fn layout(&mut self, constraints: strato_core::layout::Constraints) -> strato_core::layout::Size { + fn layout( + &mut self, + constraints: strato_core::layout::Constraints, + ) -> strato_core::layout::Size { // Fill available space strato_core::layout::Size::new( - constraints.max_width, + constraints.max_width, // 70.0 is roughly the height we want, but let's be flexible - constraints.max_height.min(70.0).max(50.0) + constraints.max_height.min(70.0).max(50.0), ) } - fn render(&self, batch: &mut strato_renderer::batch::RenderBatch, layout: strato_core::layout::Layout) { + fn render( + &self, + batch: &mut strato_renderer::batch::RenderBatch, + layout: strato_core::layout::Layout, + ) { // Update bounds for hit testing in event handling if let Ok(mut bounds) = self.bounds.lock() { - *bounds = Rect::new(layout.position.x, layout.position.y, layout.size.width, layout.size.height); + *bounds = Rect::new( + layout.position.x, + layout.position.y, + layout.size.width, + layout.size.height, + ); } let bg_color = match self.button_type { ButtonType::Operation(_) | ButtonType::Equals => Color::rgb(1.0, 0.62, 0.04), // Orange (#FF9F0A) - ButtonType::Clear | ButtonType::PlusMinus | ButtonType::Percent => Color::rgb(0.65, 0.65, 0.65), // Light gray (#A5A5A5) + ButtonType::Clear | ButtonType::PlusMinus | ButtonType::Percent => { + Color::rgb(0.65, 0.65, 0.65) + } // Light gray (#A5A5A5) _ => Color::rgb(0.2, 0.2, 0.2), // Dark gray (#333333) }; - + let text_color = match self.button_type { - ButtonType::Clear | ButtonType::PlusMinus | ButtonType::Percent => Color::rgb(0.0, 0.0, 0.0), // Black text + ButtonType::Clear | ButtonType::PlusMinus | ButtonType::Percent => { + Color::rgb(0.0, 0.0, 0.0) + } // Black text _ => Color::rgb(1.0, 1.0, 1.0), // White text }; // Animation logic let scale = if self.is_pressed { - 0.95 + 0.95 } else { // Check animation progress if releasing let t = self.anim_controller.value(); if t < 1.0 { - 0.95 + (0.05 * t) // rebound + 0.95 + (0.05 * t) // rebound } else { 1.0 } @@ -417,30 +463,37 @@ impl Widget for AnimatedButton { // Apply scale from center let center = layout.size.to_vec2() / 2.0; - let transform = Transform::translate(layout.position.x + center.x, layout.position.y + center.y) - .combine(&Transform::scale(scale, scale)) - .combine(&Transform::translate(-center.x, -center.y)); + let transform = + Transform::translate(layout.position.x + center.x, layout.position.y + center.y) + .combine(&Transform::scale(scale, scale)) + .combine(&Transform::translate(-center.x, -center.y)); if (layout.size.width - layout.size.height).abs() < 1.0 { // Perfect circle (Square aspect ratio) - let center_pt = (layout.position.x + layout.size.width / 2.0, layout.position.y + layout.size.height / 2.0); + let center_pt = ( + layout.position.x + layout.size.width / 2.0, + layout.position.y + layout.size.height / 2.0, + ); batch.add_circle(center_pt, radius, bg_color, 32, transform); } else { // Pill shape (Width > Height, e.g., '0' button) // Left circle let left_center = (layout.position.x + radius, layout.position.y + radius); batch.add_circle(left_center, radius, bg_color, 32, transform); - + // Right circle - let right_center = (layout.position.x + layout.size.width - radius, layout.position.y + radius); + let right_center = ( + layout.position.x + layout.size.width - radius, + layout.position.y + radius, + ); batch.add_circle(right_center, radius, bg_color, 32, transform); - + // Middle rect let rect = Rect::new( - layout.position.x + radius, - layout.position.y, - layout.size.width - 2.0 * radius, - layout.size.height + layout.position.x + radius, + layout.position.y, + layout.size.width - 2.0 * radius, + layout.size.height, ); batch.add_rect(rect, bg_color, transform); } @@ -449,12 +502,12 @@ impl Widget for AnimatedButton { let font_size = 32.0; let text_x = layout.position.x + layout.size.width / 2.0; let text_y = layout.position.y + layout.size.height / 2.0 - font_size / 2.0; - + batch.add_text_aligned( - self.text.clone(), - (text_x, text_y), - text_color, - font_size, + self.text.clone(), + (text_x, text_y), + text_color, + font_size, 0.0, strato_core::text::TextAlign::Center, ); @@ -465,7 +518,7 @@ impl Widget for AnimatedButton { Event::MouseDown(MouseEvent { position, .. }) => { let bounds = *self.bounds.lock().unwrap(); let point = Point::new(position.x, position.y); - + if bounds.contains(point) { self.is_pressed = true; return EventResult::Handled; @@ -476,11 +529,11 @@ impl Widget for AnimatedButton { self.is_pressed = false; self.anim_controller.reset(); self.anim_controller.start(); - + // Check if still within bounds to trigger action (standard button behavior) let bounds = *self.bounds.lock().unwrap(); let point = Point::new(position.x, position.y); - + if bounds.contains(point) { // Perform action handle_button_click(&self.button_type, self.state.clone()); @@ -488,7 +541,7 @@ impl Widget for AnimatedButton { return EventResult::Handled; } } - _ => {} + _ => {} } EventResult::Ignored } @@ -516,7 +569,7 @@ impl Widget for AnimatedButton { fn handle_button_click(button_type: &ButtonType, state: Arc>) { let mut state = state.lock().unwrap(); - + match button_type { ButtonType::Digit(digit) => { state.input_digit(*digit); @@ -547,6 +600,6 @@ fn handle_button_click(button_type: &ButtonType, state: Arc anyhow::Result<()> { // Create application @@ -20,36 +13,38 @@ fn main() -> anyhow::Result<()> { .with_title("Complex Demo") .with_size(1024.0, 768.0) .resizable(true); - + + inspector().configure(InspectorConfig { + enabled: true, + ..Default::default() + }); + let mut app = Application::new("Complex Demo", window_builder); // Create UI structure // Main container with background - let main_container = Container::new() - .background(Color::rgba(0.95, 0.95, 0.97, 1.0)) // Light gray background - .padding(20.0) - .child( - Column::new() - .spacing(20.0) - .main_axis_alignment(MainAxisAlignment::Start) - .cross_axis_alignment(CrossAxisAlignment::Stretch) - .children(vec![ - // Header Section - create_header(), - - // Content Section (Row with Sidebar and Main Content) - Box::new( - Row::new() - .spacing(20.0) - .children(vec![ - // Sidebar - create_sidebar(), - // Main Content Area - create_main_content(), - ]) - ) as Box, - ]) - ); + let main_container = InspectorOverlay::new( + Container::new() + .background(Color::rgba(0.95, 0.95, 0.97, 1.0)) // Light gray background + .padding(20.0) + .child( + Column::new() + .spacing(20.0) + .main_axis_alignment(MainAxisAlignment::Start) + .cross_axis_alignment(CrossAxisAlignment::Stretch) + .children(vec![ + // Header Section + create_header(), + // Content Section (Row with Sidebar and Main Content) + Box::new(Row::new().spacing(20.0).children(vec![ + // Sidebar + create_sidebar(), + // Main Content Area + create_main_content(), + ])) as Box, + ]), + ), + ); app.set_root(Box::new(main_container)); @@ -58,55 +53,67 @@ fn main() -> anyhow::Result<()> { } fn create_header() -> Box { - Box::new(Container::new() - .background(Color::rgba(1.0, 1.0, 1.0, 1.0)) - .padding(15.0) - .border_radius(8.0) - .child( - Row::new() - .main_axis_alignment(MainAxisAlignment::SpaceBetween) - .cross_axis_alignment(CrossAxisAlignment::Center) - .children(vec![ - Box::new( - Text::new("StratoUI Dashboard") - .font_size(24.0) - .font_weight(FontWeight::Bold) - .color(Color::rgba(0.1, 0.1, 0.2, 1.0)) - ) as Box, - Box::new( - Row::new() - .spacing(10.0) - .children(vec![ - Box::new(Button::new("Settings").ghost()) as Box, - Box::new(Button::new("Profile").primary()) as Box, - ]) - ) as Box - ]) - )) + Box::new( + Container::new() + .background(Color::rgba(1.0, 1.0, 1.0, 1.0)) + .padding(15.0) + .border_radius(8.0) + .child( + Row::new() + .main_axis_alignment(MainAxisAlignment::SpaceBetween) + .cross_axis_alignment(CrossAxisAlignment::Center) + .children(vec![ + Box::new( + Text::new("StratoUI Dashboard") + .font_size(24.0) + .font_weight(FontWeight::Bold) + .color(Color::rgba(0.1, 0.1, 0.2, 1.0)), + ) as Box, + Box::new(Row::new().spacing(10.0).children(vec![ + Box::new(Button::new("Settings").ghost()) as Box, + Box::new(Button::new("Profile").primary()) as Box, + ])) as Box, + ]), + ), + ) } fn create_sidebar() -> Box { - Box::new(Container::new() - .width(200.0) - .background(Color::rgba(1.0, 1.0, 1.0, 1.0)) - .padding(10.0) - .border_radius(8.0) - .child( - Column::new() - .spacing(5.0) - .cross_axis_alignment(CrossAxisAlignment::Stretch) - .children(vec![ - Box::new(Text::new("MENU").font_size(12.0).color(Color::rgba(0.5, 0.5, 0.5, 1.0))) as Box, - Box::new(Button::new("Dashboard").ghost().on_click(|| println!("Dashboard clicked"))) as Box, - Box::new(Button::new("Analytics").ghost()) as Box, - Box::new(Button::new("Reports").ghost()) as Box, - Box::new(Button::new("Users").ghost()) as Box, - Box::new(Container::new().height(20.0)) as Box, // Spacer - Box::new(Text::new("SYSTEM").font_size(12.0).color(Color::rgba(0.5, 0.5, 0.5, 1.0))) as Box, - Box::new(Button::new("Configuration").ghost()) as Box, - Box::new(Button::new("Logs").ghost()) as Box, - ]) - )) + Box::new( + Container::new() + .width(200.0) + .background(Color::rgba(1.0, 1.0, 1.0, 1.0)) + .padding(10.0) + .border_radius(8.0) + .child( + Column::new() + .spacing(5.0) + .cross_axis_alignment(CrossAxisAlignment::Stretch) + .children(vec![ + Box::new( + Text::new("MENU") + .font_size(12.0) + .color(Color::rgba(0.5, 0.5, 0.5, 1.0)), + ) as Box, + Box::new( + Button::new("Dashboard") + .ghost() + .on_click(|| println!("Dashboard clicked")), + ) as Box, + Box::new(Button::new("Analytics").ghost()) as Box, + Box::new(Button::new("Reports").ghost()) as Box, + Box::new(Button::new("Users").ghost()) as Box, + Box::new(Container::new().height(20.0)) as Box, // Spacer + Box::new( + Text::new("SYSTEM") + .font_size(12.0) + .color(Color::rgba(0.5, 0.5, 0.5, 1.0)), + ) as Box, + Box::new(Button::new("Configuration").ghost()) as Box, + Box::new(Button::new("Logs").ghost()) as Box, + ]), + ), + ) } fn create_main_content() -> Box { @@ -133,7 +140,7 @@ fn create_main_content() -> Box { }) ) as Box, Box::new(Container::new().height(20.0)) as Box, // Spacer - + // Cards Row Box::new( Row::new() @@ -144,9 +151,9 @@ fn create_main_content() -> Box { create_card("Server Load", "42%", Color::rgba(1.0, 0.6, 0.2, 0.1)), ]) ) as Box, - + Box::new(Container::new().height(20.0)) as Box, // Spacer - + // Typography Showcase Box::new(Text::new("Typography & Spacing").font_size(18.0).font_weight(FontWeight::Medium)) as Box, Box::new( @@ -168,19 +175,29 @@ fn create_main_content() -> Box { } fn create_card(title: &str, value: &str, bg_color: Color) -> Box { - Box::new(Container::new() - .width(150.0) - .height(100.0) - .background(bg_color) - .border_radius(8.0) - .padding(15.0) - .child( - Column::new() - .main_axis_alignment(MainAxisAlignment::Center) - .cross_axis_alignment(CrossAxisAlignment::Start) - .children(vec![ - Box::new(Text::new(title).font_size(14.0).color(Color::rgba(0.4, 0.4, 0.4, 1.0))) as Box, - Box::new(Text::new(value).font_size(24.0).font_weight(FontWeight::Bold)) as Box, - ]) - )) + Box::new( + Container::new() + .width(150.0) + .height(100.0) + .background(bg_color) + .border_radius(8.0) + .padding(15.0) + .child( + Column::new() + .main_axis_alignment(MainAxisAlignment::Center) + .cross_axis_alignment(CrossAxisAlignment::Start) + .children(vec![ + Box::new( + Text::new(title) + .font_size(14.0) + .color(Color::rgba(0.4, 0.4, 0.4, 1.0)), + ) as Box, + Box::new( + Text::new(value) + .font_size(24.0) + .font_weight(FontWeight::Bold), + ) as Box, + ]), + ), + ) } diff --git a/examples/comprehensive_test/src/main.rs b/examples/comprehensive_test/src/main.rs index 76b29fc..3c614e0 100644 --- a/examples/comprehensive_test/src/main.rs +++ b/examples/comprehensive_test/src/main.rs @@ -1,88 +1,90 @@ -use strato_platform::{ - application::Application, - window::WindowBuilder, -}; +use strato_core::inspector::{inspector, InspectorConfig}; +use strato_core::types::Color; +use strato_platform::{application::Application, window::WindowBuilder}; use strato_widgets::{ - Button, ButtonStyle, - Column, - Container, - Row, - Flex, - Text, + input::{InputType, TextInput}, + layout::{CrossAxisAlignment, MainAxisAlignment}, text::FontWeight, - input::{TextInput, InputType}, - Dropdown, - Widget, - layout::{MainAxisAlignment, CrossAxisAlignment}, + Button, ButtonStyle, Column, Container, Dropdown, Flex, InspectorOverlay, Row, Text, Widget, }; -use strato_core::types::Color; use tracing::{info, warn}; use tracing_subscriber::prelude::*; // Theme Colors -fn theme_bg() -> Color { Color::rgba(0.09, 0.09, 0.11, 1.0) } // #17171C -fn theme_surface() -> Color { Color::rgba(0.13, 0.13, 0.16, 1.0) } // #212129 -fn theme_surface_hover() -> Color { Color::rgba(0.16, 0.16, 0.20, 1.0) } // #292933 -fn theme_accent() -> Color { Color::rgba(0.39, 0.35, 0.88, 1.0) } // #6459E1 -fn theme_text_primary() -> Color { Color::rgba(0.95, 0.95, 0.97, 1.0) } -fn theme_text_secondary() -> Color { Color::rgba(0.60, 0.60, 0.65, 1.0) } +fn theme_bg() -> Color { + Color::rgba(0.09, 0.09, 0.11, 1.0) +} // #17171C +fn theme_surface() -> Color { + Color::rgba(0.13, 0.13, 0.16, 1.0) +} // #212129 +fn theme_surface_hover() -> Color { + Color::rgba(0.16, 0.16, 0.20, 1.0) +} // #292933 +fn theme_accent() -> Color { + Color::rgba(0.39, 0.35, 0.88, 1.0) +} // #6459E1 +fn theme_text_primary() -> Color { + Color::rgba(0.95, 0.95, 0.97, 1.0) +} +fn theme_text_secondary() -> Color { + Color::rgba(0.60, 0.60, 0.65, 1.0) +} const BORDER_RADIUS: f32 = 12.0; fn main() -> anyhow::Result<()> { // Setup logging to file let file_appender = tracing_appender::rolling::daily("logs", "comprehensive_test.log"); let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender); - + tracing_subscriber::registry() - .with(tracing_subscriber::fmt::layer() - .with_writer(non_blocking) - .with_ansi(false) - ) - .with(tracing_subscriber::fmt::layer() - .with_writer(std::io::stdout) + .with( + tracing_subscriber::fmt::layer() + .with_writer(non_blocking) + .with_ansi(false), ) + .with(tracing_subscriber::fmt::layer().with_writer(std::io::stdout)) .init(); info!("Starting Comprehensive Test Example - Revolutionized UI"); + inspector().configure(InspectorConfig { + enabled: true, + ..Default::default() + }); + // Create application let window_builder = WindowBuilder::new() .with_title("StratoUI Dashboard") .with_size(1280.0, 900.0) .resizable(true); - + let mut app = Application::new("StratoUI Dashboard", window_builder); // Create UI structure - let main_container = Container::new() - .background(theme_bg()) - .padding(30.0) - .child( + let main_container = InspectorOverlay::new( + Container::new().background(theme_bg()).padding(30.0).child( Column::new() .spacing(30.0) .main_axis_alignment(MainAxisAlignment::Start) .cross_axis_alignment(CrossAxisAlignment::Stretch) .children(vec![ create_header(), - // Main Content Grid (simulated with Row/Column) - Box::new(Row::new() - .spacing(30.0) - .cross_axis_alignment(CrossAxisAlignment::Start) - .children(vec![ - // Left Column (Interactive) - Box::new(Flex::new( - create_interactive_section() - )) as Box, - - // Right Column (Layouts/Cards) - Box::new(Flex::new( - create_layout_section() - )) as Box, - ]) + Box::new( + Row::new() + .spacing(30.0) + .cross_axis_alignment(CrossAxisAlignment::Start) + .children(vec![ + // Left Column (Interactive) + Box::new(Flex::new(create_interactive_section())) + as Box, + // Right Column (Layouts/Cards) + Box::new(Flex::new(create_layout_section())) as Box, + ]), ) as Box, - ]) - ); + ]), + ), + ); app.set_root(Box::new(main_container)); @@ -91,19 +93,17 @@ fn main() -> anyhow::Result<()> { } fn create_header() -> Box { - Box::new(Container::new() - .background(theme_surface()) - .padding(25.0) - .border_radius(BORDER_RADIUS) - .child( - Row::new() - .main_axis_alignment(MainAxisAlignment::SpaceBetween) - .cross_axis_alignment(CrossAxisAlignment::Center) - .children(vec![ - Box::new( - Column::new() - .spacing(5.0) - .children(vec![ + Box::new( + Container::new() + .background(theme_surface()) + .padding(25.0) + .border_radius(BORDER_RADIUS) + .child( + Row::new() + .main_axis_alignment(MainAxisAlignment::SpaceBetween) + .cross_axis_alignment(CrossAxisAlignment::Center) + .children(vec![ + Box::new(Column::new().spacing(5.0).children(vec![ Box::new( Text::new("StratoUI Dashboard") .font_size(28.0) @@ -115,13 +115,8 @@ fn create_header() -> Box { .font_size(14.0) .color(theme_text_secondary()) ) as Box, - ]) - ) as Box, - - Box::new( - Row::new() - .spacing(15.0) - .children(vec![ + ])) as Box, + Box::new(Row::new().spacing(15.0).children(vec![ Box::new( Button::new("Documentation") .outline() @@ -132,18 +127,15 @@ fn create_header() -> Box { .primary() .on_click(|| info!("New Project clicked")) ) as Box, - ]) - ) as Box, - ]) - )) + ])) as Box, + ]), + ), + ) } fn create_interactive_section() -> Box { - Box::new(Column::new() - .spacing(20.0) - .children(vec![ + Box::new(Column::new().spacing(20.0).children(vec![ create_section_title("Controls & Inputs"), - Container::new() .background(theme_surface()) .padding(20.0) @@ -154,140 +146,168 @@ fn create_interactive_section() -> Box { .cross_axis_alignment(CrossAxisAlignment::Stretch) .children(vec![ // Buttons Group - Box::new(Column::new() - .spacing(10.0) - .children(vec![ - Box::new(Text::new("Buttons").font_size(14.0).color(theme_text_secondary())) as Box, - Box::new(Row::new() - .spacing(10.0) - .children(vec![ - Box::new(Flex::new(Box::new(Button::new("Primary").primary())).flex(1.0)) as Box, - Box::new(Flex::new(Box::new(Button::new("Secondary").secondary())).flex(1.0)) as Box, - Box::new(Flex::new(Box::new(Button::new("Danger").danger())).flex(1.0)) as Box, - ]) + Box::new( + Column::new().spacing(10.0).children(vec![ + Box::new( + Text::new("Buttons") + .font_size(14.0) + .color(theme_text_secondary()), + ) as Box, + Box::new( + Row::new().spacing(10.0).children(vec![ + Box::new( + Flex::new(Box::new( + Button::new("Primary").primary(), + )) + .flex(1.0), + ) + as Box, + Box::new( + Flex::new(Box::new( + Button::new("Secondary").secondary(), + )) + .flex(1.0), + ) + as Box, + Box::new( + Flex::new(Box::new(Button::new("Danger").danger())) + .flex(1.0), + ) + as Box, + ]), ) as Box, - ]) + ]), ) as Box, - // Dropdown Group - Box::new(Column::new() - .spacing(10.0) - .children(vec![ - Box::new(Text::new("Selection").font_size(14.0).color(theme_text_secondary())) as Box, + Box::new( + Column::new().spacing(10.0).children(vec![ + Box::new( + Text::new("Selection") + .font_size(14.0) + .color(theme_text_secondary()), + ) as Box, Box::new( Dropdown::new() .add_value("Development".to_string()) .add_value("Staging".to_string()) .add_value("Production".to_string()) - .placeholder("Select Environment".to_string()) + .placeholder("Select Environment".to_string()), ) as Box, - ]) + ]), ) as Box, - // Inputs Group - Box::new(Column::new() - .spacing(15.0) - .children(vec![ - Box::new(Text::new("Authentication").font_size(14.0).color(theme_text_secondary())) as Box, + Box::new( + Column::new().spacing(15.0).children(vec![ Box::new( - TextInput::new() - .placeholder("Username or Email") + Text::new("Authentication") + .font_size(14.0) + .color(theme_text_secondary()), ) as Box, + Box::new(TextInput::new().placeholder("Username or Email")) + as Box, Box::new( TextInput::new() .placeholder("Password") - .input_type(InputType::Password) + .input_type(InputType::Password), ) as Box, - ]) + ]), ) as Box, - ]) + ]), ) - .into_boxed() - ]) - ) + .into_boxed(), + ])) } fn create_layout_section() -> Box { - Box::new(Column::new() - .spacing(20.0) - .children(vec![ + Box::new(Column::new().spacing(20.0).children(vec![ create_section_title("Project Status"), - // Stats Row - Box::new(Row::new() - .spacing(20.0) - .children(vec![ - create_stat_card("Active Users", "12.5k", "+15%", theme_accent()), - create_stat_card("Server Load", "42%", "-5%", Color::rgba(0.2, 0.8, 0.4, 1.0)), - create_stat_card("Errors", "0.01%", "-2%", Color::rgba(0.9, 0.3, 0.3, 1.0)), - ]) - ) as Box, - + Box::new(Row::new().spacing(20.0).children(vec![ + create_stat_card("Active Users", "12.5k", "+15%", theme_accent()), + create_stat_card("Server Load", "42%", "-5%", Color::rgba(0.2, 0.8, 0.4, 1.0)), + create_stat_card("Errors", "0.01%", "-2%", Color::rgba(0.9, 0.3, 0.3, 1.0)), + ])) as Box, // Detailed Cards - Box::new(Container::new() - .background(theme_surface()) - .padding(20.0) - .border_radius(BORDER_RADIUS) - .child( - Column::new() - .spacing(15.0) - .children(vec![ - Box::new(Text::new("Recent Activity").font_size(16.0).font_weight(FontWeight::SemiBold).color(theme_text_primary())) as Box, + Box::new( + Container::new() + .background(theme_surface()) + .padding(20.0) + .border_radius(BORDER_RADIUS) + .child( + Column::new().spacing(15.0).children(vec![ + Box::new( + Text::new("Recent Activity") + .font_size(16.0) + .font_weight(FontWeight::SemiBold) + .color(theme_text_primary()), + ) as Box, create_activity_item("Deployment #1024", "Successful", "2 mins ago"), create_activity_item("Database Backup", "Processing", "15 mins ago"), create_activity_item("User Registration", "New User", "1 hour ago"), - ]) - ) + ]), + ), ) as Box, - ]) - ) + ])) } fn create_section_title(title: &str) -> Box { - Box::new(Text::new(title) - .font_size(18.0) - .font_weight(FontWeight::SemiBold) - .color(theme_text_primary()) + Box::new( + Text::new(title) + .font_size(18.0) + .font_weight(FontWeight::SemiBold) + .color(theme_text_primary()), ) } fn create_stat_card(label: &str, value: &str, trend: &str, trend_color: Color) -> Box { - Box::new(Flex::new(Box::new(Container::new() - .background(theme_surface()) - .padding(20.0) - .border_radius(BORDER_RADIUS) - .child( - Column::new() - .spacing(10.0) - .children(vec![ - Box::new(Text::new(label).font_size(14.0).color(theme_text_secondary())) as Box, - Box::new(Text::new(value).font_size(24.0).font_weight(FontWeight::Bold).color(theme_text_primary())) as Box, - Box::new(Text::new(trend).font_size(12.0).color(trend_color)) as Box, - ]) - )))) + Box::new(Flex::new(Box::new( + Container::new() + .background(theme_surface()) + .padding(20.0) + .border_radius(BORDER_RADIUS) + .child(Column::new().spacing(10.0).children(vec![ + Box::new( + Text::new(label) + .font_size(14.0) + .color(theme_text_secondary()), + ) as Box, + Box::new( + Text::new(value) + .font_size(24.0) + .font_weight(FontWeight::Bold) + .color(theme_text_primary()), + ) as Box, + Box::new(Text::new(trend).font_size(12.0).color(trend_color)) + as Box, + ])), + ))) } fn create_activity_item(title: &str, status: &str, time: &str) -> Box { - Box::new(Container::new() - .background(theme_surface_hover()) - .padding(12.0) - .border_radius(8.0) - .child( - Row::new() - .main_axis_alignment(MainAxisAlignment::SpaceBetween) - .cross_axis_alignment(CrossAxisAlignment::Center) - .children(vec![ - Box::new( - Column::new() - .spacing(4.0) - .children(vec![ - Box::new(Text::new(title).font_size(14.0).color(theme_text_primary())) as Box, - Box::new(Text::new(status).font_size(12.0).color(theme_accent())) as Box, - ]) - ) as Box, - Box::new(Text::new(time).font_size(12.0).color(theme_text_secondary())) as Box, - ]) - )) + Box::new( + Container::new() + .background(theme_surface_hover()) + .padding(12.0) + .border_radius(8.0) + .child( + Row::new() + .main_axis_alignment(MainAxisAlignment::SpaceBetween) + .cross_axis_alignment(CrossAxisAlignment::Center) + .children(vec![ + Box::new(Column::new().spacing(4.0).children(vec![ + Box::new(Text::new(title).font_size(14.0).color(theme_text_primary())) + as Box, + Box::new(Text::new(status).font_size(12.0).color(theme_accent())) + as Box, + ])) as Box, + Box::new( + Text::new(time) + .font_size(12.0) + .color(theme_text_secondary()), + ) as Box, + ]), + ), + ) } // Extension trait helper to box widgets easily diff --git a/examples/counter/src/main.rs b/examples/counter/src/main.rs index 6782e77..6a18954 100644 --- a/examples/counter/src/main.rs +++ b/examples/counter/src/main.rs @@ -1,14 +1,13 @@ //! Counter example demonstrating state management in StratoUI -use strato_core::{ - types::Color, - error::Result, -}; +use parking_lot::RwLock; +use std::sync::Arc; +use strato_core::inspector::{inspector, InspectorConfig}; +use strato_core::{error::Result, types::Color}; +use strato_platform::{ApplicationBuilder, WindowBuilder}; use strato_widgets::prelude::*; use strato_widgets::ButtonStyle; -use strato_platform::{ApplicationBuilder, WindowBuilder}; -use std::sync::Arc; -use parking_lot::RwLock; +use strato_widgets::InspectorOverlay; struct CounterApp { count: Arc>, @@ -38,8 +37,15 @@ fn main() -> Result<()> { // Initialize all StratoUI modules strato_core::init()?; strato_widgets::init()?; - strato_platform::init().map_err(|e| strato_core::error::StratoError::platform(format!("{:?}", e)))?; - + strato_platform::init() + .map_err(|e| strato_core::error::StratoError::platform(format!("{:?}", e)))?; + + // Keep the inspector active even outside debug builds so the example surfaces diagnostics. + inspector().configure(InspectorConfig { + enabled: true, + ..Default::default() + }); + let app = Arc::new(CounterApp::new()); // Create and run the application @@ -48,10 +54,10 @@ fn main() -> Result<()> { .window( WindowBuilder::new() .with_size(350.0, 250.0) - .resizable(false) + .resizable(false), ) - .run(build_ui(app)); - + .run(InspectorOverlay::new(build_ui(app))); + Ok(()) } @@ -72,7 +78,7 @@ fn build_ui(app: Arc) -> impl Widget { Box::new( Text::new("Counter App") .size(28.0) - .color(Color::rgb(0.2, 0.2, 0.2)) + .color(Color::rgb(0.2, 0.2, 0.2)), ), Box::new( Container::new() @@ -82,8 +88,8 @@ fn build_ui(app: Arc) -> impl Widget { .child( Text::new(format!("{}", *app.count.read())) .size(48.0) - .color(Color::BLACK) - ) + .color(Color::BLACK), + ), ), Box::new( Row::new() @@ -97,16 +103,14 @@ fn build_ui(app: Arc) -> impl Widget { .on_click(move || { app_dec.decrement(); println!("Count: {}", *app_dec.count.read()); - }) - ), - Box::new( - Button::new("Reset") - .style(ButtonStyle::text()) - .on_click(move || { - app_reset.reset(); - println!("Count reset to 0"); - }) + }), ), + Box::new(Button::new("Reset").style(ButtonStyle::text()).on_click( + move || { + app_reset.reset(); + println!("Count reset to 0"); + }, + )), Box::new( Button::new("+") .style(ButtonStyle::primary()) @@ -114,10 +118,10 @@ fn build_ui(app: Arc) -> impl Widget { .on_click(move || { app_inc.increment(); println!("Count: {}", *app_inc.count.read()); - }) + }), ), - ]) + ]), ), - ]) + ]), ) } diff --git a/examples/custom_init/src/main.rs b/examples/custom_init/src/main.rs index 3f9411e..337c7ff 100644 --- a/examples/custom_init/src/main.rs +++ b/examples/custom_init/src/main.rs @@ -1,5 +1,6 @@ -use strato_ui::{InitBuilder, InitConfig}; +use strato_core::inspector::{inspector, InspectorConfig}; use strato_core::Result; +use strato_ui::{InitBuilder, InitConfig}; fn main() -> Result<()> { println!("StratoUI Custom Initialization Example"); @@ -9,10 +10,16 @@ fn main() -> Result<()> { println!("\n1. Basic initialization with default config:"); let mut builder = InitBuilder::new(); builder.init_all()?; - + + inspector().configure(InspectorConfig { + enabled: true, + ..Default::default() + }); + println!(" Core: ✓"); println!(" Widgets: ✓"); println!(" Platform: ✓"); + println!(" Inspector: ✓ Enabled for runtime overlays"); println!(" Complete: ✓"); // Example 2: Check global text renderer @@ -34,4 +41,4 @@ fn main() -> Result<()> { println!(" • Font management and filtering"); Ok(()) -} \ No newline at end of file +} diff --git a/examples/hello_world/src/main.rs b/examples/hello_world/src/main.rs index e4510be..fa6d878 100644 --- a/examples/hello_world/src/main.rs +++ b/examples/hello_world/src/main.rs @@ -1,17 +1,18 @@ //! Hello World example for StratoSDK showing state management and modern UI +use std::sync::{Arc, Mutex}; use strato_sdk::prelude::*; -use strato_sdk::strato_widgets::{ - Button, Column, Container, Text, - text::TextAlign, - layout::{MainAxisAlignment, CrossAxisAlignment}, -}; +use strato_sdk::strato_core::inspector::{inspector, InspectorConfig}; +use strato_sdk::strato_core::state::Signal; +use strato_sdk::strato_core::types::Color; use strato_sdk::strato_platform::{ - ApplicationBuilder, WindowBuilder, init::{InitBuilder, InitConfig}, + ApplicationBuilder, WindowBuilder, +}; +use strato_sdk::strato_widgets::{ + layout::{CrossAxisAlignment, MainAxisAlignment}, + text::TextAlign, + Button, Column, Container, InspectorOverlay, Text, }; -use strato_sdk::strato_core::types::Color; -use strato_sdk::strato_core::state::Signal; -use std::sync::{Arc, Mutex}; #[derive(Clone, Debug)] struct HelloWorldState { @@ -35,9 +36,10 @@ impl HelloWorldState { if count == 1 { self.message.set("First click! Keep going!".to_string()); } else if count == 5 { - self.message.set("You're getting the hang of it!".to_string()); + self.message + .set("You're getting the hang of it!".to_string()); } else if count == 10 { - self.message.set("Double digits! 🚀".to_string()); + self.message.set("Double digits! 🚀".to_string()); } } } @@ -51,17 +53,23 @@ fn main() -> anyhow::Result<()> { }) .init_all()?; + // Keep the inspector available in all builds so the example showcases live debugging tools. + inspector().configure(InspectorConfig { + enabled: true, + ..Default::default() + }); + println!("Hello World - StratoSDK initialized!"); ApplicationBuilder::new() .title("Hello StratoSDK") .window(WindowBuilder::new().with_size(500.0, 400.0).resizable(true)) - .run(build_ui()) + .run(InspectorOverlay::new(build_ui())) } fn build_ui() -> impl Widget { let state = Arc::new(Mutex::new(HelloWorldState::default())); - + // Main Window Background Container::new() .background(Color::rgb(0.05, 0.05, 0.05)) // Deep dark background @@ -70,9 +78,7 @@ fn build_ui() -> impl Widget { Column::new() .main_axis_alignment(MainAxisAlignment::Center) .cross_axis_alignment(CrossAxisAlignment::Center) - .children(vec![ - Box::new(create_interaction_card(state)) - ]) + .children(vec![Box::new(create_interaction_card(state))]), ) } @@ -81,7 +87,7 @@ fn create_interaction_card(state: Arc>) -> impl Widget { let state = state.lock().unwrap(); (state.counter.clone(), state.message.clone()) }; - + // Card Container Container::new() .background(Color::rgb(0.12, 0.12, 0.12)) // Slightly lighter card @@ -99,48 +105,41 @@ fn create_interaction_card(state: Arc>) -> impl Widget { Container::new() .width(60.0) .height(60.0) - .background(Color::rgb(0.25, 0.4, 0.9)) // Accent Blue - // .corner_radius(30.0) // Circle + .background(Color::rgb(0.25, 0.4, 0.9)), // Accent Blue + // .corner_radius(30.0) // Circle ), - // Title Box::new( Text::new("Hello, StratoSDK") .size(32.0) .color(Color::WHITE) - .align(TextAlign::Center) + .align(TextAlign::Center), ), - // Dynamic Message Box::new( Text::new("") .bind(message_signal) .size(16.0) .color(Color::rgb(0.7, 0.7, 0.7)) - .align(TextAlign::Center) + .align(TextAlign::Center), ), - // Spacer Box::new(Container::new().height(10.0)), - // Counter Display Box::new( Text::new("") .bind(counter_signal.map(|c| format!("Clicks: {}", c))) .size(48.0) .color(Color::rgb(0.25, 0.8, 0.4)) // Green accent - .align(TextAlign::Center) + .align(TextAlign::Center), ), - // Interactive Button Box::new( - Button::new("Increment Counter") - .on_click(move || { - let mut state = state.lock().unwrap(); - state.increment(); - }) - // .style(...) // if we had style API on button directly + Button::new("Increment Counter").on_click(move || { + let mut state = state.lock().unwrap(); + state.increment(); + }), // .style(...) // if we had style API on button directly ), - ]) + ]), ) -} \ No newline at end of file +} diff --git a/examples/macro_showcase/src/main.rs b/examples/macro_showcase/src/main.rs index 6709d4b..f99bafa 100644 --- a/examples/macro_showcase/src/main.rs +++ b/examples/macro_showcase/src/main.rs @@ -1,16 +1,23 @@ -use strato_platform::{ApplicationBuilder, WindowBuilder}; -use strato_widgets::prelude::*; +use strato_core::inspector::{inspector, InspectorConfig}; use strato_core::types::Color; use strato_macros::view; +use strato_platform::{ApplicationBuilder, WindowBuilder}; +use strato_widgets::prelude::*; +use strato_widgets::InspectorOverlay; #[tokio::main] async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt::init(); + inspector().configure(InspectorConfig { + enabled: true, + ..Default::default() + }); + let builder = ApplicationBuilder::new() .title("Macro DSL Showcase") .window(WindowBuilder::new().with_size(800.0, 600.0)); - + // Using the view! macro to declaratively build the UI let root = view! { Container { @@ -43,5 +50,5 @@ async fn main() -> anyhow::Result<()> { // Build the widget tree using the registry let root_widget = registry.build(root); - builder.run(root_widget); + builder.run(InspectorOverlay::new(root_widget)); } diff --git a/examples/modern_dashboard/src/app.rs b/examples/modern_dashboard/src/app.rs index 828752e..c174bd3 100644 --- a/examples/modern_dashboard/src/app.rs +++ b/examples/modern_dashboard/src/app.rs @@ -25,7 +25,7 @@ impl ModernDashboardApp { let sidebar = Sidebar::new(active_tab.clone()).build(); let header = Header::new().build(); - + let view_switcher = ViewSwitcher::new(active_tab); let root = Container::new() @@ -35,7 +35,7 @@ impl ModernDashboardApp { .children(vec![ // Sidebar sidebar, - + // Main Content Area Box::new(Flex::new( Box::new(Column::new() @@ -142,7 +142,7 @@ impl Widget for ViewSwitcher { last_tab: self.last_tab.clone(), }) } - + fn children(&self) -> Vec<&(dyn Widget + '_)> { let current_tab = self.active_tab.get(); if let Some(view) = self.views.get(¤t_tab) { diff --git a/examples/modern_dashboard/src/components/activity_list.rs b/examples/modern_dashboard/src/components/activity_list.rs index c73cc64..b036d1c 100644 --- a/examples/modern_dashboard/src/components/activity_list.rs +++ b/examples/modern_dashboard/src/components/activity_list.rs @@ -32,7 +32,7 @@ impl ActivityList { .font_size(18.0) .font_weight(FontWeight::Bold) .color(theme.text_primary)) as Box, - + self.activity_item("New user registered", "2 min ago", theme.success), self.activity_item("Server rebooted", "15 min ago", theme.warning), self.activity_item("Database backup completed", "1 hour ago", theme.accent), @@ -43,7 +43,7 @@ impl ActivityList { fn activity_item(&self, title: &str, time: &str, dot_color: strato_core::types::Color) -> Box { let theme = &self.theme; - + Box::new(Container::new() .padding(SPACING_SM) .child( @@ -57,7 +57,7 @@ impl ActivityList { .height(10.0) .border_radius(5.0) .background(dot_color)) as Box, - + // Content Box::new(Flex::new( Box::new(Column::new() diff --git a/examples/modern_dashboard/src/components/animated_chart.rs b/examples/modern_dashboard/src/components/animated_chart.rs index 707bc9f..3b7dc9c 100644 --- a/examples/modern_dashboard/src/components/animated_chart.rs +++ b/examples/modern_dashboard/src/components/animated_chart.rs @@ -1,5 +1,5 @@ use strato_widgets::{ - Widget, WidgetId, + Widget, WidgetId, animation::{AnimationController, Curve}, }; // Clean imports: @@ -48,7 +48,7 @@ impl Widget for AnimatedChart { fn layout(&mut self, constraints: strato_core::layout::Constraints) -> Size { // Take all available space, or default specific size if unbounded Size::new( - constraints.max_width.min(800.0).max(300.0), + constraints.max_width.min(800.0).max(300.0), constraints.max_height.min(400.0).max(200.0) ) } @@ -65,15 +65,15 @@ impl Widget for AnimatedChart { for (i, &value) in self.data.iter().enumerate() { let x = layout.position.x + i as f32 * (bar_width + gap); - + // Animate height let target_h = (value / max_val) * layout.size.height; let current_h = target_h * progress; - + let y = layout.position.y + layout.size.height - current_h; let rect = Rect::new(x, y, bar_width, current_h); - + // Use different opacity based on index to show it's dynamic let alpha = 0.5 + 0.5 * (i as f32 / bar_count as f32); let color = Color { diff --git a/examples/modern_dashboard/src/components/sidebar.rs b/examples/modern_dashboard/src/components/sidebar.rs index 8ca663f..ba02770 100644 --- a/examples/modern_dashboard/src/components/sidebar.rs +++ b/examples/modern_dashboard/src/components/sidebar.rs @@ -51,10 +51,10 @@ impl Sidebar { self.nav_item("Analytics", "analytics", active_tab.clone(), theme.clone()), self.nav_item("Users", "users", active_tab.clone(), theme.clone()), self.nav_item("Settings", "settings", active_tab.clone(), theme.clone()), - + // Spacer Box::new(Flex::new(Box::new(Container::new())).flex(1.0)) as Box, - + // Bottom Area Box::new(Container::new() .background(theme.bg_tertiary) @@ -79,7 +79,7 @@ impl Sidebar { // Note: Real interactivity would check active_signal value to change style // For now, we simulate basic structure let id_owned = id.to_string(); - + Box::new(Button::new(label) .secondary() // Use secondary style by default .on_click(move || { diff --git a/examples/modern_dashboard/src/components/stats_card.rs b/examples/modern_dashboard/src/components/stats_card.rs index f2aa7b1..01345a3 100644 --- a/examples/modern_dashboard/src/components/stats_card.rs +++ b/examples/modern_dashboard/src/components/stats_card.rs @@ -41,12 +41,12 @@ impl StatsCard { Box::new(Text::new(&self.title) .color(theme.text_secondary) .font_size(14.0)) as Box, - + Box::new(Text::new(&self.value) .color(theme.text_primary) .font_size(28.0) .font_weight(FontWeight::Bold)) as Box, - + Box::new(Text::new(&self.trend) .color(trend_color) .font_size(12.0)) as Box, diff --git a/examples/modern_dashboard/src/main.rs b/examples/modern_dashboard/src/main.rs index a22e26d..51f5733 100644 --- a/examples/modern_dashboard/src/main.rs +++ b/examples/modern_dashboard/src/main.rs @@ -1,19 +1,24 @@ -use strato_core::{ - types::Color, -}; +use strato_core::inspector::{inspector, InspectorConfig}; +use strato_core::types::Color; use strato_platform::{application::ApplicationBuilder, window::WindowBuilder}; use strato_widgets::{ - prelude::*, container::Container, - text::Text, image::Image, + layout::{Column, Flex, Row}, + prelude::*, + text::Text, top_bar::TopBar, - layout::{Column, Row, Flex}, + InspectorOverlay, }; fn main() { // We don't need registry for direct widget construction - let root_widget = build_ui(); + inspector().configure(InspectorConfig { + enabled: true, + ..Default::default() + }); + + let root_widget = InspectorOverlay::new(build_ui()); ApplicationBuilder::new() .window( @@ -22,23 +27,38 @@ fn main() { .with_size(1200.0, 800.0) .resizable(true) .transparent(true) // Enable glassmorphism support - .decorations(true) // Restore native window controls + .decorations(true), // Restore native window controls ) .run(root_widget); } - // --- Theme Colors --- // Catppuccin-inspired Logic format (0.0-1.0) // --- Theme Colors (Refined) --- -fn col_bg() -> Color { Color::rgba(0.13, 0.13, 0.18, 0.90) } // Semi-transparent background -fn col_sidebar() -> Color { Color::rgba(0.18, 0.18, 0.25, 0.95) } -fn col_card_bg() -> Color { Color::rgba(0.20, 0.20, 0.28, 0.8) } // Glassy cards -fn col_text() -> Color { Color::rgba(0.95, 0.95, 0.95, 1.0) } -fn col_text_dim() -> Color { Color::rgba(0.70, 0.70, 0.80, 1.0) } -fn col_subtext() -> Color { Color::rgba(0.60, 0.60, 0.70, 1.0) } -fn col_accent() -> Color { Color::rgba(0.12, 0.45, 0.98, 1.0) } -fn col_header() -> Color { Color::rgba(0.50, 0.50, 0.60, 1.0) } +fn col_bg() -> Color { + Color::rgba(0.13, 0.13, 0.18, 0.90) +} // Semi-transparent background +fn col_sidebar() -> Color { + Color::rgba(0.18, 0.18, 0.25, 0.95) +} +fn col_card_bg() -> Color { + Color::rgba(0.20, 0.20, 0.28, 0.8) +} // Glassy cards +fn col_text() -> Color { + Color::rgba(0.95, 0.95, 0.95, 1.0) +} +fn col_text_dim() -> Color { + Color::rgba(0.70, 0.70, 0.80, 1.0) +} +fn col_subtext() -> Color { + Color::rgba(0.60, 0.60, 0.70, 1.0) +} +fn col_accent() -> Color { + Color::rgba(0.12, 0.45, 0.98, 1.0) +} +fn col_header() -> Color { + Color::rgba(0.50, 0.50, 0.60, 1.0) +} fn build_ui() -> Container { Container::new() @@ -58,7 +78,7 @@ fn build_ui() -> Container { .children(vec![ // Window Controls Spacer Box::new(Container::new().height(20.0).child(Text::new(""))), - + // User Profile Box::new(Container::new() .padding(16.0) @@ -76,7 +96,7 @@ fn build_ui() -> Container { ]) ) ), - + // TIMELINES section_header("TIMELINES"), sidebar_item("Classic Timeline", "📅", false), @@ -84,14 +104,14 @@ fn build_ui() -> Container { sidebar_item("Federated", "🌐", false), // ACCOUNT - Box::new(Container::new().height(20.0).child(Text::new(""))), + Box::new(Container::new().height(20.0).child(Text::new(""))), section_header("ACCOUNT"), sidebar_item("Your Posts", "📝", false), sidebar_item("Followers", "👥", false), - sidebar_item("Following", "👤", true), + sidebar_item("Following", "👤", true), sidebar_item("Bookmarks", "🔖", false), sidebar_item("Favorites", "⭐️", false), - + // Spacer (Pushes content down) Box::new(Flex::new( Box::new(Container::new().height(1.0).child(Text::new(""))) @@ -106,7 +126,7 @@ fn build_ui() -> Container { .child(Row::new() .spacing(12.0) .children(vec![ - Box::new(Text::new("sun").size(14.0)), + Box::new(Text::new("sun").size(14.0)), Box::new(Text::new("Toggle Theme").color(col_text_dim()).size(14.0)) ]) ) @@ -117,7 +137,7 @@ fn build_ui() -> Container { ]) ) ), - + // Main Content Area Box::new(Container::new() .padding(0.0) @@ -140,23 +160,23 @@ fn build_ui() -> Container { .spacing(16.0) .children(vec![ card_item( - "NDR", - "@NDR", - "Moin! Hier gibt's pro Tag 3-5 Nachrichten aus Hamburg...", + "NDR", + "@NDR", + "Moin! Hier gibt's pro Tag 3-5 Nachrichten aus Hamburg...", "146 Posts 7 Following 5k Followers", "https://avatars.githubusercontent.com/u/1?v=4" ), card_item( - "tagesschau", - "@tagesschau", - "Hier trötet die tagesschau Nachrichten von https://www.tagesschau.de/", + "tagesschau", + "@tagesschau", + "Hier trötet die tagesschau Nachrichten von https://www.tagesschau.de/", "683 Posts 4 Following 20k Followers", "https://avatars.githubusercontent.com/u/2?v=4" ), card_item( - "Simon Willison", - "@simon", - "Open source developer building tools to help journalists...", + "Simon Willison", + "@simon", + "Open source developer building tools to help journalists...", "3k Posts 1k Following 17k Followers", "https://avatars.githubusercontent.com/u/3?v=4" ), @@ -173,46 +193,54 @@ fn build_ui() -> Container { // --- Components --- fn section_header(title: &str) -> Box { - Box::new(Container::new() - .padding(16.0) // Left padding alignment - .margin(0.0) - .child(Text::new(title).color(col_header()).size(11.0)) + Box::new( + Container::new() + .padding(16.0) // Left padding alignment + .margin(0.0) + .child(Text::new(title).color(col_header()).size(11.0)), ) } fn sidebar_item(label: &str, icon: &str, active: bool) -> Box { - let bg_color = if active { col_accent() } else { Color::TRANSPARENT }; + let bg_color = if active { + col_accent() + } else { + Color::TRANSPARENT + }; let text_color = if active { Color::WHITE } else { col_subtext() }; let label_clone = label.to_string(); - - Box::new(Container::new() - .background(bg_color) - .margin(0.0) - .padding(10.0) - .border_radius(6.0) // Rounded active item - .width(240.0) // Slight inset from full width - .child(Row::new() - .spacing(12.0) - .children(vec![ + + Box::new( + Container::new() + .background(bg_color) + .margin(0.0) + .padding(10.0) + .border_radius(6.0) // Rounded active item + .width(240.0) // Slight inset from full width + .child(Row::new().spacing(12.0).children(vec![ Box::new(Text::new(icon).size(14.0)), // Smaller icons - Box::new(Text::new(label).color(text_color).size(14.0)) - ]) - ) - .on_click(move || { - println!("Clicked sidebar item: {}", label_clone); - }) + Box::new(Text::new(label).color(text_color).size(14.0)), + ])) + .on_click(move || { + println!("Clicked sidebar item: {}", label_clone); + }), ) } -fn card_item(name: &str, handle: &str, content: &str, stats: &str, avatar_url: &str) -> Box { - Box::new(Container::new() - .background(col_card_bg()) - .padding(16.0) - .border_radius(10.0) // Smooth card rounding - .width(600.0) // Fixed card width for specific look - .child(Column::new() - .spacing(12.0) - .children(vec![ +fn card_item( + name: &str, + handle: &str, + content: &str, + stats: &str, + avatar_url: &str, +) -> Box { + Box::new( + Container::new() + .background(col_card_bg()) + .padding(16.0) + .border_radius(10.0) // Smooth card rounding + .width(600.0) // Fixed card width for specific look + .child(Column::new().spacing(12.0).children(vec![ // Header Row Box::new(Row::new() .spacing(12.0) @@ -258,7 +286,6 @@ fn card_item(name: &str, handle: &str, content: &str, stats: &str, avatar_url: & .padding(0.0) .child(Text::new(stats).color(col_header()).size(12.0)) ) - ]) - ) + ])), ) } diff --git a/examples/modern_dashboard/src/views/dashboard.rs b/examples/modern_dashboard/src/views/dashboard.rs index fa7c93f..a8c2c4e 100644 --- a/examples/modern_dashboard/src/views/dashboard.rs +++ b/examples/modern_dashboard/src/views/dashboard.rs @@ -69,9 +69,9 @@ impl DashboardView { Box::new(Text::new("Growth Analytics") .font_size(20.0) .color(theme.text_primary)) as Box, - + Box::new(AnimatedChart::new(vec![ - 65.0, 40.0, 100.0, 85.0, 45.0, 92.0, + 65.0, 40.0, 100.0, 85.0, 45.0, 92.0, 55.0, 70.0, 30.0, 60.0, 95.0, 80.0 ]).color(Color::rgb(0.4, 0.7, 1.0))) as Box ])