From 693d734f0285fef80730caa3c03bc3df85128d09 Mon Sep 17 00:00:00 2001 From: Hubald Verzijl Date: Wed, 20 Aug 2025 16:25:57 +0200 Subject: [PATCH] Add egui dock. --- Cargo.lock | 48 +++++++++++++++ Cargo.toml | 1 + src/app.rs | 173 +++++++++++++++++++++++++++++++---------------------- 3 files changed, 151 insertions(+), 71 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cd12ccf..1016a9e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -441,6 +441,7 @@ dependencies = [ "bioz-icd-rs", "defmt", "eframe", + "egui_dock", "egui_plot", "log", "postcard-rpc", @@ -876,6 +877,17 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +[[package]] +name = "duplicate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97af9b5f014e228b33e77d75ee0e6e87960124f0f4b16337b586a6bec91867b1" +dependencies = [ + "heck", + "proc-macro2", + "proc-macro2-diagnostics", +] + [[package]] name = "ecolor" version = "0.32.0" @@ -980,6 +992,17 @@ dependencies = [ "winit", ] +[[package]] +name = "egui_dock" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baa7ef4e24fccd35639705ba68a58a5713c19b15a2cd426c0a26901b5954882c" +dependencies = [ + "duplicate", + "egui", + "paste", +] + [[package]] name = "egui_glow" version = "0.32.0" @@ -2436,6 +2459,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "percent-encoding" version = "2.3.1" @@ -2635,6 +2664,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "version_check", + "yansi", +] + [[package]] name = "profiling" version = "1.0.17" @@ -4373,6 +4415,12 @@ version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "yoke" version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index f22e35e..d5cddc8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2024" defmt = { version = "1.0.1" } eframe = { version = "0.32.0"} egui_plot = "0.33.0" +egui_dock = "0.17.0" log = "0.4.27" simple_logger = "5.0.0" atomic_float = "1.1.0" diff --git a/src/app.rs b/src/app.rs index ead832c..8b562ff 100644 --- a/src/app.rs +++ b/src/app.rs @@ -6,12 +6,15 @@ use tokio::{sync::mpsc::{Sender}}; use eframe::egui::{self, Button, Color32, DragValue, Key, Label, Layout, Modifiers}; use egui_plot::{Corner, Legend, Line, Plot, PlotPoints, Points, PlotBounds}; +use egui_dock::tab_viewer::OnCloseResponse; +use egui_dock::{DockArea, DockState, Style}; use crate::plot::TimeSeriesPlot; use crate::signals::SingleFrequencySignal; pub struct App { + tree: DockState, run_impedancemeter_tx: Sender, pub magnitude: Arc>, pub phase: Arc>, @@ -23,9 +26,94 @@ pub struct App { pub single_frequency: u32 } +struct TabViewer { + magnitude: Arc>, + phase: Arc>, + magnitude_series: Arc>, + phase_series: Arc>, +} + +impl egui_dock::TabViewer for TabViewer { + type Tab = String; + + fn title(&mut self, tab: &mut Self::Tab) -> egui::WidgetText { + (&*tab).into() + } + + fn ui(&mut self, ui: &mut egui::Ui, tab: &mut Self::Tab) { + egui::Frame::default().inner_margin(5).show(ui, |ui| { + let available_height = ui.available_height(); + let half_height = available_height / 2.0; + + let point_pos = vec![[*self.magnitude.lock().unwrap() as f64, *self.phase.lock().unwrap() as f64]]; + + let point_pos = PlotPoints::new(point_pos); + let point_pos = Points::new("pos", point_pos) + .radius(20.0) + .color(Color32::LIGHT_RED); + + let bounds = PlotBounds::from_min_max([-1.5, -1.5], [1.5, 1.5]); + + // Use a vertical layout to stack the plots + ui.with_layout(Layout::top_down(egui::Align::Min), |ui| { + // Plot pressure + ui.allocate_ui_with_layout( + egui::vec2(ui.available_width(), half_height), + Layout::top_down(egui::Align::Min), + |ui| { + // Magnitude + let magnitude = self.magnitude_series.lock().unwrap(); + Plot::new("magnitude") + .allow_scroll(false) + .allow_drag(false) + // .center_y_axis(true) + .legend(Legend::default().position(Corner::LeftTop)) + .y_axis_label("Magnitude [Ω]") + .y_axis_min_width(2.0) + .show(ui, |plot_ui| { + plot_ui.line( + Line::new("Magnitude", magnitude.plot_values()) + .color(Color32::BLUE) + ); + }); + }, + ); + // Plot pressure + ui.allocate_ui_with_layout( + egui::vec2(ui.available_width(), half_height), + Layout::top_down(egui::Align::Min), + |ui| { + // Phase + let phase = self.phase_series.lock().unwrap(); + Plot::new("phase") + .allow_scroll(false) + .allow_drag(false) + // .center_y_axis(true) + .legend(Legend::default().position(Corner::LeftTop)) + .y_axis_label("Phase [rad]") + .y_axis_min_width(2.0) + .show(ui, |plot_ui| { + plot_ui.line( + Line::new("Phase", phase.plot_values()) + .color(Color32::RED) + ); + }); + }, + ); + }); + }); + } + + fn on_close(&mut self, _tab: &mut Self::Tab) -> OnCloseResponse { + println!("Closed tab: {_tab}"); + OnCloseResponse::Close + } +} + impl App { pub fn new(run_impedancemeter_tx: Sender) -> Self { let app = App { + tree: DockState::new(vec!["Single".to_string()]), run_impedancemeter_tx, magnitude: Arc::new(Mutex::new(0.0)), phase: Arc::new(Mutex::new(0.0)), @@ -118,77 +206,19 @@ impl eframe::App for App { }); egui::CentralPanel::default().show(ctx, |ui| { - let available_height = ui.available_height(); - let half_height = available_height / 2.0; - - let point_pos = vec![[*self.magnitude.lock().unwrap() as f64, *self.phase.lock().unwrap() as f64]]; - - let point_pos = PlotPoints::new(point_pos); - let point_pos = Points::new("pos", point_pos) - .radius(20.0) - .color(Color32::LIGHT_RED); - - let bounds = PlotBounds::from_min_max([-1.5, -1.5], [1.5, 1.5]); - - // Use a vertical layout to stack the plots - ui.with_layout(Layout::top_down(egui::Align::Min), |ui| { - // Plot pressure - ui.allocate_ui_with_layout( - egui::vec2(ui.available_width(), half_height), - Layout::top_down(egui::Align::Min), - |ui| { - // Magnitude - let magnitude = self.magnitude_series.lock().unwrap(); - Plot::new("magnitude") - .allow_scroll(false) - .allow_drag(false) - // .center_y_axis(true) - .legend(Legend::default().position(Corner::LeftTop)) - .y_axis_label("Magnitude [Ω]") - .y_axis_min_width(2.0) - .show(ui, |plot_ui| { - plot_ui.line( - Line::new("Magnitude", magnitude.plot_values()) - .color(Color32::BLUE) - ); - }); - }, - ); - // Plot pressure - ui.allocate_ui_with_layout( - egui::vec2(ui.available_width(), half_height), - Layout::top_down(egui::Align::Min), - |ui| { - // Phase - let phase = self.phase_series.lock().unwrap(); - Plot::new("phase") - .allow_scroll(false) - .allow_drag(false) - // .center_y_axis(true) - .legend(Legend::default().position(Corner::LeftTop)) - .y_axis_label("Phase [rad]") - .y_axis_min_width(2.0) - .show(ui, |plot_ui| { - plot_ui.line( - Line::new("Phase", phase.plot_values()) - .color(Color32::RED) - ); - }); - }, - ); - }); - - // Plot::new("State") - // .allow_scroll(false) - // .allow_drag(false) - // .data_aspect(1.0) - // .center_y_axis(true) - // .show(ui, |plot_ui| { - // plot_ui.points(point_pos); - // plot_ui.set_plot_bounds(bounds); - // }); + DockArea::new(&mut self.tree) + .style(Style::from_egui(ctx.style().as_ref())) + .show_leaf_close_all_buttons(false) + .show_leaf_collapse_buttons(false) + .show_close_buttons(false) + .show_inside(ui, &mut TabViewer { + magnitude: self.magnitude.clone(), + phase: self.phase.clone(), + magnitude_series: self.magnitude_series.clone(), + phase_series: self.phase_series.clone(), + }); }); - + // CMD- or control-W to close window if ctx.input(|i| i.modifiers.cmd_ctrl_matches(Modifiers::COMMAND)) && ctx.input(|i| i.key_pressed(Key::W)) @@ -215,7 +245,8 @@ impl eframe::App for App { } ctx.request_repaint(); - }} + } +} fn toggle_ui_start_stop(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {