Skip to main content

rosace_widgets/tree/
circular_progress.rs

1use rosace_core::types::{Point, Size};
2use rosace_render::Color;
3use super::{Widget, LayoutCtx, PaintCtx};
4
5/// A circular progress indicator — a determinate ring (`value` 0..1) or an
6/// indeterminate spinner. Draws a ring segment (the FillArc primitive).
7pub struct CircularProgress {
8    value: Option<f32>,     // None = indeterminate spinner
9    diameter: f32,
10    thickness: f32,
11    color: Option<Color>,
12    track: Option<Color>,
13}
14
15impl CircularProgress {
16    /// Determinate ring filled to `value` (0..1).
17    pub fn new(value: f32) -> Self {
18        Self { value: Some(value.clamp(0.0, 1.0)), diameter: 36.0, thickness: 4.0, color: None, track: None }
19    }
20    /// Indeterminate spinner.
21    pub fn spinner() -> Self {
22        Self { value: None, diameter: 36.0, thickness: 4.0, color: None, track: None }
23    }
24    pub fn diameter(mut self, d: f32) -> Self { self.diameter = d; self }
25    pub fn thickness(mut self, t: f32) -> Self { self.thickness = t; self }
26    pub fn color(mut self, c: Color) -> Self { self.color = Some(c); self }
27    pub fn track(mut self, c: Color) -> Self { self.track = Some(c); self }
28}
29
30impl Widget for CircularProgress {
31    fn layout(&self, ctx: &LayoutCtx) -> Size {
32        ctx.constraints.constrain(Size { width: self.diameter, height: self.diameter })
33    }
34
35    fn paint(&self, ctx: &mut PaintCtx) {
36        let r = ctx.rect;
37        let color = self.color.unwrap_or_else(|| ctx.tc(ctx.theme.colors.primary));
38        let center = Point { x: r.origin.x + r.size.width / 2.0, y: r.origin.y + r.size.height / 2.0 };
39        let radius = (self.diameter - self.thickness) / 2.0;
40
41        match self.value {
42            Some(v) => {
43                // Track ring + value arc from 12 o'clock, clockwise.
44                let track = self.track.unwrap_or(Color::rgba(255, 255, 255, 28));
45                ctx.fill_arc(center, radius, self.thickness, 0.0, 360.0, track);
46                if v > 0.0 {
47                    ctx.fill_arc(center, radius, self.thickness, -90.0, 360.0 * v, color);
48                }
49            }
50            None => {
51                // Spinner: a 270° arc whose start rotates with the clock.
52                let t = super::anim_clock();
53                let start = (t * 360.0) % 360.0;
54                ctx.fill_arc(center, radius, self.thickness, start, 270.0, color);
55                ctx.request_animation();
56            }
57        }
58    }
59}