use crate::{ Apply, Element, fl, iced::{Alignment, Length}, widget::{self, list}, }; use std::rc::Rc; #[derive(Debug, Default, Clone, derive_setters::Setters)] #[setters(into, strip_option)] /// Information about the application. pub struct About { /// The application's name. name: Option, /// The application's icon name. icon: Option, /// The application's version. version: Option, /// Name of the application's author. author: Option, /// Comments about the application. comments: Option, /// The application's copyright. copyright: Option, /// The license name. license: Option, /// The license url. license_url: Option, /// Artists who contributed to the application. #[setters(skip)] artists: Vec<(String, String)>, /// Designers who contributed to the application. #[setters(skip)] designers: Vec<(String, String)>, /// Developers who contributed to the application. #[setters(skip)] developers: Vec<(String, String)>, /// Documenters who contributed to the application. #[setters(skip)] documenters: Vec<(String, String)>, /// Translators who contributed to the application. #[setters(skip)] translators: Vec<(String, String)>, /// Links associated with the application. #[setters(skip)] links: Vec<(String, String)>, } fn add_contributors(contributors: Vec<(&str, &str)>) -> Vec<(String, String)> { contributors .into_iter() .map(|(name, email)| (name.into(), format!("mailto:{email}"))) .collect() } impl<'a> About { /// Artists who contributed to the application. pub fn artists(mut self, contributors: impl Into>) -> Self { self.artists = add_contributors(contributors.into()); self } /// Designers who contributed to the application. pub fn designers(mut self, contributors: impl Into>) -> Self { self.designers = add_contributors(contributors.into()); self } /// Developers who contributed to the application. pub fn developers(mut self, contributors: impl Into>) -> Self { self.developers = add_contributors(contributors.into()); self } /// Documenters who contributed to the application. pub fn documenters(mut self, contributors: impl Into>) -> Self { self.documenters = add_contributors(contributors.into()); self } /// Translators who contributed to the application. pub fn translators(mut self, contributors: impl Into>) -> Self { self.translators = add_contributors(contributors.into()); self } /// Links associated with the application. pub fn links, V: Into>( mut self, links: impl IntoIterator, ) -> Self { self.links = links .into_iter() .map(|(name, url)| (name.into(), url.into())) .collect(); self } } /// Constructs the widget for the about section. pub fn about<'a, Message: Clone + 'static>( about: &'a About, on_url_press: impl Fn(&'a str) -> Message + 'a, ) -> Element<'a, Message> { let cosmic_theme::Spacing { space_xxs, space_m, .. } = crate::theme::spacing(); let svg_accent = Rc::new(|theme: &crate::Theme| widget::svg::Style { color: Some(theme.cosmic().accent_text_color().into()), }); let section_button = |name: &'a str, url: &'a str| -> list::ListButton<'a, Message> { widget::row::with_capacity(2) .push(widget::text::body(name).width(Length::Fill)) .push_maybe( (!url.is_empty()).then_some( widget::icon::from_name("link-symbolic") .icon() .class(crate::theme::Svg::Custom(svg_accent.clone())), ), ) .align_y(Alignment::Center) .apply(list::button) .on_press(on_url_press(url)) }; let section = |list: &'a Vec<(String, String)>, title: String| { (!list.is_empty()).then_some({ let items = list.iter().map(|(name, url)| section_button(name, url)); widget::settings::section().title(title).extend(items) }) }; let header_children: Vec> = [ about.icon.as_ref().map(|i| { i.clone() .icon() .size(256) .width(Length::Fixed(128.)) .height(Length::Fixed(128.)) .content_fit(iced::ContentFit::Contain) .into() }), about.name.as_ref().map(|n| widget::text::title3(n).into()), about.author.as_ref().map(|a| widget::text::body(a).into()), about.version.as_ref().map(|v| { widget::button::standard(v) .apply(widget::container) .padding([space_xxs, 0, 0, 0]) .into() }), ] .into_iter() .flatten() .collect(); let header = (!header_children.is_empty()) .then_some(widget::column::with_children(header_children).align_x(Alignment::Center)); let links_section = section(&about.links, fl!("links")); let developers_section = section(&about.developers, fl!("developers")); let designers_section = section(&about.designers, fl!("designers")); let artists_section = section(&about.artists, fl!("artists")); let translators_section = section(&about.translators, fl!("translators")); let documenters_section = section(&about.documenters, fl!("documenters")); let license_section = about.license.as_ref().map(|license| { let url = about.license_url.as_deref().unwrap_or_default(); widget::settings::section() .title(fl!("license")) .add(section_button(license, url)) }); let copyright = about.copyright.as_ref().map(widget::text::body); let comments = about.comments.as_ref().map(widget::text::body); widget::column::with_capacity(10) .push_maybe(header) .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(license_section) .push_maybe(comments) .push_maybe(copyright) .spacing(space_m) .width(Length::Fill) .align_x(Alignment::Center) .into() }