Compare commits
21 commits
f1b1f8d799
...
57ab1ecbf4
| Author | SHA1 | Date | |
|---|---|---|---|
| 57ab1ecbf4 | |||
| 6f3adcd993 | |||
|
|
35e115fdb5 | ||
|
|
69c35ab80f | ||
|
|
d080bc85af | ||
|
|
338354c4d0 | ||
| f0538190d9 | |||
| 94c3e6c551 | |||
| af843d204d | |||
| 11d435770e | |||
| 1cf17dcde8 | |||
| 33a5c8ff99 | |||
| 8b51af1632 | |||
| 4b6d345139 | |||
| 0595296609 | |||
| 8fb2b15c68 | |||
| e8d62ae43d | |||
| a025fd6380 | |||
| 02adcc3cf6 | |||
| 04abd13d93 | |||
|
|
9bcfe7a1f5 |
17 changed files with 851 additions and 270 deletions
283
Cargo.lock
generated
283
Cargo.lock
generated
|
|
@ -255,7 +255,7 @@ version = "1.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -266,7 +266,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
|||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -328,12 +328,6 @@ version = "0.7.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "as-raw-xcb-connection"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b"
|
||||
|
||||
[[package]]
|
||||
name = "as-slice"
|
||||
version = "0.2.1"
|
||||
|
|
@ -979,7 +973,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "clipboard_macos"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c"
|
||||
source = "git+https://forge.aditua.com/leyoda/window_clipboard.git?branch=yoda-x11-optional#319db02e5219c557c8f03b0e33a8eb4075cabb85"
|
||||
dependencies = [
|
||||
"objc",
|
||||
"objc-foundation",
|
||||
|
|
@ -989,7 +983,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "clipboard_wayland"
|
||||
version = "0.2.2"
|
||||
source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c"
|
||||
source = "git+https://forge.aditua.com/leyoda/window_clipboard.git?branch=yoda-x11-optional#319db02e5219c557c8f03b0e33a8eb4075cabb85"
|
||||
dependencies = [
|
||||
"dnd",
|
||||
"mime 0.1.0",
|
||||
|
|
@ -999,7 +993,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "clipboard_x11"
|
||||
version = "0.4.2"
|
||||
source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c"
|
||||
source = "git+https://forge.aditua.com/leyoda/window_clipboard.git?branch=yoda-x11-optional#319db02e5219c557c8f03b0e33a8eb4075cabb85"
|
||||
dependencies = [
|
||||
"thiserror 1.0.69",
|
||||
"x11rb",
|
||||
|
|
@ -1314,14 +1308,13 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "cosmic-config"
|
||||
version = "1.0.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git#c423ad1bfc25057922406c687f2ddc75ead5ab67"
|
||||
dependencies = [
|
||||
"atomicwrites",
|
||||
"cosmic-config-derive",
|
||||
"cosmic-config-derive 1.0.0",
|
||||
"cosmic-settings-daemon",
|
||||
"dirs 6.0.0",
|
||||
"futures-util",
|
||||
"iced_futures",
|
||||
"iced_futures 0.14.0",
|
||||
"known-folders",
|
||||
"notify",
|
||||
"ron 0.12.1",
|
||||
|
|
@ -1332,10 +1325,35 @@ dependencies = [
|
|||
"zbus 5.14.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmic-config"
|
||||
version = "1.0.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic#dad5f1e2731dbdccb3044f136a81f18dfead9de4"
|
||||
dependencies = [
|
||||
"atomicwrites",
|
||||
"cosmic-config-derive 1.0.0 (git+https://github.com/pop-os/libcosmic)",
|
||||
"dirs 6.0.0",
|
||||
"iced_futures 0.14.0 (git+https://github.com/pop-os/libcosmic)",
|
||||
"known-folders",
|
||||
"notify",
|
||||
"ron 0.12.1",
|
||||
"serde",
|
||||
"tracing",
|
||||
"xdg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmic-config-derive"
|
||||
version = "1.0.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git#c423ad1bfc25057922406c687f2ddc75ead5ab67"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmic-config-derive"
|
||||
version = "1.0.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic#dad5f1e2731dbdccb3044f136a81f18dfead9de4"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
|
|
@ -1368,7 +1386,7 @@ dependencies = [
|
|||
"jiff-icu",
|
||||
"jxl-oxide",
|
||||
"libc",
|
||||
"libcosmic",
|
||||
"libcosmic-yoda",
|
||||
"log",
|
||||
"lzma-rust2",
|
||||
"md-5",
|
||||
|
|
@ -1461,7 +1479,7 @@ name = "cosmic-settings-config"
|
|||
version = "0.1.0"
|
||||
source = "git+https://github.com/pop-os/cosmic-settings-daemon#716da6d6af0b252e2f78aba2ad72ee19ae0241e0"
|
||||
dependencies = [
|
||||
"cosmic-config",
|
||||
"cosmic-config 1.0.0 (git+https://github.com/pop-os/libcosmic)",
|
||||
"ron 0.11.0",
|
||||
"serde",
|
||||
"serde_with",
|
||||
|
|
@ -1479,8 +1497,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cosmic-text"
|
||||
version = "0.18.2"
|
||||
source = "git+https://github.com/pop-os/cosmic-text.git#4d74f795cc771fdcc7ea0f9cacba63fcf036fad6"
|
||||
version = "0.19.0"
|
||||
source = "git+https://forge.aditua.com/leyoda/cosmic-text.git?branch=local%2Fpr-503#63072bbe29a1657d82cd3deb5db45070404ec7a1"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"fontdb",
|
||||
|
|
@ -1498,16 +1516,16 @@ dependencies = [
|
|||
"unicode-linebreak",
|
||||
"unicode-script",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmic-theme"
|
||||
version = "1.0.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git#c423ad1bfc25057922406c687f2ddc75ead5ab67"
|
||||
dependencies = [
|
||||
"almost",
|
||||
"configparser",
|
||||
"cosmic-config",
|
||||
"cosmic-config 1.0.0",
|
||||
"csscolorparser",
|
||||
"dirs 6.0.0",
|
||||
"palette",
|
||||
|
|
@ -1615,12 +1633,6 @@ dependencies = [
|
|||
"uncased",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor-lite"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e162d0c2e2068eb736b71e5597eff0b9944e6b973cd9f37b6a288ab9bf20e300"
|
||||
|
||||
[[package]]
|
||||
name = "cursor-icon"
|
||||
version = "1.2.0"
|
||||
|
|
@ -1801,7 +1813,7 @@ dependencies = [
|
|||
"libc",
|
||||
"option-ext",
|
||||
"redox_users 0.5.2",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1850,7 +1862,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "dnd"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c"
|
||||
source = "git+https://forge.aditua.com/leyoda/window_clipboard.git?branch=yoda-x11-optional#319db02e5219c557c8f03b0e33a8eb4075cabb85"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"mime 0.1.0",
|
||||
|
|
@ -1879,45 +1891,6 @@ name = "dpi"
|
|||
version = "0.1.2"
|
||||
source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366"
|
||||
|
||||
[[package]]
|
||||
name = "drm"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0f8a69e60d75ae7dab4ef26a59ca99f2a89d4c142089b537775ae0c198bdcde"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"bytemuck",
|
||||
"drm-ffi",
|
||||
"drm-fourcc",
|
||||
"rustix 0.38.44",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "drm-ffi"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41334f8405792483e32ad05fbb9c5680ff4e84491883d2947a4757dc54cb2ac6"
|
||||
dependencies = [
|
||||
"drm-sys",
|
||||
"rustix 0.38.44",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "drm-fourcc"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4"
|
||||
|
||||
[[package]]
|
||||
name = "drm-sys"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d09ff881f92f118b11105ba5e34ff8f4adf27b30dae8f12e28c193af1c83176"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"linux-raw-sys 0.6.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dyn-clone"
|
||||
version = "1.0.20"
|
||||
|
|
@ -2011,7 +1984,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2489,7 +2462,7 @@ dependencies = [
|
|||
"libc",
|
||||
"log",
|
||||
"rustversion",
|
||||
"windows-link 0.2.1",
|
||||
"windows-link 0.1.3",
|
||||
"windows-result 0.4.1",
|
||||
]
|
||||
|
||||
|
|
@ -2618,7 +2591,7 @@ dependencies = [
|
|||
"gobject-sys",
|
||||
"libc",
|
||||
"system-deps",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2988,13 +2961,12 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git#c423ad1bfc25057922406c687f2ddc75ead5ab67"
|
||||
dependencies = [
|
||||
"dnd",
|
||||
"iced_accessibility",
|
||||
"iced_core",
|
||||
"iced_core 0.14.0",
|
||||
"iced_debug",
|
||||
"iced_futures",
|
||||
"iced_futures 0.14.0",
|
||||
"iced_program",
|
||||
"iced_renderer",
|
||||
"iced_runtime",
|
||||
|
|
@ -3009,7 +2981,6 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_accessibility"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git#c423ad1bfc25057922406c687f2ddc75ead5ab67"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_winit",
|
||||
|
|
@ -3018,7 +2989,6 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_core"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git#c423ad1bfc25057922406c687f2ddc75ead5ab67"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"bytes",
|
||||
|
|
@ -3039,23 +3009,43 @@ dependencies = [
|
|||
"window_clipboard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iced_core"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic#dad5f1e2731dbdccb3044f136a81f18dfead9de4"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"bytes",
|
||||
"dnd",
|
||||
"glam",
|
||||
"lilt",
|
||||
"log",
|
||||
"mime 0.1.0",
|
||||
"num-traits",
|
||||
"palette",
|
||||
"raw-window-handle",
|
||||
"rustc-hash 2.1.2",
|
||||
"smol_str",
|
||||
"thiserror 2.0.18",
|
||||
"web-time",
|
||||
"window_clipboard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iced_debug"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git#c423ad1bfc25057922406c687f2ddc75ead5ab67"
|
||||
dependencies = [
|
||||
"iced_core",
|
||||
"iced_futures",
|
||||
"iced_core 0.14.0",
|
||||
"iced_futures 0.14.0",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iced_futures"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git#c423ad1bfc25057922406c687f2ddc75ead5ab67"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"iced_core",
|
||||
"iced_core 0.14.0",
|
||||
"log",
|
||||
"rustc-hash 2.1.2",
|
||||
"tokio",
|
||||
|
|
@ -3063,17 +3053,29 @@ dependencies = [
|
|||
"wasmtimer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iced_futures"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic#dad5f1e2731dbdccb3044f136a81f18dfead9de4"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"iced_core 0.14.0 (git+https://github.com/pop-os/libcosmic)",
|
||||
"log",
|
||||
"rustc-hash 2.1.2",
|
||||
"wasm-bindgen-futures",
|
||||
"wasmtimer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iced_graphics"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git#c423ad1bfc25057922406c687f2ddc75ead5ab67"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"bytemuck",
|
||||
"cosmic-text",
|
||||
"half",
|
||||
"iced_core",
|
||||
"iced_futures",
|
||||
"iced_core 0.14.0",
|
||||
"iced_futures 0.14.0",
|
||||
"image",
|
||||
"kamadak-exif",
|
||||
"log",
|
||||
|
|
@ -3087,7 +3089,6 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_program"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git#c423ad1bfc25057922406c687f2ddc75ead5ab67"
|
||||
dependencies = [
|
||||
"iced_graphics",
|
||||
"iced_runtime",
|
||||
|
|
@ -3096,7 +3097,6 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_renderer"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git#c423ad1bfc25057922406c687f2ddc75ead5ab67"
|
||||
dependencies = [
|
||||
"iced_graphics",
|
||||
"iced_tiny_skia",
|
||||
|
|
@ -3108,13 +3108,12 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_runtime"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git#c423ad1bfc25057922406c687f2ddc75ead5ab67"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"cosmic-client-toolkit",
|
||||
"dnd",
|
||||
"iced_core",
|
||||
"iced_futures",
|
||||
"iced_core 0.14.0",
|
||||
"iced_futures 0.14.0",
|
||||
"raw-window-handle",
|
||||
"thiserror 2.0.18",
|
||||
"window_clipboard",
|
||||
|
|
@ -3123,7 +3122,6 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_tiny_skia"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git#c423ad1bfc25057922406c687f2ddc75ead5ab67"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"cosmic-text",
|
||||
|
|
@ -3140,9 +3138,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_wgpu"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git#c423ad1bfc25057922406c687f2ddc75ead5ab67"
|
||||
dependencies = [
|
||||
"as-raw-xcb-connection",
|
||||
"bitflags 2.11.1",
|
||||
"bytemuck",
|
||||
"cosmic-client-toolkit",
|
||||
|
|
@ -3159,19 +3155,16 @@ dependencies = [
|
|||
"rustc-hash 2.1.2",
|
||||
"rustix 0.38.44",
|
||||
"thiserror 2.0.18",
|
||||
"tiny-xlib",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-protocols",
|
||||
"wayland-sys",
|
||||
"wgpu",
|
||||
"x11rb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iced_widget"
|
||||
version = "0.14.2"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git#c423ad1bfc25057922406c687f2ddc75ead5ab67"
|
||||
dependencies = [
|
||||
"cosmic-client-toolkit",
|
||||
"dnd",
|
||||
|
|
@ -3189,13 +3182,12 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_winit"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git#c423ad1bfc25057922406c687f2ddc75ead5ab67"
|
||||
dependencies = [
|
||||
"cosmic-client-toolkit",
|
||||
"cursor-icon",
|
||||
"dnd",
|
||||
"iced_debug",
|
||||
"iced_futures",
|
||||
"iced_futures 0.14.0",
|
||||
"iced_graphics",
|
||||
"iced_program",
|
||||
"iced_runtime",
|
||||
|
|
@ -3892,7 +3884,7 @@ dependencies = [
|
|||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
"serde_core",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4234,7 +4226,7 @@ version = "1.4.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a1886916523694cd6ea3d175f03a1e5010699a2a4cc13696d83d7bea1d80638"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4309,15 +4301,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f"
|
||||
|
||||
[[package]]
|
||||
name = "libcosmic"
|
||||
version = "1.0.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git#c423ad1bfc25057922406c687f2ddc75ead5ab67"
|
||||
name = "libcosmic-yoda"
|
||||
version = "0.1.0-yoda.2"
|
||||
dependencies = [
|
||||
"apply",
|
||||
"ashpd 0.12.3",
|
||||
"auto_enums",
|
||||
"cosmic-client-toolkit",
|
||||
"cosmic-config",
|
||||
"cosmic-config 1.0.0",
|
||||
"cosmic-freedesktop-icons",
|
||||
"cosmic-settings-config",
|
||||
"cosmic-settings-daemon",
|
||||
|
|
@ -4330,8 +4321,8 @@ dependencies = [
|
|||
"i18n-embed",
|
||||
"i18n-embed-fl",
|
||||
"iced",
|
||||
"iced_core",
|
||||
"iced_futures",
|
||||
"iced_core 0.14.0",
|
||||
"iced_futures 0.14.0",
|
||||
"iced_renderer",
|
||||
"iced_runtime",
|
||||
"iced_tiny_skia",
|
||||
|
|
@ -4420,12 +4411,6 @@ version = "0.4.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.12.1"
|
||||
|
|
@ -4663,7 +4648,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c"
|
||||
source = "git+https://forge.aditua.com/leyoda/window_clipboard.git?branch=yoda-x11-optional#319db02e5219c557c8f03b0e33a8eb4075cabb85"
|
||||
dependencies = [
|
||||
"smithay-clipboard",
|
||||
]
|
||||
|
|
@ -4897,7 +4882,7 @@ version = "0.50.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5274,7 +5259,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6261,7 +6246,7 @@ dependencies = [
|
|||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.12.1",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6635,7 +6620,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6643,12 +6628,10 @@ name = "softbuffer"
|
|||
version = "0.4.1"
|
||||
source = "git+https://github.com/pop-os/softbuffer?tag=cosmic-4.0#a3f77e251e7422803f693df6e3fc313c010c4dcb"
|
||||
dependencies = [
|
||||
"as-raw-xcb-connection",
|
||||
"bytemuck",
|
||||
"cfg_aliases",
|
||||
"cocoa",
|
||||
"core-graphics",
|
||||
"drm",
|
||||
"fastrand",
|
||||
"foreign-types",
|
||||
"js-sys",
|
||||
|
|
@ -6658,14 +6641,12 @@ dependencies = [
|
|||
"raw-window-handle",
|
||||
"redox_syscall 0.5.18",
|
||||
"rustix 0.38.44",
|
||||
"tiny-xlib",
|
||||
"wasm-bindgen",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-sys",
|
||||
"web-sys",
|
||||
"windows-sys 0.52.0",
|
||||
"x11rb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6857,7 +6838,7 @@ dependencies = [
|
|||
"getrandom 0.4.2",
|
||||
"once_cell",
|
||||
"rustix 1.1.4",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -7038,19 +7019,6 @@ dependencies = [
|
|||
"strict-num",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-xlib"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0324504befd01cab6e0c994f34b2ffa257849ee019d3fb3b64fb2c858887d89e"
|
||||
dependencies = [
|
||||
"as-raw-xcb-connection",
|
||||
"ctor-lite",
|
||||
"libloading",
|
||||
"pkg-config",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.3"
|
||||
|
|
@ -7299,7 +7267,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
|
|||
dependencies = [
|
||||
"memoffset",
|
||||
"tempfile",
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -8008,7 +7976,7 @@ version = "0.1.11"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -8020,7 +7988,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|||
[[package]]
|
||||
name = "window_clipboard"
|
||||
version = "0.4.1"
|
||||
source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c"
|
||||
source = "git+https://forge.aditua.com/leyoda/window_clipboard.git?branch=yoda-x11-optional#319db02e5219c557c8f03b0e33a8eb4075cabb85"
|
||||
dependencies = [
|
||||
"clipboard-win",
|
||||
"clipboard_macos",
|
||||
|
|
@ -8560,7 +8528,6 @@ dependencies = [
|
|||
"winit-wayland",
|
||||
"winit-web",
|
||||
"winit-win32",
|
||||
"winit-x11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -8611,7 +8578,6 @@ dependencies = [
|
|||
"smol_str",
|
||||
"tracing",
|
||||
"winit-core",
|
||||
"x11-dl",
|
||||
"xkbcommon-dl",
|
||||
]
|
||||
|
||||
|
|
@ -8729,29 +8695,6 @@ dependencies = [
|
|||
"winit-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winit-x11"
|
||||
version = "0.31.0-beta.2"
|
||||
source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"bytemuck",
|
||||
"calloop",
|
||||
"cursor-icon",
|
||||
"dpi",
|
||||
"libc",
|
||||
"percent-encoding",
|
||||
"raw-window-handle",
|
||||
"rustix 1.1.4",
|
||||
"smol_str",
|
||||
"tracing",
|
||||
"winit-common",
|
||||
"winit-core",
|
||||
"x11-dl",
|
||||
"x11rb",
|
||||
"xkbcommon-dl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.15"
|
||||
|
|
@ -8873,31 +8816,15 @@ dependencies = [
|
|||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11-dl"
|
||||
version = "2.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"once_cell",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11rb"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
|
||||
dependencies = [
|
||||
"as-raw-xcb-connection",
|
||||
"gethostname",
|
||||
"libc",
|
||||
"libloading",
|
||||
"once_cell",
|
||||
"rustix 1.1.4",
|
||||
"x11rb-protocol",
|
||||
"xcursor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
41
Cargo.toml
41
Cargo.toml
|
|
@ -72,8 +72,9 @@ version = "0.18"
|
|||
default-features = false
|
||||
features = ["fs", "io", "macros", "polling", "runtime"]
|
||||
|
||||
[dependencies.libcosmic]
|
||||
git = "https://github.com/pop-os/libcosmic.git"
|
||||
# Yoda fork — depend on libcosmic-yoda directly by path (no git/no patch).
|
||||
[dependencies.libcosmic-yoda]
|
||||
path = "../libcosmic"
|
||||
default-features = false
|
||||
#TODO: a11y feature crashes
|
||||
features = [
|
||||
|
|
@ -82,7 +83,7 @@ features = [
|
|||
"autosize",
|
||||
"multi-window",
|
||||
"tokio",
|
||||
"winit",
|
||||
"wayland",
|
||||
"surface-message",
|
||||
]
|
||||
|
||||
|
|
@ -110,15 +111,15 @@ default = [
|
|||
"wayland",
|
||||
"wgpu",
|
||||
]
|
||||
dbus-config = ["libcosmic/dbus-config"]
|
||||
desktop = ["libcosmic/desktop", "dep:cosmic-mime-apps", "dep:xdg"]
|
||||
dbus-config = ["libcosmic-yoda/dbus-config"]
|
||||
desktop = ["libcosmic-yoda/desktop", "dep:cosmic-mime-apps", "dep:xdg"]
|
||||
desktop-applet = []
|
||||
gvfs = ["dep:gio", "dep:glib"]
|
||||
io-uring = ["compio/io-uring"]
|
||||
jemalloc = ["dep:tikv-jemallocator"]
|
||||
notify = ["dep:notify-rust"]
|
||||
wayland = ["libcosmic/wayland", "dep:cctk", "dep:wayland-client"]
|
||||
wgpu = ["libcosmic/wgpu"]
|
||||
wayland = ["libcosmic-yoda/wayland", "dep:cctk", "dep:wayland-client"]
|
||||
wgpu = ["libcosmic-yoda/wgpu"]
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 1
|
||||
|
|
@ -144,20 +145,22 @@ fastrand = "2"
|
|||
test-log = "0.2"
|
||||
tokio = { version = "1", features = ["rt", "macros"] }
|
||||
|
||||
# [patch.'https://github.com/pop-os/cosmic-text']
|
||||
# cosmic-text = { path = "../cosmic-text" }
|
||||
# Yoda fork — libcosmic dep is now a direct path dep (libcosmic-yoda above),
|
||||
# no [patch] block needed anymore. Keeping the block below would be a no-op
|
||||
# since nothing in the dep graph still asks for pop-os/libcosmic.git.
|
||||
|
||||
# [patch.'https://github.com/pop-os/libcosmic']
|
||||
# libcosmic = { path = "../libcosmic" }
|
||||
# cosmic-config = { path = "../libcosmic/cosmic-config" }
|
||||
# cosmic-theme = { path = "../libcosmic/cosmic-theme" }
|
||||
# libcosmic = { git = "https://github.com/pop-os/libcosmic//", branch = "iced-rebase" }
|
||||
# cosmic-config = { git = "https://github.com/pop-os/libcosmic//", branch = "iced-rebase" }
|
||||
# cosmic-theme = { git = "https://github.com/pop-os/libcosmic//", branch = "iced-rebase" }
|
||||
# Yoda wayland cut: redirect window_clipboard (x11 gated behind opt-in
|
||||
# feature) and cosmic-text (PR#503: EAW monospace width fix) to our public
|
||||
# Forgejo forks. The window_clipboard patch is needed to consolidate the
|
||||
# upstream pop-os/libcosmic chain (pulled by cosmic-settings-daemon) onto
|
||||
# the same fork iced uses, otherwise cargo compiles two versions.
|
||||
[patch.'https://github.com/pop-os/window_clipboard.git']
|
||||
window_clipboard = { git = "https://forge.aditua.com/leyoda/window_clipboard.git", branch = "yoda-x11-optional" }
|
||||
dnd = { git = "https://forge.aditua.com/leyoda/window_clipboard.git", branch = "yoda-x11-optional" }
|
||||
mime = { git = "https://forge.aditua.com/leyoda/window_clipboard.git", branch = "yoda-x11-optional" }
|
||||
|
||||
|
||||
# [patch.'https://github.com/pop-os/smithay-clipboard']
|
||||
# smithay-clipboard = { path = "../smithay-clipboard" }
|
||||
[patch.'https://github.com/pop-os/cosmic-text.git']
|
||||
cosmic-text = { git = "https://forge.aditua.com/leyoda/cosmic-text.git", branch = "local/pr-503" }
|
||||
|
||||
[workspace]
|
||||
members = ["cosmic-files-applet"]
|
||||
|
|
|
|||
78
docs/local-performance-and-portal-notes.md
Normal file
78
docs/local-performance-and-portal-notes.md
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
# Local performance and portal notes
|
||||
|
||||
Date: 2026-05-05
|
||||
|
||||
This repository carries a local patch aimed at improving initial directory
|
||||
display latency in large folders, plus a system-level workaround for a COSMIC
|
||||
portal FileChooser crash observed with Firefox and Chromium.
|
||||
|
||||
## Directory listing latency
|
||||
|
||||
The slow path was the initial construction of `Item` values in `src/tab.rs`.
|
||||
On a test folder with about 2000 entries, raw filesystem enumeration and stat
|
||||
calls completed in a few milliseconds, while COSMIC Files took multiple seconds
|
||||
before showing the directory.
|
||||
|
||||
The local patch keeps initial item construction cheap:
|
||||
|
||||
- directory child counts are no longer computed synchronously in
|
||||
`item_from_entry` and `item_from_gvfs_info`;
|
||||
- initial MIME detection uses extension-based `mime_guess`;
|
||||
- regular files use a generic file icon during the initial scan instead of
|
||||
resolving the full MIME icon immediately.
|
||||
|
||||
This keeps folders and `.desktop` files special-cased, while avoiding expensive
|
||||
per-file work for ordinary files during first paint.
|
||||
|
||||
Measured locally during investigation:
|
||||
|
||||
- before the MIME/icon changes: about 3.1 seconds for `~/Téléchargements`;
|
||||
- after avoiding full MIME icon resolution during scan: below the temporary
|
||||
100 ms perf-log threshold for the same folder.
|
||||
|
||||
## File chooser portal workaround
|
||||
|
||||
Firefox and Chromium were failing to open `Save As` on the first attempt because
|
||||
`xdg-desktop-portal-cosmic` crashed while handling `FileChooser`.
|
||||
|
||||
Logs showed:
|
||||
|
||||
```text
|
||||
Backend call failed: Remote peer disconnected
|
||||
xdg-desktop-portal-cosmic ... status=11/SEGV
|
||||
```
|
||||
|
||||
The working local system workaround is to remove
|
||||
`org.freedesktop.impl.portal.FileChooser` from:
|
||||
|
||||
```text
|
||||
/usr/share/xdg-desktop-portal/portals/cosmic.portal
|
||||
```
|
||||
|
||||
so the file chooser falls back to GTK. The resulting local `cosmic.portal`
|
||||
keeps COSMIC for the other interfaces:
|
||||
|
||||
```ini
|
||||
[portal]
|
||||
DBusName=org.freedesktop.impl.portal.desktop.cosmic
|
||||
Interfaces=org.freedesktop.impl.portal.Access;org.freedesktop.impl.portal.Screenshot;org.freedesktop.impl.portal.Settings;org.freedesktop.impl.portal.ScreenCast
|
||||
UseIn=COSMIC
|
||||
```
|
||||
|
||||
Backups created locally:
|
||||
|
||||
```text
|
||||
/usr/share/xdg-desktop-portal/portals/cosmic.portal.bak-codex-20260505-filechooser
|
||||
/usr/share/xdg-desktop-portal/cosmic-portals.conf.bak-codex-20260505
|
||||
/usr/share/xdg-desktop-portal/cosmic-portals.conf.bak-codex-20260505-2
|
||||
```
|
||||
|
||||
After editing portal files, restart:
|
||||
|
||||
```sh
|
||||
systemctl --user restart xdg-desktop-portal.service xdg-desktop-portal-gtk.service
|
||||
```
|
||||
|
||||
Package updates may overwrite `/usr/share/xdg-desktop-portal/portals/cosmic.portal`.
|
||||
If `Save As` starts needing two attempts again, re-check that `FileChooser` has
|
||||
not been reintroduced in `cosmic.portal`.
|
||||
|
|
@ -96,6 +96,7 @@ save-file = Save file
|
|||
|
||||
## Open With Dialog
|
||||
open-with-title = How do you want to open "{$name}"?
|
||||
open-with-set-default = Always use this app for this file type
|
||||
browse-store = Browse {$store}
|
||||
other-apps = Other applications
|
||||
related-apps = Related applications
|
||||
|
|
@ -138,6 +139,11 @@ open-with = Open with
|
|||
owner = Owner
|
||||
group = Group
|
||||
other = Other
|
||||
toolbar = Toolbar
|
||||
toolbar-available = Available
|
||||
toolbar-empty-hint = No buttons. Drag or add from below.
|
||||
toolbar-reset = Reset to defaults
|
||||
parent-directory = Parent directory
|
||||
mixed = Mixed
|
||||
### Mode 0
|
||||
none = None
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ save-file = Enregistrer fichier
|
|||
## Open With Dialog
|
||||
|
||||
open-with-title = Comment souhaitez-vous ouvrir "{ $name }" ?
|
||||
open-with-set-default = Toujours utiliser cette application pour ce type de fichier
|
||||
browse-store = Parcourir { $store }
|
||||
|
||||
## Permanently delete Dialog
|
||||
|
|
@ -130,6 +131,11 @@ open-with = Ouvrir avec
|
|||
owner = Propriétaire
|
||||
group = Groupe
|
||||
other = Autre
|
||||
toolbar = Barre d'outils
|
||||
toolbar-available = Disponibles
|
||||
toolbar-empty-hint = Aucun bouton. Glisser-déposer ou ajouter depuis la liste ci-dessous.
|
||||
toolbar-reset = Rétablir par défaut
|
||||
parent-directory = Dossier parent
|
||||
|
||||
### Mode 0
|
||||
|
||||
|
|
|
|||
476
src/app.rs
476
src/app.rs
|
|
@ -51,6 +51,8 @@ use notify_debouncer_full::{
|
|||
};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use slotmap::Key as SlotMapKey;
|
||||
#[cfg(feature = "notify")]
|
||||
use std::sync::Mutex;
|
||||
use std::{
|
||||
any::TypeId,
|
||||
collections::{BTreeMap, BTreeSet, HashMap, VecDeque},
|
||||
|
|
@ -61,7 +63,7 @@ use std::{
|
|||
path::{Path, PathBuf},
|
||||
pin::Pin,
|
||||
process,
|
||||
sync::{Arc, LazyLock, Mutex},
|
||||
sync::{Arc, LazyLock},
|
||||
time::{self, Duration, Instant},
|
||||
};
|
||||
use tokio::sync::mpsc;
|
||||
|
|
@ -77,7 +79,7 @@ use crate::{
|
|||
},
|
||||
config::{
|
||||
AppTheme, Config, DesktopConfig, Favorite, IconSizes, State, TIME_CONFIG_ID, TabConfig,
|
||||
TimeConfig, TypeToSearch,
|
||||
TimeConfig, ToolbarAction, TypeToSearch, default_toolbar,
|
||||
},
|
||||
context_action,
|
||||
dialog::{Dialog, DialogKind, DialogMessage, DialogResult, DialogSettings},
|
||||
|
|
@ -85,7 +87,7 @@ use crate::{
|
|||
key_bind::key_binds,
|
||||
localize::LANGUAGE_SORTER,
|
||||
menu,
|
||||
mime_app::{self, MimeApp, MimeAppCache},
|
||||
mime_app::{MimeApp, MimeAppCache},
|
||||
mime_icon,
|
||||
mounter::{MOUNTERS, MounterAuth, MounterItem, MounterItems, MounterKey, MounterMessage},
|
||||
operation::{
|
||||
|
|
@ -145,6 +147,105 @@ pub struct Flags {
|
|||
pub uris: Vec<url::Url>,
|
||||
}
|
||||
|
||||
/// Yoda phase 3: MIME for the DnD payload carried when a user drags a
|
||||
/// toolbar row in the Settings editor. A single byte = ToolbarAction
|
||||
/// discriminant (see `ToolbarAction::to_u8`).
|
||||
const TOOLBAR_MIME: &str = "application/x-cosmic-files-toolbar-action";
|
||||
|
||||
/// Yoda phase 3: DnD payload wrapping a ToolbarAction discriminant.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ToolbarActionPayload(pub u8);
|
||||
|
||||
impl cosmic::iced::clipboard::mime::AsMimeTypes for ToolbarActionPayload {
|
||||
fn available(&self) -> std::borrow::Cow<'static, [String]> {
|
||||
std::borrow::Cow::Owned(vec![TOOLBAR_MIME.to_owned()])
|
||||
}
|
||||
fn as_bytes(&self, mime_type: &str) -> Option<std::borrow::Cow<'static, [u8]>> {
|
||||
if mime_type == TOOLBAR_MIME {
|
||||
Some(std::borrow::Cow::Owned(vec![self.0]))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl cosmic::iced::clipboard::mime::AllowedMimeTypes for ToolbarActionPayload {
|
||||
fn allowed() -> std::borrow::Cow<'static, [String]> {
|
||||
std::borrow::Cow::Owned(vec![TOOLBAR_MIME.to_owned()])
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<(Vec<u8>, String)> for ToolbarActionPayload {
|
||||
type Error = ();
|
||||
fn try_from((data, _mime): (Vec<u8>, String)) -> Result<Self, Self::Error> {
|
||||
if data.len() == 1 {
|
||||
Ok(Self(data[0]))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Yoda phase 3 helper: map a ToolbarAction to its button UI (icon name,
|
||||
/// localized label, app Message). Shared by the toolbar renderer in
|
||||
/// `view()` and by the Settings page row renderer so the two stay in
|
||||
/// sync.
|
||||
fn toolbar_action_ui(a: ToolbarAction) -> (&'static str, String, Message) {
|
||||
match a {
|
||||
ToolbarAction::LocationUp => (
|
||||
"go-up-symbolic",
|
||||
fl!("parent-directory"),
|
||||
Action::LocationUp.message(None),
|
||||
),
|
||||
ToolbarAction::Reload => (
|
||||
"view-refresh-symbolic",
|
||||
fl!("reload-folder"),
|
||||
Action::Reload.message(None),
|
||||
),
|
||||
ToolbarAction::NewFolder => (
|
||||
"folder-new-symbolic",
|
||||
fl!("new-folder"),
|
||||
Action::NewFolder.message(None),
|
||||
),
|
||||
ToolbarAction::NewFile => (
|
||||
"document-new-symbolic",
|
||||
fl!("new-file"),
|
||||
Action::NewFile.message(None),
|
||||
),
|
||||
ToolbarAction::Rename => (
|
||||
"pencil-symbolic",
|
||||
fl!("rename"),
|
||||
Action::Rename.message(None),
|
||||
),
|
||||
ToolbarAction::Delete => (
|
||||
"edit-delete-symbolic",
|
||||
fl!("delete"),
|
||||
Action::Delete.message(None),
|
||||
),
|
||||
ToolbarAction::Cut => ("edit-cut-symbolic", fl!("cut"), Action::Cut.message(None)),
|
||||
ToolbarAction::Copy => (
|
||||
"edit-copy-symbolic",
|
||||
fl!("copy"),
|
||||
Action::Copy.message(None),
|
||||
),
|
||||
ToolbarAction::Paste => (
|
||||
"edit-paste-symbolic",
|
||||
fl!("paste"),
|
||||
Action::Paste.message(None),
|
||||
),
|
||||
ToolbarAction::ToggleShowHidden => (
|
||||
"view-reveal-symbolic",
|
||||
fl!("show-hidden-files"),
|
||||
Action::ToggleShowHidden.message(None),
|
||||
),
|
||||
ToolbarAction::OpenTerminal => (
|
||||
"utilities-terminal-symbolic",
|
||||
fl!("open-in-terminal"),
|
||||
Action::OpenTerminal.message(None),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Action {
|
||||
About,
|
||||
|
|
@ -407,6 +508,7 @@ pub enum Message {
|
|||
OpenWithBrowse,
|
||||
OpenWithDialog(Option<Entity>),
|
||||
OpenWithSelection(usize),
|
||||
OpenWithToggleDefault(bool),
|
||||
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
|
||||
Overlap(window::Id, OverlapNotifyEvent),
|
||||
Paste(Option<Entity>),
|
||||
|
|
@ -447,6 +549,22 @@ pub enum Message {
|
|||
SearchInput(String),
|
||||
SetShowDetails(bool),
|
||||
SetShowRecents(bool),
|
||||
/// Yoda phase 3 — toolbar editing messages.
|
||||
ToolbarAdd(ToolbarAction),
|
||||
ToolbarRemove(ToolbarAction),
|
||||
ToolbarReorder {
|
||||
src: ToolbarAction,
|
||||
target: ToolbarAction,
|
||||
},
|
||||
/// Move one step up (toward index 0) inside the enabled toolbar list.
|
||||
ToolbarMoveUp(ToolbarAction),
|
||||
/// Move one step down (toward the end) inside the enabled toolbar list.
|
||||
ToolbarMoveDown(ToolbarAction),
|
||||
ToolbarReset,
|
||||
/// Click on a toolbar button (via segmented_button activation).
|
||||
ToolbarTabActivate(segmented_button::Entity),
|
||||
/// Drag-reorder inside the toolbar (via segmented_button drag).
|
||||
ToolbarTabReorder(segmented_button::ReorderEvent),
|
||||
SetTypeToSearch(TypeToSearch),
|
||||
SystemThemeModeChange,
|
||||
Size(window::Id, Size),
|
||||
|
|
@ -577,6 +695,9 @@ pub enum DialogPage {
|
|||
mime: mime_guess::Mime,
|
||||
selected: usize,
|
||||
store_opt: Option<MimeApp>,
|
||||
/// When true, the chosen app is written to mimeapps.list as the
|
||||
/// default handler for `mime` after it spawns.
|
||||
set_default: bool,
|
||||
},
|
||||
PermanentlyDelete {
|
||||
paths: Box<[PathBuf]>,
|
||||
|
|
@ -734,6 +855,11 @@ pub struct App {
|
|||
nav_bar_context_id: segmented_button::Entity,
|
||||
nav_model: segmented_button::SingleSelectModel,
|
||||
tab_model: segmented_button::Model<segmented_button::SingleSelect>,
|
||||
/// Yoda phase 3: segmented_button model mirroring config.toolbar so the
|
||||
/// toolbar row gets free drag-reorder + click activation (same widget
|
||||
/// that powers the tab bar, its reorder is proven to work in this
|
||||
/// setup unlike the generic dnd_source/dnd_destination wrappers).
|
||||
toolbar_model: segmented_button::Model<segmented_button::SingleSelect>,
|
||||
config_handler: Option<cosmic_config::Config>,
|
||||
state_handler: Option<cosmic_config::Config>,
|
||||
config: Config,
|
||||
|
|
@ -906,7 +1032,7 @@ impl App {
|
|||
for path in paths.iter().map(AsRef::as_ref) {
|
||||
match DesktopEntry::from_path::<&str>(path, None) {
|
||||
Ok(entry) => match entry.exec() {
|
||||
Some(exec) => match mime_app::exec_to_command(exec, &[] as &[&str; 0]) {
|
||||
Some(exec) => match crate::mime_app::exec_to_command(exec, &[] as &[&str; 0]) {
|
||||
Some(commands) => {
|
||||
let cwd_opt = entry.desktop_entry("Path");
|
||||
|
||||
|
|
@ -1081,7 +1207,7 @@ impl App {
|
|||
.sort_by(|a, b| (b.1.width * b.1.height).total_cmp(&(a.1.width * b.1.height)));
|
||||
|
||||
for (w_id, overlap) in sorted_overlaps {
|
||||
let Some((bl, br, tl, tr, mut size)) = self.layer_sizes.get(w_id).map(|s| {
|
||||
let Some((bl, br, tl, tr, size)) = self.layer_sizes.get(w_id).map(|s| {
|
||||
(
|
||||
Rectangle::new(
|
||||
Point::new(0., s.height / 2.),
|
||||
|
|
@ -1131,30 +1257,18 @@ impl App {
|
|||
if tl && !(tr || bl) {
|
||||
*top += min_dim.1;
|
||||
*left += min_dim.0;
|
||||
|
||||
size.height -= min_dim.1;
|
||||
size.width -= min_dim.0;
|
||||
}
|
||||
if tr && !(tl || br) {
|
||||
*top += min_dim.1;
|
||||
*right += min_dim.0;
|
||||
|
||||
size.height -= min_dim.1;
|
||||
size.width -= min_dim.0;
|
||||
}
|
||||
if bl && !(br || tl) {
|
||||
*bottom += min_dim.1;
|
||||
*left += min_dim.0;
|
||||
|
||||
size.height -= min_dim.1;
|
||||
size.width -= min_dim.0;
|
||||
}
|
||||
if br && !(bl || tr) {
|
||||
*bottom += min_dim.1;
|
||||
*right += min_dim.0;
|
||||
|
||||
size.height -= min_dim.1;
|
||||
size.width -= min_dim.0;
|
||||
}
|
||||
}
|
||||
self.margin = overlaps;
|
||||
|
|
@ -1513,12 +1627,18 @@ impl App {
|
|||
) -> Task<Message> {
|
||||
log::info!("rescan_tab {entity:?} {location:?} {selection_paths:?}");
|
||||
let icon_sizes = self.config.tab.icon_sizes;
|
||||
#[cfg(feature = "gvfs")]
|
||||
let mounter_items = self.mounter_items.clone();
|
||||
|
||||
Task::future(async move {
|
||||
let location2 = location.clone();
|
||||
match tokio::task::spawn_blocking(move || location2.scan(icon_sizes)).await {
|
||||
Ok((parent_item_opt, mut items)) => {
|
||||
Ok((parent_item_opt, items)) => {
|
||||
#[cfg(feature = "gvfs")]
|
||||
let mut items = items;
|
||||
#[cfg(not(feature = "gvfs"))]
|
||||
let items = items;
|
||||
|
||||
#[cfg(feature = "gvfs")]
|
||||
{
|
||||
let mounter_paths: Box<[_]> = mounter_items
|
||||
|
|
@ -1688,6 +1808,7 @@ impl App {
|
|||
|
||||
fn update_config(&mut self) -> Task<Message> {
|
||||
self.update_nav_model();
|
||||
self.rebuild_toolbar_model();
|
||||
// Tabs are collected first to placate the borrowck
|
||||
let tabs: Box<[_]> = self.tab_model.iter().collect();
|
||||
// Update main conf and each tab with the new config
|
||||
|
|
@ -1701,6 +1822,52 @@ impl App {
|
|||
Task::batch(commands)
|
||||
}
|
||||
|
||||
/// Yoda phase 3: rebuild `toolbar_model` so it matches `config.toolbar`.
|
||||
/// Called on init and on every config update. Each entity carries the
|
||||
/// associated `ToolbarAction` as data so click/reorder handlers can
|
||||
/// round-trip Entity → ToolbarAction without maintaining a side map.
|
||||
///
|
||||
/// We insert ONLY the icon (no `.text()`) so the toolbar renders as a
|
||||
/// clean icon row — user-visible labels stay in Settings/tooltips on
|
||||
/// other surfaces.
|
||||
fn rebuild_toolbar_model(&mut self) {
|
||||
self.toolbar_model.clear();
|
||||
for action in self.config.toolbar.iter().copied() {
|
||||
let (icon_name, _label, _msg) = toolbar_action_ui(action);
|
||||
self.toolbar_model
|
||||
.insert()
|
||||
.icon(widget::icon::from_name(icon_name).size(16).icon())
|
||||
.data::<ToolbarAction>(action);
|
||||
}
|
||||
}
|
||||
|
||||
/// Yoda phase 3: after a drag-reorder, sync `config.toolbar` with the
|
||||
/// new entity order in `toolbar_model`. Inlines what `config_set!`
|
||||
/// would do (the macro lives inside update()).
|
||||
fn sync_toolbar_config_from_model(&mut self) -> Task<Message> {
|
||||
let new_toolbar: Vec<ToolbarAction> = self
|
||||
.toolbar_model
|
||||
.iter()
|
||||
.filter_map(|e| self.toolbar_model.data::<ToolbarAction>(e).copied())
|
||||
.collect();
|
||||
if new_toolbar == self.config.toolbar {
|
||||
return Task::none();
|
||||
}
|
||||
match self.config_handler.as_ref() {
|
||||
Some(h) => {
|
||||
if let Err(err) = self.config.set_toolbar(h, new_toolbar) {
|
||||
log::warn!("failed to save toolbar order: {err}");
|
||||
}
|
||||
}
|
||||
None => self.config.toolbar = new_toolbar,
|
||||
}
|
||||
// Don't call update_config() — that would rebuild the
|
||||
// toolbar_model from config and undo the reorder the user just
|
||||
// made. The model already has the new order; config is just
|
||||
// catching up for persistence.
|
||||
Task::none()
|
||||
}
|
||||
|
||||
fn update_desktop(&mut self) -> Task<Message> {
|
||||
let needs_reload: Box<[_]> = (self.tab_model.iter())
|
||||
.filter_map(|entity| {
|
||||
|
|
@ -2280,10 +2447,134 @@ impl App {
|
|||
.toggler(self.config.show_recents, Message::SetShowRecents)
|
||||
})
|
||||
.into(),
|
||||
// Yoda phase 3: toolbar editor. Two stacked lists:
|
||||
// - top: enabled buttons in their current order (drag to reorder)
|
||||
// - bottom: available (not-yet-enabled) buttons
|
||||
// Each row's toggle adds/removes; enabled rows are also
|
||||
// drag sources + drop targets.
|
||||
self.toolbar_settings_section(),
|
||||
])
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Yoda phase 3: build the Toolbar settings section.
|
||||
fn toolbar_settings_section(&self) -> Element<'_, Message> {
|
||||
use iced::clipboard::dnd::DndAction;
|
||||
let enabled = &self.config.toolbar;
|
||||
let disabled: Vec<ToolbarAction> = ToolbarAction::ALL
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|a| !enabled.contains(a))
|
||||
.collect();
|
||||
|
||||
let space_xxs = theme::active().cosmic().spacing.space_xxs;
|
||||
|
||||
let drag_icon = |size: u16| -> Element<'static, Message> {
|
||||
widget::icon::from_name("list-drag-handle-symbolic")
|
||||
.size(size)
|
||||
.into()
|
||||
};
|
||||
|
||||
let row_enabled =
|
||||
|action: ToolbarAction, pos: usize, last: usize| -> Element<'_, Message> {
|
||||
let (icon, label, _msg) = toolbar_action_ui(action);
|
||||
let up_btn =
|
||||
widget::button::icon(widget::icon::from_name("go-up-symbolic").size(14));
|
||||
let up_btn = if pos > 0 {
|
||||
up_btn.on_press(Message::ToolbarMoveUp(action))
|
||||
} else {
|
||||
up_btn
|
||||
};
|
||||
let down_btn =
|
||||
widget::button::icon(widget::icon::from_name("go-down-symbolic").size(14));
|
||||
let down_btn = if pos < last {
|
||||
down_btn.on_press(Message::ToolbarMoveDown(action))
|
||||
} else {
|
||||
down_btn
|
||||
};
|
||||
|
||||
let row_content: Element<_> = widget::row::with_children(vec![
|
||||
drag_icon(14),
|
||||
widget::icon::from_name(icon).size(16).into(),
|
||||
widget::text::body(label).width(Length::Fill).into(),
|
||||
up_btn.into(),
|
||||
down_btn.into(),
|
||||
widget::button::icon(widget::icon::from_name("list-remove-symbolic").size(14))
|
||||
.on_press(Message::ToolbarRemove(action))
|
||||
.into(),
|
||||
])
|
||||
.spacing(space_xxs)
|
||||
.align_y(Alignment::Center)
|
||||
.into();
|
||||
|
||||
let row_container = widget::container(row_content)
|
||||
.width(Length::Fill)
|
||||
.padding(space_xxs);
|
||||
|
||||
// Wrap as DnD source (drags itself) + DnD destination (accepts
|
||||
// drops from other enabled rows; on drop, move the src before
|
||||
// this row).
|
||||
let source = widget::dnd_source::<Message, ToolbarActionPayload>(row_container)
|
||||
.drag_content(move || ToolbarActionPayload(action.to_u8()));
|
||||
widget::dnd_destination(source, vec![std::borrow::Cow::Borrowed(TOOLBAR_MIME)])
|
||||
.data_received_for::<ToolbarActionPayload>(
|
||||
move |payload: Option<ToolbarActionPayload>| {
|
||||
match payload.and_then(|p| ToolbarAction::from_u8(p.0)) {
|
||||
Some(src) if src != action => Message::ToolbarReorder {
|
||||
src,
|
||||
target: action,
|
||||
},
|
||||
// No-op if payload missing / malformed / same row.
|
||||
_ => Message::ToolbarReorder {
|
||||
src: action,
|
||||
target: action,
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
.action(DndAction::Move)
|
||||
.into()
|
||||
};
|
||||
|
||||
let row_disabled = |action: ToolbarAction| -> Element<'_, Message> {
|
||||
let (icon, label, _msg) = toolbar_action_ui(action);
|
||||
widget::row::with_children(vec![
|
||||
widget::icon::from_name(icon).size(16).into(),
|
||||
widget::text::body(label).width(Length::Fill).into(),
|
||||
widget::button::icon(widget::icon::from_name("list-add-symbolic").size(14))
|
||||
.on_press(Message::ToolbarAdd(action))
|
||||
.into(),
|
||||
])
|
||||
.spacing(space_xxs)
|
||||
.align_y(Alignment::Center)
|
||||
.padding(space_xxs)
|
||||
.into()
|
||||
};
|
||||
|
||||
let mut section = widget::settings::section().title(fl!("toolbar"));
|
||||
if enabled.is_empty() {
|
||||
section = section.add(widget::text::body(fl!("toolbar-empty-hint")));
|
||||
} else {
|
||||
let last = enabled.len() - 1;
|
||||
for (pos, a) in enabled.iter().copied().enumerate() {
|
||||
section = section.add(row_enabled(a, pos, last));
|
||||
}
|
||||
}
|
||||
|
||||
let mut col = widget::column::with_capacity(3).spacing(space_xxs);
|
||||
col = col.push(section);
|
||||
if !disabled.is_empty() {
|
||||
let mut avail = widget::settings::section().title(fl!("toolbar-available"));
|
||||
for a in disabled {
|
||||
avail = avail.add(row_disabled(a));
|
||||
}
|
||||
col = col.push(avail);
|
||||
}
|
||||
col = col
|
||||
.push(widget::button::standard(fl!("toolbar-reset")).on_press(Message::ToolbarReset));
|
||||
col.into()
|
||||
}
|
||||
|
||||
fn get_apps_for_mime(&self, mime_type: &Mime) -> Vec<(&MimeApp, MimeAppMatch)> {
|
||||
let mut results = Vec::new();
|
||||
|
||||
|
|
@ -2455,6 +2746,7 @@ impl Application for App {
|
|||
nav_bar_context_id: segmented_button::Entity::null(),
|
||||
nav_model: segmented_button::ModelBuilder::default().build(),
|
||||
tab_model: segmented_button::ModelBuilder::default().build(),
|
||||
toolbar_model: segmented_button::ModelBuilder::default().build(),
|
||||
config_handler: flags.config_handler,
|
||||
state_handler: flags.state_handler,
|
||||
config: flags.config,
|
||||
|
|
@ -3232,6 +3524,7 @@ impl Application for App {
|
|||
path,
|
||||
mime,
|
||||
selected,
|
||||
set_default,
|
||||
..
|
||||
} => {
|
||||
let available_apps = self.get_apps_for_mime(&mime);
|
||||
|
|
@ -3250,6 +3543,11 @@ impl Application for App {
|
|||
None,
|
||||
);
|
||||
}
|
||||
// Yoda: persist as default if the user asked for it.
|
||||
if set_default {
|
||||
self.mime_app_cache
|
||||
.set_default(mime.clone(), app.id.clone());
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!(
|
||||
|
|
@ -3872,6 +4170,7 @@ impl Application for App {
|
|||
.and_then(|mime| {
|
||||
self.mime_app_cache.get(&mime).first().cloned()
|
||||
}),
|
||||
set_default: false,
|
||||
},
|
||||
Some(CONFIRM_OPEN_WITH_BUTTON_ID.clone()),
|
||||
);
|
||||
|
|
@ -3883,6 +4182,13 @@ impl Application for App {
|
|||
*selected = index;
|
||||
}
|
||||
}
|
||||
Message::OpenWithToggleDefault(enabled) => {
|
||||
if let Some(DialogPage::OpenWith { set_default, .. }) =
|
||||
self.dialog_pages.front_mut()
|
||||
{
|
||||
*set_default = enabled;
|
||||
}
|
||||
}
|
||||
Message::Paste(entity_opt) => {
|
||||
let entity = entity_opt.unwrap_or_else(|| self.tab_model.active());
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity)
|
||||
|
|
@ -4359,6 +4665,86 @@ impl Application for App {
|
|||
config_set!(show_recents, show_recents);
|
||||
return self.update_config();
|
||||
}
|
||||
Message::ToolbarAdd(action) => {
|
||||
let mut tb = self.config.toolbar.clone();
|
||||
if !tb.contains(&action) {
|
||||
tb.push(action);
|
||||
}
|
||||
config_set!(toolbar, tb);
|
||||
return self.update_config();
|
||||
}
|
||||
Message::ToolbarRemove(action) => {
|
||||
let mut tb = self.config.toolbar.clone();
|
||||
tb.retain(|a| a != &action);
|
||||
config_set!(toolbar, tb);
|
||||
return self.update_config();
|
||||
}
|
||||
Message::ToolbarReorder { src, target } => {
|
||||
let mut tb = self.config.toolbar.clone();
|
||||
if let (Some(src_idx), Some(tgt_idx)) = (
|
||||
tb.iter().position(|a| a == &src),
|
||||
tb.iter().position(|a| a == &target),
|
||||
) && src_idx != tgt_idx
|
||||
{
|
||||
// Pull src out, then insert before the target's new position.
|
||||
let item = tb.remove(src_idx);
|
||||
let new_tgt = if src_idx < tgt_idx {
|
||||
tgt_idx - 1
|
||||
} else {
|
||||
tgt_idx
|
||||
};
|
||||
tb.insert(new_tgt, item);
|
||||
config_set!(toolbar, tb);
|
||||
return self.update_config();
|
||||
}
|
||||
return Task::none();
|
||||
}
|
||||
Message::ToolbarMoveUp(action) => {
|
||||
let mut tb = self.config.toolbar.clone();
|
||||
if let Some(i) = tb.iter().position(|a| a == &action)
|
||||
&& i > 0
|
||||
{
|
||||
tb.swap(i, i - 1);
|
||||
config_set!(toolbar, tb);
|
||||
return self.update_config();
|
||||
}
|
||||
return Task::none();
|
||||
}
|
||||
Message::ToolbarMoveDown(action) => {
|
||||
let mut tb = self.config.toolbar.clone();
|
||||
if let Some(i) = tb.iter().position(|a| a == &action)
|
||||
&& i + 1 < tb.len()
|
||||
{
|
||||
tb.swap(i, i + 1);
|
||||
config_set!(toolbar, tb);
|
||||
return self.update_config();
|
||||
}
|
||||
return Task::none();
|
||||
}
|
||||
Message::ToolbarReset => {
|
||||
config_set!(toolbar, default_toolbar());
|
||||
return self.update_config();
|
||||
}
|
||||
Message::ToolbarTabActivate(entity) => {
|
||||
// Dispatch the stored ToolbarAction's message, then clear
|
||||
// the "active" selection so the button doesn't stay
|
||||
// highlighted after a click (we use segmented_button for
|
||||
// layout/drag but toolbar buttons are action-firing, not
|
||||
// a mutual-exclusive choice).
|
||||
let action = self.toolbar_model.data::<ToolbarAction>(entity).copied();
|
||||
self.toolbar_model.deactivate();
|
||||
if let Some(action) = action {
|
||||
let (_, _, msg) = toolbar_action_ui(action);
|
||||
return self.update(msg);
|
||||
}
|
||||
return Task::none();
|
||||
}
|
||||
Message::ToolbarTabReorder(event) => {
|
||||
let _ = self
|
||||
.toolbar_model
|
||||
.reorder(event.dragged, event.target, event.position);
|
||||
return self.sync_toolbar_config_from_model();
|
||||
}
|
||||
Message::SetTypeToSearch(type_to_search) => {
|
||||
config_set!(type_to_search, type_to_search);
|
||||
return self.update_config();
|
||||
|
|
@ -5110,6 +5496,7 @@ impl Application for App {
|
|||
.and_then(|mime| {
|
||||
self.mime_app_cache.get(&mime).first().cloned()
|
||||
}),
|
||||
set_default: false,
|
||||
},
|
||||
None,
|
||||
);
|
||||
|
|
@ -5953,7 +6340,7 @@ impl Application for App {
|
|||
mime,
|
||||
selected,
|
||||
store_opt,
|
||||
..
|
||||
set_default,
|
||||
} => {
|
||||
let name = match path.file_name() {
|
||||
Some(file_name) => file_name.to_str(),
|
||||
|
|
@ -6038,7 +6425,21 @@ impl Application for App {
|
|||
} else {
|
||||
Length::Shrink
|
||||
}
|
||||
}));
|
||||
}))
|
||||
// Yoda: let the user make this choice stick. A plain row
|
||||
// instead of settings::item::builder because the latter
|
||||
// returns a section Item, not an Element usable in .control().
|
||||
.control(
|
||||
widget::row::with_children([
|
||||
widget::text::body(fl!("open-with-set-default")).into(),
|
||||
widget::space::horizontal().into(),
|
||||
widget::toggler(*set_default)
|
||||
.on_toggle(Message::OpenWithToggleDefault)
|
||||
.into(),
|
||||
])
|
||||
.spacing(space_s)
|
||||
.align_y(Alignment::Center),
|
||||
);
|
||||
|
||||
if let Some(app) = store_opt {
|
||||
dialog = dialog.tertiary_action(
|
||||
|
|
@ -6444,7 +6845,10 @@ impl Application for App {
|
|||
/// Creates a view after each update.
|
||||
fn view(&self) -> Element<'_, Self::Message> {
|
||||
let cosmic_theme::Spacing {
|
||||
space_xxs, space_s, ..
|
||||
space_xxs,
|
||||
space_xs,
|
||||
space_s,
|
||||
..
|
||||
} = theme::active().cosmic().spacing;
|
||||
|
||||
let mut tab_column = widget::column::with_capacity(4);
|
||||
|
|
@ -6488,6 +6892,36 @@ impl Application for App {
|
|||
);
|
||||
}
|
||||
|
||||
// Yoda phase 3: Dolphin-style quick actions toolbar via
|
||||
// segmented_button::horizontal — the same widget that powers the
|
||||
// tab bar, so its built-in drag reorder works reliably (unlike the
|
||||
// generic dnd_source+dnd_destination pairing we tried earlier).
|
||||
// Short click = action (ToolbarTabActivate → dispatch the stored
|
||||
// ToolbarAction's message). Drag past threshold = reorder
|
||||
// (ToolbarTabReorder → model.reorder + sync to config).
|
||||
if !self.config.toolbar.is_empty() {
|
||||
// Use Control style (no TabBar underline, no bottom border)
|
||||
// and let each button shrink to its icon. Spacing = space_xs
|
||||
// keeps the buttons visually separated so it looks like an
|
||||
// icon toolbar rather than a conjoined segmented control.
|
||||
let toolbar = widget::segmented_button::horizontal(&self.toolbar_model)
|
||||
.style(theme::SegmentedButton::Control)
|
||||
.button_height(32)
|
||||
.button_spacing(space_xs)
|
||||
.button_alignment(Alignment::Center)
|
||||
.minimum_button_width(32)
|
||||
.maximum_button_width(32)
|
||||
.enable_tab_drag(String::from("x-cosmic-files/toolbar-dnd"))
|
||||
.on_reorder(Message::ToolbarTabReorder)
|
||||
.tab_drag_threshold(8.)
|
||||
.on_activate(Message::ToolbarTabActivate);
|
||||
tab_column = tab_column.push(
|
||||
widget::container(toolbar)
|
||||
.width(Length::Shrink)
|
||||
.padding([space_xxs, space_s]),
|
||||
);
|
||||
}
|
||||
|
||||
let entity = self.tab_model.active();
|
||||
if let Some(tab) = self.tab_model.data::<Tab>(entity) {
|
||||
let tab_view = tab
|
||||
|
|
|
|||
|
|
@ -132,9 +132,10 @@ impl TryFrom<(Vec<u8>, String)> for ClipboardPaste {
|
|||
match mime.as_str() {
|
||||
"text/uri-list" => {
|
||||
let text = str::from_utf8(&data)?;
|
||||
let lines = text.lines();
|
||||
|
||||
for line in text.lines() {
|
||||
for line in text.lines().filter(|line| {
|
||||
let line = line.trim();
|
||||
!line.is_empty() && !line.starts_with('#')
|
||||
}) {
|
||||
let url = Url::parse(line)?;
|
||||
match url.to_file_path() {
|
||||
Ok(path) => paths.push(path),
|
||||
|
|
|
|||
|
|
@ -172,6 +172,11 @@ pub struct Config {
|
|||
pub show_details: bool,
|
||||
pub show_recents: bool,
|
||||
pub tab: TabConfig,
|
||||
/// Yoda phase 3: Dolphin-style quick actions toolbar. An ordered list
|
||||
/// of enabled buttons — position in the vec drives the toolbar order.
|
||||
/// Reorder in Settings via drag-drop; items not in the vec are
|
||||
/// hidden. Default = the minimal-6 set from phase 1.
|
||||
pub toolbar: Vec<ToolbarAction>,
|
||||
pub type_to_search: TypeToSearch,
|
||||
}
|
||||
|
||||
|
|
@ -236,11 +241,97 @@ impl Default for Config {
|
|||
show_details: false,
|
||||
show_recents: true,
|
||||
tab: TabConfig::default(),
|
||||
toolbar: default_toolbar(),
|
||||
type_to_search: TypeToSearch::Recursive,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Yoda phase 3: ordered enum of quick-action toolbar buttons.
|
||||
/// The Config stores `Vec<ToolbarAction>` so the user can pick BOTH
|
||||
/// visibility (just include/exclude the variant) AND order (position in
|
||||
/// the vec). Drag-drop reorder in the Settings page moves items around.
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub enum ToolbarAction {
|
||||
LocationUp,
|
||||
Reload,
|
||||
NewFolder,
|
||||
NewFile,
|
||||
Rename,
|
||||
Delete,
|
||||
Cut,
|
||||
Copy,
|
||||
Paste,
|
||||
ToggleShowHidden,
|
||||
OpenTerminal,
|
||||
}
|
||||
|
||||
impl ToolbarAction {
|
||||
/// Stable list of every supported action. Ordered roughly by logical
|
||||
/// grouping (location → create/edit → clipboard → view/misc) so that
|
||||
/// the default enabled set follows a sensible shape and the Settings
|
||||
/// row for a not-yet-enabled action lands in a predictable spot.
|
||||
pub const ALL: &'static [Self] = &[
|
||||
Self::LocationUp,
|
||||
Self::Reload,
|
||||
Self::NewFolder,
|
||||
Self::NewFile,
|
||||
Self::Rename,
|
||||
Self::Delete,
|
||||
Self::Cut,
|
||||
Self::Copy,
|
||||
Self::Paste,
|
||||
Self::ToggleShowHidden,
|
||||
Self::OpenTerminal,
|
||||
];
|
||||
|
||||
/// u8 discriminant used to carry the action over a DnD mime payload.
|
||||
pub const fn to_u8(self) -> u8 {
|
||||
match self {
|
||||
Self::LocationUp => 0,
|
||||
Self::Reload => 1,
|
||||
Self::NewFolder => 2,
|
||||
Self::NewFile => 3,
|
||||
Self::Rename => 4,
|
||||
Self::Delete => 5,
|
||||
Self::Cut => 6,
|
||||
Self::Copy => 7,
|
||||
Self::Paste => 8,
|
||||
Self::ToggleShowHidden => 9,
|
||||
Self::OpenTerminal => 10,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn from_u8(v: u8) -> Option<Self> {
|
||||
match v {
|
||||
0 => Some(Self::LocationUp),
|
||||
1 => Some(Self::Reload),
|
||||
2 => Some(Self::NewFolder),
|
||||
3 => Some(Self::NewFile),
|
||||
4 => Some(Self::Rename),
|
||||
5 => Some(Self::Delete),
|
||||
6 => Some(Self::Cut),
|
||||
7 => Some(Self::Copy),
|
||||
8 => Some(Self::Paste),
|
||||
9 => Some(Self::ToggleShowHidden),
|
||||
10 => Some(Self::OpenTerminal),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Default set shown on a fresh install — same "minimal 6" as phase 1/2.
|
||||
pub fn default_toolbar() -> Vec<ToolbarAction> {
|
||||
vec![
|
||||
ToolbarAction::NewFolder,
|
||||
ToolbarAction::Rename,
|
||||
ToolbarAction::Delete,
|
||||
ToolbarAction::Cut,
|
||||
ToolbarAction::Copy,
|
||||
ToolbarAction::Paste,
|
||||
]
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, CosmicConfigEntry, Deserialize, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct DesktopConfig {
|
||||
|
|
|
|||
|
|
@ -744,11 +744,17 @@ impl App {
|
|||
fn rescan_tab(&self, selection_paths: Option<Vec<PathBuf>>) -> Task<Message> {
|
||||
let location = self.tab.location.clone();
|
||||
let icon_sizes = self.tab.config.icon_sizes;
|
||||
#[cfg(feature = "gvfs")]
|
||||
let mounter_items = self.mounter_items.clone();
|
||||
Task::future(async move {
|
||||
let location2 = location.clone();
|
||||
match tokio::task::spawn_blocking(move || location2.scan(icon_sizes)).await {
|
||||
Ok((parent_item_opt, mut items)) => {
|
||||
Ok((parent_item_opt, items)) => {
|
||||
#[cfg(feature = "gvfs")]
|
||||
let mut items = items;
|
||||
#[cfg(not(feature = "gvfs"))]
|
||||
let items = items;
|
||||
|
||||
#[cfg(feature = "gvfs")]
|
||||
{
|
||||
let mounter_paths: Box<[_]> = mounter_items
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ mod zoom;
|
|||
|
||||
pub(crate) type FxOrderMap<K, V> = ordermap::OrderMap<K, V, rustc_hash::FxBuildHasher>;
|
||||
|
||||
#[cfg(feature = "gvfs")]
|
||||
pub(crate) fn err_str<T: ToString>(err: T) -> String {
|
||||
err.to_string()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ use cosmic::{
|
|||
responsive_menu_bar, space, text,
|
||||
},
|
||||
};
|
||||
#[cfg(feature = "desktop")]
|
||||
use i18n_embed::LanguageLoader;
|
||||
use mime_guess::Mime;
|
||||
use std::{collections::HashMap, sync::LazyLock};
|
||||
|
|
@ -196,11 +197,11 @@ pub fn context_menu<'a>(
|
|||
if !Trash::is_empty() {
|
||||
children.push(menu_item(fl!("empty-trash"), Action::EmptyTrash).into());
|
||||
}
|
||||
} else if let Some(entry) = selected_desktop_entry {
|
||||
} else if let Some(_entry) = selected_desktop_entry {
|
||||
children.push(menu_item(fl!("open"), Action::Open).into());
|
||||
#[cfg(feature = "desktop")]
|
||||
{
|
||||
children.extend(entry.desktop_actions.into_iter().enumerate().map(
|
||||
children.extend(_entry.desktop_actions.into_iter().enumerate().map(
|
||||
|(i, action)| menu_item(action.name, Action::ExecEntryAction(i)).into(),
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,13 +6,12 @@ use cosmic::desktop;
|
|||
use cosmic::widget;
|
||||
pub use mime_guess::Mime;
|
||||
use rustc_hash::FxHashMap;
|
||||
#[cfg(feature = "desktop")]
|
||||
use std::{cmp::Ordering, fs, io, time::Instant};
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
ffi::OsStr,
|
||||
fs, io,
|
||||
path::{Path, PathBuf},
|
||||
process,
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
// Supported exec key field codes
|
||||
|
|
@ -399,9 +398,12 @@ impl MimeAppCache {
|
|||
// The current approach works but might not adhere to the spec (yet)
|
||||
|
||||
// Look for and return preferred terminals
|
||||
//TODO: fallback order beyond cosmic-term?
|
||||
|
||||
let mut preference_order = vec!["com.system76.CosmicTerm".to_string()];
|
||||
// Yoda: cosmic-yoterm (our fork) wins over upstream cosmic-term if both
|
||||
// are installed — useful when xdg-mime default is not set.
|
||||
let mut preference_order = vec![
|
||||
"com.aditua.CosmicYoterm".to_string(),
|
||||
"com.system76.CosmicTerm".to_string(),
|
||||
];
|
||||
|
||||
if let Some(id) = self.get_default_terminal() {
|
||||
preference_order.insert(0, id);
|
||||
|
|
|
|||
|
|
@ -75,10 +75,10 @@ impl MounterItem {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn icon(&self, symbolic: bool) -> Option<widget::icon::Handle> {
|
||||
pub fn icon(&self, _symbolic: bool) -> Option<widget::icon::Handle> {
|
||||
match self {
|
||||
#[cfg(feature = "gvfs")]
|
||||
Self::Gvfs(item) => item.icon(symbolic),
|
||||
Self::Gvfs(item) => item.icon(_symbolic),
|
||||
Self::None => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
|
@ -103,6 +103,7 @@ impl MounterItem {
|
|||
pub type MounterItems = Vec<MounterItem>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub enum MounterMessage {
|
||||
Items(MounterItems),
|
||||
MountResult(MounterItem, Result<bool, String>),
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use compio::buf::{IntoInner, IoBuf};
|
|||
use compio::driver::{ToSharedFd, op::AsyncifyFd};
|
||||
use compio::io::{AsyncReadAt, AsyncWriteAt};
|
||||
use cosmic::iced::futures;
|
||||
#[cfg(feature = "gvfs")]
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
|
|
|
|||
83
src/tab.rs
83
src/tab.rs
|
|
@ -28,6 +28,7 @@ use cosmic::{
|
|||
space,
|
||||
},
|
||||
};
|
||||
#[cfg(feature = "desktop")]
|
||||
use i18n_embed::LanguageLoader;
|
||||
use icu::{
|
||||
datetime::{
|
||||
|
|
@ -302,6 +303,26 @@ pub fn folder_icon_symbolic(path: &PathBuf, icon_size: u16) -> widget::icon::Han
|
|||
.handle()
|
||||
}
|
||||
|
||||
fn generic_file_icons(
|
||||
sizes: IconSizes,
|
||||
) -> (
|
||||
widget::icon::Handle,
|
||||
widget::icon::Handle,
|
||||
widget::icon::Handle,
|
||||
) {
|
||||
(
|
||||
widget::icon::from_name("text-x-generic")
|
||||
.size(sizes.grid())
|
||||
.handle(),
|
||||
widget::icon::from_name("text-x-generic")
|
||||
.size(sizes.list())
|
||||
.handle(),
|
||||
widget::icon::from_name("text-x-generic")
|
||||
.size(sizes.list_condensed())
|
||||
.handle(),
|
||||
)
|
||||
}
|
||||
|
||||
//TODO: replace with Path::has_trailing_sep when stable
|
||||
fn has_trailing_sep(path: &Path) -> bool {
|
||||
path.as_os_str()
|
||||
|
|
@ -558,7 +579,7 @@ pub fn fs_kind(_metadata: &Metadata) -> FsKind {
|
|||
}
|
||||
|
||||
#[cfg(not(feature = "desktop"))]
|
||||
fn get_desktop_file_display_name(path: &Path) -> Option<String> {
|
||||
fn get_desktop_file_display_name(_path: &Path) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
|
|
@ -577,7 +598,7 @@ fn get_desktop_file_display_name(path: &Path) -> Option<String> {
|
|||
}
|
||||
|
||||
#[cfg(not(feature = "desktop"))]
|
||||
fn get_desktop_file_icon(path: &Path) -> Option<String> {
|
||||
fn get_desktop_file_icon(_path: &Path) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
|
|
@ -665,9 +686,9 @@ pub fn item_from_gvfs_info(path: PathBuf, file_info: gio::FileInfo, sizes: IconS
|
|||
folder_icon(&path, sizes.list_condensed()),
|
||||
)
|
||||
} else {
|
||||
// ALWAYS assume we're remote for mime guessing here, since gvfs reading can be expensive
|
||||
// @todo - expose this as a config option?
|
||||
let mime = mime_for_path(&path, None, true);
|
||||
// Keep the initial directory scan cheap. Opening files still
|
||||
// recalculates MIME from the real path before launching apps.
|
||||
let mime = mime_guess::from_path(&path).first_or_octet_stream();
|
||||
|
||||
//TODO: clean this up, implement for trash
|
||||
let icon_name_opt = if mime == "application/x-desktop" {
|
||||
|
|
@ -684,28 +705,21 @@ pub fn item_from_gvfs_info(path: PathBuf, file_info: gio::FileInfo, sizes: IconS
|
|||
desktop_icon_handle(&icon_name, sizes.list_condensed()),
|
||||
)
|
||||
} else {
|
||||
let (icon_handle_grid, icon_handle_list, icon_handle_list_condensed) =
|
||||
generic_file_icons(sizes);
|
||||
(
|
||||
mime.clone(),
|
||||
mime_icon(mime.clone(), sizes.grid()),
|
||||
mime_icon(mime.clone(), sizes.list()),
|
||||
mime_icon(mime, sizes.list_condensed()),
|
||||
mime,
|
||||
icon_handle_grid,
|
||||
icon_handle_list,
|
||||
icon_handle_list_condensed,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let mut children_opt = None;
|
||||
let children_opt = None;
|
||||
let mut dir_size = DirSize::NotDirectory;
|
||||
if is_dir && !remote {
|
||||
dir_size = DirSize::Calculating(Controller::default());
|
||||
//TODO: calculate children in the background (and make it cancellable?)
|
||||
match fs::read_dir(&path) {
|
||||
Ok(entries) => {
|
||||
children_opt = Some(entries.count());
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!("failed to read directory {}: {}", path.display(), err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let display_name = display_name_for_file(&path, &file_info.display_name(), false, is_desktop);
|
||||
|
|
@ -759,7 +773,10 @@ pub fn item_from_entry(
|
|||
sizes: IconSizes,
|
||||
) -> Item {
|
||||
let mut is_desktop = false;
|
||||
#[cfg(feature = "gvfs")]
|
||||
let mut is_gvfs = false;
|
||||
#[cfg(not(feature = "gvfs"))]
|
||||
let is_gvfs = false;
|
||||
|
||||
let hidden = name.starts_with('.') || hidden_attribute(&metadata);
|
||||
|
||||
|
|
@ -807,7 +824,9 @@ pub fn item_from_entry(
|
|||
folder_icon(&path, sizes.list_condensed()),
|
||||
)
|
||||
} else {
|
||||
let mime = mime_for_path(&path, Some(&metadata), remote);
|
||||
// Keep the initial directory scan cheap. Opening files still
|
||||
// recalculates MIME from the real path before launching apps.
|
||||
let mime = mime_guess::from_path(&path).first_or_octet_stream();
|
||||
//TODO: clean this up, implement for trash
|
||||
let icon_name_opt = if mime == "application/x-desktop" {
|
||||
is_desktop = true;
|
||||
|
|
@ -823,28 +842,21 @@ pub fn item_from_entry(
|
|||
desktop_icon_handle(&icon_name, sizes.list_condensed()),
|
||||
)
|
||||
} else {
|
||||
let (icon_handle_grid, icon_handle_list, icon_handle_list_condensed) =
|
||||
generic_file_icons(sizes);
|
||||
(
|
||||
mime.clone(),
|
||||
mime_icon(mime.clone(), sizes.grid()),
|
||||
mime_icon(mime.clone(), sizes.list()),
|
||||
mime_icon(mime, sizes.list_condensed()),
|
||||
mime,
|
||||
icon_handle_grid,
|
||||
icon_handle_list,
|
||||
icon_handle_list_condensed,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let mut children_opt = None;
|
||||
let children_opt = None;
|
||||
let mut dir_size = DirSize::NotDirectory;
|
||||
if metadata.is_dir() && !remote {
|
||||
dir_size = DirSize::Calculating(Controller::default());
|
||||
//TODO: calculate children in the background (and make it cancellable?)
|
||||
match fs::read_dir(&path) {
|
||||
Ok(entries) => {
|
||||
children_opt = Some(entries.count());
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!("failed to read directory {}: {}", path.display(), err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let display_name = display_name_for_file(&path, &name, is_gvfs, is_desktop);
|
||||
|
|
@ -958,7 +970,10 @@ pub fn item_from_path<P: Into<PathBuf>>(path: P, sizes: IconSizes) -> Result<Ite
|
|||
pub fn scan_path(tab_path: &PathBuf, sizes: IconSizes) -> Vec<Item> {
|
||||
let mut items = Vec::new();
|
||||
let mut hidden_files = Box::from([]);
|
||||
#[cfg(feature = "gvfs")]
|
||||
let mut remote_scannable = false;
|
||||
#[cfg(not(feature = "gvfs"))]
|
||||
let remote_scannable = false;
|
||||
|
||||
#[cfg(feature = "gvfs")]
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@
|
|||
use cosmic::desktop::fde::GenericEntry;
|
||||
use mime_guess::Mime;
|
||||
use rustc_hash::FxHashMap;
|
||||
#[cfg(feature = "desktop")]
|
||||
use std::{fs, time::Instant};
|
||||
use std::{
|
||||
fs,
|
||||
path::Path,
|
||||
process,
|
||||
sync::{LazyLock, Mutex},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
|
|||
12
src/trash.rs
12
src/trash.rs
|
|
@ -27,7 +27,7 @@ pub trait TrashExt {
|
|||
Vec::new()
|
||||
}
|
||||
|
||||
fn scan_search<F: Fn(SearchItem) -> bool + Sync>(callback: F, regex: &Regex) {}
|
||||
fn scan_search<F: Fn(SearchItem) -> bool + Sync>(callback: F, regex: &Regex);
|
||||
|
||||
fn icon(icon_size: u16) -> widget::icon::Handle {
|
||||
widget::icon::from_name(if Self::is_empty() {
|
||||
|
|
@ -142,4 +142,12 @@ impl TrashExt for Trash {
|
|||
not(target_os = "android")
|
||||
)
|
||||
)))]
|
||||
impl TrashExt for Trash {}
|
||||
impl TrashExt for Trash {
|
||||
fn scan_search<F: Fn(SearchItem) -> bool + Sync>(callback: F, regex: &Regex) {
|
||||
log::warn!(
|
||||
"searching trash not supported on this platform for pattern {:?}",
|
||||
regex.as_str()
|
||||
);
|
||||
drop(callback);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue