gpui_component/plot/
axis.rs

1use gpui::{
2    point, px, App, Bounds, FontWeight, Hsla, PathBuilder, Pixels, Point, SharedString, TextAlign,
3    Window,
4};
5
6use super::{label::Label, label::Text, label::TEXT_GAP, label::TEXT_SIZE, origin_point};
7
8pub const AXIS_GAP: f32 = 18.;
9
10pub struct AxisText {
11    pub text: SharedString,
12    pub tick: Pixels,
13    pub color: Hsla,
14    pub font_size: Pixels,
15    pub align: TextAlign,
16}
17
18impl AxisText {
19    pub fn new(text: impl Into<SharedString>, tick: impl Into<Pixels>, color: Hsla) -> Self {
20        Self {
21            text: text.into(),
22            tick: tick.into(),
23            color,
24            font_size: TEXT_SIZE.into(),
25            align: TextAlign::Left,
26        }
27    }
28
29    pub fn font_size(mut self, font_size: impl Into<Pixels>) -> Self {
30        self.font_size = font_size.into();
31        self
32    }
33
34    pub fn align(mut self, align: TextAlign) -> Self {
35        self.align = align;
36        self
37    }
38}
39
40#[derive(Default)]
41pub struct Axis {
42    x: Option<Pixels>,
43    x_label: Label,
44    show_x_axis: bool,
45    y: Option<Pixels>,
46    y_label: Label,
47    show_y_axis: bool,
48    stroke: Hsla,
49}
50
51impl Axis {
52    pub fn new() -> Self {
53        Self {
54            show_x_axis: true,
55            ..Default::default()
56        }
57    }
58
59    /// Set the x-axis of the Axis.
60    pub fn x(mut self, x: impl Into<Pixels>) -> Self {
61        self.x = Some(x.into());
62        self
63    }
64
65    /// Hide the x-axis of the Axis.
66    pub fn hide_x_axis(mut self) -> Self {
67        self.show_x_axis = false;
68        self
69    }
70
71    /// Set the x-label of the Axis.
72    pub fn x_label(mut self, label: impl IntoIterator<Item = AxisText>) -> Self {
73        if let Some(x) = self.x {
74            self.x_label = label
75                .into_iter()
76                .map(|t| Text {
77                    text: t.text,
78                    origin: point(t.tick, x + px(TEXT_GAP * 3.)),
79                    color: t.color,
80                    font_size: t.font_size,
81                    font_weight: FontWeight::NORMAL,
82                    align: t.align,
83                })
84                .into();
85        }
86        self
87    }
88
89    /// Set the y-axis of the Axis.
90    pub fn y(mut self, y: impl Into<Pixels>) -> Self {
91        self.y = Some(y.into());
92        self
93    }
94
95    /// Hide the y-axis of the Axis.
96    pub fn hide_y_axis(mut self) -> Self {
97        self.show_y_axis = false;
98        self
99    }
100
101    /// Set the y-label of the Axis.
102    pub fn y_label(mut self, label: impl IntoIterator<Item = AxisText>) -> Self {
103        if let Some(y) = self.y {
104            self.y_label = label
105                .into_iter()
106                .map(|t| Text {
107                    text: t.text,
108                    origin: point(y + px(TEXT_GAP), t.tick),
109                    color: t.color,
110                    font_size: t.font_size,
111                    font_weight: FontWeight::NORMAL,
112                    align: t.align,
113                })
114                .into();
115        }
116        self
117    }
118
119    /// Set the stroke color of the Axis.
120    pub fn stroke(mut self, stroke: impl Into<Hsla>) -> Self {
121        self.stroke = stroke.into();
122        self
123    }
124
125    fn draw_axis(&self, start_point: Point<Pixels>, end_point: Point<Pixels>, window: &mut Window) {
126        let mut builder = PathBuilder::stroke(px(1.));
127        builder.move_to(start_point);
128        builder.line_to(end_point);
129        if let Ok(path) = builder.build() {
130            window.paint_path(path, self.stroke);
131        }
132    }
133
134    /// Paint the Axis.
135    pub fn paint(&self, bounds: &Bounds<Pixels>, window: &mut Window, cx: &mut App) {
136        let origin = bounds.origin;
137
138        // X axis
139        if let Some(x) = self.x {
140            if self.show_x_axis {
141                self.draw_axis(
142                    origin_point(px(0.), x, origin),
143                    origin_point(bounds.size.width, x, origin),
144                    window,
145                );
146            }
147        }
148        self.x_label.paint(bounds, window, cx);
149
150        // Y axis
151        if let Some(y) = self.y {
152            if self.show_y_axis {
153                self.draw_axis(
154                    origin_point(y, px(0.), origin),
155                    origin_point(y, bounds.size.height, origin),
156                    window,
157                );
158            }
159        }
160        self.y_label.paint(bounds, window, cx);
161    }
162}