fix(wallpaper): improvements and fixes

This commit is contained in:
Michael Aaron Murphy 2023-12-13 23:04:40 +01:00 • committed by Michael Murphy
parent 39eccde230
commit bf70b14cda
12 changed files with 261 additions and 168 deletions

View file

@ -34,20 +34,51 @@ impl Config {
} }
}; };
// Get the active background folder from cosmic-config.
if let Ok(path) = context.get::<Option<PathBuf>>(CURRENT_FOLDER) { if let Ok(path) = context.get::<Option<PathBuf>>(CURRENT_FOLDER) {
config.current_folder = path; // Set current folder if it exists.
config.current_folder = path.filter(|path| path.exists());
} }
// Get custom colors stored in cosmic-config.
if let Ok(colors) = context.get::<Vec<wallpaper::Color>>(CUSTOM_COLORS) { if let Ok(colors) = context.get::<Vec<wallpaper::Color>>(CUSTOM_COLORS) {
config.custom_colors = colors; config.custom_colors = colors;
} }
// Get custom background images stored in cosmic-config.
if let Ok(images) = context.get::<Vec<PathBuf>>(CUSTOM_IMAGES) { if let Ok(images) = context.get::<Vec<PathBuf>>(CUSTOM_IMAGES) {
config.custom_images = images; // Update config if images are missing.
let mut update_config = false;
for image in images {
if image.exists() {
config.custom_images.push(image);
} else {
update_config = true;
}
}
if update_config {
let _res = config.update_custom_images();
}
} }
if let Ok(folders) = dbg!(context.get::<VecDeque<PathBuf>>(RECENT_FOLDERS)) { // Get recently-added background folders from cosmic-config.
config.recent_folders = folders; if let Ok(folders) = context.get::<VecDeque<PathBuf>>(RECENT_FOLDERS) {
// Update config if folders are missing
let mut update_config = false;
for folder in folders {
if folder.exists() {
config.recent_folders.push_back(folder);
} else {
update_config = true;
}
}
if update_config {
let _res = config.update_recent_folders();
}
} }
config.context = Some(context); config.context = Some(context);

View file

