gpui_component/plot/
axis.rs1use 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 pub fn x(mut self, x: impl Into<Pixels>) -> Self {
61 self.x = Some(x.into());
62 self
63 }
64
65 pub fn hide_x_axis(mut self) -> Self {
67 self.show_x_axis = false;
68 self
69 }
70
71 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 pub fn y(mut self, y: impl Into<Pixels>) -> Self {
91 self.y = Some(y.into());
92 self
93 }
94
95 pub fn hide_y_axis(mut self) -> Self {
97 self.show_y_axis = false;
98 self
99 }
100
101 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 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 pub fn paint(&self, bounds: &Bounds<Pixels>, window: &mut Window, cx: &mut App) {
136 let origin = bounds.origin;
137
138 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 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}