improv: create subscription channel for pages to send messages to

This commit is contained in:
Michael Aaron Murphy 2024-05-08 12:59:35 +02:00 committed by Michael Murphy
parent 11de7cbd4a
commit b443dd5b53
12 changed files with 104 additions and 26 deletions

1
Cargo.lock generated
View file

@ -1349,6 +1349,7 @@ dependencies = [
"once_cell", "once_cell",
"regex", "regex",
"slotmap", "slotmap",
"tokio",
"url", "url",
] ]

View file

@ -5,6 +5,8 @@ resolver = "2"
rust-version = "1.71.0" rust-version = "1.71.0"
sunrise_sunset = "1.0.1" sunrise_sunset = "1.0.1"
[workspace.dependencies]
tokio = { version = "1.37.0", features = ["macros"] }
[workspace.dependencies.libcosmic] [workspace.dependencies.libcosmic]
git = "https://github.com/pop-os/libcosmic" git = "https://github.com/pop-os/libcosmic"

View file

@ -6,7 +6,7 @@ license = "GPL-3.0"
rust-version = "1.65.0" rust-version = "1.65.0"
[dependencies] [dependencies]
tokio = { version = "1.35.1", features = ["macros"] } tokio.workspace = true
async-channel = "2.1.1" async-channel = "2.1.1"
color-eyre = "0.6.2" color-eyre = "0.6.2"
cosmic-bg-config = { workspace = true } cosmic-bg-config = { workspace = true }

View file

