//! `TrendChart` widget: a self-contained line or candlestick chart drawn //! with axis-aligned quads. //! //! This file carries the `script_mod!` DSL and is excluded from coverage //! (like `grid.rs`): all of the geometry lives in `chart.rs`, and this is a //! thin loop that colours and draws those rectangles. use makepad_widgets::*; use crate::chart::{self, Candle, PlotRect}; #[derive(Script, ScriptHook, Widget)] pub struct TrendChart { #[uid] uid: WidgetUid, #[source] source: ScriptObjectRef, #[walk] walk: Walk, #[layout] layout: Layout, #[redraw] #[live] draw_bg: DrawColor, #[live] draw_grid: DrawColor, #[live] draw_line: DrawColor, #[live] draw_candle: DrawColor, #[live] pub color_bg: Vec4f, #[live] pub color_grid: Vec4f, #[live] pub color_line: Vec4f, #[live] pub color_up: Vec4f, #[live] pub color_down: Vec4f, #[rust] series: Vec, #[rust] candles: Vec, } impl TrendChart { pub fn set_series(&mut self, values: &[f64]) { self.series.clear(); self.series.extend_from_slice(values); self.candles.clear(); } pub fn set_candles(&mut self, candles: Vec) { self.candles = candles; self.series.clear(); } } impl Widget for TrendChart { fn handle_event(&mut self, _cx: &mut Cx, _event: &Event, _scope: &mut Scope) {} fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep { let rect = cx.walk_turtle(walk); self.draw_bg.color = self.color_bg; self.draw_bg.draw_abs(cx, rect); if rect.size.x < 40.0 || rect.size.y < 20.0 { return DrawStep::done(); } let plot = PlotRect { x: rect.pos.x + 6.0, y: rect.pos.y + 4.0, width: (rect.size.x - 12.0).max(1.0), height: (rect.size.y - 8.0).max(1.0), }; // Faint horizontal gridlines at 25/50/75%. self.draw_grid.color = self.color_grid; for k in 1..4 { let gy = plot.y + plot.height * k as f64 / 4.0; self.draw_grid.draw_abs( cx, Rect { pos: DVec2 { x: plot.x, y: gy }, size: DVec2 { x: plot.width, y: 1.0, }, }, ); } if !self.candles.is_empty() { let shapes = chart::candle_geometry(&self.candles, plot); for s in &shapes { self.draw_candle.color = if s.up { self.color_up } else { self.color_down }; let (wx, wy, ww, wh) = s.wick; self.draw_candle.draw_abs( cx, Rect { pos: DVec2 { x: wx, y: wy }, size: DVec2 { x: ww, y: wh }, }, ); let (bx, by, bw, bh) = s.body; self.draw_candle.draw_abs( cx, Rect { pos: DVec2 { x: bx, y: by }, size: DVec2 { x: bw, y: bh }, }, ); } } else { self.draw_line.color = self.color_line; for &(x, y, w, h) in &chart::line_strips(&self.series, plot) { self.draw_line.draw_abs( cx, Rect { pos: DVec2 { x, y }, size: DVec2 { x: w, y: h }, }, ); } } DrawStep::done() } } // The script_mod! macro generates a `script_mod` function. // We wrap it in `pub fn` so `lib.rs` can call it. script_mod! { use mod.prelude.widgets.* mod.widgets.TrendChart = #(TrendChart::register_widget(vm)) { width: Fill, height: Fill color_bg: #x10101f, color_grid: #x232338 color_line: #x4fc3f7, color_up: #x81c995, color_down: #xef6a6a draw_bg +: { draw_depth: 0.0 } draw_grid +: { draw_depth: 0.1 } draw_line +: { draw_depth: 0.2 } draw_candle +: { draw_depth: 0.2 } } }