use gpui::prelude::*;
use gpui::{canvas, fill, point, px, size, App, Bounds, Hsla, IntoElement, Window};
use crate::style::ColorValue;
use crate::theme::theme;
use super::{paint_polyline, resolve_color};
const GRIDLINES: usize = 4;
#[derive(IntoElement)]
pub struct LineChart {
values: Vec<f32>,
color: Option<ColorValue>,
stroke: f32,
fill: bool,
width: Option<f32>,
height: f32,
}
impl LineChart {
pub fn new(values: impl IntoIterator<Item = f32>) -> Self {
LineChart {
values: values.into_iter().collect(),
color: None,
stroke: 2.0,
fill: false,
width: None,
height: 140.0,
}
}
pub fn color(mut self, color: impl Into<ColorValue>) -> Self {
self.color = Some(color.into());
self
}
pub fn stroke(mut self, width: f32) -> Self {
self.stroke = width.max(0.5);
self
}
pub fn fill(mut self) -> Self {
self.fill = true;
self
}
pub fn width(mut self, width: f32) -> Self {
self.width = Some(width);
self
}
pub fn height(mut self, height: f32) -> Self {
self.height = height;
self
}
}
impl RenderOnce for LineChart {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
let t = theme(cx);
let line = self
.color
.map(|c| resolve_color(t, c))
.unwrap_or_else(|| t.primary().hsla());
let area = self.fill.then_some(Hsla { a: 0.15, ..line });
let grid = t.border().alpha(0.5);
let stroke = self.stroke;
let values = self.values;
let plot = canvas(
|_, _, _| (),
move |bounds, _, window, _cx| {
let w = f32::from(bounds.size.width);
let h = f32::from(bounds.size.height);
if w <= 0.0 || h <= 0.0 {
return;
}
for i in 0..GRIDLINES {
let y = (h - 1.0) * (i as f32 / (GRIDLINES - 1) as f32);
window.paint_quad(fill(
Bounds::new(bounds.origin + point(px(0.0), px(y)), size(px(w), px(1.0))),
grid,
));
}
paint_polyline(window, bounds, &values, stroke, line, area);
},
)
.h(px(self.height));
match self.width {
Some(w) => plot.w(px(w)),
None => plot.w_full(),
}
}
}