From 8591aed6440530bb9ce543af68b9ae275eea8d22 Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Fri, 29 Aug 2025 00:56:23 +0200 Subject: [PATCH] save last opened running page in configurations --- src/cli/mod.rs | 2 + src/gui/components/header.rs | 3 +- src/gui/pages/overview_page.rs | 92 +++++++++++---------- src/gui/pages/settings_general_page.rs | 4 +- src/gui/pages/types/running_page.rs | 12 +-- src/gui/sniffer.rs | 110 +++++++++++++++---------- src/gui/types/conf.rs | 3 + 7 files changed, 128 insertions(+), 98 deletions(-) diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 887a4367..033afb8b 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -70,6 +70,7 @@ pub fn handle_cli_args() -> Task { mod tests { use serial_test::serial; + use crate::gui::pages::types::running_page::RunningPage; use crate::gui::pages::types::settings_page::SettingsPage; use crate::gui::styles::types::custom_palette::ExtraStyles; use crate::gui::styles::types::gradient_type::GradientType; @@ -130,6 +131,7 @@ fn test_restore_default_configs() { directory: "home".to_string(), }, last_opened_setting: SettingsPage::General, + last_opened_page: RunningPage::Inspect, }; // we want to be sure that modified config is different from defaults assert_ne!(Conf::default(), modified_conf); diff --git a/src/gui/components/header.rs b/src/gui/components/header.rs index fd087efd..5f0b980a 100644 --- a/src/gui/components/header.rs +++ b/src/gui/components/header.rs @@ -6,7 +6,6 @@ use iced::{Alignment, Font, Length}; use crate::gui::components::tab::notifications_badge; -use crate::gui::pages::types::running_page::RunningPage; use crate::gui::pages::types::settings_page::SettingsPage; use crate::gui::sniffer::Sniffer; use crate::gui::styles::button::ButtonType; @@ -42,7 +41,7 @@ pub fn header(sniffer: &Sniffer) -> Container<'_, Message, StyleType> { } let last_opened_setting = sniffer.conf.last_opened_setting; - let is_running = sniffer.running_page.ne(&RunningPage::Init); + let is_running = sniffer.running_page.is_some(); let logo = Icon::Sniffnet .to_text() diff --git a/src/gui/pages/overview_page.rs b/src/gui/pages/overview_page.rs index d433d3e3..41ed59df 100644 --- a/src/gui/pages/overview_page.rs +++ b/src/gui/pages/overview_page.rs @@ -62,57 +62,65 @@ pub fn overview_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> { let mut body = Column::new(); let mut tab_and_body = Column::new().height(Length::Fill); - let dots = &sniffer.dots_pulse.0; + // some packets are there! + let tabs = get_pages_tabs( + RunningPage::Overview, + font, + font_headers, + language, + sniffer.unread_notifications, + ); + tab_and_body = tab_and_body.push(tabs); - if let Some(error) = sniffer.pcap_error.as_ref() { - // pcap threw an ERROR! - body = body_pcap_error(error, dots, language, font); - } else { - // NO pcap error detected - let tot_packets = sniffer - .info_traffic - .tot_data_info - .tot_data(DataRepr::Packets); + let container_chart = container_chart(sniffer, font); - if tot_packets == 0 { - // no packets observed at all - body = body_no_packets(&sniffer.capture_source, font, language, dots); - } else { - // some packets are there! - let tabs = get_pages_tabs( - RunningPage::Overview, - font, - font_headers, - language, - sniffer.unread_notifications, - ); - tab_and_body = tab_and_body.push(tabs); + let container_info = col_info(sniffer); - let container_chart = container_chart(sniffer, font); + let container_report = row_report(sniffer); - let container_info = col_info(sniffer); - - let container_report = row_report(sniffer); - - body = body - .width(Length::Fill) - .padding(10) + body = body + .width(Length::Fill) + .padding(10) + .spacing(10) + .align_x(Alignment::Center) + .push( + Row::new() + .height(280) .spacing(10) - .align_x(Alignment::Center) - .push( - Row::new() - .height(280) - .spacing(10) - .push(container_info) - .push(container_chart), - ) - .push(container_report); - } - } + .push(container_info) + .push(container_chart), + ) + .push(container_report); Container::new(Column::new().push(tab_and_body.push(body))).height(Length::Fill) } +pub fn waiting_page(sniffer: &Sniffer) -> Option> { + let Settings { + style, language, .. + } = sniffer.conf.settings; + let font = style.get_extension().font; + + let dots = &sniffer.dots_pulse.0; + + let tot_packets = sniffer + .info_traffic + .tot_data_info + .tot_data(DataRepr::Packets); + + let body = if let Some(error) = sniffer.pcap_error.as_ref() { + // pcap threw an ERROR! + body_pcap_error(error, dots, language, font) + } else if tot_packets == 0 { + // no packets observed at all + body_no_packets(&sniffer.capture_source, font, language, dots) + } else { + return None; + }; + + Some(Container::new(Column::new().push(body)).height(Length::Fill)) +} + fn body_no_packets<'a>( cs: &CaptureSource, font: Font, diff --git a/src/gui/pages/settings_general_page.rs b/src/gui/pages/settings_general_page.rs index e54a7a28..902b9ad0 100644 --- a/src/gui/pages/settings_general_page.rs +++ b/src/gui/pages/settings_general_page.rs @@ -26,7 +26,7 @@ use crate::utils::types::file_info::FileInfo; use crate::utils::types::icon::Icon; use crate::utils::types::web_page::WebPage; -use crate::{Language, RunningPage, Sniffer, StyleType}; +use crate::{Language, Sniffer, StyleType}; pub fn settings_general_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> { let Settings { @@ -66,7 +66,7 @@ fn column_all_general_setting(sniffer: &Sniffer, font: Font) -> Column<'_, Messa .. } = sniffer.conf.settings.clone(); - let is_editable = sniffer.running_page.eq(&RunningPage::Init); + let is_editable = sniffer.running_page.is_none(); let mut column = Column::new() .align_x(Alignment::Center) diff --git a/src/gui/pages/types/running_page.rs b/src/gui/pages/types/running_page.rs index a37817c8..f0693964 100644 --- a/src/gui/pages/types/running_page.rs +++ b/src/gui/pages/types/running_page.rs @@ -3,13 +3,13 @@ use crate::translations::translations_2::inspect_translation; use crate::utils::types::icon::Icon; use crate::{Language, StyleType}; +use serde::{Deserialize, Serialize}; -/// This enum defines the current GUI page. -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +/// This enum defines the current running page. +#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize, Default)] pub enum RunningPage { - /// Initial page. - Init, /// Overview page. + #[default] Overview, /// Inspect page. Inspect, @@ -29,7 +29,6 @@ pub fn get_tab_label(&self, language: Language) -> &str { RunningPage::Overview => overview_translation(language), RunningPage::Inspect => inspect_translation(language), RunningPage::Notifications => notifications_translation(language), - RunningPage::Init => "", } } @@ -38,7 +37,6 @@ pub fn next(self) -> Self { RunningPage::Overview => RunningPage::Inspect, RunningPage::Inspect => RunningPage::Notifications, RunningPage::Notifications => RunningPage::Overview, - RunningPage::Init => RunningPage::Init, } } @@ -47,7 +45,6 @@ pub fn previous(self) -> Self { RunningPage::Overview => RunningPage::Notifications, RunningPage::Inspect => RunningPage::Overview, RunningPage::Notifications => RunningPage::Inspect, - RunningPage::Init => RunningPage::Init, } } @@ -56,7 +53,6 @@ pub fn icon<'a>(self) -> iced::widget::Text<'a, StyleType> { RunningPage::Overview => Icon::Overview, RunningPage::Inspect => Icon::Inspect, RunningPage::Notifications => Icon::Notification, - RunningPage::Init => Icon::Sniffnet, } .to_text() } diff --git a/src/gui/sniffer.rs b/src/gui/sniffer.rs index ee4d7634..bd73ed90 100644 --- a/src/gui/sniffer.rs +++ b/src/gui/sniffer.rs @@ -1,5 +1,6 @@ //! Module defining the application structure: messages, updates, subscriptions. +use crate::gui::pages::overview_page::waiting_page; use async_channel::Receiver; use iced::Event::{Keyboard, Window}; use iced::keyboard::key::Named; @@ -102,8 +103,8 @@ pub struct Sniffer { pub modal: Option, /// Currently displayed settings page; None if settings is closed pub settings_page: Option, - /// Defines the current running page - pub running_page: RunningPage, + /// Defines the current running page; None if initial page + pub running_page: Option, /// Number of unread notifications pub unread_notifications: usize, /// Search parameters of inspect page @@ -147,7 +148,7 @@ pub fn new(conf: Conf) -> Self { traffic_chart: TrafficChart::new(style, language), modal: None, settings_page: None, - running_page: RunningPage::Init, + running_page: None, unread_notifications: 0, search: SearchParameters::default(), page_number: 1, @@ -318,7 +319,8 @@ pub fn update(&mut self, message: Message) -> Task { } Message::CloseSettings => self.close_settings(), Message::ChangeRunningPage(running_page) => { - self.running_page = running_page; + self.running_page = Some(running_page); + self.conf.last_opened_page = running_page; if running_page.eq(&RunningPage::Notifications) { self.unread_notifications = 0; } @@ -358,7 +360,8 @@ pub fn update(&mut self, message: Message) -> Task { self.host_data_states.update_states(¶meters); self.page_number = 1; - self.running_page = RunningPage::Inspect; + self.running_page = Some(RunningPage::Inspect); + self.conf.last_opened_page = RunningPage::Inspect; self.search = parameters; } Message::UpdatePageNumber(increment) => { @@ -371,7 +374,9 @@ pub fn update(&mut self, message: Message) -> Task { } } Message::ArrowPressed(increment) => { - if self.running_page.eq(&RunningPage::Inspect) + if self + .running_page + .is_some_and(|p| p.eq(&RunningPage::Inspect)) && self.settings_page.is_none() && self.modal.is_none() { @@ -460,7 +465,10 @@ pub fn update(&mut self, message: Message) -> Task { window::change_level(window_id, Level::AlwaysOnTop), ]) } else { - if self.running_page.eq(&RunningPage::Notifications) { + if self + .running_page + .is_some_and(|p| p.eq(&RunningPage::Notifications)) + { self.unread_notifications = 0; } let mut commands = vec![ @@ -484,7 +492,7 @@ pub fn update(&mut self, message: Message) -> Task { } } Message::CtrlTPressed => { - if self.running_page.ne(&RunningPage::Init) + if self.running_page.is_some() && self.settings_page.is_none() && self.modal.is_none() && !self.timing_events.was_just_thumbnail_enter() @@ -553,10 +561,18 @@ pub fn view(&self) -> Element<'_, Message, StyleType> { thumbnail_page(self) } else { match self.running_page { - RunningPage::Init => initial_page(self), - RunningPage::Overview => overview_page(self), - RunningPage::Inspect => inspect_page(self), - RunningPage::Notifications => notifications_page(self), + None => initial_page(self), + Some(running_page) => { + if let Some(waiting_page) = waiting_page(self) { + waiting_page + } else { + match running_page { + RunningPage::Overview => overview_page(self), + RunningPage::Inspect => inspect_page(self), + RunningPage::Notifications => notifications_page(self), + } + } + } } }; @@ -664,7 +680,11 @@ fn refresh_data(&mut self, mut msg: InfoTraffic, no_more_packets: bool) { &self.favorite_hosts, &self.capture_source, ); - if self.thumbnail || self.running_page.ne(&RunningPage::Notifications) { + if self.thumbnail + || self + .running_page + .is_some_and(|p| p.ne(&RunningPage::Notifications)) + { self.unread_notifications += emitted_notifications; } self.traffic_chart.update_charts_data(&msg, no_more_packets); @@ -703,7 +723,7 @@ fn start(&mut self) -> Task { let capture_context = CaptureContext::new(&self.capture_source, pcap_path.as_ref(), &self.conf.filters); self.pcap_error = capture_context.error().map(ToString::to_string); - self.running_page = RunningPage::Overview; + self.running_page = Some(self.conf.last_opened_page); if capture_context.error().is_none() { // no pcap error @@ -759,7 +779,7 @@ fn reset(&mut self) { self.traffic_chart = TrafficChart::new(style, language); self.modal = None; self.settings_page = None; - self.running_page = RunningPage::Init; + self.running_page = None; self.unread_notifications = 0; self.search = SearchParameters::default(); self.page_number = 1; @@ -891,20 +911,21 @@ fn switch_page(&mut self, next: bool) { self.settings_page = Some(new_setting); self.conf.last_opened_setting = new_setting; } - ( - RunningPage::Inspect | RunningPage::Notifications | RunningPage::Overview, - None, - true, - ) => { + (Some(current_page), None, true) => { // Running with no overlays if self.info_traffic.tot_data_info.tot_data(DataRepr::Packets) > 0 { // Running with no overlays and some packets - self.running_page = if next { - self.running_page.next() + let new_page = if next { + current_page.next() } else { - self.running_page.previous() + current_page.previous() }; - if self.running_page.eq(&RunningPage::Notifications) { + self.running_page = Some(new_page); + self.conf.last_opened_page = new_page; + if self + .running_page + .is_some_and(|p| p.eq(&RunningPage::Notifications)) + { self.unread_notifications = 0; } } @@ -914,10 +935,7 @@ fn switch_page(&mut self, next: bool) { } fn shortcut_return(&mut self) -> Task { - if self.running_page.eq(&RunningPage::Init) - && self.settings_page.is_none() - && self.modal.is_none() - { + if self.running_page.is_none() && self.settings_page.is_none() && self.modal.is_none() { return Task::done(Message::Start); } else if self.modal.eq(&Some(MyModal::Reset)) { return Task::done(Message::Reset); @@ -940,7 +958,7 @@ fn shortcut_esc(&mut self) -> Task { // also called when the backspace shortcut is pressed fn reset_button_pressed(&mut self) -> Task { - if self.running_page.ne(&RunningPage::Init) { + if self.running_page.is_some() { let tot_packets = self.info_traffic.tot_data_info.tot_data(DataRepr::Packets); return if tot_packets == 0 && self.settings_page.is_none() { Task::done(Message::Reset) @@ -953,7 +971,7 @@ fn reset_button_pressed(&mut self) -> Task { fn quit_wrapper(&mut self) -> Task { let tot_packets = self.info_traffic.tot_data_info.tot_data(DataRepr::Packets); - if self.running_page.eq(&RunningPage::Init) || tot_packets == 0 { + if self.running_page.is_none() || tot_packets == 0 { Task::done(Message::Quit) } else if self.thumbnail { // TODO: uncomment once issue #653 is fixed @@ -968,7 +986,9 @@ fn quit_wrapper(&mut self) -> Task { } fn shortcut_ctrl_d(&mut self) -> Task { - if self.running_page.eq(&RunningPage::Notifications) + if self + .running_page + .is_some_and(|p| p.eq(&RunningPage::Notifications)) && !self.logged_notifications.0.is_empty() { return Task::done(Message::ShowModal(MyModal::ClearAll)); @@ -1705,7 +1725,7 @@ fn test_correctly_switch_running_and_settings_pages() { // initial status assert_eq!(sniffer.settings_page, None); assert_eq!(sniffer.modal, None); - assert_eq!(sniffer.running_page, RunningPage::Init); + assert!(sniffer.running_page.is_none()); // nothing changes sniffer.update(Message::SwitchPage(true)); assert_eq!(sniffer.settings_page, None); @@ -1714,7 +1734,7 @@ fn test_correctly_switch_running_and_settings_pages() { SettingsPage::Notifications ); assert_eq!(sniffer.modal, None); - assert_eq!(sniffer.running_page, RunningPage::Init); + assert!(sniffer.running_page.is_none()); // switch settings sniffer.update(Message::OpenLastSettings); assert_eq!(sniffer.settings_page, Some(SettingsPage::Notifications)); @@ -1722,12 +1742,12 @@ fn test_correctly_switch_running_and_settings_pages() { sniffer.conf.last_opened_setting, SettingsPage::Notifications ); - assert_eq!(sniffer.running_page, RunningPage::Init); + assert!(sniffer.running_page.is_none()); sniffer.update(Message::SwitchPage(false)); assert_eq!(sniffer.settings_page, Some(SettingsPage::General)); assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::General); assert_eq!(sniffer.modal, None); - assert_eq!(sniffer.running_page, RunningPage::Init); + assert!(sniffer.running_page.is_none()); sniffer.update(Message::SwitchPage(true)); assert_eq!(sniffer.settings_page, Some(SettingsPage::Notifications)); assert_eq!( @@ -1735,18 +1755,18 @@ fn test_correctly_switch_running_and_settings_pages() { SettingsPage::Notifications ); assert_eq!(sniffer.modal, None); - assert_eq!(sniffer.running_page, RunningPage::Init); + assert!(sniffer.running_page.is_none()); sniffer.update(Message::CloseSettings); assert_eq!(sniffer.settings_page, None); - assert_eq!(sniffer.running_page, RunningPage::Init); + assert!(sniffer.running_page.is_none()); // change state to running - sniffer.running_page = RunningPage::Overview; + sniffer.running_page = Some(RunningPage::Overview); assert_eq!(sniffer.settings_page, None); assert_eq!(sniffer.modal, None); - assert_eq!(sniffer.running_page, RunningPage::Overview); + assert_eq!(sniffer.running_page, Some(RunningPage::Overview)); // switch with closed setting and no packets received => nothing changes sniffer.update(Message::SwitchPage(true)); - assert_eq!(sniffer.running_page, RunningPage::Overview); + assert_eq!(sniffer.running_page, Some(RunningPage::Overview)); assert_eq!(sniffer.settings_page, None); // switch with closed setting and some packets received => change running page sniffer @@ -1754,25 +1774,25 @@ fn test_correctly_switch_running_and_settings_pages() { .tot_data_info .add_packet(0, TrafficDirection::Outgoing); sniffer.update(Message::SwitchPage(true)); - assert_eq!(sniffer.running_page, RunningPage::Inspect); + assert_eq!(sniffer.running_page, Some(RunningPage::Inspect)); assert_eq!(sniffer.settings_page, None); // switch with opened settings => change settings sniffer.update(Message::OpenLastSettings); - assert_eq!(sniffer.running_page, RunningPage::Inspect); + assert_eq!(sniffer.running_page, Some(RunningPage::Inspect)); assert_eq!(sniffer.settings_page, Some(SettingsPage::Notifications)); assert_eq!( sniffer.conf.last_opened_setting, SettingsPage::Notifications ); sniffer.update(Message::SwitchPage(true)); - assert_eq!(sniffer.running_page, RunningPage::Inspect); + assert_eq!(sniffer.running_page, Some(RunningPage::Inspect)); assert_eq!(sniffer.settings_page, Some(SettingsPage::Appearance)); assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance); // focus the window and try to switch => nothing changes sniffer.update(Message::WindowFocused); sniffer.update(Message::SwitchPage(true)); - assert_eq!(sniffer.running_page, RunningPage::Inspect); + assert_eq!(sniffer.running_page, Some(RunningPage::Inspect)); assert_eq!(sniffer.settings_page, Some(SettingsPage::Appearance)); } @@ -1819,6 +1839,7 @@ fn test_conf() { sniffer.update(Message::OutputPcapFile("test.cap".to_string())); sniffer.update(Message::OutputPcapDir("/".to_string())); sniffer.update(Message::SetPcapImport("/test.pcap".to_string())); + sniffer.update(Message::ChangeRunningPage(RunningPage::Notifications)); // quit the app by sending a CloseRequested message sniffer.update(Message::Quit); @@ -1862,6 +1883,7 @@ fn test_conf() { host_sort_type: SortType::Descending, service_sort_type: SortType::Descending, last_opened_setting: SettingsPage::Appearance, + last_opened_page: RunningPage::Notifications, export_pcap: ExportPcap { enabled: true, file_name: "test.cap".to_string(), diff --git a/src/gui/types/conf.rs b/src/gui/types/conf.rs index 2d82653b..fd0d82f8 100644 --- a/src/gui/types/conf.rs +++ b/src/gui/types/conf.rs @@ -1,3 +1,4 @@ +use crate::gui::pages::types::running_page::RunningPage; use crate::gui::pages::types::settings_page::SettingsPage; use crate::gui::types::config_window::ConfigWindow; use crate::gui::types::export_pcap::ExportPcap; @@ -36,6 +37,8 @@ pub struct Conf { pub service_sort_type: SortType, /// Remembers the last opened setting page pub last_opened_setting: SettingsPage, + /// Remembers the last opened running page + pub last_opened_page: RunningPage, /// Information about PCAP file export pub export_pcap: ExportPcap, /// Import path for PCAP file