From 51bd56d8d3b6091da54d58c10477541ae9395ab1 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Thu, 24 Oct 2024 17:01:57 +0200 Subject: [PATCH] feat: dynamically load pages when their sections are searched --- cosmic-settings/src/app.rs | 55 ++++++++++++++++++- .../src/pages/desktop/appearance/mod.rs | 12 +++- .../src/pages/desktop/wallpaper/mod.rs | 24 +++++++- .../src/pages/networking/vpn/mod.rs | 1 - cosmic-settings/src/pages/power/mod.rs | 16 +++++- cosmic-settings/src/pages/system/about.rs | 19 ++++++- 6 files changed, 117 insertions(+), 10 deletions(-) diff --git a/cosmic-settings/src/app.rs b/cosmic-settings/src/app.rs index 81bfbf9e..1b702e60 100644 --- a/cosmic-settings/src/app.rs +++ b/cosmic-settings/src/app.rs @@ -49,12 +49,14 @@ use desktop::{ #[cfg(feature = "wayland")] use event::wayland; use page::Entity; +use std::collections::BTreeSet; use std::{borrow::Cow, str::FromStr}; #[allow(clippy::struct_excessive_bools)] #[allow(clippy::module_name_repetitions)] pub struct SettingsApp { active_page: page::Entity, + loaded_pages: BTreeSet, config: Config, core: Core, nav_model: nav_bar::Model, @@ -167,6 +169,7 @@ impl cosmic::Application for SettingsApp { fn init(core: Core, flags: Self::Flags) -> (Self, Task) { let mut app = SettingsApp { active_page: page::Entity::default(), + loaded_pages: BTreeSet::new(), config: Config::new(), core, nav_model: nav_bar::Model::default(), @@ -337,7 +340,7 @@ impl cosmic::Application for SettingsApp { Message::SetWindowTitle => return self.set_title(), Message::SearchChanged(phrase) => { - self.search_changed(phrase); + return self.search_changed(phrase); } Message::SearchActivate => { @@ -781,6 +784,7 @@ impl SettingsApp { let mut leave_task = iced::Task::none(); if current_page != page { + self.loaded_pages.remove(¤t_page); leave_task = self .pages .on_leave(current_page) @@ -801,6 +805,8 @@ impl SettingsApp { .clone() .expect("sender should be available"); + self.loaded_pages.insert(page); + let page_task = self .pages .on_enter(page, sender) @@ -920,13 +926,15 @@ impl SettingsApp { .into() } - fn search_changed(&mut self, phrase: String) { + fn search_changed(&mut self, phrase: String) -> Task { // If the text was cleared, clear the search results too. if phrase.is_empty() { self.search_clear(); - return; + return Task::none(); } + let mut tasks = Vec::new(); + // Create a case-insensitive regular expression for the search function. let search_expression = regex::RegexBuilder::new(&phrase) .case_insensitive(true) @@ -942,10 +950,51 @@ impl SettingsApp { // Use the results if results were found. if !results.is_empty() { self.search_selections = results; + + let mut unload = BTreeSet::new(); + let mut load = BTreeSet::new(); + + 'outer: for loaded_page in &self.loaded_pages { + for (page, _) in &self.search_selections { + if loaded_page == page { + continue 'outer; + } + } + + unload.insert(*loaded_page); + } + + for (page, _) in &self.search_selections { + if !self.loaded_pages.contains(page) { + load.insert(*page); + } + } + + if let Some(ref sender) = self.page_sender { + for page in load { + eprintln!("loading {page:?}"); + self.loaded_pages.insert(page); + tasks.push(self.pages.on_enter(page, sender.clone())); + } + } + + for page in unload { + eprintln!("unloading {page:?}"); + self.loaded_pages.remove(&page); + self.pages.on_leave(page); + } } } self.search_input = phrase; + + if tasks.is_empty() { + Task::none() + } else { + cosmic::command::batch(tasks) + .map(Message::PageMessage) + .map(Into::into) + } } /// Clears the search results so that the search page will not be shown. diff --git a/cosmic-settings/src/pages/desktop/appearance/mod.rs b/cosmic-settings/src/pages/desktop/appearance/mod.rs index 623bf001..d7ad2fb1 100644 --- a/cosmic-settings/src/pages/desktop/appearance/mod.rs +++ b/cosmic-settings/src/pages/desktop/appearance/mod.rs @@ -62,6 +62,7 @@ enum ContextView { } pub struct Page { + on_enter_handle: Option, can_reset: bool, no_custom_window_hint: bool, context_view: Option, @@ -152,6 +153,7 @@ impl }); Self { + on_enter_handle: None, can_reset: if theme_mode.is_dark { theme_builder == ThemeBuilder::dark() } else { @@ -1431,7 +1433,7 @@ impl page::Page for Page { _: page::Entity, _sender: tokio::sync::mpsc::Sender, ) -> Task { - cosmic::command::batch(vec![ + let (task, handle) = cosmic::command::batch(vec![ // Load icon themes cosmic::command::future(icon_themes::fetch()).map(crate::pages::Message::Appearance), // Load font families @@ -1441,9 +1443,17 @@ impl page::Page for Page { }) .map(crate::pages::Message::Appearance), ]) + .abortable(); + + self.on_enter_handle = Some(handle); + task } fn on_leave(&mut self) -> Task { + if let Some(handle) = self.on_enter_handle.take() { + handle.abort(); + } + cosmic::command::message(crate::pages::Message::Appearance(Message::Left)) } diff --git a/cosmic-settings/src/pages/desktop/wallpaper/mod.rs b/cosmic-settings/src/pages/desktop/wallpaper/mod.rs index 2afd1d7b..4e3ae76f 100644 --- a/cosmic-settings/src/pages/desktop/wallpaper/mod.rs +++ b/cosmic-settings/src/pages/desktop/wallpaper/mod.rs @@ -137,6 +137,9 @@ enum ContextView { /// The page struct for the wallpaper view. pub struct Page { + /// Abort handle to the on_enter task. + on_enter_handle: Option, + /// Whether to show a context drawer. context_view: Option, @@ -207,11 +210,16 @@ impl page::Page for Page { _page: page::Entity, _sender: tokio::sync::mpsc::Sender, ) -> Task { + // Check if the page is already being loaded. + if self.on_enter_handle.is_some() { + return Task::none(); + } + let current_folder = self.config.current_folder().to_owned(); let recurse = self.categories.selected == Some(Category::Wallpapers); - Task::future(async move { + let (task, on_enter_handle) = Task::future(async move { let (service_config, displays) = wallpaper::config().await; let mut selection = change_folder(current_folder, recurse).await; @@ -245,6 +253,19 @@ impl page::Page for Page { selection, }))) }) + .abortable(); + + self.on_enter_handle = Some(on_enter_handle); + task + } + + fn on_leave(&mut self) -> Task { + // Cancel the on_enter task if it was running. + if let Some(handle) = self.on_enter_handle.take() { + handle.abort(); + } + + Task::none() } fn context_drawer(&self) -> Option> { @@ -265,6 +286,7 @@ impl page::AutoBind for Page {} impl Default for Page { fn default() -> Self { let mut page = Page { + on_enter_handle: None, context_view: None, show_tab_bar: false, active_output: None, diff --git a/cosmic-settings/src/pages/networking/vpn/mod.rs b/cosmic-settings/src/pages/networking/vpn/mod.rs index 3ae5dccc..d44bc10d 100644 --- a/cosmic-settings/src/pages/networking/vpn/mod.rs +++ b/cosmic-settings/src/pages/networking/vpn/mod.rs @@ -194,7 +194,6 @@ pub struct Page { dialog: Option, view_more_popup: Option, known_connections: IndexMap, - wireguard_connections: IndexMap, /// Withhold device update if the view more popup is shown. withheld_devices: Option>, /// Withhold active connections update if the view more popup is shown. diff --git a/cosmic-settings/src/pages/power/mod.rs b/cosmic-settings/src/pages/power/mod.rs index a080b689..66056886 100644 --- a/cosmic-settings/src/pages/power/mod.rs +++ b/cosmic-settings/src/pages/power/mod.rs @@ -18,6 +18,7 @@ use slotmap::SlotMap; pub struct Page { battery: Battery, connected_devices: Vec, + on_enter_handle: Option, } impl page::Page for Page { @@ -54,7 +55,20 @@ impl page::Page for Page { }), ]; - cosmic::Task::batch(futures).map(crate::pages::Message::Power) + let (task, handle) = cosmic::Task::batch(futures) + .map(crate::pages::Message::Power) + .abortable(); + + self.on_enter_handle = Some(handle); + task + } + + fn on_leave(&mut self) -> Task { + if let Some(handle) = self.on_enter_handle.take() { + handle.abort(); + } + + Task::none() } } diff --git a/cosmic-settings/src/pages/system/about.rs b/cosmic-settings/src/pages/system/about.rs index 6ece254b..0d71a55b 100644 --- a/cosmic-settings/src/pages/system/about.rs +++ b/cosmic-settings/src/pages/system/about.rs @@ -21,6 +21,7 @@ pub enum Message { pub struct Page { editing_device_name: bool, info: Info, + on_enter_handle: Option, } impl page::AutoBind for Page {} @@ -48,9 +49,21 @@ impl page::Page for Page { _page: page::Entity, _sender: tokio::sync::mpsc::Sender, ) -> Task { - Task::future( - async move { crate::pages::Message::About(Message::Info(Box::new(Info::load()))) }, - ) + let (task, handle) = Task::future(async move { + crate::pages::Message::About(Message::Info(Box::new(Info::load()))) + }) + .abortable(); + + self.on_enter_handle = Some(handle); + task + } + + fn on_leave(&mut self) -> Task { + if let Some(handle) = self.on_enter_handle.take() { + handle.abort(); + } + + Task::none() } }