tawasuyu

tawasuyu/brahman

Rust 582TOML 222Markdown 83JSON 198635 Definitions

⎇ 97c09bc96a9c41ade9d40ab3c3613e35909a9f12 crates/modules/ui_engine/widgets/lapaloma-cartesian/src/element.rs Rust · 165 lines · 5.0 KB History Who touched what raw

feat(lapaloma): backend GPUI + LapalomaChartElement + app demo 97c09bc96 · sergio ·

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}