Skip to main content

rgpui_component/plot/
label.rs

1use std::fmt::Debug;
2
3use rgpui::{
4    App, Bounds, FontWeight, Hsla, Pixels, Point, ShapedLine, SharedString, TextAlign, TextRun,
5    Window, point, px,
6};
7
8use super::origin_point;
9
10pub const TEXT_SIZE: f32 = 10.;
11pub const TEXT_GAP: f32 = 2.;
12pub const TEXT_HEIGHT: f32 = TEXT_SIZE + TEXT_GAP;
13
14fn shape_label(
15    text: &SharedString,
16    font_size: Pixels,
17    color: Hsla,
18    window: &mut Window,
19) -> ShapedLine {
20    let text_run = TextRun {
21        len: text.len(),
22        font: window.text_style().font(),
23        color,
24        background_color: None,
25        underline: None,
26        strikethrough: None,
27    };
28    window
29        .text_system()
30        .shape_line(text.clone(), font_size, &[text_run], None)
31}
32
33/// Returns the rendered width of `text` at `font_size` using the window's
34/// current text style.  Used for layout calculations that need to reserve
35/// space for labels before the scale ranges are fixed.
36pub fn measure_text_width(text: &SharedString, font_size: Pixels, window: &mut Window) -> f32 {
37    if text.is_empty() {
38        return 0.;
39    }
40    shape_label(
41        text,
42        font_size,
43        Hsla {
44            h: 0.,
45            s: 0.,
46            l: 0.,
47            a: 1.,
48        },
49        window,
50    )
51    .width()
52    .as_f32()
53}
54
55pub struct Text {
56    pub text: SharedString,
57    pub origin: Point<Pixels>,
58    pub color: Hsla,
59    pub font_size: Pixels,
60    pub font_weight: FontWeight,
61    pub align: TextAlign,
62}
63
64impl Text {
65    pub fn new<T>(text: impl Into<SharedString>, origin: Point<T>, color: Hsla) -> Self
66    where
67        T: Default + Clone + Copy + Debug + PartialEq + Into<Pixels>,
68    {
69        let origin = point(origin.x.into(), origin.y.into());
70
71        Self {
72            text: text.into(),
73            origin,
74            color,
75            font_size: TEXT_SIZE.into(),
76            font_weight: FontWeight::NORMAL,
77            align: TextAlign::Left,
78        }
79    }
80
81    /// Set the font size of the Text.
82    pub fn font_size(mut self, font_size: impl Into<Pixels>) -> Self {
83        self.font_size = font_size.into();
84        self
85    }
86
87    /// Set the font weight of the Text.
88    pub fn font_weight(mut self, font_weight: FontWeight) -> Self {
89        self.font_weight = font_weight;
90        self
91    }
92
93    /// Set the alignment of the Text.
94    pub fn align(mut self, align: TextAlign) -> Self {
95        self.align = align;
96        self
97    }
98}
99
100impl<I> From<I> for PlotLabel
101where
102    I: Iterator<Item = Text>,
103{
104    fn from(items: I) -> Self {
105        Self::new(items.collect())
106    }
107}
108
109#[derive(Default)]
110pub struct PlotLabel(Vec<Text>);
111
112impl PlotLabel {
113    pub fn new(items: Vec<Text>) -> Self {
114        Self(items)
115    }
116
117    /// Paint the Label.
118    pub fn paint(&self, bounds: &Bounds<Pixels>, window: &mut Window, cx: &mut App) {
119        for Text {
120            text,
121            origin,
122            color,
123            font_size,
124            font_weight: _,
125            align,
126        } in self.0.iter()
127        {
128            let origin = origin_point(origin.x, origin.y, bounds.origin);
129
130            let line = shape_label(text, *font_size, *color, window);
131            let origin = match align {
132                TextAlign::Left => origin,
133                TextAlign::Right => origin - point(line.width(), px(0.)),
134                _ => origin - point(line.width() / 2., px(0.)),
135            };
136            let _ = line.paint(origin, *font_size, *align, None, window, cx);
137        }
138    }
139}