diff --git a/CHANGELOG.md b/CHANGELOG.md index eed489b..fedb096 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ All notable changes to the `Serial Monitor` crate will be documented in this fil ### Added: -* ... +* Up to 4 Sentences highlightments using regex +* Groups settings in the side bar by category into collapsing menu. ## 0.3.0 - 14.10.2024 - Automatic Reconnection diff --git a/src/custom_highlighter.rs b/src/custom_highlighter.rs new file mode 100644 index 0000000..581d5a7 --- /dev/null +++ b/src/custom_highlighter.rs @@ -0,0 +1,113 @@ + + +use eframe::egui::{FontFamily, FontId}; +use eframe::egui::{self, text::LayoutJob, Color32, TextFormat}; + + +extern crate regex; +use regex::Regex; +use regex::RegexSet; +const DEFAULT_FONT_ID: FontId = FontId::new(14.0, FontFamily::Monospace); + + +#[derive(Debug)] +#[derive(Clone, Copy)] +pub struct HighLightElement +{ + pos_start:usize, + pos_end:usize, + token_idx:usize +} +impl HighLightElement{ + pub fn new(pos_start: usize, pos_end:usize,token_idx:usize) -> Self{ + Self{ + pos_start, + pos_end, + token_idx + } + } +} +pub fn highlight_impl( + _ctx: &egui::Context, + text: &str, + tokens: Vec, + default_color:Color32 +) -> Option { + // Extremely simple syntax highlighter for when we compile without syntect + + + let mut my_tokens = tokens.clone(); + for token in my_tokens.clone() + { + if token.len() < 1 + { + let index = my_tokens.iter().position(|x| *x == token).unwrap(); + my_tokens.remove(index); + } + } + + let content_string = String::from(text); + // let _ = file.read_to_string(&mut isi); + let mut regexs:Vec = Vec::new(); + for sentence in my_tokens.clone() { + match Regex::new(&sentence){ + Ok(re) => { + regexs.push(re); + }, + Err(_err) =>{ + + }, + }; + } + + let mut highlight_list : Vec = Vec::::new(); + match RegexSet::new(my_tokens.clone()){ + Ok(set) => { + for idx in set.matches(&content_string).into_iter() { + for caps in regexs[idx].captures_iter(&content_string) { + highlight_list.push(HighLightElement::new( + caps.get(0).unwrap().start(), + caps.get(0).unwrap().end(), + idx)); + } + } + }, + Err(_err) => { + + } + }; + + highlight_list.sort_by_key(|item| (item.pos_start, item.pos_end)); + + + let mut job = LayoutJob::default(); + let mut previous = HighLightElement::new(0,0, 0); + for matches in highlight_list + { + if previous.pos_end >= matches.pos_start + { + continue + } + job.append(&text[previous.pos_end..(matches.pos_start)], 0.0, TextFormat::simple(DEFAULT_FONT_ID, default_color)); + if matches.token_idx == 0 + { + job.append(&text[matches.pos_start..matches.pos_end], 0.0, TextFormat::simple(DEFAULT_FONT_ID, Color32::from_rgb(255, 100, 100))); + }else if matches.token_idx == 1 + { + job.append(&text[matches.pos_start..matches.pos_end], 0.0, TextFormat::simple(DEFAULT_FONT_ID, Color32::from_rgb(225, 159, 0))); + + }else if matches.token_idx == 2 + { + job.append(&text[matches.pos_start..matches.pos_end], 0.0, TextFormat::simple(DEFAULT_FONT_ID, Color32::from_rgb(87, 165, 171))); + }else if matches.token_idx == 3 + { + job.append(&text[matches.pos_start..matches.pos_end], 0.0, TextFormat::simple(DEFAULT_FONT_ID, Color32::from_rgb(109, 147, 226))); + } + previous = matches.clone(); + } + job.append(&text[previous.pos_end..], 0.0, TextFormat::simple(DEFAULT_FONT_ID, default_color)); + + + Some(job) +} + diff --git a/src/gui.rs b/src/gui.rs index a3db7cd..a34c29c 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -8,7 +8,8 @@ use std::time::Duration; use eframe::egui::panel::Side; use eframe::egui::{ - Align2, Color32, FontFamily, FontId, KeyboardShortcut, Pos2, Sense, Vec2, Visuals, + Align2, CollapsingHeader, Color32, FontFamily, FontId, KeyboardShortcut, Pos2, Sense, Ui, Vec2, + Visuals, }; use eframe::{egui, Storage}; use egui::ThemePreference; @@ -24,6 +25,7 @@ use crate::serial::{clear_serial_settings, save_serial_settings, Device, SerialD use crate::toggle::toggle; use crate::FileOptions; use crate::{APP_INFO, PREFS_KEY}; +use crate::custom_highlighter::highlight_impl; const DEFAULT_FONT_ID: FontId = FontId::new(14.0, FontFamily::Monospace); pub const RIGHT_PANEL_WIDTH: f32 = 350.0; @@ -31,7 +33,6 @@ const BAUD_RATES: &[u32] = &[ 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400, 128000, 460800, 576000, 921600, ]; - const SAVE_FILE_SHORTCUT: KeyboardShortcut = KeyboardShortcut::new(egui::Modifiers::COMMAND, egui::Key::S); @@ -208,6 +209,7 @@ pub struct MyApp { save_raw: bool, show_warning_window: WindowFeedback, do_not_show_clear_warning: bool, + init: bool, } #[allow(clippy::too_many_arguments)] @@ -247,8 +249,8 @@ impl MyApp { plotting_range: usize::MAX, plot_serial_display_ratio: 0.45, command: "".to_string(), - show_sent_cmds: true, - show_timestamps: true, + show_sent_cmds: false, + show_timestamps: false, save_raw: false, eol: "\\r\\n".to_string(), colors: vec![COLORS[0]], @@ -259,6 +261,7 @@ impl MyApp { plot_location: None, do_not_show_clear_warning: false, show_warning_window: WindowFeedback::None, + init: false, show_color_window: ColorWindow::NoShow, } } @@ -315,15 +318,23 @@ impl MyApp { fn draw_central_panel(&mut self, ctx: &egui::Context) { egui::CentralPanel::default().show(ctx, |ui| { let left_border = 10.0; - - let panel_height = ui.available_size().y; - let height = ui.available_size().y * self.plot_serial_display_ratio; - let plots_height = height; - // need to subtract 12.0, this seems to be the height of the separator of two adjacent plots - let plot_height = - plots_height / (self.serial_devices.number_of_plots[self.device_idx] as f32) - 12.0; - let top_spacing = 5.0; + // Width let width = ui.available_size().x - 2.0 * left_border - RIGHT_PANEL_WIDTH; + // Height + let top_spacing = 5.0; + let panel_height = ui.available_size().y; + let mut plot_height:f32 = 0.0; + + if self.serial_devices.number_of_plots[self.device_idx] > 0 { + let height:f32 ; + height = ui.available_size().y * self.plot_serial_display_ratio; + plot_height = height; + // need to subtract 12.0, this seems to be the height of the separator of two adjacent plots + plot_height = + plot_height / (self.serial_devices.number_of_plots[self.device_idx] as f32) - 12.0; + } + + let mut plot_ui_heigh:f32 = 0.0; ui.add_space(top_spacing); ui.horizontal(|ui| { @@ -332,89 +343,94 @@ impl MyApp { if let Ok(read_guard) = self.data_lock.read() { self.data = read_guard.clone(); } + if self.serial_devices.number_of_plots[self.device_idx] > 0 { - if self.data.dataset.len() != self.labels.len() { - self.labels = (0..max(self.data.dataset.len(), 1)) - .map(|i| format!("Column {i}")) - .collect(); - self.colors = (0..max(self.data.dataset.len(), 1)) - .map(|i| COLORS[i % COLORS.len()]) - .collect(); - self.color_vals = - (0..max(self.data.dataset.len(), 1)).map(|_| 0.0).collect(); - } + if self.data.dataset.len() != self.labels.len() { + self.labels = (0..max(self.data.dataset.len(), 1)) + .map(|i| format!("Column {i}")) + .collect(); + self.colors = (0..max(self.data.dataset.len(), 1)) + .map(|i| COLORS[i % COLORS.len()]) + .collect(); + self.color_vals = + (0..max(self.data.dataset.len(), 1)).map(|_| 0.0).collect(); + } - let mut graphs: Vec> = vec![vec![]; self.data.dataset.len()]; - let window = self.data.dataset[0] - .len() - .saturating_sub(self.plotting_range); - - for (i, time) in self.data.time[window..].iter().enumerate() { - let x = *time as f64 / 1000.0; - for (graph, data) in graphs.iter_mut().zip(&self.data.dataset) { - if self.data.time.len() == data.len() { - if let Some(y) = data.get(i + window) { - graph.push(PlotPoint { x, y: *y as f64 }); + let mut graphs: Vec> = vec![vec![]; self.data.dataset.len()]; + let window = self.data.dataset[0] + .len() + .saturating_sub(self.plotting_range); + + for (i, time) in self.data.time[window..].iter().enumerate() { + let x = *time as f64 / 1000.0; + for (graph, data) in graphs.iter_mut().zip(&self.data.dataset) { + if self.data.time.len() == data.len() { + if let Some(y) = data.get(i + window) { + graph.push(PlotPoint { x, y: *y as f64 }); + } } } } - } - let t_fmt = - |x: GridMark, _range: &RangeInclusive| format!("{:4.2} s", x.value); + let t_fmt = + |x: GridMark, _range: &RangeInclusive| format!("{:4.2} s", x.value); - let plots_ui = ui.vertical(|ui| { - for graph_idx in 0..self.serial_devices.number_of_plots[self.device_idx] { - if graph_idx != 0 { - ui.separator(); - } + let plots_ui = ui.vertical(|ui| { + for graph_idx in 0..self.serial_devices.number_of_plots[self.device_idx] { + if graph_idx != 0 { + ui.separator(); + } - let signal_plot = Plot::new(format!("data-{graph_idx}")) - .height(plot_height) - .width(width) - .legend(Legend::default()) - .x_grid_spacer(log_grid_spacer(10)) - .y_grid_spacer(log_grid_spacer(10)) - .x_axis_formatter(t_fmt); - - let plot_inner = signal_plot.show(ui, |signal_plot_ui| { - for (i, graph) in graphs.iter().enumerate() { - // this check needs to be here for when we change devices (not very elegant) - if i < self.labels.len() { - signal_plot_ui.line( - Line::new(PlotPoints::Owned(graph.to_vec())) - .name(&self.labels[i]) - .color(self.colors[i]), - ); + let signal_plot = Plot::new(format!("data-{graph_idx}")) + .height(plot_height) + .width(width) + .legend(Legend::default()) + .x_grid_spacer(log_grid_spacer(10)) + .y_grid_spacer(log_grid_spacer(10)) + .x_axis_formatter(t_fmt); + + let plot_inner = signal_plot.show(ui, |signal_plot_ui| { + for (i, graph) in graphs.iter().enumerate() { + // this check needs to be here for when we change devices (not very elegant) + if i < self.serial_devices.labels[self.device_idx].len() { + signal_plot_ui.line( + Line::new(PlotPoints::Owned(graph.to_vec())).name( + &self.serial_devices.labels[self.device_idx][i], + ), + ); + } } - } - }); + }); - self.plot_location = Some(plot_inner.response.rect); - } - let separator_response = ui.separator(); - let separator = ui - .interact( - separator_response.rect, - separator_response.id, - Sense::click_and_drag(), - ) - .on_hover_cursor(egui::CursorIcon::ResizeVertical); - - let resize_y = separator.drag_delta().y; - - if separator.double_clicked() { - self.plot_serial_display_ratio = 0.45; - } - self.plot_serial_display_ratio = (self.plot_serial_display_ratio - + resize_y / panel_height) - .clamp(0.1, 0.9); + self.plot_location = Some(plot_inner.response.rect); + } + let separator_response = ui.separator(); + let separator = ui + .interact( + separator_response.rect, + separator_response.id, + Sense::click_and_drag(), + ) + .on_hover_cursor(egui::CursorIcon::ResizeVertical); + + let resize_y = separator.drag_delta().y; + + if separator.double_clicked() { + self.plot_serial_display_ratio = 0.45; + } + self.plot_serial_display_ratio = (self.plot_serial_display_ratio + + resize_y / panel_height) + .clamp(0.1, 0.9); - ui.add_space(top_spacing); - }); + ui.add_space(top_spacing); + }); + plot_ui_heigh = plots_ui.response.rect.height(); + } else { + plot_ui_heigh = 0.0; + } let serial_height = panel_height - - plots_ui.response.rect.height() + - plot_ui_heigh - left_border * 2.0 - top_spacing; @@ -427,6 +443,8 @@ impl MyApp { Color32::BLACK }; + let mut text_edit_size = ui.available_size(); + text_edit_size.x = width; egui::ScrollArea::vertical() .id_salt("serial_output") .auto_shrink([false; 2]) @@ -446,15 +464,25 @@ impl MyApp { } }) .collect(); + + let mut layouter = |ui: &egui::Ui, string: &str, wrap_width: f32| { + let mut layout_job = highlight_impl(ui.ctx(), + string, + self.serial_devices.highlight_labels[self.device_idx].clone(), + Color32::from_rgb(155, 164, 167)).unwrap(); + layout_job.wrap.max_width = wrap_width; + ui.fonts(|f| f.layout_job(layout_job)) + }; + ui.add( egui::TextEdit::multiline(&mut content.as_str()) .font(DEFAULT_FONT_ID) // for cursor height .lock_focus(true) .text_color(color) - .desired_width(width), + .desired_width(width) + .layouter(&mut layouter) ); }); - ui.add_space(top_spacing / 2.0); ui.horizontal(|ui| { let cmd_line = ui.add( egui::TextEdit::singleline(&mut self.command) @@ -498,420 +526,593 @@ impl MyApp { }); } - fn draw_side_panel(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { - let mut init = false; - egui::SidePanel::new(Side::Right, "settings panel") - .min_width(RIGHT_PANEL_WIDTH) - .max_width(RIGHT_PANEL_WIDTH) - .resizable(false) - //.default_width(right_panel_width) - .show(ctx, |ui| { - ui.add_enabled_ui(true, |ui| { - ui.horizontal(|ui| { - ui.heading("Serial Monitor"); - self.paint_connection_indicator(ui); - }); + fn draw_serial_settings(&mut self, ctx: &egui::Context, ui :&mut Ui) + { + ui.horizontal(|ui| { + ui.heading("Serial Monitor"); + self.paint_connection_indicator(ui); + }); - let devices: Vec = if let Ok(read_guard) = self.devices_lock.read() { - read_guard.clone() - } else { - vec![] - }; + let devices: Vec = if let Ok(read_guard) = self.devices_lock.read() { + read_guard.clone() + } else { + vec![] + }; + + if !devices.contains(&self.device) { + self.device.clear(); + } + if let Ok(dev) = self.device_lock.read() { + if !dev.name.is_empty() { + self.device = dev.name.clone(); + } + } + ui.add_space(10.0); + ui.horizontal(|ui| { + ui.label("Device"); + ui.add_space(130.0); + ui.label("Baud"); + }); - if !devices.contains(&self.device) { - self.device.clear(); + let old_name = self.device.clone(); + ui.horizontal(|ui| { + let dev_text = self.device.replace("/dev/tty.", ""); + ui.horizontal(|ui| { + if self.connected_to_device { + ui.disable(); + } + let _response = egui::ComboBox::from_id_salt("Device") + .selected_text(dev_text) + .width(RIGHT_PANEL_WIDTH * 0.92 - 155.0) + .show_ui(ui, |ui| { + devices + .into_iter() + // on macOS each device appears as /dev/tty.* and /dev/cu.* + // we only display the /dev/tty.* here + .filter(|dev| !dev.contains("/dev/cu.")) + .for_each(|dev| { + // this makes the names shorter in the UI on UNIX and UNIX-like platforms + let dev_text = dev.replace("/dev/tty.", ""); + ui.selectable_value(&mut self.device, dev, dev_text); + }); + }).response; + // let selected_new_device = response.changed(); //somehow this does not work + // if selected_new_device { + if old_name != self.device { + if !self.data.time.is_empty() { + self.show_warning_window = WindowFeedback::Waiting; + self.old_device = old_name; + } else { + self.show_warning_window = WindowFeedback::Clear; } - if let Ok(dev) = self.device_lock.read() { - if !dev.name.is_empty() { + } + }); + match self.show_warning_window { + WindowFeedback::None => {} + WindowFeedback::Waiting => { + self.show_warning_window = self.clear_warning_window(ctx); + } + WindowFeedback::Clear => { + // new device selected, check in previously used devices + let mut device_is_already_saved = false; + for (idx, dev) in self.serial_devices.devices.iter().enumerate() { + if dev.name == self.device { + // this is the device! self.device = dev.name.clone(); + self.device_idx = idx; + self.init = true; + device_is_already_saved = true; } } - ui.add_space(10.0); - ui.horizontal(|ui| { - ui.label("Device"); - ui.add_space(130.0); - ui.label("Baud"); - }); - - let old_name = self.device.clone(); - ui.horizontal(|ui| { - let dev_text = self.device.replace("/dev/tty.", ""); - ui.horizontal(|ui| { - if self.connected_to_device { - ui.disable(); - } - let _response = egui::ComboBox::from_id_salt("Device") - .selected_text(dev_text) - .width(RIGHT_PANEL_WIDTH * 0.92 - 155.0) - .show_ui(ui, |ui| { - devices - .into_iter() - // on macOS each device appears as /dev/tty.* and /dev/cu.* - // we only display the /dev/tty.* here - .filter(|dev| !dev.contains("/dev/cu.")) - .for_each(|dev| { - // this makes the names shorter in the UI on UNIX and UNIX-like platforms - let dev_text = dev.replace("/dev/tty.", ""); - ui.selectable_value(&mut self.device, dev, dev_text); - }); - }).response; - // let selected_new_device = response.changed(); //somehow this does not work - // if selected_new_device { - if old_name != self.device { - if !self.data.time.is_empty() { - self.show_warning_window = WindowFeedback::Waiting; - self.old_device = old_name; - } else { - self.show_warning_window = WindowFeedback::Clear; - } - } - }); - match self.show_warning_window { - WindowFeedback::None => {} - WindowFeedback::Waiting => { - self.show_warning_window = self.clear_warning_window(ctx); - } - WindowFeedback::Clear => { - // new device selected, check in previously used devices - let mut device_is_already_saved = false; - for (idx, dev) in self.serial_devices.devices.iter().enumerate() { - if dev.name == self.device { - // this is the device! - self.device = dev.name.clone(); - self.device_idx = idx; - init = true; - device_is_already_saved = true; - } - } - if !device_is_already_saved { - // create new device in the archive - let mut device = Device::default(); - device.name = self.device.clone(); - self.serial_devices.devices.push(device); - self.serial_devices.number_of_plots.push(1); - self.serial_devices.labels.push(vec!["Column 0".to_string()]); - self.device_idx = self.serial_devices.devices.len() - 1; - save_serial_settings(&self.serial_devices); - } - self.clear_tx.send(true).expect("failed to send clear after choosing new device"); - // need to clear the data here such that we don't get errors in the gui (plot) - self.data = DataContainer::default(); - self.show_warning_window = WindowFeedback::None; - } - WindowFeedback::Cancel => { - self.device = self.old_device.clone(); - self.show_warning_window = WindowFeedback::None; - } - } - egui::ComboBox::from_id_salt("Baud Rate") - .selected_text(format!("{}", self.serial_devices.devices[self.device_idx].baud_rate)) - .width(80.0) - .show_ui(ui, |ui| { - if self.connected_to_device { - ui.disable(); - } - BAUD_RATES.iter().for_each(|baud_rate| { - ui.selectable_value( - &mut self.serial_devices.devices[self.device_idx].baud_rate, - *baud_rate, - baud_rate.to_string(), - ); - }); - }); - let connect_text = if self.connected_to_device { "Disconnect" } else { "Connect" }; - if ui.button(connect_text).clicked() { - if let Ok(mut device) = self.device_lock.write() { - if self.connected_to_device { - device.name.clear(); - } else { - device.name = self.serial_devices.devices[self.device_idx].name.clone(); - device.baud_rate = self.serial_devices.devices[self.device_idx].baud_rate; - } - } - } - }); - ui.add_space(5.0); - ui.horizontal(|ui| { - ui.label("Data Bits"); - ui.add_space(5.0); - ui.label("Parity"); - ui.add_space(20.0); - ui.label("Stop Bits"); - ui.label("Flow Control"); - ui.label("Timeout"); - }); - ui.horizontal(|ui| { - if self.connected_to_device { - ui.disable(); - } - egui::ComboBox::from_id_salt("Data Bits") - .selected_text(self.serial_devices.devices[self.device_idx].data_bits.to_string()) - .width(30.0) - .show_ui(ui, |ui| { - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].data_bits, DataBits::Eight, DataBits::Eight.to_string()); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].data_bits, DataBits::Seven, DataBits::Seven.to_string()); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].data_bits, DataBits::Six, DataBits::Six.to_string()); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].data_bits, DataBits::Five, DataBits::Five.to_string()); - - }); - egui::ComboBox::from_id_salt("Parity") - .selected_text(self.serial_devices.devices[self.device_idx].parity.to_string()) - .width(30.0) - .show_ui(ui, |ui| { - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].parity, Parity::None, Parity::None.to_string()); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].parity, Parity::Odd, Parity::Odd.to_string()); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].parity, Parity::Even, Parity::Even.to_string()); - }); - egui::ComboBox::from_id_salt("Stop Bits") - .selected_text(self.serial_devices.devices[self.device_idx].stop_bits.to_string()) - .width(30.0) - .show_ui(ui, |ui| { - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].stop_bits, StopBits::One, StopBits::One.to_string()); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].stop_bits, StopBits::Two, StopBits::Two.to_string()); - }); - egui::ComboBox::from_id_salt("Flow Control") - .selected_text(self.serial_devices.devices[self.device_idx].flow_control.to_string()) - .width(75.0) - .show_ui(ui, |ui| { - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].flow_control, FlowControl::None, FlowControl::None.to_string()); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].flow_control, FlowControl::Hardware, FlowControl::Hardware.to_string()); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].flow_control, FlowControl::Software, FlowControl::Software.to_string()); - }); - egui::ComboBox::from_id_salt("Timeout") - .selected_text(self.serial_devices.devices[self.device_idx].timeout.as_millis().to_string()) - .width(55.0) - .show_ui(ui, |ui| { - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].timeout, Duration::from_millis(0), "0"); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].timeout, Duration::from_millis(10), "10"); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].timeout, Duration::from_millis(100), "100"); - ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].timeout, Duration::from_millis(1000), "1000"); - }); + if !device_is_already_saved { + // create new device in the archive + let mut device = Device::default(); + device.name = self.device.clone(); + self.serial_devices.devices.push(device); + self.serial_devices.number_of_plots.push(1); + self.serial_devices.number_of_highlights.push(1); + self.serial_devices.highlight_labels.push(vec!["".to_string()]); + self.serial_devices.labels.push(vec!["Column 0".to_string()]); + self.device_idx = self.serial_devices.devices.len() - 1; + save_serial_settings(&self.serial_devices); + } + self.clear_tx.send(true).expect("failed to send clear after choosing new device"); + // need to clear the data here such that we don't get errors in the gui (plot) + self.data = DataContainer::default(); + self.show_warning_window = WindowFeedback::None; + } + WindowFeedback::Cancel => { + self.device = self.old_device.clone(); + self.show_warning_window = WindowFeedback::None; + } + } + egui::ComboBox::from_id_salt("Baud Rate") + .selected_text(format!("{}", self.serial_devices.devices[self.device_idx].baud_rate)) + .width(80.0) + .show_ui(ui, |ui| { + if self.connected_to_device { + ui.disable(); + } + BAUD_RATES.iter().for_each(|baud_rate| { + ui.selectable_value( + &mut self.serial_devices.devices[self.device_idx].baud_rate, + *baud_rate, + baud_rate.to_string(), + ); }); + }); + let connect_text = if self.connected_to_device { "Disconnect" } else { "Connect" }; + if ui.button(connect_text).clicked() { + if let Ok(mut device) = self.device_lock.write() { + if self.connected_to_device { + device.name.clear(); + } else { + device.name = self.serial_devices.devices[self.device_idx].name.clone(); + device.baud_rate = self.serial_devices.devices[self.device_idx].baud_rate; + } + } + } + }); + ui.add_space(5.0); + ui.horizontal(|ui| { + ui.label("Data Bits"); + ui.add_space(5.0); + ui.label("Parity"); + ui.add_space(20.0); + ui.label("Stop Bits"); + ui.label("Flow Control"); + ui.label("Timeout"); + }); + ui.horizontal(|ui| { + if self.connected_to_device { + ui.disable(); + } + egui::ComboBox::from_id_salt("Data Bits") + .selected_text(self.serial_devices.devices[self.device_idx].data_bits.to_string()) + .width(30.0) + .show_ui(ui, |ui| { + ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].data_bits, DataBits::Eight, DataBits::Eight.to_string()); + ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].data_bits, DataBits::Seven, DataBits::Seven.to_string()); + ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].data_bits, DataBits::Six, DataBits::Six.to_string()); + ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].data_bits, DataBits::Five, DataBits::Five.to_string()); - ui.add_space(20.0); - - egui::Grid::new("upper") - .num_columns(2) - .spacing(Vec2 { x: 10.0, y: 10.0 }) - .striped(true) - .show(ui, |ui| { - ui.label("Plotting range [#]: "); - - let window_fmt = |val: f64, _range: RangeInclusive| { - if val != usize::MAX as f64 { - val.to_string() - } else { - "∞".to_string() - } - }; + }); + egui::ComboBox::from_id_salt("Parity") + .selected_text(self.serial_devices.devices[self.device_idx].parity.to_string()) + .width(30.0) + .show_ui(ui, |ui| { + ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].parity, Parity::None, Parity::None.to_string()); + ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].parity, Parity::Odd, Parity::Odd.to_string()); + ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].parity, Parity::Even, Parity::Even.to_string()); + }); + egui::ComboBox::from_id_salt("Stop Bits") + .selected_text(self.serial_devices.devices[self.device_idx].stop_bits.to_string()) + .width(30.0) + .show_ui(ui, |ui| { + ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].stop_bits, StopBits::One, StopBits::One.to_string()); + ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].stop_bits, StopBits::Two, StopBits::Two.to_string()); + }); + egui::ComboBox::from_id_salt("Flow Control") + .selected_text(self.serial_devices.devices[self.device_idx].flow_control.to_string()) + .width(75.0) + .show_ui(ui, |ui| { + ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].flow_control, FlowControl::None, FlowControl::None.to_string()); + ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].flow_control, FlowControl::Hardware, FlowControl::Hardware.to_string()); + ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].flow_control, FlowControl::Software, FlowControl::Software.to_string()); + }); + egui::ComboBox::from_id_salt("Timeout") + .selected_text(self.serial_devices.devices[self.device_idx].timeout.as_millis().to_string()) + .width(55.0) + .show_ui(ui, |ui| { + ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].timeout, Duration::from_millis(0), "0"); + ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].timeout, Duration::from_millis(10), "10"); + ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].timeout, Duration::from_millis(100), "100"); + ui.selectable_value(&mut self.serial_devices.devices[self.device_idx].timeout, Duration::from_millis(1000), "1000"); + }); + }); + } + fn draw_export_settings(&mut self, ctx: &egui::Context, ui: &mut Ui) { + egui::Grid::new("export_settings") + .num_columns(2) + .spacing(Vec2 { x: 10.0, y: 10.0 }) + .striped(true) + .show(ui, |ui| { + if ui.button(egui::RichText::new(format!("{} Save CSV", egui_phosphor::regular::FLOPPY_DISK))) + .on_hover_text("Save Plot Data to CSV.") + .clicked() || ui.input_mut(|i| i.consume_shortcut(&SAVE_FILE_SHORTCUT)) + { + if let Some(path) = rfd::FileDialog::new().save_file() { + self.picked_path = path; + self.picked_path.set_extension("csv"); + if let Err(e) = self.save_tx.send(FileOptions { + file_path: self.picked_path.clone(), + save_absolute_time: self.gui_conf.save_absolute_time, + save_raw_traffic: self.save_raw, + names: self.serial_devices.labels[self.device_idx].clone(), + }) { + print_to_console( + &self.print_lock, + Print::Error(format!( + "save_tx thread send failed: {:?}", + e + )), + ); + } + } + }; - ui.horizontal(|ui| { - ui.add(egui::DragValue::new(&mut self.plotting_range) - .custom_formatter(window_fmt)) - .on_hover_text("Select a window of the last datapoints to be displayed in the plot."); - if ui.button("Full Dataset") - .on_hover_text("Show the full dataset.") - .clicked() { - self.plotting_range = usize::MAX; - } - }); - ui.end_row(); - ui.label("Number of plots [#]: "); + if ui + .button(egui::RichText::new(format!("{} Save Plot", egui_phosphor::regular::FLOPPY_DISK))) + .on_hover_text("Save an image of the Plot.") + .clicked() || ui.input_mut(|i| i.consume_shortcut(&SAVE_PLOT_SHORTCUT)) - ui.horizontal(|ui| { - if ui.button(egui::RichText::new(egui_phosphor::regular::ARROW_FAT_LEFT.to_string())).clicked() { - self.serial_devices.number_of_plots[self.device_idx] = - (self.serial_devices.number_of_plots[self.device_idx] - 1).clamp(1, 10); - } - ui.add(egui::DragValue::new(&mut self.serial_devices.number_of_plots[self.device_idx]) - .range(1..=10)) - .on_hover_text("Select the number of plots to be shown."); - if ui.button(egui::RichText::new(egui_phosphor::regular::ARROW_FAT_RIGHT.to_string())).clicked() { - self.serial_devices.number_of_plots[self.device_idx] = - (self.serial_devices.number_of_plots[self.device_idx] + 1).clamp(1, 10); - } - }); + { + ctx.send_viewport_cmd(egui::ViewportCommand::Screenshot); + } + ui.end_row(); + if ui.button(egui::RichText::new(format!("{} Clear Data", egui_phosphor::regular::X))) + .on_hover_text("Clear Data from Plot.") + .clicked() || ui.input_mut(|i| i.consume_shortcut(&CLEAR_PLOT_SHORTCUT)) { + print_to_console( + &self.print_lock, + Print::Ok("Cleared recorded Data".to_string()), + ); + if let Err(err) = self.clear_tx.send(true) { + print_to_console( + &self.print_lock, + Print::Error(format!( + "clear_tx thread send failed: {:?}", + err + )), + ); + } + // need to clear the data here in order to prevent errors in the gui (plot) + self.data = DataContainer::default(); + // self.names_tx.send(self.serial_devices.labels[self.device_idx].clone()).expect("Failed to send names"); - ui.end_row(); - ui.label("Show Sent Commands"); - ui.add(toggle(&mut self.show_sent_cmds)) - .on_hover_text("Show sent commands in console."); - ui.end_row(); - ui.label("Show Timestamp"); - ui.add(toggle(&mut self.show_timestamps)) - .on_hover_text("Show timestamp in console."); - ui.end_row(); - ui.label("EOL character"); - ui.add( - egui::TextEdit::singleline(&mut self.eol) - .desired_width(ui.available_width() * 0.9)) - .on_hover_text("Configure your EOL character for sent commands.."); - // ui.checkbox(&mut self.gui_conf.debug, "Debug Mode"); - ui.end_row(); - ui.end_row(); - - if ui.button(egui::RichText::new(format!("{} Save CSV", egui_phosphor::regular::FLOPPY_DISK))) - .on_hover_text("Save Plot Data to CSV.") - .clicked() || ui.input_mut(|i| i.consume_shortcut(&SAVE_FILE_SHORTCUT)) - { - if let Some(path) = rfd::FileDialog::new().save_file() { - self.picked_path = path; - self.picked_path.set_extension("csv"); - if let Err(e) = self.save_tx.send(FileOptions { - file_path: self.picked_path.clone(), - save_absolute_time: self.gui_conf.save_absolute_time, - save_raw_traffic: self.save_raw, - names: self.serial_devices.labels[self.device_idx].clone(), - }) { - print_to_console( - &self.print_lock, - Print::Error(format!( - "save_tx thread send failed: {:?}", - e - )), - ); - } - } - }; + } + ui.end_row(); + ui.label("Save Raw Traffic"); + ui.add(toggle(&mut self.save_raw)) + .on_hover_text("Save second CSV containing raw traffic.") + .changed(); + ui.end_row(); + ui.label("Save Absolute Time"); + ui.add(toggle(&mut self.gui_conf.save_absolute_time)) + .on_hover_text("Save absolute time in CSV."); + ui.end_row(); + }); + } - if ui - .button(egui::RichText::new(format!("{} Save Plot", egui_phosphor::regular::FLOPPY_DISK))) - .on_hover_text("Save an image of the Plot.") - .clicked() || ui.input_mut(|i| i.consume_shortcut(&SAVE_PLOT_SHORTCUT)) + fn draw_global_settings(&mut self, ui: &mut Ui) { + ui.add_space(20.0); + if ui + .add(ThemeSwitch::new(&mut self.gui_conf.theme_preference)) + .changed() + { + ui.ctx().set_theme(self.gui_conf.theme_preference); + }; + self.gui_conf.dark_mode = ui.visuals() == &Visuals::dark(); + ui.add_space(20.0); + + if ui + .button(egui::RichText::new(format!( + "{} Clear Data", + egui_phosphor::regular::X + ))) + .on_hover_text("Clear Data from Plot.") + .clicked() + || ui.input_mut(|i| i.consume_shortcut(&CLEAR_PLOT_SHORTCUT)) + { + print_to_console( + &self.print_lock, + Print::Ok("Cleared recorded Data".to_string()), + ); + if let Err(err) = self.clear_tx.send(true) { + print_to_console( + &self.print_lock, + Print::Error(format!("clear_tx thread send failed: {:?}", err)), + ); + } + // need to clear the data here in order to prevent errors in the gui (plot) + self.data = DataContainer::default(); + //self.names_tx.send(self.serial_devices.plot_labels[self.device_idx].clone()).expect("Failed to send names"); + } + ui.horizontal(|ui| { + if ui.button("Clear Device History").clicked() { + self.serial_devices = SerialDevices::default(); + self.device.clear(); + self.device_idx = 0; + clear_serial_settings(); + } + if ui.button("Reset Labels").clicked() { + // self.serial_devices.labels[self.device_idx] = self.serial_devices.labels.clone(); + } + }); + } - { - ctx.send_viewport_cmd(egui::ViewportCommand::Screenshot); - } - ui.end_row(); - if ui.button(egui::RichText::new(format!("{} Clear Data", egui_phosphor::regular::X))) - .on_hover_text("Clear Data from Plot.") - .clicked() || ui.input_mut(|i| i.consume_shortcut(&CLEAR_PLOT_SHORTCUT)) { - print_to_console( - &self.print_lock, - Print::Ok("Cleared recorded Data".to_string()), - ); - if let Err(err) = self.clear_tx.send(true) { - print_to_console( - &self.print_lock, - Print::Error(format!( - "clear_tx thread send failed: {:?}", - err - )), - ); - } - // need to clear the data here in order to prevent errors in the gui (plot) - self.data = DataContainer::default(); - // self.names_tx.send(self.serial_devices.labels[self.device_idx].clone()).expect("Failed to send names"); + fn draw_plot_settings(&mut self, ui: &mut Ui) { + egui::Grid::new("plot_settings") + .num_columns(2) + .spacing(Vec2 { x: 10.0, y: 10.0 }) + .striped(true) + .show(ui, |ui| { + ui.label("Plotting range [#]: "); + + let window_fmt = |val: f64, _range: RangeInclusive| { + if val != usize::MAX as f64 { + val.to_string() + } else { + "∞".to_string() + } + }; - } - ui.end_row(); - ui.label("Save Raw Traffic"); - ui.add(toggle(&mut self.save_raw)) - .on_hover_text("Save second CSV containing raw traffic.") - .changed(); - ui.end_row(); - ui.label("Save Absolute Time"); - ui.add(toggle(&mut self.gui_conf.save_absolute_time)) - .on_hover_text("Save absolute time in CSV."); - ui.end_row(); - }); - ui.add_space(25.0); - if ui.add(ThemeSwitch::new(&mut self.gui_conf.theme_preference)).changed() { - ui.ctx().set_theme(self.gui_conf.theme_preference); - }; - ui.add_space(25.0); - self.gui_conf.dark_mode = ui.visuals() == &Visuals::dark(); - ui.horizontal( |ui| { - if ui.button("Clear Device History").clicked() { - self.serial_devices = SerialDevices::default(); - self.device.clear(); - self.device_idx = 0; - clear_serial_settings(); - } - if ui.button("Reset Labels").clicked() { - // self.serial_devices.labels[self.device_idx] = self.serial_devices.labels.clone(); + ui.horizontal(|ui| { + ui.add( + egui::DragValue::new(&mut self.plotting_range).custom_formatter(window_fmt), + ) + .on_hover_text( + "Select a window of the last datapoints to be displayed in the plot.", + ); + if ui + .button("Full Dataset") + .on_hover_text("Show the full dataset.") + .clicked() + { + self.plotting_range = usize::MAX; + } + }); + ui.end_row(); + ui.label("Number of plots [#]: "); + + ui.horizontal(|ui| { + if ui + .button(egui::RichText::new( + egui_phosphor::regular::ARROW_FAT_LEFT.to_string(), + )) + .clicked() + { + if self.serial_devices.number_of_plots[self.device_idx] == 0 { + return; } - }); - if self.labels.len() == 1 { - ui.label("Detected 1 Dataset:"); - } else { - ui.label(format!("Detected {} Datasets:", self.labels.len())); + self.serial_devices.number_of_plots[self.device_idx] = + (self.serial_devices.number_of_plots[self.device_idx] - 1).clamp(0, 10); } - ui.add_space(5.0); - for i in 0..self.labels.len().min(10) { - // if init, set names to what has been stored in the device last time - if init { - // self.names_tx.send(self.labels.clone()).expect("Failed to send names"); - init = false; + ui.add( + egui::DragValue::new( + &mut self.serial_devices.number_of_plots[self.device_idx], + ) + .range(0..=10), + ) + .on_hover_text("Select the number of plots to be shown."); + if ui + .button(egui::RichText::new( + egui_phosphor::regular::ARROW_FAT_RIGHT.to_string(), + )) + .clicked() + { + if self.serial_devices.number_of_plots[self.device_idx] == 10 { + return; } + self.serial_devices.number_of_plots[self.device_idx] = + (self.serial_devices.number_of_plots[self.device_idx] + 1).clamp(0, 10); + } + }); - if self.labels.len() <= i { - break; - } - ui.horizontal(|ui| { + ui.end_row(); + ui.label("Show Sent Commands"); + ui.add(toggle(&mut self.show_sent_cmds)) + .on_hover_text("Show sent commands in console."); + ui.end_row(); + ui.label("Show Timestamp"); + ui.add(toggle(&mut self.show_timestamps)) + .on_hover_text("Show timestamp in console."); + ui.end_row(); + ui.label("EOL character"); + ui.add( + egui::TextEdit::singleline(&mut self.eol) + .desired_width(ui.available_width() * 0.9), + ) + .on_hover_text("Configure your EOL character for sent commands.."); + // ui.checkbox(&mut self.gui_conf.debug, "Debug Mode"); + ui.end_row(); + }); + ui.add_space(25.0); - let response = color_picker_widget(ui,"", &mut self.colors,i ); + if self.labels.len() == 1 { + ui.label("Detected 1 Dataset:"); + } else { + ui.label(format!("Detected {} Datasets:", self.labels.len())); + } + ui.add_space(5.0); + for i in 0..self.labels.len().min(10) { + // if init, set names to what has been stored in the device last time + if self.init { + // self.names_tx.send(self.labels.clone()).expect("Failed to send names"); + self.init = false; + } - // Check if the square was clicked and toggle color picker window - if response.clicked() { - self.show_color_window = ColorWindow::ColorIndex(i); - }; + if self.labels.len() <= i { + break; + } + ui.horizontal(|ui| { + let response = color_picker_widget(ui, "", &mut self.colors, i); - if ui.add( - egui::TextEdit::singleline(&mut self.labels[i]) - .desired_width(0.95 * RIGHT_PANEL_WIDTH) - ).on_hover_text("Use custom names for your Datasets.").changed() { - // self.names_tx.send(self.labels.clone()).expect("Failed to send names"); - }; - }); - } - match self.show_color_window {ColorWindow::NoShow => { + // Check if the square was clicked and toggle color picker window + if response.clicked() { + self.show_color_window = ColorWindow::ColorIndex(i); + }; - } - ColorWindow::ColorIndex(index) => { - if color_picker_window(ui.ctx(), &mut self.colors[index], &mut self.color_vals[index]) { - self.show_color_window = ColorWindow::NoShow; - } + if ui + .add( + egui::TextEdit::singleline(&mut self.labels[i]) + .desired_width(0.95 * RIGHT_PANEL_WIDTH), + ) + .on_hover_text("Use custom names for your Datasets.") + .changed() + { + // self.names_tx.send(self.labels.clone()).expect("Failed to send names"); + }; + }); + } + match self.show_color_window { + ColorWindow::NoShow => {} + ColorWindow::ColorIndex(index) => { + if color_picker_window( + ui.ctx(), + &mut self.colors[index], + &mut self.color_vals[index], + ) { + self.show_color_window = ColorWindow::NoShow; + } + } + } + + if self.labels.len() > 10 { + ui.label("Only renaming up to 10 Datasets is currently supported."); + } + } + + fn draw_highlight_settings(&mut self, _ctx: &egui::Context, ui: &mut Ui) { + egui::Grid::new("highlight_settings") + .num_columns(2) + .spacing(Vec2 { x: 10.0, y: 10.0 }) + .striped(true) + .show(ui, |ui| { + ui.label("Number of sentence [#]: "); + + ui.horizontal(|ui| { + if ui + .button(egui::RichText::new( + egui_phosphor::regular::ARROW_FAT_LEFT.to_string(), + )) + .clicked() + { + self.serial_devices.number_of_highlights[self.device_idx] = + (self.serial_devices.number_of_highlights[self.device_idx] - 1) + .clamp(1, 4); + while self.serial_devices.number_of_highlights[self.device_idx] + < self.serial_devices.highlight_labels[self.device_idx].len() + { + self.serial_devices.highlight_labels[self.device_idx].truncate( + self.serial_devices.number_of_highlights[self.device_idx], + ); } } - - if self.labels.len() > 10 { - ui.label("Only renaming up to 10 Datasets is currently supported."); + ui.add( + egui::DragValue::new( + &mut self.serial_devices.number_of_highlights[self.device_idx], + ) + .range(1..=4), + ) + .on_hover_text("Select the number of sentence to be highlighted."); + if ui + .button(egui::RichText::new( + egui_phosphor::regular::ARROW_FAT_RIGHT.to_string(), + )) + .clicked() + { + self.serial_devices.number_of_highlights[self.device_idx] = + (self.serial_devices.number_of_highlights[self.device_idx] + 1) + .clamp(1, 4); + } + while self.serial_devices.number_of_highlights[self.device_idx] + > self.serial_devices.highlight_labels[self.device_idx].len() + { + self.serial_devices.highlight_labels[self.device_idx].push("".to_string()); } }); + }); - if let Ok(read_guard) = self.print_lock.read() { - self.console = read_guard.clone(); - } - let num_rows = self.console.len(); - let row_height = ui.text_style_height(&egui::TextStyle::Body); - - ui.add_space(20.0); - ui.separator(); - ui.label("Debug Info:"); - ui.add_space(5.0); - egui::ScrollArea::vertical() - .id_salt("console_scroll_area") - .auto_shrink([false; 2]) - .stick_to_bottom(true) - .max_height(row_height * 15.5) - .show_rows(ui, row_height, num_rows, |ui, _row_range| { - let content: String = self - .console - .iter() - .flat_map(|row| row.scroll_area_message(&self.gui_conf)) - .map(|msg| msg.label + msg.content.as_str()) - .collect::>() - .join("\n"); - // we need to add it as one multiline object, such that we can select and copy - // text over multiple lines - ui.add( - egui::TextEdit::multiline(&mut content.as_str()) - .font(DEFAULT_FONT_ID) // for cursor height - .lock_focus(true), // TODO: add a layouter to highlight the labels - ); + ui.label(format!( + "Detected {} highlight:", + self.serial_devices.number_of_highlights[self.device_idx] + )); + + ui.add_space(5.0); + for i in 0..(self.serial_devices.number_of_highlights[self.device_idx]) { + ui.add( + egui::TextEdit::singleline(&mut self.serial_devices.highlight_labels[self.device_idx][i]) + .desired_width(0.95 * RIGHT_PANEL_WIDTH) + ).on_hover_text("Sentence to highlight"); + /* + // Todo implement the color picker for each sentence + let mut theme = + egui_extras::syntax_highlighting::CodeTheme::from_memory(ui.ctx(), ui.style()); + ui.collapsing(self.serial_devices.highlight_labels[self.device_idx][i].clone(), |ui| { + theme.ui(ui); + theme.store_in_memory(ui.ctx()); + }); + */ + } + } + + fn draw_side_panel(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + egui::SidePanel::new(Side::Right, "settings panel") + .min_width(RIGHT_PANEL_WIDTH) + .max_width(RIGHT_PANEL_WIDTH) + .resizable(false) + //.default_width(right_panel_width) + .show(ctx, |ui| { + egui::ScrollArea::vertical().show(ui, |ui| { + ui.add_enabled_ui(true, |ui| { + + self.draw_serial_settings(ctx,ui); + + self.draw_global_settings(ui); + ui.add_space(10.0); + CollapsingHeader::new("Plot") + .default_open(true) + .show(ui, |ui| { + self.draw_plot_settings(ui); + }); + + CollapsingHeader::new("Text Highlight") + .default_open(true) + .show(ui, |ui| { + self.draw_highlight_settings(ctx, ui); + }); + + CollapsingHeader::new("Export") + .default_open(true) + .show(ui, |ui| { + self.draw_export_settings(ctx, ui); + }); + + if let Ok(read_guard) = self.print_lock.read() { + self.console = read_guard.clone(); + } + let num_rows = self.console.len(); + let row_height = ui.text_style_height(&egui::TextStyle::Body); + + ui.add_space(20.0); + ui.separator(); + ui.label("Debug Info:"); + ui.add_space(5.0); + egui::ScrollArea::vertical() + .id_salt("console_scroll_area") + .auto_shrink([false; 2]) + .stick_to_bottom(true) + .max_height(row_height * 15.5) + .show_rows(ui, row_height, num_rows, |ui, _row_range| { + let content: String = self + .console + .iter() + .flat_map(|row| row.scroll_area_message(&self.gui_conf)) + .map(|msg| msg.label + msg.content.as_str()) + .collect::>() + .join("\n"); + // we need to add it as one multiline object, such that we can select and copy + // text over multiple lines + ui.add( + egui::TextEdit::multiline(&mut content.as_str()) + .font(DEFAULT_FONT_ID) // for cursor height + .lock_focus(true), // TODO: add a layouter to highlight the labels + ); + }); }); }); + }); } fn paint_connection_indicator(&self, ui: &mut egui::Ui) { @@ -937,7 +1138,6 @@ impl eframe::App for MyApp { if let Ok(read_guard) = self.connected_lock.read() { self.connected_to_device = *read_guard; } - self.draw_central_panel(ctx); self.draw_side_panel(ctx, frame); @@ -983,4 +1183,4 @@ impl eframe::App for MyApp { println!("gui settings save failed: {:?}", err); } } -} +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index c47e9db..d5fc4b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,7 @@ mod gui; mod io; mod serial; mod toggle; +mod custom_highlighter; const APP_INFO: AppInfo = AppInfo { name: "Serial Monitor", diff --git a/src/serial.rs b/src/serial.rs index 57262b3..567c005 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -15,9 +15,12 @@ use crate::{print_to_console, Packet, Print, APP_INFO, PREFS_KEY_SERIAL}; pub struct SerialDevices { pub devices: Vec, pub labels: Vec>, + pub highlight_labels: Vec>, pub colors: Vec>, pub color_vals: Vec>, pub number_of_plots: Vec, + pub number_of_highlights: Vec, + } impl Default for SerialDevices { @@ -25,9 +28,11 @@ impl Default for SerialDevices { SerialDevices { devices: vec![Device::default()], labels: vec![vec!["Column 0".to_string()]], + highlight_labels:vec![vec!["".to_string()]], colors: vec![vec![COLORS[0]]], color_vals: vec![vec![0.0]], number_of_plots: vec![1], + number_of_highlights: vec![1], } } }