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}