diff --git a/libraries/egui_extras/examples/plotsperf.rs b/libraries/egui_extras/examples/plotsperf.rs index a8148c8..6c7719e 100644 --- a/libraries/egui_extras/examples/plotsperf.rs +++ b/libraries/egui_extras/examples/plotsperf.rs @@ -3,7 +3,7 @@ // use eframe::{App, CreationContext, Frame, HardwareAcceleration, Renderer}; -use egui::{CentralPanel, Context, ThemePreference, Vec2, ViewportBuilder}; +use egui::{CentralPanel, Context, ThemePreference, Ui, Vec2, ViewportBuilder}; use irox_egui_extras::logplot::{ x_axis_time_millis_formatter, y_axis_units_formatter, BasicPlot, LineWithErrorBars, PlotPoint, }; @@ -17,7 +17,7 @@ use irox_time::Duration; use irox_tools::random::PRNG; use irox_units::quantities::Units; use log::error; -use std::sync::atomic::AtomicBool; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::thread::JoinHandle; @@ -64,7 +64,7 @@ pub fn main() { }; } const NUM_LINES_PER_PLOT: usize = 16; -const NUM_PLOTS: usize = 1; +const NUM_PLOTS: usize = 3; const DATA_RATE: Duration = Duration::from_millis(10); // 100 hz data const MAX_DATA_TO_KEEP: Duration = Duration::from_minutes(5); const AVERAGING_WINDOW: Duration = Duration::from_seconds(1); @@ -82,8 +82,7 @@ pub struct PlotOpts { } impl Drop for PlotOpts { fn drop(&mut self) { - self.running - .store(false, std::sync::atomic::Ordering::Relaxed); + self.running.store(false, Ordering::Relaxed); for handle in self.handles.drain(..) { let _ = handle.join(); } @@ -107,6 +106,7 @@ impl TestApp { for _pidx in 0..NUM_PLOTS { plots.push(Self::spawn_thread(running.clone(), &cc.egui_ctx, num_lines)); num_lines /= 2; + // num_lines *= 2; } Self { plots, running } @@ -116,6 +116,9 @@ impl TestApp { let mut plot = BasicPlot::new(ctx) .with_x_axis_formatter(x_axis_time_millis_formatter()) .with_y_axis_formatter(y_axis_units_formatter(Units::Volt)); + plot.line_highlight_focus_duration = Duration::from_seconds(1); + plot.rotate_line_highlights = true; + let mut handles = Vec::new(); let mut line_off = LINE_CTR; for lidx in 0..num_lines { @@ -196,8 +199,7 @@ impl TestApp { } impl Drop for TestApp { fn drop(&mut self) { - self.running - .store(false, std::sync::atomic::Ordering::Relaxed); + self.running.store(false, Ordering::Relaxed); } } @@ -215,4 +217,19 @@ impl App for TestApp { }); } } -impl ToolApp for TestApp {} +impl ToolApp for TestApp { + fn bottom_bar(&mut self, ui: &mut Ui) { + let mut rotate_highlights = false; + for plot in &self.plots { + rotate_highlights |= plot.plot.rotate_line_highlights; + } + if ui + .checkbox(&mut rotate_highlights, "Cycle highlights") + .changed() + { + for plot in &mut self.plots { + plot.plot.rotate_line_highlights = rotate_highlights; + } + } + } +} diff --git a/libraries/egui_extras/src/logplot.rs b/libraries/egui_extras/src/logplot.rs index 6624494..a1011ef 100644 --- a/libraries/egui_extras/src/logplot.rs +++ b/libraries/egui_extras/src/logplot.rs @@ -225,6 +225,11 @@ pub struct BasicPlot { pub title: Option, last_render_size: Rect2D, + pub rotate_line_highlights: bool, + pub line_highlight_focus_duration: Duration, + pause_line_highlight_for_hover: bool, + last_line_highlight_index: usize, + last_line_highlight_time: UnixTimestamp, } macro_rules! scale_y { ($self:ident, $ax:ident,$pos:ident) => {{ @@ -638,6 +643,23 @@ impl BasicPlot { Pos2::new(x_axis_x_max, y_axis_y_max), ); + if self.rotate_line_highlights && !self.pause_line_highlight_for_hover { + let now = UnixTimestamp::now(); + if now - self.line_highlight_focus_duration > self.last_line_highlight_time { + self.last_line_highlight_time = now; + self.last_line_highlight_index += 1; + if self.last_line_highlight_index >= self.lines.len() { + self.last_line_highlight_index = 0; + } + for line in &mut self.lines { + line.is_hovered = false; + } + if let Some(line) = self.lines.get_mut(self.last_line_highlight_index) { + line.is_hovered = true; + } + } + } + let lr = rect.into(); if lr != self.last_render_size || self.any_lines_changed() { profile_scope!("update range", self.name.as_str()); @@ -815,6 +837,11 @@ impl BasicPlot { // draw the points as individual line segments let mut start_text = rect.left_bottom(); + let text_rect = Align2::LEFT_BOTTOM.anchor_size( + Pos2::new(start_text.x, start_text.y), + Vec2::new(rect.width(), 12.), + ); + let mut any_hovered = false; for line in &mut self.lines { profile_scope!("draw controls", line.name.as_str()); let visible = line.visible.load(Ordering::Relaxed); @@ -825,27 +852,38 @@ impl BasicPlot { let galley = painter.layout_no_wrap(line.name.to_string(), line.text_font.clone(), color); let used = Align2::LEFT_BOTTOM.anchor_size(start_text, galley.size()); - let mut hovered = false; if let Some(pos) = response.hover_pos() { + let mut hovered = false; + let hvr = ui.visuals().widgets.hovered; + let fill = hvr.bg_fill; + let rnd = hvr.rounding; + let strk = hvr.bg_stroke; if used.contains(pos) { - let hvr = ui.visuals().widgets.hovered; - let fill = hvr.bg_fill; - let rnd = hvr.rounding; - let strk = hvr.bg_stroke; painter.rect(used, rnd, fill, strk); hovered = true; + any_hovered = true; if response.clicked() { line.visible.swap(!visible, Ordering::Relaxed); } } - } - if line.is_hovered != hovered { - line.is_hovered = hovered; + if text_rect.contains(pos) { + any_hovered = true; + line.is_hovered = hovered; + } else if !self.rotate_line_highlights { + // clear off the automatic hovering (if left) + line.is_hovered = false; + } } painter.galley(used.min, galley, color); start_text.x += used.width() + 10.; } + self.pause_line_highlight_for_hover = any_hovered; + if !any_hovered && !self.rotate_line_highlights { + for line in &mut self.lines { + line.is_hovered = false; + } + } self.draw_cursor(ui, &mut response, &mut painter, closest_hover, &grid_bounds);