@ -6,16 +6,17 @@ use crate::pages::desktop::{
self, appearance, dock, self, appearance, dock,
panel::{ panel::{
self, self,
applets_inner::{self, AppletsPage, APPLET_DND_ICON_ID}, applets_inner::{self, APPLET_DND_ICON_ID},
inner as _panel, inner as _panel,
}, },
}; };
use crate::pages::input::{self, keyboard}; use crate::pages::input::{self};
use crate::pages::{self, display, networking, sound, system, time}; use crate::pages::{self, display, sound, system, time};
use crate::subscription::desktop_files; use crate::subscription::desktop_files;
use crate::widget::{page_title, search_header}; use crate::widget::{page_title, search_header};
use crate::PageCommands; use crate::PageCommands;
use cosmic::app::DbusActivationMessage; use cosmic::app::DbusActivationMessage;
use cosmic::iced::futures::SinkExt;
use cosmic::iced::Subscription; use cosmic::iced::Subscription;
use cosmic::widget::{button, row, text_input}; use cosmic::widget::{button, row, text_input};
use cosmic::{ use cosmic::{
@ -42,6 +43,7 @@ pub struct SettingsApp {
config: Config, config: Config,
core: Core, core: Core,
nav_model: nav_bar::Model, nav_model: nav_bar::Model,
page_sender: Option<tokio::sync::mpsc::Sender<crate::pages::Message>>,
pages: page::Binder<crate::pages::Message>, pages: page::Binder<crate::pages::Message>,
search_active: bool, search_active: bool,
search_id: cosmic::widget::Id, search_id: cosmic::widget::Id,
@ -77,19 +79,21 @@ impl SettingsApp {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Message { pub enum Message {
CloseContextDrawer,
DelayedInit(page::Entity),
DesktopInfo, DesktopInfo,
Error(String), Error(String),
OpenContextDrawer(Cow<'static, str>),
Page(page::Entity), Page(page::Entity),
PageMessage(crate::pages::Message), PageMessage(crate::pages::Message),
PanelConfig(CosmicPanelConfig), PanelConfig(CosmicPanelConfig),
RegisterSubscriptionSender(tokio::sync::mpsc::Sender<pages::Message>),
SearchActivate, SearchActivate,
SearchChanged(String), SearchChanged(String),
SearchClear, SearchClear,
SearchSubmit, SearchSubmit,
SetWindowTitle,
OpenContextDrawer(Cow<'static, str>),
CloseContextDrawer,
SetTheme(cosmic::theme::Theme), SetTheme(cosmic::theme::Theme),
SetWindowTitle,
} }
impl cosmic::Application for SettingsApp { impl cosmic::Application for SettingsApp {
@ -113,6 +117,7 @@ impl cosmic::Application for SettingsApp {
config: Config::new(), config: Config::new(),
core, core,
nav_model: nav_bar::Model::default(), nav_model: nav_bar::Model::default(),
page_sender: None,
pages: page::Binder::default(), pages: page::Binder::default(),
search_active: false, search_active: false,
search_id: cosmic::widget::Id::unique(), search_id: cosmic::widget::Id::unique(),
@ -136,9 +141,10 @@ impl cosmic::Application for SettingsApp {
} }
.unwrap_or(desktop_id); .unwrap_or(desktop_id);
let command = app.activate_page(active_id); (
app,
(app, command) cosmic::command::message(cosmic::app::message::app(Message::DelayedInit(active_id))),
)
} }
fn nav_model(&self) -> Option<&nav_bar::Model> { fn nav_model(&self) -> Option<&nav_bar::Model> {
@ -208,6 +214,23 @@ impl cosmic::Application for SettingsApp {
}); });
Subscription::batch(vec![ Subscription::batch(vec![
// Creates a channel that listens to messages from pages.
// The sender is given back to the application so that it may pass it on.
cosmic::iced::subscription::channel(
std::any::TypeId::of::<Self>(),
4,
move |mut output| async move {
let (tx, mut rx) = tokio::sync::mpsc::channel::<pages::Message>(4);
let _res = output.send(Message::RegisterSubscriptionSender(tx)).await;
while let Some(event) = rx.recv().await {
let _res = output.send(Message::PageMessage(event)).await;
}
futures::future::pending().await
},
),
crate::subscription::daytime().map(|daytime| { crate::subscription::daytime().map(|daytime| {
Message::PageMessage(pages::Message::Appearance(appearance::Message::Daytime( Message::PageMessage(pages::Message::Appearance(appearance::Message::Daytime(
daytime, daytime,
@ -409,6 +432,21 @@ impl cosmic::Application for SettingsApp {
Message::Error(error) => { Message::Error(error) => {
tracing::error!(error, "error occurred"); tracing::error!(error, "error occurred");
} }
Message::RegisterSubscriptionSender(sender) => {
self.page_sender = Some(sender);
}
// It is necessary to delay init to allow time for the page sender to be initialized
Message::DelayedInit(active_id) => {
if self.page_sender.is_none() {
return cosmic::command::message(cosmic::app::message::app(
Message::DelayedInit(active_id),
));
}
return self.activate_page(active_id);
}
} }
Command::none() Command::none()
@ -438,7 +476,7 @@ impl cosmic::Application for SettingsApp {
} else if let Some(sub_pages) = self.pages.sub_pages(self.active_page) { } else if let Some(sub_pages) = self.pages.sub_pages(self.active_page) {
self.sub_page_view(sub_pages) self.sub_page_view(sub_pages)
} else { } else {
panic!("page without sub-pages or content"); return row::row().into();
}; };
let padding = if self.core.is_condensed() { let padding = if self.core.is_condensed() {
@ -518,9 +556,14 @@ impl SettingsApp {
self.search_active = false; self.search_active = false;
self.activate_navbar(page); self.activate_navbar(page);
let sender = self
.page_sender
.clone()
.expect("sender should be available");
let page_command = self let page_command = self
.pages .pages
.page_reload(page) .on_enter(page, sender)
.map(Message::PageMessage) .map(Message::PageMessage)
.map(cosmic::app::Message::App); .map(cosmic::app::Message::App);

View file

@ -1087,7 +1087,11 @@ impl page::Page<crate::pages::Message> for Page {
.description(fl!("appearance", "desc")) .description(fl!("appearance", "desc"))
} }
fn reload(&mut self, _: page::Entity) -> Command<crate::pages::Message> { fn on_enter(
&mut self,
_: page::Entity,
sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
) -> Command<crate::pages::Message> {
command::future(fetch_icon_themes()).map(crate::pages::Message::Appearance) command::future(fetch_icon_themes()).map(crate::pages::Message::Appearance)
} }

View file

@ -207,7 +207,11 @@ impl page::Page<crate::pages::Message> for Page {
.description(fl!("wallpaper", "desc")) .description(fl!("wallpaper", "desc"))
} }
fn reload(&mut self, _page: page::Entity) -> Command<crate::pages::Message> { fn on_enter(
&mut self,
_page: page::Entity,
sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
) -> Command<crate::pages::Message> {
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);

View file

@ -219,12 +219,20 @@ impl page::Page<crate::pages::Message> for Page {
} }
#[cfg(not(feature = "test"))] #[cfg(not(feature = "test"))]
fn reload(&mut self, _page: page::Entity) -> Command<crate::pages::Message> { fn on_enter(
command::future(reload()) &mut self,
_page: page::Entity,
sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
) -> Command<crate::pages::Message> {
command::future(on_enter())
} }
#[cfg(feature = "test")] #[cfg(feature = "test")]
fn reload(&mut self, _page: page::Entity) -> Command<crate::pages::Message> { fn on_enter(
&mut self,
_page: page::Entity,
sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
) -> Command<crate::pages::Message> {
command::future(async move { command::future(async move {
let mut randr = List::default(); let mut randr = List::default();
@ -287,7 +295,7 @@ impl Page {
} }
return cosmic::command::future(async { return cosmic::command::future(async {
crate::Message::PageMessage(reload().await) crate::Message::PageMessage(on_enter().await)
}); });
} }
@ -805,7 +813,7 @@ fn cache_rates(cached_rates: &mut Vec<String>, rates: &[u32]) {
.collect(); .collect();
} }
pub async fn reload() -> crate::pages::Message { pub async fn on_enter() -> crate::pages::Message {
let graphics_fut = graphics::fetch(); let graphics_fut = graphics::fetch();
let randr_fut = cosmic_randr_shell::list(); let randr_fut = cosmic_randr_shell::list();
let (graphics, randr) = futures::future::zip(graphics_fut, randr_fut).await; let (graphics, randr) = futures::future::zip(graphics_fut, randr_fut).await;

View file

@ -250,7 +250,11 @@ impl page::Page<crate::pages::Message> for Page {
} }
} }
fn reload(&mut self, _page: page::Entity) -> Command<crate::pages::Message> { fn on_enter(
&mut self,
_page: page::Entity,
sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
) -> Command<crate::pages::Message> {
self.xkb = super::get_config(&self.config, "xkb_config"); self.xkb = super::get_config(&self.config, "xkb_config");
match xkb_data::keyboard_layouts() { match xkb_data::keyboard_layouts() {
Ok(keyboard_layouts) => { Ok(keyboard_layouts) => {

View file

@ -43,7 +43,11 @@ impl page::Page<crate::pages::Message> for Page {
.description(fl!("about", "desc")) .description(fl!("about", "desc"))
} }
fn reload(&mut self, _page: page::Entity) -> Command<crate::pages::Message> { fn on_enter(
&mut self,
_page: page::Entity,
sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
) -> Command<crate::pages::Message> {
command::future(async move { command::future(async move {
crate::pages::Message::About(Message::Info(Box::new(Info::load()))) crate::pages::Message::About(Message::Info(Box::new(Info::load())))
}) })

View file

@ -11,4 +11,5 @@ libcosmic = { workspace = true }
generator = "0.7.5" generator = "0.7.5"
downcast-rs = "1.2.0" downcast-rs = "1.2.0"
once_cell = "1.19.0" once_cell = "1.19.0"
url = "2.5.0" tokio.workspace = true
url = "2.5.0"

View file

@ -166,9 +166,13 @@ impl<Message: 'static> Binder<Message> {
} }
/// Calls a page's load function to refresh its data. /// Calls a page's load function to refresh its data.
pub fn page_reload(&mut self, id: crate::Entity) -> Command<Message> { pub fn on_enter(
&mut self,
id: crate::Entity,
sender: tokio::sync::mpsc::Sender<Message>,
) -> Command<Message> {
if let Some(page) = self.page.get_mut(id) { if let Some(page) = self.page.get_mut(id) {
return page.reload(id); return page.on_enter(id, sender);
} }
Command::none() Command::none()

View file

@ -59,9 +59,12 @@ pub trait Page<Message: 'static>: Downcast {
} }
/// Reload page metadata via a Command. /// Reload page metadata via a Command.
#[must_use]
#[allow(unused)] #[allow(unused)]
fn reload(&mut self, page: crate::Entity) -> Command<Message> { fn on_enter(
&mut self,
page: crate::Entity,
sender: tokio::sync::mpsc::Sender<Message>,
) -> Command<Message> {
Command::none() Command::none()
} }