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, ArcData, 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 inner_radius_fn: Option<Rc<dyn Fn(&ArcData<T>) -> f32 + 'static>>,
20 outer_radius: f32,
21 outer_radius_fn: Option<Rc<dyn Fn(&ArcData<T>) -> f32 + 'static>>,
22 pad_angle: f32,
23 value: Option<Rc<dyn Fn(&T) -> f32>>,
24 color: Option<Rc<dyn Fn(&T) -> Hsla>>,
25}
26
27impl<T> PieChart<T> {
28 pub fn new<I>(data: I) -> Self
29 where
30 I: IntoIterator<Item = T>,
31 {
32 Self {
33 data: data.into_iter().collect(),
34 inner_radius: 0.,
35 inner_radius_fn: None,
36 outer_radius: 0.,
37 outer_radius_fn: None,
38 pad_angle: 0.,
39 value: None,
40 color: None,
41 }
42 }
43
44 pub fn inner_radius(mut self, inner_radius: f32) -> Self {
46 self.inner_radius = inner_radius;
47 self
48 }
49
50 pub fn inner_radius_fn(
52 mut self,
53 inner_radius_fn: impl Fn(&ArcData<T>) -> f32 + 'static,
54 ) -> Self {
55 self.inner_radius_fn = Some(Rc::new(inner_radius_fn));
56 self
57 }
58
59 fn get_inner_radius(&self, arc: &ArcData<T>) -> f32 {
60 if let Some(inner_radius_fn) = self.inner_radius_fn.as_ref() {
61 inner_radius_fn(arc)
62 } else {
63 self.inner_radius
64 }
65 }
66
67 pub fn outer_radius(mut self, outer_radius: f32) -> Self {
69 self.outer_radius = outer_radius;
70 self
71 }
72
73 pub fn outer_radius_fn(
75 mut self,
76 outer_radius_fn: impl Fn(&ArcData<T>) -> f32 + 'static,
77 ) -> Self {
78 self.outer_radius_fn = Some(Rc::new(outer_radius_fn));
79 self
80 }
81
82 fn get_outer_radius(&self, arc: &ArcData<T>) -> f32 {
83 if let Some(outer_radius_fn) = self.outer_radius_fn.as_ref() {
84 outer_radius_fn(arc)
85 } else {
86 self.outer_radius
87 }
88 }
89
90 pub fn pad_angle(mut self, pad_angle: f32) -> Self {
92 self.pad_angle = pad_angle;
93 self
94 }
95
96 pub fn value(mut self, value: impl Fn(&T) -> f32 + 'static) -> Self {
97 self.value = Some(Rc::new(value));
98 self
99 }
100
101 pub fn color<H>(mut self, color: impl Fn(&T) -> H + 'static) -> Self
103 where
104 H: Into<Hsla> + 'static,
105 {
106 self.color = Some(Rc::new(move |t| color(t).into()));
107 self
108 }
109}
110
111impl<T> Plot for PieChart<T> {
112 fn paint(&mut self, bounds: Bounds<Pixels>, window: &mut Window, cx: &mut App) {
113 let Some(value_fn) = self.value.as_ref() else {
114 return;
115 };
116
117 let outer_radius = if self.outer_radius.is_zero() {
118 bounds.size.height.as_f32() * 0.4
119 } else {
120 self.outer_radius
121 };
122
123 let arc = Arc::new()
124 .inner_radius(self.inner_radius)
125 .outer_radius(outer_radius);
126 let value_fn = value_fn.clone();
127 let mut pie = Pie::<T>::new().value(move |d| Some(value_fn(d)));
128 pie = pie.pad_angle(self.pad_angle);
129 let arcs = pie.arcs(&self.data);
130
131 for a in &arcs {
132 let inner_radius = self.get_inner_radius(a);
133 let outer_radius = self.get_outer_radius(a);
134 arc.paint(
135 a,
136 if let Some(color_fn) = self.color.as_ref() {
137 color_fn(a.data)
138 } else {
139 cx.theme().chart_2
140 },
141 Some(inner_radius),
142 Some(outer_radius),
143 &bounds,
144 window,
145 );
146 }
147 }
148}