Skip to main content

events_advanced/
events_advanced.rs

1//! Example: Advanced event listener (all event types)
2//!
3//! What it demonstrates
4//! - Subscribing to every event kind with [`EventFilter::all()`].
5//! - Printing detailed metadata for each event category.
6//! - Handling click, double-click, measurement, zoom, pan, fit-to-view,
7//!   resize, key-press, data-update, pause/resume, scope add/remove,
8//!   trace show/hide, colour change, math trace, threshold, export,
9//!   and screenshot events.
10//!
11//! How to run
12//! ```bash
13//! cargo run --example events_advanced
14//! ```
15//! Interact with the plot (click, scroll, press keys, add measurements, etc.)
16//! and watch the events stream in the terminal.
17
18use liveplot::{
19    channel_plot, run_liveplot, EventController, EventFilter, EventKind, LivePlotConfig, PlotPoint,
20};
21use std::time::{Duration, SystemTime, UNIX_EPOCH};
22
23fn main() -> eframe::Result<()> {
24    // Subscribe to ALL events.
25    let event_ctrl = EventController::new();
26    let rx = event_ctrl.subscribe(EventFilter::all());
27
28    // Background thread: pretty-print every event.
29    std::thread::spawn(move || {
30        while let Ok(evt) = rx.recv() {
31            let k = evt.kinds;
32
33            // ── Click / Double-click ──────────────────────────────────────
34            if k.contains(EventKind::CLICK) || k.contains(EventKind::DOUBLE_CLICK) {
35                let label = if k.contains(EventKind::DOUBLE_CLICK) {
36                    "DOUBLE_CLICK"
37                } else {
38                    "CLICK"
39                };
40                if let Some(c) = &evt.click {
41                    // coordinates are optional but should exist for click events
42                    let (px, py) = if let Some(p) = c.plot_pos {
43                        (p.x, p.y)
44                    } else {
45                        (f64::NAN, f64::NAN)
46                    };
47                    let (sx, sy) = if let Some(s) = c.screen_pos {
48                        (s.x, s.y)
49                    } else {
50                        (f32::NAN, f32::NAN)
51                    };
52                    println!(
53                        "[{label}] plot=({:.4},{:.4})  screen=({:.1},{:.1})  scope={}",
54                        px,
55                        py,
56                        sx,
57                        sy,
58                        c.scope_id.map_or("?".into(), |id| id.to_string()),
59                    );
60                } else {
61                    println!("[{label}]");
62                }
63            }
64
65            // ── Pause / Resume ────────────────────────────────────────────
66            if k.contains(EventKind::PAUSE) || k.contains(EventKind::RESUME) {
67                let label = if k.contains(EventKind::RESUME) {
68                    "RESUME"
69                } else {
70                    "PAUSE"
71                };
72                if let Some(p) = &evt.pause {
73                    println!("[{label}] scope={}", p.scope_id.unwrap_or(0));
74                } else {
75                    println!("[{label}]");
76                }
77            }
78
79            // ── Zoom / Pan / Fit-to-View ──────────────────────────────────
80            if k.contains(EventKind::ZOOM)
81                || k.contains(EventKind::PAN)
82                || k.contains(EventKind::FIT_TO_VIEW)
83            {
84                let label = if k.contains(EventKind::FIT_TO_VIEW) {
85                    "FIT_TO_VIEW"
86                } else if k.contains(EventKind::ZOOM) {
87                    "ZOOM"
88                } else {
89                    "PAN"
90                };
91                if let Some(v) = &evt.view_change {
92                    println!(
93                        "[{label}] x={:?}  y={:?}  scope={}",
94                        v.x_range,
95                        v.y_range,
96                        v.scope_id.map_or("?".into(), |id| id.to_string()),
97                    );
98                } else {
99                    println!("[{label}]");
100                }
101            }
102
103            // ── Measurement ───────────────────────────────────────────────
104            if k.contains(EventKind::MEASUREMENT_POINT) {
105                let complete = k.contains(EventKind::MEASUREMENT_COMPLETE);
106                if let Some(m) = &evt.measurement {
107                    println!(
108                        "[MEASUREMENT{}] name={:?} point=({:.4},{:.4}) p1={:?} p2={:?} slope={:?} dist={:?}",
109                        if complete { " COMPLETE" } else { "" },
110                        m.measurement_name,
111                        m.point[0], m.point[1],
112                        m.p1, m.p2, m.slope, m.distance,
113                    );
114                }
115            }
116            if k.contains(EventKind::MEASUREMENT_CLEARED) {
117                println!("[MEASUREMENT_CLEARED]");
118            }
119
120            // ── Resize ────────────────────────────────────────────────────
121            if k.contains(EventKind::RESIZE) {
122                if let Some(r) = &evt.resize {
123                    println!("[RESIZE] {}×{}", r.width as u32, r.height as u32);
124                }
125            }
126
127            // ── Key press ─────────────────────────────────────────────────
128            if k.contains(EventKind::KEY_PRESSED) {
129                if let Some(kp) = &evt.key_press {
130                    println!(
131                        "[KEY] {:?}  ctrl={} alt={} shift={} cmd={}",
132                        kp.key,
133                        kp.modifiers.ctrl,
134                        kp.modifiers.alt,
135                        kp.modifiers.shift,
136                        kp.modifiers.command,
137                    );
138                }
139            }
140
141            // ── Data update ───────────────────────────────────────────────
142            if k.contains(EventKind::DATA_UPDATED) {
143                if let Some(d) = &evt.data_update {
144                    println!("[DATA_UPDATED] traces={:?}", d.traces);
145                }
146            }
147
148            // ── Trace visibility / colour / offset ────────────────────────
149            if k.contains(EventKind::TRACE_SHOWN) || k.contains(EventKind::TRACE_HIDDEN) {
150                if let Some(t) = &evt.trace {
151                    println!(
152                        "[TRACE_{}] {:?} visible={:?}",
153                        if k.contains(EventKind::TRACE_SHOWN) {
154                            "SHOWN"
155                        } else {
156                            "HIDDEN"
157                        },
158                        t.trace.0,
159                        t.visible,
160                    );
161                }
162            }
163            if k.contains(EventKind::TRACE_COLOR_CHANGED) {
164                if let Some(t) = &evt.trace {
165                    println!("[TRACE_COLOR] {:?} rgb={:?}", t.trace.0, t.color_rgb);
166                }
167            }
168            if k.contains(EventKind::TRACE_OFFSET_CHANGED) {
169                if let Some(t) = &evt.trace {
170                    println!("[TRACE_OFFSET] {:?} offset={:?}", t.trace.0, t.offset);
171                }
172            }
173
174            // ── Math trace ────────────────────────────────────────────────
175            if k.contains(EventKind::MATH_TRACE_ADDED) {
176                if let Some(m) = &evt.math_trace {
177                    println!("[MATH_TRACE_ADDED] {:?} formula={:?}", m.name, m.formula);
178                }
179            }
180            if k.contains(EventKind::MATH_TRACE_REMOVED) {
181                if let Some(m) = &evt.math_trace {
182                    println!("[MATH_TRACE_REMOVED] {:?}", m.name);
183                }
184            }
185
186            // ── Threshold ─────────────────────────────────────────────────
187            if k.contains(EventKind::THRESHOLD_EXCEEDED) {
188                if let Some(t) = &evt.threshold {
189                    println!(
190                        "[THRESHOLD_EXCEEDED] {:?} trace={:?} area={:?}",
191                        t.threshold_name, t.trace, t.area,
192                    );
193                }
194            }
195            if k.contains(EventKind::THRESHOLD_REMOVED) {
196                if let Some(t) = &evt.threshold {
197                    println!("[THRESHOLD_REMOVED] {:?}", t.threshold_name);
198                }
199            }
200
201            // ── Export / Screenshot ───────────────────────────────────────
202            if k.contains(EventKind::EXPORT) || k.contains(EventKind::SCREENSHOT) {
203                let label = if k.contains(EventKind::SCREENSHOT) {
204                    "SCREENSHOT"
205                } else {
206                    "EXPORT"
207                };
208                if let Some(e) = &evt.export {
209                    println!("[{label}] format={:?} path={:?}", e.format, e.path);
210                }
211            }
212
213            // ── Scope management ──────────────────────────────────────────
214            if k.contains(EventKind::SCOPE_ADDED) {
215                if let Some(s) = &evt.scope_manage {
216                    println!("[SCOPE_ADDED] id={}", s.scope_id);
217                }
218            }
219            if k.contains(EventKind::SCOPE_REMOVED) {
220                if let Some(s) = &evt.scope_manage {
221                    println!("[SCOPE_REMOVED] id={}", s.scope_id);
222                }
223            }
224        }
225        println!("[event] channel closed");
226    });
227
228    // Set up a sine + cosine trace so there is data to interact with.
229    let (sink, data_rx) = channel_plot();
230    let t_sin = sink.create_trace("sin", Some("Sine"));
231    let t_cos = sink.create_trace("cos", Some("Cosine"));
232
233    std::thread::spawn(move || {
234        let dt = Duration::from_millis(1);
235        loop {
236            let t_s = SystemTime::now()
237                .duration_since(UNIX_EPOCH)
238                .map(|d| d.as_secs_f64())
239                .unwrap_or(0.0);
240            let _ = sink.send_point(
241                &t_sin,
242                PlotPoint {
243                    x: t_s,
244                    y: (2.0 * std::f64::consts::PI * 2.0 * t_s).sin(),
245                },
246            );
247            let _ = sink.send_point(
248                &t_cos,
249                PlotPoint {
250                    x: t_s,
251                    y: (2.0 * std::f64::consts::PI * 2.0 * t_s).cos(),
252                },
253            );
254            std::thread::sleep(dt);
255        }
256    });
257
258    let mut cfg = LivePlotConfig::default();
259    cfg.controllers.event = Some(event_ctrl);
260
261    run_liveplot(data_rx, cfg)
262}