feat: dynamically load pages when their sections are searched
This commit is contained in:
parent
6463983745
commit
51bd56d8d3
6 changed files with 117 additions and 10 deletions
|
|
@ -49,12 +49,14 @@ use desktop::{
|
||||||
#[cfg(feature = "wayland")]
|
#[cfg(feature = "wayland")]
|
||||||
use event::wayland;
|
use event::wayland;
|
||||||
use page::Entity;
|
use page::Entity;
|
||||||
|
use std::collections::BTreeSet;
|
||||||
use std::{borrow::Cow, str::FromStr};
|
use std::{borrow::Cow, str::FromStr};
|
||||||
|
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
#[allow(clippy::module_name_repetitions)]
|
#[allow(clippy::module_name_repetitions)]
|
||||||
pub struct SettingsApp {
|
pub struct SettingsApp {
|
||||||
active_page: page::Entity,
|
active_page: page::Entity,
|
||||||
|
loaded_pages: BTreeSet<page::Entity>,
|
||||||
config: Config,
|
config: Config,
|
||||||
core: Core,
|
core: Core,
|
||||||
nav_model: nav_bar::Model,
|
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>) {
|
fn init(core: Core, flags: Self::Flags) -> (Self, Task<Self::Message>) {
|
||||||
let mut app = SettingsApp {
|
let mut app = SettingsApp {
|
||||||
active_page: page::Entity::default(),
|
active_page: page::Entity::default(),
|
||||||
|
loaded_pages: BTreeSet::new(),
|
||||||
config: Config::new(),
|
config: Config::new(),
|
||||||
core,
|
core,
|
||||||
nav_model: nav_bar::Model::default(),
|
nav_model: nav_bar::Model::default(),
|
||||||
|
|
@ -337,7 +340,7 @@ impl cosmic::Application for SettingsApp {
|
||||||
Message::SetWindowTitle => return self.set_title(),
|
Message::SetWindowTitle => return self.set_title(),
|
||||||
|
|
||||||
Message::SearchChanged(phrase) => {
|
Message::SearchChanged(phrase) => {
|
||||||
self.search_changed(phrase);
|
return self.search_changed(phrase);
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::SearchActivate => {
|
Message::SearchActivate => {
|
||||||
|
|
@ -781,6 +784,7 @@ impl SettingsApp {
|
||||||
let mut leave_task = iced::Task::none();
|
let mut leave_task = iced::Task::none();
|
||||||
|
|
||||||
if current_page != page {
|
if current_page != page {
|
||||||
|
self.loaded_pages.remove(¤t_page);
|
||||||
leave_task = self
|
leave_task = self
|
||||||
.pages
|
.pages
|
||||||
.on_leave(current_page)
|
.on_leave(current_page)
|
||||||
|
|
@ -801,6 +805,8 @@ impl SettingsApp {
|
||||||
.clone()
|
.clone()
|
||||||
.expect("sender should be available");
|
.expect("sender should be available");
|
||||||
|
|
||||||
|
self.loaded_pages.insert(page);
|
||||||
|
|
||||||
let page_task = self
|
let page_task = self
|
||||||
.pages
|
.pages
|
||||||
.on_enter(page, sender)
|
.on_enter(page, sender)
|
||||||
|
|
@ -920,13 +926,15 @@ impl SettingsApp {
|
||||||
.into()
|
.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 the text was cleared, clear the search results too.
|
||||||
if phrase.is_empty() {
|
if phrase.is_empty() {
|
||||||
self.search_clear();
|
self.search_clear();
|
||||||
return;
|
return Task::none();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut tasks = Vec::new();
|
||||||
|
|
||||||
// Create a case-insensitive regular expression for the search function.
|
// Create a case-insensitive regular expression for the search function.
|
||||||
let search_expression = regex::RegexBuilder::new(&phrase)
|
let search_expression = regex::RegexBuilder::new(&phrase)
|
||||||
.case_insensitive(true)
|
.case_insensitive(true)
|
||||||
|
|
@ -942,10 +950,51 @@ impl SettingsApp {
|
||||||
// Use the results if results were found.
|
// Use the results if results were found.
|
||||||
if !results.is_empty() {
|
if !results.is_empty() {
|
||||||
self.search_selections = results;
|
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;
|
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.
|
/// Clears the search results so that the search page will not be shown.
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ enum ContextView {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Page {
|
pub struct Page {
|
||||||
|
on_enter_handle: Option<cosmic::iced::task::Handle>,
|
||||||
can_reset: bool,
|
can_reset: bool,
|
||||||
no_custom_window_hint: bool,
|
no_custom_window_hint: bool,
|
||||||
context_view: Option<ContextView>,
|
context_view: Option<ContextView>,
|
||||||
|
|
@ -152,6 +153,7 @@ impl
|
||||||
});
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
on_enter_handle: None,
|
||||||
can_reset: if theme_mode.is_dark {
|
can_reset: if theme_mode.is_dark {
|
||||||
theme_builder == ThemeBuilder::dark()
|
theme_builder == ThemeBuilder::dark()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1431,7 +1433,7 @@ impl page::Page<crate::pages::Message> for Page {
|
||||||
_: page::Entity,
|
_: page::Entity,
|
||||||
_sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
|
_sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
|
||||||
) -> Task<crate::pages::Message> {
|
) -> Task<crate::pages::Message> {
|
||||||
cosmic::command::batch(vec![
|
let (task, handle) = cosmic::command::batch(vec![
|
||||||
// Load icon themes
|
// Load icon themes
|
||||||
cosmic::command::future(icon_themes::fetch()).map(crate::pages::Message::Appearance),
|
cosmic::command::future(icon_themes::fetch()).map(crate::pages::Message::Appearance),
|
||||||
// Load font families
|
// Load font families
|
||||||
|
|
@ -1441,9 +1443,17 @@ impl page::Page<crate::pages::Message> for Page {
|
||||||
})
|
})
|
||||||
.map(crate::pages::Message::Appearance),
|
.map(crate::pages::Message::Appearance),
|
||||||
])
|
])
|
||||||
|
.abortable();
|
||||||
|
|
||||||
|
self.on_enter_handle = Some(handle);
|
||||||
|
task
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_leave(&mut self) -> Task<crate::pages::Message> {
|
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))
|
cosmic::command::message(crate::pages::Message::Appearance(Message::Left))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,9 @@ enum ContextView {
|
||||||
|
|
||||||
/// The page struct for the wallpaper view.
|
/// The page struct for the wallpaper view.
|
||||||
pub struct Page {
|
pub struct Page {
|
||||||
|
/// Abort handle to the on_enter task.
|
||||||
|
on_enter_handle: Option<cosmic::iced::task::Handle>,
|
||||||
|
|
||||||
/// Whether to show a context drawer.
|
/// Whether to show a context drawer.
|
||||||
context_view: Option<ContextView>,
|
context_view: Option<ContextView>,
|
||||||
|
|
||||||
|
|
@ -207,11 +210,16 @@ impl page::Page<crate::pages::Message> for Page {
|
||||||
_page: page::Entity,
|
_page: page::Entity,
|
||||||
_sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
|
_sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
|
||||||
) -> Task<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 current_folder = self.config.current_folder().to_owned();
|
||||||
|
|
||||||
let recurse = self.categories.selected == Some(Category::Wallpapers);
|
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 (service_config, displays) = wallpaper::config().await;
|
||||||
|
|
||||||
let mut selection = change_folder(current_folder, recurse).await;
|
let mut selection = change_folder(current_folder, recurse).await;
|
||||||
|
|
@ -245,6 +253,19 @@ impl page::Page<crate::pages::Message> for Page {
|
||||||
selection,
|
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>> {
|
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 {
|
impl Default for Page {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let mut page = Page {
|
let mut page = Page {
|
||||||
|
on_enter_handle: None,
|
||||||
context_view: None,
|
context_view: None,
|
||||||
show_tab_bar: false,
|
show_tab_bar: false,
|
||||||
active_output: None,
|
active_output: None,
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,6 @@ pub struct Page {
|
||||||
dialog: Option<VpnDialog>,
|
dialog: Option<VpnDialog>,
|
||||||
view_more_popup: Option<ConnectionId>,
|
view_more_popup: Option<ConnectionId>,
|
||||||
known_connections: IndexMap<UUID, ConnectionSettings>,
|
known_connections: IndexMap<UUID, ConnectionSettings>,
|
||||||
wireguard_connections: IndexMap<UUID, String>,
|
|
||||||
/// Withhold device update if the view more popup is shown.
|
/// Withhold device update if the view more popup is shown.
|
||||||
withheld_devices: Option<Vec<network_manager::devices::DeviceInfo>>,
|
withheld_devices: Option<Vec<network_manager::devices::DeviceInfo>>,
|
||||||
/// Withhold active connections update if the view more popup is shown.
|
/// Withhold active connections update if the view more popup is shown.
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ use slotmap::SlotMap;
|
||||||
pub struct Page {
|
pub struct Page {
|
||||||
battery: Battery,
|
battery: Battery,
|
||||||
connected_devices: Vec<ConnectedDevice>,
|
connected_devices: Vec<ConnectedDevice>,
|
||||||
|
on_enter_handle: Option<cosmic::iced::task::Handle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl page::Page<crate::pages::Message> for Page {
|
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ pub enum Message {
|
||||||
pub struct Page {
|
pub struct Page {
|
||||||
editing_device_name: bool,
|
editing_device_name: bool,
|
||||||
info: Info,
|
info: Info,
|
||||||
|
on_enter_handle: Option<cosmic::iced::task::Handle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl page::AutoBind<crate::pages::Message> for Page {}
|
impl page::AutoBind<crate::pages::Message> for Page {}
|
||||||
|
|
@ -48,9 +49,21 @@ impl page::Page<crate::pages::Message> for Page {
|
||||||
_page: page::Entity,
|
_page: page::Entity,
|
||||||
_sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
|
_sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
|
||||||
) -> Task<crate::pages::Message> {
|
) -> Task<crate::pages::Message> {
|
||||||
Task::future(
|
let (task, handle) = Task::future(async move {
|
||||||
async move { crate::pages::Message::About(Message::Info(Box::new(Info::load()))) },
|
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue