mod controllers_embedded;
mod layout;
mod liveplot_app;
mod panel_helpers;
mod run;
mod update;
pub use liveplot_app::LivePlotApp;
pub use run::run_liveplot;
use std::cell::RefCell;
use std::collections::HashMap;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::atomic::{AtomicU64, Ordering};
use eframe::egui;
use crate::config::ScopeButton;
use crate::controllers::{
FFTController, LiveplotController, ScopesController, ThresholdController, TracesController,
UiActionController, WindowController,
};
use crate::data::data::LivePlotRequests;
use crate::data::hotkeys::Hotkeys;
use crate::data::traces::TracesCollection;
use crate::events::EventController;
use crate::panels::liveplot_ui::LiveplotPanel;
use crate::panels::panel_trait::Panel;
use crate::PlotCommand;
#[cfg(feature = "fft")]
use crate::panels::fft_ui::FftPanel;
use crate::panels::{
color_scheme_ui::ColorSchemePanel, export_ui::ExportPanel, hotkeys_ui::HotkeysPanel,
math_ui::MathPanel, measurment_ui::MeasurementPanel, thresholds_ui::ThresholdsPanel,
traces_ui::TracesPanel, triggers_ui::TriggersPanel,
};
static PANEL_ID_COUNTER: AtomicU64 = AtomicU64::new(0);
pub(crate) struct EffectiveLayout {
pub top_bar_buttons: Vec<ScopeButton>,
pub sidebar_buttons: Vec<ScopeButton>,
pub show_top_bar: bool,
pub show_sidebar_panels: bool,
}
#[derive(Clone, Debug)]
pub(crate) struct ScreenshotCropTarget {
pub scope_id: usize,
pub scope_name: String,
pub rect: [f32; 4],
}
#[derive(Clone, Debug)]
pub(crate) struct PendingScreenshotCapture {
pub targets: Vec<ScreenshotCropTarget>,
pub path: Option<PathBuf>,
pub pixels_per_point: f32,
pub content_origin: [f32; 2],
}
pub struct LivePlotPanel {
pub traces_data: TracesCollection,
pub overlays: Option<
Box<
dyn for<'a> FnMut(
&mut egui_plot::PlotUi,
&crate::data::scope::ScopeData,
&crate::data::traces::TracesCollection,
) + 'static,
>,
>,
pub hotkeys: Rc<RefCell<Hotkeys>>,
pub liveplot_panel: LiveplotPanel,
pub right_side_panels: Vec<Box<dyn Panel>>,
pub left_side_panels: Vec<Box<dyn Panel>>,
pub bottom_panels: Vec<Box<dyn Panel>>,
pub detached_panels: Vec<Box<dyn Panel>>,
pub empty_panels: Vec<Box<dyn Panel>>,
pub(crate) window_ctrl: Option<WindowController>,
pub(crate) ui_ctrl: Option<UiActionController>,
pub(crate) traces_ctrl: Option<TracesController>,
pub(crate) scopes_ctrl: Option<ScopesController>,
pub(crate) liveplot_ctrl: Option<LiveplotController>,
pub(crate) fft_ctrl: Option<FFTController>,
pub(crate) threshold_ctrl: Option<ThresholdController>,
pub(crate) event_ctrl: Option<EventController>,
pub(crate) last_frame_paused: bool,
pub(crate) suppress_next_pause_emit: bool,
pub(crate) pending_explicit_pause: Option<bool>,
pub(crate) pending_view_change: Option<crate::events::ViewChangeMeta>,
pub(crate) suppress_next_view_change_emit: bool,
pub(crate) threshold_event_cursors: HashMap<String, usize>,
pub pending_requests: LivePlotRequests,
pub(crate) pending_screenshot_capture: Option<PendingScreenshotCapture>,
pub top_bar_buttons: Option<Vec<ScopeButton>>,
pub sidebar_buttons: Option<Vec<ScopeButton>>,
pub min_height_for_top_bar: f32,
pub min_width_for_sidebar: f32,
pub min_height_for_sidebar: f32,
pub(crate) last_plot_size: egui::Vec2,
pub(crate) last_widget_rect: [f32; 4],
pub(crate) panel_id: u64,
pub compact: bool,
pub traces_dirty: bool,
pub undo_stack: crate::undo::LivePlotUndoStack,
pub(crate) suppress_undo: bool,
pub(crate) pending_undo: bool,
pub(crate) pending_redo: bool,
pub show_undo_redo_buttons: bool,
pub(crate) last_settings_json: Option<String>,
pub(crate) side_panels_changed: bool,
}
impl LivePlotPanel {
pub fn new(rx: std::sync::mpsc::Receiver<PlotCommand>) -> Self {
let hotkeys = Rc::new(RefCell::new(Hotkeys::default()));
Self {
traces_data: TracesCollection::new(rx),
overlays: None,
hotkeys: hotkeys.clone(),
liveplot_panel: LiveplotPanel::default(),
right_side_panels: vec![
Box::new(TracesPanel::default()),
Box::new(MathPanel::default()),
Box::new(HotkeysPanel::new(hotkeys.clone())),
Box::new(ThresholdsPanel::default()),
Box::new(TriggersPanel::default()),
Box::new(MeasurementPanel::default()),
Box::new(ColorSchemePanel::default()),
],
left_side_panels: vec![],
#[cfg(feature = "fft")]
bottom_panels: vec![Box::new(FftPanel::default())],
#[cfg(not(feature = "fft"))]
bottom_panels: vec![],
detached_panels: vec![],
empty_panels: vec![Box::new(ExportPanel::default())],
window_ctrl: None,
ui_ctrl: None,
traces_ctrl: None,
scopes_ctrl: None,
liveplot_ctrl: None,
fft_ctrl: None,
threshold_ctrl: None,
event_ctrl: None,
last_frame_paused: false,
suppress_next_pause_emit: false,
pending_explicit_pause: None,
pending_view_change: None,
suppress_next_view_change_emit: false,
threshold_event_cursors: HashMap::new(),
pending_requests: LivePlotRequests::default(),
pending_screenshot_capture: None,
top_bar_buttons: None,
sidebar_buttons: None,
min_height_for_top_bar: 200.0,
min_width_for_sidebar: 550.0,
min_height_for_sidebar: 280.0,
last_widget_rect: [0.0, 0.0, 0.0, 0.0],
last_plot_size: egui::Vec2::new(10_000.0, 10_000.0),
panel_id: PANEL_ID_COUNTER.fetch_add(1, Ordering::Relaxed),
compact: false,
traces_dirty: false,
undo_stack: crate::undo::LivePlotUndoStack::new(),
suppress_undo: false,
pending_undo: false,
pending_redo: false,
show_undo_redo_buttons: true,
last_settings_json: None,
side_panels_changed: false,
}
}
pub fn set_controllers(
&mut self,
window_ctrl: Option<WindowController>,
ui_ctrl: Option<UiActionController>,
traces_ctrl: Option<TracesController>,
scopes_ctrl: Option<ScopesController>,
liveplot_ctrl: Option<LiveplotController>,
fft_ctrl: Option<FFTController>,
threshold_ctrl: Option<ThresholdController>,
) {
self.window_ctrl = window_ctrl;
self.ui_ctrl = ui_ctrl;
self.traces_ctrl = traces_ctrl;
self.scopes_ctrl = scopes_ctrl;
self.liveplot_ctrl = liveplot_ctrl;
self.fft_ctrl = fft_ctrl;
self.threshold_ctrl = threshold_ctrl;
}
pub fn set_event_controller(&mut self, event_ctrl: Option<EventController>) {
self.event_ctrl = event_ctrl;
}
pub fn pause_all(&mut self) {
self.suppress_next_pause_emit = true;
for scope in self.liveplot_panel.get_data_mut() {
scope.paused = true;
}
self.traces_data.take_snapshot();
}
pub fn resume_all(&mut self) {
self.suppress_next_pause_emit = true;
for scope in self.liveplot_panel.get_data_mut() {
scope.paused = false;
}
self.traces_data.clear_snapshot();
}
pub fn pause_state_changed(&mut self) -> Option<bool> {
let currently_paused = self.liveplot_panel.get_data_mut().iter().all(|s| s.paused)
&& self.traces_data.has_snapshot();
if self.suppress_next_pause_emit {
self.suppress_next_pause_emit = false;
self.last_frame_paused = currently_paused;
return None;
}
if currently_paused != self.last_frame_paused {
self.last_frame_paused = currently_paused;
Some(currently_paused)
} else {
None
}
}
pub fn trigger_pause_all(&mut self) {
self.pending_explicit_pause = Some(true);
self.pause_all();
}
pub fn trigger_resume_all(&mut self) {
self.pending_explicit_pause = Some(false);
self.resume_all();
}
pub fn trigger_toggle_pause(&mut self) {
let currently_paused = self.liveplot_panel.get_data().iter().all(|s| s.paused)
&& self.traces_data.has_snapshot();
if currently_paused {
self.trigger_resume_all();
} else {
self.trigger_pause_all();
}
}
pub fn take_explicit_pause(&mut self) -> Option<bool> {
self.pending_explicit_pause.take()
}
pub fn take_side_panels_changed(&mut self) -> bool {
std::mem::take(&mut self.side_panels_changed)
}
pub fn take_view_change(&mut self) -> Option<crate::events::ViewChangeMeta> {
if self.suppress_next_view_change_emit {
self.suppress_next_view_change_emit = false;
self.pending_view_change.take();
return None;
}
self.pending_view_change.take()
}
pub fn set_x_range(&mut self, x_range: (f64, f64)) {
self.suppress_next_view_change_emit = true;
for scope in self.liveplot_panel.get_data_mut() {
if scope.scope_type == crate::data::scope::ScopeType::TimeScope {
scope.x_axis.bounds = x_range;
scope.time_window = x_range.1 - x_range.0;
}
}
}
}