Skip to main content

egui_plot/items/
text.rs

1use std::ops::RangeInclusive;
2
3use egui::Color32;
4use egui::Id;
5use egui::Shape;
6use egui::Stroke;
7use egui::TextStyle;
8use egui::Ui;
9use egui::WidgetText;
10use egui::epaint::TextShape;
11use emath::Align2;
12
13use crate::axis::PlotTransform;
14use crate::bounds::PlotBounds;
15use crate::bounds::PlotPoint;
16use crate::items::PlotGeometry;
17use crate::items::PlotItem;
18use crate::items::PlotItemBase;
19
20impl Text {
21    pub fn new(name: impl Into<String>, position: PlotPoint, text: impl Into<WidgetText>) -> Self {
22        Self {
23            base: PlotItemBase::new(name.into()),
24            text: text.into(),
25            position,
26            color: Color32::TRANSPARENT,
27            anchor: Align2::CENTER_CENTER,
28        }
29    }
30
31    /// Text color.
32    #[inline]
33    pub fn color(mut self, color: impl Into<Color32>) -> Self {
34        self.color = color.into();
35        self
36    }
37
38    /// Anchor position of the text. Default is `Align2::CENTER_CENTER`.
39    #[inline]
40    pub fn anchor(mut self, anchor: Align2) -> Self {
41        self.anchor = anchor;
42        self
43    }
44
45    /// Name of this plot item.
46    ///
47    /// This name will show up in the plot legend, if legends are turned on.
48    ///
49    /// Setting the name via this method does not change the item's id, so you
50    /// can use it to change the name dynamically between frames without
51    /// losing the item's state. You should make sure the name passed to
52    /// [`Self::new`] is unique and stable for each item, or set unique and
53    /// stable ids explicitly via [`Self::id`].
54    #[expect(clippy::needless_pass_by_value, reason = "to allow various string types")]
55    #[inline]
56    pub fn name(mut self, name: impl ToString) -> Self {
57        self.base_mut().name = name.to_string();
58        self
59    }
60
61    /// Highlight this plot item, typically by scaling it up.
62    ///
63    /// If false, the item may still be highlighted via user interaction.
64    #[inline]
65    pub fn highlight(mut self, highlight: bool) -> Self {
66        self.base_mut().highlight = highlight;
67        self
68    }
69
70    /// Allowed hovering this item in the plot. Default: `true`.
71    #[inline]
72    pub fn allow_hover(mut self, hovering: bool) -> Self {
73        self.base_mut().allow_hover = hovering;
74        self
75    }
76
77    /// Sets the id of this plot item.
78    ///
79    /// By default the id is determined from the name passed to [`Self::new`],
80    /// but it can be explicitly set to a different value.
81    #[inline]
82    pub fn id(mut self, id: impl Into<Id>) -> Self {
83        self.base_mut().id = id.into();
84        self
85    }
86}
87
88impl PlotItem for Text {
89    fn shapes(&self, ui: &Ui, transform: &PlotTransform, shapes: &mut Vec<Shape>) {
90        let color = if self.color == Color32::TRANSPARENT {
91            ui.style().visuals.text_color()
92        } else {
93            self.color
94        };
95
96        let galley =
97            self.text
98                .clone()
99                .into_galley(ui, Some(egui::TextWrapMode::Extend), f32::INFINITY, TextStyle::Small);
100
101        let pos = transform.position_from_point(&self.position);
102        let rect = self.anchor.anchor_size(pos, galley.size());
103
104        shapes.push(TextShape::new(rect.min, galley, color).into());
105
106        if self.base.highlight {
107            shapes.push(Shape::rect_stroke(
108                rect.expand(1.0),
109                1.0,
110                Stroke::new(0.5, color),
111                egui::StrokeKind::Outside,
112            ));
113        }
114    }
115
116    fn initialize(&mut self, _x_range: RangeInclusive<f64>) {}
117
118    fn color(&self) -> Color32 {
119        self.color
120    }
121
122    fn geometry(&self) -> PlotGeometry<'_> {
123        PlotGeometry::None
124    }
125
126    fn bounds(&self) -> PlotBounds {
127        let mut bounds = PlotBounds::NOTHING;
128        bounds.extend_with(&self.position);
129        bounds
130    }
131
132    fn base(&self) -> &PlotItemBase {
133        &self.base
134    }
135
136    fn base_mut(&mut self) -> &mut PlotItemBase {
137        &mut self.base
138    }
139}
140
141/// Text inside the plot.
142#[derive(Clone)]
143pub struct Text {
144    base: PlotItemBase,
145    pub(crate) text: WidgetText,
146    pub(crate) position: PlotPoint,
147    pub(crate) color: Color32,
148    pub(crate) anchor: Align2,
149}