Skip to main content

gpui_component/plot/
mod.rs

1mod axis;
2mod grid;
3pub mod label;
4pub mod scale;
5pub mod shape;
6pub mod tooltip;
7
8pub use gpui_component_macros::IntoPlot;
9
10use std::{fmt::Debug, ops::Add};
11
12use gpui::{
13    AnyElement, App, Bounds, ElementId, IntoElement, Path, PathBuilder, Pixels, Point, Window,
14    point, px,
15};
16
17pub use axis::{AXIS_GAP, AxisLabelSide, AxisText, PlotAxis};
18pub use grid::Grid;
19pub use label::PlotLabel;
20
21use tooltip::TooltipState;
22
23pub trait Plot: IntoElement {
24    /// Lay out and place the child elements this plot hosts (e.g. element labels).
25    ///
26    /// Called during the element's prepaint phase, so implementations may use
27    /// [`AnyElement::layout_as_root`] / [`AnyElement::prepaint_at`] to measure and
28    /// position children — neither is legal from [`Plot::paint`]. The returned
29    /// elements are painted right after `paint`, below the tooltip overlay.
30    ///
31    /// Runs before [`Plot::tooltip_state`] and [`Plot::tooltip`], so anything
32    /// resolved here can be reused by them.
33    ///
34    /// The default returns no children.
35    fn prepaint(
36        &mut self,
37        _bounds: Bounds<Pixels>,
38        _window: &mut Window,
39        _cx: &mut App,
40    ) -> Vec<AnyElement> {
41        vec![]
42    }
43
44    fn paint(&mut self, bounds: Bounds<Pixels>, window: &mut Window, cx: &mut App);
45
46    /// A stable element id that enables interactive tooltip support for this plot.
47    ///
48    /// Return `Some(id)` to opt in to tooltips; the id must be unique among sibling
49    /// elements. Returning `None` (the default) disables all tooltip behavior, leaving
50    /// the plot a pure, non-interactive element identical to the pre-tooltip behavior.
51    fn id(&self) -> Option<ElementId> {
52        None
53    }
54
55    /// Map the cursor to the tooltip state to display.
56    ///
57    /// `position` is the cursor position relative to the plot's top-left origin (already
58    /// origin-subtracted), and `bounds` is the painted area. Return the [`TooltipState`]
59    /// to display (highlighted index, crosshair point, dots, side), or `None` to show
60    /// nothing. Only called while the cursor is inside `bounds`.
61    ///
62    /// The default returns `None`.
63    fn tooltip_state(
64        &self,
65        _position: Point<Pixels>,
66        _bounds: Bounds<Pixels>,
67        _cx: &App,
68    ) -> Option<TooltipState> {
69        None
70    }
71
72    /// Render the tooltip overlay for the active [`TooltipState`].
73    ///
74    /// `cursor` is the live cursor position (relative to the plot origin) and `bounds` is the
75    /// plot's painted area, so the tooltip box can follow the cursor (pass `cursor` and
76    /// `bounds.size` to [`tooltip::Tooltip::new`]). Return the overlay element; it is painted
77    /// absolutely positioned above the plot graphics but below sibling content drawn after
78    /// the plot ([`tooltip::Tooltip`] defers its box to paint above everything). The default
79    /// returns `None`.
80    fn tooltip(
81        &self,
82        _state: &TooltipState,
83        _cursor: Point<Pixels>,
84        _bounds: Bounds<Pixels>,
85        _window: &mut Window,
86        _cx: &mut App,
87    ) -> Option<AnyElement> {
88        None
89    }
90}
91
92#[derive(Clone, Copy, Default)]
93pub enum StrokeStyle {
94    #[default]
95    Natural,
96    Linear,
97    StepAfter,
98}
99
100pub fn origin_point<T>(x: T, y: T, origin: Point<T>) -> Point<T>
101where
102    T: Default + Clone + Debug + PartialEq + Add<Output = T>,
103{
104    point(x, y) + origin
105}
106
107pub fn polygon<T>(points: &[Point<T>], bounds: &Bounds<Pixels>) -> Option<Path<Pixels>>
108where
109    T: Default + Clone + Copy + Debug + Into<f32> + PartialEq,
110{
111    let mut path = PathBuilder::stroke(px(1.));
112    let points = &points
113        .iter()
114        .map(|p| {
115            point(
116                px(p.x.into() + bounds.origin.x.as_f32()),
117                px(p.y.into() + bounds.origin.y.as_f32()),
118            )
119        })
120        .collect::<Vec<_>>();
121    path.add_polygon(points, false);
122    path.build().ok()
123}