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