From 8ca65bafe442f67293ad15efd4ddaa2d809072cb Mon Sep 17 00:00:00 2001 From: Eduardo Flores Date: Fri, 13 Jun 2025 16:23:28 -0700 Subject: [PATCH] fix: wallpaper discovery Allows settings to show installed wallpapers. - Modified `load_each_from_path` to recursively discover wallpapers, limited depth to 3 for performance. --- Cargo.lock | 1 + .../src/pages/desktop/wallpaper/mod.rs | 5 -- pages/wallpapers/Cargo.toml | 1 + pages/wallpapers/src/lib.rs | 76 ++++++++----------- 4 files changed, 34 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 90808f1..7b45e8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1844,6 +1844,7 @@ dependencies = [ "jxl-oxide", "tokio", "tracing", + "walkdir", ] [[package]] diff --git a/cosmic-settings/src/pages/desktop/wallpaper/mod.rs b/cosmic-settings/src/pages/desktop/wallpaper/mod.rs index ef667ab..64005bd 100644 --- a/cosmic-settings/src/pages/desktop/wallpaper/mod.rs +++ b/cosmic-settings/src/pages/desktop/wallpaper/mod.rs @@ -1178,11 +1178,6 @@ pub async fn change_folder(current_folder: PathBuf) -> Context { let mut update = Context::default(); let mut streams = Vec::with_capacity(2); - // Include the cosmic background folder when loading the system wallpapers. - if current_folder == Config::default_folder() { - streams.push(wallpaper::load_each_from_path(Config::default_folder().join("cosmic")).await); - } - streams.push(wallpaper::load_each_from_path(current_folder).await); for mut wallpapers in streams { diff --git a/pages/wallpapers/Cargo.toml b/pages/wallpapers/Cargo.toml index ff88d5c..3f6cd48 100644 --- a/pages/wallpapers/Cargo.toml +++ b/pages/wallpapers/Cargo.toml @@ -23,3 +23,4 @@ infer = "0.16.0" jxl-oxide = "0.11.3" tokio = { version = "1.44.1", features = ["sync"] } tracing = "0.1.41" +walkdir = "=2.5.0" diff --git a/pages/wallpapers/src/lib.rs b/pages/wallpapers/src/lib.rs index b328369..ec5046f 100644 --- a/pages/wallpapers/src/lib.rs +++ b/pages/wallpapers/src/lib.rs @@ -2,18 +2,20 @@ pub use cosmic_bg_config::{Color, Config, Entry, Gradient, ScalingMode, Source}; use eyre::{eyre, OptionExt}; use fast_image_resize::SrcCropping; use futures_lite::Stream; +use futures_util::StreamExt; use image::imageops::FilterType; use image::{DynamicImage, ImageBuffer, Rgba, RgbaImage}; use jxl_oxide::{EnumColourEncoding, JxlImage, PixelFormat}; use std::os::unix::ffi::OsStrExt; use std::{ borrow::Cow, - collections::{hash_map::DefaultHasher, BTreeSet, HashMap}, + collections::{hash_map::DefaultHasher, HashMap}, hash::{Hash, Hasher}, io::Read, path::{Path, PathBuf}, pin::Pin, }; +use walkdir::WalkDir; pub const DEFAULT_COLORS: &[Color] = &[ Color::Single([0.580, 0.922, 0.922]), @@ -99,54 +101,40 @@ pub fn cache_dir() -> Option { pub async fn load_each_from_path( path: PathBuf, ) -> Pin>> { - let wallpapers = tokio::task::spawn_blocking(move || { - // Discovered image files that will be loaded as wallpapers. - let mut wallpapers = BTreeSet::new(); + let candidate_paths: Vec<_> = WalkDir::new(path) + .max_depth(3) + .into_iter() + .filter_map(Result::ok) + .filter(|entry| entry.file_type().is_file()) + .map(|entry| entry.path().to_path_buf()) + .collect(); - if let Ok(dir) = path.read_dir() { - for entry in dir.filter_map(Result::ok) { - let Ok(file_type) = entry.file_type() else { - continue; + let future = futures_util::stream::iter(candidate_paths) + .map(|path| { + tokio::task::spawn_blocking(move || { + let is_jxl = path.extension().map(|ext| ext == "jxl").unwrap_or_default(); + let is_image = if !is_jxl { + if let Ok(Some(kind)) = infer::get_from_path(&path) { + infer::MatcherType::Image == kind.matcher_type() + } else { + false + } + } else { + true }; - let path = entry.path(); - - if file_type.is_file() { - let path = if path.extension().map(|ext| ext == "jxl").unwrap_or_default() { - path - } else if let Ok(Some(kind)) = infer::get_from_path(&path) { - if infer::MatcherType::Image == kind.matcher_type() { - path - } else { - continue; - } - } else { - continue; - }; - - wallpapers.insert(path); - - if wallpapers.len() > 99 { - break; - } + if is_jxl || is_image { + load_image_with_thumbnail(path) + } else { + None } - } - } + }) + }) + .buffered(4) + .filter_map(|value| async { value.ok().flatten() }) + .take(100); - wallpapers - }); - - if let Ok(wallpapers) = wallpapers.await { - use futures_util::StreamExt; - let future = futures_util::stream::iter(wallpapers) - .map(|path| tokio::task::spawn_blocking(|| load_image_with_thumbnail(path))) - .buffered(4) - .filter_map(|value| async { value.ok()? }); - - Box::pin(future) - } else { - Box::pin(futures_lite::stream::empty()) - } + Box::pin(future) } #[must_use]