diff --git a/Cargo.lock b/Cargo.lock index fc7d1f5..5043e86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -295,6 +295,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "arrayvec" version = "0.7.6" @@ -644,7 +650,7 @@ dependencies = [ "aligned", "anyhow", "arg_enum_proc_macro", - "arrayvec", + "arrayvec 0.7.6", "log", "num-rational", "num-traits", @@ -662,7 +668,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8" dependencies = [ "anyhow", - "arrayvec", + "arrayvec 0.7.6", "log", "nom", "num-rational", @@ -675,9 +681,15 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f" dependencies = [ - "arrayvec", + "arrayvec 0.7.6", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.22.1" @@ -738,6 +750,17 @@ dependencies = [ "core2", ] +[[package]] +name = "blake2b_simd" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" +dependencies = [ + "arrayref", + "arrayvec 0.5.2", + "constant_time_eq", +] + [[package]] name = "block" version = "0.1.6" @@ -1150,6 +1173,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "core-foundation" version = "0.9.4" @@ -1518,6 +1547,17 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dirs" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" +dependencies = [ + "libc", + "redox_users 0.3.5", + "winapi", +] + [[package]] name = "dirs" version = "5.0.1" @@ -1681,6 +1721,15 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" +[[package]] +name = "enquote" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c36cb11dbde389f4096111698d8b567c0720e3452fd5ac3e6b4e47e1939932" +dependencies = [ + "thiserror 1.0.69", +] + [[package]] name = "enumflags2" version = "0.7.12" @@ -2212,6 +2261,17 @@ dependencies = [ "windows-link", ] +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -2220,7 +2280,7 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", ] [[package]] @@ -2927,7 +2987,7 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3e98b1520e49e252237edc238a39869da9f3241f2ec19dc788c1d24694d1e4" dependencies = [ - "arrayvec", + "arrayvec 0.7.6", ] [[package]] @@ -3183,7 +3243,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1618d4ebd923e97d67e7cd363d80aef35fe961005cbbbb3d2dad8bdd1bc63440" dependencies = [ - "arrayvec", + "arrayvec 0.7.6", "smallvec", ] @@ -3193,7 +3253,7 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" dependencies = [ - "arrayvec", + "arrayvec 0.7.6", "euclid", "smallvec", ] @@ -3399,7 +3459,7 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e260b6de923e6e47adfedf6243013a7a874684165a6a277594ee3906021b2343" dependencies = [ - "arrayvec", + "arrayvec 0.7.6", "euclid", "num-traits", ] @@ -3527,7 +3587,7 @@ checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.61.2", ] @@ -3553,7 +3613,7 @@ version = "22.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bd5a652b6faf21496f2cfd88fc49989c8db0825d1f6746b1a71a6ede24a63ad" dependencies = [ - "arrayvec", + "arrayvec 0.7.6", "bit-set", "bitflags 2.10.0", "cfg_aliases 0.1.1", @@ -3644,6 +3704,7 @@ dependencies = [ "rust-embed", "simple_logger", "tokio", + "wallpaper", ] [[package]] @@ -4700,7 +4761,7 @@ dependencies = [ "aligned-vec", "arbitrary", "arg_enum_proc_macro", - "arrayvec", + "arrayvec 0.7.6", "av-scenechange", "av1-grain", "bitstream-io", @@ -4788,6 +4849,12 @@ dependencies = [ "font-types", ] +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + [[package]] name = "redox_syscall" version = "0.2.16" @@ -4815,6 +4882,17 @@ dependencies = [ "bitflags 2.10.0", ] +[[package]] +name = "redox_users" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" +dependencies = [ + "getrandom 0.1.16", + "redox_syscall 0.1.57", + "rust-argon2", +] + [[package]] name = "redox_users" version = "0.4.6" @@ -4927,7 +5005,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db09040cc89e461f1a265139777a2bde7f8d8c67c4936f700c63ce3e2904d468" dependencies = [ - "base64", + "base64 0.22.1", "bitflags 2.10.0", "serde", "serde_derive", @@ -4940,6 +5018,18 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" +[[package]] +name = "rust-argon2" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" +dependencies = [ + "base64 0.13.1", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + [[package]] name = "rust-embed" version = "8.9.0" @@ -4974,6 +5064,12 @@ dependencies = [ "walkdir", ] +[[package]] +name = "rust-ini" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac66e816614e124a692b6ac1b8437237a518c9155a3aacab83a373982630c715" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -5500,7 +5596,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41ba83ebaf2954d31d05d67340fd46cebe99da2b7133b0dd68d70c65473a437b" dependencies = [ - "arrayvec", + "arrayvec 0.7.6", "grid", "serde", "slotmap", @@ -5622,7 +5718,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" dependencies = [ "arrayref", - "arrayvec", + "arrayvec 0.7.6", "bytemuck", "cfg-if", "log", @@ -5959,7 +6055,7 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032" dependencies = [ - "base64", + "base64 0.22.1", "data-url", "flate2", "fontdb 0.18.0", @@ -6036,6 +6132,25 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wallpaper" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0547c84bf49b1096b20ce49736b86cd27f8225fc426665d3fba19e71e44c4d46" +dependencies = [ + "dirs 1.0.5", + "enquote", + "rust-ini", + "winapi", + "winreg", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -6306,7 +6421,7 @@ version = "22.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d1c4ba43f80542cf63a0a6ed3134629ae73e8ab51e4b765a67f3aa062eb433" dependencies = [ - "arrayvec", + "arrayvec 0.7.6", "cfg_aliases 0.1.1", "document-features", "js-sys", @@ -6331,7 +6446,7 @@ version = "22.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0348c840d1051b8e86c3bcd31206080c5e71e5933dabd79be1ce732b0b2f089a" dependencies = [ - "arrayvec", + "arrayvec 0.7.6", "bit-vec", "bitflags 2.10.0", "cfg_aliases 0.1.1", @@ -6357,7 +6472,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6bbf4b4de8b2a83c0401d9e5ae0080a2792055f25859a02bf9be97952bbed4f" dependencies = [ "android_system_properties", - "arrayvec", + "arrayvec 0.7.6", "ash", "bit-set", "bitflags 2.10.0", @@ -6955,6 +7070,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16cdb3898397cf7f624c294948669beafaeebc5577d5ec53d0afb76633593597" +dependencies = [ + "winapi", +] + [[package]] name = "wit-bindgen" version = "0.46.0" diff --git a/Cargo.toml b/Cargo.toml index 291859b..bea26a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ dirs = "5.0" image = "0.25.9" clap = { version = "4.5.54", features = ["derive"] } env_logger = "0.11.8" +wallpaper = "3.2" [dependencies.libcosmic] git = "https://github.com/pop-os/libcosmic.git" diff --git a/docs/features.md b/docs/features.md index 05f006e..932eeed 100644 --- a/docs/features.md +++ b/docs/features.md @@ -104,6 +104,10 @@ This document describes the implemented and planned features of Noctua, a modern - **Properties panel**: - Image metadata display - File information + - Action buttons: + - Set as Wallpaper (works with COSMIC, GNOME, KDE, XFCE, and tiling WMs) + - Open With… (planned) + - Show in Folder (planned) - Toggle with `i` key or toolbar button - **Navigation panel** (Left sidebar): - Toggle with `n` key or toolbar button @@ -116,6 +120,23 @@ Full keyboard-driven workflow: - Pan: `Ctrl + ←` `Ctrl + →` `Ctrl + ↑` `Ctrl + ↓` - Transform: `r` `Shift+r` `h` `v` - Panels: `i` `n` +- Actions: `w` (Set as Wallpaper) + +### Desktop Integration + +#### Wallpaper Support (Implemented) +- **Set as Wallpaper**: One-click wallpaper setting with cross-desktop compatibility +- **Supported desktop environments**: + - COSMIC Desktop (direct config file integration) + - GNOME (via gsettings) + - KDE Plasma (via wallpaper crate) + - XFCE (via wallpaper crate) + - Tiling window managers (via feh) +- **Multiple access methods**: + - Keyboard shortcut: `w` + - Icon button in Properties panel + - Tooltip support for discoverability +- **Automatic fallback**: Tries multiple methods until one succeeds ### Configuration diff --git a/docs/usage.md b/docs/usage.md index b1bc2b6..9524677 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -75,6 +75,12 @@ All transformations are lossless and show in real-time. | `i` | Toggle properties | Show/hide the properties panel (metadata)| | `n` | Toggle navigation | Show/hide the navigation sidebar | +### Actions + +| Key | Action | Description | +|:----|:-----------------------|:-----------------------------------------| +| `w` | Set as wallpaper | Set the current image as desktop wallpaper| + ## Mouse Controls ### Zoom @@ -101,6 +107,26 @@ The header toolbar provides quick access to common operations: ### Right Side - **Properties toggle**: Show/hide the metadata panel +## Properties Panel + +The properties panel (toggle with `i`) displays image metadata and provides quick actions: + +### Action Buttons +Located at the top-right of the properties panel: + +- **Set as Wallpaper** (`w` key): Set the current image as your desktop wallpaper + - Works with COSMIC, GNOME, KDE, XFCE, and tiling window managers + - Automatically detects your desktop environment + - Falls back to alternative methods if the primary method fails + +- **Open With** (planned): Open the image with another application + +- **Show in Folder** (planned): Open the containing folder in your file manager + +### Metadata Display +- **File Information**: Name, format, dimensions, file size, color type +- **Camera Information** (if available): Camera model, date taken, exposure settings, GPS location + ## Footer Information The footer displays useful information: diff --git a/i18n/en/noctua.ftl b/i18n/en/noctua.ftl index 63f1c28..f736b39 100644 --- a/i18n/en/noctua.ftl +++ b/i18n/en/noctua.ftl @@ -40,9 +40,15 @@ error-unsupported-format = Unsupported file format. ## Properties panel panel-properties = Properties +panel-actions = Actions meta-section-file = File Information meta-section-exif = Camera Information +## Action buttons +action-set-wallpaper = Set as Wallpaper +action-open-with = Open With… +action-show-in-folder = Show in Folder + ## Basic metadata meta-filename = Name meta-format = Format diff --git a/src/app/document/mod.rs b/src/app/document/mod.rs index 0a08be1..e5faa21 100644 --- a/src/app/document/mod.rs +++ b/src/app/document/mod.rs @@ -110,3 +110,35 @@ impl DocumentContent { } } } + +/// Set an image file as desktop wallpaper. +/// +/// This function attempts multiple methods in order: +/// 1. COSMIC Desktop (direct config file modification) +/// 2. wallpaper crate (KDE, XFCE, Windows, macOS) +/// 3. gsettings (GNOME) +/// 4. feh (tiling window managers) +/// +/// The operation is performed asynchronously and logs success/failure. +pub fn set_as_wallpaper(path: &Path) { + // Canonicalize to absolute path + let abs_path = match path.canonicalize() { + Ok(p) => p, + Err(e) => { + log::error!("Failed to canonicalize path {}: {}", path.display(), e); + return; + } + }; + + // Convert to string + let path_str = match abs_path.to_str() { + Some(s) => s.to_string(), + None => { + log::error!("Invalid UTF-8 in path: {}", abs_path.display()); + return; + } + }; + + // Delegate to utils with concrete string type + utils::set_as_wallpaper(&path_str); +} diff --git a/src/app/document/utils.rs b/src/app/document/utils.rs index 2271d98..63f546a 100644 --- a/src/app/document/utils.rs +++ b/src/app/document/utils.rs @@ -1,2 +1,109 @@ // SPDX-License-Identifier: GPL-3.0-or-later // src/app/document/utils.rs +// +// Utility functions for document operations. + +/// Set an image as desktop wallpaper using multiple fallback methods. +/// +/// Expects an absolute path as string. +pub fn set_as_wallpaper(path_str: &str) { + log::info!("Attempting to set wallpaper: {}", path_str); + + // Method 1: Try COSMIC Desktop (direct config file modification) + if let Some(home) = dirs::home_dir() { + let cosmic_config = home.join(".config/cosmic/com.system76.CosmicBackground/v1/all"); + + if cosmic_config.exists() { + let config_content = format!( + r#"( + output: "all", + source: Path("{}"), + filter_by_theme: true, + rotation_frequency: 300, + filter_method: Lanczos, + scaling_mode: Zoom, + sampling_method: Alphanumeric, +)"#, + path_str + ); + + match std::fs::write(&cosmic_config, config_content) { + Ok(_) => { + log::info!("✓ Wallpaper set via COSMIC config file"); + return; + } + Err(e) => { + log::warn!("Failed to write COSMIC config: {}", e); + } + } + } + } + + // Method 2: Try wallpaper crate (supports KDE, XFCE, Windows, macOS) + match wallpaper::set_from_path(path_str) { + Ok(_) => { + log::info!("✓ Wallpaper set successfully via wallpaper crate"); + return; + } + Err(e) => { + log::warn!("wallpaper crate failed: {}", e); + } + } + + // Method 3: Try GNOME via gsettings + let uri = format!("file://{}", path_str); + log::info!("Trying gsettings with URI: {}", uri); + + match std::process::Command::new("gsettings") + .args(&[ + "set", + "org.gnome.desktop.background", + "picture-uri", + &uri, + ]) + .output() + { + Ok(output) if output.status.success() => { + log::info!("✓ Wallpaper set via gsettings (light mode)"); + + // Also set dark mode wallpaper + let _ = std::process::Command::new("gsettings") + .args(&[ + "set", + "org.gnome.desktop.background", + "picture-uri-dark", + &uri, + ]) + .output(); + return; + } + Ok(output) => { + log::warn!( + "gsettings failed: {}", + String::from_utf8_lossy(&output.stderr) + ); + } + Err(e) => { + log::warn!("gsettings command failed: {}", e); + } + } + + // Method 4: Try feh (common on tiling WMs like i3, sway) + match std::process::Command::new("feh") + .args(&["--bg-scale", path_str]) + .output() + { + Ok(output) if output.status.success() => { + log::info!("✓ Wallpaper set via feh"); + return; + } + Ok(_) => { + log::warn!("feh failed"); + } + Err(_) => { + log::warn!("feh not available"); + } + } + + log::error!("✗ All methods failed to set wallpaper"); +} diff --git a/src/app/message.rs b/src/app/message.rs index 8fc4eae..b020cda 100644 --- a/src/app/message.rs +++ b/src/app/message.rs @@ -70,6 +70,10 @@ pub enum AppMessage { #[allow(dead_code)] RefreshMetadata, + // === Wallpaper === + /// Set current image as wallpaper. + SetAsWallpaper, + // === Errors === /// Display an error message. #[allow(dead_code)] diff --git a/src/app/mod.rs b/src/app/mod.rs index 9f684b2..b9d2c42 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -244,6 +244,9 @@ fn handle_key_press(key: Key, modifiers: Modifiers) -> Option { } Key::Character(ch) if ch.eq_ignore_ascii_case("n") => Some(ToggleNavBar), + // Wallpaper. + Key::Character(ch) if ch.eq_ignore_ascii_case("w") => Some(SetAsWallpaper), + _ => None, } } diff --git a/src/app/update.rs b/src/app/update.rs index 8ec2b56..1201683 100644 --- a/src/app/update.rs +++ b/src/app/update.rs @@ -104,6 +104,11 @@ pub fn update(model: &mut AppModel, msg: AppMessage) { refresh_metadata(model); } + // ===== Wallpaper ================================================================= + AppMessage::SetAsWallpaper => { + set_as_wallpaper(model); + } + // ===== Error handling ============================================================ AppMessage::ShowError(msg) => { model.set_error(msg); @@ -154,3 +159,18 @@ fn current_zoom(model: &AppModel) -> f32 { fn refresh_metadata(model: &mut AppModel) { model.metadata = model.document.as_ref().map(|doc| doc.extract_meta()); } + +/// Set the current image as desktop wallpaper. +fn set_as_wallpaper(model: &mut AppModel) { + let Some(path) = model.current_path.as_ref() else { + model.set_error("No image loaded"); + return; + }; + + let path = path.clone(); + + // Spawn async task to set wallpaper + tokio::spawn(async move { + document::set_as_wallpaper(&path); + }); +} diff --git a/src/app/view/header.rs b/src/app/view/header.rs index 3082992..ed47d8f 100644 --- a/src/app/view/header.rs +++ b/src/app/view/header.rs @@ -52,7 +52,10 @@ pub fn header_start(model: &AppModel) -> Vec> { /// Build the right side of the header bar. pub fn header_end(_model: &AppModel) -> Vec> { - vec![button::icon(icon::from_name("dialog-information-symbolic")) - .on_press(AppMessage::ToggleContextPage(ContextPage::Properties)) - .into()] + vec![ + // Info panel toggle + button::icon(icon::from_name("dialog-information-symbolic")) + .on_press(AppMessage::ToggleContextPage(ContextPage::Properties)) + .into(), + ] } diff --git a/src/app/view/panels.rs b/src/app/view/panels.rs index e5c618a..df5bfc8 100644 --- a/src/app/view/panels.rs +++ b/src/app/view/panels.rs @@ -3,7 +3,8 @@ // // Panel content for COSMIC context drawer. -use cosmic::widget::{column, divider, row, text}; +use cosmic::iced::Length; +use cosmic::widget::{button, column, divider, horizontal_space, icon, row, text}; use cosmic::Element; use crate::app::{AppMessage, AppModel}; @@ -13,8 +14,8 @@ use crate::fl; pub fn properties_panel(model: &AppModel) -> Element<'static, AppMessage> { let mut content = column::with_capacity(16).spacing(8); - // Header. - content = content.push(text::title4(fl!("panel-properties"))); + // Header with action icons + content = content.push(panel_header(model)); // Display document metadata if available. if let Some(ref doc) = model.document { @@ -117,3 +118,30 @@ fn meta_row_small(label: String, value: String) -> Element<'static, AppMessage> .push(text::caption(value)) .into() } + +/// Panel header with title and action icon buttons. +fn panel_header(model: &AppModel) -> Element<'static, AppMessage> { + let has_doc = model.document.is_some(); + + row::with_capacity(5) + .spacing(4) + .align_y(cosmic::iced::Alignment::Center) + .push(text::title4(fl!("panel-properties"))) + .push(horizontal_space().width(Length::Fill)) + .push( + button::icon(icon::from_name("image-x-generic-symbolic")) + .on_press_maybe(has_doc.then_some(AppMessage::SetAsWallpaper)) + .tooltip(fl!("action-set-wallpaper")) + ) + // .push( + // button::icon(icon::from_name("system-run-symbolic")) + // .on_press_maybe(has_doc.then_some(AppMessage::NoOp)) // TODO: Implement + // .tooltip(fl!("action-open-with")) + // ) + // .push( + // button::icon(icon::from_name("system-file-manager-symbolic")) + // .on_press_maybe(has_doc.then_some(AppMessage::NoOp)) // TODO: Implement + // .tooltip(fl!("action-show-in-folder")) + // ) + .into() +}