feat: add set as wallpaper functionality
Add ability to set current image as desktop wallpaper with keyboard shortcut 'W' and icon button in Properties panel. Supports COSMIC, GNOME, KDE, XFCE, and tiling window managers via automatic detection and fallback mechanism. Implementation uses wallpaper crate with custom COSMIC config file integration and gsettings/feh fallbacks.
This commit is contained in:
parent
ca7661fa3e
commit
220a886acc
12 changed files with 399 additions and 24 deletions
160
Cargo.lock
generated
160
Cargo.lock
generated
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -244,6 +244,9 @@ fn handle_key_press(key: Key, modifiers: Modifiers) -> Option<AppMessage> {
|
|||
}
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,10 @@ pub fn header_start(model: &AppModel) -> Vec<Element<'_, AppMessage>> {
|
|||
|
||||
/// Build the right side of the header bar.
|
||||
pub fn header_end(_model: &AppModel) -> Vec<Element<'_, AppMessage>> {
|
||||
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(),
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue