⎇ 97c09bc96a9c41ade9d40ab3c3613e35909a9f12
crates/modules/ui_engine/widgets/lapaloma-cartesian/src/element.rs
History
Who touched what
raw
feat(lapaloma): backend GPUI + LapalomaChartElement + app demo
1//! `LapalomaChartElement` — el `Element` GPUI que envuelve el
2//! pipeline cartesian.
3//!
4//! Owns un `DataBuffer` y un `ChartViewport`. En `paint()` arma el
5//! `WindowCanvas` adapter de `lapaloma-render` y delega a una
6//! [`LineSeries`]. El resultado: una sola `stroke_polyline` =
7//! una sola `paint_path` de GPUI = un solo draw call.
8//!
9//! Sin event handlers todavía — pan/zoom interactivos van en una
10//! fase posterior cuando enganchemos los gesture handlers de GPUI.
11
12use std::panic;
13
14use gpui::{
15 App, Bounds, Element, ElementId, GlobalElementId, InspectorElementId, IntoElement, LayoutId,
16 Pixels, Style, Window,
17};
18
19use lapaloma_core::buffer::DataBuffer;
20use lapaloma_render::{Canvas, Color, Rect, StrokeStyle, WindowCanvas};
21
22use crate::coord_system::CoordinateSystem;
23use crate::series::{LineSeries, PaintCtx, RenderMode, Series};
24use crate::viewport::ChartViewport;
25
26/// Chart cartesiano de una sola serie. Para múltiples series va a
27/// venir un `LapalomaChart` que componga varios `Series` boxed —
28/// por ahora arrancamos con el caso mono-serie.
29pub struct LapalomaChartElement {
30 pub data: DataBuffer,
31 pub viewport: ChartViewport,
32 pub stroke: StrokeStyle,
33 /// Color de fondo del plot. `None` = transparente, hereda
34 /// el container.
35 pub background: Option<Color>,
36 /// Padding interior del plot (deja espacio para futuros ejes).
37 pub padding: f32,
38 /// Scratch buffer reusable entre frames. Sin Arc/Mutex porque
39 /// el Element se mueve al árbol y no se comparte.
40 scratch: Vec<f32>,
41}
42
43impl LapalomaChartElement {
44 pub fn new(data: DataBuffer, viewport: ChartViewport, stroke: StrokeStyle) -> Self {
45 Self {
46 data,
47 viewport,
48 stroke,
49 background: None,
50 padding: 8.0,
51 scratch: Vec::new(),
52 }
53 }
54
55 pub fn background(mut self, color: Color) -> Self {
56 self.background = Some(color);
57 self
58 }
59
60 pub fn padding(mut self, px: f32) -> Self {
61 self.padding = px;
62 self
63 }
64}
65
66impl IntoElement for LapalomaChartElement {
67 type Element = Self;
68 fn into_element(self) -> Self::Element {
69 self
70 }
71}
72
73impl Element for LapalomaChartElement {
74 type RequestLayoutState = ();
75 type PrepaintState = ();
76
77 fn id(&self) -> Option<ElementId> {
78 None
79 }
80
81 fn source_location(&self) -> Option<&'static panic::Location<'static>> {
82 None
83 }
84
85 fn request_layout(
86 &mut self,
87 _id: Option<&GlobalElementId>,
88 _inspector_id: Option<&InspectorElementId>,
89 window: &mut Window,
90 cx: &mut App,
91 ) -> (LayoutId, Self::RequestLayoutState) {
92 // Layout default: ocupa lo que su parent le dé via
93 // size_full(). El usuario arma el sizing afuera con
94 // div().w_full().h(px(N)) o equivalente.
95 let mut style = Style::default();
96 style.size.width = gpui::Length::Definite(gpui::DefiniteLength::Fraction(1.0));
97 style.size.height = gpui::Length::Definite(gpui::DefiniteLength::Fraction(1.0));
98 let id = window.request_layout(style, [], cx);
99 (id, ())
100 }
101
102 fn prepaint(
103 &mut self,
104 _id: Option<&GlobalElementId>,
105 _inspector_id: Option<&InspectorElementId>,
106 _bounds: Bounds<Pixels>,
107 _request_layout: &mut Self::RequestLayoutState,
108 _window: &mut Window,
109 _cx: &mut App,
110 ) -> Self::PrepaintState {
111 }
112
113 fn paint(
114 &mut self,
115 _id: Option<&GlobalElementId>,
116 _inspector_id: Option<&InspectorElementId>,
117 bounds: Bounds<Pixels>,
118 _request_layout: &mut Self::RequestLayoutState,
119 _prepaint: &mut Self::PrepaintState,
120 window: &mut Window,
121 _cx: &mut App,
122 ) {
123 let ox: f32 = bounds.origin.x.into();
124 let oy: f32 = bounds.origin.y.into();
125 let w: f32 = bounds.size.width.into();
126 let h: f32 = bounds.size.height.into();
127 let plot = Rect::new(
128 ox + self.padding,
129 oy + self.padding,
130 (w - self.padding * 2.0).max(1.0),
131 (h - self.padding * 2.0).max(1.0),
132 );
133
134 let cs = CoordinateSystem::new(self.viewport, plot);
135 let mut canvas = WindowCanvas::new(window);
136
137 if let Some(bg) = self.background {
138 canvas.fill_rect(Rect::new(ox, oy, w, h), bg);
139 }
140
141 let series = LineSeries::new(&self.data, self.stroke);
142 self.scratch.clear();
143 let mut ctx = PaintCtx {
144 cs,
145 mode: RenderMode::UiRich,
146 scratch: &mut self.scratch,
147 };
148 series.paint(&mut ctx, &mut canvas);
149 }
150}
151
152/// Helper builder-style para uso ergonómico desde `Render::render`.
153///
154/// ```ignore
155/// div().w_full().h(px(300.)).child(
156/// lapaloma_chart(buf, viewport, stroke).background(rgb(0xff000000))
157/// )
158/// ```
159pub fn lapaloma_chart(
160 data: DataBuffer,
161 viewport: ChartViewport,
162 stroke: StrokeStyle,
163) -> LapalomaChartElement {
164 LapalomaChartElement::new(data, viewport, stroke)
165}