chart(pie): Support dynamic inner and outer radius (#1444)
## ⚠️Breaking Change Add `inner_radius` and `outer_radius` options for `Arc` paint function. ```diff - arc.paint(a, "#9d9d9d", bounds, window) + arc.paint(a, "#9d9d9d", Some(20.), Some(30.), bounds, window) ``` ## Demo <img width="410" height="442" alt="SCR-20251028-kohu" src="https://github.com/user-attachments/assets/8b849053-22e6-4d6f-933f-b9fb2a47f5e3" />
This commit is contained in:
parent
904c9f3fa7
commit
19a54d6065
4 changed files with 69 additions and 14 deletions
|
|
@ -1,13 +1,14 @@
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, linear_color_stop, linear_gradient, prelude::FluentBuilder, px, App, AppContext, Context,
|
App, AppContext, Context, Entity, FocusHandle, Focusable, Hsla, IntoElement, ParentElement,
|
||||||
Entity, FocusHandle, Focusable, Hsla, IntoElement, ParentElement, Render, SharedString, Styled,
|
Render, SharedString, Styled, Window, div, linear_color_stop, linear_gradient,
|
||||||
Window,
|
prelude::FluentBuilder, px,
|
||||||
};
|
};
|
||||||
use gpui_component::{
|
use gpui_component::{
|
||||||
|
ActiveTheme, StyledExt,
|
||||||
chart::{AreaChart, BarChart, LineChart, PieChart},
|
chart::{AreaChart, BarChart, LineChart, PieChart},
|
||||||
divider::Divider,
|
divider::Divider,
|
||||||
dock::PanelControl,
|
dock::PanelControl,
|
||||||
h_flex, v_flex, ActiveTheme, StyledExt,
|
h_flex, v_flex,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
|
@ -174,8 +175,8 @@ impl Render for ChartStory {
|
||||||
"Pie Chart - Donut",
|
"Pie Chart - Donut",
|
||||||
PieChart::new(self.monthly_devices.clone())
|
PieChart::new(self.monthly_devices.clone())
|
||||||
.value(|d| d.desktop as f32)
|
.value(|d| d.desktop as f32)
|
||||||
.outer_radius(100.)
|
|
||||||
.inner_radius(60.)
|
.inner_radius(60.)
|
||||||
|
.outer_radius_fn(|d| 100. - d.index as f32 * 4.)
|
||||||
.color(move |d| d.color(color)),
|
.color(move |d| d.color(color)),
|
||||||
true,
|
true,
|
||||||
cx,
|
cx,
|
||||||
|
|
@ -184,8 +185,8 @@ impl Render for ChartStory {
|
||||||
"Pie Chart - Pad Angle",
|
"Pie Chart - Pad Angle",
|
||||||
PieChart::new(self.monthly_devices.clone())
|
PieChart::new(self.monthly_devices.clone())
|
||||||
.value(|d| d.desktop as f32)
|
.value(|d| d.desktop as f32)
|
||||||
.outer_radius(100.)
|
|
||||||
.inner_radius(60.)
|
.inner_radius(60.)
|
||||||
|
.outer_radius(100.)
|
||||||
.pad_angle(4. / 100.)
|
.pad_angle(4. / 100.)
|
||||||
.color(move |d| d.color(color)),
|
.color(move |d| d.color(color)),
|
||||||
true,
|
true,
|
||||||
|
|
@ -227,7 +228,7 @@ impl Render for ChartStory {
|
||||||
.child(Divider::horizontal())
|
.child(Divider::horizontal())
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.gap_x_8()
|
.gap_x_4()
|
||||||
.h(px(400.))
|
.h(px(400.))
|
||||||
.child(chart_container(
|
.child(chart_container(
|
||||||
"Line Chart",
|
"Line Chart",
|
||||||
|
|
@ -268,7 +269,7 @@ impl Render for ChartStory {
|
||||||
.child(Divider::horizontal())
|
.child(Divider::horizontal())
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.gap_x_8()
|
.gap_x_4()
|
||||||
.h(px(400.))
|
.h(px(400.))
|
||||||
.child(chart_container(
|
.child(chart_container(
|
||||||
"Area Chart",
|
"Area Chart",
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use num_traits::Zero;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
plot::{
|
plot::{
|
||||||
shape::{Arc, Pie},
|
shape::{Arc, ArcData, Pie},
|
||||||
Plot,
|
Plot,
|
||||||
},
|
},
|
||||||
ActiveTheme, PixelsExt,
|
ActiveTheme, PixelsExt,
|
||||||
|
|
@ -16,7 +16,9 @@ use crate::{
|
||||||
pub struct PieChart<T: 'static> {
|
pub struct PieChart<T: 'static> {
|
||||||
data: Vec<T>,
|
data: Vec<T>,
|
||||||
inner_radius: f32,
|
inner_radius: f32,
|
||||||
|
inner_radius_fn: Option<Rc<dyn Fn(&ArcData<T>) -> f32 + 'static>>,
|
||||||
outer_radius: f32,
|
outer_radius: f32,
|
||||||
|
outer_radius_fn: Option<Rc<dyn Fn(&ArcData<T>) -> f32 + 'static>>,
|
||||||
pad_angle: f32,
|
pad_angle: f32,
|
||||||
value: Option<Rc<dyn Fn(&T) -> f32>>,
|
value: Option<Rc<dyn Fn(&T) -> f32>>,
|
||||||
color: Option<Rc<dyn Fn(&T) -> Hsla>>,
|
color: Option<Rc<dyn Fn(&T) -> Hsla>>,
|
||||||
|
|
@ -30,23 +32,62 @@ impl<T> PieChart<T> {
|
||||||
Self {
|
Self {
|
||||||
data: data.into_iter().collect(),
|
data: data.into_iter().collect(),
|
||||||
inner_radius: 0.,
|
inner_radius: 0.,
|
||||||
|
inner_radius_fn: None,
|
||||||
outer_radius: 0.,
|
outer_radius: 0.,
|
||||||
|
outer_radius_fn: None,
|
||||||
pad_angle: 0.,
|
pad_angle: 0.,
|
||||||
value: None,
|
value: None,
|
||||||
color: None,
|
color: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the inner radius of the pie chart.
|
||||||
pub fn inner_radius(mut self, inner_radius: f32) -> Self {
|
pub fn inner_radius(mut self, inner_radius: f32) -> Self {
|
||||||
self.inner_radius = inner_radius;
|
self.inner_radius = inner_radius;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the inner radius of the pie chart based on the arc data.
|
||||||
|
pub fn inner_radius_fn(
|
||||||
|
mut self,
|
||||||
|
inner_radius_fn: impl Fn(&ArcData<T>) -> f32 + 'static,
|
||||||
|
) -> Self {
|
||||||
|
self.inner_radius_fn = Some(Rc::new(inner_radius_fn));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_inner_radius(&self, arc: &ArcData<T>) -> f32 {
|
||||||
|
if let Some(inner_radius_fn) = self.inner_radius_fn.as_ref() {
|
||||||
|
inner_radius_fn(arc)
|
||||||
|
} else {
|
||||||
|
self.inner_radius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the outer radius of the pie chart.
|
||||||
pub fn outer_radius(mut self, outer_radius: f32) -> Self {
|
pub fn outer_radius(mut self, outer_radius: f32) -> Self {
|
||||||
self.outer_radius = outer_radius;
|
self.outer_radius = outer_radius;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the outer radius of the pie chart based on the arc data.
|
||||||
|
pub fn outer_radius_fn(
|
||||||
|
mut self,
|
||||||
|
outer_radius_fn: impl Fn(&ArcData<T>) -> f32 + 'static,
|
||||||
|
) -> Self {
|
||||||
|
self.outer_radius_fn = Some(Rc::new(outer_radius_fn));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_outer_radius(&self, arc: &ArcData<T>) -> f32 {
|
||||||
|
if let Some(outer_radius_fn) = self.outer_radius_fn.as_ref() {
|
||||||
|
outer_radius_fn(arc)
|
||||||
|
} else {
|
||||||
|
self.outer_radius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the pad angle of the pie chart.
|
||||||
pub fn pad_angle(mut self, pad_angle: f32) -> Self {
|
pub fn pad_angle(mut self, pad_angle: f32) -> Self {
|
||||||
self.pad_angle = pad_angle;
|
self.pad_angle = pad_angle;
|
||||||
self
|
self
|
||||||
|
|
@ -57,6 +98,7 @@ impl<T> PieChart<T> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the color of the pie chart.
|
||||||
pub fn color<H>(mut self, color: impl Fn(&T) -> H + 'static) -> Self
|
pub fn color<H>(mut self, color: impl Fn(&T) -> H + 'static) -> Self
|
||||||
where
|
where
|
||||||
H: Into<Hsla> + 'static,
|
H: Into<Hsla> + 'static,
|
||||||
|
|
@ -87,6 +129,8 @@ impl<T> Plot for PieChart<T> {
|
||||||
let arcs = pie.arcs(&self.data);
|
let arcs = pie.arcs(&self.data);
|
||||||
|
|
||||||
for a in &arcs {
|
for a in &arcs {
|
||||||
|
let inner_radius = self.get_inner_radius(a);
|
||||||
|
let outer_radius = self.get_outer_radius(a);
|
||||||
arc.paint(
|
arc.paint(
|
||||||
a,
|
a,
|
||||||
if let Some(color_fn) = self.color.as_ref() {
|
if let Some(color_fn) = self.color.as_ref() {
|
||||||
|
|
@ -94,6 +138,8 @@ impl<T> Plot for PieChart<T> {
|
||||||
} else {
|
} else {
|
||||||
cx.theme().chart_2
|
cx.theme().chart_2
|
||||||
},
|
},
|
||||||
|
Some(inner_radius),
|
||||||
|
Some(outer_radius),
|
||||||
&bounds,
|
&bounds,
|
||||||
window,
|
window,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ mod bar;
|
||||||
mod line;
|
mod line;
|
||||||
mod pie;
|
mod pie;
|
||||||
|
|
||||||
pub use arc::Arc;
|
pub use arc::{Arc, ArcData};
|
||||||
pub use area::Area;
|
pub use area::Area;
|
||||||
pub use bar::Bar;
|
pub use bar::Bar;
|
||||||
pub use line::Line;
|
pub use line::Line;
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,13 @@ impl Arc {
|
||||||
point(r * a.cos(), r * a.sin())
|
point(r * a.cos(), r * a.sin())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path<T>(&self, arc: &ArcData<T>, bounds: &Bounds<Pixels>) -> Option<Path<Pixels>> {
|
fn path<T>(
|
||||||
|
&self,
|
||||||
|
arc: &ArcData<T>,
|
||||||
|
inner_radius: Option<f32>,
|
||||||
|
outer_radius: Option<f32>,
|
||||||
|
bounds: &Bounds<Pixels>,
|
||||||
|
) -> Option<Path<Pixels>> {
|
||||||
let start_angle = arc.start_angle - HALF_PI;
|
let start_angle = arc.start_angle - HALF_PI;
|
||||||
let end_angle = arc.end_angle - HALF_PI;
|
let end_angle = arc.end_angle - HALF_PI;
|
||||||
let da = end_angle - start_angle;
|
let da = end_angle - start_angle;
|
||||||
|
|
@ -80,8 +86,8 @@ impl Arc {
|
||||||
} else {
|
} else {
|
||||||
arc.pad_angle
|
arc.pad_angle
|
||||||
};
|
};
|
||||||
let r0 = self.inner_radius.max(0.);
|
let r0 = inner_radius.unwrap_or(self.inner_radius).max(0.);
|
||||||
let r1 = self.outer_radius.max(0.);
|
let r1 = outer_radius.unwrap_or(self.outer_radius).max(0.);
|
||||||
|
|
||||||
// Calculate the center point.
|
// Calculate the center point.
|
||||||
let center_x = bounds.origin.x.as_f32() + bounds.size.width.as_f32() / 2.;
|
let center_x = bounds.origin.x.as_f32() + bounds.size.width.as_f32() / 2.;
|
||||||
|
|
@ -173,10 +179,12 @@ impl Arc {
|
||||||
&self,
|
&self,
|
||||||
arc: &ArcData<T>,
|
arc: &ArcData<T>,
|
||||||
color: impl Into<Hsla>,
|
color: impl Into<Hsla>,
|
||||||
|
inner_radius: Option<f32>,
|
||||||
|
outer_radius: Option<f32>,
|
||||||
bounds: &Bounds<Pixels>,
|
bounds: &Bounds<Pixels>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
) {
|
) {
|
||||||
let path = self.path(arc, bounds);
|
let path = self.path(arc, inner_radius, outer_radius, bounds);
|
||||||
if let Some(path) = path {
|
if let Some(path) = path {
|
||||||
window.paint_path(path, color.into());
|
window.paint_path(path, color.into());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue