feat: dynamically load pages when their sections are searched

This commit is contained in:
Michael Aaron Murphy 2024-10-24 17:01:57 +02:00 committed by Michael Murphy
parent 6463983745
commit 51bd56d8d3
6 changed files with 117 additions and 10 deletions

View file

@ -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<page::Entity>,
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<Self::Message>) {
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(&current_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<crate::Message> {
// 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.

View file

@ -62,6 +62,7 @@ enum ContextView {
}
pub struct Page {
on_enter_handle: Option<cosmic::iced::task::Handle>,
can_reset: bool,
no_custom_window_hint: bool,
context_view: Option<ContextView>,
@ -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<crate::pages::Message> for Page {
_: page::Entity,
_sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
) -> Task<crate::pages::Message> {
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<crate::pages::Message> for Page {
})
.map(crate::pages::Message::Appearance),
])
.abortable();
self.on_enter_handle = Some(handle);
task
}
fn on_leave(&mut self) -> Task<crate::pages::Message> {
if let Some(handle) = self.on_enter_handle.take() {
handle.abort();
}
cosmic::command::message(crate::pages::Message::Appearance(Message::Left))
}

View file

@ -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<cosmic::iced::task::Handle>,
/// Whether to show a context drawer.
context_view: Option<ContextView>,
@ -207,11 +210,16 @@ impl page::Page<crate::pages::Message> for Page {
_page: page::Entity,
_sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
) -> Task<crate::pages::Message> {
// 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<crate::pages::Message> for Page {
selection,
})))
})
.abortable();
self.on_enter_handle = Some(on_enter_handle);
task
}
fn on_leave(&mut self) -> Task<crate::pages::Message> {
// 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<Element<'_, crate::pages::Message>> {
@ -265,6 +286,7 @@ impl page::AutoBind<crate::pages::Message> 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,

View file

@ -194,7 +194,6 @@ pub struct Page {
dialog: Option<VpnDialog>,
view_more_popup: Option<ConnectionId>,
known_connections: IndexMap<UUID, ConnectionSettings>,
wireguard_connections: IndexMap<UUID, String>,
/// Withhold device update if the view more popup is shown.
withheld_devices: Option<Vec<network_manager::devices::DeviceInfo>>,
/// Withhold active connections update if the view more popup is shown.

View file

@ -18,6 +18,7 @@ use slotmap::SlotMap;
pub struct Page {
battery: Battery,
connected_devices: Vec<ConnectedDevice>,
on_enter_handle: Option<cosmic::iced::task::Handle>,
}
impl page::Page<crate::pages::Message> for Page {
@ -54,7 +55,20 @@ impl page::Page<crate::pages::Message> 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<crate::pages::Message> {
if let Some(handle) = self.on_enter_handle.take() {
handle.abort();
}
Task::none()
}
}

View file

@ -21,6 +21,7 @@ pub enum Message {
pub struct Page {
editing_device_name: bool,
info: Info,
on_enter_handle: Option<cosmic::iced::task::Handle>,
}
impl page::AutoBind<crate::pages::Message> for Page {}
@ -48,9 +49,21 @@ impl page::Page<crate::pages::Message> for Page {
_page: page::Entity,
_sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
) -> Task<crate::pages::Message> {
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<crate::pages::Message> {
if let Some(handle) = self.on_enter_handle.take() {
handle.abort();
}
Task::none()
}
}