From 162ff02b1205784689e2dd5d2104f06eb2e528ef Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Wed, 31 May 2023 05:13:56 +0200 Subject: [PATCH] feat(wallpaper): per-display backgrounds, slideshow, rotation frequency --- app/src/pages/desktop/mod.rs | 48 -------- app/src/pages/desktop/wallpaper.rs | 184 ++++++++++++++++++++++++----- i18n/en/cosmic_settings.ftl | 7 ++ 3 files changed, 163 insertions(+), 76 deletions(-) diff --git a/app/src/pages/desktop/mod.rs b/app/src/pages/desktop/mod.rs index 9f7100f..b60a6f5 100644 --- a/app/src/pages/desktop/mod.rs +++ b/app/src/pages/desktop/mod.rs @@ -57,51 +57,3 @@ impl Page { } } } - -// impl From for Message { -// fn from(page: page::Info) -> Message { -// Message::page::Info(page) -// } -// } - -// pub enum Output { -// page::Info(page::Info), -// } - -// impl Subpage::Info for Desktoppage::Info { -// //TODO: translate -// fn title(&self) -> &'static str { -// use Desktoppage::Info::*; -// match self { -// Workspaces => "Workspaces", -// Notifications => "Notifications", -// } -// } - -// //TODO: translate -// fn description(&self) -> &'static str { -// use Desktoppage::Info::*; -// match self { -// Workspaces => "Set workspace number, behavior, and placement.", -// Notifications => { -// "Do Not Disturb, lockscreen notifications, and per-application settings." -// } -// } -// } - -// fn icon_name(&self) -> &'static str { -// use Desktoppage::Info::*; -// match self { -// Workspaces => "preferences-pop-desktop-workspaces-symbolic", -// Notifications => "preferences-system-notifications-symbolic", -// } -// } - -// fn parent_page(&self) -> page::Info { -// page::Info::Desktop(None) -// } - -// fn into_page(self) -> page::Info { -// page::Info::Desktop(Some(self)) -// } -// } diff --git a/app/src/pages/desktop/wallpaper.rs b/app/src/pages/desktop/wallpaper.rs index fa500cb..90d7d2a 100644 --- a/app/src/pages/desktop/wallpaper.rs +++ b/app/src/pages/desktop/wallpaper.rs @@ -9,7 +9,11 @@ use cosmic::{ iced::widget::{column, row}, iced::Length, iced_runtime::core::image::Handle as ImageHandle, - widget::{list_column, settings, toggler}, + widget::{ + list_column, + segmented_button::{self, SingleSelectModel}, + settings, toggler, + }, Element, }; use cosmic_settings_desktop::wallpaper::{self, Entry, Output, ScalingMode}; @@ -20,6 +24,8 @@ use slotmap::{DefaultKey, SecondaryMap, SlotMap}; #[derive(Clone, Debug)] pub enum Message { Fit(String), + Output(segmented_button::Entity), + RotationFrequency(String), SameBackground(bool), Select(DefaultKey), Slideshow(bool), @@ -27,27 +33,51 @@ pub enum Message { } pub struct Page { + pub active_output: Option, pub config: wallpaper::Config, + pub current_directory: PathBuf, pub fit_options: Vec, - pub outputs: HashMap, + pub outputs: SingleSelectModel, + pub rotation_frequency: u64, + pub rotation_options: Vec, pub same_background: bool, - pub selected_fit: u32, + pub selected_fit: usize, + pub selected_rotation: usize, pub selection: Context, pub slideshow: bool, } -const FIT: u32 = 0; -const STRETCH: u32 = 1; -const ZOOM: u32 = 2; +const FIT: usize = 0; +const STRETCH: usize = 1; +const ZOOM: usize = 2; + +const MINUTES_5: usize = 0; +const MINUTES_10: usize = 1; +const MINUTES_15: usize = 2; +const MINUTES_30: usize = 3; +const HOUR_1: usize = 4; +const HOUR_2: usize = 5; impl Default for Page { fn default() -> Self { Page { + active_output: None, config: wallpaper::Config::default(), + current_directory: PathBuf::from("/usr/share/backgrounds/pop/"), fit_options: vec![fl!("fit-to-screen"), fl!("stretch"), fl!("zoom")], - outputs: HashMap::new(), + outputs: SingleSelectModel::default(), + rotation_frequency: 300, + rotation_options: vec![ + fl!("x-minutes", number = 5), + fl!("x-minutes", number = 10), + fl!("x-minutes", number = 15), + fl!("x-minutes", number = 30), + fl!("x-hours", number = 1), + fl!("x-hours", number = 2), + ], same_background: true, selected_fit: 0, + selected_rotation: 0, selection: Context::default(), slideshow: false, } @@ -62,18 +92,38 @@ pub struct Context { } impl Page { + /// Applies the current settings to cosmic-bg. pub fn apply(&mut self) { - let Some(path) = self.selection.paths.get(self.selection.active) else { - return + let path = if self.slideshow { + &self.current_directory + } else if let Some(path) = self.selection.paths.get(self.selection.active) { + path + } else { + return; }; - let mut entry = Entry::new(Output::All, path.clone()); + let output = if self.same_background { + Output::All + } else if let Some(name) = self.outputs.active_data::() { + Output::Name(name.clone()) + } else { + return; + }; - match self.selected_fit { - FIT => entry.scaling_mode = ScalingMode::Fit([0.0, 0.0, 0.0]), - STRETCH => entry.scaling_mode = ScalingMode::Stretch, - ZOOM => entry.scaling_mode = ScalingMode::Zoom, - _ => (), + let scaling_mode = match self.selected_fit { + FIT => ScalingMode::Fit([0.0, 0.0, 0.0]), + STRETCH => ScalingMode::Stretch, + ZOOM => ScalingMode::Zoom, + _ => return, + }; + + let entry = Entry::new(output.clone(), path.clone()) + .scaling_mode(scaling_mode) + .rotation_frequency(self.rotation_frequency); + + if output != Output::All { + self.config.backgrounds.clear(); + self.config.outputs.clear(); } wallpaper::set(&mut self.config, entry); @@ -87,9 +137,33 @@ impl Page { .iter() .enumerate() .find(|(_, key)| **key == option) - .map_or(0, |(indice, _)| indice as u32); + .map_or(0, |(indice, _)| indice); + } - self.apply(); + Message::Output(id) => { + self.outputs.activate(id); + if let Some(name) = self.outputs.data::(id) { + self.active_output = Some(name.clone()); + } + } + + Message::RotationFrequency(option) => { + self.selected_rotation = self + .fit_options + .iter() + .enumerate() + .find(|(_, key)| **key == option) + .map_or(0, |(indice, _)| indice); + + self.rotation_frequency = match self.selected_rotation { + MINUTES_5 => 300, + MINUTES_10 => 600, + MINUTES_15 => 900, + MINUTES_30 => 1800, + HOUR_1 => 3600, + HOUR_2 => 7200, + _ => 10800, + }; } Message::SameBackground(value) => { @@ -98,15 +172,35 @@ impl Page { Message::Select(id) => { self.selection.active = id; - self.apply(); } - Message::Slideshow(value) => self.slideshow = value, + Message::Slideshow(value) => { + self.slideshow = value; + } Message::Update((config, outputs, selection)) => { self.config = config; self.selection = selection; - self.outputs = outputs; + self.outputs.clear(); + + { + let mut first = None; + for (name, model) in outputs { + let entity = self + .outputs + .insert() + .text(format!("{model} ({name})")) + .data(name); + + if first.is_none() { + first = Some(entity.id()); + } + } + + if let Some(id) = first { + self.outputs.activate(id); + } + } if let Some(entry) = self .config @@ -118,16 +212,32 @@ impl Page { for (entity, path) in self.selection.paths.iter() { if path == &entry.source { self.selection.active = entity; + match entry.scaling_mode { ScalingMode::Fit(_) => self.selected_fit = FIT, ScalingMode::Stretch => self.selected_fit = STRETCH, ScalingMode::Zoom => self.selected_fit = ZOOM, } + + self.slideshow = path.is_dir(); + + match entry.rotation_frequency { + 600 => self.selected_rotation = MINUTES_10, + 900 => self.selected_rotation = MINUTES_15, + 1800 => self.selected_rotation = MINUTES_30, + 3600 => self.selected_rotation = HOUR_1, + 7200 => self.selected_rotation = HOUR_2, + _ => self.selected_rotation = MINUTES_5, + } + + self.rotation_frequency = entry.rotation_frequency; } } } } } + + self.apply(); } } @@ -208,15 +318,19 @@ pub fn settings() -> Section { children.push(crate::widget::display_container(image.clone(), 300.0)); } - children.push( + children.push(if page.same_background { cosmic::widget::text("All Displays") .horizontal_alignment(Horizontal::Center) .width(Length::Fill) .apply(cosmic::iced::widget::container) .width(Length::Fill) .padding([0, 0, 16, 0]) - .into(), - ); + .into() + } else { + cosmic::widget::horiontal_view_switcher(&page.outputs) + .on_activate(Message::Output) + .into() + }); let background_fit = cosmic::iced::widget::pick_list( &page.fit_options, @@ -224,8 +338,8 @@ pub fn settings() -> Section { Message::Fit, ); - children.push( - list_column() + children.push({ + let column = list_column() .add(settings::item( &descriptions[0], toggler(None, page.same_background, Message::SameBackground), @@ -234,9 +348,23 @@ pub fn settings() -> Section { .add(settings::item( &descriptions[2], toggler(None, page.slideshow, Message::Slideshow), - )) - .into(), - ); + )); + + if page.slideshow { + column + .add(settings::item( + &descriptions[3], + cosmic::iced::widget::pick_list( + &page.rotation_options, + page.rotation_options.get(page.selected_rotation).cloned(), + Message::RotationFrequency, + ), + )) + .into() + } else { + column.into() + } + }); children.push(column(image_column).spacing(12).padding(0).into()); diff --git a/i18n/en/cosmic_settings.ftl b/i18n/en/cosmic_settings.ftl index 7c79fd0..099abb8 100644 --- a/i18n/en/cosmic_settings.ftl +++ b/i18n/en/cosmic_settings.ftl @@ -99,6 +99,13 @@ fit-to-screen = Fit to Screen stretch = Stretch zoom = Zoom +x-minutes = { $number } minutes + +x-hours = { $number -> + [1] 1 hour + *[other] { $number } hours +} + ## Desktop: Workspaces workspaces = Workspaces