diff --git a/Cargo.toml b/Cargo.toml index 19cb5532..d9265d42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,7 @@ desktop = [ "process", "dep:freedesktop-desktop-entry", "dep:mime", - "dep:open", + "dep:shlex", "tokio?/io-util", "tokio?/net", @@ -99,7 +99,6 @@ image = { version = "0.25.1", optional = true } lazy_static = "1.4.0" libc = { version = "0.2.155", optional = true } mime = { version = "0.3.17", optional = true } -open = { version = "5.3.0", optional = true } palette = "0.7.3" rfd = { version = "0.14.0", optional = true } rustix = { version = "0.38.34", features = [ diff --git a/examples/about/Cargo.toml b/examples/about/Cargo.toml index 4dd3aed9..76a35791 100644 --- a/examples/about/Cargo.toml +++ b/examples/about/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" tracing = "0.1.37" tracing-subscriber = "0.3.17" tracing-log = "0.2.0" +open = "5.3.0" [dependencies.libcosmic] path = "../../" diff --git a/examples/about/src/main.rs b/examples/about/src/main.rs index ed061f61..1e299444 100644 --- a/examples/about/src/main.rs +++ b/examples/about/src/main.rs @@ -3,10 +3,10 @@ //! Application API example -use cosmic::app::{about::About, Core, Settings, Task}; +use cosmic::app::{Core, Settings, Task}; use cosmic::iced::widget::column; use cosmic::iced_core::Size; -use cosmic::widget::{self, nav_bar}; +use cosmic::widget::{self, nav_bar, About}; use cosmic::{executor, iced, ApplicationExt, Element}; /// Runs application with these settings @@ -27,7 +27,7 @@ fn main() -> Result<(), Box> { #[derive(Clone, Debug)] pub enum Message { ToggleAbout, - Cosmic(cosmic::app::cosmic::Message), + Open(String), } /// The [`App`] stores application-specific state. @@ -100,11 +100,7 @@ impl cosmic::Application for App { return None; } - if let Some(abuot_view) = self.about_view() { - Some(abuot_view.map(Message::Cosmic)) - } else { - None - } + Some(widget::about(&self.about, Message::Open)) } /// Handle application events here. @@ -114,17 +110,14 @@ impl cosmic::Application for App { self.core.window.show_context = !self.core.window.show_context; self.core.set_show_context(self.core.window.show_context) } - Message::Cosmic(message) => { - return cosmic::command::message(cosmic::app::Message::Cosmic(message)) - } + Message::Open(url) => match open::that_detached(url) { + Ok(_) => (), + Err(err) => tracing::error!("Failed to open URL: {err}"), + }, } Task::none() } - fn about(&self) -> Option<&About> { - Some(&self.about) - } - /// Creates a view after each update. fn view(&self) -> Element { let centered = cosmic::widget::container( diff --git a/src/app/cosmic.rs b/src/app/cosmic.rs index 969209ce..a2b229e6 100644 --- a/src/app/cosmic.rs +++ b/src/app/cosmic.rs @@ -78,9 +78,6 @@ pub enum Message { /// Tracks updates to window suggested size. #[cfg(feature = "applet")] SuggestedBounds(Option), - #[cfg(feature = "desktop")] - /// Opens the provided URL. - OpenUrl(String), } #[derive(Default)] @@ -664,11 +661,6 @@ impl Cosmic { let core = self.app.core_mut(); core.applet.suggested_bounds = b; } - #[cfg(feature = "desktop")] - Message::OpenUrl(url) => match open::that_detached(url) { - Ok(_) => (), - Err(err) => tracing::error!("{err}"), - }, _ => {} } diff --git a/src/app/mod.rs b/src/app/mod.rs index 12940d7c..96a3f5f6 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -6,9 +6,6 @@ //! Check out our [application](https://github.com/pop-os/libcosmic/tree/master/examples/application) //! example in our repository. -#[cfg(feature = "desktop")] -pub mod about; - pub mod command; mod core; pub mod cosmic; @@ -76,7 +73,6 @@ use { #[cfg(feature = "desktop")] use { - crate::app::about::About, crate::widget, iced::{alignment::Vertical, Alignment}, std::collections::BTreeMap, @@ -602,90 +598,6 @@ where panic!("no view for window {id:?}"); } - #[cfg(feature = "desktop")] - /// Provides information about the application. - fn about(&self) -> Option<&About> { - None - } - - #[cfg(feature = "desktop")] - /// Constructs the view for the about section. - fn about_view<'a>(&'a self) -> Option> { - let about = self.about()?; - - let spacing = crate::theme::active().cosmic().spacing; - - let section = |list: &'a BTreeMap, title: &'a str| { - if list.is_empty() { - None - } else { - let developers: Vec> = list - .into_iter() - .map(|(name, url)| { - widget::button::custom( - widget::row() - .push(widget::text(name)) - .push(horizontal_space()) - .push(crate::widget::icon::from_name("link-symbolic").icon()) - .padding(spacing.space_xxs) - .align_y(Vertical::Center), - ) - .class(crate::theme::Button::Text) - .on_press(crate::app::cosmic::Message::OpenUrl(url.clone())) - .width(Length::Fill) - .into() - }) - .collect(); - Some(widget::settings::section().title(title).extend(developers)) - } - }; - - let application_name = about.application_name.as_ref().map(widget::text::title3); - let application_icon = about - .application_icon - .as_ref() - .map(|icon| crate::desktop::IconSource::Name(icon.clone()).as_cosmic_icon()); - - let links_section = section(&about.links, "Links"); - let developers_section = section(&about.developers, "Developers"); - let designers_section = section(&about.designers, "Designers"); - let artists_section = section(&about.artists, "Artists"); - let translators_section = section(&about.translators, "Translators"); - let documenters_section = section(&about.documenters, "Documenters"); - - let developer_name = about.developer_name.as_ref().map(widget::text); - let version = about.version.as_ref().map(widget::button::standard); - let license = about.license_type.as_ref().map(widget::button::standard); - let copyright = about.copyright.as_ref().map(widget::text::body); - let comments = about.comments.as_ref().map(widget::text::body); - - let about = widget::scrollable( - widget::column() - .push_maybe(application_icon) - .push_maybe(application_name) - .push_maybe(developer_name) - .push( - widget::row() - .push_maybe(version) - .push_maybe(license) - .spacing(spacing.space_xs), - ) - .push_maybe(links_section) - .push_maybe(developers_section) - .push_maybe(designers_section) - .push_maybe(artists_section) - .push_maybe(translators_section) - .push_maybe(documenters_section) - .push_maybe(comments) - .push_maybe(copyright) - .align_x(Alignment::Center) - .spacing(spacing.space_xs) - .width(Length::Fill), - ) - .into(); - Some(about) - } - /// Overrides the default style for applications fn style(&self) -> Option { None diff --git a/src/app/about.rs b/src/widget/about.rs similarity index 55% rename from src/app/about.rs rename to src/widget/about.rs index 5bb34225..9eacc1f9 100644 --- a/src/app/about.rs +++ b/src/widget/about.rs @@ -1,9 +1,17 @@ #[cfg(feature = "desktop")] -use std::collections::BTreeMap; +use { + crate::{ + iced::{alignment::Vertical, Alignment, Length}, + widget::{self, horizontal_space}, + Element, + }, + std::collections::BTreeMap, +}; #[cfg(feature = "desktop")] #[derive(Debug, Default, Clone, derive_setters::Setters)] #[setters(prefix = "set_", into, strip_option)] +/// Information about the application. pub struct About { /// The application's name. pub application_name: Option, @@ -117,3 +125,81 @@ impl About { self } } + +#[cfg(feature = "desktop")] +/// Constructs the widget for the about section. +pub fn about<'a, Message: Clone + 'static>( + about: &'a About, + on_url_press: impl Fn(String) -> Message, +) -> Element<'a, Message> { + let spacing = crate::theme::active().cosmic().spacing; + + let section = |list: &'a BTreeMap, title: &'a str| { + if list.is_empty() { + None + } else { + let developers: Vec> = list + .into_iter() + .map(|(name, url)| { + widget::button::custom( + widget::row() + .push(widget::text(name)) + .push(horizontal_space()) + .push(crate::widget::icon::from_name("link-symbolic").icon()) + .padding(spacing.space_xxs) + .align_y(Vertical::Center), + ) + .class(crate::theme::Button::Text) + .on_press(on_url_press(url.clone())) + .width(Length::Fill) + .into() + }) + .collect(); + Some(widget::settings::section().title(title).extend(developers)) + } + }; + + let application_name = about.application_name.as_ref().map(widget::text::title3); + let application_icon = about + .application_icon + .as_ref() + .map(|icon| crate::desktop::IconSource::Name(icon.clone()).as_cosmic_icon()); + + let links_section = section(&about.links, "Links"); + let developers_section = section(&about.developers, "Developers"); + let designers_section = section(&about.designers, "Designers"); + let artists_section = section(&about.artists, "Artists"); + let translators_section = section(&about.translators, "Translators"); + let documenters_section = section(&about.documenters, "Documenters"); + + let developer_name = about.developer_name.as_ref().map(widget::text); + let version = about.version.as_ref().map(widget::button::standard); + let license = about.license_type.as_ref().map(widget::button::standard); + let copyright = about.copyright.as_ref().map(widget::text::body); + let comments = about.comments.as_ref().map(widget::text::body); + + widget::scrollable( + widget::column() + .push_maybe(application_icon) + .push_maybe(application_name) + .push_maybe(developer_name) + .push( + widget::row() + .push_maybe(version) + .push_maybe(license) + .spacing(spacing.space_xs), + ) + .push_maybe(links_section) + .push_maybe(developers_section) + .push_maybe(designers_section) + .push_maybe(artists_section) + .push_maybe(translators_section) + .push_maybe(documenters_section) + .push_maybe(comments) + .push_maybe(copyright) + .align_x(Alignment::Center) + .spacing(spacing.space_xs) + .width(Length::Fill), + ) + .into() +} diff --git a/src/widget/mod.rs b/src/widget/mod.rs index 6c7fcd08..b9e2040b 100644 --- a/src/widget/mod.rs +++ b/src/widget/mod.rs @@ -365,3 +365,7 @@ pub use warning::*; #[cfg(feature = "markdown")] #[doc(inline)] pub use iced::widget::markdown; + +#[cfg(feature = "desktop")] +pub mod about; +pub use about::{about, About};