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/components/animated_chart.rs
StratoSDK / examples / modern_dashboard / src / components / animated_chart.rs
1use strato_widgets::{
2 Widget, WidgetId,
3 animation::{AnimationController, Curve},
4};
5// Clean imports:
6use strato_core::layout::{Layout, Size};
7use strato_renderer::batch::RenderBatch;
8use strato_core::{
9 types::{Color, Point, Rect},
10 event::{Event, EventResult},
11};
12use std::any::Any;
13use std::time::Duration;
14 
15#[derive(Debug)]
16pub struct AnimatedChart {
17 id: WidgetId,
18 data: Vec<f32>,
19 bar_color: Color,
20 anim_controller: AnimationController,
21}
22 
23impl AnimatedChart {
24 pub fn new(data: Vec<f32>) -> Self {
25 let mut controller = AnimationController::new(Duration::from_millis(800))
26 .with_curve(Curve::EaseOut);
27 controller.start();
28 
29 Self {
30 id: strato_widgets::widget::generate_id(),
31 data,
32 bar_color: Color::rgb(0.2, 0.6, 1.0), // Blue-ish
33 anim_controller: controller,
34 }
35 }
36 
37 pub fn color(mut self, color: Color) -> Self {
38 self.bar_color = color;
39 self
40 }
41}
42 
43impl Widget for AnimatedChart {
44 fn id(&self) -> WidgetId {
45 self.id
46 }
47 
48 fn layout(&mut self, constraints: strato_core::layout::Constraints) -> Size {
49 // Take all available space, or default specific size if unbounded
50 Size::new(
51 constraints.max_width.min(800.0).max(300.0),
52 constraints.max_height.min(400.0).max(200.0)
53 )
54 }
55 
56 fn render(&self, batch: &mut RenderBatch, layout: Layout) {
57 let progress = self.anim_controller.value();
58 let bar_count = self.data.len();
59 if bar_count == 0 { return; }
60 
61 let gap = 10.0;
62 let total_gap = gap * (bar_count - 1) as f32;
63 let bar_width = (layout.size.width - total_gap) / bar_count as f32;
64 let max_val = self.data.iter().cloned().fold(0.0f32, f32::max);
65 
66 for (i, &value) in self.data.iter().enumerate() {
67 let x = layout.position.x + i as f32 * (bar_width + gap);
68 
69 // Animate height
70 let target_h = (value / max_val) * layout.size.height;
71 let current_h = target_h * progress;
72 
73 let y = layout.position.y + layout.size.height - current_h;
74 
75 let rect = Rect::new(x, y, bar_width, current_h);
76 
77 // Use different opacity based on index to show it's dynamic
78 let alpha = 0.5 + 0.5 * (i as f32 / bar_count as f32);
79 let color = Color {
80 r: self.bar_color.r,
81 g: self.bar_color.g,
82 b: self.bar_color.b,
83 a: alpha,
84 };
85 
86 batch.add_rect(rect, color, strato_core::types::Transform::identity());
87 }
88 }
89 
90 fn handle_event(&mut self, event: &Event) -> EventResult {
91 // Simple hover effect or restart animation on click could go here
92 if let Event::MouseDown(_) = event {
93 self.anim_controller.reset();
94 self.anim_controller.start();
95 return EventResult::Handled;
96 }
97 EventResult::Ignored
98 }
99 
100 fn as_any(&self) -> &dyn Any {
101 self
102 }
103 
104 fn as_any_mut(&mut self) -> &mut dyn Any {
105 self
106 }
107 
108 fn clone_widget(&self) -> Box<dyn Widget> {
109 Box::new(Self {
110 id: strato_widgets::widget::generate_id(),
111 data: self.data.clone(),
112 bar_color: self.bar_color,
113 anim_controller: self.anim_controller.clone(),
114 })
115 }
116}
117