gpui_component/chart/
pie_chart.rs1use 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}