flashkraft_gui/components/
animated_progress.rs1use iced::widget::canvas::{self, Cache, Frame, Geometry, Path, Stroke};
7use iced::{Color, Element, Length, Point, Rectangle, Renderer, Size, Theme};
8
9#[derive(Debug)]
11pub struct AnimatedProgress {
12 progress: f32,
14 animation_time: f32,
16 cache: Cache,
18 theme: Theme,
20}
21
22impl AnimatedProgress {
23 pub fn new() -> Self {
25 Self {
26 progress: 0.0,
27 animation_time: 0.0,
28 cache: Cache::new(),
29 theme: Theme::Dark,
30 }
31 }
32
33 pub fn set_progress(&mut self, progress: f32) {
35 let new_progress = progress.clamp(0.0, 1.0);
36 if (self.progress - new_progress).abs() > 0.001 {
37 self.progress = new_progress;
38 self.cache.clear();
39 }
40 }
41
42 pub fn tick(&mut self, speed_mb_s: f32) {
47 let speed_multiplier = (speed_mb_s / 20.0).clamp(0.3, 3.0);
52 self.animation_time += 0.05 * speed_multiplier;
53 if self.animation_time > 1000.0 {
54 self.animation_time = 0.0;
55 }
56 self.cache.clear();
57 }
58
59 pub fn set_theme(&mut self, theme: Theme) {
61 self.theme = theme;
62 self.cache.clear();
63 }
64
65 pub fn view<'a, Message: 'a>(&'a self) -> Element<'a, Message, Theme, Renderer> {
67 iced::widget::canvas(self)
68 .width(Length::Fill)
69 .height(Length::Fixed(20.0))
70 .into()
71 }
72}
73
74impl Default for AnimatedProgress {
75 fn default() -> Self {
76 Self::new()
77 }
78}
79
80impl<Message> canvas::Program<Message, Theme, Renderer> for AnimatedProgress {
81 type State = ();
82
83 fn draw(
84 &self,
85 _state: &Self::State,
86 renderer: &Renderer,
87 _theme: &Theme,
88 bounds: Rectangle,
89 _cursor: iced::mouse::Cursor,
90 ) -> Vec<Geometry<Renderer>> {
91 let geometry = self.cache.draw(renderer, bounds.size(), |frame| {
92 draw_animated_progress(
93 frame,
94 bounds.size(),
95 self.progress,
96 self.animation_time,
97 &self.theme,
98 );
99 });
100
101 vec![geometry]
102 }
103}
104
105fn draw_animated_progress(frame: &mut Frame, size: Size, progress: f32, time: f32, theme: &Theme) {
107 let width = size.width;
108 let height = size.height;
109
110 let background = Path::rectangle(Point::ORIGIN, size);
112 frame.fill(&background, Color::from_rgb(0.15, 0.15, 0.15));
113
114 if progress > 0.0 {
115 let progress_width = width * progress;
116
117 draw_gradient_progress(frame, progress_width, height, time, theme);
119
120 draw_shimmer_effect(frame, progress_width, height, time);
122
123 if progress < 1.0 {
125 draw_edge_pulse(frame, progress_width, height, time);
126 }
127 }
128
129 let border = Path::rectangle(Point::ORIGIN, size);
131 frame.stroke(
132 &border,
133 Stroke::default()
134 .with_color(Color::from_rgb(0.3, 0.3, 0.3))
135 .with_width(1.0),
136 );
137}
138
139fn draw_gradient_progress(frame: &mut Frame, width: f32, height: f32, time: f32, theme: &Theme) {
141 let palette = theme.palette();
143 let primary = palette.primary;
144
145 let color_start = Color::from_rgb(primary.r, primary.g, primary.b);
147 let color_end = Color::from_rgb(
148 (primary.r * 0.7 + 0.3).min(1.0),
149 (primary.g * 0.9 + 0.1).min(1.0),
150 (primary.b * 0.95 + 0.05).min(1.0),
151 );
152 let segments = 20;
154 let segment_width = width / segments as f32;
155
156 for i in 0..segments {
157 let x = i as f32 * segment_width;
158 let progress_ratio = i as f32 / segments as f32;
159
160 let hue_shift = (time * 0.2 + progress_ratio * 2.0).sin() * 0.08;
162
163 let base_color = interpolate_color(color_start, color_end, progress_ratio);
165
166 let animated_color = Color {
167 r: (base_color.r + hue_shift).clamp(0.0, 1.0),
168 g: (base_color.g + hue_shift * 0.5).clamp(0.0, 1.0),
169 b: (base_color.b - hue_shift * 0.3).clamp(0.0, 1.0),
170 a: 1.0,
171 };
172
173 let segment = Path::rectangle(Point::new(x, 0.0), Size::new(segment_width + 1.0, height));
174 frame.fill(&segment, animated_color);
175 }
176}
177
178fn draw_shimmer_effect(frame: &mut Frame, width: f32, height: f32, time: f32) {
180 let shine_position = (time * 0.3).fract();
182 let shine_x = width * shine_position;
183 let shine_width = width * 0.15;
184
185 let shimmer_segments = 10;
187 for i in 0..shimmer_segments {
188 let offset = i as f32 / shimmer_segments as f32;
189 let x = shine_x + (offset - 0.5) * shine_width;
190
191 if x >= 0.0 && x <= width {
192 let distance_from_center = ((offset - 0.5) * 2.0).abs();
194 let alpha = (1.0 - distance_from_center.powf(2.0)) * 0.3;
195
196 let shimmer = Path::rectangle(
197 Point::new(x, 0.0),
198 Size::new(shine_width / shimmer_segments as f32, height),
199 );
200
201 frame.fill(
202 &shimmer,
203 Color {
204 r: 1.0,
205 g: 1.0,
206 b: 1.0,
207 a: alpha,
208 },
209 );
210 }
211 }
212}
213
214fn draw_edge_pulse(frame: &mut Frame, width: f32, height: f32, time: f32) {
216 let pulse = (time * 3.0).sin() * 0.5 + 0.5;
217 let pulse_width = 4.0 + pulse * 2.0;
218
219 let edge = Path::rectangle(
221 Point::new(width - pulse_width / 2.0, 0.0),
222 Size::new(pulse_width, height),
223 );
224
225 frame.fill(
226 &edge,
227 Color {
228 r: 1.0,
229 g: 1.0,
230 b: 1.0,
231 a: 0.3 + pulse * 0.3,
232 },
233 );
234}
235
236fn interpolate_color(start: Color, end: Color, t: f32) -> Color {
238 Color {
239 r: start.r + (end.r - start.r) * t,
240 g: start.g + (end.g - start.g) * t,
241 b: start.b + (end.b - start.b) * t,
242 a: start.a + (end.a - start.a) * t,
243 }
244}