@ -13,8 +13,15 @@ use std::{
}; };
use apply::Apply; use apply::Apply;
use cosmic::iced::{wayland::actions::window::SctkWindowSettings, window, Color, Length}; use cosmic::{
use cosmic::iced_sctk::commands::window::{close_window, get_window}; iced::{wayland::actions::window::SctkWindowSettings, window, Color, Length},
prelude::CollectionWidget,
};
use cosmic::{
iced_core::Alignment,
iced_sctk::commands::window::{close_window, get_window},
widget::icon,
};
use cosmic::{ use cosmic::{
iced_core::{alignment, layout}, iced_core::{alignment, layout},
iced_runtime::core::image::Handle as ImageHandle, iced_runtime::core::image::Handle as ImageHandle,
@ -57,35 +64,44 @@ pub type Image = ImageBuffer<Rgba<u8>, Vec<u8>>;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Message { pub enum Message {
ChangeCategory(Category),
ChangeFolder(Context), ChangeFolder(Context),
ColorAddDialog, ColorAddDialog,
ColorDialogUpdate(ColorPickerUpdate), ColorDialogUpdate(ColorPickerUpdate),
ColorRemove(wallpaper::Color), ColorRemove(wallpaper::Color),
ChangeCategory(Category),
ColorSelect(wallpaper::Color), ColorSelect(wallpaper::Color),
DragColorDialog, DragColorDialog,
Fit(usize), Fit(usize),
ImageAdd(Option<Arc<(PathBuf, Image, Image)>>), ImageAdd(Option<Arc<(PathBuf, Image, Image)>>),
ImageAddDialog, ImageAddDialog,
ImageRemove(DefaultKey), ImageRemove(DefaultKey),
Init(Box<(wallpaper::Config, HashMap<String, String>, Context)>),
Output(segmented_button::Entity), Output(segmented_button::Entity),
RotationFrequency(usize), RotationFrequency(usize),
SameBackground(bool), SameWallpaper(bool),
Select(DefaultKey), Select(DefaultKey),
Slideshow(bool), Slideshow(bool),
Init(Box<(wallpaper::Config, HashMap<String, String>, Context)>),
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Category { pub enum Category {
Backgrounds, AddFolder,
Colors, Colors,
RecentFolder(usize), RecentFolder(usize),
Wallpapers,
}
#[derive(Copy, Clone)]
enum ActiveDialog {
AddFolder,
AddImage,
None,
} }
pub struct Page { pub struct Page {
pub active_output: Option<String>, pub active_output: Option<String>,
pub background_service_config: wallpaper::Config, active_dialog: ActiveDialog,
pub wallpaper_service_config: wallpaper::Config,
pub cached_display_handle: Option<ImageHandle>, pub cached_display_handle: Option<ImageHandle>,
pub categories: dropdown::multi::Model<String, Category>, pub categories: dropdown::multi::Model<String, Category>,
pub color_dialog: window::Id, pub color_dialog: window::Id,
@ -116,21 +132,41 @@ impl page::Page<crate::pages::Message> for Page {
} }
fn file_chooser(&mut self, selections: Vec<url::Url>) -> Command<crate::pages::Message> { fn file_chooser(&mut self, selections: Vec<url::Url>) -> Command<crate::pages::Message> {
let active_dialog = self.active_dialog;
self.active_dialog = ActiveDialog::None;
if let Some(selection) = selections.first() { if let Some(selection) = selections.first() {
let path = PathBuf::from(selection.path()); let path = PathBuf::from(selection.path());
if path.is_dir() { match active_dialog {
self.add_recent_folder(path); ActiveDialog::AddFolder => {
} else { if path.is_dir() {
if let Some(parent) = path.parent() { self.add_recent_folder(path.clone());
self.add_recent_folder(parent.to_owned());
let _res = self.config.set_current_folder(Some(path.clone()));
return cosmic::command::future(async move {
crate::pages::Message::DesktopWallpaper(Message::ChangeFolder(
change_folder(path).await,
))
});
}
} }
return cosmic::command::future(async move { ActiveDialog::AddImage => {
let result = wallpaper::load_image_with_thumbnail(&mut Vec::new(), path).await; if path.is_file() {
return cosmic::command::future(async move {
let result =
wallpaper::load_image_with_thumbnail(&mut Vec::new(), path).await;
crate::pages::Message::DesktopWallpaper(Message::ImageAdd(result.map(Arc::new))) crate::pages::Message::DesktopWallpaper(Message::ImageAdd(
}); result.map(Arc::new),
))
});
}
}
ActiveDialog::None => (),
} }
} }
@ -141,12 +177,12 @@ impl page::Page<crate::pages::Message> for Page {
let current_folder = self.config.current_folder().to_owned(); let current_folder = self.config.current_folder().to_owned();
Some(Box::pin(async move { Some(Box::pin(async move {
let (background_service_config, outputs) = wallpaper::config(); let (wallpaper_service_config, outputs) = wallpaper::config();
let update = change_folder(current_folder).await; let update = change_folder(current_folder).await;
crate::pages::Message::DesktopWallpaper(Message::Init(Box::new(( crate::pages::Message::DesktopWallpaper(Message::Init(Box::new((
background_service_config, wallpaper_service_config,
outputs, outputs,
update, update,
)))) ))))
@ -159,6 +195,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 {
active_dialog: ActiveDialog::None,
active_output: None, active_output: None,
cached_display_handle: None, cached_display_handle: None,
categories: { categories: {
@ -166,7 +203,7 @@ impl Default for Page {
categories.insert(dropdown::multi::list( categories.insert(dropdown::multi::list(
None, None,
vec![(fl!("system-backgrounds"), Category::Backgrounds)], vec![(fl!("wallpaper", "plural"), Category::Wallpapers)],
)); ));
categories.insert(dropdown::multi::list( categories.insert(dropdown::multi::list(
@ -174,16 +211,21 @@ impl Default for Page {
vec![(fl!("colors"), Category::Colors)], vec![(fl!("colors"), Category::Colors)],
)); ));
categories.insert(dropdown::multi::list(
None,
vec![(fl!("open-new-folder"), Category::AddFolder)],
));
categories.insert(dropdown::multi::list( categories.insert(dropdown::multi::list(
Some(fl!("recent-folders")), Some(fl!("recent-folders")),
Vec::with_capacity(5), Vec::with_capacity(5),
)); ));
categories.selected = Some(Category::Backgrounds); categories.selected = Some(Category::Wallpapers);
categories categories
}, },
background_service_config: wallpaper::Config::default(), wallpaper_service_config: wallpaper::Config::default(),
color_dialog: window::Id::unique(), color_dialog: window::Id::unique(),
color_model: ColorPickerModel::new(fl!("hex"), fl!("rgb"), None, Some(Color::WHITE)), color_model: ColorPickerModel::new(fl!("hex"), fl!("rgb"), None, Some(Color::WHITE)),
config: Config::new(), config: Config::new(),
@ -233,7 +275,7 @@ impl Page {
} }
fn assign_recent_folders(&mut self) { fn assign_recent_folders(&mut self) {
let recent_list = &mut self.categories.lists[2]; let recent_list = &mut self.categories.lists[3];
recent_list.options.clear(); recent_list.options.clear();
for (id, folder) in self.config.recent_folders().iter().enumerate() { for (id, folder) in self.config.recent_folders().iter().enumerate() {
@ -248,7 +290,7 @@ impl Page {
fn cache_display_image(&mut self) { fn cache_display_image(&mut self) {
let choice = match self.selection.active { let choice = match self.selection.active {
Choice::Background(id) => self.selection.display_images.get(id), Choice::Wallpaper(id) => self.selection.display_images.get(id),
Choice::Slideshow => self.selection.display_images.values().next(), Choice::Slideshow => self.selection.display_images.values().next(),
@ -312,7 +354,7 @@ impl Page {
} }
fn config_output(&self) -> Option<String> { fn config_output(&self) -> Option<String> {
if self.background_service_config.same_on_all { if self.wallpaper_service_config.same_on_all {
Some(String::from("all")) Some(String::from("all"))
} else { } else {
self.outputs.active_data::<String>().cloned() self.outputs.active_data::<String>().cloned()
@ -325,13 +367,13 @@ impl Page {
return; return;
}; };
if self.background_service_config.same_on_all { if self.wallpaper_service_config.same_on_all {
self.background_service_config.outputs.clear(); self.wallpaper_service_config.outputs.clear();
} }
let entry = match self.selection.active { let entry = match self.selection.active {
Choice::Slideshow => { Choice::Slideshow => {
match self.config_background_entry( match self.config_wallpaper_entry(
output.clone(), output.clone(),
self.config.current_folder().to_path_buf(), self.config.current_folder().to_path_buf(),
) { ) {
@ -339,9 +381,10 @@ impl Page {
None => return, None => return,
} }
} }
Choice::Background(key) => {
Choice::Wallpaper(key) => {
if let Some(path) = self.selection.paths.get(key) { if let Some(path) = self.selection.paths.get(key) {
match self.config_background_entry(output.clone(), path.clone()) { match self.config_wallpaper_entry(output.clone(), path.clone()) {
Some(entry) => entry, Some(entry) => entry,
None => return, None => return,
} }
@ -356,30 +399,30 @@ impl Page {
}; };
if output != "all" { if output != "all" {
self.background_service_config.backgrounds.clear(); self.wallpaper_service_config.backgrounds.clear();
self.background_service_config.outputs.clear(); self.wallpaper_service_config.outputs.clear();
} }
wallpaper::set(&mut self.background_service_config, entry); wallpaper::set(&mut self.wallpaper_service_config, entry);
} }
/// Locate the ID of a background that's already stored in memory /// Locate the ID of a wallpaper that's already stored in memory
fn background_id_from_path(&self, path: &Path) -> Option<DefaultKey> { fn wallpaper_id_from_path(&self, path: &Path) -> Option<DefaultKey> {
self.selection self.selection
.paths .paths
.iter() .iter()
.find(|(_id, background)| *background == path) .find(|(_id, wallpaper)| *wallpaper == path)
.map(|(id, _)| id) .map(|(id, _)| id)
} }
/// Updates configuration from the background service. /// Updates configuration from the wallpaper service.
fn background_service_config_update( fn wallpaper_service_config_update(
&mut self, &mut self,
background_service_config: wallpaper::Config, wallpaper_service_config: wallpaper::Config,
displays: HashMap<String, String>, displays: HashMap<String, String>,
selection: Context, selection: Context,
) { ) {
self.background_service_config = background_service_config; self.wallpaper_service_config = wallpaper_service_config;
self.selection = selection; self.selection = selection;
self.outputs.clear(); self.outputs.clear();
@ -400,43 +443,30 @@ impl Page {
self.outputs.activate(id); self.outputs.activate(id);
} }
if self.background_service_config.same_on_all if self.wallpaper_service_config.same_on_all
|| self.background_service_config.backgrounds.is_empty() || self.wallpaper_service_config.backgrounds.is_empty()
{ {
let entry = self.background_service_config.default_background.clone(); let entry = self.wallpaper_service_config.default_background.clone();
self.select_background_entry(&entry); self.select_wallpaper_entry(&entry);
if let Some(current) = entry_directory(self.config.current_folder(), &entry) {
if let Err(why) = self.config.set_current_folder(Some(current)) {
tracing::error!(?why, "cannot set current folder");
}
}
} else if let Some(data) = self.outputs.active_data::<String>() { } else if let Some(data) = self.outputs.active_data::<String>() {
let mut backgrounds = Vec::new(); let mut wallpapers = Vec::new();
std::mem::swap( std::mem::swap(
&mut self.background_service_config.backgrounds, &mut self.wallpaper_service_config.backgrounds,
&mut backgrounds, &mut wallpapers,
); );
for background in &backgrounds { for wallpaper in &wallpapers {
if &background.output == data { if &wallpaper.output == data {
self.active_output = Some(data.clone()); self.active_output = Some(data.clone());
self.select_background_entry(background); self.select_wallpaper_entry(wallpaper);
if let Some(current) = entry_directory(self.config.current_folder(), background)
{
if let Err(why) = self.config.set_current_folder(Some(current)) {
tracing::error!(?why, "cannot set current folder");
}
}
break; break;
} }
} }
std::mem::swap( std::mem::swap(
&mut self.background_service_config.backgrounds, &mut self.wallpaper_service_config.backgrounds,
&mut backgrounds, &mut wallpapers,
); );
} }
} }
@ -446,7 +476,7 @@ impl Page {
let mut command = Command::none(); let mut command = Command::none();
match category { match category {
Category::Backgrounds => { Category::Wallpapers => {
if self.config.current_folder.is_some() { if self.config.current_folder.is_some() {
let _ = self.config.set_current_folder(None); let _ = self.config.set_current_folder(None);
command = cosmic::command::future(async move { command = cosmic::command::future(async move {
@ -457,7 +487,7 @@ impl Page {
)) ))
}); });
} else { } else {
self.select_first_background(); self.select_first_wallpaper();
} }
} }
@ -479,6 +509,19 @@ impl Page {
}); });
} }
} }
Category::AddFolder => {
self.active_dialog = ActiveDialog::AddFolder;
return cosmic::command::message(crate::Message::FileChooser(
crate::app::FileChooser::Open {
title: fl!("wallpaper", "folder-dialog"),
accept_label: fl!("dialog-add"),
include_directories: true,
modal: false,
multiple_files: false,
},
));
}
} }
self.categories.selected = Some(category); self.categories.selected = Some(category);
@ -493,7 +536,7 @@ impl Page {
} }
} }
/// Changes the slideshow background rotation frequency /// Changes the slideshow wallpaper rotation frequency
pub fn change_rotation_frequency(&mut self, option: usize) { pub fn change_rotation_frequency(&mut self, option: usize) {
self.selected_rotation = option; self.selected_rotation = option;
@ -508,8 +551,8 @@ impl Page {
}; };
} }
/// Updates configuration for background image. /// Updates configuration for wallpaper image.
fn config_background_entry(&self, output: String, path: PathBuf) -> Option<Entry> { fn config_wallpaper_entry(&self, output: String, path: PathBuf) -> Option<Entry> {
let scaling_mode = match self.selected_fit { let scaling_mode = match self.selected_fit {
FIT => ScalingMode::Fit([0.0, 0.0, 0.0]), FIT => ScalingMode::Fit([0.0, 0.0, 0.0]),
STRETCH => ScalingMode::Stretch, STRETCH => ScalingMode::Stretch,
@ -589,13 +632,11 @@ impl Page {
let selection = context.selection_handles.remove(image); let selection = context.selection_handles.remove(image);
if let Some(((display, selection), path)) = display.zip(selection).zip(path) { if let Some(((display, selection), path)) = display.zip(selection).zip(path) {
let key = self.selection.paths.insert(path); self.selection.add_custom_image(path, display, selection);
self.selection.display_images.insert(key, display);
self.selection.selection_handles.insert(key, selection);
} }
} }
self.select_first_background(); self.select_first_wallpaper();
} }
Message::ColorAddDialog => { Message::ColorAddDialog => {
@ -622,14 +663,23 @@ impl Page {
tracing::error!(?path, ?why, "could add custom image to config"); tracing::error!(?path, ?why, "could add custom image to config");
} }
self.selection.add_custom_image(path, display, selection); self.selection.add_custom_image(
path,
display,
ImageHandle::from_pixels(
selection.width(),
selection.height(),
selection.into_vec(),
),
);
} }
Message::ImageAddDialog => { Message::ImageAddDialog => {
self.active_dialog = ActiveDialog::AddImage;
return cosmic::command::message(crate::Message::FileChooser( return cosmic::command::message(crate::Message::FileChooser(
crate::app::FileChooser::Open { crate::app::FileChooser::Open {
title: fl!("wallpaper-dialog-image"), title: fl!("wallpaper", "image-dialog"),
accept_label: fl!("wallpaper-dialog-image", "accept"), accept_label: fl!("dialog-add"),
include_directories: false, include_directories: false,
modal: false, modal: false,
multiple_files: false, multiple_files: false,
@ -663,13 +713,13 @@ impl Page {
Message::RotationFrequency(pos) => self.change_rotation_frequency(pos), Message::RotationFrequency(pos) => self.change_rotation_frequency(pos),
Message::SameBackground(value) => { Message::SameWallpaper(value) => {
self.background_service_config.same_on_all = value; self.wallpaper_service_config.same_on_all = value;
self.background_service_config.backgrounds.clear(); self.wallpaper_service_config.backgrounds.clear();
} }
Message::Select(id) => { Message::Select(id) => {
self.selection.active = Choice::Background(id); self.selection.active = Choice::Wallpaper(id);
self.cache_display_image(); self.cache_display_image();
} }
@ -678,12 +728,12 @@ impl Page {
self.selection.active = Choice::Slideshow; self.selection.active = Choice::Slideshow;
self.cache_display_image(); self.cache_display_image();
} else { } else {
self.select_first_background(); self.select_first_wallpaper();
} }
} }
Message::Init(update) => { Message::Init(update) => {
self.background_service_config_update(update.0, update.1, update.2); self.wallpaper_service_config_update(update.0, update.1, update.2);
self.config_apply(); self.config_apply();
// Sync custom colors from config. // Sync custom colors from config.
@ -692,7 +742,7 @@ impl Page {
} }
// Set the default selection if an image was selected. // Set the default selection if an image was selected.
if let Choice::Background(_) | Choice::Slideshow = self.selection.active { if let Choice::Wallpaper(_) | Choice::Slideshow = self.selection.active {
let folder = self.config.current_folder(); let folder = self.config.current_folder();
for (id, recent) in self.config.recent_folders().iter().enumerate() { for (id, recent) in self.config.recent_folders().iter().enumerate() {
if recent == folder { if recent == folder {
@ -723,15 +773,15 @@ impl Page {
Command::none() Command::none()
} }
/// Selects the given background entry. /// Selects the given wallpaper entry.
fn select_background_entry(&mut self, entry: &wallpaper::Entry) { fn select_wallpaper_entry(&mut self, entry: &wallpaper::Entry) {
match entry.source { match entry.source {
wallpaper::Source::Path(ref path) => { wallpaper::Source::Path(ref path) => {
if path.is_dir() { if path.is_dir() {
self.selection.active = Choice::Slideshow; self.selection.active = Choice::Slideshow;
self.cache_display_image(); self.cache_display_image();
} else if let Some(entity) = self.background_id_from_path(path) { } else if let Some(entity) = self.wallpaper_id_from_path(path) {
self.select_background(entry, entity, path.is_dir()); self.select_wallpaper(entry, entity, path.is_dir());
} }
} }
@ -743,25 +793,32 @@ impl Page {
} }
} }
/// Selects the first background from the wallpaper select options. /// Selects the first wallpaper from the wallpaper select options.
fn select_first_background(&mut self) { fn select_first_wallpaper(&mut self) {
let (entity, path) = match self.selection.custom_images.last() { let (entity, path) = if let Some(Category::Wallpapers) = self.categories.selected {
Some(entity) => (*entity, &self.selection.paths[*entity]), match self.selection.custom_images.last() {
None => match self.selection.paths.iter().next() { Some(entity) => (*entity, &self.selection.paths[*entity]),
None => match self.selection.paths.iter().next() {
Some(value) => value,
None => return,
},
}
} else {
match self.selection.paths.iter().next() {
Some(value) => value, Some(value) => value,
None => return, None => return,
}, }
}; };
if let Some(output) = self.config_output() { if let Some(output) = self.config_output() {
if let Some(entry) = self.config_background_entry(output, path.clone()) { if let Some(entry) = self.config_wallpaper_entry(output, path.clone()) {
self.select_background(&entry, entity, path.is_dir()); self.select_wallpaper(&entry, entity, path.is_dir());
} }
} }
} }
/// Selects the given background /// Selects the given wallpaper
fn select_background( fn select_wallpaper(
&mut self, &mut self,
entry: &wallpaper::Entry, entry: &wallpaper::Entry,
entity: DefaultKey, entity: DefaultKey,
@ -770,7 +827,7 @@ impl Page {
self.selection.active = if is_slideshow { self.selection.active = if is_slideshow {
Choice::Slideshow Choice::Slideshow
} else { } else {
Choice::Background(entity) Choice::Wallpaper(entity)
}; };
match entry.scaling_mode { match entry.scaling_mode {
@ -805,14 +862,14 @@ impl Page {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
enum Choice { enum Choice {
Background(DefaultKey), Wallpaper(DefaultKey),
Color(wallpaper::Color), Color(wallpaper::Color),
Slideshow, Slideshow,
} }
impl Default for Choice { impl Default for Choice {
fn default() -> Self { fn default() -> Self {
Self::Background(DefaultKey::default()) Self::Wallpaper(DefaultKey::default())
} }
} }
@ -822,7 +879,7 @@ pub struct Context {
custom_images: Vec<DefaultKey>, custom_images: Vec<DefaultKey>,
custom_colors: Vec<wallpaper::Color>, custom_colors: Vec<wallpaper::Color>,
paths: SlotMap<DefaultKey, PathBuf>, paths: SlotMap<DefaultKey, PathBuf>,
is_custom: SecondaryMap<DefaultKey, bool>, is_custom: SecondaryMap<DefaultKey, ()>,
display_images: SecondaryMap<DefaultKey, image::RgbaImage>, display_images: SecondaryMap<DefaultKey, image::RgbaImage>,
selection_handles: SecondaryMap<DefaultKey, ImageHandle>, selection_handles: SecondaryMap<DefaultKey, ImageHandle>,
} }
@ -834,15 +891,12 @@ impl Context {
} }
} }
fn add_custom_image(&mut self, path: PathBuf, display: Image, selection: Image) { fn add_custom_image(&mut self, path: PathBuf, display: Image, selection: ImageHandle) {
let key = self.paths.insert(path); let key = self.paths.insert(path);
self.is_custom.insert(key, true); self.is_custom.insert(key, ());
self.display_images.insert(key, display); self.display_images.insert(key, display);
self.custom_images.push(key); self.custom_images.push(key);
self.selection_handles.insert( self.selection_handles.insert(key, selection);
key,
ImageHandle::from_pixels(selection.width(), selection.height(), selection.into_vec()),
);
} }
fn remove_custom_color(&mut self, color: &wallpaper::Color) { fn remove_custom_color(&mut self, color: &wallpaper::Color) {
@ -852,13 +906,14 @@ impl Context {
} }
fn remove_custom_image(&mut self, image: DefaultKey) -> Option<PathBuf> { fn remove_custom_image(&mut self, image: DefaultKey) -> Option<PathBuf> {
if let Some(true) = self.is_custom.remove(image) { if self.is_custom.contains_key(image) {
if let Some(id) = self.custom_images.iter().position(|i| i == &image) { if let Some(id) = self.custom_images.iter().position(|i| i == &image) {
self.custom_images.remove(id); self.custom_images.remove(id);
} }
self.display_images.remove(image); self.display_images.remove(image);
self.selection_handles.remove(image); self.selection_handles.remove(image);
self.is_custom.remove(image);
return self.paths.remove(image); return self.paths.remove(image);
} }
@ -868,9 +923,9 @@ impl Context {
pub async fn change_folder(current_folder: PathBuf) -> Context { pub async fn change_folder(current_folder: PathBuf) -> Context {
let mut update = Context::default(); let mut update = Context::default();
let mut backgrounds = wallpaper::load_each_from_path(current_folder); let mut wallpapers = wallpaper::load_each_from_path(current_folder);
while let Some((path, display_image, selection_image)) = backgrounds.recv().await { while let Some((path, display_image, selection_image)) = wallpapers.recv().await {
let id = update.paths.insert(path); let id = update.paths.insert(path);
update.display_images.insert(id, display_image); update.display_images.insert(id, display_image);
@ -916,14 +971,14 @@ pub fn settings() -> Section<crate::pages::Message> {
children.push(crate::widget::display_container( children.push(crate::widget::display_container(
match page.selection.active { match page.selection.active {
// Shows background options, with the slideshow toggle enabled // Shows wallpaper options, with the slideshow toggle enabled
Choice::Slideshow => { Choice::Slideshow => {
slideshow_enabled = true; slideshow_enabled = true;
page.display_image_view() page.display_image_view()
} }
// Shows background options, with the slideshow toggle visible // Shows wallpaper options, with the slideshow toggle visible
Choice::Background(_) => page.display_image_view(), Choice::Wallpaper(_) => page.display_image_view(),
// Displays color options, and hides the slideshow toggle // Displays color options, and hides the slideshow toggle
Choice::Color(ref color) => { Choice::Color(ref color) => {
@ -938,7 +993,7 @@ pub fn settings() -> Section<crate::pages::Message> {
}, },
)); ));
children.push(if page.background_service_config.same_on_all { children.push(if page.wallpaper_service_config.same_on_all {
text(fl!("all-displays")) text(fl!("all-displays"))
.font(cosmic::font::FONT_SEMIBOLD) .font(cosmic::font::FONT_SEMIBOLD)
.horizontal_alignment(alignment::Horizontal::Center) .horizontal_alignment(alignment::Horizontal::Center)
@ -955,7 +1010,7 @@ pub fn settings() -> Section<crate::pages::Message> {
.into() .into()
}); });
let background_fit = let wallpaper_fit =
cosmic::widget::dropdown(&page.fit_options, Some(page.selected_fit), Message::Fit); cosmic::widget::dropdown(&page.fit_options, Some(page.selected_fit), Message::Fit);
children.push({ children.push({
@ -964,11 +1019,11 @@ pub fn settings() -> Section<crate::pages::Message> {
&*WALLPAPER_SAME, &*WALLPAPER_SAME,
toggler( toggler(
None, None,
page.background_service_config.same_on_all, page.wallpaper_service_config.same_on_all,
Message::SameBackground, Message::SameWallpaper,
), ),
)) ))
.add(settings::item(&*WALLPAPER_FIT, background_fit)); .add(settings::item(&*WALLPAPER_FIT, wallpaper_fit));
if show_slideshow_toggle { if show_slideshow_toggle {
column = column.add(settings::item( column = column.add(settings::item(
@ -997,38 +1052,50 @@ pub fn settings() -> Section<crate::pages::Message> {
let category_selection = let category_selection =
dropdown::multi::dropdown(&page.categories, Message::ChangeCategory); dropdown::multi::dropdown(&page.categories, Message::ChangeCategory);
let add_button = { // Show the add button only on the Colors and Wallpapers pages
let (text, message) = if Some(Category::Colors) == page.categories.selected { let add_button =
(fl!("add-color"), Message::ColorAddDialog) if let Some(Category::Colors | Category::Wallpapers) = page.categories.selected {
} else { let (text, message) = if Some(Category::Colors) == page.categories.selected {
(fl!("add-image"), Message::ImageAddDialog) (fl!("add-color"), Message::ColorAddDialog)
}; } else {
(fl!("add-image"), Message::ImageAddDialog)
};
button::link(text) let button = button::link(text).trailing_icon(true).on_press(message);
.trailing_icon(true)
.on_press(message) Some(button)
.apply(cosmic::widget::container) } else {
.align_y(alignment::Vertical::Bottom) None
}; };
children.push( children.push(
row::with_capacity(2) row::with_capacity(2)
.align_items(Alignment::Center)
// Show a folder icon if the active category is a custom folder.
.push_maybe(
if let Some(Category::RecentFolder(_)) = page.categories.selected {
Some(icon::from_name("folder-symbolic").size(16).icon())
} else {
None
},
)
.push(category_selection) .push(category_selection)
.push(cosmic::widget::horizontal_space(Length::Fill)) .push(cosmic::widget::horizontal_space(Length::Fill))
.push(add_button) .push_maybe(add_button)
.into(), .into(),
); );
match page.categories.selected { match page.categories.selected {
// Displays system wallpapers that are available to select from // Displays system wallpapers that are available to select from
Some(Category::Backgrounds | Category::RecentFolder(_)) => { Some(Category::Wallpapers | Category::RecentFolder(_)) => {
children.push(widgets::wallpaper_select_options( children.push(widgets::wallpaper_select_options(
page, page,
if let Choice::Background(selection) = page.selection.active { if let Choice::Wallpaper(selection) = page.selection.active {
Some(selection) Some(selection)
} else { } else {
None None
}, },
matches!(page.categories.selected, Some(Category::Wallpapers)),
)); ));
} }
@ -1044,7 +1111,7 @@ pub fn settings() -> Section<crate::pages::Message> {
)); ));
} }
None => (), _ => (),
} }
cosmic::widget::column::with_children(children) cosmic::widget::column::with_children(children)

View file

@ -15,6 +15,7 @@ const COLUMN_SPACING: u16 = 12;
const ROW_SPACING: u16 = 16; const ROW_SPACING: u16 = 16;
/// A button for selecting a color or gradient. /// A button for selecting a color or gradient.
#[must_use]
pub fn color_button( pub fn color_button(
color: wallpaper::Color, color: wallpaper::Color,
removable: bool, removable: bool,
@ -36,6 +37,7 @@ pub fn color_button(
} }
/// A sized container that's filled with a color or gradient. /// A sized container that's filled with a color or gradient.
#[must_use]
pub fn color_image<'a, M: 'a>( pub fn color_image<'a, M: 'a>(
color: wallpaper::Color, color: wallpaper::Color,
width: u16, width: u16,
@ -78,6 +80,7 @@ pub fn color_image<'a, M: 'a>(
} }
/// Color selection list /// Color selection list
#[must_use]
pub fn color_select_options( pub fn color_select_options(
context: &super::Context, context: &super::Context,
selected: Option<&wallpaper::Color>, selected: Option<&wallpaper::Color>,
@ -106,22 +109,26 @@ pub fn color_select_options(
} }
/// Background selection list /// Background selection list
#[must_use]
pub fn wallpaper_select_options( pub fn wallpaper_select_options(
page: &super::Page, page: &super::Page,
selected: Option<DefaultKey>, selected: Option<DefaultKey>,
show_custom_images: bool,
) -> Element<Message> { ) -> Element<Message> {
let mut vec = Vec::with_capacity(page.selection.selection_handles.len()); let mut vec = Vec::with_capacity(page.selection.selection_handles.len());
// Place removable custom images first if show_custom_images {
for id in page.selection.custom_images.iter().rev() { // Place removable custom images first
let handle = &page.selection.selection_handles[*id]; for id in page.selection.custom_images.iter().rev() {
let handle = &page.selection.selection_handles[*id];
vec.push(wallpaper_button( vec.push(wallpaper_button(
handle, handle,
*id, *id,
true, true,
selected.map_or(false, |selection| id == &selection), selected.map_or(false, |selection| id == &selection),
)); ));
}
} }
// Then place non-removable images from the current folder // Then place non-removable images from the current folder

View file

@ -112,7 +112,6 @@ all-displays = Všechny monitory
colors = Barvy colors = Barvy
fit-to-screen = Vyplnit obrazovku fit-to-screen = Vyplnit obrazovku
stretch = Roztáhnout stretch = Roztáhnout
system-backgrounds = Systémové pozadí
zoom = Přiblížení zoom = Přiblížení
x-minutes = { $number } minut x-minutes = { $number } minut

View file

@ -144,25 +144,26 @@ panel-missing = Panel Configuration is Missing
## Desktop: Wallpaper ## Desktop: Wallpaper
wallpaper = Wallpaper wallpaper = Wallpaper
.desc = Background images, colors, and slideshow options.
.same = Same background on all displays
.fit = Background fit
.slide = Slideshow
.change = Change image every .change = Change image every
.desc = Wallpaper images, colors, and slideshow options.
.fit = Wallpaper fit
.folder-dialog = Choose wallpaper folder
.image-dialog = Choose wallpaper image
.plural = Wallpapers
.same = Same wallpaper on all displays
.slide = Slideshow
add-color = Add color add-color = Add color
add-image = Add image add-image = Add image
all-displays = All Displays all-displays = All Displays
colors = Colors colors = Colors
dialog-add = _Add
fit-to-screen = Fit to Screen fit-to-screen = Fit to Screen
open-new-folder = Open new folder
recent-folders = Recent Folders recent-folders = Recent Folders
stretch = Stretch stretch = Stretch
system-backgrounds = System backgrounds
zoom = Zoom zoom = Zoom
wallpaper-dialog-image = Choose wallpaper image
.accept = _Add
x-minutes = { $number } minutes x-minutes = { $number } minutes
x-hours = { $number -> x-hours = { $number ->
[1] 1 hour [1] 1 hour

View file

@ -107,7 +107,6 @@ all-displays = Tutti gli Schermi
colors = Colori colors = Colori
fit-to-screen = Adatta allo Schermo fit-to-screen = Adatta allo Schermo
stretch = Allarga stretch = Allarga
system-backgrounds = Sfondi di sistema
zoom = Zoom zoom = Zoom
x-minutes = { $number } minuti x-minutes = { $number } minuti

View file

@ -155,7 +155,6 @@ all-displays = Все экраны
colors = Цвета colors = Цвета
fit-to-screen = Подгонять под экран fit-to-screen = Подгонять под экран
stretch = Растянуть stretch = Растянуть
system-backgrounds = Системные фоны
zoom = Увеличить zoom = Увеличить
x-minutes = { $number } минут x-minutes = { $number } минут

View file

@ -155,7 +155,6 @@ all-displays = Сви екрани
colors = Боје colors = Боје
fit-to-screen = Уклопи у екран fit-to-screen = Уклопи у екран
stretch = Развуци stretch = Развуци
system-backgrounds = Системске позадине
zoom = Увећај zoom = Увећај
x-minutes = { $number } мин. x-minutes = { $number } мин.

View file

@ -155,7 +155,6 @@ all-displays = Svi ekrani
colors = Boje colors = Boje
fit-to-screen = Uklopi u ekran fit-to-screen = Uklopi u ekran
stretch = Razvuci stretch = Razvuci
system-backgrounds = Sistemske pozadine
zoom = Uvećaj zoom = Uvećaj
x-minutes = { $number } min. x-minutes = { $number } min.

View file

@ -108,7 +108,6 @@ all-displays = Tüm Ekranlar
colors = Renkler colors = Renkler
fit-to-screen = Ekrana Sığdır fit-to-screen = Ekrana Sığdır
stretch = Uzat stretch = Uzat
system-backgrounds = Sistem duvar kağıtları
zoom = Yakınlaştır zoom = Yakınlaştır
x-minutes = { $number } dakika x-minutes = { $number } dakika

View file

@ -148,7 +148,6 @@ all-displays = 所有顯示器
colors = 顏色 colors = 顏色
fit-to-screen = 延伸至全螢幕 fit-to-screen = 延伸至全螢幕
stretch = 延伸 stretch = 延伸
system-backgrounds = 系統背景
zoom = 縮放 zoom = 縮放
x-minutes = { $number } 分鐘 x-minutes = { $number } 分鐘

View file

@ -69,12 +69,6 @@ pub fn config() -> (Config, HashMap<String, String>) {
pub fn set(config: &mut Config, entry: Entry) { pub fn set(config: &mut Config, entry: Entry) {
if let Ok(context) = Config::helper() { if let Ok(context) = Config::helper() {
tracing::debug!(
output = entry.output.to_string(),
source = ?entry.source,
"setting wallpaper",
);
let _res = Config::set_same_on_all(&context, config.same_on_all); let _res = Config::set_same_on_all(&context, config.same_on_all);
if let Err(why) = config.set_entry(&context, entry) { if let Err(why) = config.set_entry(&context, entry) {