Skip to main content

shape_viz_core/
event.rs

1//! Core event types and simple state container for interactive charting
2//!
3//! This module intentionally keeps the API lightweight so it can be used by
4//! *any* frontend (terminal, native window, etc.) while remaining
5//! completely platform-agnostic.
6
7/// High-level user-interaction events that the chart core understands.
8#[derive(Debug, Clone)]
9pub enum ChartEvent {
10    /// Pan by screen-space pixel delta.
11    Pan { dx: f32, dy: f32 },
12    /// Zoom by factor around a screen-space centre.
13    Zoom {
14        factor: f32,
15        center_x: f32,
16        center_y: f32,
17    },
18    /// Mouse move in screen-space.
19    MouseMove { x: f32, y: f32 },
20    /// Resize the output surface.
21    Resize { width: u32, height: u32 },
22    /// Signal that data has been updated (frontend manages the actual data).
23    DataUpdated,
24}
25
26/// Minimal mutable state shared between render frames in interactive mode.
27/// The goal is *not* to be a full chart implementation – the existing `Chart`
28/// struct already handles that.  Instead this type acts as a convenient
29/// scratch-pad that front-ends can own and mutate while delegating heavy work
30/// to `Chart`.
31///
32/// Note: Data management is the responsibility of the frontend. This state
33/// only tracks interaction state (pan, zoom, etc.).
34#[derive(Clone)]
35pub struct ChartState {
36    /// Pending pan.
37    pan_dx: f32,
38    pan_dy: f32,
39    /// Pending zoom.
40    zoom_factor: f32,
41    zoom_center_x: f32,
42    zoom_center_y: f32,
43    /// Dirty flag so callers know when to re-render.
44    dirty: bool,
45}
46
47impl Default for ChartState {
48    fn default() -> Self {
49        Self::new()
50    }
51}
52
53impl ChartState {
54    pub fn new() -> Self {
55        Self {
56            pan_dx: 0.0,
57            pan_dy: 0.0,
58            zoom_factor: 1.0,
59            zoom_center_x: 0.0,
60            zoom_center_y: 0.0,
61            dirty: true,
62        }
63    }
64
65    /// Front-ends call this whenever an interaction happens.
66    pub fn handle_event(&mut self, ev: ChartEvent) {
67        match ev {
68            ChartEvent::Pan { dx, dy } => {
69                self.pan_dx += dx;
70                self.pan_dy += dy;
71                self.dirty = true;
72            }
73            ChartEvent::Zoom {
74                factor,
75                center_x,
76                center_y,
77            } => {
78                self.zoom_factor *= factor;
79                self.zoom_center_x = center_x;
80                self.zoom_center_y = center_y;
81                self.dirty = true;
82            }
83            ChartEvent::MouseMove { .. } => {
84                // For now mouse move does not mutate state but callers might
85                // still want to redraw cross-hair etc.
86                self.dirty = true;
87            }
88            ChartEvent::Resize { .. } => {
89                self.dirty = true; // External code will recreate renderer.
90            }
91            ChartEvent::DataUpdated => {
92                self.dirty = true;
93            }
94        }
95    }
96
97    /// True if something changed since the last time the flag was queried.
98    pub fn check_needs_redraw(&mut self) -> bool {
99        let d = self.dirty;
100        self.dirty = false;
101        d
102    }
103
104    /// Get accumulated pan delta and reset it.
105    pub fn take_pan(&mut self) -> (f32, f32) {
106        let pan = (self.pan_dx, self.pan_dy);
107        self.pan_dx = 0.0;
108        self.pan_dy = 0.0;
109        pan
110    }
111
112    /// Get zoom state.
113    pub fn zoom(&self) -> (f32, f32, f32) {
114        (self.zoom_factor, self.zoom_center_x, self.zoom_center_y)
115    }
116
117    /// Reset zoom factor to 1.0.
118    pub fn reset_zoom(&mut self) {
119        self.zoom_factor = 1.0;
120    }
121}