gpui_component/chart/
pie_chart.rs

1use std::rc::Rc;
2
3use gpui::{App, Bounds, Hsla, Pixels, Window};
4use gpui_component_macros::IntoPlot;
5use num_traits::Zero;
6
7use crate::{
8    plot::{
9        shape::{Arc, Pie},
10        Plot,
11    },
12    ActiveTheme, PixelsExt,
13};
14
15#[derive(IntoPlot)]
16pub struct PieChart<T: 'static> {
17    data: Vec<T>,
18    inner_radius: f32,
19    outer_radius: f32,
20    pad_angle: f32,
21    value: Option<Rc<dyn Fn(&T) -> f32>>,
22    color: Option<Rc<dyn Fn(&T) -> Hsla>>,
23}
24
25impl<T> PieChart<T> {
26    pub fn new<I>(data: I) -> Self
27    where
28        I: IntoIterator<Item = T>,
29    {
30        Self {
31            data: data.into_iter().collect(),
32            inner_radius: 0.,
33            outer_radius: 0.,
34            pad_angle: 0.,
35            value: None,
36            color: None,
37        }
38    }
39
40    pub fn inner_radius(mut self, inner_radius: f32) -> Self {
41        self.inner_radius = inner_radius;
42        self
43    }
44
45    pub fn outer_radius(mut self, outer_radius: f32) -> Self {
46        self.outer_radius = outer_radius;
47        self
48    }
49
50    pub fn pad_angle(mut self, pad_angle: f32) -> Self {
51        self.pad_angle = pad_angle;
52        self
53    }
54
55    pub fn value(mut self, value: impl Fn(&T) -> f32 + 'static) -> Self {
56        self.value = Some(Rc::new(value));
57        self
58    }
59
60    pub fn color<H>(mut self, color: impl Fn(&T) -> H + 'static) -> Self
61    where
62        H: Into<Hsla> + 'static,
63    {
64        self.color = Some(Rc::new(move |t| color(t).into()));
65        self
66    }
67}
68
69impl<T> Plot for PieChart<T> {
70    fn paint(&mut self, bounds: Bounds<Pixels>, window: &mut Window, cx: &mut App) {
71        let Some(value_fn) = self.value.as_ref() else {
72            return;
73        };
74
75        let outer_radius = if self.outer_radius.is_zero() {
76            bounds.size.height.as_f32() * 0.4
77        } else {
78            self.outer_radius
79        };
80
81        let arc = Arc::new()
82            .inner_radius(self.inner_radius)
83            .outer_radius(outer_radius);
84        let value_fn = value_fn.clone();
85        let mut pie = Pie::<T>::new().value(move |d| Some(value_fn(d)));
86        pie = pie.pad_angle(self.pad_angle);
87        let arcs = pie.arcs(&self.data);
88
89        for a in &arcs {
90            arc.paint(
91                a,
92                if let Some(color_fn) = self.color.as_ref() {
93                    color_fn(a.data)
94                } else {
95                    cx.theme().chart_2
96                },
97                &bounds,
98                window,
99            );
100        }
101    }
102}