diff --git a/.zed/settings.json b/.zed/settings.json deleted file mode 100644 index 2cc7b98..0000000 --- a/.zed/settings.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "format_on_save": "on", - "lsp": { - "rust-analyzer": { - "initialization_options": { - "check": { - "command": "clippy", - }, - "rustfmt": { - "extraArgs": ["+nightly"], - }, - }, - }, - }, -} diff --git a/Cargo.lock b/Cargo.lock index 92d1d9c..7e4753b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,7 +21,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "accesskit" version = "0.22.0" -source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" dependencies = [ "uuid", ] @@ -29,19 +29,19 @@ dependencies = [ [[package]] name = "accesskit_atspi_common" version = "0.15.0" -source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" dependencies = [ "accesskit", "accesskit_consumer", "atspi-common", "serde", - "zvariant 5.15.0", + "zvariant 5.10.0", ] [[package]] name = "accesskit_consumer" version = "0.32.0" -source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" dependencies = [ "accesskit", "hashbrown 0.16.1", @@ -50,7 +50,7 @@ dependencies = [ [[package]] name = "accesskit_macos" version = "0.23.0" -source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" dependencies = [ "accesskit", "accesskit_consumer", @@ -63,7 +63,7 @@ dependencies = [ [[package]] name = "accesskit_unix" version = "0.18.0" -source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" dependencies = [ "accesskit", "accesskit_atspi_common", @@ -72,13 +72,13 @@ dependencies = [ "serde", "tokio", "tokio-stream", - "zbus 5.19.0", + "zbus 5.14.0", ] [[package]] name = "accesskit_windows" version = "0.30.0" -source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" dependencies = [ "accesskit", "accesskit_consumer", @@ -91,7 +91,7 @@ dependencies = [ [[package]] name = "accesskit_winit" version = "0.30.0" -source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912" +source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f" dependencies = [ "accesskit", "accesskit_macos", @@ -109,13 +109,13 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aes" -version = "0.9.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35f0f96ce78e38c3dc6d8948aa8163d06385be74000f3c7a95bf1eef35d3ea32" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ + "cfg-if", "cipher", - "cpubits", - "cpufeatures 0.3.1", + "cpufeatures", ] [[package]] @@ -133,9 +133,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.5" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -172,19 +172,13 @@ checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" [[package]] name = "alloc-stdlib" -version = "0.2.4" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" dependencies = [ "alloc-no-stdlib", ] -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - [[package]] name = "almost" version = "0.2.0" @@ -193,21 +187,23 @@ checksum = "3aa2999eb46af81abb65c2d30d446778d7e613b60bbf4e174a027e80f90a3c14" [[package]] name = "android-activity" -version = "0.6.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f2a1bb052857d5dd49572219344a7332b31b76405648eabac5bc68978251bcd" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.13.2", + "bitflags 2.11.0", "cc", + "cesu8", "jni", + "jni-sys", "libc", "log", "ndk", "ndk-context", "ndk-sys", "num_enum", - "thiserror 2.0.20", + "thiserror 1.0.69", ] [[package]] @@ -218,18 +214,18 @@ checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" [[package]] name = "android_system_properties" -version = "0.1.6" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] [[package]] name = "anstream" -version = "1.0.0" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -248,9 +244,9 @@ checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" -version = "1.0.0" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] @@ -277,9 +273,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.104" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "apply" @@ -304,9 +300,9 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" [[package]] name = "arc-swap" -version = "1.9.2" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b" +checksum = "f9f3647c145568cec02c42054e07bdf9a5a698e15b466fb2341bfc393cd24aa5" dependencies = [ "rustversion", ] @@ -319,7 +315,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] @@ -330,9 +326,15 @@ checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.8" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" +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" @@ -361,13 +363,13 @@ dependencies = [ "enumflags2", "futures-channel", "futures-util", - "rand 0.9.5", + "rand 0.9.2", "raw-window-handle", "serde", "serde_repr", "tokio", "url", - "zbus 5.19.0", + "zbus 5.14.0", ] [[package]] @@ -379,7 +381,7 @@ dependencies = [ "enumflags2", "futures-channel", "futures-util", - "rand 0.9.5", + "rand 0.9.2", "serde", "serde_repr", "tokio", @@ -387,7 +389,7 @@ dependencies = [ "wayland-backend", "wayland-client", "wayland-protocols", - "zbus 5.19.0", + "zbus 5.14.0", ] [[package]] @@ -494,14 +496,14 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] name = "async-signal" -version = "0.2.14" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" +checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" dependencies = [ "async-io", "async-lock", @@ -523,13 +525,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.92" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn", ] [[package]] @@ -538,12 +540,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "atomic_float" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628d228f918ac3b82fe590352cc719d30664a0c13ca3a60266fe02c7132d480a" - [[package]] name = "atomicwrites" version = "0.4.2" @@ -573,11 +569,11 @@ dependencies = [ "enumflags2", "serde", "static_assertions", - "zbus 5.19.0", + "zbus 5.14.0", "zbus-lockstep", "zbus-lockstep-macros", - "zbus_names 4.3.4", - "zvariant 5.15.0", + "zbus_names 4.3.1", + "zvariant 5.10.0", ] [[package]] @@ -588,26 +584,26 @@ checksum = "2230e48787ed3eb4088996eab66a32ca20c0b67bbd4fd6cdfe79f04f1f04c9fc" dependencies = [ "atspi-common", "serde", - "zbus 5.19.0", + "zbus 5.14.0", ] [[package]] name = "auto_enums" -version = "0.8.10" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3091d68264354f211516b91dce6f71046e444fab1867716035f736667243affb" +checksum = "65398a2893f41bce5c9259f6e1a4f03fbae40637c1bdc755b4f387f48c613b03" dependencies = [ "derive_utils", "proc-macro2", "quote", - "syn 3.0.5", + "syn", ] [[package]] name = "autocfg" -version = "1.5.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "av-scenechange" @@ -624,7 +620,7 @@ dependencies = [ "num-traits", "pastey", "rayon", - "thiserror 2.0.20", + "thiserror 2.0.18", "v_frame", "y4m", ] @@ -645,9 +641,9 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.9" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7178fe5f7d460b13895ebb9dcb28a3a6216d2df2574a0806cb51b555d297f38" +checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d" dependencies = [ "arrayvec", ] @@ -658,12 +654,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "base64" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" - [[package]] name = "basic-toml" version = "0.1.10" @@ -702,20 +692,20 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.13.2" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" dependencies = [ "serde_core", ] [[package]] name = "bitstream-io" -version = "4.10.0" +version = "4.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eff00be299a18769011411c9def0d827e8f2d7bf0c3dbf53633147a8867fd1f" +checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757" dependencies = [ - "no_std_io2", + "core2", ] [[package]] @@ -733,16 +723,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-buffer" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" -dependencies = [ - "hybrid-array", - "zeroize", -] - [[package]] name = "block2" version = "0.5.1" @@ -763,9 +743,9 @@ dependencies = [ [[package]] name = "blocking" -version = "1.7.0" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a70e4329df6cb94385eed412ec92375c3cdd8a6e502493d1229b6414e4036dfa" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" dependencies = [ "async-channel", "async-task", @@ -776,42 +756,32 @@ dependencies = [ [[package]] name = "borsh" -version = "1.8.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "553c5d846a6ba5150c65e3b1b8ec073bcf1abc20f9b7220de384a4443ea4e20a" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" dependencies = [ - "bytes", "cfg_aliases", ] [[package]] name = "brotli-decompressor" -version = "5.0.3" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", ] -[[package]] -name = "bs58" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" -dependencies = [ - "tinyvec", -] - [[package]] name = "bstr" -version = "1.13.1" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" dependencies = [ "memchr", "regex-automata", - "serde_core", + "serde", ] [[package]] @@ -823,24 +793,17 @@ dependencies = [ "num-traits", ] -[[package]] -name = "build_helpers" -version = "0.14.0" -dependencies = [ - "cfg_aliases", -] - [[package]] name = "built" -version = "0.8.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c0e531d93d39c34eef561e929e8a7f86d77a5af08aac4f6d6e39976c51858e9" +checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" [[package]] name = "bumpalo" -version = "3.20.3" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "by_address" @@ -850,22 +813,22 @@ checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" [[package]] name = "bytemuck" -version = "1.25.2" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.12.1" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a1f896587b6f2c069c73d2f0913e2d590c3990285cd2f0b6aa02b786b4c679c" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn", ] [[package]] @@ -882,9 +845,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.12.1" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "bzip2" @@ -897,9 +860,9 @@ dependencies = [ [[package]] name = "calendrical_calculations" -version = "0.2.4" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5abbd6eeda6885048d357edc66748eea6e0268e3dd11f326fff5bd248d779c26" +checksum = "3a0b39595c6ee54a8d0900204ba4c401d0ab4eb45adaf07178e8d017541529e7" dependencies = [ "core_maths", "displaydoc", @@ -911,7 +874,7 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "polling", "rustix 1.1.4", "slab", @@ -932,16 +895,22 @@ dependencies = [ [[package]] name = "cc" -version = "1.4.6" +version = "1.2.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3eb0f42d6c360dc3f8a821f6bf2fdea7f72bfd36b3076eb0e6d1e9e0752fff4" +checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" dependencies = [ "find-msvc-tools", "jobserver", "libc", - "shlex 2.0.1", + "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfb" version = "0.7.3" @@ -955,9 +924,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.20.9" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4ece8474b5f766c63426647e7b4b316b67431ade1036a8313cee24a03ae917" +checksum = "3c6b04e07d8080154ed4ac03546d9a2b303cc2fe1901ba0b35b301516e289368" dependencies = [ "smallvec", "target-lexicon", @@ -971,19 +940,20 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.45" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "pure-rust-locales", "serde", "wasm-bindgen", "windows-link 0.2.1", @@ -991,11 +961,11 @@ dependencies = [ [[package]] name = "cipher" -version = "0.5.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "crypto-common 0.2.2", + "crypto-common", "inout", ] @@ -1011,6 +981,7 @@ dependencies = [ [[package]] name = "clipboard_macos" version = "0.1.0" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#3a7af79e54db6854d8aa9d9e2866a9288d0f95d5" dependencies = [ "objc", "objc-foundation", @@ -1020,6 +991,7 @@ dependencies = [ [[package]] name = "clipboard_wayland" version = "0.2.2" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#3a7af79e54db6854d8aa9d9e2866a9288d0f95d5" dependencies = [ "dnd", "mime 0.1.0", @@ -1027,10 +999,13 @@ dependencies = [ ] [[package]] -name = "cmov" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" +name = "clipboard_x11" +version = "0.4.2" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#3a7af79e54db6854d8aa9d9e2866a9288d0f95d5" +dependencies = [ + "thiserror 1.0.69", + "x11rb", +] [[package]] name = "cocoa" @@ -1087,9 +1062,9 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "combine" -version = "4.6.8" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ "bytes", "memchr", @@ -1112,9 +1087,9 @@ dependencies = [ [[package]] name = "compio-buf" -version = "0.8.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b51a2c35873865376ed4cdb6cfeb602b6cf569815f017ddd7bd8f86ad49b34c7" +checksum = "3e8777c3ad31ab42f8a3a4a1bd629b78f688371df9b0f528d94dfbdbe5c945c9" dependencies = [ "arrayvec", "bytes", @@ -1123,9 +1098,9 @@ dependencies = [ [[package]] name = "compio-driver" -version = "0.11.4" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d42d98dc890ee4db00c1e68a723391711aab6d67085880d716b72830f7c715" +checksum = "ec8557d3804525f0e97594681f675929931782c9f8783a51c9f86da86b819150" dependencies = [ "cfg-if", "cfg_aliases", @@ -1198,7 +1173,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] @@ -1234,21 +1209,15 @@ dependencies = [ [[package]] name = "configparser" -version = "3.3.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "031ad3d167622974ed636505ae4556267ed4aa2aea0464122fa6bc9698a32825" - -[[package]] -name = "const-oid" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" +checksum = "e57e3272f0190c3f1584272d613719ba5fc7df7f4942fe542e63d949cf3a649b" [[package]] name = "constant_time_eq" -version = "0.4.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "core-foundation" @@ -1306,11 +1275,20 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "core-foundation 0.10.1", "libc", ] +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "core_affinity" version = "0.8.3" @@ -1333,10 +1311,11 @@ dependencies = [ [[package]] name = "cosmic-client-toolkit" -version = "0.2.0" +version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-protocols?rev=d0e95be#d0e95be25e423cfe523b11111a3666ed7aaf0dc4" dependencies = [ - "bitflags 2.13.2", - "cosmic-protocols 0.2.0", + "bitflags 2.11.0", + "cosmic-protocols 0.1.0", "libc", "smithay-client-toolkit", "wayland-client", @@ -1346,10 +1325,10 @@ dependencies = [ [[package]] name = "cosmic-client-toolkit" version = "0.2.0" -source = "git+https://github.com/pop-os/cosmic-protocols?rev=c0cff4d#c0cff4db14c37ed954983158e4055aa94c7741d9" +source = "git+https://github.com/pop-os/cosmic-protocols?rev=160b086#160b086abe03cd34a8a375d7fbe47b24308d1f38" dependencies = [ - "bitflags 2.13.2", - "cosmic-protocols 0.2.0 (git+https://github.com/pop-os/cosmic-protocols?rev=c0cff4d)", + "bitflags 2.11.0", + "cosmic-protocols 0.2.0", "libc", "smithay-client-toolkit", "wayland-client", @@ -1359,6 +1338,7 @@ dependencies = [ [[package]] name = "cosmic-config" version = "1.0.0" +source = "git+https://github.com/pop-os/libcosmic.git#9602dfd2f12b667e0afacdccd0e403d8152dde5a" dependencies = [ "atomicwrites", "cosmic-config-derive", @@ -1368,34 +1348,35 @@ dependencies = [ "iced_futures", "known-folders", "notify", - "ron 0.12.2", + "ron 0.12.0", "serde", "tokio", "tracing", "xdg", - "zbus 5.19.0", + "zbus 5.14.0", ] [[package]] name = "cosmic-config-derive" version = "1.0.0" +source = "git+https://github.com/pop-os/libcosmic.git#9602dfd2f12b667e0afacdccd0e403d8152dde5a" dependencies = [ "quote", - "syn 2.0.119", + "syn", ] [[package]] name = "cosmic-files" -version = "1.9.0" +version = "1.0.8" dependencies = [ "anyhow", - "atomic_float", - "bstr", "bzip2", + "chrono", "compio", - "cosmic-client-toolkit 0.2.0 (git+https://github.com/pop-os/cosmic-protocols?rev=c0cff4d)", + "cosmic-client-toolkit 0.1.0", "cosmic-mime-apps", "dirs 6.0.0", + "env_logger", "fastrand", "filetime", "flate2", @@ -1408,11 +1389,9 @@ dependencies = [ "icu", "ignore", "image", - "jiff", - "jiff-icu", "jxl-oxide", "libc", - "libcosmic-yoda", + "libcosmic", "log", "lzma-rust2", "md-5", @@ -1420,7 +1399,6 @@ dependencies = [ "notify-debouncer-full", "notify-rust", "num_cpus", - "num_enum", "open", "ordermap", "paste", @@ -1429,19 +1407,15 @@ dependencies = [ "recently-used-xbel", "regex", "rust-embed", - "rustc-hash 2.1.3", + "rustc-hash 2.1.1", "serde", - "sha2 0.10.9", - "shlex 1.3.0", + "shlex", "slotmap", "tar", "tempfile", "test-log", - "thiserror 2.0.20", "tikv-jemallocator", "tokio", - "tracing", - "tracing-subscriber", "trash", "url", "uzers", @@ -1455,38 +1429,31 @@ dependencies = [ [[package]] name = "cosmic-files-applet" -version = "1.9.0" +version = "1.0.8" dependencies = [ "cosmic-files", "log", "zbus 4.4.0", ] -[[package]] -name = "cosmic-files-thumbnailer" -version = "1.9.0" -dependencies = [ - "image", - "pelite", -] - [[package]] name = "cosmic-freedesktop-icons" version = "0.4.0" +source = "git+https://github.com/pop-os/freedesktop-icons#7a61a704f6d1ec41f71cbe766e3cc484858523fa" dependencies = [ "bstr", "btoi", "memchr", - "memmap2 0.9.11", - "thiserror 2.0.20", + "memmap2 0.9.10", + "thiserror 2.0.18", "tracing", "xdg", ] [[package]] name = "cosmic-mime-apps" -version = "0.2.0" -source = "git+https://github.com/pop-os/cosmic-mime-apps.git#8fc81e8857e46c018ea21ab985aaf30ac853c007" +version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-mime-apps.git#673f3758920a7b20d307d356d586deef714df6b0" dependencies = [ "freedesktop-desktop-entry", "mime 0.3.17", @@ -1496,9 +1463,10 @@ dependencies = [ [[package]] name = "cosmic-protocols" -version = "0.2.0" +version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-protocols?rev=d0e95be#d0e95be25e423cfe523b11111a3666ed7aaf0dc4" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -1510,9 +1478,9 @@ dependencies = [ [[package]] name = "cosmic-protocols" version = "0.2.0" -source = "git+https://github.com/pop-os/cosmic-protocols?rev=c0cff4d#c0cff4db14c37ed954983158e4055aa94c7741d9" +source = "git+https://github.com/pop-os/cosmic-protocols?rev=160b086#160b086abe03cd34a8a375d7fbe47b24308d1f38" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -1523,7 +1491,8 @@ dependencies = [ [[package]] name = "cosmic-settings-config" -version = "1.9.0" +version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-settings-daemon#e37160f14d1e7ee428f973cd2848b4e95f83dfe1" dependencies = [ "cosmic-config", "ron 0.11.0", @@ -1535,22 +1504,24 @@ dependencies = [ [[package]] name = "cosmic-settings-daemon" -version = "0.1.1-yoda.1" +version = "0.1.0" +source = "git+https://github.com/pop-os/dbus-settings-bindings#507e342c21d3ce6ae41b1d4f3fa2f0ad5ee23e75" dependencies = [ - "zbus 5.19.0", + "zbus 5.14.0", ] [[package]] name = "cosmic-text" -version = "0.19.0" +version = "0.18.2" +source = "git+https://github.com/pop-os/cosmic-text.git#d5a972a2b63649fad11ea3a7e80f7dc4c592f01a" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "fontdb", "harfrust", "linebender_resource_handle", "log", "rangemap", - "rustc-hash 2.1.3", + "rustc-hash 2.1.1", "self_cell", "skrifa 0.40.0", "smol_str", @@ -1560,32 +1531,25 @@ 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#9602dfd2f12b667e0afacdccd0e403d8152dde5a" dependencies = [ "almost", "configparser", "cosmic-config", "csscolorparser", "dirs 6.0.0", - "hex_color", "palette", - "ron 0.12.2", + "ron 0.12.0", "serde", "serde_json", - "thiserror 2.0.20", + "thiserror 2.0.18", ] -[[package]] -name = "cpubits" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" - [[package]] name = "cpufeatures" version = "0.2.17" @@ -1596,28 +1560,34 @@ dependencies = [ ] [[package]] -name = "cpufeatures" -version = "0.3.1" +name = "crc" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" dependencies = [ - "libc", + "crc-catalog", ] [[package]] -name = "crc32fast" -version = "1.5.2" +name = "crc-catalog" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a7799fd6b852db0e61728dde9a204c423b44d689dbd432522543614b490e78" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-deque" -version = "0.8.8" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -1625,27 +1595,27 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.21" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-queue" -version = "0.3.14" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e8bd762f7479489c70ed6c768ddca99d7296857de437a68dcb2a94365b3fae" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.23" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" @@ -1656,11 +1626,12 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "cryoglyph" version = "0.1.0" +source = "git+https://github.com/pop-os/glyphon.git?tag=cosmic-0.14#c49de15bce4d8254ac136d1be9911960cc85ce12" dependencies = [ "cosmic-text", "etagere", "lru", - "rustc-hash 2.1.3", + "rustc-hash 2.1.1", "wgpu", ] @@ -1674,15 +1645,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "crypto-common" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" -dependencies = [ - "hybrid-array", -] - [[package]] name = "css-color" version = "0.2.8" @@ -1691,24 +1653,21 @@ checksum = "42aaeae719fd78ce501d77c6cdf01f7e96f26bcd5617a4903a1c2b97e388543a" [[package]] name = "csscolorparser" -version = "0.8.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168dbdf4dced8f57fa4246ac9080598452b49d70bb2d0e46422142f113140dd8" +checksum = "199f851bd3cb5004c09474252c7f74e7c047441ed0979bf3688a7106a13da952" dependencies = [ "num-traits", - "phf", + "phf 0.13.1", "serde", "uncased", ] [[package]] -name = "ctutils" -version = "0.4.2" +name = "ctor-lite" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" -dependencies = [ - "cmov", -] +checksum = "e162d0c2e2068eb736b71e5597eff0b9944e6b973cd9f37b6a288ab9bf20e300" [[package]] name = "cursor-icon" @@ -1728,12 +1687,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.24.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" dependencies = [ - "darling_core 0.24.1", - "darling_macro 0.24.1", + "darling_core 0.23.0", + "darling_macro 0.23.0", ] [[package]] @@ -1747,20 +1706,20 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.119", + "syn", ] [[package]] name = "darling_core" -version = "0.24.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" dependencies = [ "ident_case", "proc-macro2", "quote", "strsim", - "syn 3.0.5", + "syn", ] [[package]] @@ -1771,18 +1730,18 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn 2.0.119", + "syn", ] [[package]] name = "darling_macro" -version = "0.24.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ - "darling_core 0.24.1", + "darling_core 0.23.0", "quote", - "syn 3.0.5", + "syn", ] [[package]] @@ -1791,51 +1750,11 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" -[[package]] -name = "dataview" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daba87f72c730b508641c9fb6411fc9bba73939eed2cab611c399500511880d0" -dependencies = [ - "derive_pod", -] - [[package]] name = "deflate64" -version = "0.1.12" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac6b926516df9c60bfa16e107b21086399f8285a44ca9711344b9e553c5146e2" - -[[package]] -name = "defmt" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" -dependencies = [ - "bitflags 1.3.2", - "defmt-macros", -] - -[[package]] -name = "defmt-macros" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" -dependencies = [ - "defmt-parser", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "defmt-parser" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" -dependencies = [ - "thiserror 2.0.20", -] +checksum = "807800ff3288b621186fe0a8f3392c4652068257302709c24efd918c3dffcdc2" [[package]] name = "deranged" @@ -1843,15 +1762,10 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ + "powerfmt", "serde_core", ] -[[package]] -name = "derive_pod" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ea6706d74fca54e15f1d40b5cf7fe7f764aaec61352a9fcec58fe27e042fc8" - [[package]] name = "derive_setters" version = "0.1.9" @@ -1861,18 +1775,18 @@ dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] name = "derive_utils" -version = "0.16.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc05a5d33db20c784f873e84934ad94bb209a090987ac5f62fede2c178234f23" +checksum = "362f47930db19fe7735f527e6595e4900316b893ebf6d48ad3d31be928d57dd6" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn", ] [[package]] @@ -1881,21 +1795,9 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", - "crypto-common 0.1.7", -] - -[[package]] -name = "digest" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" -dependencies = [ - "block-buffer 0.12.1", - "const-oid", - "crypto-common 0.2.2", - "ctutils", - "zeroize", + "block-buffer", + "crypto-common", + "subtle", ] [[package]] @@ -1967,7 +1869,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "block2 0.6.2", "libc", "objc2 0.6.4", @@ -1975,13 +1877,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.7" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn", ] [[package]] @@ -1996,8 +1898,9 @@ dependencies = [ [[package]] name = "dnd" version = "0.1.0" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#3a7af79e54db6854d8aa9d9e2866a9288d0f95d5" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "mime 0.1.0", "raw-window-handle", "smithay-client-toolkit", @@ -2022,6 +1925,46 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "dpi" version = "0.1.2" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#a610ac9c7a72b39ff102ed4d946291618dc725b6" + +[[package]] +name = "drm" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0f8a69e60d75ae7dab4ef26a59ca99f2a89d4c142089b537775ae0c198bdcde" +dependencies = [ + "bitflags 2.11.0", + "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" @@ -2031,9 +1974,9 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "either" -version = "1.18.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "endi" @@ -2059,14 +2002,14 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] name = "env_filter" -version = "2.0.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900d271a03799a1ee8d1ca9b19893b48ca674a9284fefcfb85f05e74ed314217" +checksum = "7a1c3cc8e57274ec99de65301228b537f1e4eedc1b8e0f9411c6caac8ae7308f" dependencies = [ "log", "regex", @@ -2074,13 +2017,14 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.11" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de671bd27a75a797dc9ae289ba1e77276e75e2026408aab65185384e2d5cd3f6" +checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d" dependencies = [ "anstream", "anstyle", "env_filter", + "jiff", "log", ] @@ -2101,7 +2045,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] @@ -2122,9 +2066,9 @@ dependencies = [ [[package]] name = "error-code" -version = "3.4.0" +version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5343afd4a8365a643ac588dab4cf234a190c7f6c88c9f6dd6ffe00837661b7" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" [[package]] name = "etagere" @@ -2138,19 +2082,20 @@ dependencies = [ [[package]] name = "euclid" -version = "0.22.14" +version = "0.22.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" +checksum = "df61bf483e837f88d5c2291dcf55c67be7e676b3a51acc48db3a7b163b91ed63" dependencies = [ "num-traits", ] [[package]] name = "event-listener" -version = "5.4.2" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" dependencies = [ + "concurrent-queue", "parking", "pin-project-lite", ] @@ -2167,32 +2112,50 @@ dependencies = [ [[package]] name = "exr" -version = "1.74.2" +version = "1.74.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711fe42c9964295e01ee3fba3f9fe0e1d24b98886950d68efe81b1c76e21adf3" +checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" dependencies = [ "bit_field", "half", "lebe", - "miniz_oxide 0.8.9", - "num-complex", - "pulp", + "miniz_oxide", "rayon-core", "smallvec", "zune-inflate", ] [[package]] -name = "fastrand" -version = "2.5.0" +name = "fast-srgb8" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" +checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fax" -version = "0.2.7" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" +checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" +dependencies = [ + "fax_derive", +] + +[[package]] +name = "fax_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "fdeflate" @@ -2214,12 +2177,13 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.29" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" dependencies = [ "cfg-if", "libc", + "libredox", ] [[package]] @@ -2233,15 +2197,15 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.12" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fixed_decimal" -version = "0.7.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79c3c892f121fff406e5dd6b28c1b30096b95111c30701a899d4f2b18da6d1bd" +checksum = "35eabf480f94d69182677e37571d3be065822acfafd12f2f085db44fbbcc8e57" dependencies = [ "displaydoc", "smallvec", @@ -2250,12 +2214,12 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.10" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", - "miniz_oxide 0.9.1", + "miniz_oxide", "zlib-rs", ] @@ -2300,7 +2264,7 @@ dependencies = [ "fluent-syntax", "intl-memoizer", "intl_pluralrules", - "rustc-hash 2.1.3", + "rustc-hash 2.1.1", "self_cell", "smallvec", "unic-langid", @@ -2322,7 +2286,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198" dependencies = [ "memchr", - "thiserror 2.0.20", + "thiserror 2.0.18", ] [[package]] @@ -2354,18 +2318,18 @@ checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "font-types" -version = "0.11.3" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b38ad915f6dadd993ced50848a8291a543bd41ca62bc10740d5e64e2ab4cfd7" +checksum = "39a654f404bbcbd48ea58c617c2993ee91d1cb63727a37bf2323a4edeed1b8c5" dependencies = [ "bytemuck", ] [[package]] name = "font-types" -version = "0.12.5" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8eb065f3251655b3c90e22e5e363f310fc5332fb3402e37bbc94752283248f6" +checksum = "b1e4d2d0cf79d38430cc9dc9aadec84774bff2e1ba30ae2bf6c16cfce9385a23" dependencies = [ "bytemuck", ] @@ -2387,7 +2351,7 @@ checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" dependencies = [ "fontconfig-parser", "log", - "memmap2 0.9.11", + "memmap2 0.9.10", "slotmap", "tinyvec", "ttf-parser", @@ -2405,13 +2369,13 @@ dependencies = [ [[package]] name = "foreign-types-macros" -version = "0.2.4" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn", ] [[package]] @@ -2422,9 +2386,9 @@ checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" [[package]] name = "fork" -version = "0.7.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bcc4b4161e53d499e41af904acb23950adf85682c772921ef3957cf1ecc98b3" +checksum = "29714eb48a54d35d6f107e38cc58003f2e26825f222119db8369d28b24b79f2a" dependencies = [ "libc", ] @@ -2440,15 +2404,15 @@ dependencies = [ [[package]] name = "freedesktop-desktop-entry" -version = "0.8.3" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cfa09933e03f1ced380ad639822d65cb59a1914217da6c72aa8db3563e94214" +checksum = "28273c5c6b97a5f07724f6652f064c0c7f637f9aa5e7c09c83bc3bc4ad4ea245" dependencies = [ "bstr", "gettext-rs", "log", "memchr", - "thiserror 2.0.20", + "thiserror 2.0.18", "unicase", "xdg", ] @@ -2459,7 +2423,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6d3a3635983a889f065aa9ce760384713f23a9b4a04f696f86c39a5d7a6a5a" dependencies = [ - "indexmap 2.14.2", + "indexmap 2.13.0", "nom 8.0.0", ] @@ -2474,9 +2438,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.34" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -2489,9 +2453,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.34" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -2499,15 +2463,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.34" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.34" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -2516,9 +2480,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.34" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-lite" @@ -2535,32 +2499,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.34" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn", ] [[package]] name = "futures-sink" -version = "0.3.34" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.34" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.34" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -2575,9 +2539,9 @@ dependencies = [ [[package]] name = "generator" -version = "0.8.9" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3b854b0e584ead1a33f18b2fcad7cf7be18b3875c78816b753639aa501513ae" +checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9" dependencies = [ "cc", "cfg-if", @@ -2598,6 +2562,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix 1.1.4", + "windows-link 0.2.1", +] + [[package]] name = "getrandom" version = "0.2.17" @@ -2616,29 +2590,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 5.3.0", "wasip2", -] - -[[package]] -name = "getrandom" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi 6.0.0", "wasm-bindgen", ] [[package]] -name = "gettext-rs" -version = "0.8.0" +name = "getrandom" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9df5e3fa8c077b15fdfa8ce130e2bc73d663b8aaa6d51b71421b8659980b1d80" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "gettext-rs" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5857dc1b7f0fee86961de833f434e29494d72af102ce5355738c0664222bdf" dependencies = [ "gettext-sys", "locale_config", @@ -2646,9 +2622,9 @@ dependencies = [ [[package]] name = "gettext-sys" -version = "0.27.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4caa7e4460b196107cc1bb2629488e8b6dbb7f34f6683ea6753c4ac22f590785" +checksum = "4ea859ab0dd7e70ff823032b3e077d03d39c965d68c6c10775add60e999d8ee9" dependencies = [ "cc", "temp-dir", @@ -2666,9 +2642,9 @@ dependencies = [ [[package]] name = "gif" -version = "0.14.2" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" +checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e" dependencies = [ "color_quant", "weezl", @@ -2727,7 +2703,7 @@ version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16de123c2e6c90ce3b573b7330de19be649080ec612033d397d72da265f1bd8b" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "futures-channel", "futures-core", "futures-executor", @@ -2752,7 +2728,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] @@ -2767,15 +2743,15 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "globset" -version = "0.4.20" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c34a9410465b45bd9787443bc7370f37735bad04b0f0cd57ff1a3186c98988" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" dependencies = [ "aho-corasick", "bstr", @@ -2817,17 +2793,34 @@ dependencies = [ ] [[package]] -name = "gpu-allocator" -version = "0.28.0" +name = "gpu-alloc" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51255ea7cfaadb6c5f1528d43e92a82acb2b96c43365989a28b2d44ee38f8795" +checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" +dependencies = [ + "bitflags 2.11.0", + "gpu-alloc-types", +] + +[[package]] +name = "gpu-alloc-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "gpu-allocator" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" dependencies = [ - "ash", - "hashbrown 0.16.1", "log", "presser", - "thiserror 2.0.20", - "windows 0.62.2", + "thiserror 1.0.69", + "windows 0.58.0", ] [[package]] @@ -2836,7 +2829,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "gpu-descriptor-types", "hashbrown 0.15.5", ] @@ -2847,14 +2840,14 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", ] [[package]] name = "grid" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b40ca9252762c466af32d0b1002e91e4e1bc5398f77455e55474deb466355ff5" +checksum = "f9e2d4c0a8296178d8802098410ca05d86b17a10bb5ab559b3fb404c1f948220" [[package]] name = "guillotiere" @@ -2884,7 +2877,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9da2e5ae821f6e96664977bf974d6d6a2d6682f9ccee23e62ec1d134246845f9" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "bytemuck", "core_maths", "read-fonts 0.37.0", @@ -2912,17 +2905,9 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ - "allocator-api2", - "equivalent", "foldhash 0.2.0", ] -[[package]] -name = "hashbrown" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" - [[package]] name = "heck" version = "0.4.1" @@ -2937,9 +2922,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.5.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -2947,17 +2932,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hex_color" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d37f101bf4c633f7ca2e4b5e136050314503dd198e78e325ea602c327c484ef0" -dependencies = [ - "arrayvec", - "rand 0.8.8", - "serde", -] - [[package]] name = "hexf-parse" version = "0.2.1" @@ -2966,20 +2940,11 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] name = "hmac" -version = "0.13.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.11.3", -] - -[[package]] -name = "hybrid-array" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" -dependencies = [ - "typenum", + "digest", ] [[package]] @@ -3019,20 +2984,20 @@ dependencies = [ [[package]] name = "i18n-embed-fl" -version = "0.10.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "602e6bd30c3db2749e13e38b363a3d98d9d41de1d8de7a79c31bb69e45b47cda" +checksum = "e598ed73b67db92f61e04672e599eef2991a262a40e1666735b8a86d2e7e9f30" dependencies = [ "find-crate", "fluent", "fluent-syntax", "i18n-config", "i18n-embed", - "proc-macro-error3", + "proc-macro-error2", "proc-macro2", "quote", "strsim", - "syn 2.0.119", + "syn", "unic-langid", ] @@ -3046,7 +3011,7 @@ dependencies = [ "i18n-config", "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] @@ -3076,8 +3041,8 @@ dependencies = [ [[package]] name = "iced" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git#9602dfd2f12b667e0afacdccd0e403d8152dde5a" dependencies = [ - "build_helpers", "dnd", "iced_accessibility", "iced_core", @@ -3090,13 +3055,14 @@ dependencies = [ "iced_winit", "image", "mime 0.1.0", - "thiserror 2.0.20", + "thiserror 2.0.18", "window_clipboard", ] [[package]] name = "iced_accessibility" version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic.git#9602dfd2f12b667e0afacdccd0e403d8152dde5a" dependencies = [ "accesskit", "accesskit_winit", @@ -3105,9 +3071,9 @@ dependencies = [ [[package]] name = "iced_core" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git#9602dfd2f12b667e0afacdccd0e403d8152dde5a" dependencies = [ - "bitflags 2.13.2", - "build_helpers", + "bitflags 2.11.0", "bytes", "cosmic-client-toolkit 0.2.0", "dnd", @@ -3118,11 +3084,10 @@ dependencies = [ "num-traits", "palette", "raw-window-handle", - "rustc-hash 2.1.3", + "rustc-hash 2.1.1", "serde", "smol_str", - "thiserror 2.0.20", - "unicode-segmentation", + "thiserror 2.0.18", "web-time", "window_clipboard", ] @@ -3130,6 +3095,7 @@ dependencies = [ [[package]] name = "iced_debug" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git#9602dfd2f12b667e0afacdccd0e403d8152dde5a" dependencies = [ "iced_core", "iced_futures", @@ -3139,11 +3105,12 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git#9602dfd2f12b667e0afacdccd0e403d8152dde5a" dependencies = [ "futures", "iced_core", "log", - "rustc-hash 2.1.3", + "rustc-hash 2.1.1", "tokio", "wasm-bindgen-futures", "wasmtimer", @@ -3152,8 +3119,9 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git#9602dfd2f12b667e0afacdccd0e403d8152dde5a" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "bytemuck", "cosmic-text", "half", @@ -3164,14 +3132,15 @@ dependencies = [ "log", "lyon_path", "raw-window-handle", - "rustc-hash 2.1.3", - "thiserror 2.0.20", + "rustc-hash 2.1.1", + "thiserror 2.0.18", "unicode-segmentation", ] [[package]] name = "iced_program" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git#9602dfd2f12b667e0afacdccd0e403d8152dde5a" dependencies = [ "iced_graphics", "iced_runtime", @@ -3180,32 +3149,34 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git#9602dfd2f12b667e0afacdccd0e403d8152dde5a" dependencies = [ "iced_graphics", "iced_tiny_skia", "iced_wgpu", "log", - "thiserror 2.0.20", + "thiserror 2.0.18", ] [[package]] name = "iced_runtime" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git#9602dfd2f12b667e0afacdccd0e403d8152dde5a" dependencies = [ - "build_helpers", "bytes", "cosmic-client-toolkit 0.2.0", "dnd", "iced_core", "iced_futures", "raw-window-handle", - "thiserror 2.0.20", + "thiserror 2.0.18", "window_clipboard", ] [[package]] name = "iced_tiny_skia" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git#9602dfd2f12b667e0afacdccd0e403d8152dde5a" dependencies = [ "bytemuck", "cosmic-text", @@ -3214,17 +3185,18 @@ dependencies = [ "kurbo 0.10.4", "log", "resvg", - "rustc-hash 2.1.3", + "rustc-hash 2.1.1", "softbuffer", - "tiny-skia 0.11.4", + "tiny-skia", ] [[package]] name = "iced_wgpu" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git#9602dfd2f12b667e0afacdccd0e403d8152dde5a" dependencies = [ - "bitflags 2.13.2", - "build_helpers", + "as-raw-xcb-connection", + "bitflags 2.11.0", "bytemuck", "cosmic-client-toolkit 0.2.0", "cryoglyph", @@ -3237,21 +3209,23 @@ dependencies = [ "lyon", "raw-window-handle", "resvg", - "rustc-hash 2.1.3", + "rustc-hash 2.1.1", "rustix 0.38.44", - "thiserror 2.0.20", + "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#9602dfd2f12b667e0afacdccd0e403d8152dde5a" dependencies = [ - "build_helpers", "cosmic-client-toolkit 0.2.0", "dnd", "iced_renderer", @@ -3259,8 +3233,8 @@ dependencies = [ "log", "num-traits", "ouroboros", - "rustc-hash 2.1.3", - "thiserror 2.0.20", + "rustc-hash 2.1.1", + "thiserror 2.0.18", "unicode-segmentation", "window_clipboard", ] @@ -3268,8 +3242,8 @@ dependencies = [ [[package]] name = "iced_winit" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic.git#9602dfd2f12b667e0afacdccd0e403d8152dde5a" dependencies = [ - "build_helpers", "cosmic-client-toolkit 0.2.0", "cursor-icon", "dnd", @@ -3280,9 +3254,9 @@ dependencies = [ "iced_runtime", "log", "raw-window-handle", - "rustc-hash 2.1.3", + "rustc-hash 2.1.1", "rustix 0.38.44", - "thiserror 2.0.20", + "thiserror 2.0.18", "wasm-bindgen-futures", "wayland-backend", "wayland-client", @@ -3299,9 +3273,9 @@ dependencies = [ [[package]] name = "icu" -version = "2.3.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4867dd3a39b9d48632416cc7fc3dfa1913d89fd36cc02fe7bfc1dddc6fda3cde" +checksum = "67ab713dd86fa032cb5487f9ac3a85d47b5dcf4c7b8c7dd00210b3cadd6a6551" dependencies = [ "icu_calendar", "icu_casemap", @@ -3323,15 +3297,15 @@ dependencies = [ [[package]] name = "icu_calendar" -version = "2.3.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58655df2f728e46e4eee80bddebefacf52ec3e77cdb925014b47c5a5905eb55c" +checksum = "d6f0e52e009b6b16ba9c0693578796f2dd4aaa59a7f8f920423706714a89ac4e" dependencies = [ "calendrical_calculations", "displaydoc", "icu_calendar_data", + "icu_locale", "icu_locale_core", - "icu_locale_fallback", "icu_provider", "ixdtf", "serde", @@ -3341,15 +3315,15 @@ dependencies = [ [[package]] name = "icu_calendar_data" -version = "2.3.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc00caaa3fb3201ff7a18aa458e8c3ab042b9da154cfdf948bdde3936c31c36e" +checksum = "527f04223b17edfe0bd43baf14a0cb1b017830db65f3950dc00224860a9a446d" [[package]] name = "icu_casemap" -version = "2.3.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2edfd120d8445de853cf53a1abec916f638f933a70a6f4bf8f7e2d7b7f884e9" +checksum = "d4ca9983e8bf51223c2f89014fa4eaa9e9b336c47f3af0d000538f86f841fba1" dependencies = [ "icu_casemap_data", "icu_collections", @@ -3363,20 +3337,20 @@ dependencies = [ [[package]] name = "icu_casemap_data" -version = "2.3.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "059fcda0fc8d33effc6633a8cf0d8a3c17caf7d7c91918fba9918744f501e4e8" +checksum = "98d4663d0f99b301033a19e0acf94e9d2fa4b107638580165e5a6ccc49ad1450" [[package]] name = "icu_collator" -version = "2.3.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08984ed58ac439ebf3e13d2cf26b0c46a60afcd21721c1d14087c0b240344dda" +checksum = "32eed11a5572f1088b63fa21dc2e70d4a865e5739fc2d10abc05be93bae97019" dependencies = [ "icu_collator_data", "icu_collections", + "icu_locale", "icu_locale_core", - "icu_locale_fallback", "icu_normalizer", "icu_properties", "icu_provider", @@ -3388,20 +3362,19 @@ dependencies = [ [[package]] name = "icu_collator_data" -version = "2.3.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d7e54efdddeb1208c08dd5d32b53a879ac00d2d3d051b2255fd26821d16368" +checksum = "5ab06f0e83a613efddba3e4913e00e43ed4001fae651cb7d40fc7e66b83b6fb9" [[package]] name = "icu_collections" -version = "2.3.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", "serde", - "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -3409,17 +3382,17 @@ dependencies = [ [[package]] name = "icu_datetime" -version = "2.3.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c44df99c4c5297b05653d1a1629f13bdc9fd4044acfbbaf30b15194a1eed4cd6" +checksum = "1b9d49f41ded8e63761b6b4c3120dfdc289415a1ed10107db6198eb311057ca5" dependencies = [ "displaydoc", "fixed_decimal", "icu_calendar", "icu_datetime_data", "icu_decimal", + "icu_locale", "icu_locale_core", - "icu_locale_fallback", "icu_pattern", "icu_plurals", "icu_provider", @@ -3432,23 +3405,20 @@ dependencies = [ [[package]] name = "icu_datetime_data" -version = "2.3.0" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bb5555f6e4b42233f58564351ce16d73e9e6ebfce6bdcf29875d0cfc554d6a8" +checksum = "46597233625417b7c8052a63d916e4fdc73df21614ac0b679492a5d6e3b01aeb" [[package]] name = "icu_decimal" -version = "2.3.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eb8f655bba2d0c0459e43a5b0e6cd3cd388109898fb3d85d048165e8b0abd08" +checksum = "a38c52231bc348f9b982c1868a2af3195199623007ba2c7650f432038f5b3e8e" dependencies = [ - "displaydoc", "fixed_decimal", "icu_decimal_data", + "icu_locale", "icu_locale_core", - "icu_locale_fallback", - "icu_pattern", - "icu_plurals", "icu_provider", "serde", "writeable", @@ -3457,15 +3427,15 @@ dependencies = [ [[package]] name = "icu_decimal_data" -version = "2.3.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c887fc7d4c297cf3f9436864af9e3dba7f8be9415a6c2a7f392f3b1636298c" +checksum = "2905b4044eab2dd848fe84199f9195567b63ab3a93094711501363f63546fef7" [[package]] name = "icu_experimental" -version = "0.6.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c09609ad18e249b97a0afa20e19502c4d11ab039e6e35f5ecf48bebd4eedd7e" +checksum = "f4ffa4d60b9cb8b024082afaf9e94d853184e483ec69322c74dc437bf8a882a5" dependencies = [ "displaydoc", "either", @@ -3477,7 +3447,6 @@ dependencies = [ "icu_list", "icu_locale", "icu_locale_core", - "icu_locale_fallback", "icu_normalizer", "icu_pattern", "icu_plurals", @@ -3497,18 +3466,18 @@ dependencies = [ [[package]] name = "icu_experimental_data" -version = "0.6.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "377481a8935d6774f410424d79a6dc8a09d9bd8def9560cff17abe755a246280" +checksum = "a0bce39e12480e91c7ddb748218050c459e241f491d130ea6ee92c3e5cd254f7" [[package]] name = "icu_list" -version = "2.3.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5c625125085fac4bf9ad111888a90c6ab9dc7bcaec392ca79cab417f46e3fb" +checksum = "d3a0b7b126e2fc42777d3c348611553d540bd3683caa39b387c5dd1036bb21a8" dependencies = [ "icu_list_data", - "icu_locale_fallback", + "icu_locale", "icu_provider", "regex-automata", "serde", @@ -3518,30 +3487,30 @@ dependencies = [ [[package]] name = "icu_list_data" -version = "2.3.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ffef91ee351bd195a803115615a034dfb9f02bb48850980548c31cb78568a91" +checksum = "51044c242fe2a882cc0a464314bbdb9f441556a1cb238fb527fc47355ec2827b" [[package]] name = "icu_locale" -version = "2.3.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "785f595c61ef57169a467eeed7b4b6936a66f6cacbb116a96ee86da983251bf4" +checksum = "532b11722e350ab6bf916ba6eb0efe3ee54b932666afec989465f9243fe6dd60" dependencies = [ "icu_collections", "icu_locale_core", "icu_locale_data", - "icu_locale_fallback", "icu_provider", + "potential_utf", "tinystr", "zerovec", ] [[package]] name = "icu_locale_core" -version = "2.3.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -3553,35 +3522,15 @@ dependencies = [ [[package]] name = "icu_locale_data" -version = "2.3.0" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d37d91460e362a5cf58907cd7ce871411775ee6294e49a5cc8e6cec16dfa75ea" - -[[package]] -name = "icu_locale_fallback" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "251af8e57c9400e3eb58242fe5b8b1152b2a64fdf4cf632f923c38ccee6f2fa9" -dependencies = [ - "icu_locale_core", - "icu_locale_fallback_data", - "icu_provider", - "potential_utf", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locale_fallback_data" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "decf2a22ec8fa68f1a0c1129a3f8583f8f8bc24e8b9ccbe98ead99f62a4dc3a8" +checksum = "1c5f1d16b4c3a2642d3a719f18f6b06070ab0aef246a6418130c955ae08aa831" [[package]] name = "icu_normalizer" -version = "2.3.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -3596,15 +3545,15 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.3.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_pattern" -version = "0.5.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b3db611446bd92c246b03e4aa0bafa71ddb194af431ddbb42b9d70e4bfce1a0" +checksum = "7a7ff8c0ff6f61cdce299dcb54f557b0a251adbc78f6f0c35a21332c452b4a1b" dependencies = [ "displaydoc", "either", @@ -3616,12 +3565,12 @@ dependencies = [ [[package]] name = "icu_plurals" -version = "2.3.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e475e6766ef87b1d3c1f97be6363995b0bfaeddbc789671615df598a97ff0593" +checksum = "4f9cfe49f5b1d1163cc58db451562339916a9ca5cbcaae83924d41a0bf839474" dependencies = [ "fixed_decimal", - "icu_locale_fallback", + "icu_locale", "icu_plurals_data", "icu_provider", "zerovec", @@ -3629,17 +3578,16 @@ dependencies = [ [[package]] name = "icu_plurals_data" -version = "2.3.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1251aa39a95e1333888e499b1263e1c196447a28948acf5bdabd82c046f153bf" +checksum = "f018a98dccf7f0eb02ba06ac0ff67d102d8ded80734724305e924de304e12ff0" [[package]] name = "icu_properties" -version = "2.3.0" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", @@ -3651,15 +3599,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.3.0" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" -version = "2.3.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", @@ -3674,32 +3622,31 @@ dependencies = [ [[package]] name = "icu_segmenter" -version = "2.3.0" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d07aafccd67af15d02512a6adf5896fbc5ed00f2e99b471d2efa14016db3db" +checksum = "a807a7488f3f758629ae86d99d9d30dce24da2fb2945d74c80a4f4a62c71db73" dependencies = [ "core_maths", "icu_collections", - "icu_locale_fallback", + "icu_locale", "icu_provider", "icu_segmenter_data", "potential_utf", - "smallvec", "utf8_iter", "zerovec", ] [[package]] name = "icu_segmenter_data" -version = "2.3.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae293c039020f9ec10710af98d29ce6aa2051486638b49c9a6409f3b4a9e98ad" +checksum = "6ebbb7321d9e21d25f5660366cb6c08201d0175898a3a6f7a41ee9685af21c80" [[package]] name = "icu_time" -version = "2.3.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af37e1c3b9f3ab564b619fdf4b22de169f6381eb0f7048e3ca454290c438126b" +checksum = "8242b00da3b3b6678f731437a11c8833a43c821ae081eca60ba1b7579d45b6d8" dependencies = [ "calendrical_calculations", "displaydoc", @@ -3715,9 +3662,15 @@ dependencies = [ [[package]] name = "icu_time_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e10b0e5e87a2c84bd5fa407705732052edebe69291d347d0c3033785470edbf" + +[[package]] +name = "id-arena" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005ff86fa5851a77fc0a977b72d717f777bd9230415a247219d7898aefb1517a" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" [[package]] name = "ident_case" @@ -3738,9 +3691,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -3748,9 +3701,9 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.33" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b69833ed729dc5aa7d19541d96d6cf8e9137194207a04916d658e43168402f" +checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a" dependencies = [ "crossbeam-deque", "globset", @@ -3772,7 +3725,7 @@ dependencies = [ "byteorder-lite", "color_quant", "exr", - "gif 0.14.2", + "gif 0.14.1", "image-webp", "moxcms", "num-traits", @@ -3782,17 +3735,8 @@ dependencies = [ "rayon", "rgb", "tiff", - "zune-core 0.5.3", - "zune-jpeg 0.5.15", -] - -[[package]] -name = "image-extras" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60d02eb2c9ccbbab470538fce34c7bc3be7b4e59268e65a3171367b296cdb842" -dependencies = [ - "image", + "zune-core 0.5.1", + "zune-jpeg 0.5.13", ] [[package]] @@ -3813,9 +3757,9 @@ checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" [[package]] name = "imgref" -version = "1.12.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e44b0a4eaa4c82f441d50a963f2d5f05a787240aeee097597033e72accfd22f" +checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" [[package]] name = "indexmap" @@ -3830,12 +3774,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.14.2" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.17.1", + "hashbrown 0.16.1", "serde", "serde_core", ] @@ -3851,31 +3795,31 @@ dependencies = [ [[package]] name = "inotify" -version = "0.11.5" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cc00ea907cab49550b7da656f80ebb97be1b997d931fbcd28d39734e17ce592" +checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "inotify-sys", "libc", ] [[package]] name = "inotify-sys" -version = "0.1.8" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c033f80b2c113cdf91ab7a33faa9cbc014726dcad99880c8609af2a370edf37d" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" dependencies = [ "libc", ] [[package]] name = "inout" -version = "0.2.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ - "hybrid-array", + "generic-array", ] [[package]] @@ -3886,7 +3830,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] @@ -3910,11 +3854,11 @@ dependencies = [ [[package]] name = "io-uring" -version = "0.7.15" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3bd0ecfbb87805f538bb7b32e5239ca0763890c623e349860ecba69469f2bb" +checksum = "fdd7bddefd0a8833b88a4b68f90dae22c7450d11b354198baee3874fd811b344" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "cfg-if", "libc", ] @@ -3966,71 +3910,47 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.18" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "ixdtf" -version = "0.6.6" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d3667095d64c3ecffc96463a21157b04bf3e252f6e8d5750b20c02e33c194e3" +checksum = "84de9d95a6d2547d9b77ee3f25fa0ee32e3c3a6484d47a55adebc0439c077992" [[package]] name = "jiff" -version = "0.2.37" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1baf72f08796de0260609515130699b890ac25f30e610ad894bc5856cafdb" +checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" dependencies = [ - "defmt", - "jiff-core", "jiff-static", "jiff-tzdb-platform", "log", "portable-atomic", "portable-atomic-util", "serde_core", - "windows-link 0.2.1", -] - -[[package]] -name = "jiff-core" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e52fe76043ccecc9005d2305ebaadf7d7fc0cc89ca6baa10a94d6bc68c7128c" -dependencies = [ - "defmt", - "log", -] - -[[package]] -name = "jiff-icu" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e67c2beaae8b10a82d849b9aabb698a43a682f32b17bcdc035d5ecadb44d646" -dependencies = [ - "icu_calendar", - "icu_time", - "jiff", + "windows-sys 0.61.2", ] [[package]] name = "jiff-static" -version = "0.2.37" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "378268a1116ad67ae6228701118ac9f491d78fda38a40a1f1a9e1348de6f7212" +checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" dependencies = [ - "jiff-core", "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] name = "jiff-tzdb" -version = "0.1.8" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" +checksum = "c900ef84826f1338a557697dc8fc601df9ca9af4ac137c7fb61d4c6f2dfd3076" [[package]] name = "jiff-tzdb-platform" @@ -4043,80 +3963,43 @@ dependencies = [ [[package]] name = "jni" -version = "0.22.4" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" dependencies = [ + "cesu8", "cfg-if", "combine", - "jni-macros", - "jni-sys 0.4.1", + "jni-sys", "log", - "simd_cesu8", - "thiserror 2.0.20", + "thiserror 1.0.69", "walkdir", - "windows-link 0.2.1", -] - -[[package]] -name = "jni-macros" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" -dependencies = [ - "proc-macro2", - "quote", - "rustc_version", - "simd_cesu8", - "syn 2.0.119", + "windows-sys 0.45.0", ] [[package]] name = "jni-sys" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" -dependencies = [ - "jni-sys 0.4.1", -] - -[[package]] -name = "jni-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" -dependencies = [ - "jni-sys-macros", -] - -[[package]] -name = "jni-sys-macros" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" -dependencies = [ - "quote", - "syn 2.0.119", -] +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.35" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom 0.4.3", + "getrandom 0.3.4", "libc", ] [[package]] name = "js-sys" -version = "0.3.105" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" dependencies = [ - "cfg-if", - "futures-util", + "once_cell", "wasm-bindgen", ] @@ -4173,9 +4056,9 @@ dependencies = [ [[package]] name = "jxl-grid" -version = "0.6.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01671307879a033bfa52e6e8784b941aca770b3f3a7d33830b455b6844f793fb" +checksum = "a0e0ef92d5d60e76bf41098e57e985f523185e08fad54268da448637feca6989" dependencies = [ "tracing", ] @@ -4212,9 +4095,9 @@ dependencies = [ [[package]] name = "jxl-modular" -version = "0.11.3" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2f045b24c738dd91d482be385512b512721ae08a671bd4b27bf1c47f215235" +checksum = "da758b2f989aafd9eeb39489fe43d7be5a3a0d2ad61cf1bad705eb6990a6053c" dependencies = [ "jxl-bitstream", "jxl-coding", @@ -4226,9 +4109,9 @@ dependencies = [ [[package]] name = "jxl-oxide" -version = "0.12.6" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d36c662923f47586880211f3bc7c0d83fb3a9b410d278c7bde93450748abeef3" +checksum = "ee8ecd2678ed70c1eda42b811ccb2e25ab836edeb18e7f1178c1f917ed36b772" dependencies = [ "brotli-decompressor", "bytemuck", @@ -4256,9 +4139,9 @@ dependencies = [ [[package]] name = "jxl-render" -version = "0.12.4" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34386bfdb6a19b5a30cc9beb4d475d537422c31ae8c39bb69640fcce3fcaf19" +checksum = "aa0c3100918bd3c41bb0f8ce1f4f1664e48f3032ff8eeab0d6a2cfc3276f462d" dependencies = [ "bytemuck", "jxl-bitstream", @@ -4315,7 +4198,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fbe853b403ae61a04233030ae8a79d94975281ed9770a1f9e246732b534b28d" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "serde", ] @@ -4338,18 +4221,18 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "known-folders" -version = "1.4.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a1886916523694cd6ea3d175f03a1e5010699a2a4cc13696d83d7bea1d80638" +checksum = "770919970f7d2f74fea948900d35e2ef64f44129e8ae4015f59de1f0aca7c2a5" dependencies = [ "windows-sys 0.61.2", ] [[package]] name = "kqueue" -version = "1.2.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d763e5b24120b4ddf50de6c92308156765aabfbbccebf401da7cff2d70a41ea" +checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" dependencies = [ "kqueue-sys", "libc", @@ -4357,11 +4240,11 @@ dependencies = [ [[package]] name = "kqueue-sys" -version = "1.1.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" dependencies = [ - "bitflags 2.13.2", + "bitflags 1.3.2", "libc", ] @@ -4392,6 +4275,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "lebe" version = "0.5.3" @@ -4400,24 +4289,24 @@ checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" [[package]] name = "libbz2-rs-sys" -version = "0.2.5" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34b357333733e8260735ba5894eb928c02ecc69c78715f01a8019e7fa7f2db4c" +checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" [[package]] name = "libc" -version = "0.2.189" +version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] -name = "libcosmic-yoda" -version = "0.1.0-yoda.2" +name = "libcosmic" +version = "1.0.0" +source = "git+https://github.com/pop-os/libcosmic.git#9602dfd2f12b667e0afacdccd0e403d8152dde5a" dependencies = [ "apply", "ashpd 0.12.3", "auto_enums", - "build_helpers", "cosmic-client-toolkit 0.2.0", "cosmic-config", "cosmic-freedesktop-icons", @@ -4426,7 +4315,6 @@ dependencies = [ "cosmic-theme", "css-color", "derive_setters", - "enumflags2", "float-cmp 0.10.0", "freedesktop-desktop-entry", "futures", @@ -4442,34 +4330,33 @@ dependencies = [ "iced_widget", "iced_winit", "image", - "image-extras", "jiff", "libc", "log", "mime 0.3.17", "palette", - "phf", + "phf 0.13.1", + "raw-window-handle", "rfd", - "roxmltree", "rust-embed", "rustix 1.1.4", "serde", - "shlex 1.3.0", + "shlex", "slotmap", "taffy", - "thiserror 2.0.20", + "thiserror 2.0.18", "tokio", "tracing", "unicode-segmentation", "url", - "zbus 5.19.0", + "zbus 5.14.0", ] [[package]] name = "libfuzzer-sys" -version = "0.4.13" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9fd2f41a1cba099f79a0b6b6c35656cf7c03351a7bae8ff0f28f25270f929d2" +checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" dependencies = [ "arbitrary", "cc", @@ -4493,21 +4380,21 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.24" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6480ccc157a1389bb2e4891b24751b0f798ba640d22386f23143fbcc89da195a" +checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "libc", "plain", - "redox_syscall 0.9.4", + "redox_syscall 0.7.3", ] [[package]] name = "lilt" -version = "0.8.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "337d4c256f7d9f2dbd633891d48ace853efa5b1554122d9220b172ad9c03c3a9" +checksum = "f67562e5eff6b20553fa9be1c503356768420994e28f67e3eafe6f41910e57ad" dependencies = [ "web-time", ] @@ -4524,6 +4411,12 @@ 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" @@ -4532,9 +4425,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.3" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "litrs" @@ -4566,9 +4459,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.34" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "loom" @@ -4595,9 +4488,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.16.4" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" +checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" [[package]] name = "lyon" @@ -4611,9 +4504,9 @@ dependencies = [ [[package]] name = "lyon_algorithms" -version = "1.0.21" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdfa8785f95e57914ddb35e3b59994aeba6f5e79e9cfd03da1c269f010f36009" +checksum = "9815fac08e6fd96733a11dce4f9d15a3f338e96a2e2311ee21e1b738efc2bc0f" dependencies = [ "lyon_path", "num-traits", @@ -4642,9 +4535,9 @@ dependencies = [ [[package]] name = "lyon_tessellation" -version = "1.0.22" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b8dcf906637ecef61b3c0740c7a4e7f27caeb31257cfac0cc579ce15be6005" +checksum = "05a35a7dd71b845ff317ce1834c4185506b79790294bde397df8d5c23031e357" dependencies = [ "float_next_after", "lyon_path", @@ -4653,25 +4546,24 @@ dependencies = [ [[package]] name = "lzma-rust2" -version = "0.16.5" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca93e534d1142d1d0dcca6d25fe302508a5dfb40b302802904577725ea0b695b" +checksum = "1670343e58806300d87950e3401e820b519b9384281bbabfb15e3636689ffd69" dependencies = [ - "sha2 0.11.0", + "crc", + "sha2", ] [[package]] name = "mac-notification-sys" -version = "0.6.15" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd604973958ddcc11b561193c0fb96ba146506ef2f231ef2e7c35fd2cbc9beca" +checksum = "29a16783dd1a47849b8c8133c9cd3eb2112cfbc6901670af3dba47c8bbfb07d3" dependencies = [ "cc", - "log", "objc2 0.6.4", "objc2-foundation 0.3.2", "time", - "uuid", ] [[package]] @@ -4709,14 +4601,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ "cfg-if", - "digest 0.10.7", + "digest", ] [[package]] name = "memchr" -version = "2.8.3" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memmap2" @@ -4729,9 +4621,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.9.11" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" dependencies = [ "libc", ] @@ -4747,11 +4639,11 @@ dependencies = [ [[package]] name = "metal" -version = "0.33.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7047791b5bc903b8cd963014b355f71dc9864a9a0b727057676c1dcae5cbc15" +checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "block", "core-graphics-types 0.2.0", "foreign-types", @@ -4763,6 +4655,7 @@ dependencies = [ [[package]] name = "mime" version = "0.1.0" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#3a7af79e54db6854d8aa9d9e2866a9288d0f95d5" dependencies = [ "smithay-clipboard", ] @@ -4799,21 +4692,11 @@ dependencies = [ "simd-adler32", ] -[[package]] -name = "miniz_oxide" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" -dependencies = [ - "adler2", - "simd-adler32", -] - [[package]] name = "mio" -version = "1.2.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "log", @@ -4839,27 +4722,27 @@ checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af" [[package]] name = "naga" -version = "28.0.0" +version = "27.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "618f667225063219ddfc61251087db8a9aec3c3f0950c916b614e403486f1135" +checksum = "066cf25f0e8b11ee0df221219010f213ad429855f57c494f995590c861a9a7d8" dependencies = [ "arrayvec", "bit-set", - "bitflags 2.13.2", + "bitflags 2.11.0", "cfg-if", "cfg_aliases", "codespan-reporting", "half", "hashbrown 0.16.1", "hexf-parse", - "indexmap 2.14.2", + "indexmap 2.13.0", "libm", "log", "num-traits", "once_cell", "rustc-hash 1.1.0", "spirv", - "thiserror 2.0.20", + "thiserror 2.0.18", "unicode-ident", ] @@ -4869,8 +4752,8 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.13.2", - "jni-sys 0.3.1", + "bitflags 2.11.0", + "jni-sys", "log", "ndk-sys", "num_enum", @@ -4890,7 +4773,7 @@ version = "0.6.0+11769913" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" dependencies = [ - "jni-sys 0.3.1", + "jni-sys", ] [[package]] @@ -4905,28 +4788,13 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "cfg-if", "cfg_aliases", "libc", "memoffset", ] -[[package]] -name = "no-std-compat" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" - -[[package]] -name = "no_std_io2" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418abd1b6d34fbf6cae440dc874771b0525a604428704c76e48b29a5e67b8003" -dependencies = [ - "memchr", -] - [[package]] name = "nom" version = "7.1.3" @@ -4958,7 +4826,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "fsevent-sys", "inotify", "kqueue", @@ -4985,16 +4853,16 @@ dependencies = [ [[package]] name = "notify-rust" -version = "4.18.0" +version = "4.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b4c1b4f2aa9f25f63a7a49d3dd0ed567b3670da15330a66b29434be899b891" +checksum = "21af20a1b50be5ac5861f74af1a863da53a11c38684d9818d82f1c42f7fdc6c2" dependencies = [ "futures-lite", "log", "mac-notification-sys", "serde", "tauri-winrt-notification", - "zbus 5.19.0", + "zbus 5.14.0", ] [[package]] @@ -5003,7 +4871,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", ] [[package]] @@ -5017,29 +4885,19 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.8" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", ] -[[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "bytemuck", - "num-traits", -] - [[package]] name = "num-conv" -version = "0.2.2" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" [[package]] name = "num-derive" @@ -5049,14 +4907,14 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] name = "num-integer" -version = "0.1.47" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ "num-traits", ] @@ -5111,7 +4969,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] @@ -5165,7 +5023,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -5181,7 +5039,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "block2 0.6.2", "objc2 0.6.4", "objc2-core-foundation", @@ -5194,7 +5052,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -5206,7 +5064,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "block2 0.6.2", "dispatch2", "objc2 0.6.4", @@ -5218,7 +5076,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "libc", "objc2-core-foundation", ] @@ -5241,7 +5099,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "objc2-core-foundation", "objc2-core-graphics", ] @@ -5258,7 +5116,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -5270,7 +5128,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "block2 0.6.2", "libc", "objc2 0.6.4", @@ -5283,7 +5141,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -5295,7 +5153,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -5308,7 +5166,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "objc2 0.6.4", "objc2-core-foundation", "objc2-foundation 0.3.2", @@ -5337,12 +5195,13 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "open" -version = "5.4.4" +version = "5.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa576c76302b7b808eecc68061e67336c47833ef9d22caa74dda10fa9675eebc" +checksum = "43bb73a7fa3799b198970490a51174027ba0d4ec504b03cd08caf513d40024bc" dependencies = [ "is-wsl", "libc", + "pathdiff", ] [[package]] @@ -5353,9 +5212,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "orbclient" -version = "0.3.55" +version = "0.3.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5df339f526ea9a60e371768d50efc2f2508c7203290731565d1f7a6f71d21747" +checksum = "59aed3b33578edcfa1bc96a321d590d31832b6ad55a26f0313362ce687e9abd6" dependencies = [ "libc", "libredox", @@ -5363,9 +5222,9 @@ dependencies = [ [[package]] name = "ordered-float" -version = "5.5.0" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c9e0d9b23589f26070720bac724174bfec1083e82f7854cdd0267518343c0" +checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d" dependencies = [ "num-traits", ] @@ -5382,11 +5241,11 @@ dependencies = [ [[package]] name = "ordermap" -version = "1.2.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16a756ddcdeb6c0efc8927bdf0ed112885815a35ba350472a467be5104f01ca1" +checksum = "cfa78c92071bbd3628c22b1a964f7e0eb201dc1456555db072beb1662ecd6715" dependencies = [ - "indexmap 2.14.2", + "indexmap 2.13.0", "serde", "serde_core", ] @@ -5422,7 +5281,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.119", + "syn", ] [[package]] @@ -5436,35 +5295,29 @@ dependencies = [ [[package]] name = "palette" -version = "0.7.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddeed8580d347d2abf3dcf06a5f0b3dc020258338526b277847cd4248a70fc64" +checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" dependencies = [ "approx", + "fast-srgb8", "palette_derive", - "palette_math", - "phf", + "phf 0.11.3", "serde", ] [[package]] name = "palette_derive" -version = "0.7.7" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88537020289b719d81be994ccf1bbf4990f477e2f69ee52fe3e45f43a02e56be" +checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30" dependencies = [ "by_address", "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] -[[package]] -name = "palette_math" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e6eb142958d64335fb0e345c5b9ead2ecd6fc438c307e9d7d3c4fd428dbaf12" - [[package]] name = "parking" version = "2.2.1" @@ -5507,51 +5360,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" [[package]] -name = "pbkdf2" -version = "0.13.0" +name = "pathdiff" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112d82ceb8c5bf524d9af484d4e4970c9fd5a0cc15ba14ad93dccd28873b0629" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "digest 0.11.3", + "digest", "hmac", ] -[[package]] -name = "pelite" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88dccf4bd32294364aeb7bd55d749604450e9db54605887551f21baea7617685" -dependencies = [ - "dataview", - "libc", - "no-std-compat", - "pelite-macros", - "winapi", -] - -[[package]] -name = "pelite-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a7cf3f8ecebb0f4895f4892a8be0a0dc81b498f9d56735cb769dc31bf00815b" - [[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros 0.11.3", + "phf_shared 0.11.3", +] + [[package]] name = "phf" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" dependencies = [ - "phf_macros", - "phf_shared", + "phf_macros 0.13.1", + "phf_shared 0.13.1", "serde", ] +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared 0.11.3", + "rand 0.8.5", +] + [[package]] name = "phf_generator" version = "0.13.1" @@ -5559,7 +5419,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" dependencies = [ "fastrand", - "phf_shared", + "phf_shared 0.13.1", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -5568,14 +5441,23 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.13.1", + "phf_shared 0.13.1", "proc-macro2", "quote", - "syn 2.0.119", + "syn", "uncased", ] +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + [[package]] name = "phf_shared" version = "0.13.1" @@ -5594,22 +5476,22 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" -version = "1.1.13" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.13" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] @@ -5637,9 +5519,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.34" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plain" @@ -5657,7 +5539,7 @@ dependencies = [ "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.8.9", + "miniz_oxide", ] [[package]] @@ -5666,11 +5548,11 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.8.9", + "miniz_oxide", ] [[package]] @@ -5695,24 +5577,24 @@ checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" [[package]] name = "portable-atomic" -version = "1.15.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" -version = "0.2.8" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715" +checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3" dependencies = [ "portable-atomic", ] [[package]] name = "potential_utf" -version = "0.1.6" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "serde_core", "writeable", @@ -5727,9 +5609,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppmd-rust" -version = "1.5.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196a7c80b9a7652aba7cc070827516c2abe4ccdf53d128e1944003cf5726cff1" +checksum = "efca4c95a19a79d1c98f791f10aebd5c1363b473244630bb7dbde1dc98455a24" [[package]] name = "ppv-lite86" @@ -5746,6 +5628,16 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro-crate" version = "3.5.0" @@ -5756,32 +5648,32 @@ dependencies = [ ] [[package]] -name = "proc-macro-error-attr3" -version = "3.1.1" +name = "proc-macro-error-attr2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e564d14133360e1ae169ffde5da25881b5fa47261665b8e5713c212c27799da" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" dependencies = [ "proc-macro2", "quote", ] [[package]] -name = "proc-macro-error3" -version = "3.1.1" +name = "proc-macro-error2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f0d4471b3436c22106b21913b1dda531558918ae9b7ec55d58aa84b43552233" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" dependencies = [ - "proc-macro-error-attr3", + "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 3.0.5", + "syn", ] [[package]] name = "proc-macro2" -version = "1.0.107" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -5794,7 +5686,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", "version_check", "yansi", ] @@ -5805,7 +5697,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25485360a54d6861439d60facef26de713b1e126bf015ec8f98239467a2b82f7" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "chrono", "flate2", "procfs-core", @@ -5818,58 +5710,41 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6401bf7b6af22f78b563665d15a22e9aef27775b79b149a66ca022468a4e405" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "chrono", "hex", ] [[package]] name = "profiling" -version = "1.0.18" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" dependencies = [ "profiling-procmacros", ] [[package]] name = "profiling-procmacros" -version = "1.0.18" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb" +checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" dependencies = [ "quote", - "syn 2.0.119", + "syn", ] [[package]] -name = "pulp" -version = "0.22.3" +name = "pure-rust-locales" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046aa45b989642ec2e4717c8e72d677b13edd831a4d3b6cf37d9a3e54912496a" -dependencies = [ - "bytemuck", - "cfg-if", - "libm", - "num-complex", - "paste", - "pulp-wasm-simd-flag", - "raw-cpuid", - "reborrow", - "version_check", -] - -[[package]] -name = "pulp-wasm-simd-flag" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d8f70e07b9c3962945a74e59ca1c511bba65b6419468acc217c457d93f3c740" +checksum = "869675ad2d7541aea90c6d88c81f46a7f4ea9af8cd0395d38f11a95126998a0d" [[package]] name = "pxfm" -version = "0.1.30" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" +checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" [[package]] name = "qoi" @@ -5898,27 +5773,37 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.38.4" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" dependencies = [ "memchr", ] [[package]] name = "quick-xml" -version = "0.41.0" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "quick-xml" +version = "0.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" dependencies = [ "memchr", ] [[package]] name = "quote" -version = "1.0.47" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -5937,9 +5822,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.8" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -5948,9 +5833,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.5" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -6002,9 +5887,9 @@ checksum = "ca45419789ae5a7899559e9512e58ca889e41f04f1f2445e9f4b290ceccd1d08" [[package]] name = "rangemap" -version = "1.8.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a611d15b50743feb4c76b7d03edcb0e64f399c26961e4efe6975bc398be6aa3d" +checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68" [[package]] name = "rav1e" @@ -6033,10 +5918,10 @@ dependencies = [ "num-traits", "paste", "profiling", - "rand 0.9.5", + "rand 0.9.2", "rand_chacha 0.9.0", "simd_helpers", - "thiserror 2.0.20", + "thiserror 2.0.18", "v_frame", "wasm-bindgen", ] @@ -6056,15 +5941,6 @@ dependencies = [ "rgb", ] -[[package]] -name = "raw-cpuid" -version = "11.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" -dependencies = [ - "bitflags 2.13.2", -] - [[package]] name = "raw-window-handle" version = "0.6.2" @@ -6073,9 +5949,9 @@ checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "rayon" -version = "1.12.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -6091,6 +5967,16 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "read-fonts" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717cf23b488adf64b9d711329542ba34de147df262370221940dfabc2c91358" +dependencies = [ + "bytemuck", + "font-types 0.10.1", +] + [[package]] name = "read-fonts" version = "0.37.0" @@ -6099,26 +5985,9 @@ checksum = "7b634fabf032fab15307ffd272149b622260f55974d9fad689292a5d33df02e5" dependencies = [ "bytemuck", "core_maths", - "font-types 0.11.3", + "font-types 0.11.0", ] -[[package]] -name = "read-fonts" -version = "0.41.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046a7d674daf459825b32f5062056d6882db0d2f5a479fbd76ccfc870ac18709" -dependencies = [ - "bytemuck", - "font-types 0.12.5", - "once_cell", -] - -[[package]] -name = "reborrow" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03251193000f4bd3b042892be858ee50e8b3719f2b08e5833ac4353724632430" - [[package]] name = "recently-used-xbel" version = "1.2.0" @@ -6135,32 +6004,22 @@ dependencies = [ "url", ] -[[package]] -name = "redox_event" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5018d583d6d2f5499352aea8d177e9067d1eb03ab17c78169d5ba7a30001b15" -dependencies = [ - "bitflags 2.13.2", - "libredox", -] - [[package]] name = "redox_syscall" version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", ] [[package]] name = "redox_syscall" -version = "0.9.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "737970939a87c6fa31e7acad13307bccbb017a073b695b6089a2c484f929e20e" +checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", ] [[package]] @@ -6182,34 +6041,34 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.17", "libredox", - "thiserror 2.0.20", + "thiserror 2.0.18", ] [[package]] name = "ref-cast" -version = "1.0.27" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.27" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn", ] [[package]] name = "regex" -version = "1.13.1" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -6219,9 +6078,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.18" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -6230,9 +6089,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.11" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "renderdoc-sys" @@ -6252,7 +6111,7 @@ dependencies = [ "pico-args", "rgb", "svgtypes", - "tiny-skia 0.11.4", + "tiny-skia", "usvg", "zune-jpeg 0.4.21", ] @@ -6296,8 +6155,8 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db09040cc89e461f1a265139777a2bde7f8d8c67c4936f700c63ce3e2904d468" dependencies = [ - "base64 0.22.1", - "bitflags 2.13.2", + "base64", + "bitflags 2.11.0", "serde", "serde_derive", "unicode-ident", @@ -6305,11 +6164,11 @@ dependencies = [ [[package]] name = "ron" -version = "0.12.2" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81116b9531d61eabc41aeb228e4b6b2435bcca3233b98cf3b3077d4e6e9debb3" +checksum = "fd490c5b18261893f14449cbd28cb9c0b637aebf161cd77900bfdedaff21ec32" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "once_cell", "serde", "serde_derive", @@ -6325,9 +6184,9 @@ checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" [[package]] name = "rust-embed" -version = "8.12.0" +version = "8.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e7760e252aaba7b09f4be00e36476cf585bdb68a53552ac954cdf504ab4bc9" +checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -6336,25 +6195,24 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "8.12.0" +version = "8.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcfc4d6f53af43755f7a723e4b6b8794fcce052a178dd8c6c1dadc5f5343097" +checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa" dependencies = [ - "mime_guess", "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.119", + "syn", "walkdir", ] [[package]] name = "rust-embed-utils" -version = "8.12.0" +version = "8.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ffa149f6aa81b58a5b3011d01a857c4ed12c7a732d2c51947a4c7c692185f0" +checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1" dependencies = [ - "sha2 0.11.0", + "sha2", "walkdir", ] @@ -6366,18 +6224,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.1.3" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustix" @@ -6385,7 +6234,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -6398,7 +6247,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys 0.12.1", @@ -6407,9 +6256,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.23" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rustybuzz" @@ -6417,7 +6266,7 @@ version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "bytemuck", "core_maths", "log", @@ -6452,9 +6301,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" dependencies = [ "dyn-clone", "ref-cast", @@ -6476,34 +6325,34 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sctk-adwaita" -version = "0.11.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2a4360b9abcdcee43809e6fc11d6d61ca5d25734026a8fc46c2883eea3f13f" +checksum = "1dd3accc0f3f4bbaf2c9e1957a030dc582028130c67660d44c0a0345a22ca69b" dependencies = [ "ab_glyph", "log", - "memmap2 0.9.11", + "memmap2 0.9.10", "smithay-client-toolkit", - "tiny-skia 0.12.0", + "tiny-skia", ] [[package]] name = "self_cell" -version = "1.3.0" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813" +checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" [[package]] name = "semver" -version = "1.0.28" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" -version = "1.0.229" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", @@ -6511,31 +6360,31 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.229" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.229" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn", ] [[package]] name = "serde_json" -version = "1.0.151" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ - "indexmap 2.14.2", + "indexmap 2.13.0", "itoa", "memchr", "serde", @@ -6545,39 +6394,37 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.21" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn", ] [[package]] name = "serde_spanned" -version = "1.1.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" dependencies = [ "serde_core", ] [[package]] name = "serde_with" -version = "3.23.0" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "935177bb8c0cd8ca1a4e6d1a2ac8988bea69cab4f9d3a31311e012ad27868ea4" +checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" dependencies = [ - "base64 0.23.1", - "bs58", + "base64", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.14.2", - "jiff", + "indexmap 2.13.0", "schemars 0.9.0", - "schemars 1.2.2", + "schemars 1.2.1", "serde_core", "serde_json", "serde_with_macros", @@ -6586,36 +6433,25 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.23.0" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d607aa01a3cb0ad757d6fd216136910db3c97b102fe686585689615a02dbcdc" +checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" dependencies = [ - "darling 0.24.1", + "darling 0.23.0", "proc-macro2", "quote", - "syn 3.0.5", + "syn", ] [[package]] name = "sha1" -version = "0.10.7" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", - "cpufeatures 0.2.17", - "digest 0.10.7", -] - -[[package]] -name = "sha1" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" -dependencies = [ - "cfg-if", - "cpufeatures 0.3.1", - "digest 0.11.3", + "cpufeatures", + "digest", ] [[package]] @@ -6625,19 +6461,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures 0.2.17", - "digest 0.10.7", -] - -[[package]] -name = "sha2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" -dependencies = [ - "cfg-if", - "cpufeatures 0.3.1", - "digest 0.11.3", + "cpufeatures", + "digest", ] [[package]] @@ -6655,12 +6480,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - [[package]] name = "signal-hook-registry" version = "1.4.8" @@ -6673,19 +6492,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.10" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" - -[[package]] -name = "simd_cesu8" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" -dependencies = [ - "rustc_version", - "simdutf8", -] +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "simd_helpers" @@ -6696,12 +6505,6 @@ dependencies = [ "quote", ] -[[package]] -name = "simdutf8" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" - [[package]] name = "simplecss" version = "0.2.2" @@ -6713,9 +6516,19 @@ dependencies = [ [[package]] name = "siphasher" -version = "1.0.3" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + +[[package]] +name = "skrifa" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c31071dedf532758ecf3fed987cdb4bd9509f900e026ab684b4ecb81ea49841" +dependencies = [ + "bytemuck", + "read-fonts 0.35.0", +] [[package]] name = "skrifa" @@ -6727,16 +6540,6 @@ dependencies = [ "read-fonts 0.37.0", ] -[[package]] -name = "skrifa" -version = "0.44.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819ab7d62b1d3e72d9d9dea5650bac30424f9111364bb94928dbf5ecad1baa68" -dependencies = [ - "bytemuck", - "read-fonts 0.41.0", -] - [[package]] name = "slab" version = "0.4.12" @@ -6754,9 +6557,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.16.1" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba467056f1b547ed52077911161fc86985becbc60e8e1857c8a144dab0def891" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smithay-client-toolkit" @@ -6764,17 +6567,17 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "bytemuck", "calloop", "calloop-wayland-source", "cursor-icon", "libc", "log", - "memmap2 0.9.11", + "memmap2 0.9.10", "pkg-config", "rustix 1.1.4", - "thiserror 2.0.20", + "thiserror 2.0.18", "wayland-backend", "wayland-client", "wayland-csd-frame", @@ -6791,6 +6594,7 @@ dependencies = [ [[package]] name = "smithay-clipboard" version = "0.8.0" +source = "git+https://github.com/pop-os/smithay-clipboard?tag=sctk-0.20#859b02c88f45c554049a67c6ddeec1692ce0e20b" dependencies = [ "libc", "raw-window-handle", @@ -6810,9 +6614,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.5" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", "windows-sys 0.61.2", @@ -6821,33 +6625,38 @@ dependencies = [ [[package]] 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", - "libredox", "log", - "memmap2 0.9.11", + "memmap2 0.9.10", "objc", "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]] name = "spin" -version = "0.9.9" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" dependencies = [ "lock_api", ] @@ -6858,7 +6667,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", ] [[package]] @@ -6888,6 +6697,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "svg_fmt" version = "0.4.5" @@ -6906,31 +6721,20 @@ dependencies = [ [[package]] name = "swash" -version = "0.2.10" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c2499c2d826531388872b2268718aed907a39bd785ab0dcfe57fab26283f92e" +checksum = "47846491253e976bdd07d0f9cc24b7daf24720d11309302ccbbc6e6b6e53550a" dependencies = [ - "skrifa 0.44.0", + "skrifa 0.37.0", "yazi", "zeno", ] [[package]] name = "syn" -version = "2.0.119" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "3.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -6939,9 +6743,9 @@ dependencies = [ [[package]] name = "synchrony" -version = "0.1.9" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d6d5fbc4583cf3e5eee953506f13853a42ee6b4a21a983dffffb10c765cb80" +checksum = "e6d5f5d3091c2d998f6ab4c8b495c0d232ad0aecbc1fa9ac52c247a79d497e16" dependencies = [ "futures-util", "loom", @@ -6955,7 +6759,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] @@ -6969,14 +6773,14 @@ dependencies = [ [[package]] name = "system-deps" -version = "7.0.8" +version = "7.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396a35feb67335377e0251fcbc1092fc85c484bd4e3a7a54319399da127796e7" +checksum = "48c8f33736f986f16d69b6cb8b03f55ddcad5c41acc4ccc39dd88e84aa805e7f" dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml 1.1.6+spec-1.1.0", + "toml 0.9.12+spec-1.1.0", "version-compare", ] @@ -6994,9 +6798,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.46" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" dependencies = [ "filetime", "libc", @@ -7005,17 +6809,18 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.13.5" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" +checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" [[package]] name = "tauri-winrt-notification" -version = "0.7.3" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed071c670382e85fc2f48ae706492d8c338f4f89bf72520d32f8abfe880aade" +checksum = "0b1e66e07de489fe43a46678dd0b8df65e0c973909df1b60ba33874e297ba9b9" dependencies = [ - "thiserror 2.0.20", + "quick-xml 0.37.5", + "thiserror 2.0.18", "windows 0.61.3", "windows-version", ] @@ -7033,7 +6838,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.3", + "getrandom 0.4.2", "once_cell", "rustix 1.1.4", "windows-sys 0.61.2", @@ -7050,9 +6855,9 @@ dependencies = [ [[package]] name = "test-log" -version = "0.2.21" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b9c218384242b5c89b68303ab6f6fc53a312d923f0c14dc6bb860c6aeee40f1" +checksum = "37d53ac171c92a39e4769491c4b4dde7022c60042254b5fc044ae409d34a24d4" dependencies = [ "env_logger", "test-log-macros", @@ -7060,24 +6865,14 @@ dependencies = [ ] [[package]] -name = "test-log-core" -version = "0.2.21" +name = "test-log-macros" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c26ef8b00e4d382e59f6a8ddb3cd790b3a5bb29f21a358a9a69ea2f29f13f27b" +checksum = "be35209fd0781c5401458ab66e4f98accf63553e8fae7425503e92fdd319783b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", -] - -[[package]] -name = "test-log-macros" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "944ad38adcbb71eaa682c56bceeb079e4ca82b4b3edc2a0fde5cb297b77dac8d" -dependencies = [ - "syn 2.0.119", - "test-log-core", + "syn", ] [[package]] @@ -7097,11 +6892,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.20" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.20", + "thiserror-impl 2.0.18", ] [[package]] @@ -7112,25 +6907,25 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] name = "thiserror-impl" -version = "2.0.20" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn", ] [[package]] name = "thread_local" -version = "1.1.10" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", ] @@ -7146,7 +6941,7 @@ dependencies = [ "half", "quick-error", "weezl", - "zune-jpeg 0.5.15", + "zune-jpeg 0.5.13", ] [[package]] @@ -7171,11 +6966,12 @@ dependencies = [ [[package]] name = "time" -version = "0.3.55" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", + "itoa", "js-sys", "num-conv", "powerfmt", @@ -7186,15 +6982,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.9" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.32" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" dependencies = [ "num-conv", "time-core", @@ -7212,21 +7008,7 @@ dependencies = [ "cfg-if", "log", "png 0.17.16", - "tiny-skia-path 0.11.4", -] - -[[package]] -name = "tiny-skia" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47ffee5eaaf5527f630fb0e356b90ebdec84d5d18d937c5e440350f88c5a91ea" -dependencies = [ - "arrayref", - "arrayvec", - "bytemuck", - "cfg-if", - "log", - "tiny-skia-path 0.12.0", + "tiny-skia-path", ] [[package]] @@ -7241,21 +7023,23 @@ dependencies = [ ] [[package]] -name = "tiny-skia-path" -version = "0.12.0" +name = "tiny-xlib" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca365c3faccca67d06593c5980fa6c57687de727a03131735bb85f01fdeeb9" +checksum = "0324504befd01cab6e0c994f34b2ffa257849ee019d3fb3b64fb2c858887d89e" dependencies = [ - "arrayref", - "bytemuck", - "strict-num", + "as-raw-xcb-connection", + "ctor-lite", + "libloading", + "pkg-config", + "tracing", ] [[package]] name = "tinystr" -version = "0.8.4" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "serde_core", @@ -7264,15 +7048,24 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.13.3" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.53.1" +version = "1.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" dependencies = [ "bytes", "libc", @@ -7287,20 +7080,20 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.7.2" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn", ] [[package]] name = "tokio-stream" -version = "0.1.19" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" dependencies = [ "futures-core", "pin-project-lite", @@ -7318,14 +7111,14 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.6+spec-1.1.0" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "920602543f0911ab71da12c50d59701da54c196d1a2bf5cb4b75667f137a406a" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ - "indexmap 2.14.2", + "indexmap 2.13.0", "serde_core", "serde_spanned", - "toml_datetime", + "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", "winnow", @@ -7333,39 +7126,48 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.1.1+spec-1.1.0" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_datetime" +version = "1.0.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.25.15+spec-1.1.0" +version = "0.25.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1340ea94a5856333492c9064b02c778b191dd2c853778d9609debdcdfea3a614" +checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2" dependencies = [ - "indexmap 2.14.2", - "toml_datetime", + "indexmap 2.13.0", + "toml_datetime 1.0.0+spec-1.1.0", "toml_parser", "winnow", ] [[package]] name = "toml_parser" -version = "1.1.3+spec-1.1.0" +version = "1.0.9+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" dependencies = [ "winnow", ] [[package]] name = "toml_writer" -version = "1.1.2+spec-1.1.0" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" [[package]] name = "tracing" @@ -7387,7 +7189,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] @@ -7431,20 +7233,19 @@ dependencies = [ [[package]] name = "trash" -version = "5.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be89b3fe156965d29ac4f8522f3a640c655affdd9f21cb4f36857f0c92c00317" +version = "5.2.2" +source = "git+https://github.com/jackpot51/trash-rs.git?branch=cosmic#a225f753a88e722aeeb27bb2fb00144739911035" dependencies = [ "chrono", "libc", "log", - "objc2 0.6.4", - "objc2-foundation 0.3.2", + "objc2 0.5.2", + "objc2-foundation 0.2.2", "once_cell", "percent-encoding", "scopeguard", "urlencoding", - "windows 0.62.2", + "windows 0.56.0", ] [[package]] @@ -7462,7 +7263,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90" dependencies = [ - "rustc-hash 2.1.3", + "rustc-hash 2.1.1", ] [[package]] @@ -7479,9 +7280,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.20.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "uds_windows" @@ -7572,9 +7373,9 @@ checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" [[package]] name = "unicode-segmentation" -version = "1.13.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-vo" @@ -7588,6 +7389,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "url" version = "2.5.8" @@ -7613,7 +7420,7 @@ version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef" dependencies = [ - "base64 0.22.1", + "base64", "data-url", "flate2", "fontdb", @@ -7627,7 +7434,7 @@ dependencies = [ "siphasher", "strict-num", "svgtypes", - "tiny-skia-path 0.11.4", + "tiny-skia-path", "unicode-bidi", "unicode-script", "unicode-vo", @@ -7654,11 +7461,10 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.26.1" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ef6dac1e96601b4fb3acccccff2139741fcb757cb9a36089bf5be91cfb285ce" +checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" dependencies = [ - "getrandom 0.4.3", "js-sys", "serde_core", "wasm-bindgen", @@ -7721,18 +7527,27 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.4+wasi-0.2.12" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.128" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" dependencies = [ "cfg-if", "once_cell", @@ -7743,19 +7558,23 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.78" +version = "0.4.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef4c5d3d2cdf5c54f4231181768f5510842e350db025faf1f7163b1030ed928" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" dependencies = [ + "cfg-if", + "futures-util", "js-sys", + "once_cell", "wasm-bindgen", + "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.128" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -7763,26 +7582,60 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.128" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 3.0.5", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.128" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap 2.13.0", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap 2.13.0", + "semver", +] + [[package]] name = "wasmtimer" version = "0.4.3" @@ -7799,9 +7652,9 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.17" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38a91b4eaddff87b1cd1074985e3713da4af2c49742d1b356b2c01670a67a078" +checksum = "aa75f400b7f719bcd68b3f47cd939ba654cedeef690f486db71331eec4c6a406" dependencies = [ "cc", "downcast-rs", @@ -7813,11 +7666,11 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.15" +version = "0.31.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c36a0f861ad76d0901f2800b46321410d9f73f2ea88aac0650d86c32688073" +checksum = "ab51d9f7c071abeee76007e2b742499e535148035bb835f97aaed1338cf516c3" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "rustix 1.1.4", "wayland-backend", "wayland-scanner", @@ -7829,16 +7682,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "cursor-icon", "wayland-backend", ] [[package]] name = "wayland-cursor" -version = "0.31.14" +version = "0.31.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a52d18780be9b1314328a3de5f930b73d2200112e3849ca6cb11822793fb34d" +checksum = "4b3298683470fbdc6ca40151dfc48c8f2fd4c41a26e13042f801f85002384091" dependencies = [ "rustix 1.1.4", "wayland-client", @@ -7847,11 +7700,11 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.13" +version = "0.32.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6" +checksum = "b23b5df31ceff1328f06ac607591d5ba360cf58f90c8fad4ac8d3a55a3c4aec7" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -7864,7 +7717,7 @@ version = "20250721.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -7873,11 +7726,11 @@ dependencies = [ [[package]] name = "wayland-protocols-misc" -version = "0.3.12" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8" +checksum = "429b99200febaf95d4f4e46deff6fe4382bcff3280ee16a41cf887b3c3364984" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -7886,11 +7739,11 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.12" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91" +checksum = "d392fc283a87774afc9beefcd6f931582bb97fe0e6ced0b306a62cb1d026527c" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -7899,11 +7752,11 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.12" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" +checksum = "78248e4cc0eff8163370ba5c158630dcae1f3497a586b826eca2ef5f348d6235" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -7913,22 +7766,22 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.11" +version = "0.31.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0" +checksum = "c86287151a309799b821ca709b7345a048a2956af05957c89cb824ab919fa4e3" dependencies = [ "proc-macro2", - "quick-xml 0.41.0", + "quick-xml 0.39.2", "quote", ] [[package]] name = "wayland-server" -version = "0.31.14" +version = "0.31.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde9c29be0f723a573977de51ee455bf3dfa03652730a74f9dd3b337e374d75" +checksum = "63736a4a73e781cf6a736aa32c5d6773c3eb5389197562742a8c611b49b5e359" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "downcast-rs", "rustix 1.1.4", "wayland-backend", @@ -7937,9 +7790,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.31.11" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" +checksum = "374f6b70e8e0d6bf9461a32988fd553b59ff630964924dad6e4a4eb6bd538d17" dependencies = [ "dlib", "log", @@ -7949,9 +7802,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.105" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbddc4a036f00ec4f18c83445bd3115cb306a91da554919a099d9222fe4a7f8" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" dependencies = [ "js-sys", "wasm-bindgen", @@ -7975,13 +7828,12 @@ checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" [[package]] name = "wgpu" -version = "28.0.0" +version = "27.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9cb534d5ffd109c7d1135f34cdae29e60eab94855a625dcfe1705f8bc7ad79f" +checksum = "bfe68bac7cde125de7a731c3400723cadaaf1703795ad3f4805f187459cd7a77" dependencies = [ "arrayvec", - "bitflags 2.13.2", - "bytemuck", + "bitflags 2.11.0", "cfg-if", "cfg_aliases", "document-features", @@ -8005,19 +7857,19 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "28.0.1" +version = "27.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23f4642f53f666adcfd2d3218ab174d1e6681101aef18696b90cbe64d1c10f9" +checksum = "27a75de515543b1897b26119f93731b385a19aea165a1ec5f0e3acecc229cae7" dependencies = [ "arrayvec", "bit-set", "bit-vec", - "bitflags 2.13.2", + "bitflags 2.11.0", "bytemuck", "cfg_aliases", "document-features", "hashbrown 0.16.1", - "indexmap 2.14.2", + "indexmap 2.13.0", "log", "naga", "once_cell", @@ -8027,7 +7879,7 @@ dependencies = [ "raw-window-handle", "rustc-hash 1.1.0", "smallvec", - "thiserror 2.0.20", + "thiserror 2.0.18", "wgpu-core-deps-apple", "wgpu-core-deps-emscripten", "wgpu-core-deps-windows-linux-android", @@ -8037,42 +7889,42 @@ dependencies = [ [[package]] name = "wgpu-core-deps-apple" -version = "28.0.0" +version = "27.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87b7b696b918f337c486bf93142454080a32a37832ba8a31e4f48221890047da" +checksum = "0772ae958e9be0c729561d5e3fd9a19679bcdfb945b8b1a1969d9bfe8056d233" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-emscripten" -version = "28.0.0" +version = "27.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34b251c331f84feac147de3c4aa3aa45112622a95dd7ee1b74384fa0458dbd79" +checksum = "b06ac3444a95b0813ecfd81ddb2774b66220b264b3e2031152a4a29fda4da6b5" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-windows-linux-android" -version = "28.0.0" +version = "27.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ca976e72b2c9964eb243e281f6ce7f14a514e409920920dcda12ae40febaae" +checksum = "71197027d61a71748e4120f05a9242b2ad142e3c01f8c1b47707945a879a03c3" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-hal" -version = "28.0.1" +version = "27.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d6cb474beb218824dcc9e1ce679d973f719262789bfb27407da560cac20eeb" +checksum = "5b21cb61c57ee198bc4aff71aeadff4cbb80b927beb912506af9c780d64313ce" dependencies = [ "android_system_properties", "arrayvec", "ash", "bit-set", - "bitflags 2.13.2", + "bitflags 2.11.0", "block", "bytemuck", "cfg-if", @@ -8080,6 +7932,7 @@ dependencies = [ "core-graphics-types 0.2.0", "glow", "glutin_wgl_sys", + "gpu-alloc", "gpu-allocator", "gpu-descriptor", "hashbrown 0.16.1", @@ -8102,24 +7955,25 @@ dependencies = [ "raw-window-handle", "renderdoc-sys", "smallvec", - "thiserror 2.0.20", + "thiserror 2.0.18", "wasm-bindgen", "web-sys", "wgpu-types", - "windows 0.62.2", - "windows-core 0.62.2", + "windows 0.58.0", + "windows-core 0.58.0", ] [[package]] name = "wgpu-types" -version = "28.0.0" +version = "27.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e18308757e594ed2cd27dddbb16a139c42a683819d32a2e0b1b0167552f5840c" +checksum = "afdcf84c395990db737f2dd91628706cb31e86d72e53482320d368e52b5da5eb" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "bytemuck", "js-sys", "log", + "thiserror 2.0.18", "web-sys", ] @@ -8163,10 +8017,12 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "window_clipboard" version = "0.4.1" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#3a7af79e54db6854d8aa9d9e2866a9288d0f95d5" dependencies = [ "clipboard-win", "clipboard_macos", "clipboard_wayland", + "clipboard_x11", "dnd", "mime 0.1.0", "raw-window-handle", @@ -8175,27 +8031,35 @@ dependencies = [ [[package]] name = "windows" -version = "0.61.3" +version = "0.56.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132" dependencies = [ - "windows-collections 0.2.0", - "windows-core 0.61.2", - "windows-future 0.2.1", - "windows-link 0.1.3", - "windows-numerics 0.2.0", + "windows-core 0.56.0", + "windows-targets 0.52.6", ] [[package]] name = "windows" -version = "0.62.2" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ - "windows-collections 0.3.2", - "windows-core 0.62.2", - "windows-future 0.3.2", - "windows-numerics 0.3.1", + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", ] [[package]] @@ -8208,12 +8072,28 @@ dependencies = [ ] [[package]] -name = "windows-collections" -version = "0.3.2" +name = "windows-core" +version = "0.56.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6" dependencies = [ - "windows-core 0.62.2", + "windows-implement 0.56.0", + "windows-interface 0.56.0", + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", + "windows-targets 0.52.6", ] [[package]] @@ -8222,8 +8102,8 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-implement", - "windows-interface", + "windows-implement 0.60.2", + "windows-interface 0.59.3", "windows-link 0.1.3", "windows-result 0.3.4", "windows-strings 0.4.2", @@ -8235,8 +8115,8 @@ version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-implement", - "windows-interface", + "windows-implement 0.60.2", + "windows-interface 0.59.3", "windows-link 0.2.1", "windows-result 0.4.1", "windows-strings 0.5.1", @@ -8250,18 +8130,29 @@ checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core 0.61.2", "windows-link 0.1.3", - "windows-threading 0.1.0", + "windows-threading", ] [[package]] -name = "windows-future" -version = "0.3.2" +name = "windows-implement" +version = "0.56.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" dependencies = [ - "windows-core 0.62.2", - "windows-link 0.2.1", - "windows-threading 0.2.1", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -8272,7 +8163,29 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -8283,7 +8196,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] @@ -8309,13 +8222,21 @@ dependencies = [ ] [[package]] -name = "windows-numerics" -version = "0.3.1" +name = "windows-result" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ - "windows-core 0.62.2", - "windows-link 0.2.1", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -8336,6 +8257,16 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows-strings" version = "0.4.2" @@ -8354,6 +8285,15 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -8399,6 +8339,21 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -8456,15 +8411,6 @@ dependencies = [ "windows-link 0.1.3", ] -[[package]] -name = "windows-threading" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" -dependencies = [ - "windows-link 0.2.1", -] - [[package]] name = "windows-version" version = "0.1.7" @@ -8474,6 +8420,12 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -8492,6 +8444,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -8510,6 +8468,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -8540,6 +8504,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -8558,6 +8528,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -8576,6 +8552,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -8594,6 +8576,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -8615,8 +8603,9 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winit" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "cfg_aliases", "cursor-icon", "dpi", @@ -8634,14 +8623,16 @@ dependencies = [ "winit-wayland", "winit-web", "winit-win32", + "winit-x11", ] [[package]] name = "winit-android" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ "android-activity", - "bitflags 2.13.2", + "bitflags 2.11.0", "dpi", "ndk", "raw-window-handle", @@ -8653,8 +8644,9 @@ dependencies = [ [[package]] name = "winit-appkit" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "block2 0.6.2", "dispatch2", "dpi", @@ -8674,21 +8666,24 @@ dependencies = [ [[package]] name = "winit-common" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ - "memmap2 0.9.11", + "memmap2 0.9.10", "objc2 0.6.4", "objc2-core-foundation", "smol_str", "tracing", "winit-core", + "x11-dl", "xkbcommon-dl", ] [[package]] name = "winit-core" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "cursor-icon", "dpi", "keyboard-types", @@ -8700,13 +8695,14 @@ dependencies = [ [[package]] name = "winit-orbital" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "dpi", "libredox", "orbclient", "raw-window-handle", - "redox_event", + "redox_syscall 0.7.3", "smol_str", "tracing", "winit-core", @@ -8715,8 +8711,9 @@ dependencies = [ [[package]] name = "winit-uikit" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "block2 0.6.2", "dispatch2", "dpi", @@ -8734,14 +8731,15 @@ dependencies = [ [[package]] name = "winit-wayland" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ "ahash", - "bitflags 2.13.2", + "bitflags 2.11.0", "calloop", "cursor-icon", "dpi", "libc", - "memmap2 0.9.11", + "memmap2 0.9.10", "raw-window-handle", "rustix 1.1.4", "sctk-adwaita", @@ -8759,9 +8757,10 @@ dependencies = [ [[package]] name = "winit-web" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ "atomic-waker", - "bitflags 2.13.2", + "bitflags 2.11.0", "concurrent-queue", "cursor-icon", "dpi", @@ -8780,8 +8779,9 @@ dependencies = [ [[package]] name = "winit-win32" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "cursor-icon", "dpi", "raw-window-handle", @@ -8792,20 +8792,125 @@ 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#a610ac9c7a72b39ff102ed4d946291618dc725b6" +dependencies = [ + "bitflags 2.11.0", + "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 = "1.0.4" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen" -version = "0.57.1" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck 0.5.0", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck 0.5.0", + "indexmap 2.13.0", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap 2.13.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.13.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "write16" @@ -8815,13 +8920,46 @@ checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" [[package]] name = "writeable" -version = "0.6.4" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" 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]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + [[package]] name = "xattr" version = "1.6.1" @@ -8834,9 +8972,9 @@ dependencies = [ [[package]] name = "xcursor" -version = "0.3.11" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "163b33ed8786455e2fa5d72f554057ce3f3182425434f756cd39c99839d88e23" +checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" [[package]] name = "xdg" @@ -8896,7 +9034,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d66ca9352cbd4eecbbc40871d8a11b4ac8107cfc528a6e14d7c19c69d0e1ac9" dependencies = [ "libc", - "memmap2 0.9.11", + "memmap2 0.9.10", "xkeysym", ] @@ -8907,7 +9045,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a974f48060a14e95705c01f24ad9c3345022f4d97441b8a36beb7ed5c4a02d" dependencies = [ "libc", - "memmap2 0.9.11", + "memmap2 0.9.10", "xkeysym", ] @@ -8917,7 +9055,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.13.2", + "bitflags 2.11.0", "dlib", "log", "once_cell", @@ -8935,15 +9073,15 @@ dependencies = [ [[package]] name = "xml" -version = "1.4.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f45bb2c13fec6a6cb4c0f76a7e94839e110a14ec803ec2940777a94c347bc52" +checksum = "b8aa498d22c9bbaf482329839bc5620c46be275a19a812e9a22a2b07529a642a" [[package]] name = "xml-rs" -version = "0.8.29" +version = "0.8.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" [[package]] name = "xmltree" @@ -8980,9 +9118,9 @@ checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5" [[package]] name = "yoke" -version = "0.8.3" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -8991,13 +9129,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", "synstructure", ] @@ -9025,10 +9163,10 @@ dependencies = [ "hex", "nix", "ordered-stream", - "rand 0.8.8", + "rand 0.8.5", "serde", "serde_repr", - "sha1 0.10.7", + "sha1", "static_assertions", "tracing", "uds_windows", @@ -9041,9 +9179,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.19.0" +version = "5.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db4be7c075cb421e4b7ee645541604239bd243ba7c357511f4ff3a74b555907" +checksum = "ca82f95dbd3943a40a53cfded6c2d0a2ca26192011846a1810c4256ef92c60bc" dependencies = [ "async-broadcast", "async-executor", @@ -9070,9 +9208,9 @@ dependencies = [ "uuid", "windows-sys 0.61.2", "winnow", - "zbus_macros 5.19.0", - "zbus_names 4.3.4", - "zvariant 5.15.0", + "zbus_macros 5.14.0", + "zbus_names 4.3.1", + "zvariant 5.10.0", ] [[package]] @@ -9082,7 +9220,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6998de05217a084b7578728a9443d04ea4cd80f2a0839b8d78770b76ccd45863" dependencies = [ "zbus_xml", - "zvariant 5.15.0", + "zvariant 5.10.0", ] [[package]] @@ -9093,10 +9231,10 @@ checksum = "10da05367f3a7b7553c8cdf8fa91aee6b64afebe32b51c95177957efc47ca3a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", "zbus-lockstep", "zbus_xml", - "zvariant 5.15.0", + "zvariant 5.10.0", ] [[package]] @@ -9108,23 +9246,23 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.119", + "syn", "zvariant_utils 2.1.0", ] [[package]] name = "zbus_macros" -version = "5.19.0" +version = "5.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2990635d09ade6df1868f72f8cac69a876a90981e8bd3c40b1be413f8dc88f40" +checksum = "897e79616e84aac4b2c46e9132a4f63b93105d54fe8c0e8f6bffc21fa8d49222" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 3.0.5", - "zbus_names 4.3.4", - "zvariant 5.15.0", - "zvariant_utils 4.2.0", + "syn", + "zbus_names 4.3.1", + "zvariant 5.10.0", + "zvariant_utils 3.3.0", ] [[package]] @@ -9140,34 +9278,25 @@ dependencies = [ [[package]] name = "zbus_names" -version = "4.3.4" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8bf88b4a3ff53e883001e0e0115b297a9d53c31b9c1edd2bfdd853e3428624e" +checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" dependencies = [ "serde", "winnow", - "zvariant 5.15.0", + "zvariant 5.10.0", ] [[package]] name = "zbus_xml" -version = "5.2.1" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1586c021a01ca0a9216dcd874e546382e156a5cbab5fab6cb5f10087e22682a" -dependencies = [ - "serde", - "winnow", - "zbus_names 4.3.4", - "zvariant 5.15.0", -] - -[[package]] -name = "zcheapstr" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1afec51604565183aeb5c54c20aeab286120d4e4460f7f76e3e8bb8c0d99473" +checksum = "441a0064125265655bccc3a6af6bef56814d9277ac83fce48b1cd7e160b80eac" dependencies = [ + "quick-xml 0.38.4", "serde", + "zbus_names 4.3.1", + "zvariant 5.10.0", ] [[package]] @@ -9178,68 +9307,81 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" [[package]] name = "zerocopy" -version = "0.8.57" +version = "0.8.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d35102a9f36d089ccae9e4c6802bc118be4487b80aaffc0ab4e0cf5ce92d2873" +checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.57" +version = "0.8.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c01f5ab44258da43cf276c74a2763db2ff3969c9c652c3f2de07041d0b2bc" +checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] name = "zerofrom" -version = "0.1.8" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.7" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", "synstructure", ] [[package]] name = "zeroize" -version = "1.9.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "zerotrie" -version = "0.2.5" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", "zerofrom", - "zerovec", ] [[package]] name = "zerovec" -version = "0.11.8" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "serde", "yoke", @@ -9249,20 +9391,20 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.6" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn", ] [[package]] name = "zip" -version = "8.6.0" +version = "7.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b" +checksum = "c42e33efc22a0650c311c2ef19115ce232583abbe80850bc8b66509ebef02de0" dependencies = [ "aes", "bzip2", @@ -9270,14 +9412,15 @@ dependencies = [ "crc32fast", "deflate64", "flate2", - "getrandom 0.4.3", + "generic-array", + "getrandom 0.3.4", "hmac", - "indexmap 2.14.2", + "indexmap 2.13.0", "lzma-rust2", "memchr", "pbkdf2", "ppmd-rust", - "sha1 0.11.0", + "sha1", "time", "typed-path", "zeroize", @@ -9287,15 +9430,15 @@ dependencies = [ [[package]] name = "zlib-rs" -version = "0.6.8" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b268e58e7c693d7c271f93ffc4ba3b380412554231c85bf61ca7af91042a4112" +checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" [[package]] name = "zmij" -version = "1.0.23" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] name = "zopfli" @@ -9320,18 +9463,18 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "7.3.0" +version = "7.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d80649ab6db9d9f6f9c80a40becd948eda4714a0a5ac8c4d157a32231c7882" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.1.0+zstd.1.5.7" +version = "2.0.16+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef0a8027ec3ee71300ab3bcbcd0393f434aa72b91ca6d635a39941deae8eea0" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" dependencies = [ "cc", "pkg-config", @@ -9345,9 +9488,9 @@ checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" [[package]] name = "zune-core" -version = "0.5.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56377fd46368984a170bc5aac5567e52ca5da874caa60bea39fcbca78fb658b" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" [[package]] name = "zune-inflate" @@ -9369,11 +9512,11 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.5.15" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" +checksum = "ec5f41c76397b7da451efd19915684f727d7e1d516384ca6bd0ec43ec94de23c" dependencies = [ - "zune-core 0.5.3", + "zune-core 0.5.1", ] [[package]] @@ -9391,18 +9534,17 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.15.0" +version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1d34c27cc6cdd1f458427519dd6b8612f7b7e3f7b9a0b2355d041dda9869147" +checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" dependencies = [ "endi", "enumflags2", "serde", "url", "winnow", - "zcheapstr", - "zvariant_derive 5.15.0", - "zvariant_utils 4.2.0", + "zvariant_derive 5.10.0", + "zvariant_utils 3.3.0", ] [[package]] @@ -9414,21 +9556,21 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.119", + "syn", "zvariant_utils 2.1.0", ] [[package]] name = "zvariant_derive" -version = "5.15.0" +version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864155e69b4352db0c7f374917bf45d1e0c8d17659c8b3dbf9795f3673f8c497" +checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 3.0.5", - "zvariant_utils 4.2.0", + "syn", + "zvariant_utils 3.3.0", ] [[package]] @@ -9439,37 +9581,18 @@ checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn", ] [[package]] name = "zvariant_utils" -version = "4.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad0294361a320b694a328460dc73add56c306150f5cb6bfafc44446120008a3" +checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" dependencies = [ "proc-macro2", "quote", "serde", - "syn 3.0.5", + "syn", "winnow", ] - -[[patch.unused]] -name = "winit-x11" -version = "0.31.0-beta.2" - -[[patch.unused]] -name = "dnd" -version = "0.1.0" -source = "git+https://forge.aditua.com/leyoda/window_clipboard.git?branch=yoda-x11-optional#af8e6a872d8ac4956e044b9cb05a615a48261a57" - -[[patch.unused]] -name = "mime" -version = "0.1.0" -source = "git+https://forge.aditua.com/leyoda/window_clipboard.git?branch=yoda-x11-optional#af8e6a872d8ac4956e044b9cb05a615a48261a57" - -[[patch.unused]] -name = "window_clipboard" -version = "0.4.1" -source = "git+https://forge.aditua.com/leyoda/window_clipboard.git?branch=yoda-x11-optional#af8e6a872d8ac4956e044b9cb05a615a48261a57" diff --git a/Cargo.toml b/Cargo.toml index d366d84..be0b5ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,30 +1,30 @@ [package] name = "cosmic-files" -version = "1.9.0" +version = "1.0.8" authors = ["Jeremy Soller "] edition = "2024" license = "GPL-3.0-only" -rust-version = "1.93" +rust-version = "1.90" [dependencies] anyhow = "1" -jiff = "0.2" -jiff-icu = "0.2" -icu = { version = "2.2.0", features = ["compiled_data"] } -cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "c0cff4d", optional = true } +chrono = { version = "0.4", features = ["unstable-locales"] } +icu = { version = "2.1.1", features = ["compiled_data"] } +cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "d0e95be", optional = true } cosmic-mime-apps = { git = "https://github.com/pop-os/cosmic-mime-apps.git", optional = true } dirs = "6.0.0" +env_logger = "0.11" gio = { version = "0.21", optional = true } glib = { version = "0.21", optional = true } glob = "0.3" ignore = "0.4" -image.workspace = true +image = "0.25" libc = "0.2" -log.workspace = true +log = "0.4" mime_guess = "2" notify-debouncer-full = "0.7" notify-rust = { version = "4", optional = true } -open = "5.3.4" +open = "5.3.3" paste = "1.0" regex = "1" rustc-hash = "2.1" @@ -33,18 +33,18 @@ shlex = { version = "1.3" } tempfile = "3" tikv-jemallocator = { version = "0.6", optional = true } tokio = { version = "1", features = ["process", "sync"] } -trash = "5.2.6" +trash = { git = "https://github.com/jackpot51/trash-rs.git", branch = "cosmic" } url = "2.5" walkdir = "2.5.0" -wayland-client = { version = "0.31.14", optional = true } +wayland-client = { version = "0.31.12", optional = true } xdg = { version = "3.0", optional = true } xdg-mime = { git = "https://github.com/ebassi/xdg-mime-rs" } # Compression bzip2 = { version = "0.6", optional = true } #TODO: replace with pure Rust crate flate2 = "1.1" -tar = "0.4.45" -lzma-rust2 = { version = "0.16", optional = true } -ordermap = { version = "1.2.0", features = ["serde"] } +tar = "0.4.44" +lzma-rust2 = { version = "0.15.7", optional = true } +ordermap = { version = "1.1.0", features = ["serde"] } # Internationalization i18n-embed = { version = "0.16", features = [ "fluent-system", @@ -54,19 +54,13 @@ i18n-embed-fl = "0.10" rust-embed = "8" slotmap = "1.1.1" recently-used-xbel = "1.2.0" -zip = "8" +zip = "7" +uzers = "0.12.2" md-5 = "0.10.6" -sha2 = "0.10" png = "0.18" jxl-oxide = { version = "0.12.5", features = ["image"] } num_cpus = "1.17.0" filetime = "0.2" -tracing = "0.1.44" -tracing-subscriber = { version = "0.3.23", features = ["env-filter"] } -thiserror = "2.0.18" -atomic_float = "1.1.0" -num_enum = "0.7.6" -bstr = "1.12.1" # Completion-based IO runtime to enable io_uring / IOCP file IO support. [dependencies.compio] @@ -74,18 +68,17 @@ version = "0.18" default-features = false features = ["fs", "io", "macros", "polling", "runtime"] -# Yoda fork — depend on libcosmic-yoda directly by path (no git/no patch). -[dependencies.libcosmic-yoda] -path = "../libcosmic" +[dependencies.libcosmic] +git = "https://github.com/pop-os/libcosmic.git" default-features = false #TODO: a11y feature crashes features = [ "about", - "advanced-shaping", "autosize", + "desktop", "multi-window", "tokio", - "wayland", + "winit", "surface-message", ] @@ -113,15 +106,15 @@ default = [ "wayland", "wgpu", ] -dbus-config = ["libcosmic-yoda/dbus-config"] -desktop = ["libcosmic-yoda/desktop", "dep:cosmic-mime-apps", "dep:xdg"] +dbus-config = ["libcosmic/dbus-config"] +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-yoda/wayland", "dep:cctk", "dep:wayland-client"] -wgpu = ["libcosmic-yoda/wgpu"] +wayland = ["libcosmic/wayland", "dep:cctk", "dep:wayland-client"] +wgpu = ["libcosmic/wgpu"] [profile.dev] opt-level = 1 @@ -131,8 +124,7 @@ inherits = "release" debug = true [target.'cfg(unix)'.dependencies] -fork = "0.7" -uzers = "0.12.2" +fork = "0.6" [target.'cfg(target_os = "linux")'.dependencies] procfs = "0.18" @@ -147,27 +139,20 @@ fastrand = "2" test-log = "0.2" tokio = { version = "1", features = ["rt", "macros"] } -# 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/cosmic-text'] +# cosmic-text = { path = "../cosmic-text" } -# Yoda wayland cut: redirect window_clipboard (x11 gated behind opt-in -# feature) to our public Forgejo fork and cosmic-text to the local checkout -# carrying PR#503 plus the EAW monospace width fix. 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/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" } -[patch.'https://github.com/pop-os/cosmic-text.git'] -cosmic-text = { path = "../cosmic-text" } + +# [patch.'https://github.com/pop-os/smithay-clipboard'] +# smithay-clipboard = { path = "../smithay-clipboard" } [workspace] -members = ["cosmic-files-applet", "cosmic-files-thumbnailer"] - -[workspace.dependencies] -image = "0.25" -log = "0.4" +members = ["cosmic-files-applet"] diff --git a/build.rs b/build.rs index 206e897..5edc53b 100644 --- a/build.rs +++ b/build.rs @@ -1,5 +1,4 @@ -use std::path::PathBuf; -use std::{env, fs}; +use std::{env, fs, path::PathBuf}; use xdgen::{App, Context, FluentString}; fn main() { diff --git a/cosmic-files-applet/Cargo.toml b/cosmic-files-applet/Cargo.toml index 3cf3264..0fc8e0e 100644 --- a/cosmic-files-applet/Cargo.toml +++ b/cosmic-files-applet/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "cosmic-files-applet" -version = "1.9.0" +version = "1.0.8" edition = "2024" [dependencies] -log.workspace = true +log = "0.4" zbus = "4" # Blocking connection in zbus 5 hangs [dependencies.cosmic-files] diff --git a/cosmic-files-thumbnailer/Cargo.toml b/cosmic-files-thumbnailer/Cargo.toml deleted file mode 100644 index 53cf47b..0000000 --- a/cosmic-files-thumbnailer/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "cosmic-files-thumbnailer" -version = "1.9.0" -edition = "2024" - -[dependencies] -image.workspace = true -pelite = "0.10" diff --git a/cosmic-files-thumbnailer/src/main.rs b/cosmic-files-thumbnailer/src/main.rs deleted file mode 100644 index 2463849..0000000 --- a/cosmic-files-thumbnailer/src/main.rs +++ /dev/null @@ -1,254 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only - -use image::{DynamicImage, ImageFormat, RgbaImage}; -use std::error::Error; -use std::ffi::{OsStr, OsString}; -use std::io; -use std::path::Path; - -fn main() -> Result<(), Box> { - thumbnail_from_args(std::env::args_os().skip(1)) -} - -#[allow(dead_code)] -pub fn thumbnail_from_args(mut args: impl Iterator) -> Result<(), Box> { - let usage = || { - io::Error::new( - io::ErrorKind::InvalidInput, - "usage: cosmic-files-thumbnailer OUTPUT --size SIZE INPUT", - ) - }; - let output = args.next().ok_or_else(usage)?; - if args.next().as_deref() != Some(OsStr::new("--size")) { - return Err(usage().into()); - } - let size = args - .next() - .and_then(|arg| arg.into_string().ok()) - .and_then(|arg| arg.parse::().ok()) - .filter(|size| *size > 0) - .ok_or_else(usage)?; - let input = args.next().ok_or_else(usage)?; - if args.next().is_some() { - return Err(usage().into()); - } - - thumbnail(Path::new(&input), Path::new(&output), size) -} - -#[allow(dead_code)] -pub fn thumbnail(input: &Path, output: &Path, size: u32) -> Result<(), Box> { - let icon = thumbnail_image(input, size)?; - DynamicImage::ImageRgba8(icon).save_with_format(output, ImageFormat::Png)?; - - Ok(()) -} - -pub fn thumbnail_image(input: &Path, size: u32) -> Result> { - if size == 0 { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "thumbnail size must be positive", - ) - .into()); - } - - // Map the PE so only the headers and resource pages touched by pelite are read. - let data = pelite::FileMap::open(input)?; - let bytes = data.as_ref(); - if bytes.len() < 64 || !bytes.starts_with(b"MZ") { - return Err(io::Error::new(io::ErrorKind::InvalidData, "input is not a PE file").into()); - } - - let file = pelite::PeFile::from_bytes(bytes).map_err(invalid_data)?; - let resources = file.resources().map_err(invalid_data)?; - let (_name, group) = resources - .icons() - .next() - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "PE file has no icon group"))? - .map_err(invalid_data)?; - - let mut ico = Vec::new(); - group.write(&mut ico).map_err(invalid_data)?; - - Ok( - DynamicImage::ImageRgba8(largest_icon(&ico).map_err(invalid_data)?) - .thumbnail(size, size) - .into_rgba8(), - ) -} - -fn invalid_data(error: impl std::fmt::Display) -> io::Error { - io::Error::new(io::ErrorKind::InvalidData, error.to_string()) -} - -fn largest_icon(ico: &[u8]) -> Result { - if ico.len() < 6 { - return Err("ICO header is truncated".to_string()); - } - let count = usize::from(u16::from_le_bytes([ico[4], ico[5]])); - let mut best: Option<(u64, RgbaImage)> = None; - let mut errors = Vec::new(); - - for i in 0..count.min(32) { - let entry = 6 + i * 16; - if ico.len() < entry + 16 { - break; - } - let size = u32::from_le_bytes([ - ico[entry + 8], - ico[entry + 9], - ico[entry + 10], - ico[entry + 11], - ]) as usize; - let offset = u32::from_le_bytes([ - ico[entry + 12], - ico[entry + 13], - ico[entry + 14], - ico[entry + 15], - ]) as usize; - let Some(end) = offset.checked_add(size) else { - continue; - }; - let Some(data) = ico.get(offset..end) else { - continue; - }; - - match decode_icon(&ico[entry..entry + 16], data) { - Ok(image) => { - let pixels = u64::from(image.width()) * u64::from(image.height()); - if best - .as_ref() - .is_none_or(|(best_pixels, _)| pixels > *best_pixels) - { - best = Some((pixels, image)); - } - } - Err(err) => errors.push(format!("frame {i}: {err}")), - } - } - - best.map(|(_, image)| image).ok_or_else(|| { - if errors.is_empty() { - "PE icon contains no complete frames".to_string() - } else { - format!("PE icon could not be decoded ({})", errors.join("; ")) - } - }) -} - -fn decode_icon(entry: &[u8], data: &[u8]) -> Result { - if data.starts_with(b"\x89PNG\r\n\x1a\n") { - return image::ImageReader::with_format(io::Cursor::new(data), ImageFormat::Png) - .decode() - .map(DynamicImage::into_rgba8) - .map_err(|err| err.to_string()); - } - - let capacity = 22usize - .checked_add(data.len()) - .ok_or_else(|| "frame is too large".to_string())?; - let data_len = u32::try_from(data.len()).map_err(|_| "frame is too large".to_string())?; - let mut ico = Vec::with_capacity(capacity); - ico.extend_from_slice(&[0, 0, 1, 0, 1, 0]); - ico.extend_from_slice(entry); - ico[14..18].copy_from_slice(&data_len.to_le_bytes()); - ico[18..22].copy_from_slice(&22u32.to_le_bytes()); - ico.extend_from_slice(data); - - image::ImageReader::with_format(io::Cursor::new(ico), ImageFormat::Ico) - .decode() - .map(DynamicImage::into_rgba8) - .map_err(|err| err.to_string()) -} - -#[cfg(test)] -mod tests { - use super::{largest_icon, thumbnail_from_args}; - use image::{DynamicImage, ImageFormat, Rgb, RgbImage, Rgba, RgbaImage}; - use std::ffi::OsString; - use std::io::Cursor; - - #[test] - fn malformed_thumbnail_request_returns_an_error() { - let args = [OsString::from("output.png")]; - assert!(thumbnail_from_args(args.into_iter()).is_err()); - } - - #[test] - fn largest_icon_skips_invalid_entries() { - let image = RgbaImage::from_pixel(2, 2, Rgba([1, 2, 3, 255])); - let mut png = Cursor::new(Vec::new()); - DynamicImage::ImageRgba8(image) - .write_to(&mut png, ImageFormat::Png) - .unwrap(); - let png = png.into_inner(); - - let mut ico = vec![0, 0, 1, 0, 2, 0]; - let mut invalid_entry = [0; 16]; - invalid_entry[8..12].copy_from_slice(&4u32.to_le_bytes()); - invalid_entry[12..16].copy_from_slice(&u32::MAX.to_le_bytes()); - ico.extend_from_slice(&invalid_entry); - - let mut valid_entry = [0; 16]; - valid_entry[..8].copy_from_slice(&[2, 2, 0, 0, 1, 0, 32, 0]); - valid_entry[8..12].copy_from_slice(&u32::try_from(png.len()).unwrap().to_le_bytes()); - valid_entry[12..16].copy_from_slice(&38u32.to_le_bytes()); - ico.extend_from_slice(&valid_entry); - ico.extend_from_slice(&png); - - let decoded = largest_icon(&ico).expect("valid icon after malformed entry"); - assert_eq!(decoded.dimensions(), (2, 2)); - assert_eq!(decoded.get_pixel(0, 0), &Rgba([1, 2, 3, 255])); - } - - #[test] - fn dib_and_mask_is_applied() { - let mut dib = vec![0; 40]; - dib[0..4].copy_from_slice(&40u32.to_le_bytes()); - dib[4..8].copy_from_slice(&2i32.to_le_bytes()); - dib[8..12].copy_from_slice(&4i32.to_le_bytes()); - dib[12..14].copy_from_slice(&1u16.to_le_bytes()); - dib[14..16].copy_from_slice(&24u16.to_le_bytes()); - - // Two bottom-up BGR rows, padded to four-byte boundaries. - dib.extend_from_slice(&[0, 0, 255, 0, 0, 255, 0, 0]); - dib.extend_from_slice(&[0, 0, 255, 0, 0, 255, 0, 0]); - // The first mask row is the bottom image row; its first pixel is transparent. - dib.extend_from_slice(&[0b1000_0000, 0, 0, 0]); - dib.extend_from_slice(&[0, 0, 0, 0]); - - let mut ico = vec![0, 0, 1, 0, 1, 0]; - let mut entry = [0; 16]; - entry[..8].copy_from_slice(&[2, 2, 0, 0, 1, 0, 24, 0]); - entry[8..12].copy_from_slice(&u32::try_from(dib.len()).unwrap().to_le_bytes()); - entry[12..16].copy_from_slice(&22u32.to_le_bytes()); - ico.extend_from_slice(&entry); - ico.extend_from_slice(&dib); - - let decoded = largest_icon(&ico).expect("valid DIB icon"); - assert_eq!(decoded.get_pixel(0, 0), &Rgba([255, 0, 0, 255])); - assert_eq!(decoded.get_pixel(0, 1), &Rgba([255, 0, 0, 0])); - } - - #[test] - fn rgb_png_icon_is_decoded() { - let image = RgbImage::from_pixel(2, 2, Rgb([1, 2, 3])); - let mut png = Cursor::new(Vec::new()); - DynamicImage::ImageRgb8(image) - .write_to(&mut png, ImageFormat::Png) - .unwrap(); - let png = png.into_inner(); - - let mut ico = vec![0, 0, 1, 0, 1, 0]; - let mut entry = [0; 16]; - entry[..8].copy_from_slice(&[2, 2, 0, 0, 1, 0, 24, 0]); - entry[8..12].copy_from_slice(&u32::try_from(png.len()).unwrap().to_le_bytes()); - entry[12..16].copy_from_slice(&22u32.to_le_bytes()); - ico.extend_from_slice(&entry); - ico.extend_from_slice(&png); - - let decoded = largest_icon(&ico).expect("valid RGB PNG icon"); - assert_eq!(decoded.get_pixel(0, 0), &Rgba([1, 2, 3, 255])); - } -} diff --git a/debian/changelog b/debian/changelog index 48a34d7..c4358c8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,99 +1,3 @@ -cosmic-files (1.9.0) noble; urgency=medium - - * Epoch 1.9.0 version update - - -- Michael Murphy Mon, 14 Sep 2026 20:35:19 +0200 - -cosmic-files (1.8.0) noble; urgency=medium - - * Epoch 1.8.0 version update - - -- Jeremy Soller Wed, 02 Sep 2026 08:14:53 -0600 - -cosmic-files (1.7.0) noble; urgency=medium - - * Epoch 1.7.0 version update - - -- Jeremy Soller Tue, 25 Aug 2026 09:20:22 -0600 - -cosmic-files (1.6.0) noble; urgency=medium - - * Epoch 1.6.0 version update - - -- Jeremy Soller Tue, 18 Aug 2026 07:13:48 -0600 - -cosmic-files (1.5.0) noble; urgency=medium - - * Epoch 1.5.0 version update - - -- Jeremy Soller Tue, 28 Jul 2026 09:14:09 -0600 - -cosmic-files (1.4.0) noble; urgency=medium - - * Epoch 1.4.0 version update - - -- Jeremy Soller Mon, 20 Jul 2026 14:36:06 -0600 - -cosmic-files (1.3.0) noble; urgency=medium - - * Epoch 1.3.0 version update - - -- Jeremy Soller Sun, 12 Jul 2026 13:21:18 -0600 - -cosmic-files (1.2.0) noble; urgency=medium - - * Epoch 1.2.0 version update - - -- Jeremy Soller Tue, 30 Jun 2026 12:21:07 -0600 - -cosmic-files (1.1.0) noble; urgency=medium - - * Epoch 1.1.0 version update - - -- Jeremy Soller Tue, 23 Jun 2026 08:47:18 -0600 - -cosmic-files (1.0.16) noble; urgency=medium - - * Epoch 1.0.16 version update - - -- Jeremy Soller Tue, 09 Jun 2026 10:26:05 -0600 - -cosmic-files (1.0.15) noble; urgency=medium - - * Epoch 1.0.15 version update - - -- Jeremy Soller Tue, 02 Jun 2026 11:02:29 -0600 - -cosmic-files (1.0.14) noble; urgency=medium - - * Epoch 1.0.14 version update - - -- Jeremy Soller Tue, 26 May 2026 08:30:59 -0600 - -cosmic-files (1.0.13) noble; urgency=medium - - * Epoch 1.0.13 version update - - -- Jeremy Soller Tue, 12 May 2026 09:39:14 -0600 - -cosmic-files (1.0.12) noble; urgency=medium - - * Epoch 1.0.12 version update - - -- Jeremy Soller Tue, 05 May 2026 10:23:57 -0600 - -cosmic-files (1.0.11) noble; urgency=medium - - * Epoch 1.0.11 version update - - -- Jeremy Soller Tue, 14 Apr 2026 11:09:44 -0600 - -cosmic-files (1.0.9) noble; urgency=medium - - * Epoch 1.0.9 version update - - -- Jeremy Soller Mon, 06 Apr 2026 15:10:13 -0600 - cosmic-files (1.0.8) noble; urgency=medium * Epoch 1.0.8 version update diff --git a/debian/source/options b/debian/source/options index 2f8c4a1..5d4ff73 100644 --- a/debian/source/options +++ b/debian/source/options @@ -1,6 +1,4 @@ tar-ignore=.github -tar-ignore=target/.rustc_info.json -tar-ignore=target/CACHEDIR.TAG -tar-ignore=target/debug -tar-ignore=target/release +tar-ignore=.vscode tar-ignore=vendor +tar-ignore=target \ No newline at end of file diff --git a/docs/local-performance-and-portal-notes.md b/docs/local-performance-and-portal-notes.md deleted file mode 100644 index a3dabbe..0000000 --- a/docs/local-performance-and-portal-notes.md +++ /dev/null @@ -1,78 +0,0 @@ -# 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`. diff --git a/examples/copy.rs b/examples/copy.rs index 7f37abd..6396209 100644 --- a/examples/copy.rs +++ b/examples/copy.rs @@ -1,8 +1,6 @@ -use cosmic_files::operation::recursive::{Context, Method}; -use cosmic_files::operation::{Controller, ReplaceResult}; -use std::error::Error; -use std::io; -use std::path::PathBuf; +use cosmic_files::operation::recursive::Method; +use cosmic_files::operation::{Controller, ReplaceResult, recursive::Context}; +use std::{error::Error, io, path::PathBuf}; #[compio::main] async fn main() -> Result<(), Box> { diff --git a/examples/dialog.rs b/examples/dialog.rs index df77069..687d070 100644 --- a/examples/dialog.rs +++ b/examples/dialog.rs @@ -1,30 +1,17 @@ -use cosmic::app::{self, Core, Settings, Task}; -use cosmic::iced::{Subscription, window}; -use cosmic::{Application, Element, executor, widget}; +use cosmic::{ + Application, Element, + app::{self, Core, Settings, Task}, + executor, + iced::{Subscription, window}, + widget, +}; use cosmic_files::dialog::{ Dialog, DialogChoice, DialogChoiceOption, DialogFilter, DialogFilterPattern, DialogKind, DialogMessage, DialogResult, DialogSettings, }; -use tracing_subscriber::layer::SubscriberExt; -use tracing_subscriber::util::SubscriberInitExt; fn main() -> Result<(), Box> { - let log_format = tracing_subscriber::fmt::format() - .pretty() - .without_time() - .with_line_number(true) - .with_file(true) - .with_target(false) - .with_thread_names(true); - - let log_layer = tracing_subscriber::fmt::Layer::default() - .with_writer(std::io::stderr) - .event_format(log_format); - - tracing_subscriber::registry() - .with(tracing_subscriber::EnvFilter::from_env("RUST_LOG")) - .with(log_layer) - .init(); + env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init(); let settings = Settings::default(); app::run::(settings, ())?; @@ -161,7 +148,7 @@ impl Application for App { } fn view(&self) -> Element<'_, Message> { - let mut column = widget::column::with_capacity(8).spacing(8).padding(8); + let mut column = widget::column().spacing(8).padding(8); { let mut button = widget::button::standard("Open File"); if self.dialog_opt.is_none() { diff --git a/i18n/ar/cosmic_files.ftl b/i18n/ar/cosmic_files.ftl index 479555b..f9b641d 100644 --- a/i18n/ar/cosmic_files.ftl +++ b/i18n/ar/cosmic_files.ftl @@ -22,10 +22,10 @@ empty-trash-warning = سيتم حذف العناصر الموجودة في مج ## New File/Folder Dialog -create-new-file = أنشئ ملف جديد -create-new-folder = أنشئ مجلَّد جديد +create-new-file = إنشاء ملف جديد +create-new-folder = إنشاء مجلد جديد file-name = اسم الملف -folder-name = اسم المجلَّد +folder-name = اسم المجلد file-already-exists = يوجد ملف بهذا الاسم بالفعل folder-already-exists = يوجد مجلد بهذا الاسم بالفعل name-hidden = الاسماء التي تبدأ بنقطة «.» ستكون مخفية @@ -34,10 +34,10 @@ name-no-slashes = لا يمكن أن يحتوي الاسم على شرطات م ## Open/Save Dialog -cancel = ألغِ -open = افتح -open-file = افتح ملف -open-folder = افتح مجلَّد +cancel = إلغاء +open = فتح +open-file = فتح ملف +open-folder = افتح مجلد open-in-new-tab = افتح في لسان جديد open-in-new-window = افتح في نافذة جديدة open-multiple-files = افتح عدة ملفات @@ -84,7 +84,7 @@ settings = الإعدادات ### Appearance appearance = المظهر -theme = السمة +theme = النسق match-desktop = طابق سطح المكتب dark = داكن light = فاتح @@ -110,7 +110,7 @@ new-tab = لسان جديد new-window = نافذة جديدة rename = غيِّر الاسم... close-tab = أغلق اللسان -quit = إنهِ +quit = أنهِ ## Edit @@ -147,15 +147,15 @@ icon-size-and-spacing = حجم الأيقونة والتباعد icon-size = حجم الأيقونة grid-spacing = تباعد شبكي trashed-on = مهمل -details = تفاصيل +details = التفاصيل pause = ألبث resume = استئناف -create-archive = أنشئ أرشيف +create-archive = إنشاء أرشيف extract-password-required = كلمة السر مطلوبة extract-to = استخرِج إلى... extract-to-title = استخرِج إلى مجلّد mount-error = تعذر الوصول إلى القرص -create = أنشئ +create = إنشاء open-item-location = افتح مكان العنصر set-executable-and-launch-description = أتريد تعيين «{ $name }» كقابل للتنفيذ وتشغيله؟ favorite-path-error-description = @@ -259,7 +259,7 @@ related-apps = تطبيقات ذات صلة selected-items = العناصر { $items } المحدّدة permanently-delete-question = احذف نهائيًا؟ delete = احذف -permanently-delete-warning = سيُحذف { $target } نهائيًا. لا يمكن التراجع عن هذا الإجراء. +permanently-delete-warning = هل أنت متأكد من أنك تريد حذف { $target } نهائيًا؟ لا يمكن التراجع عن هذا الإجراء. replace-warning-operation = أتريد استبداله؟ استبداله سيكتب فوق محتواه. original-file = الملف الأصلي replace-with = استبدل بـ @@ -371,19 +371,3 @@ move-to = انقل إلى... show-recents = مجلد الحديثة في الشريط الجانبي clear-recents-history = امحُ التأريخ الحديث copy-path = انسخ المسار -mixed = مختلط -context-action = إجراء السياق -context-action-confirm-title = شغِّل "{ $name }"؟ -context-action-confirm-warning = - سيُشغِّل هذا على { $items } { $items -> - [one] عنصر - [two] عنصرين - [few] عناصر - [many] عنصرًا - *[other] عنصر - }. -run = شغِّل -checksum = تدقيق المجموع { $kind } -calculate = احسب -error = خطأ -rename-confirm = أعد التسمية diff --git a/i18n/ca/cosmic_files.ftl b/i18n/ca/cosmic_files.ftl index 71b5aa8..010ddb0 100644 --- a/i18n/ca/cosmic_files.ftl +++ b/i18n/ca/cosmic_files.ftl @@ -1,11 +1,11 @@ cosmic-files = Fitxers del COSMIC empty-folder = Carpeta buida -empty-folder-hidden = Carpeta buida (conté elements ocults) +empty-folder-hidden = Carpeta buida (té elements ocults) no-results = No s'ha trobat cap resultat filesystem = Sistema de fitxers home = Inici networks = Xarxes -notification-in-progress = Operacions de fitxers en curs +notification-in-progress = Hi ha operacions amb fitxers en curs. trash = Paperera recents = Recents undo = Desfés @@ -29,14 +29,14 @@ details = Detalls dismiss = Descarta el missatge operations-running = { $running } { $running -> - [one] operació + [one] operacion *[other] operacions } en curs ({ $percent }%)... operations-running-finished = { $running }{ $running -> - [one] operació + [one] operacion *[other] operacions - } en curs ({ $percent }%), { $finished } acabada/es... + } en curs ({ $percent }%), { $finished } acabat... pause = Pausa resume = Reprèn @@ -54,7 +54,7 @@ extract-password-required = Cal una contrasenya ## Empty Trash Dialog empty-trash = Buida la paperera -empty-trash-warning = Els elements dins la Paperera s'eliminaran permanentment +empty-trash-warning = Voleu suprimir permanentment tots els fitxers de la paperera? ## Mount Error Dialog @@ -64,20 +64,20 @@ mount-error = No es pot accedir a la unitat create-new-file = Crea un nou fitxer create-new-folder = Crea una nova carpeta -file-name = Nom del fitxer +file-name = Nom del fixer folder-name = Nom de la carpeta -file-already-exists = Ja existeix un fitxer amb aquest nom -folder-already-exists = Ja existeix una carpeta amb aquest nom -name-hidden = Els noms que comencin amb "." s'ocultaran. -name-invalid = El nom no pot ser "{ $filename }" +file-already-exists = Ja existeix un fitxer amb aquest nom. +folder-already-exists = Ja existeix una carpeta amb aquest nom. +name-hidden = Els noms que comencin amb "." seran ocults. +name-invalid = El nom no pot ser "{ $filename }". name-no-slashes = El nom no pot contenir barres. ## Open/Save Dialog -cancel = Cancel·lar +cancel = Cancel·la create = Crea open = Obre -open-file = Obre el fitxer +open-file = Obre el fixer open-folder = Obre la carpeta open-in-new-tab = Obre en una pestanya nova open-in-new-window = Obre en una finestra nova @@ -352,14 +352,3 @@ sort-newest-first = Primer més recents sort-oldest-first = Primer més antics sort-smallest-to-largest = De petit a gran sort-largest-to-smallest = De gran a petit -run = Executa -extract-to-title = Extreu a una carpeta -empty-trash-title = Voleu buidar la paperera? -extract-to = Extreu a... -delete = Suprimeix -comment = Gestor de fitxers de l'escriptori COSMIC -copy-to-title = Escolliu el destí de la còpia -copy-to-button-label = Copia -move-to-title = Escolliu el destí del trasllat -move-to-button-label = Mou -keywords = Carpeta;Fitxers;Arxius;Gestor;Explorador; diff --git a/i18n/cs/cosmic_files.ftl b/i18n/cs/cosmic_files.ftl index d544dc8..fde9f4f 100644 --- a/i18n/cs/cosmic_files.ftl +++ b/i18n/cs/cosmic_files.ftl @@ -17,7 +17,7 @@ size = Velikost ## Empty Trash Dialog -empty-trash = Vyprázdnit koš +empty-trash = Vysypat koš empty-trash-warning = Položky v koši budou trvale smazány ## New File/Folder Dialog @@ -176,7 +176,7 @@ set-executable-and-launch-description = Chcete povolit spouštění souboru „{ set-and-launch = Povolit a spustit open-with = Otevřít pomocí other = Ostatní -none = Žádné +none = Žádný icon-size-and-spacing = Velikost a rozestupy ikon grid-spacing = Rozestupy mřížky deleting = @@ -195,8 +195,8 @@ deleted = [few] položky *[other] položek } z koše -emptying-trash = Vyprazdňování koše ({ $progress })... -emptied-trash = Koš vyprázdněn +emptying-trash = Vysypávání koše ({ $progress })... +emptied-trash = Koš vysypán restoring = Obnovování { $items } { $items -> [one] položky @@ -403,7 +403,7 @@ sort-largest-to-smallest = Od největšího po nejmenší gallery-preview = Náhled galerie sort = Řazení sort-a-z = A-Z -empty-trash-title = Vyprázdnit koš? +empty-trash-title = Vysypat koš? type-to-search-select = Vybere první shodující se soubor nebo složku pasted-image = Vložený obrázek pasted-text = Vložený text @@ -417,20 +417,3 @@ move-to-title = Vyberte cíl přesunutí show-recents = Složka „Nedávné“ v postranním panelu copy-path = Kopírovat cestu clear-recents-history = Vymazat historii „Nedávné“ -mixed = Různé -context-action = Kontextová akce -context-action-confirm-title = Spustit „{ $name }“? -context-action-confirm-warning = - Tato akce se spustí pro { $items } { $items -> - [one] položku - [few] položky - *[other] položek - }. -run = Spustit -rename-confirm = Přejmenovat -checksum = Kontrolní součet { $kind } -calculate = Vypočítat -error = Chyba -search-application = Hledat podle názvu aplikace -sidebar-label = Popisek v postranním panelu -change-sidebar-label = Změnit popisek diff --git a/i18n/da/cosmic_files.ftl b/i18n/da/cosmic_files.ftl index 5666fdb..82e7f27 100644 --- a/i18n/da/cosmic_files.ftl +++ b/i18n/da/cosmic_files.ftl @@ -1,43 +1,35 @@ -cosmic-files = COSMIC Filhåndtering +cosmic-files = COSMIC Filer empty-folder = Tom mappe -empty-folder-hidden = Tom mappe (har skjulte emner) -no-results = Fandt ingen resultater +empty-folder-hidden = Tom mappe (har skjulte filer) +no-results = Ingen resultater filesystem = Filsystem home = Hjem networks = Netværk -notification-in-progress = Fil-operationer er igang -trash = Papirkurv +notification-in-progress = Filoperationer er igangværende. +trash = Skraldespand recents = Seneste undo = Fortryd today = I dag # Desktop view options -desktop-view-options = Valgmuligheder for skrivebordsvisning... -show-on-desktop = Vis på Skrivebord -desktop-folder-content = Skrivebords-mappeindhold +desktop-view-options = Indstillinger for skrivebordsudseende... +show-on-desktop = Vis på Skrivebordet +desktop-folder-content = Indhold på Skrivebordet mounted-drives = Monterede drev -trash-folder-icon = Papirkurv-ikon -icon-size-and-spacing = Ikonstørrelse og -afstand +trash-folder-icon = Skraldespandsikon +icon-size-and-spacing = Ikonstørrelse og afstand icon-size = Ikonstørrelse # List view name = Navn modified = Ændret -trashed-on = Kasseret +trashed-on = Smidt ud size = Størrelse # Progress footer details = Detaljer -dismiss = Luk meddelelse -operations-running = - { $running } { $running -> - [one] operation - *[other] operationer - } kører ({ $percent }%)... -operations-running-finished = - { $running } { $running -> - [one] operation - *[other] operationer - } kører ({ $percent }%), { $finished } færdig... +dismiss = Afvis besked +operations-running = { $running } operationer er i gang ({ $percent }%)... +operations-running-finished = { $running } operationer er i gang ({ $percent }%), { $finished } færdiggjort... pause = Pause -resume = Genoptag +resume = Fortsæt # Dialogs @@ -48,12 +40,12 @@ create-archive = Opret arkiv ## Empty Trash Dialog -empty-trash = Tøm papirkurv -empty-trash-warning = Emner i Papirkurv-mappen vil blive slettet permanent +empty-trash = Tøm skraldespand +empty-trash-warning = Er du sikker på du vil slette alle objekter i Skraldespanden permanent? ## Mount Error Dialog -mount-error = Ude af stand til at tilgå drev +mount-error = Kan ikke tilgå drev ## New File/Folder Dialog @@ -61,22 +53,22 @@ create-new-file = Opret ny fil create-new-folder = Opret ny mappe file-name = Filnavn folder-name = Mappenavn -file-already-exists = En fil med det navn eksisterer allerede -folder-already-exists = En mappe med det navn eksisterer allerede -name-hidden = Navne begyndende med "." vil være skjulte -name-invalid = Navn må ikke være "{ $filename }" -name-no-slashes = Navn må ikke indeholde skråstreger +file-already-exists = En fil med det navn eksisterer allerede. +folder-already-exists = En mappe med det navn eksisterer allerede. +name-hidden = Navne begyndende med "." vil blive skjult. +name-invalid = Navn kan ikke være "{ $filename }". +name-no-slashes = Navn kan ikke indeholde skråstreg. ## Open/Save Dialog -cancel = Afbryd +cancel = Annullér create = Opret open = Åbn open-file = Åbn fil open-folder = Åbn mappe -open-in-new-tab = Åbn i nyt faneblad +open-in-new-tab = Åbn i ny fane open-in-new-window = Åbn i nyt vindue -open-item-location = Åbn emne-placering +open-item-location = Åbn placering for objekt open-multiple-files = Åbn flere filer open-multiple-folders = Åbn flere mapper save = Gem @@ -95,20 +87,20 @@ rename-folder = Omdøb mappe ## Replace Dialog replace = Erstat -replace-title = "{ $filename }" eksisterer allerede i denne placering -replace-warning = Ønsker du at erstatte den med den, du er ved at gemme? Erstatning af den vil overskrive dens indhold. -replace-warning-operation = Ønsker du at erstatte den? Erstatning af den vil overskrive dens indhold. +replace-title = "{ $filename }" eksisterer allerede på denne placering. +replace-warning = Vil du erstatte den med den du er ved at gemme? Hvis du erstatter den, overskriver du dens indhold. +replace-warning-operation = Vil du erstatte den? Hvis du erstatter den, overskriver du dens indhold. original-file = Original fil replace-with = Erstat med -apply-to-all = Anvend på alle +apply-to-all = Anvend for alle keep-both = Behold begge skip = Spring over ## Set as Executable and Launch Dialog -set-executable-and-launch = Angiv som eksekverbar og start -set-executable-and-launch-description = Vil du angive "{ $name }" som eksekverbar og starte den? -set-and-launch = Angiv og start +set-executable-and-launch = Indstil som eksekverbar fil og start +set-executable-and-launch-description = Vil du indstille "{ $name }" som en eksekverbar fil og starte den? +set-and-launch = Indstil og start ## Metadata Dialog @@ -247,13 +239,13 @@ settings = Indstillinger appearance = Udseende theme = Tema -match-desktop = Følg skrivebordets indstillinger -dark = Mørkt -light = Lyst +match-desktop = Match skrivebord +dark = Mørk +light = Lys # Context menu add-to-sidebar = Tilføj til sidebjælke -compress = Komprimer… -extract-here = Uddrag +compress = Komprimér +extract-here = Extract new-file = Ny fil... new-folder = Ny mappe... open-in-terminal = Åbn i terminal @@ -288,15 +280,15 @@ quit = Afslut edit = Redigér cut = Klip copy = Kopiér -paste = Indsæt -select-all = Vælg alle +paste = Sæt ind +select-all = Vælg alt ## View zoom-in = Zoom ind default-size = Standardstørrelse zoom-out = Zoom ud -view = Vis +view = Visning grid-view = Gitter-visning list-view = Liste-visning show-hidden-files = Vis skjulte filer @@ -314,102 +306,3 @@ sort-newest-first = Nyeste først sort-oldest-first = Ældste først sort-smallest-to-largest = Mindste til største sort-largest-to-smallest = Største til mindste -delete = Slet -run = Kør -repository = Pakkearkiv -grid-spacing = Gitter-afstand -extract-password-required = Adgangskode påkrævet -extract-to = Udpak Til... -extract-to-title = Udpak til mappe -empty-trash-title = Tøm papirkurv? -keywords = Mappe;Håndtering; -comment = Filhåndteringsprogram til COSMIC-skrivebordet -copy-to-title = Vælg kopi destination -copy-to-button-label = Kopier -move-to-title = Vælg flytte destination -move-to-button-label = Flyt -deleted = - Slettede { $items } { $items -> - [one] element - *[other] elementer - } fra { trash } -removing-from-recents = - Fjerner { $items } { $items -> - [one] element - *[other] elementer - } fra { recents } -type-to-search-enter-path = Indtast stien til mappen eller filen -permanently-deleting = - Sletter permanent { $items } { $items -> - [one] element - *[other] elementer - } -execute-only = Kun kørsel -removed-from-recents = - Fjernede { $items } { $items -> - [one] element - *[other] elementer - } fra { recents } -permanently-deleted = - Slettede permanent { $items } { $items -> - [one] element - *[other] elementer - } -write-only = Kun skrivning -remove = Fjern -context-action = Konteksthandling -context-action-confirm-title = Kør “{ $name }”? -context-action-confirm-warning = - Dette vil køre på { $items } { $items -> - [one] item - *[other] items - }. -selected-items = De { $items } valgte elementer -mixed = Blandet -none = Ingen -write-execute = Skriv og kør -read-only = Kun læsning -read-execute = Læs og kør -read-write = Læs og skriv -read-write-execute = Læs, skriv og kør -keep = Behold -support = Support -progress-failed = { $percent } %, mislykkedes -pasted-image = Indsat billede -pasted-text = Indsat tekst -pasted-video = Indsat video -deleting = - Sletter { $items } { $items -> - [one] element - *[other] elementer - } fra { trash } ({ $progress })... -setting-permissions = Indstiller tilladelser for “{ $name }” til { $mode } -set-permissions = Sæt tilladelser for “{ $name }” til { $mode } -show-recents = Seneste-mappe i sidepanelet -type-to-search = Skriv for at søge -type-to-search-recursive = Søger i den aktuelle mappe og alle undermapper -type-to-search-select = Vælger den første matchende fil eller mappe -clear-recents-history = Ryd seneste historik -copy-to = Kopiér til… -delete-permanently = Slet permanent -eject = Skub ud -move-to = Flyt til… -remove-from-recents = Fjern fra seneste -reload-folder = Genindlæs mappe -copy-path = Kopiér sti -rename-confirm = Omdøb -calculate = Udregn -error = Fejl -checksum = { $kind }-kontrolsum -other-apps = Andre applikationer -related-apps = Relaterede applikationer -permanently-delete-question = Slet permanent? -permanently-delete-warning = { $target } vil blive slettet permanent. Denne handling kan ikke fortrydes. -open-with = Åbn med -favorite-path-error = Fejl ved åbning af sti -favorite-path-error-description = - Ude af stand til at åbne "{ $path }" - "{ $path }" Eksisterer muligvis ikke, eller du har måske ikketilladelse til at åbne den - - Ønsker du at fjerne den fra sidebjælken? -single-click = Enkelt klik for at åbne diff --git a/i18n/de/cosmic_files.ftl b/i18n/de/cosmic_files.ftl index 87ba8b8..c300df6 100644 --- a/i18n/de/cosmic_files.ftl +++ b/i18n/de/cosmic_files.ftl @@ -11,7 +11,7 @@ recents = Zuletzt verwendet undo = Rückgängig today = Heute # Optionen für die Desktop-Ansicht -desktop-view-options = Optionen für die Desktop-Ansicht ... +desktop-view-options = Optionen für die Desktop-Ansicht… show-on-desktop = Auf Desktop anzeigen desktop-folder-content = Inhalt des Desktop-Ordners mounted-drives = Eingehängte Laufwerke @@ -31,12 +31,12 @@ operations-running = { $running } { $running -> [one] laufender Vorgang *[other] laufende Vorgänge - } ({ $percent } %) ... + } ({ $percent } %)... operations-running-finished = { $running } { $running -> [one] laufender Vorgang *[other] laufende Vorgänge - } ({ $percent } %), { $finished } abgeschlossen ... + } ({ $percent } %), { $finished } abgeschlossen... pause = Pause resume = Fortsetzen @@ -50,7 +50,7 @@ create-archive = Archiv erstellen ## Entpacken-Dialog extract-password-required = Passwort erforderlich -extract-to = Entpacken nach ... +extract-to = Entpacken nach... extract-to-title = In Ordner entpacken ## Dialog zum Leeren des Papierkorbs @@ -179,7 +179,7 @@ keep = Behalten add-network-drive = Netzlaufwerk hinzufügen connect = Verbinden connect-anonymously = Anonym verbinden -connecting = Wird verbunden ... +connecting = Wird verbunden... domain = Domain enter-server-address = Serveradresse eingeben network-drive-description = @@ -218,7 +218,7 @@ compressing = { $items } { $items -> [one] Element wird *[other] Elemente werden - } von „{ $from }“ nach „{ $to }“ komprimiert ({ $progress }) ... + } von „{ $from }“ nach „{ $to }“ komprimiert ({ $progress })... compressed = { $items } { $items -> [one] Element wurde @@ -231,7 +231,7 @@ copying = { $items } { $items -> [one] Element wird *[other] Elemente werden - } von „{ $from }“ nach „{ $to }“ kopiert ({ $progress }) ... + } von „{ $from }“ nach „{ $to }“ kopiert ({ $progress })... copied = { $items } { $items -> [one] Element wurde @@ -241,19 +241,19 @@ deleting = { $items } { $items -> [one] Element wird *[other] Elemente werden - } aus dem { trash } gelöscht ({ $progress }) ... + } aus dem { trash } gelöscht ({ $progress })... deleted = { $items } { $items -> [one] Element wurde *[other] Elemente wurden } aus dem { trash } gelöscht -emptying-trash = { trash } wird geleert ({ $progress }) ... +emptying-trash = { trash } wird geleert ({ $progress })... emptied-trash = { trash } geleert extracting = { $items } { $items -> [one] Element wird *[other] Elemente werden - } von „{ $from }“ nach „{ $to }“ entpackt ({ $progress }) ... + } von „{ $from }“ nach „{ $to }“ entpackt ({ $progress })... extracted = { $items } { $items -> [one] Element wurde @@ -267,7 +267,7 @@ moving = { $items } { $items -> [one] Element wird *[other] Elemente werden - } von „{ $from }“ nach „{ $to }“ verschoben ({ $progress }) ... + } von „{ $from }“ nach „{ $to }“ verschoben ({ $progress })... moved = { $items } { $items -> [one] Element wurde @@ -289,7 +289,7 @@ restoring = { $items } { $items -> [one] Element wird *[other] Elemente werden - } aus dem { trash } wiederhergestellt ({ $progress }) ... + } aus dem { trash } wiederhergestellt ({ $progress })... restored = { $items } { $items -> [one] Element wurde @@ -299,7 +299,7 @@ unknown-folder = unbekannter Ordner ## Öffnen mit -menu-open-with = Öffnen mit ... +menu-open-with = Öffnen mit... default-app = { $name } (Standard) ## Details anzeigen @@ -311,7 +311,7 @@ item-size = Größe: { $size } item-created = Erstellt: { $created } item-modified = Geändert: { $modified } item-accessed = Zugegriffen: { $accessed } -calculating = Wird berechnet ... +calculating = Wird berechnet... ## Einstellungen @@ -330,18 +330,18 @@ light = Hell type-to-search = Zum Suchen tippen type-to-search-recursive = Durchsucht den aktuellen Ordner und alle Unterordner -type-to-search-enter-path = Gib den Pfad zum Verzeichnis oder zur Datei ein +type-to-search-enter-path = Gibt den Pfad zu einem Verzeichnis oder einer Datei ein # Kontextmenü add-to-sidebar = Zur Seitenleiste hinzufügen -compress = Komprimieren ... +compress = Komprimieren... delete-permanently = Endgültig löschen extract-here = Entpacken -new-file = Neue Datei ... -new-folder = Neuer Ordner ... +new-file = Neue Datei... +new-folder = Neuer Ordner... open-in-terminal = Im Terminal öffnen move-to-trash = In den Papierkorb verschieben restore-from-trash = Aus dem Papierkorb wiederherstellen -remove-from-sidebar = Aus der Seitenleiste entfernen +remove-from-sidebar = Von der Seitenleiste entfernen sort-by-name = Nach Name sortieren sort-by-modified = Nach Änderung sortieren sort-by-size = Nach Größe sortieren @@ -349,9 +349,9 @@ sort-by-trashed = Nach Löschzeitpunkt sortieren ## Desktop -change-wallpaper = Hintergrundbild ändern ... -desktop-appearance = Desktop-Aussehen ... -display-settings = Anzeigeeinstellungen ... +change-wallpaper = Hintergrundbild ändern... +desktop-appearance = Desktop-Aussehen... +display-settings = Anzeigeeinstellungen... # Menü @@ -362,7 +362,7 @@ file = Datei new-tab = Neuer Tab new-window = Neues Fenster reload-folder = Ordner neu laden -rename = Umbenennen ... +rename = Umbenennen... close-tab = Tab schließen quit = Beenden @@ -376,28 +376,28 @@ select-all = Alles auswählen ## Ansicht -zoom-in = Hineinzoomen +zoom-in = Vergrößern default-size = Standardgröße -zoom-out = Herauszoomen +zoom-out = Verkleinern view = Ansicht grid-view = Rasteransicht list-view = Listenansicht show-hidden-files = Versteckte Dateien anzeigen list-directories-first = Verzeichnisse zuerst auflisten gallery-preview = Galerie-Vorschau -menu-settings = Einstellungen ... -menu-about = Über COSMIC Dateien ... +menu-settings = Einstellungen... +menu-about = Über COSMIC Dateien... ## Sortieren -sort = Sortierung +sort = Sortieren sort-a-z = A-Z sort-z-a = Z-A sort-newest-first = Neueste zuerst sort-oldest-first = Älteste zuerst sort-smallest-to-largest = Von klein nach groß sort-largest-to-smallest = Von groß nach klein -repository = Repositorium +repository = Repository empty-trash-title = Papierkorb leeren? copy-to-title = Kopierziel auswählen copy-to-button-label = Kopieren @@ -418,28 +418,9 @@ type-to-search-select = Wählt die erste übereinstimmende Datei oder den ersten pasted-image = Eingefügtes Bild pasted-text = Eingefügter Text pasted-video = Eingefügtes Video -copy-to = Kopieren nach ... +copy-to = Kopieren nach... show-recents = Ordner zuletzt verwendeter Elemente in der Seitenleiste clear-recents-history = Verlauf zuletzt verwendeter Elemente leeren comment = Dateimanager für den COSMIC Desktop keywords = Ordner;Manager; move-to-button-label = Verschieben -move-to-title = Verschiebeziel auswählen -remove-from-recents = Aus den zuletzt verwendeten Elementen entfernen -move-to = Verschieben nach ... -copy-path = Pfad kopieren -mixed = Gemischt -context-action = Kontextaktion -context-action-confirm-title = „{ $name }“ ausführen? -context-action-confirm-warning = - Dies wird ausgeführt auf { $items } { $items -> - [one] Element - *[other] Elementen - }. -run = Ausführen -rename-confirm = Umbenennen -checksum = { $kind } Prüfsumme -calculate = Berechnen -error = Fehler -sidebar-label = Bezeichnung in der Seitenleiste -search-application = Nach App-Name suchen diff --git a/i18n/el/cosmic_files.ftl b/i18n/el/cosmic_files.ftl index 6443d4b..b28272c 100644 --- a/i18n/el/cosmic_files.ftl +++ b/i18n/el/cosmic_files.ftl @@ -1,336 +1,15 @@ empty-folder = Άδειος φάκελος no-results = Δεν βρέθηκαν αποτελέσματα -trash = Απορρίμματα +trash = Κάδος Ανακύκλωσης recents = Πρόσφατα -cosmic-files = Αρχεία COSMIC -empty-folder-hidden = Άδειος φάκελος (περιέχει κρυφά στοιχεία) +cosmic-files = COSMIC Αρχεία +empty-folder-hidden = Άδειος φάκελος (περιέχει κρυφά αντικείμενα) filesystem = Σύστημα αρχείων home = Προσωπικός φάκελος -networks = Δίκτυα -comment = Διαχείριση αρχείων για το περιβάλλον επιφάνειας εργασίας COSMIC -keywords = Αρχείο;Φάκελος;Διαχείριση;Folder;Manager; +networks = Δίκτυο +comment = Αρχεία για το COSMIC περιβάλλον +keywords = Φάκελος;Διαχειριστής; rename = Μετονομασία... close-tab = Κλείσιμο καρτέλας -light = Ανοιχτόχρωμο -dark = Σκουρόχρωμο -connect = Σύνδεση -dismiss = Απόρριψη μηνύματος -copy_noun = Αντιγραφή -open-file = Άνοιγμα αρχείου -save = Αποθήκευση -password = Κωδικός πρόσβασης -remove = Αφαίρεση -create = Δημιουργία -pause = Παύση -quit = Τερματισμός -calculating = Υπολογισμός... -keep = Διατήρηση -edit = Επεξεργασία -connecting = Σύνδεση... -copy = Αντιγραφή -theme = Θέμα -appearance = Εμφάνιση -name = Όνομα -resume = Συνέχιση -username = Όνομα χρήστη -delete = Διαγραφή -repository = Αποθετήριο -support = Υποστήριξη -eject = Εξαγωγή -group = Ομάδα -skip = Παράλειψη -paste = Επικόλληση -menu-settings = Ρυθμίσεις... -view = Προβολή -undo = Αναίρεση -details = Λεπτομέρειες -sort-a-z = Α-Ω -extract-here = Αποσυμπίεση -cancel = Ακύρωση -sort-z-a = Ω-Α -open = Άνοιγμα -history = Ιστορικό -domain = Τομέας -sort = Ταξινόμηση -settings = Ρυθμίσεις -pending = Σε εκκρεμότητα -open-folder = Άνοιγμα φακέλου -replace = Αντικατάσταση -cut = Αποκοπή -file = Αρχείο -today = Σήμερα -compress = Συμπίεση... -size = Μέγεθος -related-apps = Σχετικές εφαρμογές -zoom-in = Μεγέθυνση -select-all = Επιλογή όλων -icon-size-and-spacing = Μέγεθος και απόσταση εικονιδίων -new-window = Νέο παράθυρο -zoom-out = Σμίκρυνση -default-size = Προεπιλεγμένο μέγεθος -create-archive = Δημιουργία συμπιεσμένου αρχείου -other-apps = Άλλες εφαρμογές -rename-folder = Μετονομασία φακέλου -folder-name = Όνομα φακέλου -connect-anonymously = Ανώνυμη σύνδεση -replace-with = Αντικατάσταση με -mounted-drives = Προσαρτημένες μονάδες -desktop-view-options = Επιλογές προβολής επιφάνειας εργασίας... -show-on-desktop = Εμφάνιση στην επιφάνεια εργασίας -trash-folder-icon = Εικονίδιο φακέλου απορριμμάτων -open-with = Άνοιγμα με -keep-both = Διατήρηση αμφότερων -icon-size = Μέγεθος εικονιδίων -open-with-title = Πώς θέλετε να ανοίξετε το «{ $name }»; -extract-password-required = Απαιτείται κωδικός πρόσβασης -rename-file = Μετονομασία αρχείου -file-name = Όνομα αρχείου -save-file = Αποθήκευση αρχείου -name-hidden = Τα ονόματα που ξεκινούν με «.» θα αποκρύπτονται -folder-already-exists = Υπάρχει ήδη ένας φάκελος με αυτό το όνομα -empty-trash = Άδειασμα απορριμμάτων -permanently-delete-question = Οριστική διαγραφή; -copy-to-button-label = Αντιγραφή -move-to-button-label = Μετακίνηση -run = Εκτέλεση -copy-to-title = Επιλογή προορισμού αντιγραφής -sort-newest-first = Πρώτα τα νεότερα -default-app = { $name } (προεπιλογή) -renamed = Έγινε μετονομασία από «{ $from }» σε «{ $to }» -read-execute = Ανάγνωση και εκτέλεση -deleted = - Έγινε διαγραφή { $items } { $items -> - [one] στοιχείου - *[other] στοιχείων - } από τα { trash } -item-modified = Ημερομηνία τροποποίησης: { $modified } -list-view = Προβολή λίστας -reload-folder = Επαναφόρτωση φακέλου -favorite-path-error = Σφάλμα ανοίγματος καταλόγου -progress = { $percent }% -remove-from-sidebar = Αφαίρεση από την πλαϊνή στήλη -restoring = - Ανάκτηση { $items } { $items -> - [one] στοιχείου - *[other] στοιχείων - } από τα { trash } ({ $progress })... -network-drive-error = Αδυναμία πρόσβασης σε μονάδα δικτύου -gallery-preview = Προεπισκόπηση συλλογής -sort-smallest-to-largest = Από τα μικρότερα στα μεγαλύτερα -removing-from-recents = - Αφαίρεση { $items } { $items -> - [one] στοιχείου - *[other] στοιχείων - } από τα { recents } -type-to-search-enter-path = Εισέρχεται στη διαδρομή προς τον κατάλογο ή το αρχείο -emptying-trash = Άδειασμα φακέλου «{ trash }» ({ $progress })... -trashed-on = Ημερομηνία διαγραφής -compressing = - Συμπίεση { $items } { $items -> - [one] στοιχείου - *[other] στοιχείων - } από τον φάκελο «{ $from }» στο αρχείο «{ $to }» ({ $progress })... -move-to-trash = Μετακίνηση στα απορρίμματα -menu-about = Σχετικά με τα Αρχεία COSMIC... -setting-executable-and-launching = Ορισμός του «{ $name }» ως εκτελέσιμου και εκκίνηση -open-multiple-files = Άνοιγμα πολλαπλών αρχείων -menu-open-with = Άνοιγμα με... -extracted = - Έγινε αποσυμπίεση { $items } { $items -> - [one] στοιχείου - *[other] στοιχείων - } από το αρχείο «{ $from }» στον φάκελο «{ $to }» -create-new-folder = Δημιουργία νέου φακέλου -original-file = Πρωτότυπο αρχείο -read-write-execute = Ανάγνωση, εγγραφή και εκτέλεση -set-permissions = Έγινε ορισμός των δικαιωμάτων για το «{ $name }» σε: { $mode } -sort-by-size = Ταξινόμηση κατά μέγεθος -item-size = Μέγεθος: { $size } -permanently-deleting = - Οριστική διαγραφή { $items } { $items -> - [one] στοιχείου - *[other] στοιχείων - } -read-write = Ανάγνωση και εγγραφή -none = Κανένα -items = Στοιχεία: { $items } -type = Τύπος: { $mime } -compressed = - Έγινε συμπίεση { $items } { $items -> - [one] στοιχείου - *[other] στοιχείων - } από τον φάκελο «{ $from }» στο αρχείο «{ $to }» -replace-warning = Θέλετε να το αντικαταστήσετε με αυτό που αποθηκεύετε; Αυτό θα οδηγήσει στην αντικατάσταση του περιεχομένου του. -new-file = Νέο αρχείο... -open-in-terminal = Άνοιγμα σε τερματικό -open-multiple-folders = Άνοιγμα πολλαπλών φακέλων -remember-password = Απομνημόνευση κωδικού πρόσβασης -show-details = Εμφάνιση λεπτομερειών -grid-spacing = Απόσταση πλέγματος -extract-to = Αποσυμπίεση σε... -add-network-drive = Προσθήκη μονάδας δικτύου -copying = - Αντιγραφή { $items } { $items -> - [one] στοιχείου - *[other] στοιχείων - } από τον φάκελο «{ $from }» στον φάκελο «{ $to }» ({ $progress })... -sort-oldest-first = Πρώτα τα παλαιότερα -create-new-file = Δημιουργία νέου αρχείου -sort-by-trashed = Ταξινόμηση κατά ημερομηνία διαγραφής -replace-warning-operation = Θέλετε να το αντικαταστήσετε; Αυτό θα οδηγήσει στην αντικατάσταση του περιεχομένου του. -try-again = Δοκιμή ξανά -copied = - Έγινε αντιγραφή { $items } { $items -> - [one] στοιχείου - *[other] στοιχείων - } από τον φάκελο «{ $from }» στον φάκελο «{ $to }» -other = Άλλο -open-in-new-window = Άνοιγμα σε νέο παράθυρο -sort-by-modified = Ταξινόμηση κατά ημερομηνία τροποποίησης -list-directories-first = Παράθεση των καταλόγων πρώτα -read-only = Μόνο ανάγνωση -browse-store = Περιήγηση στο { $store } -enter-server-address = Εισαγάγετε τη διεύθυνση διακομιστή -remove-from-recents = Αφαίρεση από τα πρόσφατα -apply-to-all = Εφαρμογή σε όλα -moving = - Μετακίνηση { $items } { $items -> - [one] στοιχείου - *[other] στοιχείων - } από τον φάκελο «{ $from }» στον φάκελο «{ $to }» ({ $progress })... -change-wallpaper = Αλλαγή ταπετσαρίας... -network-drive-description = - Οι διευθύνσεις διακομιστών αποτελούνται από ένα πρόθεμα πρωτοκόλλου και μια διεύθυνση. - Παραδείγματα: ssh://192.168.0.1, ftp://[2001:db8::1] -deleting = - Διαγραφή { $items } { $items -> - [one] στοιχείου - *[other] στοιχείων - } από τα { trash } ({ $progress })... -single-click = Μονό κλικ για άνοιγμα -setting-permissions = Ορισμός των δικαιωμάτων για το «{ $name }» σε: { $mode } -owner = Κάτοχος -creating = Δημιουργία του «{ $name }» στον φάκελο «{ $parent }» -execute-only = Μόνο εκτέλεση -open-item-location = Άνοιγμα τοποθεσίας στοιχείου -set-executable-and-launched = Έγινε ορισμός του «{ $name }» ως εκτελέσιμου και εκκινήθηκε -mount-error = Αδυναμία πρόσβασης στη μονάδα -grid-view = Προβολή πλέγματος -set-and-launch = Ορισμός και εκκίνηση -removed-from-recents = - Έγινε αφαίρεση { $items } { $items -> - [one] στοιχείου - *[other] στοιχείων - } από τα { recents } -add-to-sidebar = Προσθήκη στην πλαϊνή στήλη -item-created = Ημερομηνία δημιουργίας: { $created } -network-drive-schemes = - Διαθέσιμα πρωτόκολλα,Πρόθεμα - AppleTalk,afp:// - File Transfer Protocol,ftp:// ή ftps:// - Network File System,nfs:// - Server Message Block,smb:// - SSH File Transfer Protocol,sftp:// ή ssh:// - WebDAV,dav:// ή davs:// -set-executable-and-launch = Ορισμός ως εκτελέσιμο και εκκίνηση -restored = - Έγινε ανάκτηση { $items } { $items -> - [one] στοιχείου - *[other] στοιχείων - } από τα { trash } -type-to-search-recursive = Κάνει αναζήτηση στον τρέχοντα φάκελο και όλους τους υποφακέλους -progress-paused = { $percent }%, σε παύση -cancelled = Ακυρωμένες -new-folder = Νέος φάκελος... -match-desktop = Συμφωνία με την επιφάνεια εργασίας -operations-running-finished = - Εκτέλεση { $running } { $running -> - [one] διεργασίας - *[other] διεργασιών - } ({ $percent }%), { $finished } ολοκληρωμένες... -sort-by-name = Ταξινόμηση κατά όνομα -edit-history = Ιστορικό επεξεργασιών -show-hidden-files = Εμφάνιση κρυφών αρχείων -progress-failed = { $percent }%, απέτυχε -item-accessed = Ημερομηνία πρόσβασης: { $accessed } -extract-to-title = Αποσυμπίεση σε φάκελο -extracting = - Αποσυμπίεση { $items } { $items -> - [one] στοιχείου - *[other] στοιχείων - } από το αρχείο «{ $from }» στον φάκελο «{ $to }» ({ $progress })... -permanently-deleted = - Έγινε οριστική διαγραφή { $items } { $items -> - [one] στοιχείου - *[other] στοιχείων - } -complete = Ολοκληρωμένες -write-execute = Εγγραφή και εκτέλεση -desktop-folder-content = Περιεχόμενο φακέλου επιφάνειας εργασίας -renaming = Μετονομασία από «{ $from }» σε «{ $to }» -set-executable-and-launch-description = Θέλετε να ορίσετε το «{ $name }» ως εκτελέσιμο και να το εκκινήσετε; -no-history = Δεν υπάρχουν στοιχεία στο ιστορικό. -emptied-trash = Έγινε άδειασμα του φακέλου «{ trash }» -sort-largest-to-smallest = Από τα μεγαλύτερα στα μικρότερα -restore-from-trash = Ανάκτηση από τα απορρίμματα -moved = - Έγινε μετακίνηση { $items } { $items -> - [one] στοιχείου - *[other] στοιχείων - } από τον φάκελο «{ $from }» στον φάκελο «{ $to }» -progress-cancelled = { $percent }%, ακυρώθηκε -open-in-new-tab = Άνοιγμα σε νέα καρτέλα -unknown-folder = άγνωστος φάκελος -created = Έγινε δημιουργία του «{ $name }» στον φάκελο «{ $parent }» -delete-permanently = Οριστική διαγραφή -write-only = Μόνο εγγραφή -display-settings = Ρυθμίσεις οθόνης... -new-tab = Νέα καρτέλα -failed = Αποτυχημένες -modified = Ημερομηνία τροποποίησης -desktop-appearance = Εμφάνιση επιφάνειας εργασίας... -file-already-exists = Υπάρχει ήδη ένα αρχείο με αυτό το όνομα -permanently-delete-warning = Θα διαγραφούν οριστικά τα εξής: { $target }. Δεν είναι δυνατή η αναίρεση αυτής της ενέργειας. -favorite-path-error-description = - Αδυναμία ανοίγματος του «{ $path }» - Το «{ $path }» ενδέχεται να μην υπάρχει ή να μην έχετε το δικαίωμα να το ανοίξετε - - Θέλετε να το αφαιρέσετε από την πλαϊνή στήλη; -empty-trash-warning = Τα στοιχεία του φακέλου «Απορρίμματα» θα διαγραφούν οριστικά -empty-trash-title = Άδειασμα απορριμμάτων; -type-to-search = Πληκτρολόγηση για αναζήτηση -notification-in-progress = Βρίσκονται σε εξέλιξη διεργασίες αρχείων -name-no-slashes = Το όνομα δεν μπορεί να περιέχει καθέτους -replace-title = Το «{ $filename }» υπάρχει ήδη σε αυτήν την τοποθεσία -name-invalid = Το όνομα δεν μπορεί να είναι «{ $filename }» -operations-running = - Εκτέλεση { $running } { $running -> - [one] διεργασίας - *[other] διεργασιών - } ({ $percent }%)... -context-action-confirm-title = Εκτέλεση του «{ $name }»; -pasted-image = Επικολλημένη εικόνα -pasted-text = Επικολλημένο κείμενο -pasted-video = Επικολλημένο βίντεο -show-recents = Φάκελος «Πρόσφατα» στην πλαϊνή στήλη -move-to = Μετακίνηση σε... -copy-path = Αντιγραφή διαδρομής -move-to-title = Επιλογή προορισμού μετακίνησης -selected-items = { $items } επιλεγμένα στοιχεία -context-action = Ενέργεια περιβάλλοντος -copy-to = Αντιγραφή σε... -mixed = Μικτό -type-to-search-select = Επιλέγει την πρώτη αντιστοιχία αρχείου ή φακέλου -clear-recents-history = Απαλοιφή ιστορικού πρόσφατων -context-action-confirm-warning = - Θα εκτελεστεί σε { $items } { $items -> - [one] στοιχείο - *[other] στοιχεία - }. -rename-confirm = Μετονομασία -checksum = Άθροισμα ελέγχου { $kind } -calculate = Υπολογισμός -error = Σφάλμα -sidebar-label = Ετικέτα πλαϊνής στήλης -search-application = Αναζήτηση με όνομα εφαρμογής -change-sidebar-label = Αλλαγή ετικέτας πλαϊνής στήλης +light = Φωτεινό +dark = Σκοτεινό diff --git a/i18n/en-GB/cosmic_files.ftl b/i18n/en-GB/cosmic_files.ftl index 62d9b75..e69de29 100644 --- a/i18n/en-GB/cosmic_files.ftl +++ b/i18n/en-GB/cosmic_files.ftl @@ -1,100 +0,0 @@ -save = Save -quit = Quit -name = Name -delete = Delete -cancel = Cancel -run = Run -new-window = New window -theme = Theme -appearance = Appearance -light = Light -repository = Repository -support = Support -dark = Dark -match-desktop = Match desktop -settings = Settings -replace = Replace -file = File -zoom-in = Zoom in -select-all = Select all -zoom-out = Zoom out -default-size = Default size -edit = Edit -copy = Copy -paste = Paste -view = View -undo = Undo -cut = Cut -icon-size-and-spacing = Icon size and spacing -cosmic-files = COSMIC Files -trash = Rubbish -empty-folder-hidden = Empty folder (has hidden items) -no-results = No results found -empty-folder = Empty folder -menu-settings = Settings... -recents = Recents -mounted-drives = Mounted drives -filesystem = Filesystem -home = Home -desktop-view-options = Desktop view options... -show-on-desktop = Show on Desktop -trash-folder-icon = Bin folder icon -desktop-folder-content = Desktop folder content -networks = Networks -today = Today -notification-in-progress = File operations are in progress -comment = File manager for the COSMIC desktop -keywords = Folder;Manager; -size = Size -dismiss = Dismiss message -trashed-on = Binned -create-archive = Create archive -pause = Pause -resume = Resume -grid-spacing = Grid spacing -details = Details -operations-running-finished = - { $running } { $running -> - [one] operation - *[other] operations - } running ({ $percent }%), { $finished } finished... -icon-size = Icon size -modified = Modified -operations-running = - { $running } { $running -> - [one] operation - *[other] operations - } running ({ $percent }%)... -copy-to-title = Select copy destination -open-file = Open file -open-multiple-files = Open multiple files -create-new-folder = Create new folder -create = Create -other-apps = Other applications -open-multiple-folders = Open multiple folders -extract-to = Extract To... -create-new-file = Create new file -open-in-new-window = Open in new window -folder-name = Folder name -browse-store = Browse { $store } -open-item-location = Open item location -mount-error = Unable to access drive -open = Open -extract-to-title = Extract to folder -open-with-title = How do you want to open "{ $name }"? -extract-password-required = Password required -open-folder = Open folder -open-in-new-tab = Open in new tab -file-name = File name -save-file = Save file -file-already-exists = A file with that name already exists -name-hidden = Names starting with "." will be hidden -folder-already-exists = A folder with that name already exists -empty-trash = Empty bin -empty-trash-title = Empty bin? -name-no-slashes = Name cannot contain slashes -name-invalid = Name cannot be "{ $filename }" -copy-to-button-label = Copy -move-to-title = Select move destination -move-to-button-label = Move -context-action = Context action diff --git a/i18n/en/cosmic_files.ftl b/i18n/en/cosmic_files.ftl index b97ff88..654e9ab 100644 --- a/i18n/en/cosmic_files.ftl +++ b/i18n/en/cosmic_files.ftl @@ -96,17 +96,9 @@ 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 -context-action = Context action -context-action-confirm-title = Run "{$name}"? -context-action-confirm-warning = This will run on {$items} {$items -> - [one] item - *[other] items - }. -run = Run ## Permanently delete Dialog selected-items = The {$items} selected items @@ -117,10 +109,6 @@ permanently-delete-warning = {$target} will be permanently deleted. This action ## Rename Dialog rename-file = Rename file rename-folder = Rename folder -rename-confirm = Rename - -## Change Sidebar Label Dialog -sidebar-label = Sidebar label ## Replace Dialog replace = Replace @@ -143,12 +131,6 @@ 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 ### Mode 1 (unusual) @@ -302,7 +284,6 @@ unknown-folder = unknown folder ## Open with menu-open-with = Open with... default-app = {$name} (default) -search-application = Search by app name ## Show details show-details = Show details @@ -313,9 +294,6 @@ item-created = Created: {$created} item-modified = Modified: {$modified} item-accessed = Accessed: {$accessed} calculating = Calculating... -checksum = {$kind} checksum -calculate = Calculate -error = Error ## Settings settings = Settings @@ -346,12 +324,10 @@ extract-here = Extract new-file = New file... new-folder = New folder... open-in-terminal = Open in terminal -edit-desktop-entry = Edit desktop entry move-to = Move to... move-to-trash = Move to trash restore-from-trash = Restore from trash remove-from-sidebar = Remove from sidebar -change-sidebar-label = Change sidebar label sort-by-name = Sort by name sort-by-modified = Sort by modified sort-by-size = Sort by size diff --git a/i18n/es-419/cosmic_files.ftl b/i18n/es-419/cosmic_files.ftl index 9f8ef72..ca17ec5 100644 --- a/i18n/es-419/cosmic_files.ftl +++ b/i18n/es-419/cosmic_files.ftl @@ -5,7 +5,7 @@ no-results = No se encontraron resultados filesystem = Sistema de archivos home = Inicio networks = Redes -notification-in-progress = Las operaciones de archivo están en progreso +notification-in-progress = Las operaciones de archivo están en progreso. trash = Papelera recents = Recientes undo = Deshacer diff --git a/i18n/eu/cosmic_files.ftl b/i18n/eu/cosmic_files.ftl deleted file mode 100644 index e69de29..0000000 diff --git a/i18n/fa/cosmic_files.ftl b/i18n/fa/cosmic_files.ftl index 5f5eaee..972e3c9 100644 --- a/i18n/fa/cosmic_files.ftl +++ b/i18n/fa/cosmic_files.ftl @@ -345,4 +345,3 @@ sort-newest-first = جدیدترین در ابتدا sort-oldest-first = قدیمی‌ترین در ابتدا sort-smallest-to-largest = کوچک‌ترین به بزرگ‌ترین sort-largest-to-smallest = بزرگ‌ترین به کوچک‌ترین -run = اجرا diff --git a/i18n/fi/cosmic_files.ftl b/i18n/fi/cosmic_files.ftl index ce1f7ff..105bff6 100644 --- a/i18n/fi/cosmic_files.ftl +++ b/i18n/fi/cosmic_files.ftl @@ -5,7 +5,7 @@ no-results = Ei tuloksia filesystem = Tiedostojärjestelmä home = Koti networks = Verkot -notification-in-progress = Tiedostotoimintoja käynnissä +notification-in-progress = Tiedostotoimintoja käynnissä. trash = Roskakori recents = Viimeaikaiset undo = Kumoa @@ -16,7 +16,7 @@ today = Tänään desktop-view-options = Työpöytänäkymän asetukset… show-on-desktop = Näytä työpöydällä desktop-folder-content = Työpöytäkansion sisältö -mounted-drives = Liitetyt asemat +mounted-drives = Tiedostojärjestelmään liitetyt kovalevyt trash-folder-icon = Roskakorikansion kuvake icon-size-and-spacing = Kuvakkeen koko ja välistys icon-size = Kuvakkeen koko @@ -38,7 +38,7 @@ create-archive = Luo arkisto ## Empty Trash Dialog empty-trash = Tyhjennä roskakori -empty-trash-warning = Roskakorikansion kohteet poistetaan pysyvästi +empty-trash-warning = Haluatko varmasti tyhjentää koko roskakorin pysyvästi? ## Mount Error Dialog @@ -73,8 +73,8 @@ save-file = Tallenna tiedosto ## Open With Dialog -open-with-title = Miten haluat avata kohteen "{ $name }"? -browse-store = Selaa { $store }a +open-with-title = Kuinka haluat avata kohteen "{ $name }"? +browse-store = Selaa { $store } ## Rename Dialog @@ -88,15 +88,15 @@ replace-title = "{ $filename }" on jo olemassa tässä sijainnissa replace-warning = Haluatko korvata sen tallentamallasi kohteella? Korvaaminen ylikirjoittaa kohteen sisällön. replace-warning-operation = Haluatko korvata sen? Korvaaminen ylikirjoittaa sen sisällön. original-file = Alkuperäinen tiedosto -replace-with = Korvaa käyttäen -apply-to-all = Toteuta kaikkiin +replace-with = Korvaa kohteella +apply-to-all = Sovella kaikkiin keep-both = Pidä molemmat skip = Ohita ## Set as Executable and Launch Dialog set-executable-and-launch = Aseta käynnistettäväksi ja käynnistä -set-executable-and-launch-description = Haluatko asettaa kohteen "{ $name }" käynnistettäväksi ja käynnistää sen? +set-executable-and-launch-description = Haluatko asettaa kohteen "{ $name }" käynnistettväksi ja käynnistää sen? set-and-launch = Aseta ja käynnistä ## Metadata Dialog @@ -116,9 +116,9 @@ other = Muut add-network-drive = Lisää verkkolevy connect = Yhdistä connect-anonymously = Yhdistä nimettömästi -connecting = Yhdistetään… +connecting = Yhdistää… domain = Verkkotunnus -enter-server-address = Kirjoita palvelimen osoite +enter-server-address = Syötä palvelimen osoite network-drive-description = Palvelinosoitteet sisältävät protokollaetuliitteen sekä osoitteen. Esimerkkejä: ssh://192.168.0.1, ftp://[2001:db8::1] @@ -126,91 +126,91 @@ network-drive-description = ### Make sure to keep the comma which separates the columns network-drive-schemes = - Saatavilla olevat yhteyskäytännöt,Etuliite + Saatavissa olevat protokollat,Etuliite AppleTalk,afp:// - File Transfer Protocol,ftp:// tai ftps:// + File Transfer Protocol,ftp:// or ftps:// Network File System,nfs:// Server Message Block,smb:// - SSH File Transfer Protocol,sftp:// tai ssh:// - WebDav,dav:// tai davs:// -network-drive-error = Verkkolevy ei saatavilla + SSH File Transfer Protocol,sftp:// or ssh:// + WebDav,dav:// or davs:// +network-drive-error = Verkkolevy saavuttamattomissa password = Salasana remember-password = Muista salasana try-again = Yritä uudelleen -username = Käyttäjätunnus +username = Käyttäjänimi ## Operations edit-history = Muokkaa historiaa history = Historia no-history = Historia on tyhjä. -pending = Jonossa -failed = Epäonnistuneet -complete = Valmiit +pending = Odottaa käsittelyä +failed = Epäonnistui +complete = Valmis compressing = - Pakataan { $items } { $items -> + Tiivistetään { $items } { $items -> [one] kohde - *[other] kohdetta - } sijainnista "{ $from }" arkistoon "{ $to }" ({ $progress })… + *[other] kohteita + } lähteestä "{ $from }" arkistoon "{ $to }" compressed = - Pakattu { $items } { $items -> + Tiivistetty { $items } { $items -> [one] kohde - *[other] kohdetta - } sijainnista "{ $from }" arkistoon "{ $to }" + *[other] kohteet + } lähteestä "{ $from }" arkistoon "{ $to }" copy_noun = Kopio -creating = Luodaan "{ $name }" kohteen "{ $parent }" alle -created = Luotu "{ $name }" kohteen "{ $parent }" alle +creating = Luodaan kohdetta "{ $name }" kohteen "{ $parent }" alle +created = Luotu kohde "{ $name }" kohteen "{ $parent }" alle copying = Kopioidaan { $items } { $items -> [one] kohde - *[other] kohdetta - } sijainnista "{ $from }" kohteeseen "{ $to }" ({ $progress })… + *[other] kohteita + } lähteestä "{ $from }" kohteeseen "{ $to }" copied = Kopioitu { $items } { $items -> [one] kohde - *[other] kohdetta - } sijainnista "{ $from }" kohteeseen "{ $to }" -emptying-trash = Tyhjennetään { trash } ({ $progress })… + *[other] kohteet + } lähteestä "{ $from }" kohteeseen "{ $to }" +emptying-trash = Tyhjennetään { trash } emptied-trash = Tyhjennetty { trash } extracting = Puretaan { $items } { $items -> [one] kohde - *[other] kohdetta - } arkistosta "{ $from }" kohteeseen "{ $to }" ({ $progress })… + *[other] kohteet + } arkistosta "{ $from }" kohteeseen "{ $to }" extracted = Purettu { $items } { $items -> [one] kohde - *[other] kohdetta + *[other] kohteet } arkistosta "{ $from }" kohteeseen "{ $to }" setting-executable-and-launching = Asetetaan "{ $name }" käynnistettäväksi ja käynnistetään set-executable-and-launched = Asetettu "{ $name }" käynnistettäväksi ja käynnistetty moving = Siirretään { $items } { $items -> [one] kohde - *[other] kohdetta - } sijainnista "{ $from }" kohteeseen "{ $to }" ({ $progress })… + *[other] kohteet + } lähteestä "{ $from }" kohteeseen "{ $to }" moved = Siirretty { $items } { $items -> [one] kohde - *[other] kohdetta - } sijainnista "{ $from }" kohteeseen "{ $to }" + *[other] kohteet + } lähteestä "{ $from }" kohteeseen "{ $to }" renaming = Nimetään kohde "{ $from }" muotoon "{ $to }" renamed = Nimetty kohde "{ $from }" muotoon "{ $to }" restoring = Palautetaan { $items } { $items -> [one] kohde - *[other] kohdetta - } roskakorista ({ $progress })… + *[other] kohteet + } { trash }sta restored = Palautettu { $items } { $items -> [one] kohde - *[other] kohdetta - } roskakorista -unknown-folder = tuntematon kansio + *[other] kohteet + } { trash }sta +unknown-folder = Tuntematon kansio ## Open with -menu-open-with = Avaa sovelluksella… +menu-open-with = Avaa ohjelmalla… default-app = { $name } (oletus) ## Show details @@ -225,14 +225,14 @@ settings = Asetukset appearance = Ulkoasu theme = Teema -match-desktop = Sama kuin työpöytä +match-desktop = Sovita yhteen työpöydän kanssa dark = Tumma light = Vaalea # Context menu add-to-sidebar = Lisää sivupalkkiin -compress = Pakkaa… +compress = Pakkaa extract-here = Pura new-file = Uusi tiedosto… new-folder = Uusi kansio… @@ -241,9 +241,9 @@ move-to-trash = Siirrä roskakoriin restore-from-trash = Palauta roskakorista remove-from-sidebar = Poista sivupalkista sort-by-name = Järjestä nimen mukaan -sort-by-modified = Järjestä muokkausajan mukaan +sort-by-modified = Järjestä muokkauspäivämäärän mukaan sort-by-size = Järjestä koon mukaan -sort-by-trashed = Järjestä poistamisajan mukaan +sort-by-trashed = Järjestä poistamispäivämäärän mukaan ## Desktop @@ -281,9 +281,9 @@ grid-view = Ruudukkonäkymä list-view = Listanäkymä show-hidden-files = Näytä piilotetut tiedostot list-directories-first = Näytä kansiot ensin -gallery-preview = Gallerian esikatselu +gallery-preview = Gallerian esinäkymä menu-settings = Asetukset… -menu-about = Tietoa COSMICin tiedostonhallinnasta… +menu-about = Tietoa COSMIC Tiedostoista… ## Sort @@ -318,106 +318,3 @@ move-to-button-label = Siirrä clear-recents-history = Tyhjennä viimeaikaisten historia copy-path = Kopioi polku dismiss = Hylkää viesti -operations-running = - { $running } { $running -> - [one] toiminto - *[other] toimintoa - } käynnissä ({ $percent } %)... -operations-running-finished = - { $running } { $running -> - [one] toiminto - *[other] toimintoa - } käynnissä ({ $percent } %), { $finished } valmistunut… -pause = Keskeytä -extract-to = Pura sijaintiin… -permanently-delete-warning = { $target } tullaan poistamaan pysyvästi. Tätä toimintoa ei voi perua. -execute-only = Vain suoritus -write-only = Vain kirjoitus -write-execute = Kirjoita ja suorita -read-only = Vain luku -read-execute = Lue ja suorita -read-write = Lue ja kirjoita -read-write-execute = Lue, kirjoita sekä suorita -calculating = Lasketaan… -single-click = Yhden napsautuksen avaus -type-to-search = Kirjoita etsiäksesi -type-to-search-recursive = Etsii nykyisestä kansiosta ja kaikista alikansioista -remove-from-recents = Poista viimeaikaisista -selected-items = { $items } valittua kohdetta -show-recents = Viimeaikaisten kansio sivupalkissa -copy-to = Kopioi… -move-to = Siirrä… -details = Yksityiskohdat -grid-spacing = Ruudukkovälit -none = Ei mitään -favorite-path-error = Virhe avattaessa kansiota -favorite-path-error-description = - Polun { $path } avaaminen ei onnistunut - "{ $path }" ei välttämättä ole olemassa tai oikeutesi eivät riitä sen avaamiseen - - Haluatko poistaa sen sivupalkista? -keep = Pidä -repository = Tietovarasto -support = Tuki -progress = { $percent } % -progress-cancelled = { $percent } %, peruttu -progress-failed = { $percent } %, epäonnistui -progress-paused = { $percent } %, keskeytetty -setting-permissions = Asetetaan kohteen "{ $name }" käyttöoikeudeksi { $mode } -set-permissions = Asetettu kohteen { $name } käyttöoikeudeksi { $mode } -permanently-deleting = - Poistetaan pysyvästi { $items } { $items -> - [one] kohde - *[other] kohdetta - } -permanently-deleted = - Poistettu pysyvästi { $items } { $items -> - [one] kohde - *[other] kohdetta - } -items = Kohteita: { $items } -item-accessed = Käytetty: { $accessed } -type-to-search-enter-path = Kirjoittaa polun kansioon tai tiedostoon -eject = Poista asemasta -copy-to-title = Valitse mihin kopioidaan -move-to-title = Valitse mihin siirretään -pasted-image = Liitetty kuva -pasted-text = Liitetty teksti -pasted-video = Liitetty video -type-to-search-select = Valitsee ensimmäisen täsmäävän tiedoston tai kansion -deleting = - Poistetaan { $items } { $items -> - [one] kohde - *[other] kohdetta - } roskakorista ({ $progress })… -deleted = - Poistettu { $items } { $items -> - [one] kohde - *[other] kohdetta - } roskakorista -removing-from-recents = - Poistetaan { $items } { $items -> - [one] kohde - *[other] kohdetta - } viimeaikaisista -removed-from-recents = - Poistettu { $items } { $items -> - [one] kohde - *[other] kohdetta - } viimeaikaisista -mixed = Sekoitettu -context-action = Kontekstitoiminto -context-action-confirm-title = Suoritetaanko "{ $name }"? -context-action-confirm-warning = - Tämä suorittaa { $items } { $items -> - [one] kohteen - *[other] kohdetta - }. -run = Suorita -rename-confirm = Nimeä uudelleen -checksum = { $kind }-tarkistussumma -calculate = Laske -error = Virhe -search-application = Hae sovelluksen nimellä -sidebar-label = Sivupalkin nimike -change-sidebar-label = Muuta sivupalkin nimikettä diff --git a/i18n/fil/cosmic_files.ftl b/i18n/fil/cosmic_files.ftl deleted file mode 100644 index e69de29..0000000 diff --git a/i18n/fr/cosmic_files.ftl b/i18n/fr/cosmic_files.ftl index cdcfe35..fa54fd7 100644 --- a/i18n/fr/cosmic_files.ftl +++ b/i18n/fr/cosmic_files.ftl @@ -80,19 +80,18 @@ cancel = Annuler create = Créer open = Ouvrir open-file = Ouvrir le fichier -open-folder = Ouvrir le dossier +open-folder = Ouvrir dossier open-in-new-tab = Ouvrir dans un nouvel onglet open-in-new-window = Ouvrir dans une nouvelle fenêtre open-item-location = Ouvrir l'emplacement de l'élément open-multiple-files = Ouvrir plusieurs fichiers open-multiple-folders = Ouvrir plusieurs dossiers save = Enregistrer -save-file = Enregistrer le fichier +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 @@ -131,11 +130,6 @@ 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 @@ -189,7 +183,7 @@ keep = Conserver ## Add Network Drive add-network-drive = Ajouter un lecteur réseau -connect = Se connecter +connect = Connecter connect-anonymously = Se connecter anonymement connecting = Connexion... domain = Domaine @@ -206,8 +200,8 @@ network-drive-schemes = File Transfer Protocol,ftp:// or ftps:// Network File System,nfs:// Server Message Block,smb:// - SSH File Transfer Protocol,sftp:// ou ssh:// - WebDAV,dav:// ou davs:// + SSH File Transfer Protocol,sftp:// or ssh:// + WebDav,dav:// or davs:// network-drive-error = Impossible d'accéder au lecteur réseau password = Mot de passe remember-password = Se souvenir du mot de passe @@ -222,7 +216,7 @@ history = Historique no-history = Aucun élément dans l'historique. pending = En attente progress = { $percent }% -progress-cancelled = { $percent }%, annulé +progress-cancelled = { $percent }%, annulation progress-paused = { $percent }%, en pause failed = Échoué complete = Terminé @@ -259,7 +253,7 @@ deleted = [one] élément supprimé *[other] éléments supprimés } depuis { trash } -emptying-trash = Nettoyage en cours de { trash } ({ $progress })... +emptying-trash = { trash } en cours de nettoyage ({ $progress })... emptied-trash = { trash } vidée extracting = Extraction de { $items } { $items -> @@ -271,7 +265,7 @@ extracted = [one] élément extrait *[other] éléments extraits } depuis "{ $from }" vers "{ $to }" -setting-executable-and-launching = Définition de "{ $name }" comme exécutable et lancement en cours +setting-executable-and-launching = Paramétrage de "{ $name }" comme exécutable et prêt à être lancé set-executable-and-launched = Défini "{ $name }" comme exécutable et lancé moving = Déplacement de { $items } { $items -> @@ -282,7 +276,7 @@ moved = { $items } { $items -> [one] élément déplacé *[other] éléments déplacés - } de "{ $from }" vers "{ $to }" + } de "{ $from }" à "{ $to }" permanently-deleting = Suppression définitive de { $items } { $items -> [one] élément @@ -310,7 +304,7 @@ unknown-folder = Dossier inconnu ## Open with menu-open-with = Ouvrir avec... -default-app = { $name } (par défaut) +default-app = { $name } (défaut) ## Show details @@ -349,10 +343,9 @@ extract-here = Extraire new-file = Nouveau fichier... new-folder = Nouveau dossier... open-in-terminal = Ouvrir dans le terminal -edit-desktop-entry = Modifier le fichier .desktop move-to-trash = Déplacer vers la corbeille restore-from-trash = Restaurer depuis la corbeille -remove-from-sidebar = Retirer de la barre latérale +remove-from-sidebar = Supprimer de la barre latérale sort-by-name = Trier par nom sort-by-modified = Trier par date de modification sort-by-size = Trier par taille @@ -415,11 +408,11 @@ removing-from-recents = *[other] éléments } de { recents } other-apps = Autres applications -set-permissions = Permissions définies pour "{ $name }" à { $mode } +set-permissions = Définir les permissions pour "{ $name }" à { $mode } repository = Dépôt support = Support eject = Éjecter -remove-from-recents = Retirer des récents +remove-from-recents = Supprimer des récents empty-trash-title = Vider la corbeille ? setting-permissions = Définition des permissions pour "{ $name }" à { $mode } removed-from-recents = @@ -428,9 +421,9 @@ removed-from-recents = *[other] éléments supprimés } de { recents } progress-failed = { $percent }%, échec -type-to-search-select = Sélectionne le premier fichier ou dossier correspondant +type-to-search-select = Sélectionne le premier fichier ou dossier qui convient pasted-image = Image collée -pasted-text = Texte collé +pasted-text = Text collé pasted-video = Vidéo collée copy-to-title = Sélectionner la destination de la copie copy-to-button-label = Copier @@ -443,19 +436,3 @@ keywords = Dossier;Gestionnaire; show-recents = Dossier Récents dans la barre latérale copy-path = Copier le chemin clear-recents-history = Effacer l'historique des Récents -mixed = Mixte -context-action-confirm-title = Exécuter "{ $name }" ? -context-action-confirm-warning = - Cela exécutera sur { $items } { $items -> - [one] élément - *[other] éléments - }. -run = Exécuter -context-action = Action contextuelle -rename-confirm = Renommer -calculate = Calculer -error = Erreur -checksum = Somme de contrôle { $kind } -sidebar-label = Libellé de la barre latérale -search-application = Chercher par nom d'appli -change-sidebar-label = Changer le libellé de la barre latérale diff --git a/i18n/ga/cosmic_files.ftl b/i18n/ga/cosmic_files.ftl index 24b203b..7a9cfdf 100644 --- a/i18n/ga/cosmic_files.ftl +++ b/i18n/ga/cosmic_files.ftl @@ -307,7 +307,7 @@ single-click = Cliceáil amháin le hoscailt ### Appearance -appearance = Dealramh +appearance = Cuma theme = Téama match-desktop = Meaitseáil deasc dark = Dorcha @@ -433,16 +433,3 @@ keywords = Fillteán;Bainisteoir; show-recents = Fillteán le déanaí sa bharra taoibh clear-recents-history = Glan stair na n-earraí le déanaí copy-path = Cóipeáil an chosán -mixed = Measctha -context-action = Gníomh comhthéacsúil -context-action-confirm-title = Rith "{ $name }"? -context-action-confirm-warning = - Rithfidh sé seo ar { $items } { $items -> - [one] mhír - *[other] míreanna - }. -run = Rith -rename-confirm = Athainmnigh -checksum = { $kind } suim sheiceála -calculate = Ríomh -error = Earráid diff --git a/i18n/gl/cosmic_files.ftl b/i18n/gl/cosmic_files.ftl deleted file mode 100644 index 77a5757..0000000 --- a/i18n/gl/cosmic_files.ftl +++ /dev/null @@ -1,333 +0,0 @@ -name = Nome -save = Gardar -cancel = Cancelar -run = Executar -delete = Eliminar -repository = Repositorio -support = Soporte -settings = Axustes -appearance = Apariencia -theme = Tema -match-desktop = Axustar ao escritorio -dark = Escuro -light = Claro -replace = Remplazar -file = Ficheiro -new-window = Nova xanela -quit = Pechar -edit = Editar -undo = Desfacer -cut = Cortar -copy = Copiar -paste = Pegar -select-all = Seleccionar todo -view = Ver -zoom-in = Aumentar Zoom -default-size = Tamaño por defecto -zoom-out = Afastar Zoom -menu-settings = Axustes... -cosmic-files = Ficheiros COSMIC -comment = Xestor de ficheiros para o escritorio COSMIC -keywords = Cartafol;Xestor; -empty-folder = Cartafol baleiro -empty-folder-hidden = Cartafol baleiro (contén elementos agochados) -no-results = Non se atoparon resultados -filesystem = Sistema de ficheiros -home = Inicio -networks = Redes -notification-in-progress = Operacións de ficheiros en progreso -trash = Lixo -recents = Recentes -today = Hoxe -desktop-view-options = Axustes da vista do escritorio… -show-on-desktop = Amosar no Escritorio -desktop-folder-content = Contido do cartafol Escritorio -mounted-drives = Unidades montadas -trash-folder-icon = Icona do cartafol do Lixo -icon-size-and-spacing = Tamaño e espazado das iconas -icon-size = Tamaño de icona -grid-spacing = Espazado da grella -modified = Modificado -trashed-on = No lixo -size = Tamaño -details = Detalles -dismiss = Descartar a mensaxe -operations-running = - { $running } { $executandose -> - [unha] operacion - *[outra] operacions - } executandose ({ $percent }%)... -operations-running-finished = - { $running } { $executandose -> - [unha] operacion - *[outra] operacions - } executandose ({ $percent }%), { $finished } rematadas... -pause = Pausa -resume = Retomar -create-archive = Crear arquivo comprimido -copy-to-title = Seleccionar o destino da copia -copy-to-button-label = Copiar -extract-password-required = Contrasinal necesario -extract-to = Extraer en… -extract-to-title = Extraer a cartafol -empty-trash = Baleirar Lixo -empty-trash-title = Baleirar o lixo? -empty-trash-warning = Os elementos do cartafol do lixo eliminaranse de forma permanente -mount-error = Non é posible acceder a unidade -move-to-title = Seleccionar o destino do movemento -move-to-button-label = Mover -create-new-file = Crear un novo ficheiro -create-new-folder = Crear novo cartafol -file-name = Nome do ficheiro -folder-name = Nome do cartafol -file-already-exists = Existe un ficheiro co mesmo nome -folder-already-exists = Existe un cartafol co mesmo nome -name-hidden = Os nomes que comecen por "." agocharanse -name-invalid = O nome non pode ser "{ $filename }" -name-no-slashes = O nome non pode conter barras -create = Crear -open = Abrir -open-file = Abrir ficheiro -open-folder = Abrir cartafol -open-in-new-tab = Abrir nunha nova lapela -open-in-new-window = Abrir nunha nova xanela -open-item-location = Abrir ubicación do ítem -open-multiple-files = Abrir multiples ficheiros -open-multiple-folders = Abrir multiples cartafoles -save-file = Gardar ficheiro -open-with-title = Como queres abrir «{ $name }»? -browse-store = Atopar { $store } -other-apps = Outras aplicacións -related-apps = Aplicacións relacionadas -context-action = Acción contextual -context-action-confirm-title = Executar "{ $name }"? -context-action-confirm-warning = - Esto vaise executar en { $items } { $items -> - [un] item - *[outro] items - }. -selected-items = Os { $items } elementos seleccionados -permanently-delete-question = Eliminar permanentemente? -permanently-delete-warning = { $target } será eliminado permanentemente. Esta acción non se poderá desfacer. -rename-file = Renomear ficheiro -rename-folder = Renomear cartafol -rename-confirm = Renomear -replace-title = "{ $filename }" xa existe nesta ubicación -replace-warning = Queres substituílo polo que estás a gardar? Ao substituílo sobrescribirase o seu contido. -replace-warning-operation = Queres substituílo? Ao substituílo sobrescribirase o seu contido. -original-file = Ficheiro orixinal -replace-with = Remplazar con -apply-to-all = Aplicar a todo -keep-both = Manter ambos -skip = Saltar -set-executable-and-launch = Marcar como executable e lanzalo -set-executable-and-launch-description = Queres marcar «{ $name }» como executábel e lanzalo? -set-and-launch = Marcar e executar -open-with = Abrir con -owner = Propietario -group = Grupo -other = Outro -mixed = Mesturar -none = Ningún -execute-only = Só execución -write-only = Só escritura -write-execute = Escritura e execución -read-only = Só lectura -read-execute = Lectura e execución -read-write = Lectura e escritura -read-write-execute = Lectura, escritura e execución -favorite-path-error = Erro abrindo o directorio -favorite-path-error-description = - Non foi posíbel abrir «{ $path }». - É posíbel que «{ $path }» non exista ou que non teñas permisos para abrilo. - - Queres retiralo da barra lateral? -remove = Eliminar -keep = Manter -add-network-drive = Engadir unidade de rede -connect = Conectar -connect-anonymously = Conectar de xeito anonimo -connecting = Conectando... -domain = Dominio -enter-server-address = Introducir a dirección do servidor -network-drive-description = - Os enderezos dos servidores inclúen un prefixo de protocolo e un enderezo. - Exemplos: ssh://192.168.0.1, ftp://[2001:db8::1] -network-drive-schemes = - Protocolos dispoñíbeis,Prefixo - AppleTalk,afp:// - Protocolo de transferencia de ficheiros,ftp:// ou ftps:// - Sistema de ficheiros en rede,nfs:// - Server Message Block,smb:// - Protocolo de transferencia de ficheiros por SSH,sftp:// ou ssh:// - WebDAV,dav:// ou davs:// -network-drive-error = Non foi posible acceder a unidade de rede -password = Contrasinal -remember-password = Recordar o contrasinal -try-again = Voltar a intentar -username = Nome de usuario -cancelled = Cancelado -edit-history = Editar historial -history = Historial -no-history = Non hai elementos no historial. -pending = Pendente -progress = { $percent }% -progress-cancelled = { $percent }%, cancelado -progress-failed = { $percent }%, fallou -progress-paused = { $percent }%, pausado -failed = Fallou -complete = Completado -compressing = - Comprimindo { $items } { $items -> - [one] elemento - *[other] elementos - } de «{ $from }» a «{ $to }» ({ $progress })... -compressed = - Comprimidos { $items } { $items -> - [one] elemento - *[other] elementos - } de «{ $from }» a «{ $to }» -copy_noun = Copiar -pasted-image = Imaxe pegada -pasted-text = Texto Pegado -pasted-video = Video pegado -creating = Creando "{ $name }" en "{ $parent }" -created = Creado "{ $name }" en "{ $parent }" -copying = - Copiando { $items } { $items -> - [one] elemento - *[other] elementos - } de «{ $from }» a «{ $to }» ({ $progress })... -copied = - Copiados { $items } { $items -> - [one] elemento - *[other] elementos - } de «{ $from }» a «{ $to }» -deleted = - Eliminando { $items } { $items -> - [one] elemento - *[other] elementos - } do lixo ({ $progress }) -emptying-trash = Baleirando { trash } ({ $progress })... -emptied-trash = Baleirado o { trash } -extracting = - Extraendo { $items } { $items -> - [one] elemento - *[other] elementos - } de «{ $from }» a «{ $to }» ({ $progress })... -extracted = - Extraídos { $items } { $items -> - [one] elemento - *[other] elementos - } de «{ $from }» a «{ $to }» -setting-executable-and-launching = Marcando «{ $name }» como executábel e executándoo -set-executable-and-launched = «{ $name }» marcouse como executábel e executouse -setting-permissions = Establecendo os permisos de «{ $name }» en { $mode } -set-permissions = Establecéronse os permisos de «{ $name }» en { $mode } -moving = - Movendo { $items } { $items -> - [one] elemento - *[other] elementos - } de «{ $from }» a «{ $to }» ({ $progress })... -moved = - Movidos { $items } { $items -> - [one] elemento - *[other] elementos - } de «{ $from }» a «{ $to }» -permanently-deleting = - Eliminando permanentemente { $items } { $items -> - [one] elemento - *[other] elementos - } -permanently-deleted = - Eliminados permanentemente { $items } { $items -> - [one] elemento - *[other] elementos - } -removing-from-recents = - Retirando { $items } { $items -> - [one] elemento - *[other] elementos - } dos { recents } -removed-from-recents = - Retirado { $items } { $items -> - [one] elemento - *[other] elementos - } de { recents } -renaming = Renomeando "{ $from }" a "{ $to }" -renamed = Renomeado "{ $from }" a "{ $to }" -restoring = - Restaurando { $items } { $items -> - [one] elemento - *[other] elementos - } de { trash } ({ $progress })... -restored = - Restaurado { $items } { $items -> - [one] elemento - *[other] elementos - } de { trash } -unknown-folder = cartafol descoñecido -menu-open-with = Abrir con... -default-app = { $name } (por defecto) -show-details = Amosar detalles -type = Tipo: { $mime } -items = Elementos: { $items } -item-size = Tamaño: { $size } -item-created = Creado: { $created } -item-modified = Modificado: { $modified } -item-accessed = Accedido: { $accessed } -calculating = Calculando... -checksum = { $kind } checksum -calculate = Calculado -error = Erro -single-click = Clic único para abrir -show-recents = Cartafol de recentes no panel lateral -type-to-search = Escribe para atopar -type-to-search-recursive = Busca no cartafol actual e en todos os subcartafoles -type-to-search-enter-path = Introduce a ruta do cartafol ou ficheiro -type-to-search-select = Selecciona o primeiro ficheiro ou cartafol coincidente -add-to-sidebar = Engadir o panel lateral -clear-recents-history = Limpar historial de recentes -compress = Comprimir... -copy-to = Copiar a... -delete-permanently = Eliminar permanentemente -eject = Expulsar -extract-here = Extraer -new-file = Novo ficheiro... -new-folder = Novo cartafol... -open-in-terminal = Abrir no terminal -move-to = Mover a... -move-to-trash = Mover ao lixo -restore-from-trash = Recuperar do lixo -remove-from-sidebar = Eliminar do panel lateral -sort-by-name = Ordenar por nome -sort-by-modified = Ordenar por modificación -sort-by-size = Ordenar por tamaño -sort-by-trashed = Ordenar pola data de eliminación -remove-from-recents = Eliminar de recentes -change-wallpaper = Cambiar fondo de pantalla... -desktop-appearance = Apariencia do Escritorio... -display-settings = Axustes de pantalla... -new-tab = Nova lapela -reload-folder = Recargar cartafol -rename = Renomear... -close-tab = Pechar lapela -copy-path = Copiar ruta -grid-view = Vista de grella -list-view = Vista de lista -show-hidden-files = Amosar ficheiros ocultos -list-directories-first = Listar primeiro os directorios -gallery-preview = Vista previa da galería -menu-about = Sobre Ficheiros COSMIC... -sort = Ordenar -sort-a-z = A-Z -sort-z-a = Z-A -sort-newest-first = Novos primeiro -sort-oldest-first = Antigos primeiro -sort-smallest-to-largest = Do máis pequeno ao máis grande -sort-largest-to-smallest = Do máis grande ao máis pequeno -deleting = - Eliminando { $items } { $items -> - [one] elemento - *[other] elementos - } da { trash } ({ $progress })... diff --git a/i18n/hi/cosmic_files.ftl b/i18n/hi/cosmic_files.ftl index 8b5d8f8..03e0db4 100644 --- a/i18n/hi/cosmic_files.ftl +++ b/i18n/hi/cosmic_files.ftl @@ -5,7 +5,7 @@ no-results = कोई परिणाम नहीं filesystem = फाइल सिस्टम home = होम networks = नेटवर्क्स -notification-in-progress = फ़ाइल संचालन प्रगति पर हैं +notification-in-progress = फाइल संचालन प्रगति पर है। trash = कचरा recents = हाल के undo = पूर्ववत करें @@ -33,8 +33,8 @@ create-archive = संग्रह बनाएँ ## Empty Trash Dialog -empty-trash = रद्दी साफ़ करें -empty-trash-warning = रद्दी फ़ोल्डर में मौजूद आइटम स्थायी रूप से हटा दिए जाएंगे +empty-trash = कचरा खाली करें +empty-trash-warning = क्या आप वाकई कचरे में सभी आइटमों को स्थायी रूप से हटाना चाहते हैं? ## New File/Folder Dialog @@ -42,11 +42,11 @@ create-new-file = नई फाइल बनाएँ create-new-folder = नया फ़ोल्डर बनाएँ file-name = फाइल का नाम folder-name = फ़ोल्डर का नाम -file-already-exists = उस नाम की फ़ाइल पहले से मौजूद है -folder-already-exists = उस नाम का फ़ोल्डर पहले से मौजूद है -name-hidden = "." से शुरू होने वाले नाम छिपे होंगे -name-invalid = नाम "{ $filename }" नहीं हो सकता -name-no-slashes = नाम में स्लैश नहीं हो सकते +file-already-exists = इसी नाम की फाइल पहले से मौजूद है। +folder-already-exists = इसी नाम का फ़ोल्डर पहले से मौजूद है। +name-hidden = "." से शुरू होने वाले नाम छिपे रहेंगे। +name-invalid = नाम "{ $filename }" मान्य नहीं है। +name-no-slashes = नाम में स्लैश का उपयोग नहीं किया जा सकता है। ## Open/Save Dialog @@ -75,8 +75,8 @@ rename-folder = फ़ोल्डर का नाम बदलें ## Replace Dialog -replace = बदलें -replace-title = { $filename } पहले से इस स्थान पर मौजूद है +replace = प्रतिस्थापित करें +replace-title = { $filename } पहले से इस स्थान पर मौजूद है। replace-warning = क्या आप इसे प्रतिस्थापित करना चाहते हैं? यदि प्रतिस्थापित किया गया, तो मौजूदा फ़ाइल को ओवरराइट किया जाएगा। replace-warning-operation = क्या आप इसे बदलना चाहते हैं? प्रतिस्थापित करने पर मौजूदा फ़ाइल ओवरराइट हो जाएगी। original-file = मूल फ़ाइल @@ -211,13 +211,13 @@ show-details = विवरण दिखाएँ ## Settings -settings = सेटिंग्स +settings = सेटिंग ### Appearance -appearance = रुप-रंग -theme = प्रसंग -match-desktop = डेस्कटॉप से मेल खाएँ +appearance = रूप +theme = थीम +match-desktop = सिस्टम सेटिंग्स से मेल खाएँ dark = डार्क light = लाइट # Context menu @@ -251,27 +251,27 @@ new-tab = नया टैब new-window = नई विंडो rename = नाम बदलें... close-tab = टैब बंद करें -quit = बाहर जाएँ +quit = बंद करें ## Edit -edit = संपादन -cut = काटें -copy = कॉपी करें +edit = संपादित करें +cut = कट करें +copy = नकल करें paste = चिपकाएँ select-all = सभी चुनें ## View -zoom-in = बड़ा करें -default-size = मूल आकार -zoom-out = छोटा करें +zoom-in = ज़ूम इन करें +default-size = डिफ़ॉल्ट आकार +zoom-out = ज़ूम आउट करें view = दृश्य grid-view = ग्रिड दृश्य list-view = सूची दृश्य show-hidden-files = छिपी हुई फाइलें दिखाएँ list-directories-first = सबसे पहले डाइरेक्ट्री दिखाएँ -menu-settings = सेटिंग्स..। +menu-settings = सेटिंग्स... menu-about = कॉस्मिक फाइल्स के बारे में... ## Sort @@ -284,98 +284,4 @@ sort-oldest-first = पुराने से नए sort-smallest-to-largest = छोटे से बड़े sort-largest-to-smallest = बड़े से छोटे repository = रिपॉजिटरी -support = समर्थन -read-execute = पढ़ें और निष्पादित करें -deleted = - { trash } से { $items } { $items -> - [one] आइटम मिटाया गया - *[other] आइटम मिटाए गए - } -dismiss = संदेश खारिज करें -favorite-path-error = निर्देशिका खोलने में त्रुटि -progress = { $percent }% -related-apps = संबंधित ऐप्स -removing-from-recents = - { recents } से { $items } { $items -> - [one] आइटम हटाया जा रहा है - *[other] आइटम हटाए जा रहे हैं - } -remove = हटाएँ -read-write-execute = पढ़ें, लिखें और निष्पादित करें -other-apps = अन्य ऐप्स -pause = विराम -keep = रखें -permanently-deleting = - { $items } { $items -> - [one] आइटम स्थायी रूप से मिटाया जा रहा है - *[other] आइटम स्थायी रूप से मिटाए जा रहे हैं - } -read-write = पढ़ें और लिखें -none = कोई नहीं -resume = फिर से शुरू करें -grid-spacing = ग्रिड स्पेसिंग -extract-to = इस रूप में निकालें..। -delete = हटाएं -read-only = केवल पढ़ने के लिए -deleting = - { trash } से { $items } { $items -> - [one] आइटम मिटाया जा रहा है - *[other] आइटम मिटाए जा रहे हैं - } ({ $progress })..। -execute-only = केवल निष्पादित करें -details = विवरण -mount-error = ड्राइव तक पहुँचने में असमर्थ -removed-from-recents = - { recents } से { $items } { $items -> - [one] आइटम हटाया गया - *[other] आइटम हटाए गए - } -progress-paused = { $percent }%, रुका हुआ -cancelled = रद्द किया गया -operations-running-finished = - { $running } { $running -> - [one] ऑपरेशन - *[other] ऑपरेशन - } चल रहा है ({ $percent }%), { $finished } समाप्त..। -progress-failed = { $percent }%, विफल -extract-to-title = फ़ोल्डर में निकालें -open-with = इससे खोलें -permanently-deleted = - { $items } { $items -> - [one] आइटम स्थायी रूप से मिटाया गया - *[other] आइटम स्थायी रूप से मिटाए गए - } -write-execute = लिखें और निष्पादित करें -extract-password-required = पासवर्ड आवश्यक है -progress-cancelled = { $percent }%, रद्द किया गया -write-only = केवल लिखने के लिए -permanently-delete-warning = { $target } को स्थाई रूप से हटा दिया जाएगा। इस कार्रवाई को पूर्ववत नहीं किया जा सकता है। -favorite-path-error-description = - "{ $path }" को खोला नहीं जा सकता - "{ $path }" मौजूद नहीं हो सकता है या आपके पास इसे खोलने की अनुमति नहीं हो सकती है - - क्या आप इसे साइडबार से हटाना चाहते हैं? -empty-trash-title = रद्दी साफ़ करें? -permanently-delete-question = स्थाई रूप से हटाएं? -operations-running = - { $running } { $running -> - [one] ऑपरेशन - *[other] ऑपरेशन - } चल रहा है ({ $percent }%)..। -copy-to-title = कॉपी गंतव्य चुनें -copy-to-button-label = कॉपी -move-to-title = मूव गंतव्य चुनें -move-to-button-label = मूव -context-action = संदर्भ क्रिया -context-action-confirm-title = "{ $name }" चलाएँ? -context-action-confirm-warning = - यह { $items } { $items -> - [one] आइटम - *[other] आइटम - } पर चलेगा। -run = चलाएँ -rename-confirm = नाम बदलें -mixed = मिश्रित -pasted-image = चिपकाई गई छवि -pasted-text = चिपकाया गया पाठ -pasted-video = चिपकाया गया वीडियो +support = सहायता diff --git a/i18n/hu/cosmic_files.ftl b/i18n/hu/cosmic_files.ftl index fa15499..dcf9f84 100644 --- a/i18n/hu/cosmic_files.ftl +++ b/i18n/hu/cosmic_files.ftl @@ -2,7 +2,7 @@ cosmic-files = COSMIC Fájlok comment = Fájlkezelő a COSMIC asztali környezethez keywords = mappa;fájl;kezelő; empty-folder = Üres mappa -empty-folder-hidden = Üres mappa (rejtett elemeket tartalmaz) +empty-folder-hidden = Üres mappa (Rejtett elemeket tartalmaz) no-results = Nincs találat filesystem = Fájlrendszer home = Saját mappa @@ -17,7 +17,7 @@ desktop-view-options = Asztali nézet beállításai… show-on-desktop = Megjelenítés az asztalon desktop-folder-content = Asztal mappa tartalma mounted-drives = Csatolt meghajtók -trash-folder-icon = Kuka ikon +trash-folder-icon = Kuka ikonja icon-size-and-spacing = Ikonméret és távolság icon-size = Ikonméret grid-spacing = Rácsköz @@ -47,18 +47,18 @@ resume = Folytatás ## Compress Dialog -create-archive = Archívum létrehozása +create-archive = Tömörített fájl létrehozása ## Extract Dialog extract-password-required = Jelszó szükséges extract-to = Kibontás ide… -extract-to-title = Kibontási cél kiválasztása +extract-to-title = Kibontás mappába ## Empty Trash Dialog -empty-trash = A Kuka ürítése -empty-trash-warning = A Kukában lévő összes elem véglegesen törölve lesz +empty-trash = Kuka ürítése +empty-trash-warning = A kukában lévő összes elem véglegesen törölve lesz ## Mount Error Dialog @@ -93,7 +93,7 @@ save-file = Fájl mentése ## Open With Dialog -open-with-title = Hogyan szeretnéd megnyitni a(z) „{ $name }” fájlt? +open-with-title = Hogyan szeretnéd megnyitni a következőt: „{ $name }”? browse-store = { $store } böngészése other-apps = Egyéb alkalmazások related-apps = Hasonló alkalmazások @@ -103,7 +103,7 @@ related-apps = Hasonló alkalmazások selected-items = A(z) { $items } kijelölt elem permanently-delete-question = Végleges törlés? delete = Törlés -permanently-delete-warning = { $target } véglegesen törölve lesz. A művelet nem vonható vissza. +permanently-delete-warning = { $target } véglegesen törölve lesz. A művelet nem visszavonható. ## Rename Dialog @@ -113,7 +113,7 @@ rename-folder = Mappa átnevezése ## Replace Dialog replace = Csere -replace-title = „{ $filename }” már létezik ezen a helyen +replace-title = „{ $filename }” már létezik replace-warning = Szeretnéd lecserélni a meglévő fájlt a mentendő fájllal? A cserével felülírod annak tartalmát. replace-warning-operation = Szeretnéd lecserélni? A csere felülírja annak tartalmát. original-file = Eredeti fájl @@ -133,7 +133,7 @@ set-and-launch = Beállítás és indítás open-with = Megnyitás ezzel owner = Tulajdonos group = Csoport -other = Mások +other = Többi ### Mode 0 @@ -172,7 +172,7 @@ read-write-execute = Olvasás, írás és végrehajtás favorite-path-error = Hiba a könyvtár megnyitásakor favorite-path-error-description = Nem sikerült megnyitni ezt: „{ $path }” - A(z) „{ $path }” útvonal lehet, hogy nem létezik, vagy nincs jogosultságod a megnyitásához + „{ $path }” lehet nem létezik, vagy nincs jogosultságod a megnyitásához Szeretnéd eltávolítani az oldalsávról? remove = Eltávolítás @@ -187,9 +187,9 @@ keep = Megtartás ## Add Network Drive add-network-drive = Hálózati meghajtó hozzáadása -connect = Kapcsolódás -connect-anonymously = Kapcsolódás névtelenül -connecting = Kapcsolódás… +connect = Csatlakozás +connect-anonymously = Csatlakozás névtelenül +connecting = Csatlakozás… domain = Tartomány enter-server-address = Add meg a kiszolgáló címét network-drive-description = @@ -209,7 +209,7 @@ network-drive-schemes = network-drive-error = Nem érhető el a hálózati meghajtó password = Jelszó remember-password = Jelszó megjegyzése -try-again = Próbáld újra +try-again = Újra username = Felhasználónév ## Operations @@ -217,7 +217,7 @@ username = Felhasználónév cancelled = Megszakítva edit-history = Fájlműveleti előzmények history = Előzmények -no-history = Nem találhatók elemek az előzményekben. +no-history = Nem találhatók elemek az előzményekben pending = Függőben progress = { $percent }% progress-cancelled = { $percent }%, megszakítva @@ -251,13 +251,13 @@ deleting = { $items } { $items -> [one] elem *[other] elem - } törlése a Kukából ({ $progress })… + } törlése a kukából ({ $progress })… deleted = { $items } { $items -> [one] elem *[other] elem - } törölve a Kukából -emptying-trash = { trash } ürítése ({ $progress })… + } törölve a kukából +emptying-trash = { trash } kiürítése ({ $progress })… emptied-trash = { trash } kiürítve extracting = { $items } { $items -> @@ -309,12 +309,12 @@ restoring = { $items } { $items -> [one] elem *[other] elem - } visszaállítása a Kukából ({ $progress })… + } visszaállítása a kukából ({ $progress })… restored = { $items } { $items -> [one] elem *[other] elem - } visszaállítva a Kukából + } visszaállítva a kukából unknown-folder = ismeretlen mappa ## Open with @@ -326,11 +326,11 @@ default-app = { $name } (alapértelmezett) show-details = Részletek megjelenítése type = Típus: { $mime } -items = Elemek száma: { $items } +items = Elemek: { $items } item-size = Méret: { $size } item-created = Létrehozva: { $created } item-modified = Módosítva: { $modified } -item-accessed = Utolsó hozzáférés: { $accessed } +item-accessed = Hozzáférve: { $accessed } calculating = Számítás… ## Settings @@ -349,8 +349,8 @@ light = Világos ### Type to Search type-to-search = Gépeléssel keresés -type-to-search-recursive = Keresés a jelenlegi mappában és az almappákban -type-to-search-enter-path = Elérési út megnyitása +type-to-search-recursive = Keresés a jelenlegi mappában és almappákban +type-to-search-enter-path = Elérési út megadása # Context menu add-to-sidebar = Hozzáadás az oldalsávhoz compress = Tömörítés… @@ -360,8 +360,8 @@ extract-here = Kibontás new-file = Új fájl… new-folder = Új mappa… open-in-terminal = Megnyitás a terminálban -move-to-trash = Áthelyezés a Kukába -restore-from-trash = Visszaállítás a Kukából +move-to-trash = Kukába helyezés +restore-from-trash = Visszaállítás a kukából remove-from-sidebar = Eltávolítás az oldalsávról sort-by-name = Név szerinti rendezés sort-by-modified = Módosítás szerinti rendezés @@ -372,8 +372,8 @@ remove-from-recents = Eltávolítás a legutóbbiak közül ## Desktop change-wallpaper = Háttérkép cseréje… -desktop-appearance = Asztal megjelenése… -display-settings = Kijelzőbeállítások… +desktop-appearance = Asztali megjelenés… +display-settings = Képernyő-beállítások… # Menu @@ -406,7 +406,7 @@ grid-view = Rácsnézet list-view = Listanézet show-hidden-files = Rejtett fájlok megjelenítése list-directories-first = Könyvtárak listázása először -gallery-preview = Galéria-előnézet +gallery-preview = Galéria előnézet menu-settings = Beállítások… menu-about = A COSMIC Fájlok névjegye… @@ -415,14 +415,14 @@ menu-about = A COSMIC Fájlok névjegye… sort = Rendezés sort-a-z = A-Z sort-z-a = Z-A -sort-newest-first = Legújabb elöl -sort-oldest-first = Legrégibb elöl +sort-newest-first = Legújabb előre +sort-oldest-first = Legrégibb előre sort-smallest-to-largest = Legkisebbtől a legnagyobbig sort-largest-to-smallest = Legnagyobbtól a legkisebbig repository = Tároló support = Támogatás progress-failed = { $percent }%, sikertelen -empty-trash-title = Kiüríted a Kukát? +empty-trash-title = Kuka ürítése? type-to-search-select = Kijelöli az első egyező fájlt vagy mappát pasted-image = Beillesztett kép pasted-text = Beillesztett szöveg @@ -436,19 +436,3 @@ move-to = Áthelyezés ide… show-recents = Legutóbbiak mappa megjelenítése az oldalsávban copy-path = Útvonal másolása clear-recents-history = Legutóbbiak előzményének törlése -mixed = Vegyes -context-action = Helyi művelet -context-action-confirm-title = Futtatod ezt: „{ $name }”? -context-action-confirm-warning = - Ez a művelet { $items } { $items -> - [one] elemen - *[other] elemen - } fog lefutni. -run = Futtatás -rename-confirm = Átnevezés -checksum = { $kind }-ellenőrzőösszeg -calculate = Számítás -error = Hiba -sidebar-label = Oldalsávcímke -search-application = Keresés alkalmazásnév alapján -change-sidebar-label = Oldalsávcímke módosítása diff --git a/i18n/id/cosmic_files.ftl b/i18n/id/cosmic_files.ftl index f1203e7..9a1e8c4 100644 --- a/i18n/id/cosmic_files.ftl +++ b/i18n/id/cosmic_files.ftl @@ -249,7 +249,7 @@ settings = Pengaturan single-click = Klik sekali untuk membuka appearance = Tampilan theme = Tema -match-desktop = Cocok desktop +match-desktop = Cocokkan desktop dark = Gelap light = Terang type-to-search = Ketik untuk mencari @@ -318,19 +318,3 @@ comment = Pengelola berkas untuk desktop COSMIC show-recents = Map terbaru di bilah sisi clear-recents-history = Bersihkan riwayat Terbaru copy-path = Salin jalur -mixed = Bercampur -context-action = Tindakan konteks -context-action-confirm-title = Jalankan "{ $name }"? -run = Jalankan -context-action-confirm-warning = - Ini akan dijalankan pada { $items } { $items -> - [one] item - *[other] item - }. -rename-confirm = Ganti nama -checksum = { $kind } checksum -calculate = Hitung -error = Galat -sidebar-label = Label bilah sisi -search-application = Cari berdasarkan nama aplikasi -change-sidebar-label = Ubah label bilah sisi diff --git a/i18n/it/cosmic_files.ftl b/i18n/it/cosmic_files.ftl index 17abad0..e3766e5 100644 --- a/i18n/it/cosmic_files.ftl +++ b/i18n/it/cosmic_files.ftl @@ -1,5 +1,5 @@ cosmic-files = COSMIC Files -comment = File manager per il desktop COSMIC +comment = File manager di COSMIC keywords = File;Archivi;Cartelle;Explorer; empty-folder = Cartella vuota empty-folder-hidden = Cartella vuota (con elementi nascosti) @@ -7,7 +7,7 @@ no-results = Nessun risultato trovato filesystem = Filesystem home = Home networks = Reti -notification-in-progress = Operazioni sui file in corso +notification-in-progress = Operazioni sui file in corso. trash = Cestino recents = Recenti undo = Annulla @@ -322,7 +322,7 @@ single-click = Click singolo per aprire appearance = Aspetto theme = Tema -match-desktop = Abbina al desktop +match-desktop = Sistema dark = Scuro light = Chiaro @@ -422,4 +422,3 @@ removed-from-recents = [one] elemento *[other] elementi } da { recents } -run = Esegui diff --git a/i18n/kab/cosmic_files.ftl b/i18n/kab/cosmic_files.ftl index f0f776e..e759fa5 100644 --- a/i18n/kab/cosmic_files.ftl +++ b/i18n/kab/cosmic_files.ftl @@ -1,322 +1 @@ change-wallpaper = Beddel aɣrab n ugdil… -cosmic-files = Ifuyla COSMIC -empty-folder = Akaram d ilem -empty-folder-hidden = Akaram d ilem (yesɛa iferdisen yeffren) -no-results = Ulac igmaḍ yettwafen -home = Agejdan -networks = Iẓeḍwa -notification-in-progress = Timhalin ɣef ifuyla la tteddunt -trash = Iḍumman -recents = Melmi kan -undo = Ssemmet -today = Ass-a -desktop-view-options = Iɣewwaṛen n tmeẓri n tnarit… -show-on-desktop = Sken deg tnarit -desktop-folder-content = Agbur n ukaram n tnarit -trash-folder-icon = Tignit n ukaram n iḍumman -icon-size-and-spacing = Tiddi n tignit akked tallunt -icon-size = Tiddi n tignit -name = Isem -modified = Ittusnifel -trashed-on = Yettwakkes ɣer tqecwalt n yiḍumman -size = Tiddi -details = Ifatusen -pause = Serǧu -resume = Kemmel -create-archive = Snulfu-d aɣbaṛ -extract-to = Ssef ɣer... -extract-to-title = Ssef ɣer ukaram -empty-trash = Silem iḍumman -rename-folder = Snifel isem n ukaram -filesystem = Anagraw n yifuyla -dismiss = Zgel izen -empty-trash-title = Silem iḍumman? -empty-trash-warning = Iferdisen n ukaram n iḍumman ad ttwakksen i lebda -create-new-file = Snulfu-d afaylu amaynut -create-new-folder = Snulfu-d akaram amaynut -file-name = Isem n ufaylu -folder-name = Isem n ukaram -file-already-exists = Afaylu s yisem-agi yella yakan -folder-already-exists = Akaram s yisem-agi yella yakan -name-hidden = Ismawen ibeddun s "." ad ttwaffren -name-invalid = Isem ur yezmir ara ad yili "{ $filename }" -cancel = Semmet -create = Snulfu-d -open = Ldi -open-file = Ldi afaylu -open-folder = Ldi akaram -open-in-new-tab = Ldi deg yiccer amaynut -open-in-new-window = Ldi deg usfaylu amaynut -open-item-location = Ldi adig n uferdis -open-multiple-files = Ldi aget n ifuyla -mounted-drives = imeɣriyen yettuserkben -mount-error = Ulamek anekcum ɣer umeɣri -operations-running = - { $running } { $running -> - [one] n temhelt la tteddu - *[other] n temhal la tteddunt - } ({ $percent }%)... -operations-running-finished = - { $running } { $running -> - [one] n temhelt la tteddu - *[other] n temhal la tteddunt - } ({ $percent }%), { $finished } { $finished -> - [one] tfukk - *[other] fukkent - }... -copy-to-title = Fren taɣerwaḍt n unɣel -copy-to-button-label = Nɣel -move-to-title = Fren taɣerwaḍt n usmutti -move-to-button-label = Smutti -comment = Amsefrak n yifuyla i tnarit COSMIC -keywords = Akaram;Amsefrak; -delete = kkes -replace = Semselsi -support = Tallelt -settings = Iɣewwaṛen -appearance = Timeẓri -theme = Asentel -dark = Aɣmayan -light = Aceɛlal -paste = Senteḍ -select-all = Fren akk -zoom-in = Asemɣeṛ -default-size = Tiddi tamezwert -zoom-out = Asemẓi -view = Wali -menu-settings = Iɣewwaṛen… -save = Sekles -match-desktop = Semṣada akked tnarit -file = Afaylu -new-window = Asfaylu amaynut -quit = Ffeɣ -edit = Ẓreg -cut = Gzem -copy = Nɣel -repository = Asarsay -pasted-image = Tettwasenṭeḍ tugna -pasted-text = Yettwasenṭeḍ uḍris -pasted-video = Tettwasenṭeḍ tvidyut -compress = Skussem… -grid-spacing = Tallunt n iẓiki -extract-password-required = Awal uffir yettwasra -permanently-delete-question = Kkes s wudem imezgi? -permanently-delete-warning = { $target } ad yettwakkes s wudem imezgi. Tigawt-agi ur tezmir ara ad tettwasefsex. -rename-file = Snifel isem n ufaylu -replace-title = "{ $filename }" yella yakan deg wadig-a -replace-warning = Tebɣiḍ ad t-tsemselsiḍ s win ara teskelseḍ? Asemselsi-ines ad yaru sennig ugbur-is. -replace-warning-operation = Tebɣiḍ ad t-tsemselsiḍ? Asemselsi-ines ad yaru sennig ugbur-is. -original-file = Afaylu aneṣli -replace-with = Semselsi s -apply-to-all = Snes i meṛṛa -keep-both = Eǧǧ-iten deg sin -skip = Zgel -set-executable-and-launch = Sbeddet am umselkam syinna senker -set-executable-and-launch-description = Tebɣiḍ ad tesbeddeḍ "{ $name }" am umselkam syinna ad tessenkreḍ? -set-and-launch = Sbadu sakin senker -add-network-drive = Rnu ameɣri n uẓeṭṭa -connect = Qqen -connect-anonymously = Qqen s wudem udrig -connecting = Tuqqna… -domain = Taɣult -enter-server-address = Sekcem tansa n uqeddac -network-drive-description = - Tansiwin n uqeddac gebrent azwir n uneggaf akked tansa. - Imedyaten: ssh://192.168.0.1, ftp://[2001:db8::1] -network-drive-error = Ur izmir ara ad yekcem ɣer umeɣri n uzeṭṭa -password = Awal uffir -remember-password = Cfu ɣef wawal uffir -try-again = ɛreḍ tikelt nniḍen -username = Isem n useqdac -cancelled = Yettwasefsex -edit-history = Ẓreg amazray -history = Amazray -no-history = Ulac iferdisen deg umazray. -pending = Yettṛaǧu -progress-cancelled = { $percent }%, yettwasefsex -progress-failed = { $percent }%, ur yeddi ara -failed = Ur yeddi ara -renaming = Asnifel n yisem "{ $from }" ɣer "{ $to }" -renamed = Yettwasenfel yisem n "{ $from }" ɣer "{ $to }" -unknown-folder = akaram arussin -menu-open-with = Ldi s… -default-app = { $name } (amezwer) -show-details = Sken talqayt -type = Anaw: { $mime } -items = Iferdisen: { $items } -item-size = Tiddi: { $size } -item-created = Yettwarna: { $created } -item-modified = Ittusnifel: { $modified } -item-accessed = Yettwakcem: { $accessed } -calculating = Asiḍen… -single-click = Asiti asuf i ulday -type-to-search = Aru iwakken ad tnadiḍ -type-to-search-recursive = Nadi akaram amiran akked ikaramen inaddawen meṛṛa -type-to-search-enter-path = Sekcem abrid ɣer ukaram neɣ afaylu -add-to-sidebar = Rnu ɣer ufeggag adisan -delete-permanently = Kkes i lebda -grid-view = Askan s iẓiki -list-view = Askan s tebdart -show-hidden-files = Sken ifuyla uffiren -gallery-preview = Taskant n temidelt -menu-about = Ɣef Ifuyla COSMIC… -sort = Asmizzwer -sort-newest-first = Amaynut d amezwaru -sort-oldest-first = Aqbur d amezwaru -sort-smallest-to-largest = Seg umeẓyan akk ɣer umeqqran -sort-largest-to-smallest = Seg umeqqran akk ɣer umeẓyan -open-multiple-folders = Ldi usgit n ikaramen -save-file = Sekles afaylu -open-with-title = Amek tebɣiḍ ad teldiḍ "{ $name }"? -browse-store = Snirem { $store } -other-apps = Isnasen-nniḍen -emptying-trash = Silem { trash } ({ $progress })… -emptied-trash = D ilem { trash } -set-permissions = Sbadu isirigen i "{ $name }" ɣer { $mode } -eject = Ḍeqqer -extract-here = Ssef -new-file = Afaylu amaynut… -new-folder = Akaram amaynut… -open-in-terminal = Ldi deg yixef -move-to-trash = Smutti ɣer tqecwalt n yiḍumman -restore-from-trash = Err-d seg tqecwalt n yiḍumman -remove-from-sidebar = Kkes seg ugalis adisan -sort-by-name = Smizzwer s yisem -sort-by-modified = Asmizzwer s usnifel -sort-by-size = Asmizzwer s tiddi -sort-by-trashed = Asmizzwer s wakud n tukksa -remove-from-recents = Kkes seg ineggura -desktop-appearance = Timeẓri n tnarit… -display-settings = Iɣewwaṛen n ubeqqeḍ... -new-tab = Iccer amaynut -reload-folder = Ales asali n ukaram -rename = Snifel isem... -close-tab = Mdel iccer -list-directories-first = Sken di tazwara ikaramen -open-with = Ldi s -owner = Bab -group = Agraw -other = Ayen nniḍen -none = Ula Yiwen -execute-only = Selkem kan -write-only = Aru kan -write-execute = Aru u selkem -read-only = I tɣuri kan -read-execute = Ɣeṛ u selkem -read-write = Ɣeṛ u aru -read-write-execute = Ɣeṛ, aru, u selkem -favorite-path-error = Tuccḍa deg ulday n ukaram -remove = Kkes -keep = Eǧǧ -progress = { $percent }% -progress-paused = { $percent }%, ibedd -complete = Immed -copy_noun = Nɣel -creating = Asnulfu n "{ $name }" deg "{ $parent }" -created = Yettwarna "{ $name }" deg "{ $parent }" -setting-executable-and-launching = Asbadu n "{ $name }" am umselkam syin ad yettwasekker -set-executable-and-launched = Sbadu "{ $name }" am umselkam syin sekker-it -setting-permissions = Asbadu n tsirag i "{ $name }" ɣer { $mode } -related-apps = Isnasen icudden -favorite-path-error-description = - Ur izmir ara ad yeldi "{ $path }" - "{ $path }" yezmer lḥal ulac-it neɣ ahat ur tesɛiḍ ara tisirag akken ad t-teldiḍ - - Tebɣiḍ ad t-tekkseḍ seg ufeggag adisan? -compressing = - La issekkussum { $items } { $items -> - [one] n uferdis - *[other] n yiferdisen - } seg "{ $from }" ɣer "{ $to }" ({ $progress })... -compressed = - Yekussem { $items } { $items -> - [one] n uferdis - *[other] n yiferdisen - } seg "{ $from }" ɣer "{ $to }" -copying = - Anɣal { $items } { $items -> - [one] n uferdis - *[other] n yiferdisen - } seg "{ $from }" ɣer "{ $to }" ({ $progress })... -copied = - Yettwanɣel { $items } { $items -> - [one] n uferdis - *[other] n yiferdisen - } seg "{ $from }" ɣer "{ $to }" -deleting = - Tukksa { $items } { $items -> - [one] n uferdis - *[other] n yiferdisen - } seg { trash } ({ $progress })... -deleted = - Yettwakkes { $items } { $items -> - [one] n uferdis - *[other] n yiferdisen - } seg { trash } -extracting = - Tussfa { $items } { $items -> - [one] n uferdis - *[other] n yiferdisen - } seg "{ $from }" ɣer "{ $to }" ({ $progress })... -extracted = - Yettusef { $items } { $items -> - [one] n uferdis - *[other] n yiferdisen - } seg "{ $from }" ɣer "{ $to }" -moving = - Asmutti { $items } { $items -> - [one] n uferdis - *[other] n yiferdisen - } seg "{ $from }" ɣer "{ $to }" ({ $progress })... -moved = - Yettwasenkez { $items } { $items -> - [one] n uferdis - *[other] n yiferdisen - } seg "{ $from }" ɣer "{ $to }" -permanently-deleting = - Tukksa i lebda { $items } { $items -> - [one] n uferdis - *[other] n yiferdisen - } -permanently-deleted = - I lebda yettwakkes { $items } { $items -> - [one] n uferdis - *[other] n yiferdisen - } -removing-from-recents = - Tukksa { $items } { $items -> - [one] n uferdis - *[other] n yiferdisen - } seg { recents } -removed-from-recents = - Yettwakkes { $items } { $items -> - [one] n uferdis - *[other] n yiferdisen - } seg { recents } -restoring = - Tiririt { $items } { $items -> - [one] n uferdis - *[other] n yiferdisen - } seg { trash } ({ $progress })… -restored = - Yettwarr-d { $items } { $items -> - [one] n uferdis - *[other] n yiferdisen - } seg { trash } -network-drive-schemes = - Ineggafen iwejden, azwir - AppleTalk,afp:// - Aneggaf n usiweḍ n yifuyla,ftp:// neɣ ftps:// - Anagraw n yifuyla n uzeṭṭa,nfs:// - Iḥder n yizen n uqeddac,smb:// - SSH Aneggaf n usiweḍ n yifuyla,sftp:// neɣ ssh:// - WebDAV:// neɣ davs:// -sort-a-z = A-Ẓ -sort-z-a = Ẓ-A -selected-items = { $items } n yiferdisen yettwafernen -type-to-search-select = Fren afaylu amezwaru neɣ akaram yemṣadan -copy-to = Nɣel ɣer... -move-to = Smutti ɣer… -show-recents = Akaram n melmi kan deg ufeggag adisan -clear-recents-history = Sfeḍ azray n melmi kan -copy-path = Nɣel abrid diff --git a/i18n/kk/cosmic_files.ftl b/i18n/kk/cosmic_files.ftl index c2b4cb1..6afb314 100644 --- a/i18n/kk/cosmic_files.ftl +++ b/i18n/kk/cosmic_files.ftl @@ -22,7 +22,7 @@ name = Аты modified = Өзгертілген trashed-on = Қоқыс шелегіне тасталған size = Өлшемі -details = Толық ақпарат +details = Ақпараты dismiss = Хабарламаны елемеу operations-running = { $running } { $running -> @@ -246,17 +246,17 @@ item-accessed = Қол жеткізілген: { $accessed } calculating = Есептеу... settings = Баптаулар single-click = Ашу үшін бір рет шерту -appearance = Сыртқы көрініс -theme = Тема -match-desktop = Жұмыс үстеліне сәйкес болу -dark = Қою +appearance = Сыртқы түрі +theme = Тақырып +match-desktop = Жұмыс үстеліне сәйкес келу +dark = Күңгірт light = Ашық type-to-search = Іздеу үшін теру type-to-search-recursive = Ағымдағы бума мен барлық ішкі бумаларды іздейді type-to-search-enter-path = Бумаға немесе файлға жолды енгізеді type-to-search-select = Бірінші сәйкес келетін файлды немесе буманы таңдайды add-to-sidebar = Бүйірлік панельге қосу -compress = Сығу... +compress = Сығу delete-permanently = Біржолата өшіру eject = Шығару extract-here = Тарқату @@ -318,16 +318,3 @@ keywords = Folder;Manager;Бума;Басқарушы; show-recents = Бүйір панеліндегі «Жуырдағы құжаттар» бумасы clear-recents-history = Жуырдағылар тарихын өшіру copy-path = Орналасқан жолын көшіру -mixed = Аралас -context-action = Контекст әрекеті -context-action-confirm-title = "{ $name }" орындау керек пе? -context-action-confirm-warning = - Бұл { $items } орындалады { $items -> - [one] нәрсеге - *[other] нәрсеге - }. -run = Орындау -rename-confirm = Атын өзгерту -checksum = { $kind } бақылау сомасы -calculate = Есептеу -error = Қате diff --git a/i18n/kmr/cosmic_files.ftl b/i18n/kmr/cosmic_files.ftl index b15e92d..e69de29 100644 --- a/i18n/kmr/cosmic_files.ftl +++ b/i18n/kmr/cosmic_files.ftl @@ -1,336 +0,0 @@ -sort-newest-first = Pêşî ya herî nû -connect = Girê bide -deleted = - { $items } hat jêbirin{ $items -> - [one] hêman - *[other] hêman - } ji { trash } -dismiss = Peyamê paşguh bike -list-view = Dîtina lîsteyê -copy_noun = Jê bigire -favorite-path-error = Şaşetiya vekirina rêgehê -progress = { $percent }% -open-file = Pelê veke -remove-from-sidebar = Ji benda kêlekê rake -related-apps = Bernameyên têkildar -network-drive-error = Nikaribû bigihêje ajokarê torê -gallery-preview = Pêşdîtina galeriyê -sort-smallest-to-largest = Biçûktir bo mezintir -zoom-in = Nêzîk bike -select-all = Hemûyan hilbijêre -removing-from-recents = - { $items } tê rakirin { $items -> - [one] hêman - *[other] hêman - } ji { recents } -password = Borînpeyv -cosmic-files = Pelên COSMIC -emptying-trash = { trash } tê valakirin({ $progress })... -new-window = Çarçoveya nû -remove = Rake -zoom-out = Dûr bike -compressing = - { $items } { $items -> - [one] hêman - *[other] hêman - } tê guvaştin ji "{ $from }" bo "{ $to }" ({ $progress })... -move-to-trash = Bilivîne bo jêbirdankê -menu-about = Derbarê Pelên COSMIC... -file-already-exists = Pelek bi heman navî jixwe heye -setting-executable-and-launching = "{ $name }" wekî pelê xebitandinê tê sazkirin û xebitandin -open-multiple-files = Gelek pelan veke -default-size = Mezinahiya berdest -name-hidden = Navên ku bi "." tê destpêkirin wê werin veşartin -extracted = - { $items } { $items -> - [one] hêman - *[other] hêman - } hate derxistin ji "{ $from }" bo "{ $to }" -create-new-folder = Peldanka nû biafrîne -folder-already-exists = Peldankek bi heman navî jixwe heye -create-archive = Erşîvê biafrîne -permanently-delete-warning = { $target } wê bi mayînde were jêbirin û nayê vegerandin. -favorite-path-error-description = - Nikaribû "{ $path }" veke. - "{ $path }" dibe ku tune be yan jî mafê te yê - vekirina wê tune be. - Tu dixwazî wê ji benda kêlekê rakî? -empty-trash-warning = Hêmanên di jêbirdankê de wê bi mayînde werin jêbirin -other-apps = Bernameyên din -set-permissions = Maf hatine sazkirin ji bo "{ $name }" bo { $mode } -quit = Biqedîne -sort-by-size = Li gorî mezinahiyê rêz bike -rename = Navê biguhêrîne... -empty-folder-hidden = Peldanka vala (tiştên veşartî tê de hene) -keep = Bihêle -operations-running = - { $running } { $running -> - [one] pêvajo - *[other] pêvajo - } dixebite ({ $percent }%)... -permanently-deleting = - { $items } bi mayînde tê jêbirin { $items -> - [one] hêman - *[other] hêman - } -edit = Biguhêrîne -copy = Jê bigire -none = Ne yek -no-results = Tu encam nehatin dîtin -theme = Rûkar -compressed = - { $items } { $items -> - [one] hêman - *[other] hêman - } hate guvaştin ji "{ $from }" bo "{ $to }" -appearance = Xuyang -rename-folder = Navê peldankê biguherîne -new-file = Pela nû... -close-tab = Rûgerê bigire -open-in-terminal = Di termînalê de veke -open-multiple-folders = Gelek peldankan veke -remember-password = Borînpeyvê bi bîr bîne -username = Navê bikarhêner -light = Ronî -extract-to = Derxîne bo... -add-network-drive = Ajokarê torê tevlî bike -copying = - { $items } { $items -> - [one] hêman - *[other] hêman - } tê jêgirtin ji "{ $from }" bo "{ $to }" ({ $progress })... -create-new-file = Pelê nû biafrîne -sort-by-trashed = Li gorî demê rêz bike -support = Piştgirî -try-again = Dîsa hewl bide -eject = Biavêje -copied = - { $items } { $items -> - [one] hêman - *[other] hêman - } hate jêgirtin ji "{ $from }" bo "{ $to }" -open-in-new-window = Di çarçoveyeke nû de veke -empty-folder = Peldanka vala -empty-trash = Jêbirdankê vala bike -sort-by-modified = Li gorî guhertinê rêz bike -list-directories-first = Pêşî rêgehan lîste bike -folder-name = Navê peldankê -browse-store = { $store } bigere -remove-from-recents = Ji dawiyê rake -paste = Pê ve bike -empty-trash-title = Jêbirdankê vala bike? -menu-settings = Sazkarî... -moving = - { $items } { $items -> - [one] hêman - *[other] hêman - } tê livandin ji "{ $from }" bo "{ $to }" ({ $progress })... -change-wallpaper = Wêneyê dîwêr biguhêrîne... -network-drive-description = - Di nav navnîşanên rajekarê de pêşgira protokolekê û navnîşanek heye. - Mînak: ssh://192.168.0.1, ftp://[2001:db8::1] -deleting = - { $items } tê jêbirin { $items -> - [one] hêman - *[other] hêman - } ji { trash } ({ $progress })... -single-click = Tikandina yekane bo vekriinê -view = Dîtin -setting-permissions = Maf tên sazkirin ji bo "{ $name }" bo { $mode } -creating = "{ $name }" tê afirandin di "{ $parent }" de -execute-only = Tenê xebitandin -open-item-location = Cihê hêmanê veke -type-to-search = Bo lêgerînê binivîse -set-executable-and-launched = "{ $name }" wekî pelê xebitandinê hat sazkirin û xebitandin -mounted-drives = Ajokarên siwarkirî -sort-a-z = A-Z -mount-error = Nikare bigihêje ajokerê -extract-here = Derxîne -grid-view = Dîtina torê -add-to-sidebar = Tevlî benda kêlekê bike -dark = Tarî -notification-in-progress = Pêvajoyên pelê didome -home = Rûpela sereke -sort-z-a = Z-A -type-to-search-recursive = Di peldank û binpeldankên heyî de digere -history = Dîrok -progress-paused = { $percent }%, hate rawestandin -desktop-view-options = Vebijêrkên dîtina sermaseyê... -show-on-desktop = Li ser sermaseyê nîşan bide -cancelled = Têkbirî -new-folder = Peldanka nû... -match-desktop = Bi sermaseyê re li hev bîne -name-no-slashes = Nav nabe ku "/" tê de hebe -operations-running-finished = - { $running } { $running -> - [one] pêvajo - *[other] pêvajo - } dixebite ({ $percent }%), { $finished } qediya... -sort-by-name = Li gorî navê rêz bike -edit-history = Dîrokê biguhêrînê -show-hidden-files = Pelên veşartî nîşan bide -progress-failed = { $percent }%, têk çû -trash-folder-icon = Îkona peldanka jêbirdankê -extract-to-title = Derxîne bo peldankê -icon-size = Mezinahiya îkonê -open-with-title = Tu çawa dixwazî "{ $name }" vekî? -extracting = - { $items } { $items -> - [one] hêman - *[other] hêman - } tê derxistin ji "{ $from }" bo"{ $to }" ({ $progress })... -settings = Sazkarî -complete = Qediya -permanently-delete-question = Bi mayînde jê bibe? -extract-password-required = Borînpeyv pêdivî ye -pending = Li bendê ye -desktop-folder-content = Naveroka peldanka sermaseyê -no-history = Tu hêman di dîrok de tune ne. -open-folder = Peldankê veke -emptied-trash = { trash } hate valakirin -rename-file = Navê pelê biguherîne -sort-largest-to-smallest = Mezintir bo biçûktir -restore-from-trash = Ji jêbirdankê vegerîne -cut = Jê bike -moved = - { $items } { $items -> - [one] hêman - *[other] hêman - } hate livandin ji "{ $from }" bo "{ $to }" -progress-cancelled = { $percent }%, hat têkbirin -open-in-new-tab = Di rûgerê nû de veke -file = Pel -file-name = Navê pelê -save-file = Pelê tomar bike -created = "{ $name }" hate afirandin di "{ $parent }" de -delete-permanently = Bi mayînde jê bibe -display-settings = Sazkariyên dîmenderê... -new-tab = Rûgera nû -failed = Têk çû -name-invalid = Nav nabe ku \"{ $filename }\" be -desktop-appearance = Xuyanga sermaseyê... -comment = Rêveberê pelê ji bo sermaseya COSMIC -keywords = Rêveber;Peldank; -filesystem = Pergala pelê -networks = Tor -recents = Dawî -open = Veke -cancel = Têk bibe -trash = Jêbirdank -undo = Vegerîne -today = Îro -name = Nav -modified = Guhertin -size = Mezinahî -details = Hûrgilî -pause = Rawestîne -resume = Bidomîne -copy-to-title = Rêgeha jêgirtinê hilbijêre -copy-to-button-label = Jê bigire -move-to-title = Rêgeha livandinê hilbijêre -move-to-button-label = Bilivîne -create = Afirandin -save = Tomar bike -context-action = Çalakiya naverokê -context-action-confirm-title = "{ $name }" bixebtîne? -context-action-confirm-warning = - Ev ê li ser { $items } { $items -> - [one] hêman - *[other] hêman - } bixebite. -run = Bixebitîne -selected-items = { $items } hêmanên hilbijartî -delete = Jê bibe -rename-confirm = Navê biguherîne -pasted-image = Wêneya pêvekirî -pasted-text = Nivîsa pêvekirî -pasted-video = Vîdiyoya pêvekirî -copy-path = Rêgehê jê bigire -show-recents = Peldanka dawî di benda kêlekê de -clear-recents-history = Dîroka dawî pak bike -compress = Biguvşîne... -copy-to = Jê bigire bo... -move-to = Bilivîne bo... -default-app = { $name } (berdest) -renamed = Nav hate guhertin ji "{ $from }" bo "{ $to }" -read-execute = Xwendin û xebitandin -item-modified = Guhertin: { $modified } -reload-folder = Peldankê ji nû ve bar bike -restoring = - { $items } tê vegerandin { $items -> - [one] hêman - *[other] hêman - } ji { trash } ({ $progress })... -icon-size-and-spacing = Mezinahiya îkonan û valahiya di navbera wan de -type-to-search-enter-path = Riya rêgehê yan jî pelê têdixîne -menu-open-with = Veke bi riya... -original-file = Pelê resen -read-write-execute = Xwendin, nivîsandin û xebitandin -calculating = Tê jimartin... -item-size = Mezinahî: { $size } -connecting = Tê girêdan... -read-write = Xwendin û nivîsandin -items = Hêman: { $items } -type = Cûre: { $mime } -replace-warning = Tu dixwazî bi ya ku tu niha tomar dikî biguhêrînî? Naveroka berê wê were jêbirin. -show-details = Hûrgiliyan nîşan bide -grid-spacing = Valahiya torê -sort-oldest-first = Pêşî ya herî kevn -repository = Embar -replace-warning-operation = Tu dixwazî biguherînî? Naveroka berê wê were jêbirin. -other = Ên din -read-only = Tenê xwendin -enter-server-address = Navnîşana rajekarê têxîne -connect-anonymously = Bi awayekî anonîm girê bide -group = Kom -apply-to-all = Bo hemûyan bisepîne -skip = Derbas bike -replace-with = Biguhêrîne bi -owner = Xwedî -set-and-launch = Saz bike û dest pê bike -removed-from-recents = - { $items } hate rakirin { $items -> - [one] hêman - *[other] hêman - } ji { recents } -item-created = Afirandin:{ $created } -network-drive-schemes = - Protokolên berdest,pêşgirê - AppleTalk,afp:// - Protokola şandina pelê,ftp:// an ftps:// - Pergala pelê torê,nfs:// - Astengiya peyama rajekarê,smb:// - SSH protokola şandina pelê,sftp:// an ssh:// - WebDAV, dav:// an davs:// -set-executable-and-launch = Wekî pelê xebitandinê saz bike û bixebitîne -restored = - { $items } hate vegerandin { $items -> - [one] hêman - *[other] hêman - } ji { trash } -domain = Navper -sort = Rêz bike -item-accessed = Gihîştin: { $accessed } -open-with = Veke bi riya -keep-both = Herduyan bihêle -permanently-deleted = - { $items } bi mayînde hate jêbirin{ $items -> - [one] hêman - *[other] hêman - } -write-execute = Nivîsandin û xebitandin -renaming = Navê ji "{ $from }" biguhêrîne bo "{ $to }" -set-executable-and-launch-description = Tu dixwazî "{ $name }" wekî pelê xebitandinê saz bikî û bixebtînî? -replace = Biguhêrîne -replace-title = "{ $filename }" jixwe di vê cihê de heye -unknown-folder = Peldanka nenas -write-only = Tenê nivîsandin -trashed-on = Jêbirî -mixed = Tevlihev -type-to-search-select = Pel an peldanka yekem a lihevhatî hildibijêre -calculate = Bijmêre -error = Şaşetî -checksum = Tevahiya { $kind } kontrol bike -sidebar-label = Nîşanka benda kêlekê -search-application = Li gorî navê sepanê bigere -change-sidebar-label = Nîşanka benda kêlekê biguhêrîne diff --git a/i18n/ko/cosmic_files.ftl b/i18n/ko/cosmic_files.ftl index 0441da5..aec0e3f 100644 --- a/i18n/ko/cosmic_files.ftl +++ b/i18n/ko/cosmic_files.ftl @@ -110,7 +110,7 @@ dismiss = 메시지 무시 copy_noun = 복사 progress = { $percent }% related-apps = 관련 앱 -compress = 압축... +compress = 압축 network-drive-error = 네트워크 드라이브에 접근할 수 없음 icon-size-and-spacing = 아이콘 크기 및 간격 password = 암호 @@ -272,18 +272,3 @@ network-drive-schemes = WebDAV,dav:// 또는 davs:// type-to-search-select = 일치하는 첫 번째 파일 또는 폴더를 선택합니다 comment = COSMIC 데스크톱용 위한 파일 관리자 -keywords = 폴더;관리자; -copy-to-button-label = 복사 -move-to-button-label = 이동 -clear-recents-history = 최근 기록 비우기 -copy-path = 복사 경로 -move-to-title = 이동 위치 선택 -copy-to-title = 복사 경로 선택 -run = 실행 -rename-confirm = 이름 변경 -mixed = 혼합됨 -pasted-image = 붙여넣은 이미지 -pasted-text = 붙여넣은 텍스트 -pasted-video = 붙여넣은 영상 -copy-to = 복사하기... -move-to = 이동하기... diff --git a/i18n/lo/cosmic_files.ftl b/i18n/lo/cosmic_files.ftl deleted file mode 100644 index e69de29..0000000 diff --git a/i18n/lt/cosmic_files.ftl b/i18n/lt/cosmic_files.ftl index 96b6ee1..8faf6d7 100644 --- a/i18n/lt/cosmic_files.ftl +++ b/i18n/lt/cosmic_files.ftl @@ -1,5 +1,5 @@ progress = { $percent }% -cosmic-files = COSMIC Failai +cosmic-files = Cosmic Files empty-folder = Tuščias aplankas empty-folder-hidden = Tuščias aplankas (turi paslėptų failų) no-results = Rezultatų nėra @@ -76,7 +76,7 @@ delete = Ištrinti permanently-delete-warning = { $target } bus ištrintas visam laikui. Šis veiksmas yra negrįžtamas. rename-file = Pervadinti failą rename-folder = Pervadinti aplanką -replace = Keisti +replace = Pakeisti replace-title = „{ $filename }“ jau egzistuoja šioje vietoje replace-warning = Ar norite pakeisti tai su tuo, kas yra įrašoma? Keičiant bus pakeistas turinys. replace-warning-operation = Ar norite pakeisti tai? Pakeičiant bus keičiamas turinys. @@ -244,7 +244,7 @@ item-created = Sukurtas: { $created } item-modified = Modifikuota: { $modified } item-accessed = Paskutinė prieiga: { $accessed } calculating = Skaičiuojama... -settings = Nuostatai +settings = Nustatymai single-click = Vieno paspaudimo atidarymas appearance = Išvaizda match-desktop = Pagal darbalaukio temą @@ -252,7 +252,7 @@ type-to-search = Norint ieškoti, pradėkite rašyti type-to-search-recursive = Paieška dabartiniame aplanke ir jo poaplankiuose type-to-search-enter-path = Įvedamas aplanko ar failo kelias add-to-sidebar = Pridėti į šonjuostę -compress = Suspausti... +compress = Suspausti delete-permanently = Ištrinti visam laikui eject = Išstumti extract-here = Išskleisti @@ -276,22 +276,22 @@ new-window = Naujas langas reload-folder = Perkrauti aplanką rename = Pervadinti... close-tab = Uždaryti skirtuką -quit = Išjungti +quit = Išeiti edit = Redaguoti cut = Iškirpti copy = Kopijuoti -paste = Įdėti -select-all = Žymėti viską -zoom-in = Artinti +paste = Įklijuoti +select-all = Pažymėti viską +zoom-in = Priartinti default-size = Numatytas dydis -zoom-out = Tolinti +zoom-out = Nutolinti view = Rodymas grid-view = Tinklelio išdėstymas list-view = Sąrašo išdėstymas show-hidden-files = Rodyti paslėptus failus list-directories-first = Pirmiau pateikti aplankus gallery-preview = Galerijos peržiūra -menu-settings = Nuostatai... +menu-settings = Nustatymai... menu-about = Apie COSMIC Files... sort = Rikiuoti sort-a-z = A-Ž @@ -302,7 +302,7 @@ sort-smallest-to-largest = Nuo mažiausio iki didžiausio sort-largest-to-smallest = Nuo didžiausio iki mažiausio dark = Tamsus light = Šviesus -comment = COSMIC aplinkos failų tvarkyklė +comment = COSMIC desktop failų tvarkyklė keywords = Aplankas;Tvarkyklė; copy-to-title = Pasirinkti kopijavimo vietą copy-to-button-label = Kopijuoti @@ -317,10 +317,3 @@ clear-recents-history = Išvalyti Neseniai naudotų istoriją copy-to = Kopijuoti į... move-to = Perkeltiį į... copy-path = Kopijuoti kelią -theme = Stilius -context-action = Konteksto veiksmas -context-action-confirm-title = Paleisti „{ $name }“? -run = Paleisti -rename-confirm = Pervadinti -calculate = Skaičiuoti -error = Klaida diff --git a/i18n/nb-NO/cosmic_files.ftl b/i18n/nb-NO/cosmic_files.ftl index cae2b5b..e0fb82c 100644 --- a/i18n/nb-NO/cosmic_files.ftl +++ b/i18n/nb-NO/cosmic_files.ftl @@ -98,7 +98,7 @@ permanently-delete-question = Slett permanent permanently-delete-warning = Er du sukker på om du vil permanent slette { $target }? Dette kan ikke angres. rename-file = Endre filnavn rename-folder = Endre mappenavn -replace-title = «{ $filename }» finnes allerede på denne plasseringen +replace-title = "{ $filename }" finnes allerede på denne plasseringen. replace-warning = Vil du bytte den ut med filen du lagrer? Dette vil overskrive innholdet. replace-warning-operation = Vil du erstatte den? Dette vil overskrive innholdet. original-file = Orginalfil @@ -122,8 +122,8 @@ read-write = Kun skrive- og lesbar read-write-execute = Lesbar, skrivbar og kjørbar favorite-path-error = Feil ved åpning av mappen favorite-path-error-description = - Klarte ikke åpne «{ $path }». - «{ $path }» finnes ikke eller så har du ikke tilgang til å åpne den. + Kunne ikke åpne "{ $path }". + Den kan ikke eksistere eller så har du ikke tilgang til å åpne den. Vil du fjerne den fra sidepanelet? keep = Behold @@ -140,7 +140,7 @@ zoom-out = Zoom ut cut = Klipp ut set-and-launch = Still inn og start domain = Domene -enter-server-address = Angi tjeneradresse +enter-server-address = Angi serveradresse network-drive-description = Serveradresser inkluderer ett protokollprefiks og en addresse. Examples: ssh://192.168.0.1, ftp://[2001:db8::1] diff --git a/i18n/nl/cosmic_files.ftl b/i18n/nl/cosmic_files.ftl index aa4ef5a..2be219f 100644 --- a/i18n/nl/cosmic_files.ftl +++ b/i18n/nl/cosmic_files.ftl @@ -425,7 +425,7 @@ removed-from-recents = } uit { recents } verwijderd remove-from-recents = Uit recente verwijderen type-to-search-select = Dit selecteert het eerst overeenkomende bestand of map -comment = Bestandsbeheerder voor COSMIC +comment = Bestandsbeheerder voor COSMIC desktop copy-to-title = Kopieerbestemming aanwijzen copy-to-button-label = Kopiëren move-to-button-label = Verplaatsen diff --git a/i18n/oc/cosmic_files.ftl b/i18n/oc/cosmic_files.ftl index 71afc45..e69de29 100644 --- a/i18n/oc/cosmic_files.ftl +++ b/i18n/oc/cosmic_files.ftl @@ -1,33 +0,0 @@ -appearance = Aparéncia -theme = Tèma -match-desktop = Assortir al burèu -dark = Escur -light = Clar -file = Fichièr -details = Detalhs -name = Nom -cancel = Anullar -repository = Depaus -settings = Configuracion -menu-settings = Configuracion... -quit = Quitar -cosmic-files = Fichièrs de COSMIC -comment = Gestionari de fichièrs pel burèu COSMIC -save-file = Enregistrar lo fichièr -open-with-title = Cossí volètz dobrir « { $name } » ? -other-apps = Autras aplicacions -context-action-confirm-title = Executar « { $name } » ? -run = Executar -delete = Suprimir -replace = Remplaçar -owner = Propietari -group = Grop -write-execute = Escritura e execucion -read-only = Lectura sola -read-execute = Lectura e execucion -read-write = Lectura e execucion -read-write-execute = Lectura, escritura e execucion -remove = Suprimir -connecting = Connexion... -domain = Domeni -password = Senhal diff --git a/i18n/pa/cosmic_files.ftl b/i18n/pa/cosmic_files.ftl index aae4541..e5c829f 100644 --- a/i18n/pa/cosmic_files.ftl +++ b/i18n/pa/cosmic_files.ftl @@ -32,7 +32,7 @@ pause = ਵਿਰਾਮ resume = ਮੁੜ-ਚਾਲੂ create-archive = ਅਕਾਇਵ ਬਣਾਓ extract-password-required = ਪਾਸਵਰਡ ਚਾਹੀਦਾ ਹੈ -extract-to = ਖਿਲਾਰੋ... +extract-to = Extract To... extract-to-title = ਫੋਲਡਰ ਵਿੱਚ ਖਿਲਾਰੋ empty-trash = ਰੱਦੀ ਨੂੰ ਖਾਲੀ ਕਰੋ empty-trash-title = ਰੱਦੀ ਨੂੰ ਖਾਲੀ ਕਰਨਾ ਹੈ? @@ -113,7 +113,7 @@ replace-title = "{ $filename }" ਪਹਿਲਾਂ ਹੀ ਇਸ ਟਿਕਾਣ favorite-path-error = ਡਾਇਰੈਕਟਰੀ ਖੋਲ੍ਹਣ ਦੌਰਾਨ ਗਲਤੀ add-network-drive = ਨੈੱਟਵਰਕ ਡਰਾਇਵ ਜੋੜੋ connect-anonymously = ਅਣਪਛਾਤੇ ਵਜੋਂ ਕਨੈਕਟ ਕਰੋ -connecting = ਕਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ... +connecting = Connecting... domain = ਡੋਮੇਨ enter-server-address = ਸਰਵਰ ਦਾ ਸਿਰਨਾਵਾਂ ਦਿਓ password = ਪਾਸਵਰਡ @@ -138,15 +138,15 @@ sort-by-modified = ਸੋਧ ਨਾਲ ਲੜੀਬੱਧ sort-by-size = ਆਕਾਰ ਨਾਲ ਲੜੀਬੱਧ sort-by-trashed = ਹਟਾਉਣ ਸਮੇਂ ਨਾਲ ਲੜੀਬੱਧ remove-from-recents = ਸੱਜਰਿਆਂ ਵਿੱਚੋਂ ਹਟਾਓ -change-wallpaper = ਵਾਲਪੇਪਰ ਨੂੰ ਬਦਲੋ... -desktop-appearance = ਡੈਸਕਟਾਪ ਦੀ ਦਿੱਖ... -display-settings = ਡਿਸਪਲੇਅ ਸੈਟਿੰਗਾਂ... +change-wallpaper = Change wallpaper... +desktop-appearance = Desktop appearance... +display-settings = Display settings... file = ਫ਼ਾਇਲ new-tab = ਨਵੀਂ ਟੈਬ new-window = ਨਵੀਂ ਵਿੰਡੋ rename = ਨਾਂ ਨੂੰ ਬਦਲੋ... close-tab = ਟੈਬ ਨੂੰ ਬੰਦ ਕਰੋ -quit = ਸਮਾਪਤ +quit = ਬਾਹਰ edit = ਸੋਧੋ cut = ਕੱਟੋ copy = ਕਾਪੀ ਕਰੋ @@ -188,18 +188,18 @@ item-accessed = ਪਹੁੰਚ: { $accessed } calculating = ਗਿਣਤੀ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ... single-click = ਇੱਕ ਵਾਰ ਕਲਿੱਕ ਕਰਕੇ ਖੋਲ੍ਹੋ show-recents = ਬਾਹੀ ਵਿੱਚ ਸੱਜਰੇ ਫੋਲਡਰ -match-desktop = ਡੈਸਕਟਾਪ ਨਾਲ ਮਿਲਾਓ +match-desktop = ਡੈਸਕਟਾਪ ਨਾਲ ਮਿਲਦਾ dark = ਗੂੜ੍ਹਾ -light = ਫਿੱਕਾ +light = ਹਲਕਾ type-to-search = ਖੋਜਣ ਲਈ ਲਿਖੋ type-to-search-recursive = ਮੌਜੂਦਾ ਫੋਲਡਰ ਅਤੇ ਸਭ ਅਧੀਨ-ਫੋਲਡਰਾਂ ਵਿੱਚ ਖੋਜੋ add-to-sidebar = ਬਾਹੀ ਵਿੱਚ ਜੋੜੋ compress = ਕੰਪਰੈਸ -copy-to = ਇੱਥੇ ਕਾਪੀ ਕਰੋ... +copy-to = Copy to... delete-permanently = ਪੱਕੇ ਤੌਰ ਉੱਤੇ ਹਟਾਓ eject = ਬਾਹਰ extract-here = ਖਿਲਾਰੋ -move-to = ਇੱਥੇ ਭੇਜੋ... +move-to = Move to... remove-from-sidebar = ਬਾਹੀ ਵਿੱਚੋਂ ਹਟਾਓ reload-folder = ਫੋਲਡਰ ਨੂੰ ਮੁੜ-ਲੋਡ ਕਰੋ list-view = ਸੂਚੀ ਝਲਕ diff --git a/i18n/pl/cosmic_files.ftl b/i18n/pl/cosmic_files.ftl index e8d6ab2..daf1007 100644 --- a/i18n/pl/cosmic_files.ftl +++ b/i18n/pl/cosmic_files.ftl @@ -1,5 +1,5 @@ cosmic-files = Pliki COSMIC -comment = Menedżer plików dla środowiska COSMIC +comment = Menedżer plików pulpitu COSMIC keywords = Katalogi;Pliki;Menedżer; empty-folder = Pusty katalog empty-folder-hidden = Pusty katalog (z ukrytymi plikami) @@ -27,7 +27,7 @@ modified = Zmodyfikowano trashed-on = Wyrzucono do kosza size = Rozmiar # Progress footer -details = Szczegóły +details = Detale dismiss = Odrzuć wiadomość operations-running = { $running } bieżące { $running -> @@ -390,7 +390,7 @@ new-window = Nowe okno reload-folder = Odśwież katalog rename = Zmień nazwę… close-tab = Zamknij kartę -quit = Zakończ +quit = Zamknij ## Edit @@ -437,19 +437,3 @@ move-to = Przenieś do… show-recents = Ostatnie katalogi w panelu bocznym clear-recents-history = Wyczyść bierzącą historię copy-path = Skopiuj ścieżkę -mixed = Mieszane -context-action = Akcja sytuacyjna -context-action-confirm-title = Uruchomić "{ $name }"? -context-action-confirm-warning = - Zostanie uruchomionych { $items } { $items -> - [one] element - *[other] elementów - }. -run = Uruchom -rename-confirm = Zmień nazwę -checksum = { $kind } suma kontrolna -calculate = Oblicz -error = Błąd -sidebar-label = Etykieta panelu bocznego -search-application = Wyszukaj po nazwie aplikacji -change-sidebar-label = Zmień etykietę panelu bocznego diff --git a/i18n/pt-BR/cosmic_files.ftl b/i18n/pt-BR/cosmic_files.ftl index fd10ac3..732d0fb 100644 --- a/i18n/pt-BR/cosmic_files.ftl +++ b/i18n/pt-BR/cosmic_files.ftl @@ -109,10 +109,6 @@ permanently-delete-warning = Deseja realmente excluir permanentemente { $target rename-file = Renomear arquivo rename-folder = Renomear pasta -## Change Sidebar Label Dialog - -sidebar-label = Legenda da barra lateral - ## Replace Dialog replace = Substituir @@ -354,7 +350,7 @@ light = Estilo claro ### Type to Search -type-to-search = Digitar para pesquisar +type-to-search = Digite para pesquisar type-to-search-recursive = Pesquisa na pasta atual e em todas as subpastas type-to-search-enter-path = Insere o caminho do diretório ou arquivo # Context menu @@ -369,7 +365,6 @@ open-in-terminal = Abrir no terminal move-to-trash = Mover para a lixeira restore-from-trash = Restaurar da lixeira remove-from-sidebar = Remover da barra lateral -change-sidebar-label = Alterar legenda da barra lateral sort-by-name = Ordenar por nome sort-by-modified = Ordenar por data de modificação sort-by-size = Ordenar por tamanho @@ -393,7 +388,7 @@ new-window = Nova janela reload-folder = Recarregar pasta rename = Renomear... close-tab = Fechar aba -quit = Encerrar +quit = Sair ## Edit @@ -429,8 +424,8 @@ sort-largest-to-smallest = Do maior para o menor empty-trash-title = Esvaziar a lixeira? type-to-search-select = Seleciona o primeiro arquivo ou pasta correspondente pasted-image = Imagem colada -pasted-text = Texto colado -pasted-video = Vídeo colado +pasted-text = Texto copiado +pasted-video = Vídeo copiado copy-to-title = Selecione o destino da cópia copy-to-button-label = Copiar move-to-title = Selecione o destino da movimentação @@ -438,20 +433,6 @@ move-to-button-label = Mover copy-to = Copiar para... move-to = Mover para... keywords = Pasta;Gerenciador; -show-recents = Pasta de recentes na barra lateral +show-recents = Pasta Recentes na barra lateral clear-recents-history = Limpar histórico de recentes copy-path = Copiar caminho -mixed = Misto -context-action = Ação de contexto -context-action-confirm-title = Executar "{ $name }"? -context-action-confirm-warning = - Isso será executado em { $items } { $items -> - [one] item - *[other] itens - }. -run = Executar -rename-confirm = Renomear -checksum = Soma de verificação { $kind } -calculate = Calcular -error = Erro -search-application = Pesquisar por nome de aplicativo diff --git a/i18n/pt/cosmic_files.ftl b/i18n/pt/cosmic_files.ftl index b0c7e30..33921f1 100644 --- a/i18n/pt/cosmic_files.ftl +++ b/i18n/pt/cosmic_files.ftl @@ -1,12 +1,12 @@ cosmic-files = Ficheiros COSMIC comment = Gerenciador de arquivos do COSMIC -keywords = Pasta;Gestor; +keywords = Pastas;Gerenciador;Arquivos;Gestor;Explorer; empty-folder = Pasta vazia empty-folder-hidden = Pasta vazia (tem ficheiros ocultos) no-results = Nenhum resultado encontrado filesystem = Sistema de ficheiros home = Pasta Pessoal -notification-in-progress = Operações em curso +notification-in-progress = Operações em curso. trash = Lixo undo = Anular # List view @@ -204,7 +204,7 @@ desktop-view-options = Opções de visualização da área de trabalho... show-on-desktop = Mostrar na área de trabalho desktop-folder-content = Conteúdo da pasta da área de trabalho mounted-drives = Dispositivos montados -trash-folder-icon = Ícone da pasta lixo +trash-folder-icon = Ícone do lixo icon-size-and-spacing = Tamanho e espaçamento do ícone icon-size = Tamanho do ícone grid-spacing = Espaçamento entre ícones @@ -352,6 +352,3 @@ sort-newest-first = Mais recentes primeiro sort-oldest-first = Mais antigos primeiro sort-smallest-to-largest = Do menor para o maior sort-largest-to-smallest = Do maior para o menor -context-action-confirm-title = Executar "{ $name }"? -run = Executar -rename-confirm = Renomear diff --git a/i18n/ru/cosmic_files.ftl b/i18n/ru/cosmic_files.ftl index 185421f..15b8eb1 100644 --- a/i18n/ru/cosmic_files.ftl +++ b/i18n/ru/cosmic_files.ftl @@ -174,7 +174,6 @@ unknown-folder = неизвестная папка menu-open-with = Открыть с помощью… default-app = { $name } (по умолчанию) -search-application = Поиск по названию приложения ## Show details @@ -189,14 +188,14 @@ settings = Параметры ### Appearance -appearance = Внешний вид +appearance = Оформление theme = Тема match-desktop = Как в системе dark = Тёмная light = Светлая # Context menu add-to-sidebar = Добавить на боковую панель -compress = Сжать... +compress = Сжать extract-here = Распаковать new-file = Новый файл… new-folder = Новая папка… @@ -219,7 +218,7 @@ new-tab = Новая вкладка new-window = Новое окно rename = Переименовать… close-tab = Закрыть вкладку -quit = Завершить +quit = Выйти ## Edit @@ -272,8 +271,8 @@ extract-to = Распаковать в… extract-to-title = Распаковать в папку mount-error = Не удалось получить доступ к диску create = Создать -open-with-title = Как хотите открыть «{ $name }»? -browse-store = Перейти в { $store } +open-with-title = Как вы хотите открыть «{ $name }»? +browse-store = Искать в { $store } other-apps = Другие приложения related-apps = Связанные приложения selected-items = { $items } выделенных элем. @@ -321,7 +320,7 @@ type-to-search-enter-path = Ввод пути к каталогу или фай delete-permanently = Удалить навсегда eject = Извлечь remove-from-recents = Убрать из недавних -change-wallpaper = Изменить фон... +change-wallpaper = Изменить обои… desktop-appearance = Параметры оформления… display-settings = Параметры экрана… reload-folder = Обновить папку @@ -381,18 +380,3 @@ keywords = Папка;Менеджер; show-recents = «Недавние документы» в бок. панели clear-recents-history = Очистить историю недавних copy-path = Копировать путь -mixed = Смешанные -context-action = Контекстная команда -context-action-confirm-title = Запустить «{ $name }»? -context-action-confirm-warning = - Команда затронет { $items } { $items -> - [one] элемент - *[other] элем. - }. -run = Запустить -rename-confirm = Переименовать -checksum = Хеш-сумма { $kind } -calculate = Вычислить -error = Ошибка -sidebar-label = Подпись в боковой панели -change-sidebar-label = Изменить подпись diff --git a/i18n/sat/cosmic_files.ftl b/i18n/sat/cosmic_files.ftl deleted file mode 100644 index 61dcbc2..0000000 --- a/i18n/sat/cosmic_files.ftl +++ /dev/null @@ -1,3 +0,0 @@ -cancel = ᱵᱟᱹᱛᱤᱞ -name = ᱧᱩᱛᱩᱢ -save = ᱥᱟᱧᱪᱟᱣ diff --git a/i18n/sr/cosmic_files.ftl b/i18n/sr/cosmic_files.ftl index 98a9e35..e69de29 100644 --- a/i18n/sr/cosmic_files.ftl +++ b/i18n/sr/cosmic_files.ftl @@ -1,330 +0,0 @@ -open-file = Отвори датотеку -quit = Изађи -cancel = Откажи -open = Отвори -run = Покрени -connect = Повежи -save = Сачувај -password = Лозинка -remove = Уклони -appearance = Изглед -username = Корисничко име -light = Светла -dark = Тамна -settings = Подешавања -replace = Замени -size = Величина -sort-newest-first = Најновије прво -default-app = { $name } (подразумевано) -renamed = Преименована „{ $from }“ у „{ $to }“ -read-execute = Читање и извршавање -deleted = - Обрисано је { $items } { $items -> - [one] ставка - *[other] ставки - } из { trash } -item-modified = Измењено: { $modified } -dismiss = Одбаци поруку -list-view = Преглед у виду списака -reload-folder = Поново учитај фасциклу -copy_noun = Копија -favorite-path-error = Грешка при отварању директоријума -progress = { $percent }% -remove-from-sidebar = Уклони из бочне траке -related-apps = Повезани програми -restoring = - Враћање { $items } { $items -> - [one] ставке - *[other] ставки - } из { trash } ({ $progress })... -network-drive-error = Немогуће приступити мрежном уређају -gallery-preview = Преглед галерије -sort-smallest-to-largest = Од најмање до највеће -zoom-in = Увећајте приказ -select-all = Означи све -icon-size-and-spacing = Величина и размак иконица -removing-from-recents = - Уклањање { $items } { $items -> - [one] ставке - *[other] ставки - } из { recents } -cosmic-files = Космик датотеке -type-to-search-enter-path = Уноси путању до директоријума или датотеке -trash = Смеће -emptying-trash = Пражњење { trash } ({ $progress })... -trashed-on = Премештено у смеће -new-window = Нови прозор -zoom-out = Умањите приказ -compressing = - Сажимање { $items } { $items -> - [one] ставке - *[other] ставки - } из „{ $from }“ у „{ $to }“ ({ $progress })... -move-to-trash = Премести у смеће -menu-about = О програму Космик Датотеке... -setting-executable-and-launching = Подешавање „{ $name }“ као извршне датотеке и покретање -open-multiple-files = Отвори више датотека -default-size = Подразумевана величина -menu-open-with = Отвори програмом... -extracted = - Извлачено { $items } { $items -> - [one] ставка - *[other] ставки - } из „{ $from }“ у „{ $to }“ -create-new-folder = Направи нову фасциклу -original-file = Изворна датотека -create = Направи -create-archive = Направи архиву -read-write-execute = Читање, уписивање и извршавање -other-apps = Остали програми -set-permissions = Овлашћења за „{ $name }“ су постављена на { $mode } -pause = Паузирај -calculating = Израчунавам… -sort-by-size = Поређај по величини -rename = Преименуј... -empty-folder-hidden = Празна фасцикла (има сакривених ставки) -keep = Задржи -item-size = Величина: { $size } -permanently-deleting = - Трајно брисање { $items } { $items -> - [one] ставке - *[other] ставки - } -edit = Уреди -connecting = Повезујем се... -read-write = Читање и уписивање -copy = Умножи -none = Ништа -items = Ставки: { $items } -no-results = Нису пронађени резултати -theme = Тема -type = Врста: { $mime } -compressed = - Сажето { $items } { $items -> - [one] ставка - *[other] ставки - } из „{ $from }“ у „{ $to }“ -replace-warning = Да ли желите да га замените оним који снимате? Замена ће преписати његов садржај. -rename-folder = Преименуј фасциклу -new-file = Нова датотека... -close-tab = Затвори језичак -name = Назив -open-in-terminal = Отвори у терминалу -resume = Настави -open-multiple-folders = Отвори више фасцикла -remember-password = Запамти лозинку -show-details = Прикажи детаље -grid-spacing = Размак мреже -extract-to = Распакуј у... -add-network-drive = Додај мрежни уређај -copying = - Умножавање { $items } { $items -> - [one] ставке - *[other] ставки - } из „{ $from }“ у „{ $to }“ ({ $progress })... -delete = Обриши -sort-oldest-first = Најстарије прво -repository = Ризница -create-new-file = Направи нову датотеку -sort-by-trashed = Поређај по времену брисања -replace-warning-operation = Да ли желите да га замените? Замена ће преписати његов садржај. -support = Подршка -try-again = Покушај поново -eject = Избаци -copied = - Умножено { $items } { $items -> - [one] ставке - *[other] ставки - } из „{ $from }“ у „{ $to }“ -other = Друго -open-in-new-window = Отвори у новом прозору -empty-folder = Празна фасцикла -sort-by-modified = Поређај по датуму измене -list-directories-first = Прикажи директоријуме прво -read-only = Само за читање -folder-name = Назив фасцикле -browse-store = Разгледај { $store } -enter-server-address = Унесите адресу сервера -remove-from-recents = Уклони из недавних -connect-anonymously = Повежи се анонимно -group = Група -apply-to-all = Примени на све -skip = Прескочи -paste = Залепи -menu-settings = Подешавања... -moving = - Премештање { $items } { $items -> - [one] датотеке - *[other] датотека - } из „{ $from }“ у „{ $to }“ ({ $progress })... -replace-with = Замени са -recents = Недавно -change-wallpaper = Промени позадину... -network-drive-description = - Адресе сервера укључују префикс протокола и адресу. - Примери: ssh://192.168.0.1, ftp://[2001:db8::1] -deleting = - Брише се { $items } { $items -> - [one] ставка - *[other] ставки - } из { trash } ({ $progress })... -single-click = Један клик за отварање -view = Преглед -undo = Опозови -setting-permissions = Подешавање овлашћења за „{ $name }“ на { $mode } -owner = Власник -creating = Правим „{ $name }“ у „{ $parent }“ -execute-only = Само извршавање -open-item-location = Отвори локацију ставке -details = Детаљи -set-executable-and-launched = Постављено „{ $name }“ као извршну датотеку и покренуто -mounted-drives = Прикључени дискови -sort-a-z = А-Ш -mount-error = Немогуће приступити уређају -extract-here = Извуци -grid-view = Преглед у виду мреже -filesystem = Систем датотека -set-and-launch = Подеси и покрени -removed-from-recents = - Уклоњено { $items } { $items -> - [one] ставке - *[other] ставки - } из { recents } -add-to-sidebar = Додај у страничник -item-created = Направљено: { $created } -network-drive-schemes = - Доступни протоколи,Префикс - ЕплТок,afp:// - Протокол за пренос датотека,ftp:// или ftps:// - Мрежни систем датотека,nfs:// - Серверски блок порука,smb:// - SSH протокол за пренос датотека,sftp:// или ssh:// - ВебДАВ,dav:// или davs:// -home = Лична -set-executable-and-launch = Постави као извршну и покрени -restored = - Враћено { $items } { $items -> - [one] ставка - *[other] ставки - } из { trash } -sort-z-a = Ш-А -type-to-search-recursive = Претражује тренутну фасциклу и све подфасцикле -history = Историјат -progress-paused = { $percent }%, паузирано -desktop-view-options = Могућности приказа радне површине... -show-on-desktop = Прикажи на радној површини -cancelled = Отказано -new-folder = Нова фасцикла... -match-desktop = Прати радну површину -domain = Домен -operations-running-finished = - { $running } { $running -> - [one] радња покренута - *[other] радње покренуте - } ({ $percent }%), { $finished } завршено... -sort-by-name = Поређај по називу -edit-history = Историјат уређивања -sort = Поређај -show-hidden-files = Прикажи скривене датотеке -progress-failed = { $percent }%, није успело -trash-folder-icon = Иконица фасцикле Смеће -item-accessed = Приступљено: { $accessed } -extract-to-title = Распакуј у фасциклу -open-with = Отвори помоћу -keep-both = Задржи оба -icon-size = Величина иконице -open-with-title = Како желите да отворите „{ $name }“? -extracting = - Извлачење { $items } { $items -> - [one] ставке - *[other] ставки - } из „{ $from }“ у „{ $to }“ ({ $progress })... -permanently-deleted = - Трајно обрисано { $items } { $items -> - [one] ставке - *[other] ставки - } -complete = Завршено -write-execute = Писање и извршавање -extract-password-required = Потребна је лозинка -pending = У току -desktop-folder-content = Садржај фасцикле радне површине -renaming = Преименовање „{ $from }“ у „{ $to }“ -set-executable-and-launch-description = Да ли желите да поставите „{ $name }“ као извршну и покренете је? -no-history = Нема ставки у историјату. -open-folder = Отвори фасциклу -emptied-trash = Опражњено { trash } -rename-file = Преименуј датотеку -sort-largest-to-smallest = Од највеће до најмање -restore-from-trash = Врати из смећа -cut = Исеци -moved = - Премештено { $items } { $items -> - [one] датотеке - *[other] датотека - } из „{ $from }“ у „{ $to }“ -progress-cancelled = { $percent }%, отказано -open-in-new-tab = Отвори у новом језичку -unknown-folder = непозната фасцикла -file = Датотека -file-name = Назив датотеке -save-file = Сачувај датотеку -created = Направљено „{ $name }“ у „{ $parent }“ -delete-permanently = Трајно обриши -networks = Мреже -write-only = Само писање -today = Данас -display-settings = Подешавања екрана... -new-tab = Нови језичак -failed = Неуспешно -modified = Измењено -desktop-appearance = Изглед радне површине... -file-already-exists = Датотека са овим називом већ постоји -name-hidden = Називи који почињу тачком „.“ ће бити сакривени -folder-already-exists = Фасцикла са овим називом већ постоји -permanently-delete-warning = { $target } ће бити трајно обрисано. Ова радња се не може поништити. -favorite-path-error-description = - Не можемо да отворимо „{ $path }“ - „{ $path }“ можда не постоји или немате дозволу за његово отварање - - Желите ли да га уклоните из бочне површи? -empty-trash-warning = Ставке у смећу биће трајно обрисане -empty-trash = Испразни смеће -empty-trash-title = Испразнити смеће? -type-to-search = Куцајте за претрагу -notification-in-progress = Радње са датотекама су у току -name-no-slashes = Назив не може садржати косе црте -permanently-delete-question = Трајно обриши? -replace-title = „{ $filename }“ већ постоји на овој локацији -name-invalid = Назив не може бити „{ $filename }“ -operations-running = - { $running } { $running -> - [one] радња покренута - *[other] радње покренуте - } ({ $percent }%)... -comment = Управник датотека за Космик радну површину -keywords = Folder;Manager;Фасцикла;Управник;fascikla;upravnik; -copy-to-title = Изабери одредиште умножавања -copy-to-button-label = Умножи -move-to-title = Изабери одредиште премештања -move-to-button-label = Помери -context-action = Контекстна радња -context-action-confirm-title = Покрени „{ $name }“? -context-action-confirm-warning = - Ово ће се извршити на { $items } { $items -> - [one] ставку - *[other] ставки - }. -selected-items = Изабраних { $items } ставки -mixed = Помешано -pasted-image = Убачена слика -pasted-text = Убачен текст -pasted-video = Убачен видео -show-recents = Недавна фасцикла у бочној површи -type-to-search-select = Обира прву подударајућу датотеку или фасциклу -clear-recents-history = Очисти историју недавних -compress = Сажми... -copy-to = Умножи у... -move-to = Помери у... -copy-path = Умножи путању -rename-confirm = Преименуј diff --git a/i18n/sv/cosmic_files.ftl b/i18n/sv/cosmic_files.ftl index 4714773..2527c4e 100644 --- a/i18n/sv/cosmic_files.ftl +++ b/i18n/sv/cosmic_files.ftl @@ -101,7 +101,6 @@ open-with = Öppna med owner = Ägare group = Grupp other = Andra -mixed = Blandade # Listvy name = Namn modified = Ändrad @@ -319,7 +318,7 @@ list-view = Listvy show-hidden-files = Visa dolda filer list-directories-first = Lista mappar först gallery-preview = Galleri förhandsvisning -menu-settings = Inställningar... +menu-settings = Inställningar… menu-about = Om COSMIC Filer... ## Sortera @@ -410,18 +409,3 @@ move-to = Flytta till... show-recents = Mapp för senast använda filer i sidofältet clear-recents-history = Töm historik för Senaste copy-path = Kopiera sökväg -context-action = Kontextåtgärd -context-action-confirm-title = Kör "{ $name }"? -context-action-confirm-warning = - Detta kommer att köras på { $items } { $items -> - [one] objekt - *[other] objekt - }. -run = Kör -rename-confirm = Byt namn -checksum = { $kind }-kontrollsumma -calculate = Beräkna -error = Fel -sidebar-label = Sidofältsetikett -search-application = Sök på programnamn -change-sidebar-label = Ändra sidofältsetikett diff --git a/i18n/th/cosmic_files.ftl b/i18n/th/cosmic_files.ftl index 86a20b3..c0da7bc 100644 --- a/i18n/th/cosmic_files.ftl +++ b/i18n/th/cosmic_files.ftl @@ -202,9 +202,17 @@ moved = *[other] ไฟล์ } จาก "{ $from }" สู่ "{ $to }" renaming = กำลังเปลี่ยนชื่อจาก "{ $from }" เป็น "{ $to }" -renamed = เปลี่ยนชื่อจาก "{ $from }" เป็น "{ $to }" แล้ว -restoring = กำลังกู้คืน { $items } รายการจาก { trash } ({ $progress })... -restored = กู้คืน { $items } รายการจาก { trash } แล้ว +renamed = Renamed "{ $from }" to "{ $to }" +restoring = + Restoring { $items } { $items -> + [one] item + *[other] items + } from { trash } ({ $progress })... +restored = + Restored { $items } { $items -> + [one] item + *[other] items + } from { trash } unknown-folder = แฟ้มที่ไม่รู้จัก ## Open with @@ -232,8 +240,8 @@ settings = การตั้งค่า appearance = ลักษณะ theme = ธีม match-desktop = ใช้ตามธีมหน้าจอหลัก -dark = มืด -light = สว่าง +dark = ธีมมืด +light = ธีมสว่าง # Context menu add-to-sidebar = เพิ่มเข้าแถบด้านข้าง compress = บีบอัด @@ -298,4 +306,3 @@ sort-newest-first = ไฟล์ใหม่ก่อน sort-oldest-first = ไฟล์เก่าก่อน sort-smallest-to-largest = ขนาดเล็กก่อน sort-largest-to-smallest = ขนาดใหญ่ก่อน -remove = ลบ diff --git a/i18n/uk/cosmic_files.ftl b/i18n/uk/cosmic_files.ftl index 8a9c9d6..1413460 100644 --- a/i18n/uk/cosmic_files.ftl +++ b/i18n/uk/cosmic_files.ftl @@ -5,7 +5,7 @@ filesystem = Файлова система home = Домівка trash = Смітник recents = Нещодавні -undo = Скасувати +undo = Відмінити # List view name = Назва modified = Змінено @@ -82,11 +82,10 @@ copying = Копіювання { $items } { $items -> [one] елемента *[other] елементів - } з «{ $from }» до «{ $to }» ({ $progress })... + } з «{ $from }» в «{ $to }» ({ $progress })... copied = Скопійовано { $items } { $items -> [one] елемент - [few] елементи *[other] елеменів } з «{ $from }» в «{ $to }» emptying-trash = Спорожнення { trash } ({ $progress })... @@ -95,12 +94,11 @@ moving = Переміщення { $items } { $items -> [one] елемента *[other] елементів - } з «{ $from }» до «{ $to }» ({ $progress })... + } з «{ $from }» в «{ $to }» ({ $progress })... moved = Переміщено { $items } { $items -> [one] елемент - [few] елементи - *[other] елементів + *[other] елементи } з «{ $from }» в «{ $to }» renaming = Перейменування «{ $from }» на «{ $to }» renamed = Перейменовано «{ $from }» на «{ $to }» @@ -112,8 +110,7 @@ restoring = restored = Відновлено { $items } { $items -> [one] елемент - [few] елементи - *[other] елементів + *[other] елементи } з { trash } unknown-folder = невідома тека @@ -158,7 +155,7 @@ new-tab = Нова вкладка new-window = Нове вікно rename = Перейменувати... close-tab = Закрити вкладку -quit = Завершити +quit = Вийти ## Edit @@ -182,7 +179,7 @@ menu-settings = Налаштування... menu-about = Про Файли COSMIC... repository = Сховище support = Підтримка -details = Подробиці +details = Деталі dismiss = Закрити повідомлення remove = Вилучити cancelled = Скасовані @@ -226,7 +223,7 @@ permanently-delete-question = Остаточно видалити? delete = Видалити permanently-delete-warning = { $target } буде остаточно видалено. Цю дію не можна скасувати. set-executable-and-launch = Зробити виконуваним і запустити -set-executable-and-launch-description = Бажаєте зробити «{ $name }» виконуваним і запустити його? +set-executable-and-launch-description = Бажаєте зробити "{ $name }" виконуваним і запустити його? set-and-launch = Зробити і запустити open-with = Відкрити за допомогою owner = Власник @@ -248,7 +245,7 @@ favorite-path-error-description = Вилучити з бічної панелі? keep = Залишити add-network-drive = Додати мережевий диск -connect = З’єднати +connect = З'єднати connect-anonymously = З'єднатись анонімно connecting = З'єднання… domain = Домен @@ -277,13 +274,12 @@ compressing = Стиснення { $items } { $items -> [one] елемента *[other] елементів - } з «{ $from }» до «{ $to }» ({ $progress })... + } з "{ $from }" до "{ $to }" ({ $progress })... compressed = Стиснуто { $items } { $items -> [one] елемент - [few] елементи - *[other] елементів - } з «{ $from }» до «{ $to }» + *[other] елементи + } з "{ $from }" до "{ $to }" deleting = Видалення { $items } { $items -> [one] елемента @@ -292,19 +288,17 @@ deleting = deleted = Видалено { $items } { $items -> [one] елемент - [few] елементи *[other] елементи } з { trash } extracting = Видобування { $items } { $items -> [one] елемента *[other] елементів - } з «{ $from }» до «{ $to }» ({ $progress })... + } з «{ $from }» в «{ $to }» ({ $progress })... extracted = Видобуто { $items } { $items -> [one] елемент - [few] елементи - *[other] елементів + *[other] елементи } з «{ $from }» в «{ $to }» setting-executable-and-launching = Надання «{ $name }» прав на виконання та запуск set-executable-and-launched = «{ $name }» надано права на виконання і відкрито @@ -323,7 +317,7 @@ single-click = Відкривати одним клацанням type-to-search = Введіть для пошуку type-to-search-recursive = Шукає у поточній теці та всіх підтеках type-to-search-enter-path = Вводить шлях до каталогу або файлу -compress = Стиснути... +compress = Стиснути delete-permanently = Остаточно видалити eject = Безпечно вилучити extract-here = Видобути @@ -349,8 +343,7 @@ permanently-deleting = permanently-deleted = Остаточно вилучено { $items } { $items -> [one] елемент - [few] елементи - *[other] елементів + *[other] елементи } removing-from-recents = Вилучення { $items } { $items -> @@ -360,14 +353,13 @@ removing-from-recents = removed-from-recents = Вилучено { $items } { $items -> [one] елемент - [few] елементи - *[other] елементів + *[other] елементи } з { recents } empty-trash-title = Спорожити смітник? -type-to-search-select = Вибирає перший відповідний файл або теку -pasted-image = Вставлене зображення -pasted-text = Вставлений текст -pasted-video = Вставлене видиво +type-to-search-select = Вибирає перший відповідний файл або папку +pasted-image = Вставлене Зображення +pasted-text = Вставлений Текст +pasted-video = Вставлене Видиво copy-to-button-label = Копіювати move-to-button-label = Перемістити copy-to = Копіювати до… @@ -379,19 +371,3 @@ keywords = Тека;Папка;Провідник;Менеджер;Катало show-recents = Тека «Нещодавні» на бічній панелі copy-path = Копіювати шлях clear-recents-history = Очистити нещодавні -context-action-confirm-title = Запустити «{ $name }»? -run = Виконати -context-action-confirm-warning = - Запуститься для { $items } { $items -> - [one] елемента - *[other] елементів - }. -context-action = Контекстна дія -rename-confirm = Перейменувати -mixed = Змішано -checksum = контрольна сума { $kind } -calculate = Вирахувати -error = Помилка -sidebar-label = Назва бічної панелі -search-application = Пошук за назвою застосунку -change-sidebar-label = Змінити назву бічної панелі diff --git a/i18n/zh-CN/cosmic_files.ftl b/i18n/zh-CN/cosmic_files.ftl index cc674a3..0002c17 100644 --- a/i18n/zh-CN/cosmic_files.ftl +++ b/i18n/zh-CN/cosmic_files.ftl @@ -11,7 +11,7 @@ recents = 最近访问 undo = 撤销 today = 今天 # Desktop view options -desktop-view-options = 桌面视图选项… +desktop-view-options = 桌面视图选项... show-on-desktop = 在桌面显示 desktop-folder-content = 桌面文件夹内容 mounted-drives = 已装载驱动器 @@ -25,13 +25,13 @@ modified = 修改时间 trashed-on = 删除时间 size = 大小 # Progress footer -details = 详情 +details = 详细信息 dismiss = 清除消息 operations-running = 正在进行 { $running } { $running -> [one] 个操作 *[other] 个操作 - }({ $percent }%)… + }({ $percent }%)... operations-running-finished = 正在进行 { $running } { $running -> [one] 个操作 @@ -50,7 +50,7 @@ create-archive = 创建压缩包 ## Extract Dialog extract-password-required = 需要密码 -extract-to = 提取到… +extract-to = 提取到... extract-to-title = 提取到文件夹 ## Empty Trash Dialog @@ -167,10 +167,10 @@ read-write-execute = 读取、写入和执行 ## Favorite Path Error Dialog -favorite-path-error = 打开目录时出错 +favorite-path-error = 打开路径时出错 favorite-path-error-description = - 无法打开“{ $path }” 。 - “{ $path }”可能不存在或您没有权限打开它。 + 无法打开 "{ $path }" 。 + "{ $path }" 可能不存在或您没有权限打开它。 您想要从侧边栏中移除它吗? remove = 移除 @@ -267,10 +267,10 @@ extracted = [one] 项目 *[other] 项目 }到“{ $to }” -setting-executable-and-launching = 设置 “{ $name }” 为可执行文件并启动 -set-executable-and-launched = 已设 “{ $name }” 为可执行文件并且启动 -setting-permissions = 正在设置 “{ $name }” 的权限为 { $mode } -set-permissions = 设置 “{ $name }” 的权限为 { $mode } +setting-executable-and-launching = 设置 "{ $name }" 为可执行文件并启动 +set-executable-and-launched = 已设 "{ $name }" 为可执行文件并且启动 +setting-permissions = 正在设置 "{ $name }" 的权限为 { $mode } +set-permissions = 设置 "{ $name }" 的权限为 { $mode } moving = 正在从“{ $from }”移动 { $items } { $items -> [one] 项目 @@ -302,7 +302,7 @@ removed-from-recents = *[other] 项目 } renaming = 正在将“{ $from }”重命名为“{ $to }” -renamed = 已重命名 “{ $from }” 为 “{ $to }” +renamed = 已重命名"{ $from }"为"{ $to }" restoring = 正在从{ trash }中还原 { $items } { $items -> [one] 项目 @@ -317,7 +317,7 @@ unknown-folder = 未知文件夹 ## Open with -menu-open-with = 打开方式… +menu-open-with = 打开方式... default-app = { $name }(默认) ## Show details @@ -329,7 +329,7 @@ item-size = 文件大小:{ $size } item-created = 创建于:{ $created } item-modified = 修改于:{ $modified } item-accessed = 访问于:{ $accessed } -calculating = 计算中… +calculating = 计算中... ## Settings @@ -348,15 +348,15 @@ light = 亮色模式 type-to-search = 输入即可搜索 type-to-search-recursive = 搜索当前文件夹及其所有子文件夹 -type-to-search-enter-path = 输入文件夹或文件目录 +type-to-search-enter-path = 输入文件夹或文件路径 # Context menu add-to-sidebar = 加入侧边栏 compress = 压缩… delete-permanently = 永久删除 eject = 弹出 extract-here = 解压到此处 -new-file = 新建文件… -new-folder = 新建文件夹… +new-file = 新建文件... +new-folder = 新建文件夹... open-in-terminal = 在终端模拟器中打开 move-to-trash = 移动到回收站 restore-from-trash = 从回收站中还原 @@ -369,9 +369,9 @@ remove-from-recents = 从最近访问中移除 ## Desktop -change-wallpaper = 更改壁纸… -desktop-appearance = 桌面外观… -display-settings = 显示设置… +change-wallpaper = 更改壁纸... +desktop-appearance = 桌面外观... +display-settings = 显示设置... # Menu @@ -382,7 +382,7 @@ file = 文件 new-tab = 新建标签 new-window = 新建窗口 reload-folder = 刷新文件夹 -rename = 重命名… +rename = 重命名... close-tab = 关闭标签 quit = 退出 @@ -405,7 +405,7 @@ list-view = 列表视图 show-hidden-files = 显示隐藏文件 list-directories-first = 优先列出目录 gallery-preview = 图库预览 -menu-settings = 设置… +menu-settings = 设置... menu-about = 关于 COSMIC 文件… ## Sort @@ -435,20 +435,4 @@ comment = COSMIC 桌面的文件管理器 keywords = 文件夹;管理器; clear-recents-history = 清除最近访问历史 copy-path = 复制文件路径 -show-recents = 侧边栏中的最近访问 -mixed = 混合 -context-action-confirm-title = 运行 “{ $name }” ? -run = 运行 -context-action-confirm-warning = - 该行动将会在 { $items } { $items -> - [one] 项目 - *[other] 项目 - } 上运行。 -context-action = 环境行动 -rename-confirm = 重命名 -calculate = 计算 -error = 错误 -checksum = { $kind } 校验和 -sidebar-label = 侧边栏标签 -search-application = 使用应用名称搜索 -change-sidebar-label = 更改侧边栏标签 +show-recents = 侧边栏显示最近访问 diff --git a/i18n/zh-TW/cosmic_files.ftl b/i18n/zh-TW/cosmic_files.ftl index 36badae..1149a62 100644 --- a/i18n/zh-TW/cosmic_files.ftl +++ b/i18n/zh-TW/cosmic_files.ftl @@ -1,18 +1,18 @@ -cosmic-files = COSMIC 檔案 +cosmic-files = COSMIC 檔案總管 empty-folder = 空資料夾 empty-folder-hidden = 空資料夾(包含隱藏項目) no-results = 找不到結果 filesystem = 檔案系統 -home = 家目錄 +home = 主目錄 networks = 網路 -notification-in-progress = 檔案操作正在進行中 +notification-in-progress = 檔案操作正在進行中。 trash = 垃圾桶 recents = 最近使用 undo = 復原 today = 今天 # List view name = 名稱 -modified = 修改時間 +modified = 修改日期 size = 大小 # Dialogs @@ -25,7 +25,7 @@ create-archive = 建立壓縮檔案 ## Empty Trash Dialog empty-trash = 清空垃圾桶 -empty-trash-warning = 垃圾桶中的項目將被永久刪除 +empty-trash-warning = 你確定要永久刪除垃圾桶中的所有項目嗎? ## New File/Folder Dialog @@ -33,16 +33,16 @@ create-new-file = 建立新檔案 create-new-folder = 建立新資料夾 file-name = 檔案名稱 folder-name = 資料夾名稱 -file-already-exists = 相同名稱的檔案已經存在 -folder-already-exists = 相同名稱的資料夾已經存在 -name-hidden = 以「.」開頭的名稱將會被隱藏 -name-invalid = 名稱不能是「{ $filename }」 -name-no-slashes = 名稱不能包含斜線 +file-already-exists = 已存在同名檔案。 +folder-already-exists = 已存在同名資料夾。 +name-hidden = 以「.」開頭的名稱將會被隱藏。 +name-invalid = 名稱不能是 「{ $filename }」。 +name-no-slashes = 名稱不能包含斜線。 ## Open/Save Dialog cancel = 取消 -create = 創作 +create = 建立 open = 開啟 open-file = 開啟檔案 open-folder = 開啟資料夾 @@ -62,12 +62,12 @@ rename-folder = 重新命名資料夾 ## Replace Dialog replace = 取代 -replace-title = 「{ $filename }」已存在於此位置 +replace-title = 檔案「{ $filename }」已存在於此位置。 replace-warning = 你要取代它嗎?取代將覆蓋其內容。 replace-warning-operation = 你要取代它嗎?取代將覆蓋其內容。 original-file = 原始檔案 replace-with = 取代為 -apply-to-all = 套用至全部 +apply-to-all = 套用至所有項目 keep-both = 保留兩者 skip = 跳過 @@ -95,13 +95,13 @@ network-drive-description = 伺服器地址包括協定前綴和地址。 範例:ssh://192.168.0.1, ftp://[2001:db8::1] network-drive-schemes = - 可用協定,前綴 - AppleTalk,afp:// - 檔案傳輸協定,ftp:// 或 ftps:// - 網路檔案系統,nfs:// - 伺服器訊息區塊,smb:// - SSH 檔案傳輸協定,sftp:// 或 ssh:// - WebDav,dav:// 或 davs:// + 可用協定,前綴 + AppleTalk,afp:// + 檔案傳輸協定,ftp:// 或 ftps:// + 網路檔案系統,nfs:// + 伺服器訊息區塊,smb:// + SSH 檔案傳輸協定,sftp:// 或 ssh:// + WebDav,dav:// 或 davs:// network-drive-error = 無法存取網路磁碟機 password = 密碼 remember-password = 記住密碼 @@ -112,7 +112,7 @@ username = 使用者名稱 edit-history = 編輯歷史 history = 歷史紀錄 -no-history = 無歷史記錄項目。 +no-history = 沒有歷史項目。 pending = 待處理 failed = 失敗 complete = 完成 @@ -133,56 +133,56 @@ copying = 正在複製 { $items } { $items -> [one] 項目 *[other] 項目 - }從「{ $from }」到「{ $to }」({ $progress })... + } 從「{ $from }」到「{ $to }」({ $progress })... copied = 已複製 { $items } { $items -> [one] 項目 *[other] 項目 }從「{ $from }」到「{ $to }」 -emptying-trash = 正在清空 { trash }({ $progress })… -emptied-trash = 已經清空 { trash } +emptying-trash = 正在清空{ trash }({ $progress })… +emptied-trash = 已清空{ trash } extracting = 正在解壓縮 { $items } 項目 { $items -> [one] 項目 *[other] 項目 - }從「{ $from }」至「{ $to }」({ $progress })... + } 從「{ $from }」到「{ $to }」({ $progress })... extracted = 已解壓縮 { $items } 項目 { $items -> [one] 項目 *[other] 項目 - }從「{ $from }」到「{ $to }」 + } 從 { $from } 到 { $to } moving = 正在移動 { $items } { $items -> [one] 項目 *[other] 項目 - }從「{ $from }」到「{ $to }」({ $progress })... + } 從「{ $from }」到「{ $to }」({ $progress })... moved = - 已經移動 { $items } { $items -> + 已移動 { $items } { $items -> [one] 項目 *[other] 項目 - } 從「{ $from }」至「{ $to }」 -renaming = 正在將「{ $from }」重新命名為「{ $to }」 -renamed = 已將「{ $from }」重新命名為「{ $to }」 + } 從「{ $from }」到「{ $to }」 +renaming = 正在重新命名「{ $from }」為「{ $to }」 +renamed = 已重新命名 { $from } 為 { $to } restoring = 正在還原 { $items } 項目 { $items -> [one] 項目 *[other] 項目 - }自 { trash } ({ $progress })... + } 從{ trash }({ $progress })... restored = - 已經還原 { $items } 項目 { $items -> + 已還原 { $items } 項目 { $items -> [one] 項目 *[other] 項目 - }從 { trash } -unknown-folder = 不明資料夾 + } 從{ trash } +unknown-folder = 未知資料夾 ## Open with -menu-open-with = 開啟檔案... +menu-open-with = 開啟方式... default-app = { $name } (預設) ## Show details -show-details = 顯示詳細資訊 +show-details = 顯示詳細資料 ## Settings @@ -192,17 +192,17 @@ settings = 設定 appearance = 外觀 theme = 主題 -match-desktop = 符合桌面 -dark = 深色 -light = 淺色 +match-desktop = 與桌面一致 +dark = 暗色模式 +light = 亮色模式 # Context menu add-to-sidebar = 加入側邊欄 -compress = 壓縮… -extract-here = 解壓縮 -new-file = 新增檔案... -new-folder = 新增資料夾... +compress = 壓縮 +extract-here = 解壓縮至此 +new-file = 新檔案... +new-folder = 新資料夾... open-in-terminal = 在終端機中開啟 -move-to-trash = 丟入垃圾桶 +move-to-trash = 移動至垃圾桶 restore-from-trash = 從垃圾桶還原 remove-from-sidebar = 從側邊欄移除 sort-by-name = 依名稱排序 @@ -215,11 +215,11 @@ sort-by-size = 依大小排序 ## File file = 檔案 -new-tab = 新增分頁 -new-window = 新增視窗 +new-tab = 新分頁 +new-window = 新視窗 rename = 重新命名... close-tab = 關閉分頁 -quit = 結束 +quit = 退出 ## Edit @@ -238,164 +238,46 @@ view = 檢視 grid-view = 網格檢視 list-view = 列表檢視 show-hidden-files = 顯示隱藏檔案 -list-directories-first = 目錄優先列出 +list-directories-first = 優先列出目錄 menu-settings = 設定... -menu-about = 關於 COSMIC 檔案... +menu-about = 關於 COSMIC 檔案總管... ## Sort sort = 排序 sort-a-z = A-Z sort-z-a = Z-A -sort-newest-first = 最新優先 -sort-oldest-first = 最舊優先 -sort-smallest-to-largest = 從小到大 -sort-largest-to-smallest = 從大到小 +sort-newest-first = 最新的在前 +sort-oldest-first = 最舊的在前 +sort-smallest-to-largest = 由小至大 +sort-largest-to-smallest = 由大至小 deleted = - 已經刪除 { $items } { $items -> + 已刪除 { $items } { $items -> [one] 項目 *[other] 項目 - }從 { trash } + }從{ trash } permanently-deleting = 正在永久刪除 { $items } { $items -> - [one] 項目 - *[other] 項目 + [one] 项目 + *[other] 项目 } permanently-deleted = - 已經永久刪除 { $items } { $items -> - [one] 項目 - *[other] 項目 + 已永久刪除 { $items } { $items -> + [one] 项目 + *[other] 项目 } removing-from-recents = - 正在從 { recents } 中移除 { $items } { $items -> - [one] 項目 - *[other] 項目 + 正在從{ recents }中移除 { $items } { $items -> + [one] 项目 + *[other] 项目 } deleting = 正在刪除 { $items } { $items -> [one] 项目 *[other] 项目 - }從 { trash }({ $progress })… + }從{ trash }({ $progress })… removed-from-recents = - 已經從 { recents } 中移除 { $items } { $items -> - [one] 項目 - *[other] 項目 + 已從{ recents }中移除 { $items } { $items -> + [one] 项目 + *[other] 项目 } -repository = 軟體庫 -desktop-view-options = 桌面檢視選項... -show-on-desktop = 顯示在桌面 -desktop-folder-content = 桌面資料夾內容 -mounted-drives = 已經掛載的磁碟機 -trash-folder-icon = 垃圾桶圖示 -trashed-on = 丟入時間 -icon-size-and-spacing = 圖示大小與間距 -icon-size = 圖示大小 -grid-spacing = 網格間距 -details = 詳細資訊 -dismiss = 關閉訊息 -delete = 刪除 -remove = 移除 -support = 支援 -cancelled = 已取消 -keywords = 資料夾;管理器; -empty-trash-title = 清空垃圾桶? -pause = 暫停 -resume = 繼續 -extract-password-required = 需要密碼 -extract-to = 解壓縮至... -extract-to-title = 解壓縮至資料夾 -mount-error = 無法存取磁碟機 -open-with-title = 您要如何開啟「{ $name }」? -browse-store = 瀏覽 { $store } -other-apps = 其他應用程式 -related-apps = 相關應用程式 -permanently-delete-question = 永久刪除? -set-executable-and-launch = 設定為可以執行並啟動 -read-only = 唯讀 -read-execute = 讀取和執行 -read-write = 讀取和寫入 -read-write-execute = 讀取、寫入和執行 -favorite-path-error = 開啟目錄時發生錯誤 -set-executable-and-launch-description = 您是否要將「{ $name }」設為可執行並啟動它? -set-and-launch = 設定並啟動 -none = 無 -execute-only = 僅執行 -write-only = 僅寫入 -write-execute = 寫入和執行 -operations-running = - { $running } { $running -> - [one] 個操作 - *[other] 個操作 - }正在執行({ $percent }%)... -operations-running-finished = - { $running } { $running -> - [one] 個操作 - *[other] 個操作 - }正在執行({ $percent }%), { $finished } 個已經完成... -permanently-delete-warning = 「{ $target }」將被永久刪除。此操作無法復原。 -open-with = 開啟檔案 -selected-items = 已經選定 { $items } 個項目 -copy-to-title = 選擇複製目的地 -copy-to-button-label = 複製 -move-to-title = 選擇移動目的地 -move-to-button-label = 移動 -keep = 保留 -progress = { $percent }% -progress-cancelled = { $percent }%,已經取消 -progress-failed = { $percent }%,失敗 -progress-paused = { $percent }%,已經暫停 -favorite-path-error-description = - 無法開啟「{ $path }」 - 「{ $path }」可能不存在,或您可能沒有權限開啟它。 - - 您是否要將它從側邊欄移除? -comment = COSMIC 桌面檔案管理器 -pasted-image = 已經貼上的圖片 -pasted-text = 已經貼上的文字 -pasted-video = 已經貼上的影片 -sort-by-trashed = 依丟入時間排序 -calculating = 計算中... -single-click = 點按以開啟 -type-to-search = 輸入進行搜尋 -type-to-search-recursive = 搜尋目前資料夾及全部子資料夾 -type-to-search-enter-path = 輸入目錄或檔案的目錄 -delete-permanently = 永久刪除 -eject = 彈出 -remove-from-recents = 從最近項目中移除 -change-wallpaper = 變更桌布... -desktop-appearance = 桌面外觀... -display-settings = 顯示設定... -reload-folder = 重新載入資料夾 -gallery-preview = 圖庫預覽 -type = 類型:{ $mime } -items = 項目:{ $items } -item-size = 大小:{ $size } -item-created = 建立時間:{ $created } -item-modified = 修改時間:{ $modified } -item-accessed = 存取時間:{ $accessed } -type-to-search-select = 選取第一個符合條件的檔案或資料夾 -copy-to = 複製至... -move-to = 移動至... -show-recents = 側邊欄中的最近使用資料夾 -clear-recents-history = 清除最近使用歷史記錄 -copy-path = 複製路徑 -setting-executable-and-launching = 設定「{ $name }」為可以執行並進行啟動 -set-executable-and-launched = 設定「{ $name }」為可以執行並已經啟動 -setting-permissions = 設定「{ $name }」的權限至 { $mode } -set-permissions = 設定「{ $name }」的權限至 { $mode } -mixed = 混合 -context-action = 環境行動 -context-action-confirm-title = 執行「{ $name }」嗎? -context-action-confirm-warning = - 該行動將會在 { $items } { $items -> - [one] 項目 - *[other] 項目 - } 上執行。 -run = 執行 -rename-confirm = 重新命名 -checksum = { $kind } 核對和 -calculate = 計算 -error = 錯誤 -sidebar-label = 側邊欄標籤 -search-application = 依應用程式名稱搜尋 -change-sidebar-label = 變更側邊欄標籤 diff --git a/justfile b/justfile index 004854d..9522e98 100644 --- a/justfile +++ b/justfile @@ -12,10 +12,6 @@ cargo-target-dir := env('CARGO_TARGET_DIR', 'target') bin-src := cargo-target-dir / 'release' / name bin-dst := base-dir / 'bin' / name -thumbnailer-bin-name := name + '-thumbnailer' -thumbnailer-bin-src := cargo-target-dir / 'release' / thumbnailer-bin-name -thumbnailer-bin-dst := base-dir / 'bin' / thumbnailer-bin-name - applet-name := name + '-applet' applet-src := cargo-target-dir / 'release' / applet-name applet-dst := base-dir / 'bin' / applet-name @@ -28,10 +24,6 @@ metainfo := APPID + '.metainfo.xml' metainfo-src := 'target/xdgen' / metainfo metainfo-dst := clean(rootdir / prefix) / 'share' / 'metainfo' / metainfo -thumbnailer := APPID + '.thumbnailer' -thumbnailer-src := 'res' / thumbnailer -thumbnailer-dst := clean(rootdir / prefix) / 'share' / 'thumbnailers' / thumbnailer - icons-src := 'res' / 'icons' / 'hicolor' icons-dst := clean(rootdir / prefix) / 'share' / 'icons' / 'hicolor' @@ -53,10 +45,13 @@ clean-dist: clean clean-vendor build-debug *args: cargo build {{args}} cargo build --package {{applet-name}} {{args}} - cargo build --package {{thumbnailer-bin-name}} {{args}} # Compiles with release profile build-release *args: (build-debug '--release' args) + +# Compiles applet with release profile +build-release-applet *args: + cargo build --package {{applet-name}} --release {{args}} # Compiles release profile with vendored dependencies build-vendored *args: vendor-extract (build-release '--frozen --offline' args) @@ -97,11 +92,9 @@ heaptrack *args: # Installs files install: install -Dm0755 {{bin-src}} {{bin-dst}} - install -Dm0755 {{thumbnailer-bin-src}} {{thumbnailer-bin-dst}} install -Dm0755 {{applet-src}} {{applet-dst}} install -Dm0644 {{desktop-src}} {{desktop-dst}} install -Dm0644 {{metainfo-src}} {{metainfo-dst}} - install -Dm0644 {{thumbnailer-src}} {{thumbnailer-dst}} for size in `ls {{icons-src}}`; do \ install -Dm0644 "{{icons-src}}/$size/apps/{{APPID}}.svg" "{{icons-dst}}/$size/apps/{{APPID}}.svg"; \ done @@ -112,13 +105,13 @@ install-applet: # Uninstalls installed files uninstall: - rm -f {{bin-dst}} {{thumbnailer-bin-dst}} {{applet-dst}} {{thumbnailer-dst}} + rm -f {{bin-dst}} {{applet-dst}} # Vendor dependencies locally vendor: #!/usr/bin/env bash mkdir -p .cargo - cargo vendor --locked --sync Cargo.toml | head -n -1 > .cargo/config.toml + cargo vendor --sync Cargo.toml | head -n -1 > .cargo/config.toml echo 'directory = "vendor"' >> .cargo/config.toml echo >> .cargo/config.toml echo '[env]' >> .cargo/config.toml diff --git a/res/com.system76.CosmicFiles.thumbnailer b/res/com.system76.CosmicFiles.thumbnailer deleted file mode 100644 index 4c936cc..0000000 --- a/res/com.system76.CosmicFiles.thumbnailer +++ /dev/null @@ -1,4 +0,0 @@ -[Thumbnailer Entry] -TryExec=cosmic-files-thumbnailer -Exec=cosmic-files-thumbnailer %o --size %s %i -MimeType=application/vnd.microsoft.portable-executable;application/x-msdownload; diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index fb5449a..0000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,3 +0,0 @@ -[toolchain] -channel = "1.93.0" -components = ["clippy", "rustfmt"] diff --git a/rustfmt.toml b/rustfmt.toml deleted file mode 100644 index c1578aa..0000000 --- a/rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -imports_granularity = "Module" diff --git a/src/app.rs b/src/app.rs index 22459c6..4e20f48 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,48 +1,57 @@ // Copyright 2023 System76 // SPDX-License-Identifier: GPL-3.0-only -use cosmic::app::{self, Core, Task, context_drawer}; -use cosmic::core::Auto; -use cosmic::cosmic_config::{self, ConfigSet}; -use cosmic::iced::clipboard::dnd::DndAction; -use cosmic::iced::core::SmolStr; -use cosmic::iced::core::widget::operation::focusable::unfocus; -use cosmic::iced::futures::{self, SinkExt}; -use cosmic::iced::keyboard::key::Physical; -use cosmic::iced::keyboard::{Event as KeyEvent, Key, Modifiers}; -#[cfg(all(feature = "wayland", feature = "desktop-applet"))] -use cosmic::iced::platform_specific::shell::wayland::commands::overlap_notify::overlap_notify; -use cosmic::iced::runtime::{clipboard, task}; -use cosmic::iced::widget::button::focus; -use cosmic::iced::widget::scrollable; -use cosmic::iced::widget::scrollable::AbsoluteOffset; -use cosmic::iced::window::{self, Event as WindowEvent, Id as WindowId}; -use cosmic::iced::{self, Alignment, Event, Length, Size, Subscription, event, mouse, stream}; #[cfg(all(feature = "wayland", feature = "desktop-applet"))] use cosmic::iced::{ - Limits, Point, Rectangle, + Limits, Point, event::wayland::{Event as WaylandEvent, OutputEvent, OverlapNotifyEvent}, platform_specific::runtime::wayland::layer_surface::{ IcedMargin, IcedOutput, SctkLayerSurfaceSettings, }, platform_specific::shell::wayland::commands::layer_surface::{ - Anchor, KeyboardInteractivity, Layer, destroy_layer_surface, + Anchor, KeyboardInteractivity, Layer, destroy_layer_surface, get_layer_surface, + }, +}; +#[cfg(all(feature = "wayland", feature = "desktop-applet"))] +use cosmic::iced_winit::commands::overlap_notify::overlap_notify; +use cosmic::{ + Application, ApplicationExt, Element, + app::{self, Core, Task, context_drawer}, + cosmic_config::{self, ConfigSet}, + cosmic_theme, + desktop::fde::DesktopEntry, + executor, + iced::{ + self, Alignment, Event, Length, Rectangle, Size, Subscription, + clipboard::dnd::DndAction, + core::SmolStr, + event, + futures::{self, SinkExt}, + keyboard::{Event as KeyEvent, Key, Modifiers}, + mouse, stream, + widget::scrollable, + window::{self, Event as WindowEvent, Id as WindowId}, + }, + iced_runtime::clipboard, + iced_widget::{button::focus, scrollable::AbsoluteOffset}, + style, surface, theme, + widget::{ + self, + about::About, + dnd_destination::DragId, + icon, + menu::{action::MenuAction, key_bind::KeyBind}, + segmented_button::{self, Entity, ReorderEvent}, + space, }, }; -use cosmic::widget::about::About; -use cosmic::widget::dnd_destination::DragId; -use cosmic::widget::menu::action::MenuAction; -use cosmic::widget::menu::key_bind::KeyBind; -use cosmic::widget::segmented_button::{self, Entity, ReorderEvent}; -use cosmic::widget::{self, icon, settings, space}; -use cosmic::{Application, ApplicationExt, Element, cosmic_theme, executor, surface, theme}; use mime_guess::Mime; -use notify_debouncer_full::notify::{self, RecommendedWatcher}; -use notify_debouncer_full::{DebouncedEvent, Debouncer, RecommendedCache, new_debouncer}; +use notify_debouncer_full::{ + DebouncedEvent, Debouncer, RecommendedCache, new_debouncer, + notify::{self, RecommendedWatcher}, +}; 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}, @@ -53,7 +62,7 @@ use std::{ path::{Path, PathBuf}, pin::Pin, process, - sync::{Arc, LazyLock}, + sync::{Arc, LazyLock, Mutex}, time::{self, Duration, Instant}, }; use tokio::sync::mpsc; @@ -61,33 +70,35 @@ use trash::TrashItem; #[cfg(all(feature = "wayland", feature = "desktop-applet"))] use wayland_client::{Proxy, protocol::wl_output::WlOutput}; -use crate::clipboard::{ - ClipboardCache, ClipboardCopy, ClipboardKind, ClipboardPaste, ClipboardPasteImage, - ClipboardPasteText, ClipboardPasteVideo, +use crate::{ + FxOrderMap, + clipboard::{ + ClipboardCache, ClipboardCopy, ClipboardKind, ClipboardPaste, ClipboardPasteImage, + ClipboardPasteText, ClipboardPasteVideo, + }, + config::{ + AppTheme, Config, DesktopConfig, Favorite, IconSizes, State, TIME_CONFIG_ID, TabConfig, + TimeConfig, TypeToSearch, + }, + dialog::{Dialog, DialogKind, DialogMessage, DialogResult, DialogSettings}, + fl, home_dir, + key_bind::key_binds, + localize::LANGUAGE_SORTER, + menu, + mime_app::{self, MimeApp, MimeAppCache}, + mime_icon, + mounter::{MOUNTERS, MounterAuth, MounterItem, MounterItems, MounterKey, MounterMessage}, + operation::{ + Controller, Operation, OperationError, OperationErrorType, OperationSelection, + ReplaceResult, copy_unique_path, + }, + spawn_detached::spawn_detached, + tab::{ + self, HOVER_DURATION, HeadingOptions, ItemMetadata, Location, SORT_OPTION_FALLBACK, + SearchLocation, Tab, + }, + zoom::{zoom_in_view, zoom_out_view, zoom_to_default}, }; -use crate::config::{ - AppTheme, Config, DesktopConfig, Favorite, IconSizes, State, TIME_CONFIG_ID, TabConfig, - TimeConfig, ToolbarAction, TypeToSearch, default_toolbar, -}; -use crate::dialog::{Dialog, DialogKind, DialogMessage, DialogResult, DialogSettings}; -use crate::key_bind::key_binds; -use crate::localize::LANGUAGE_SORTER; -use crate::mime_app::{self, MimeApp, MimeAppCache, MimeAppMatch}; -use crate::mounter::{ - MOUNTERS, MounterAuth, MounterItem, MounterItems, MounterKey, MounterMessage, -}; -use crate::operation::{ - Controller, Operation, OperationError, OperationErrorType, OperationSelection, ReplaceResult, - copy_unique_path, -}; -use crate::spawn_detached::spawn_detached; -use crate::tab::{ - self, HOVER_DURATION, HeadingOptions, ItemMetadata, Location, SORT_OPTION_FALLBACK, - SearchLocation, Tab, -}; -use crate::trash::{Trash, TrashExt}; -use crate::zoom::{zoom_in_view, zoom_out_view, zoom_to_default}; -use crate::{FxOrderMap, context_action, fl, home_dir, menu, mime_icon}; static PERMANENT_DELETE_BUTTON_ID: LazyLock = LazyLock::new(|| widget::Id::new("permanent-delete-button")); @@ -98,9 +109,6 @@ static DELETE_TRASH_BUTTON_ID: LazyLock = static CONFIRM_OPEN_WITH_BUTTON_ID: LazyLock = LazyLock::new(|| widget::Id::new("confirm-open-with-button")); -static CONFIRM_CONTEXT_ACTION_BUTTON_ID: LazyLock = - LazyLock::new(|| widget::Id::new("confirm-context-action-button")); - static EMPTY_TRASH_BUTTON_ID: LazyLock = LazyLock::new(|| widget::Id::new("empty-trash-button")); @@ -133,105 +141,6 @@ pub struct Flags { pub uris: Vec, } -/// 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> { - 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, String)> for ToolbarActionPayload { - type Error = (); - fn try_from((data, _mime): (Vec, String)) -> Result { - 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, @@ -246,7 +155,6 @@ pub enum Action { CosmicSettingsWallpaper, DesktopViewOptions, Delete, - EditDesktopEntry, EditHistory, EditLocation, Eject, @@ -260,8 +168,6 @@ pub enum Action { HistoryPrevious, ItemDown, ItemLeft, - ItemPageDown, - ItemPageUp, ItemRight, ItemUp, LocationUp, @@ -274,7 +180,6 @@ pub enum Action { OpenItemLocation, OpenTerminal, OpenWith, - RunContextAction(usize), Paste, PermanentlyDelete, Preview, @@ -320,7 +225,6 @@ impl Action { Self::CosmicSettingsWallpaper => Message::CosmicSettings("wallpaper"), Self::Delete => Message::Delete(entity_opt), Self::DesktopViewOptions => Message::DesktopViewOptions, - Self::EditDesktopEntry => Message::EditDesktopEntry(entity_opt), Self::EditHistory => Message::ToggleContextPage(ContextPage::EditHistory), Self::EditLocation => Message::TabMessage(entity_opt, tab::Message::EditLocationEnable), Self::Eject => Message::Eject, @@ -336,8 +240,6 @@ impl Action { Self::HistoryPrevious => Message::TabMessage(entity_opt, tab::Message::GoPrevious), Self::ItemDown => Message::TabMessage(entity_opt, tab::Message::ItemDown), Self::ItemLeft => Message::TabMessage(entity_opt, tab::Message::ItemLeft), - Self::ItemPageDown => Message::TabMessage(entity_opt, tab::Message::ItemPageDown), - Self::ItemPageUp => Message::TabMessage(entity_opt, tab::Message::ItemPageUp), Self::ItemRight => Message::TabMessage(entity_opt, tab::Message::ItemRight), Self::ItemUp => Message::TabMessage(entity_opt, tab::Message::ItemUp), Self::LocationUp => Message::TabMessage(entity_opt, tab::Message::LocationUp), @@ -350,9 +252,6 @@ impl Action { Self::OpenItemLocation => Message::OpenItemLocation(entity_opt), Self::OpenTerminal => Message::OpenTerminal(entity_opt), Self::OpenWith => Message::OpenWithDialog(entity_opt), - Self::RunContextAction(action) => { - Message::TabMessage(entity_opt, tab::Message::RunContextAction(*action)) - } Self::Paste => Message::Paste(entity_opt), Self::PermanentlyDelete => Message::PermanentlyDelete(entity_opt), Self::Preview => Message::Preview(entity_opt), @@ -398,7 +297,7 @@ impl MenuAction for Action { } #[derive(Clone, Debug)] -pub struct PreviewItem(pub Box); +pub struct PreviewItem(pub tab::Item); impl PartialEq for PreviewItem { fn eq(&self, other: &Self) -> bool { @@ -424,9 +323,7 @@ pub enum NavMenuAction { OpenInNewTab(segmented_button::Entity), OpenInNewWindow(segmented_button::Entity), Preview(segmented_button::Entity), - RunContextAction(segmented_button::Entity, usize), RemoveFromSidebar(segmented_button::Entity), - ChangeSidebarLabel(segmented_button::Entity), } impl MenuAction for NavMenuAction { @@ -457,7 +354,6 @@ pub enum Message { DesktopDialogs(bool), DialogCancel, DialogComplete, - EditDesktopEntry(Option), Eject, FileDialogMessage(DialogMessage), DialogPush(DialogPage, Option), @@ -468,7 +364,7 @@ pub enum Message { ExtractToResult(DialogResult), #[cfg(all(feature = "wayland", feature = "desktop-applet"))] Focused(window::Id), - Key(window::Id, Modifiers, Key, Physical, Option), + Key(window::Id, Modifiers, Key, Option), LaunchUrl(String), MaybeExit, ModifiersChanged(window::Id, Modifiers), @@ -502,8 +398,6 @@ pub enum Message { OpenWithBrowse, OpenWithDialog(Option), OpenWithSelection(usize), - OpenWithSearchClear, - OpenWithToggleDefault(bool), #[cfg(all(feature = "wayland", feature = "desktop-applet"))] Overlap(window::Id, OverlapNotifyEvent), Paste(Option), @@ -518,19 +412,16 @@ pub enum Message { CheckClipboardImage, CheckClipboardVideo, CheckClipboardText, - RetryCheckClipboard(ClipboardCache), ClipboardCached(ClipboardCache), PendingCancel(u64), PendingCancelAll, PendingComplete(u64, OperationSelection), PendingDismiss, PendingError(u64, OperationError), - PendingResults(Vec<(u64, OperationSelection)>, Vec<(u64, OperationError)>), PendingPause(u64, bool), PendingPauseAll(bool), PermanentlyDelete(Option), Preview(Option), - ReloadMimeAppCache, ReorderTab(ReorderEvent), RescanRecents, RescanTrash, @@ -545,22 +436,6 @@ 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), @@ -574,7 +449,7 @@ pub enum Message { TabRescan( Entity, Location, - Option>, + Option, Vec, Option>, ), @@ -606,7 +481,7 @@ pub enum Message { OutputEvent(OutputEvent, WlOutput), Cosmic(app::Action), None, - Surface(surface::Action), + Surface(surface::Action), CutPaths(Vec), } @@ -656,7 +531,6 @@ pub enum DialogPage { }, EmptyTrash, FailedOperation(u64), - FailedOperations(Vec), ExtractPassword { id: u64, password: String, @@ -682,19 +556,11 @@ pub enum DialogPage { name: String, dir: bool, }, - RunContextAction { - action: usize, - paths: Box<[PathBuf]>, - }, OpenWith { path: PathBuf, mime: mime_guess::Mime, selected: usize, - store_opt: Option>, - search_app_name: String, - /// When true, the chosen app is written to mimeapps.list as the - /// default handler for `mime` after it spawns. - set_default: bool, + store_opt: Option, }, PermanentlyDelete { paths: Box<[PathBuf]>, @@ -702,10 +568,6 @@ pub enum DialogPage { DeleteTrash { items: Vec, }, - ChangeSidebarLabel { - entity: Entity, - label: String, - }, RenameItem { from: PathBuf, parent: PathBuf, @@ -713,8 +575,8 @@ pub enum DialogPage { dir: bool, }, Replace { - from: Box, - to: Box, + from: tab::Item, + to: tab::Item, multiple: bool, apply_to_all: bool, conflict_count: usize, @@ -794,10 +656,18 @@ impl DialogPages { pub struct FavoriteIndex(usize); +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum MimeAppMatch { + Exact, + Related, + Other, +} + pub struct MounterData(MounterKey, MounterItem); #[derive(Clone, Debug)] pub enum WindowKind { + ContextMenu(Entity, widget::Id), Desktop(Entity), DesktopViewOptions, Dialogs(widget::Id), @@ -848,11 +718,6 @@ pub struct App { nav_bar_context_id: segmented_button::Entity, nav_model: segmented_button::SingleSelectModel, tab_model: segmented_button::Model, - /// 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, config_handler: Option, state_handler: Option, config: Config, @@ -952,12 +817,9 @@ impl App { // First launch apps that can be launched directly if mime == "application/x-desktop" { - #[cfg(feature = "desktop")] - { - // Try opening desktop application - Self::launch_desktop_entries(&paths); - continue; - } + // Try opening desktop application + Self::launch_desktop_entries(&paths); + continue; } else if mime == "application/x-executable" || mime == "application/vnd.appimage" { // Try opening executable for path in paths { @@ -1018,42 +880,31 @@ impl App { Task::batch(tasks) } - #[cfg(feature = "desktop")] fn launch_desktop_entries(paths: &[impl AsRef]) { - use cosmic::desktop::fde::DesktopEntry; - let locales = cosmic::desktop::fde::get_languages_from_env(); - 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, - entry.name(&locales).as_deref().unwrap_or_default(), - Some(path), - &[] as &[&str; 0], - ) { - Some(commands) => { - let cwd_opt = entry.desktop_entry("Path"); + Some(exec) => match mime_app::exec_to_command(exec, &[] as &[&str; 0]) { + Some(commands) => { + let cwd_opt = entry.desktop_entry("Path"); - for mut command in commands { - if let Some(cwd) = cwd_opt { - command.current_dir(cwd); - } + for mut command in commands { + if let Some(cwd) = cwd_opt { + command.current_dir(cwd); + } - if let Err(err) = spawn_detached(&mut command) { - log::warn!("failed to execute {}: {}", path.display(), err); - } + if let Err(err) = spawn_detached(&mut command) { + log::warn!("failed to execute {}: {}", path.display(), err); } } - None => { - log::warn!( - "failed to parse {}: invalid Desktop Entry/Exec", - path.display() - ); - } } - } + None => { + log::warn!( + "failed to parse {}: invalid Desktop Entry/Exec", + path.display() + ); + } + }, None => { log::warn!( "failed to parse {}: missing Desktop Entry/Exec", @@ -1068,21 +919,6 @@ impl App { } } - /// Open paths with the default text editor, ignoring their own MIME type. - /// - /// Desktop entries launch the application they describe when opened, so - /// `open_file` never shows the file itself. This gives the file manager a - /// way to edit one without falling back to a terminal. - fn edit_file(&self, paths: &[PathBuf]) -> Task { - let Ok(text_plain) = "text/plain".parse::() else { - return Task::none(); - }; - if !self.launch_from_mime_cache(&text_plain, paths) { - log::warn!("no application registered for text/plain, cannot edit {paths:?}"); - } - Task::none() - } - fn launch_from_mime_cache

(&self, mime: &Mime, paths: &[P]) -> bool where P: std::fmt::Debug + AsRef + AsRef, @@ -1162,12 +998,6 @@ impl App { .and_then(|first| first.as_ref().parent()) .map(Path::to_path_buf) { - let mut tasks = Vec::new(); - if let Some(old_dialog) = self.file_dialog_opt.take() { - let old_id = old_dialog.window_id(); - self.windows.remove(&old_id); - tasks.push(window::close(old_id)); - } let (mut dialog, dialog_task) = Dialog::new( DialogSettings::new() .kind(DialogKind::OpenFolder) @@ -1184,9 +1014,7 @@ impl App { ))), ); self.file_dialog_opt = Some(dialog); - tasks.push(set_title_task); - tasks.push(dialog_task); - Task::batch(tasks) + Task::batch([set_title_task, dialog_task]) } else { Task::none() } @@ -1231,7 +1059,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, size)) = self.layer_sizes.get(w_id).map(|s| { + let Some((bl, br, tl, tr, mut size)) = self.layer_sizes.get(w_id).map(|s| { ( Rectangle::new( Point::new(0., s.height / 2.), @@ -1281,18 +1109,30 @@ 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; @@ -1335,14 +1175,14 @@ impl App { entity.id() }; - let mut tasks = Vec::with_capacity(4); - if activate { - tasks.push(task::widget(unfocus())); - } - tasks.push(self.update_title()); - tasks.push(self.update_watcher()); - tasks.push(self.update_tab(entity, location, selection_paths)); - (entity, Task::batch(tasks)) + ( + entity, + Task::batch([ + self.update_title(), + self.update_watcher(), + self.update_tab(entity, location, selection_paths), + ]), + ) } fn open_tab( @@ -1410,186 +1250,43 @@ impl App { .insert(id, (operation.clone(), controller.clone())); // Use a task to send operations to the compio runtime thread. - cosmic::Task::stream(cosmic::iced::stream::channel(4, move |msg_tx| async move { - let (tx, rx) = tokio::sync::oneshot::channel(); + cosmic::Task::stream(cosmic::iced_futures::stream::channel( + 4, + move |msg_tx| async move { + let (tx, rx) = tokio::sync::oneshot::channel(); - let msg_tx = Arc::new(tokio::sync::Mutex::new(msg_tx)); + let msg_tx = Arc::new(tokio::sync::Mutex::new(msg_tx)); - let msg_tx_clone = msg_tx.clone(); + let msg_tx_clone = msg_tx.clone(); - _ = compio_tx - .send(Box::pin(async move { - let msg = match operation.perform(&msg_tx_clone, controller).await { - Ok(result_paths) => Message::PendingComplete(id, result_paths), - Err(err) => Message::PendingError(id, err), - }; + _ = compio_tx + .send(Box::pin(async move { + let msg = match operation.perform(&msg_tx_clone, controller).await { + Ok(result_paths) => Message::PendingComplete(id, result_paths), + Err(err) => Message::PendingError(id, err), + }; - _ = tx.send(msg); - })) - .await; + _ = tx.send(msg); + })) + .await; - if let Ok(msg) = rx.await { - let _ = msg_tx.lock().await.send(msg).await; - } - })) + if let Ok(msg) = rx.await { + let _ = msg_tx.lock().await.send(msg).await; + } + }, + )) .map(cosmic::Action::App) } - /// Will join operations together into a single task that will return a single - /// Message::PendingResults message when all operations are complete. - fn join_operations(&mut self, operations: Vec) -> Task { - Task::batch( - operations - .into_iter() - .map(|operation| self.operation(operation)), - ) - .collect() - .map(|messages| { - let results = messages.into_iter().fold( - Message::PendingResults(Vec::new(), Vec::new()), - |mut acc, message| { - if let Message::PendingResults(completed, errors) = &mut acc { - match message { - cosmic::Action::App(Message::PendingComplete(id, selection)) => { - completed.push((id, selection)); - } - cosmic::Action::App(Message::PendingError(id, err)) => { - errors.push((id, err)); - } - _ => {} - } - } - acc - }, - ); - cosmic::Action::App(results) - }) - } - - fn handle_completed_operations( - &mut self, - completed: Vec<(u64, OperationSelection)>, - ) -> Task { - let mut commands = Vec::with_capacity(4 * completed.len()); - let mut op_sel = OperationSelection::default(); - for (id, op_sel_pending) in completed { - op_sel.ignored.extend(op_sel_pending.ignored); - op_sel.selected.extend(op_sel_pending.selected); - if let Some((op, _)) = self.pending_operations.remove(&id) { - // Show toast for some operations - if let Some(description) = op.toast() { - if let Operation::Delete { ref paths } = op { - let paths: Arc<[PathBuf]> = Arc::from(paths.as_slice()); - commands.push( - self.toasts - .push( - widget::toaster::Toast::new(description) - .action(fl!("undo"), move |tid| { - Message::UndoTrash(tid, paths.clone()) - }), - ) - .map(cosmic::Action::App), - ); - } else { - commands.push( - self.toasts - .push(widget::toaster::Toast::new(description)) - .map(cosmic::Action::App), - ); - } - } - - // If a favorite for a path has been renamed or moved, update it. - if let Operation::Rename { ref from, ref to } = op { - if self.update_favorites([(from, to)].as_slice()) { - commands.push(self.update_config()); - } - } else if let Operation::Move { - ref paths, ref to, .. - } = op - { - let path_changes: Box<[_]> = paths - .iter() - .filter_map(|from| from.file_name().map(|name| (from, to.join(name)))) - .collect(); - if self.update_favorites(&path_changes) { - commands.push(self.update_config()); - } - } - - if matches!(op, Operation::RemoveFromRecents { .. }) { - commands.push(self.rescan_recents()); - } - - self.complete_operations.insert(id, op); - } - } - // Close progress notification if all relevant operations are finished - if !self - .pending_operations - .values() - .any(|(op, _)| op.show_progress_notification()) - { - self.progress_operations.clear(); - } - // Potentially show a notification - commands.push(self.update_notification()); - // Rescan and select based on operation - commands.push(self.rescan_operation_selection(op_sel)); - // Manually rescan any trash tabs after any operation is completed - commands.push(self.rescan_trash()); - - Task::batch(commands) - } - - fn handle_operation_errors(&mut self, errors: Vec<(u64, OperationError)>) -> Task { - let mut tasks = Vec::new(); - let mut failed = Vec::new(); - for (id, err) in errors.into_iter() { - if let Some((op, controller)) = self.pending_operations.remove(&id) { - // Only show dialog if not cancelled - if !controller.is_cancelled() { - match err.kind { - OperationErrorType::Generic(_) => failed.push(id), - OperationErrorType::PasswordRequired => { - tasks.push(self.dialog_pages.push_back(DialogPage::ExtractPassword { - id, - password: String::new(), - })); - } - } - } - - // Remove from progress - self.progress_operations.remove(&id); - self.failed_operations - .insert(id, (op, controller, err.to_string())); - } - } - if !failed.is_empty() { - tasks.push( - self.dialog_pages - .push_back(DialogPage::FailedOperations(failed)), - ); - tasks.push(widget::text_input::focus(self.dialog_text_input.clone())); - } - - // Close progress notification if all relevant operations are finished - if !self - .pending_operations - .values() - .any(|(op, _)| op.show_progress_notification()) - { - self.progress_operations.clear(); - } - // Manually rescan any trash tabs after any operation is completed - tasks.push(self.rescan_trash()); - Task::batch(tasks) - } - fn remove_window(&mut self, id: &window::Id) { if let Some(window) = self.windows.remove(id) { match window.kind { + WindowKind::ContextMenu(entity, _) => { + // Close context menu + if let Some(tab) = self.tab_model.data_mut::(entity) { + tab.context_menu = None; + } + } WindowKind::Desktop(entity) => { // Remove the tab from the tab model self.tab_model.remove(entity); @@ -1645,18 +1342,12 @@ impl App { ) -> Task { 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, items)) => { - #[cfg(feature = "gvfs")] - let mut items = items; - #[cfg(not(feature = "gvfs"))] - let items = items; - + Ok((parent_item_opt, mut items)) => { #[cfg(feature = "gvfs")] { let mounter_paths: Box<[_]> = mounter_items @@ -1761,7 +1452,7 @@ impl App { }; search_location.map(|search_location| { - ( + return ( Location::Search( search_location, term, @@ -1769,7 +1460,7 @@ impl App { Instant::now(), ), true, - ) + ); }) } None => match &tab.location { @@ -1826,7 +1517,6 @@ impl App { fn update_config(&mut self) -> Task { 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 @@ -1840,52 +1530,6 @@ 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::(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 { - let new_toolbar: Vec = self - .toolbar_model - .iter() - .filter_map(|e| self.toolbar_model.data::(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 { let needs_reload: Box<[_]> = (self.tab_model.iter()) .filter_map(|entity| { @@ -1926,6 +1570,21 @@ impl App { } } + fn close_context_menus(&mut self) -> Task { + let active = self.tab_model.active(); + if let Some(tab) = self.tab_model.data_mut::(active) { + tab.location_context_menu_index = None; + if tab.context_menu.is_some() { + return self.update(Message::TabMessage( + Some(active), + tab::Message::ContextMenu(None, None), + )); + } + } + + Task::none() + } + fn update_nav_model(&mut self) { let mut nav_model = segmented_button::ModelBuilder::default(); @@ -1939,7 +1598,13 @@ impl App { for (favorite_i, favorite) in self.config.favorites.iter().enumerate() { if let Some(path) = favorite.path_opt() { - let name = favorite.display_name().unwrap_or_else(|| fl!("filesystem")); + let name = if matches!(favorite, Favorite::Home) { + fl!("home") + } else if let Some(file_name) = path.file_name().and_then(|x| x.to_str()) { + file_name.to_string() + } else { + fl!("filesystem") + }; nav_model = nav_model.insert(move |b| { b.text(name.clone()) .icon( @@ -1963,7 +1628,7 @@ impl App { nav_model = nav_model.insert(|b| { b.text(fl!("trash")) - .icon(icon::icon(Trash::icon_symbolic(16))) + .icon(icon::icon(tab::trash_helpers::trash_icon_symbolic(16))) .data(Location::Trash) .divider_above() }); @@ -2110,7 +1775,7 @@ impl App { fn network_drive(&self) -> Element<'_, Message> { let cosmic_theme::Spacing { space_xxs, space_m, .. - } = theme::spacing(); + } = theme::active().cosmic().spacing; let mut table = widget::column::with_capacity(8); for (i, line) in fl!("network-drive-schemes").lines().enumerate() { let mut row = widget::row::with_capacity(2); @@ -2141,23 +1806,25 @@ impl App { fn desktop_view_options(&self) -> Element<'_, Message> { let cosmic_theme::Spacing { space_m, space_l, .. - } = theme::spacing(); + } = theme::active().cosmic().spacing; let config = self.config.desktop; - let show_on_desktop = settings::section() - .title(fl!("show-on-desktop")) - .add( - settings::item::builder(fl!("desktop-folder-content")).toggler( - config.show_content, - move |show_content| { - Message::DesktopConfig(DesktopConfig { - show_content, - ..config - }) - }, - ), - ) - .add(settings::item::builder(fl!("mounted-drives")).toggler( + let mut column = widget::column::with_capacity(2); + + let mut section = widget::settings::section().title(fl!("show-on-desktop")); + section = section.add( + widget::settings::item::builder(fl!("desktop-folder-content")).toggler( + config.show_content, + move |show_content| { + Message::DesktopConfig(DesktopConfig { + show_content, + ..config + }) + }, + ), + ); + section = section.add( + widget::settings::item::builder(fl!("mounted-drives")).toggler( config.show_mounted_drives, move |show_mounted_drives| { Message::DesktopConfig(DesktopConfig { @@ -2165,8 +1832,10 @@ impl App { ..config }) }, - )) - .add(settings::item::builder(fl!("trash-folder-icon")).toggler( + ), + ); + section = section.add( + widget::settings::item::builder(fl!("trash-folder-icon")).toggler( config.show_trash, move |show_trash| { Message::DesktopConfig(DesktopConfig { @@ -2174,49 +1843,50 @@ impl App { ..config }) }, - )); + ), + ); + column = column.push(section); + let mut section = widget::settings::section().title(fl!("icon-size-and-spacing")); let icon_size = config.icon_size; - let grid_spacing = config.grid_spacing; - let icon_size_and_spacing = settings::section() - .title(fl!("icon-size-and-spacing")) - .add( - settings::item::builder(fl!("icon-size")) - .description(format!("{icon_size}%")) - .control( - widget::slider(50..=500, icon_size.get(), move |new_value| { - Message::DesktopConfig(DesktopConfig { - icon_size: NonZeroU16::new(new_value).unwrap_or(icon_size), - ..config - }) + section = section.add( + widget::settings::item::builder(fl!("icon-size")) + .description(format!("{icon_size}%")) + .control( + widget::slider(50..=500, icon_size.get(), move |new_value| { + Message::DesktopConfig(DesktopConfig { + icon_size: NonZeroU16::new(new_value).unwrap_or(icon_size), + ..config }) - .step(25u16), - ), - ) - .add( - settings::item::builder(fl!("grid-spacing")) - .description(format!("{grid_spacing}%")) - .control( - widget::slider(50..=500, grid_spacing.get(), move |new_value| { - Message::DesktopConfig(DesktopConfig { - grid_spacing: NonZeroU16::new(new_value).unwrap_or(grid_spacing), - ..config - }) - }) - .step(25u16), - ), - ); + }) + .step(25u16), + ), + ); - widget::column::with_capacity(2) + let grid_spacing = config.grid_spacing; + section = section.add( + widget::settings::item::builder(fl!("grid-spacing")) + .description(format!("{grid_spacing}%")) + .control( + widget::slider(50..=500, grid_spacing.get(), move |new_value| { + Message::DesktopConfig(DesktopConfig { + grid_spacing: NonZeroU16::new(new_value).unwrap_or(grid_spacing), + ..config + }) + }) + .step(25u16), + ), + ); + column = column.push(section); + + column .padding([0, space_l, space_l, space_l]) .spacing(space_m) - .push(show_on_desktop) - .push(icon_size_and_spacing) .into() } fn edit_history(&self) -> Element<'_, Message> { - let cosmic_theme::Spacing { space_m, .. } = theme::spacing(); + let cosmic_theme::Spacing { space_m, .. } = theme::active().cosmic().spacing; let mut children = Vec::new(); @@ -2229,8 +1899,7 @@ impl App { let progress = controller.progress(); section = section.add(widget::column::with_children([ widget::row::with_children([ - widget::determinate_linear(progress) - .width(Length::Fill) + widget::progress_bar(0.0..=1.0, progress) .girth(progress_bar_height) .into(), if controller.is_paused() { @@ -2308,7 +1977,7 @@ impl App { kind: &'a PreviewKind, context_drawer: bool, ) -> Element<'a, tab::Message> { - let cosmic_theme::Spacing { space_l, .. } = theme::spacing(); + let cosmic_theme::Spacing { space_l, .. } = theme::active().cosmic().spacing; let mut children = Vec::with_capacity(1); let entity = entity_opt.unwrap_or_else(|| self.tab_model.active()); @@ -2341,9 +2010,7 @@ impl App { match (selected.next(), selected.next()) { // At least two selected items - (Some(_), Some(_)) => { - Some(tab.multi_preview_view(Some(&self.mime_app_cache))) - } + (Some(_), Some(_)) => Some(tab.multi_preview_view()), // Exactly one selected item (Some(item), None) => { Some(item.preview_view(Some(&self.mime_app_cache), military_time)) @@ -2378,8 +2045,8 @@ impl App { let tab_config = self.config.tab; // TODO: Should dialog be updated here too? - settings::view_column(vec![ - settings::section() + widget::settings::view_column(vec![ + widget::settings::section() .title(fl!("appearance")) .add({ let app_theme_selected = match self.config.app_theme { @@ -2387,7 +2054,7 @@ impl App { AppTheme::Light => 2, AppTheme::System => 0, }; - settings::item::builder(fl!("theme")).control(widget::dropdown( + widget::settings::item::builder(fl!("theme")).control(widget::dropdown( &self.app_themes, Some(app_theme_selected), move |index| { @@ -2400,32 +2067,31 @@ impl App { )) }) .into(), - settings::section() + widget::settings::section() .title(fl!("type-to-search")) - .add( - settings::item::builder(fl!("type-to-search-recursive")).radio( - TypeToSearch::Recursive, - Some(self.config.type_to_search), - Message::SetTypeToSearch, - ), - ) - .add( - settings::item::builder(fl!("type-to-search-enter-path")).radio( - TypeToSearch::EnterPath, - Some(self.config.type_to_search), - Message::SetTypeToSearch, - ), - ) - .add(settings::item::builder(fl!("type-to-search-select")).radio( + .add(widget::radio( + widget::text::body(fl!("type-to-search-recursive")), + TypeToSearch::Recursive, + Some(self.config.type_to_search), + Message::SetTypeToSearch, + )) + .add(widget::radio( + widget::text::body(fl!("type-to-search-enter-path")), + TypeToSearch::EnterPath, + Some(self.config.type_to_search), + Message::SetTypeToSearch, + )) + .add(widget::radio( + widget::text::body(fl!("type-to-search-select")), TypeToSearch::SelectByPrefix, Some(self.config.type_to_search), Message::SetTypeToSearch, )) .into(), - settings::section() + widget::settings::section() .title(fl!("other")) .add({ - settings::item::builder(fl!("single-click")).toggler( + widget::settings::item::builder(fl!("single-click")).toggler( tab_config.single_click, move |single_click| { Message::TabConfig(TabConfig { @@ -2436,136 +2102,51 @@ impl App { ) }) .add({ - settings::item::builder(fl!("show-recents")) + widget::settings::item::builder(fl!("show-recents")) .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::ALL - .iter() - .copied() - .filter(|a| !enabled.contains(a)) - .collect(); + fn get_apps_for_mime(&self, mime_type: &Mime) -> Vec<(&MimeApp, MimeAppMatch)> { + let mut results = Vec::new(); - let space_xxs = theme::active().cosmic().spacing.space_xxs; + let mut dedupe = FxHashSet::default(); - let drag_icon = |size: u16| -> Element<'static, Message> { - widget::icon::from_name("list-drag-handle-symbolic") - .size(size) - .into() - }; + // start with exact matches + results.extend( + self.mime_app_cache + .get(mime_type) + .iter() + .filter(|&mime_app| dedupe.insert(&mime_app.id)) + .map(|mime_app| (mime_app, MimeAppMatch::Exact)), + ); - 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::(row_container) - .drag_content(move || ToolbarActionPayload(action.to_u8())); - widget::dnd_destination(source, vec![std::borrow::Cow::Borrowed(TOOLBAR_MIME)]) - .data_received_for::( - move |payload: Option| { - 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)); + // grab matches based off of subclass / parent mime type + if let Some(parent_types) = mime_icon::parent_mime_types(mime_type) { + for parent_type in parent_types { + results.extend( + self.mime_app_cache + .get(&parent_type) + .iter() + .filter(|&mime_app| dedupe.insert(&mime_app.id)) + .map(|mime_app| (mime_app, MimeAppMatch::Related)), + ); } } - 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() + // Add other apps + results.extend( + self.mime_app_cache + .apps() + .iter() + .filter(|&mime_app| dedupe.insert(&mime_app.id)) + .map(|mime_app| (mime_app, MimeAppMatch::Other)), + ); + + results } // Update favorites based on renaming or moving dirs. @@ -2576,33 +2157,15 @@ impl App { .favorites .iter() .map(|favorite| { - match favorite { - Favorite::Path(path) => { - for (from, to) in path_changes.iter().map(|(f, t)| (f.as_ref(), t.as_ref())) + if let Favorite::Path(path) = favorite { + for (from, to) in path_changes.iter().map(|(f, t)| (f.as_ref(), t.as_ref())) { + if path.starts_with(from) + && let Ok(relative) = path.strip_prefix(from) { - if path.starts_with(from) - && let Ok(relative) = path.strip_prefix(from) - { - favorites_changed = true; - return Favorite::from_path(to.join(relative)); - } + favorites_changed = true; + return Favorite::from_path(to.join(relative)); } } - Favorite::Named { path, name } => { - for (from, to) in path_changes.iter().map(|(f, t)| (f.as_ref(), t.as_ref())) - { - if path.starts_with(from) - && let Ok(relative) = path.strip_prefix(from) - { - favorites_changed = true; - return Favorite::Named { - path: to.join(relative), - name: name.clone(), - }; - } - } - } - _ => {} } favorite.clone() }) @@ -2712,16 +2275,12 @@ impl Application for App { ), ]); - if matches!(flags.mode, Mode::Desktop) { - core.set_auto_blur(Auto::Window | Auto::Popup); - } let mut app = Self { core, about, 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, @@ -2832,17 +2391,9 @@ impl Application for App { .on_middle_press(|entity| { cosmic::Action::App(Message::NavMenuAction(NavMenuAction::OpenInNewTab(entity))) }) - .context_menu(self.nav_context_menu()) - .close_icon(icon::from_name("media-eject-symbolic").size(16).icon()); - - #[cfg(feature = "wayland")] - { - nav = nav - .window_id_maybe(self.core().main_window_id()) - .on_surface_action(|action| cosmic::Action::Surface(action.flatten())) - } - - let mut nav = nav.into_container(); + .context_menu(self.nav_context_menu(self.nav_bar_context_id)) + .close_icon(icon::from_name("media-eject-symbolic").size(16).icon()) + .into_container(); if !self.core.is_condensed() { nav = nav.max_width(280); @@ -2853,105 +2404,77 @@ impl Application for App { )) } - fn nav_context_menu(&self) -> Option>>> { - let items = self.nav_model.iter().map(|entity| { - let favorite_index_opt = self.nav_model.data::(entity); - let location_opt = self.nav_model.data::(entity); + fn nav_context_menu( + &self, + entity: widget::nav_bar::Id, + ) -> Option>>> { + let favorite_index_opt = self.nav_model.data::(entity); + let location_opt = self.nav_model.data::(entity); - let mut items: Vec> = Vec::with_capacity(7); + let mut items = Vec::with_capacity(7); - if location_opt - .and_then(Location::path_opt) - .is_some_and(|x| x.is_file()) - { - items.push(cosmic::widget::menu::Item::Button( - fl!("open"), - None, - NavMenuAction::Open(entity), - )); - items.push(cosmic::widget::menu::Item::Button( - fl!("menu-open-with"), - None, - NavMenuAction::OpenWith(entity), - )); - } else { - items.push(cosmic::widget::menu::Item::Button( - fl!("open-in-new-tab"), - None, - NavMenuAction::OpenInNewTab(entity), - )); - items.push(cosmic::widget::menu::Item::Button( - fl!("open-in-new-window"), - None, - NavMenuAction::OpenInNewWindow(entity), - )); - } - if let Some(path) = location_opt.and_then(Location::path_opt) { - let selected_dir = usize::from(path.is_dir()); - let action_items: Vec<_> = self - .config - .context_actions - .iter() - .enumerate() - .filter(|(_, action)| action.matches_selection(1, selected_dir)) - .map(|(i, action)| { - cosmic::widget::menu::Item::Button( - action.name.clone(), - None, - NavMenuAction::RunContextAction(entity, i), - ) - }) - .collect(); + if location_opt + .and_then(Location::path_opt) + .is_some_and(|x| x.is_file()) + { + items.push(cosmic::widget::menu::Item::Button( + fl!("open"), + None, + NavMenuAction::Open(entity), + )); + items.push(cosmic::widget::menu::Item::Button( + fl!("menu-open-with"), + None, + NavMenuAction::OpenWith(entity), + )); + } else { + items.push(cosmic::widget::menu::Item::Button( + fl!("open-in-new-tab"), + None, + NavMenuAction::OpenInNewTab(entity), + )); + items.push(cosmic::widget::menu::Item::Button( + fl!("open-in-new-window"), + None, + NavMenuAction::OpenInNewWindow(entity), + )); + } + items.push(cosmic::widget::menu::Item::Divider); + if matches!(location_opt, Some(Location::Path(..))) { + items.push(cosmic::widget::menu::Item::Button( + fl!("show-details"), + None, + NavMenuAction::Preview(entity), + )); + } + items.push(cosmic::widget::menu::Item::Divider); + if favorite_index_opt.is_some() { + items.push(cosmic::widget::menu::Item::Button( + fl!("remove-from-sidebar"), + None, + NavMenuAction::RemoveFromSidebar(entity), + )); + } - if !action_items.is_empty() { - items.push(cosmic::widget::menu::Item::Divider); - items.extend(action_items); - } - } - items.push(cosmic::widget::menu::Item::Divider); - if matches!(location_opt, Some(Location::Path(..))) { - items.push(cosmic::widget::menu::Item::Button( - fl!("show-details"), - None, - NavMenuAction::Preview(entity), - )); - } - items.push(cosmic::widget::menu::Item::Divider); - if favorite_index_opt.is_some() { - items.push(cosmic::widget::menu::Item::Button( - fl!("change-sidebar-label"), - None, - NavMenuAction::ChangeSidebarLabel(entity), - )); - items.push(cosmic::widget::menu::Item::Button( - fl!("remove-from-sidebar"), - None, - NavMenuAction::RemoveFromSidebar(entity), - )); - } + if matches!(location_opt, Some(Location::Recents)) && tab::has_recents() { + items.push(cosmic::widget::menu::Item::Button( + fl!("clear-recents-history"), + None, + NavMenuAction::ClearRecents, + )); + } - if matches!(location_opt, Some(Location::Recents)) && tab::has_recents() { - items.push(cosmic::widget::menu::Item::Button( - fl!("clear-recents-history"), - None, - NavMenuAction::ClearRecents, - )); - } + if matches!(location_opt, Some(Location::Trash)) + && !trash::os_limited::is_empty().unwrap_or(true) + { + items.push(cosmic::widget::menu::Item::Button( + fl!("empty-trash"), + None, + NavMenuAction::EmptyTrash, + )); + } - if matches!(location_opt, Some(Location::Trash)) && !Trash::is_empty() { - items.push(cosmic::widget::menu::Item::Button( - fl!("empty-trash"), - None, - NavMenuAction::EmptyTrash, - )); - } - items - }); - - Some(cosmic::widget::menu::nav_context( - &HashMap::new(), - items.collect(), - )) + Some(cosmic::widget::menu::items(&HashMap::new(), items)) } fn nav_model(&self) -> Option<&segmented_button::SingleSelectModel> { @@ -2990,14 +2513,10 @@ impl Application for App { }) && let Some(mounter) = MOUNTERS.get(&key) { - return mounter.network_drive(uri.clone()).map(move |mounted| { - if mounted { - cosmic::Action::App(Message::NetworkDriveOpenEntityAfterMount { - entity, - }) - } else { - cosmic::action::none() - } + return mounter.network_drive(uri.clone()).map(move |()| { + cosmic::Action::App(Message::NetworkDriveOpenEntityAfterMount { + entity, + }) }); } @@ -3107,6 +2626,18 @@ impl Application for App { return cosmic::task::message(cosmic::action::app(Message::SetShowDetails(false))); } if let Some(tab) = self.tab_model.data_mut::(entity) { + if tab.location_context_menu_index.is_some() { + tab.location_context_menu_index = None; + return Task::none(); + } + + if tab.context_menu.is_some() { + return self.update(Message::TabMessage( + Some(entity), + tab::Message::ContextMenu(None, None), + )); + } + if tab.edit_location.is_some() { tab.edit_location = None; return Task::none(); @@ -3202,8 +2733,7 @@ impl Application for App { } else { Favorite::from_path(path) }; - let favorite_path = favorite.path_opt(); - if !favorites.iter().any(|f| f.path_opt() == favorite_path) { + if !favorites.contains(&favorite) { favorites.push(favorite); } } @@ -3237,12 +2767,10 @@ impl Application for App { Message::Config(config) => { if config != self.config { log::info!("update config"); - // Show details and military time are preserved for existing instances + // Show details is preserved for existing instances let show_details = self.config.show_details; - let military_time = self.config.tab.military_time; self.config = config; self.config.show_details = show_details; - self.config.tab.military_time = military_time; return self.update_config(); } } @@ -3253,12 +2781,7 @@ impl Application for App { tab.refresh_cut(&[]); } let paths = self.selected_paths(entity_opt); - self.clipboard_cache = ClipboardCache::Files(ClipboardPaste { - paths: paths.map(|p| p.to_path_buf()).collect(), - kind: ClipboardKind::Copy, - }); - let contents = - ClipboardCopy::new(ClipboardKind::Copy, self.selected_paths(entity_opt)); + let contents = ClipboardCopy::new(ClipboardKind::Copy, paths); return clipboard::write_data(contents); } Message::CopyPath(entity_opt) => { @@ -3299,15 +2822,7 @@ impl Application for App { Message::Cut(entity_opt) => { self.set_cut(entity_opt); let paths = self.selected_paths(entity_opt); - self.clipboard_cache = ClipboardCache::Files(ClipboardPaste { - paths: paths.map(|p| p.to_path_buf()).collect(), - kind: ClipboardKind::Cut { is_dnd: false }, - }); - let contents = ClipboardCopy::new( - ClipboardKind::Cut { is_dnd: false }, - self.selected_paths(entity_opt), - ); - + let contents = ClipboardCopy::new(ClipboardKind::Cut { is_dnd: false }, paths); return clipboard::write_data(contents); } Message::CloseToast(id) => { @@ -3451,9 +2966,6 @@ impl Application for App { DialogPage::FailedOperation(id) => { log::warn!("TODO: retry operation {id}"); } - DialogPage::FailedOperations(_ids) => { - log::warn!("TODO: retry operations"); - } DialogPage::ExtractPassword { id, password } => { let (operation, _, _err) = self.failed_operations.get(&id).unwrap(); let new_op = match &operation { @@ -3503,25 +3015,13 @@ impl Application for App { Operation::NewFile { path } })); } - DialogPage::RunContextAction { action, paths } => { - context_action::run(&self.config.context_actions, action, &paths); - } DialogPage::OpenWith { path, mime, selected, - search_app_name, - set_default, .. } => { - let mut available_apps = - self.mime_app_cache.get_apps_for_mime(&mime, true); - available_apps.retain(|(app, _)| { - app.name - .to_lowercase() - .trim() - .contains(search_app_name.to_lowercase().as_str().trim()) - }); + let available_apps = self.get_apps_for_mime(&mime); if let Some((app, _)) = available_apps.get(selected) { if let Some(mut command) = @@ -3537,11 +3037,6 @@ 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!( @@ -3567,18 +3062,6 @@ impl Application for App { DialogPage::DeleteTrash { items } => { tasks.push(self.operation(Operation::DeleteTrash { items })); } - DialogPage::ChangeSidebarLabel { entity, label } => { - if let Some(FavoriteIndex(favorite_i)) = - self.nav_model.data::(entity) - { - let mut favorites = self.config.favorites.clone(); - if let Some(favorite) = favorites.get_mut(*favorite_i) { - *favorite = favorite.with_label(label.trim()); - config_set!(favorites, favorites); - tasks.push(self.update_config()); - } - } - } DialogPage::RenameItem { from, parent, name, .. } => { @@ -3665,7 +3148,7 @@ impl Application for App { return dialog.update(dialog_message); } } - Message::Key(window_id, modifiers, key, physical_key, text) => { + Message::Key(window_id, modifiers, key, text) => { #[cfg(all(feature = "wayland", feature = "desktop-applet"))] let in_surface_ids = self.surface_ids.values().any(|id| *id == window_id); #[cfg(not(all(feature = "wayland", feature = "desktop-applet")))] @@ -3673,7 +3156,7 @@ impl Application for App { if self.core.main_window_id() == Some(window_id) || in_surface_ids { let entity = self.tab_model.active(); for (key_bind, action) in &self.key_binds { - if key_bind.matches(modifiers, &key, Some(&physical_key)) { + if key_bind.matches(modifiers, &key) { return self.update(action.message(Some(entity))); } } @@ -3864,7 +3347,7 @@ impl Application for App { Message::Mouse(window_id, _button) => { // Close context menu when clicking outside. if self.core.main_window_id() == Some(window_id) { - return Task::none(); + return self.close_context_menus(); } } Message::MoveTo(entity_opt) => { @@ -3917,7 +3400,7 @@ impl Application for App { Some((*mounter_key, self.network_drive_input.clone())); return mounter .network_drive(self.network_drive_input.clone()) - .map(|_| cosmic::action::none()); + .map(|()| cosmic::action::none()); } log::warn!( "no mounter found for connecting to {:?}", @@ -4153,21 +3636,6 @@ impl Application for App { } None => {} }, - Message::EditDesktopEntry(entity_opt) => { - let entity = entity_opt.unwrap_or_else(|| self.tab_model.active()); - if let Some(tab) = self.tab_model.data::(entity) - && let Some(items) = tab.items_opt() - { - let paths: Vec = items - .iter() - .filter(|item| item.selected) - .filter_map(|item| item.path_opt().cloned()) - .collect(); - if !paths.is_empty() { - return self.edit_file(&paths); - } - } - } Message::OpenWithDialog(entity_opt) => { let entity = entity_opt.unwrap_or_else(|| self.tab_model.active()); if let Some(tab) = self.tab_model.data::(entity) @@ -4180,25 +3648,20 @@ impl Application for App { let Some(path) = item.path_opt() else { continue; }; - return Task::batch([ - self.push_dialog( - DialogPage::OpenWith { - path: path.clone(), - mime: item.mime.clone(), - selected: 0, - store_opt: "x-scheme-handler/mime" - .parse::() - .ok() - .and_then(|mime| { - self.mime_app_cache.get(&mime).first().cloned() - }), - search_app_name: String::new(), - set_default: false, - }, - Some(CONFIRM_OPEN_WITH_BUTTON_ID.clone()), - ), - widget::text_input::focus(self.dialog_text_input.clone()), - ]); + return self.push_dialog( + DialogPage::OpenWith { + path: path.clone(), + mime: item.mime.clone(), + selected: 0, + store_opt: "x-scheme-handler/mime" + .parse::() + .ok() + .and_then(|mime| { + self.mime_app_cache.get(&mime).first().cloned() + }), + }, + Some(CONFIRM_OPEN_WITH_BUTTON_ID.clone()), + ); } } } @@ -4207,21 +3670,6 @@ 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::OpenWithSearchClear => { - if let Some(DialogPage::OpenWith { - search_app_name, .. - }) = self.dialog_pages.front_mut() - { - *search_app_name = String::new(); - } - } Message::Paste(entity_opt) => { let entity = entity_opt.unwrap_or_else(|| self.tab_model.active()); if let Some(tab) = self.tab_model.data_mut::(entity) @@ -4232,24 +3680,6 @@ impl Application for App { // Use cached clipboard data if available (needed for Wayland popups) match &self.clipboard_cache { ClipboardCache::Files(contents) => { - if contents.paths.is_empty() { - return iced::Task::future(tokio::time::sleep( - std::time::Duration::from_millis(300), - )) - .discard() - .chain( - clipboard::read_data::().map( - move |contents_opt| match contents_opt { - Some(contents) => cosmic::action::app( - Message::PasteContents(to.clone(), contents), - ), - None => { - cosmic::action::app(Message::PasteImage(to.clone())) - } - }, - ), - ); - } return self .update(Message::PasteContents(to.clone(), contents.clone())); } @@ -4397,13 +3827,10 @@ impl Application for App { // Check if clipboard has any paste-able content and cache it return clipboard::read_data::().map(|contents_opt| { match contents_opt { - Some(contents) if contents.paths.is_empty() => cosmic::action::app( - Message::RetryCheckClipboard(ClipboardCache::Files(contents)), - ), Some(contents) => cosmic::action::app(Message::ClipboardCached( ClipboardCache::Files(contents), )), - _ => cosmic::action::app(Message::CheckClipboardImage), + None => cosmic::action::app(Message::CheckClipboardImage), } }); } @@ -4435,28 +3862,6 @@ impl Application for App { })) }); } - Message::RetryCheckClipboard(cache) => { - let mut cmds = Vec::new(); - cmds.push(self.update(Message::ClipboardCached(cache))); - - cmds.push( - iced::Task::future(tokio::time::sleep(Duration::from_millis(300))) - .discard() - .chain( - clipboard::read_data::().map(|contents_opt| { - match contents_opt { - Some(contents) if !contents.paths.is_empty() => { - cosmic::action::app(Message::ClipboardCached( - ClipboardCache::Files(contents), - )) - } - _ => cosmic::action::app(Message::CheckClipboardImage), - } - }), - ), - ); - return Task::batch(cmds); - } Message::ClipboardCached(cache) => { self.clipboard_cache = cache; } @@ -4473,19 +3878,106 @@ impl Application for App { } } Message::PendingComplete(id, op_sel) => { - return self.handle_completed_operations(vec![(id, op_sel)]); + let mut commands = Vec::with_capacity(4); + if let Some((op, _)) = self.pending_operations.remove(&id) { + // Show toast for some operations + if let Some(description) = op.toast() { + if let Operation::Delete { ref paths } = op { + let paths: Arc<[PathBuf]> = Arc::from(paths.as_slice()); + commands.push( + self.toasts + .push( + widget::toaster::Toast::new(description) + .action(fl!("undo"), move |tid| { + Message::UndoTrash(tid, paths.clone()) + }), + ) + .map(cosmic::Action::App), + ); + } else { + commands.push( + self.toasts + .push(widget::toaster::Toast::new(description)) + .map(cosmic::Action::App), + ); + } + } + + // If a favorite for a path has been renamed or moved, update it. + if let Operation::Rename { ref from, ref to } = op { + if self.update_favorites([(from, to)].as_slice()) { + commands.push(self.update_config()); + } + } else if let Operation::Move { + ref paths, ref to, .. + } = op + { + let path_changes: Box<[_]> = paths + .iter() + .filter_map(|from| from.file_name().map(|name| (from, to.join(name)))) + .collect(); + if self.update_favorites(&path_changes) { + commands.push(self.update_config()); + } + } + + if matches!(op, Operation::RemoveFromRecents { .. }) { + commands.push(self.rescan_recents()); + } + + self.complete_operations.insert(id, op); + } + // Close progress notification if all relevant operations are finished + if !self + .pending_operations + .values() + .any(|(op, _)| op.show_progress_notification()) + { + self.progress_operations.clear(); + } + // Potentially show a notification + commands.push(self.update_notification()); + // Rescan and select based on operation + commands.push(self.rescan_operation_selection(op_sel)); + // Manually rescan any trash tabs after any operation is completed + commands.push(self.rescan_trash()); + + return Task::batch(commands); } Message::PendingDismiss => { self.progress_operations.clear(); } Message::PendingError(id, err) => { - return self.handle_operation_errors(vec![(id, err)]); - } - Message::PendingResults(completed, errors) => { - return Task::batch(vec![ - self.handle_completed_operations(completed), - self.handle_operation_errors(errors), - ]); + let mut tasks = Vec::new(); + if let Some((op, controller)) = self.pending_operations.remove(&id) { + // Only show dialog if not cancelled + if !controller.is_cancelled() { + tasks.push(self.dialog_pages.push_back(match err.kind { + OperationErrorType::Generic(_) => DialogPage::FailedOperation(id), + OperationErrorType::PasswordRequired => DialogPage::ExtractPassword { + id, + password: String::new(), + }, + })); + } + tasks.push(widget::text_input::focus(self.dialog_text_input.clone())); + + // Remove from progress + self.progress_operations.remove(&id); + self.failed_operations + .insert(id, (op, controller, err.to_string())); + } + // Close progress notification if all relevant operations are finished + if !self + .pending_operations + .values() + .any(|(op, _)| op.show_progress_notification()) + { + self.progress_operations.clear(); + } + // Manually rescan any trash tabs after any operation is completed + tasks.push(self.rescan_trash()); + return Task::batch(tasks); } Message::PendingPause(id, pause) => { if let Some((_, controller)) = self.pending_operations.get(&id) { @@ -4568,9 +4060,6 @@ impl Application for App { let paths: Box<[_]> = self.selected_paths(entity_opt).collect(); return self.operation(Operation::RemoveFromRecents { paths }); } - Message::ReloadMimeAppCache => { - self.mime_app_cache.reload(); - } Message::RescanRecents => { return self.rescan_recents(); } @@ -4582,8 +4071,10 @@ impl Application for App { .is_some_and(|loc| matches!(loc, Location::Trash)) }); if let Some(entity) = maybe_entity { - self.nav_model - .icon_set(entity, icon::icon(Trash::icon_symbolic(16))); + self.nav_model.icon_set( + entity, + icon::icon(tab::trash_helpers::trash_icon_symbolic(16)), + ); } return Task::batch([self.rescan_trash(), self.update_desktop()]); @@ -4605,14 +4096,12 @@ impl Application for App { .collect(); if !selected.is_empty() { //TODO: batch rename - let mut last_name = String::new(); - let tasks: Vec<_> = selected + let tasks = selected .into_iter() .filter_map(|path| { let parent = path.parent()?.to_path_buf(); let name = path.file_name()?.to_str()?.to_string(); let dir = path.is_dir(); - last_name = name.clone(); Some(self.dialog_pages.push_back(DialogPage::RenameItem { from: path, parent, @@ -4620,15 +4109,9 @@ impl Application for App { dir, })) }) - .collect(); - let tasks = tasks.into_iter().chain([ - widget::text_input::focus(self.dialog_text_input.clone()), - widget::text_input::select_until_last( + .chain(std::iter::once(widget::text_input::focus( self.dialog_text_input.clone(), - &last_name, - '.', - ), - ]); + ))); return Task::batch(tasks); } } @@ -4677,7 +4160,7 @@ impl Application for App { )); } Message::SearchActivate => { - let mut tasks = vec![]; + let mut tasks = vec![self.close_context_menus()]; if self.search_get().is_none() { tasks.push(self.search_set_active(Some(String::new()))); @@ -4688,7 +4171,7 @@ impl Application for App { return Task::batch(tasks); } Message::SearchClear => { - return self.search_set_active(None); + return Task::batch([self.close_context_menus(), self.search_set_active(None)]); } Message::SearchInput(input) => { return self.search_set_active(Some(input)); @@ -4701,86 +4184,6 @@ 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::(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(); @@ -4789,7 +4192,7 @@ impl Application for App { return self.update_config(); } Message::TabActivate(entity) => { - let mut tasks = vec![]; + let mut tasks = vec![self.close_context_menus()]; // Activate new tab self.tab_model.activate(entity); @@ -4851,9 +4254,7 @@ impl Application for App { tasks.push(Task::future(async move { cosmic::action::app(Message::WindowClose) })); - } else if entity == self.tab_model.active() - && let Some(position) = self.tab_model.position(entity) - { + } else if let Some(position) = self.tab_model.position(entity) { let new_position = if position > 0 { position - 1 } else { @@ -4890,8 +4291,6 @@ impl Application for App { } Message::TabMessage(entity_opt, tab_message) => { let entity = entity_opt.unwrap_or_else(|| self.tab_model.active()); - // The context menu opens on right-button release, so refresh paste availability now - let right_click = matches!(tab_message, tab::Message::RightClick(..)); let tab_commands = match self.tab_model.data_mut::(entity) { Some(tab) => tab.update(tab_message, self.modifiers), @@ -4899,9 +4298,6 @@ impl Application for App { }; let mut commands = Vec::new(); - if right_click { - commands.push(self.update(Message::CheckClipboard)); - } for tab_command in tab_commands { match tab_command { tab::Command::Action(action) => { @@ -4914,8 +4310,7 @@ impl Application for App { tab::Command::AddToSidebar(path) => { let mut favorites = self.config.favorites.clone(); let favorite = Favorite::from_path(path); - let favorite_path = favorite.path_opt(); - if !favorites.iter().any(|f| f.path_opt() == favorite_path) { + if !favorites.contains(&favorite) { favorites.push(favorite); } config_set!(favorites, favorites); @@ -4942,24 +4337,81 @@ impl Application for App { self.update_tab(entity, tab_path, selection_paths), ])); } - tab::Command::Surface(action) => { - // re-type the tab's surface action the way its messages are re-typed - let action = action - .map(move |message| Message::TabMessage(Some(entity), message)); - commands.push(self.update(Message::Surface(action))); + tab::Command::ContextMenu(point_opt, parent_id) => { + #[cfg(feature = "wayland")] + if let Some(point) = point_opt { + if crate::is_wayland() { + // Open context menu + use cctk::wayland_protocols::xdg::shell::client::xdg_positioner::{ + Anchor, Gravity, + }; + use cosmic::iced_runtime::platform_specific::wayland::popup::{ + SctkPopupSettings, SctkPositioner, + }; + let window_id = WindowId::unique(); + self.windows.insert( + window_id, + Window::new(WindowKind::ContextMenu( + entity, + widget::Id::unique(), + )), + ); + commands.push(self.update(Message::Surface( + cosmic::surface::action::app_popup( + move |app: &mut Self| -> SctkPopupSettings { + let anchor_rect = Rectangle { + x: point.x as i32, + y: point.y as i32, + width: 1, + height: 1, + }; + let positioner = SctkPositioner { + size: None, + anchor_rect, + anchor: Anchor::None, + gravity: Gravity::BottomRight, + reactive: true, + ..Default::default() + }; + SctkPopupSettings { + parent: parent_id.unwrap_or( + app.core + .main_window_id() + .unwrap_or(WindowId::NONE), + ), + id: window_id, + positioner, + parent_size: None, + grab: true, + close_with_children: false, + input_zone: None, + } + }, + None, + ), + ))); + } + } else { + // Destroy previous popup + let mut window_ids = Vec::new(); + for (window_id, window) in &self.windows { + if let WindowKind::ContextMenu(e, _) = &window.kind + && *e == entity + { + window_ids.push(*window_id); + } + } + for window_id in window_ids { + commands.push(self.update(Message::Surface( + cosmic::surface::action::destroy_popup(window_id), + ))); + } + } } tab::Command::Delete(paths) => commands.push(self.delete(paths)), tab::Command::DropFiles(to, from) => { commands.push(self.update(Message::PasteContents(to, from))); } - tab::Command::ClearRecents => { - match recently_used_xbel::clear_recently_used() { - Ok(()) => {} - Err(err) => { - log::warn!("failed to clear recents history: {}", err); - } - } - } tab::Command::EmptyTrash => { return self.push_dialog( DialogPage::EmptyTrash, @@ -4970,25 +4422,6 @@ impl Application for App { tab::Command::ExecEntryAction(entry, action) => { Self::exec_entry_action(&entry, action); } - tab::Command::RunContextAction(action) => { - let paths: Box<[_]> = self.selected_paths(Some(entity)).collect(); - if let Some(preset) = self.config.context_actions.get(action) { - if preset.confirm { - commands.push(self.push_dialog( - DialogPage::RunContextAction { action, paths }, - Some(CONFIRM_CONTEXT_ACTION_BUTTON_ID.clone()), - )); - } else { - context_action::run( - &self.config.context_actions, - action, - &paths, - ); - } - } else { - log::warn!("invalid context action index `{action}`"); - } - } tab::Command::Iced(iced_command) => { commands.push(iced_command.0.map(move |x| { cosmic::action::app(Message::TabMessage(Some(entity), x)) @@ -4996,6 +4429,7 @@ impl Application for App { } tab::Command::OpenFile(paths) => commands.push(self.open_file(&paths)), tab::Command::OpenInNewTab(path) => { + commands.push(self.close_context_menus()); commands.push(self.open_tab(Location::Path(path), false, None)); } tab::Command::OpenInNewWindow(path) => match env::current_exe() { @@ -5031,19 +4465,6 @@ impl Application for App { tab::Command::SetPermissions(path, mode) => { commands.push(self.operation(Operation::SetPermissions { path, mode })); } - tab::Command::SetMultiplePermissions(permissions) => { - commands.push( - self.join_operations( - permissions - .into_iter() - .map(|(path, mode)| Operation::SetPermissions { - path, - mode, - }) - .collect(), - ), - ); - } tab::Command::WindowDrag => { if let Some(window_id) = self.core.main_window_id() { commands.push(window::drag(window_id)); @@ -5432,6 +4853,12 @@ impl Application for App { if let Some(tab) = self.tab_model.data_mut::(tab_entity) { // Close location editing if enabled tab.edit_location = None; + // Close other context menus. + tab.location_context_menu_index = None; + return Task::done(cosmic::Action::App(Message::TabMessage( + Some(tab_entity), + tab::Message::ContextMenu(None, None), + ))); } } Message::NavMenuAction(action) => match action { @@ -5475,8 +4902,6 @@ impl Application for App { .and_then(|mime| { self.mime_app_cache.get(&mime).first().cloned() }), - search_app_name: String::new(), - set_default: false, }, None, ); @@ -5491,30 +4916,6 @@ impl Application for App { } } } - NavMenuAction::RunContextAction(entity, action) => { - if let Some(path) = self - .nav_model - .data::(entity) - .and_then(Location::path_opt) - .cloned() - { - let paths = vec![path]; - if let Some(preset) = self.config.context_actions.get(action) { - if preset.confirm { - return self.push_dialog( - DialogPage::RunContextAction { - action, - paths: paths.into_boxed_slice(), - }, - Some(CONFIRM_CONTEXT_ACTION_BUTTON_ID.clone()), - ); - } - context_action::run(&self.config.context_actions, action, &paths); - } else { - log::warn!("invalid context action index `{action}`"); - } - } - } NavMenuAction::OpenInNewTab(entity) => { let open_task = match self.nav_model.data::(entity) { Some(Location::Network(uri, display_name, path)) => self.open_tab( @@ -5530,7 +4931,7 @@ impl Application for App { _ => Task::none(), }; - return open_task; + return Task::batch([self.close_context_menus(), open_task]); } // Open the selected path in a new cosmic-files window. @@ -5586,7 +4987,7 @@ impl Application for App { Ok(item) => { self.context_page = ContextPage::Preview( None, - PreviewKind::Custom(PreviewItem(Box::new(item))), + PreviewKind::Custom(PreviewItem(item)), ); self.set_show_context(true); } @@ -5611,20 +5012,6 @@ impl Application for App { return self.update_config(); } } - - NavMenuAction::ChangeSidebarLabel(entity) => { - if let Some(favorite) = self.nav_model.data::(entity).and_then( - |FavoriteIndex(favorite_i)| self.config.favorites.get(*favorite_i), - ) { - let label = favorite.display_name().unwrap_or_else(|| fl!("filesystem")); - return Task::batch([ - self.dialog_pages - .push_back(DialogPage::ChangeSidebarLabel { entity, label }), - widget::text_input::focus(self.dialog_text_input.clone()), - widget::text_input::select_all(self.dialog_text_input.clone()), - ]); - } - } }, Message::Recents => { if self.config.show_recents { @@ -5676,33 +5063,24 @@ impl Application for App { .insert(surface_id, Window::new(WindowKind::Desktop(entity))); return Task::batch([ command, - cosmic::task::message(cosmic::Action::Surface( - cosmic::surface::action::app_layer_shell( - |_| Default::default(), - move |_: &mut App| SctkLayerSurfaceSettings { - id: surface_id, - layer: Layer::Bottom, - keyboard_interactivity: KeyboardInteractivity::OnDemand, - input_zone: None, - anchor: Anchor::TOP - | Anchor::BOTTOM - | Anchor::LEFT - | Anchor::RIGHT, - output: IcedOutput::Output(output.clone()), - namespace: "cosmic-files-applet".into(), - size: Some((None, None)), - margin: IcedMargin { - top: 0, - bottom: 0, - left: 0, - right: 0, - }, - exclusive_zone: 0, - size_limits: Limits::NONE.min_width(1.0).min_height(1.0), - }, - None, - ), - )), + get_layer_surface(SctkLayerSurfaceSettings { + id: surface_id, + layer: Layer::Bottom, + keyboard_interactivity: KeyboardInteractivity::OnDemand, + input_zone: None, + anchor: Anchor::TOP | Anchor::BOTTOM | Anchor::LEFT | Anchor::RIGHT, + output: IcedOutput::Output(output), + namespace: "cosmic-files-applet".into(), + size: Some((None, None)), + margin: IcedMargin { + top: 0, + bottom: 0, + left: 0, + right: 0, + }, + exclusive_zone: 0, + size_limits: Limits::NONE.min_width(1.0).min_height(1.0), + }), #[cfg(all(feature = "wayland", feature = "desktop-applet"))] overlap_notify(surface_id, true), ]); @@ -5788,14 +5166,12 @@ impl Application for App { }; } // Check clipboard when window gains focus - // HACK: Wait a moment for the data to be available. - return cosmic::task::future(async { - _ = tokio::time::sleep(Duration::from_millis(300)).await; - cosmic::action::app(Message::CheckClipboard) - }); + return self.update(Message::CheckClipboard); } Message::Surface(action) => { - return cosmic::task::message(cosmic::Action::Surface(action)); + return cosmic::task::message(cosmic::Action::Cosmic( + cosmic::app::Action::Surface(action), + )); } Message::SaveSortNames => { self.must_save_sort_names = false; @@ -5914,7 +5290,7 @@ impl Application for App { let cosmic_theme::Spacing { space_xxs, space_s, .. - } = theme::spacing(); + } = theme::active().cosmic().spacing; let dialog = match dialog_page { DialogPage::Compress { @@ -6051,25 +5427,6 @@ impl Application for App { widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel), ) } - DialogPage::FailedOperations(ids) => { - let errors: Vec = ids - .iter() - .filter_map(|id| match self.failed_operations.get(id) { - Some((operation, _, err)) => Some(format!("{operation:#?}\n{err}")), - _ => None, - }) - .collect(); - - //TODO: nice description of error - widget::dialog() - .title("Failed operations") - .body(errors.join("\n\n")) - .icon(icon::from_name("dialog-error").size(64)) - //TODO: retry action - .primary_action( - widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel), - ) - } DialogPage::ExtractPassword { id, password } => widget::dialog() .title(fl!("extract-password-required")) .icon(icon::from_name("dialog-error").size(64)) @@ -6316,33 +5673,11 @@ impl Application for App { .spacing(space_xxs), ) } - DialogPage::RunContextAction { action, paths } => { - let name = self - .config - .context_actions - .get(*action) - .map_or_else(|| fl!("context-action"), |preset| preset.name.clone()); - - widget::dialog() - .title(fl!("context-action-confirm-title", name = name)) - .body(fl!("context-action-confirm-warning", items = paths.len())) - .icon(icon::from_name("dialog-error").size(64)) - .primary_action( - widget::button::suggested(fl!("run")) - .on_press(Message::DialogComplete) - .id(CONFIRM_CONTEXT_ACTION_BUTTON_ID.clone()), - ) - .secondary_action( - widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel), - ) - } DialogPage::OpenWith { path, mime, selected, store_opt, - search_app_name, - set_default, .. } => { let name = match path.file_name() { @@ -6351,13 +5686,7 @@ impl Application for App { }; let mut column = widget::list_column(); - let mut available_apps = self.mime_app_cache.get_apps_for_mime(mime, true); - available_apps.retain(|(app, _)| { - app.name - .to_lowercase() - .trim() - .contains(search_app_name.to_lowercase().as_str().trim()) - }); + let available_apps = self.get_apps_for_mime(mime); let item_height = 32.0; let mut displayed_default = false; let mut last_kind = MimeAppMatch::Exact; @@ -6378,8 +5707,8 @@ impl Application for App { widget::mouse_area( widget::button::custom( widget::row::with_children([ - icon(app.icon()).size(32).into(), - if app.is_default(mime) && !displayed_default { + icon(app.icon.clone()).size(32).into(), + if app.is_default && !displayed_default { displayed_default = true; widget::text::body(fl!( "default-app", @@ -6421,25 +5750,6 @@ impl Application for App { .secondary_action( widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel), ) - .control( - widget::text_input::search_input( - fl!("search-application"), - search_app_name, - ) - .id(self.dialog_text_input.clone()) - .on_clear(Message::OpenWithSearchClear) - .on_input(move |search_app_name| { - Message::DialogUpdate(DialogPage::OpenWith { - path: path.clone(), - mime: mime.clone(), - selected: *selected, - store_opt: store_opt.clone(), - search_app_name, - set_default: *set_default, - }) - }) - .on_submit(|_| Message::DialogComplete), - ) .control(widget::scrollable(column).height({ let max_size = self .size @@ -6453,21 +5763,7 @@ 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( @@ -6528,40 +5824,6 @@ impl Application for App { target = target ))) } - DialogPage::ChangeSidebarLabel { entity, label } => { - let entity = *entity; - let complete_maybe = if label.trim().is_empty() { - None - } else { - Some(Message::DialogComplete) - }; - - widget::dialog() - .title(fl!("change-sidebar-label")) - .primary_action( - widget::button::suggested(fl!("save")) - .on_press_maybe(complete_maybe.clone()), - ) - .secondary_action( - widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel), - ) - .control( - widget::column::with_children([ - widget::text::body(fl!("sidebar-label")).into(), - widget::text_input("", label.as_str()) - .id(self.dialog_text_input.clone()) - .on_input(move |label| { - Message::DialogUpdate(DialogPage::ChangeSidebarLabel { - entity, - label, - }) - }) - .on_submit_maybe(complete_maybe.map(|maybe| move |_| maybe.clone())) - .into(), - ]) - .spacing(space_xxs), - ) - } DialogPage::RenameItem { from, parent, @@ -6607,7 +5869,7 @@ impl Application for App { dialog .primary_action( - widget::button::suggested(fl!("rename-confirm")) + widget::button::suggested(fl!("rename")) .on_press_maybe(complete_maybe.clone()), ) .secondary_action( @@ -6623,7 +5885,6 @@ impl Application for App { .into(), widget::text_input("", name.as_str()) .id(self.dialog_text_input.clone()) - .double_click_select_delimiter('.') .on_input(move |name| { Message::DialogUpdate(DialogPage::RenameItem { from: from.clone(), @@ -6751,7 +6012,7 @@ impl Application for App { let cosmic_theme::Spacing { space_xs, space_s, .. - } = theme::spacing(); + } = theme::active().cosmic().spacing; let mut title = String::new(); let mut total_progress = 0.0; @@ -6799,9 +6060,8 @@ impl Application for App { //TODO: get height from theme? let progress_bar_height = Length::Fixed(4.0); - let progress_bar = widget::determinate_linear(total_progress) - .width(Length::Fill) - .girth(progress_bar_height); + let progress_bar = + widget::progress_bar(0.0..=1.0, total_progress).girth(progress_bar_height); let container = widget::layer_container(widget::column::with_children([ widget::row::with_children([ @@ -6907,11 +6167,8 @@ impl Application for App { /// Creates a view after each update. fn view(&self) -> Element<'_, Self::Message> { let cosmic_theme::Spacing { - space_xxs, - space_xs, - space_s, - .. - } = theme::spacing(); + space_xxs, space_s, .. + } = theme::active().cosmic().spacing; let mut tab_column = widget::column::with_capacity(4); @@ -6937,52 +6194,23 @@ impl Application for App { .button_height(32) .button_spacing(space_xxs) .enable_tab_drag(String::from("x-cosmic-files/tab-dnd")) - .on_reorder(Message::ReorderTab) + .on_reorder(move |event| Message::ReorderTab(event)) .tab_drag_threshold(25.) .on_activate(Message::TabActivate) .on_close(|entity| Message::TabClose(Some(entity))) - .on_dnd_enter(Message::DndEnterTab) + .on_dnd_enter(|entity, mimes| Message::DndEnterTab(entity, mimes)) .on_dnd_leave(|_| Message::DndExitTab) .on_dnd_drop(|entity, data, action| { Message::DndDropTab(entity, data, action) }) .drag_id(self.tab_drag_id), ) + .class(style::Container::Background) .width(Length::Fill) .padding([0, space_s]), ); } - // 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::(entity) { let tab_view = tab @@ -6990,7 +6218,6 @@ impl Application for App { &self.key_binds, &self.modifiers, self.clipboard_has_content(), - &self.config.context_actions, ) .map(move |message| Message::TabMessage(Some(entity), message)); tab_column = tab_column.push(tab_view); @@ -7011,6 +6238,22 @@ impl Application for App { fn view_window(&self, id: WindowId) -> Element<'_, Self::Message> { let content = match self.windows.get(&id) { Some(window) => match &window.kind { + WindowKind::ContextMenu(entity, id) => match self.tab_model.data::(*entity) { + Some(tab) => { + return widget::autosize::autosize( + menu::context_menu( + tab, + &self.key_binds, + &window.modifiers, + self.clipboard_has_content(), + ) + .map(|x| Message::TabMessage(Some(*entity), x)), + id.clone(), + ) + .into(); + } + None => widget::text("Unknown tab ID").into(), + }, WindowKind::Desktop(entity) => { let mut tab_column = widget::column::with_capacity(3); @@ -7020,7 +6263,6 @@ impl Application for App { &self.key_binds, &window.modifiers, self.clipboard_has_content(), - &self.config.context_actions, ) .map(move |message| Message::TabMessage(Some(*entity), message)), None => widget::space::vertical().into(), @@ -7070,7 +6312,6 @@ impl Application for App { return self.view_main().map(|message| match message { cosmic::Action::App(app) => app, cosmic::Action::Cosmic(cosmic) => Message::Cosmic(cosmic), - cosmic::Action::Surface(action) => Message::Surface(action), cosmic::Action::None => Message::None, }); } @@ -7114,14 +6355,11 @@ impl Application for App { }, Event::Keyboard(KeyEvent::KeyPressed { key, - physical_key, modifiers, text, .. }) => match status { - event::Status::Ignored => { - Some(Message::Key(window_id, modifiers, key, physical_key, text)) - } + event::Status::Ignored => Some(Message::Key(window_id, modifiers, key, text)), event::Status::Captured => None, }, Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => { @@ -7253,22 +6491,6 @@ impl Application for App { }, ) }), - #[cfg(feature = "desktop")] - Subscription::run(|| { - stream::channel( - 1, - |mut output: futures::channel::mpsc::Sender| async move { - mime_app::watch(move || { - futures::executor::block_on(async { - _ = output.send(Message::ReloadMimeAppCache).await; - }) - }) - .await; - - std::future::pending().await - }, - ) - }), Subscription::run_with(TypeId::of::(), |_| { stream::channel( 1, @@ -7302,7 +6524,14 @@ impl Application for App { }, ); - match (watcher_res, Trash::folders()) { + // TODO: Trash watching support for Windows, macOS, and other OSes + #[cfg(all( + unix, + not(target_os = "macos"), + not(target_os = "ios"), + not(target_os = "android") + ))] + match (watcher_res, trash::os_limited::trash_folders()) { (Ok(mut watcher), Ok(trash_bins)) => { // Watch the "bins" themselves as well as the files folder where // trashed items are placed. This allows us to avoid recursively @@ -7459,7 +6688,8 @@ impl Application for App { |_| { stream::channel( 1, - move |mut msg_tx: futures::channel::mpsc::Sender<_>| async move { + move |msg_tx: futures::channel::mpsc::Sender<_>| async move { + let msg_tx = Arc::new(tokio::sync::Mutex::new(msg_tx)); tokio::task::spawn_blocking(move || { match notify_rust::Notification::new() .summary(&fl!("notification-in-progress")) @@ -7469,6 +6699,8 @@ impl Application for App { Ok(notification) => { let _ = futures::executor::block_on(async { msg_tx + .lock() + .await .send(Message::Notification(Arc::new( Mutex::new(notification), ))) @@ -7535,17 +6767,21 @@ impl Application for App { // Ideally, tests would use the cap-std crate which limits path traversal. #[cfg(test)] pub(crate) mod test_utils { - use std::cmp::Ordering; - use std::fs::File; - use std::io::{self, Write}; - use std::iter; - use std::path::Path; + use std::{ + cmp::Ordering, + fs::File, + io::{self, Write}, + iter, + path::Path, + }; use log::{debug, trace}; use tempfile::{TempDir, tempdir}; - use crate::config::{IconSizes, TabConfig, ThumbCfg}; - use crate::tab::Item; + use crate::{ + config::{IconSizes, TabConfig, ThumbCfg}, + tab::Item, + }; use super::*; diff --git a/src/archive.rs b/src/archive.rs index d51b01b..5786ff7 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -1,14 +1,17 @@ -use crate::mime_icon::mime_for_path; -use crate::operation::{Controller, OpReader, OperationError, OperationErrorType}; +use crate::{ + mime_icon::mime_for_path, + operation::{Controller, OpReader, OperationError, OperationErrorType, sync_to_disk}, +}; +use chrono::TimeZone; +use chrono::{Datelike, Timelike}; use cosmic::iced::futures; -use jiff::Zoned; -use jiff::civil::DateTime; -use jiff::tz::TimeZone; -use std::collections::HashSet; -use std::fs; -use std::io::{self, Read, Write}; -use std::path::{Path, PathBuf}; -use std::time::SystemTime; +use std::{ + collections::HashSet, + fs, + io::{self, Read, Write}, + path::{Path, PathBuf}, + time::SystemTime, +}; use zip::result::ZipError; pub const SUPPORTED_ARCHIVE_TYPES: &[&str] = &[ @@ -45,7 +48,7 @@ pub fn extract( new_dir: &Path, password: &Option, controller: &Controller, -) -> Result<(Vec, HashSet), OperationError> { +) -> Result<(), OperationError> { let mime = mime_for_path(path, None, false); let password = password.as_deref(); match mime.essence_str() { @@ -55,15 +58,13 @@ pub fn extract( .map(flate2::read::GzDecoder::new) .map(tar::Archive::new) .and_then(|mut archive| archive.unpack(new_dir)) - .map_err(|e| OperationError::from_err(e, controller)) - .map(|_| Default::default()) + .map_err(|e| OperationError::from_err(e, controller))?; } "application/x-tar" => OpReader::new(path, controller.clone()) .map(io::BufReader::new) .map(tar::Archive::new) .and_then(|mut archive| archive.unpack(new_dir)) - .map_err(|e| OperationError::from_err(e, controller)) - .map(|_| Default::default()), + .map_err(|e| OperationError::from_err(e, controller))?, "application/zip" => fs::File::open(path) .map(io::BufReader::new) .map(zip::ZipArchive::new) @@ -77,7 +78,7 @@ pub fn extract( OperationError::from_kind(OperationErrorType::PasswordRequired, controller) } _ => OperationError::from_err(e, controller), - }), + })?, #[cfg(feature = "bzip2")] "application/x-bzip" | "application/x-bzip-compressed-tar" @@ -87,8 +88,7 @@ pub fn extract( .map(bzip2::read::BzDecoder::new) .map(tar::Archive::new) .and_then(|mut archive| archive.unpack(new_dir)) - .map_err(|e| OperationError::from_err(e, controller)) - .map(|_| Default::default()), + .map_err(|e| OperationError::from_err(e, controller))?, #[cfg(feature = "lzma-rust2")] "application/x-xz" | "application/x-xz-compressed-tar" => { OpReader::new(path, controller.clone()) @@ -96,14 +96,14 @@ pub fn extract( .map(|reader| lzma_rust2::XzReader::new(reader, true)) .map(tar::Archive::new) .and_then(|mut archive| archive.unpack(new_dir)) - .map_err(|e| OperationError::from_err(e, controller)) - .map(|_| Default::default()) + .map_err(|e| OperationError::from_err(e, controller))?; } _ => Err(OperationError::from_err( format!("unsupported mime type {mime:?}"), controller, - )), + ))?, } + Ok(()) } // From https://docs.rs/zip/latest/zip/read/struct.ZipArchive.html#method.extract, with cancellation and progress added @@ -112,9 +112,8 @@ fn zip_extract>( directory: P, password: Option<&str>, controller: Controller, -) -> zip::result::ZipResult<(Vec, HashSet)> { - use std::ffi::OsString; - use std::fs; +) -> zip::result::ZipResult<()> { + use std::{ffi::OsString, fs}; use zip::result::ZipError; fn make_writable_dir_all>( @@ -191,8 +190,6 @@ fn zip_extract>( if file.is_symlink() && (cfg!(unix) || cfg!(windows)) { let mut target = Vec::with_capacity(file.size() as usize); file.read_to_end(&mut target)?; - // File no longer needed, drop to allow reading target on windows - drop(file); #[cfg(unix)] { @@ -203,15 +200,11 @@ fn zip_extract>( #[cfg(windows)] { let Ok(target) = String::from_utf8(target) else { - return Err(ZipError::InvalidArchive( - "Invalid UTF-8 as symlink target".into(), - )); + return Err(ZipError::InvalidArchive("Invalid UTF-8 as symlink target")); }; - let target_is_dir_from_archive = match password { - None => archive.by_name(&target), - Some(pwd) => archive.by_name_decrypt(&target, pwd.as_bytes()), - } - .map_or(false, |x| x.is_dir()); + let target = target.into_boxed_str(); + let target_is_dir_from_archive = + archive.shared.files.contains_key(&target) && is_dir(&target); let target_path = directory.as_ref().join(OsString::from(target.to_string())); let target_is_dir = if target_is_dir_from_archive { true @@ -285,29 +278,32 @@ fn zip_extract>( } } - Ok((written_files, target_dirs)) + // Flush files to disk + futures::executor::block_on(async { sync_to_disk(written_files, target_dirs).await }); + + Ok(()) } fn zip_date_time_to_system_time(date_time: zip::DateTime) -> Option { - let dt = DateTime::new( - date_time.year() as i16, - date_time.month() as i8, - date_time.day() as i8, - date_time.hour() as i8, - date_time.minute() as i8, - date_time.second() as i8, - 0, - ) - .ok()?; - TimeZone::system() - .to_ambiguous_zoned(dt) - .later() - .ok() + let date = chrono::NaiveDate::from_ymd_opt( + date_time.year() as i32, + date_time.month() as u32, + date_time.day() as u32, + )?; + let time = chrono::NaiveTime::from_hms_opt( + date_time.hour() as u32, + date_time.minute() as u32, + date_time.second() as u32, + )?; + let naive = chrono::NaiveDateTime::new(date, time); + chrono::Local + .from_local_datetime(&naive) + .latest() .map(SystemTime::from) } pub fn system_time_to_zip_date_time(system_time: SystemTime) -> Option { - let date_time = Zoned::try_from(system_time).ok()?; + let date_time: chrono::DateTime = system_time.into(); zip::DateTime::from_date_and_time( date_time.year() as u16, diff --git a/src/channel.rs b/src/channel.rs deleted file mode 100644 index 2a295f9..0000000 --- a/src/channel.rs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2025 System76 -// SPDX-License-Identifier: MPL-2.0 - -use std::collections::VecDeque; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, Mutex}; - -/// Create a channel backed by `tokio::sync::Notify` and a sync mutex with a vec deque. -pub fn channel() -> (Sender, Receiver) { - let channel = Arc::new(Channel { - queue: Mutex::new(VecDeque::default()), - notify: tokio::sync::Notify::const_new(), - closed: AtomicBool::new(false), - }); - - (Sender(channel.clone()), Receiver(channel)) -} - -/// A channel backed by `tokio::sync::Notify` and a sync mutex with a vec deque. -struct Channel { - pub(self) queue: Mutex>, - /// Set when a new message has been stored. - pub(self) notify: tokio::sync::Notify, - /// Set when the receiver is dropped. - pub(self) closed: AtomicBool, -} - -pub struct Sender(Arc>); - -impl Sender { - pub fn send(&self, message: Message) { - self.0.queue.lock().unwrap().push_back(message); - self.0.notify.notify_one(); - } -} - -impl Drop for Sender { - fn drop(&mut self) { - self.0.closed.store(true, Ordering::SeqCst); - self.0.notify.notify_one(); - } -} - -pub struct Receiver(Arc>); - -impl Receiver { - /// Returns a value until the sender is dropped. - pub async fn recv(&self) -> Option { - loop { - { - let mut queue = self.0.queue.lock().unwrap(); - if let Some(value) = queue.pop_front() { - if queue.capacity() - queue.len() > 32 { - let capacity = queue.len().next_power_of_two(); - queue.shrink_to(capacity); - } - drop(queue); - return Some(value); - } - } - - if self.0.closed.load(Ordering::SeqCst) { - return None; - } - - self.0.notify.notified().await; - } - } - - pub fn try_recv(&self) -> Option { - self.0.queue.lock().unwrap().pop_front() - } -} diff --git a/src/clipboard.rs b/src/clipboard.rs index 7d06be5..637303e 100644 --- a/src/clipboard.rs +++ b/src/clipboard.rs @@ -2,10 +2,12 @@ // SPDX-License-Identifier: GPL-3.0-only use cosmic::iced::clipboard::mime::{AllowedMimeTypes, AsMimeTypes}; -use std::borrow::Cow; -use std::error::Error; -use std::path::{Path, PathBuf}; -use std::str; +use std::{ + borrow::Cow, + error::Error, + path::{Path, PathBuf}, + str, +}; use url::Url; #[derive(Clone, Copy, Debug)] @@ -126,15 +128,10 @@ impl TryFrom<(Vec, String)> for ClipboardPaste { // Assume the kind is Copy if not provided by the mime type let mut kind = ClipboardKind::Copy; let mut paths = Vec::new(); - match mime.as_str() { "text/uri-list" => { let text = str::from_utf8(&data)?; - - for line in text.lines().filter(|line| { - let line = line.trim(); - !line.is_empty() && !line.starts_with('#') - }) { + for line in text.lines() { let url = Url::parse(line)?; match url.to_file_path() { Ok(path) => paths.push(path), @@ -144,7 +141,6 @@ impl TryFrom<(Vec, String)> for ClipboardPaste { } "x-special/gnome-copied-files" => { let text = str::from_utf8(&data)?; - for (i, line) in text.lines().enumerate() { if i == 0 { kind = match line { diff --git a/src/config.rs b/src/config.rs index 3855b56..cbe84a6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,20 +1,20 @@ // SPDX-License-Identifier: GPL-3.0-only -use std::any::TypeId; -use std::num::NonZeroU16; -use std::path::PathBuf; +use std::{any::TypeId, num::NonZeroU16, path::PathBuf}; -use cosmic::cosmic_config::cosmic_config_derive::CosmicConfigEntry; -use cosmic::cosmic_config::{self, CosmicConfigEntry}; -use cosmic::iced::Subscription; -use cosmic::{Application, theme}; +use cosmic::{ + Application, + cosmic_config::{self, CosmicConfigEntry, cosmic_config_derive::CosmicConfigEntry}, + iced::Subscription, + theme, +}; use serde::{Deserialize, Serialize}; -use crate::FxOrderMap; -use crate::app::App; -use crate::tab::{HeadingOptions, Location, View}; - -pub use crate::context_action::{ContextActionPreset, ContextActionSelection}; +use crate::{ + FxOrderMap, + app::App, + tab::{HeadingOptions, Location, View}, +}; pub const CONFIG_VERSION: u64 = 1; @@ -70,11 +70,6 @@ pub enum Favorite { name: String, path: PathBuf, }, - /// A path with a custom name chosen by the user - Named { - path: PathBuf, - name: String, - }, } impl Favorite { @@ -103,38 +98,6 @@ impl Favorite { Self::Videos => dirs::video_dir(), Self::Path(path) => Some(path.clone()), Self::Network { path, .. } => Some(path.clone()), - Self::Named { path, .. } => Some(path.clone()), - } - } - - /// Name shown in the sidebar, or `None` if the path has no usable file name - pub fn display_name(&self) -> Option { - match self { - Self::Home => Some(crate::fl!("home")), - Self::Named { name, .. } | Self::Network { name, .. } => Some(name.clone()), - _ => self - .path_opt()? - .file_name() - .and_then(|x| x.to_str()) - .map(ToString::to_string), - } - } - - /// Return this favorite with a custom sidebar label chosen by the user - pub fn with_label(&self, name: &str) -> Self { - match self { - Self::Network { uri, path, .. } => Self::Network { - uri: uri.clone(), - name: name.to_string(), - path: path.clone(), - }, - other => match other.path_opt() { - Some(path) => Self::Named { - path, - name: name.to_string(), - }, - None => other.clone(), - }, } } } @@ -150,7 +113,6 @@ pub enum TypeToSearch { #[serde(default)] pub struct State { pub sort_names: FxOrderMap, - pub dialog_sort_name: (HeadingOptions, bool), } impl Default for State { @@ -162,7 +124,6 @@ impl Default for State { (HeadingOptions::Modified, false), ) })), - dialog_sort_name: (HeadingOptions::Modified, false), } } } @@ -203,17 +164,11 @@ pub struct Config { pub app_theme: AppTheme, pub dialog: DialogConfig, pub desktop: DesktopConfig, - pub context_actions: Vec, pub thumb_cfg: ThumbCfg, pub favorites: Vec, 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, pub type_to_search: TypeToSearch, } @@ -265,7 +220,6 @@ impl Default for Config { app_theme: AppTheme::System, desktop: DesktopConfig::default(), dialog: DialogConfig::default(), - context_actions: Vec::new(), thumb_cfg: ThumbCfg::default(), favorites: vec![ Favorite::Home, @@ -278,97 +232,11 @@ 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` 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 { - 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 { - 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 { @@ -515,66 +383,3 @@ pub const TIME_CONFIG_ID: &str = "com.system76.CosmicAppletTime"; pub struct TimeConfig { pub military_time: bool, } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn favorite_with_label_converts_path_to_named() { - let favorite = Favorite::Path(PathBuf::from("/some/dir")); - assert_eq!( - favorite.with_label("Custom"), - Favorite::Named { - path: PathBuf::from("/some/dir"), - name: "Custom".to_string(), - } - ); - } - - #[test] - fn favorite_with_label_updates_network_in_place() { - let favorite = Favorite::Network { - uri: "sftp://example.com/".to_string(), - name: "example.com".to_string(), - path: PathBuf::from("/run/mount/example"), - }; - assert_eq!( - favorite.with_label("Custom"), - Favorite::Network { - uri: "sftp://example.com/".to_string(), - name: "Custom".to_string(), - path: PathBuf::from("/run/mount/example"), - } - ); - } - - #[test] - fn favorite_with_label_converts_special_folder_to_named() { - let home = dirs::home_dir().unwrap(); - assert_eq!( - Favorite::Home.with_label("Custom"), - Favorite::Named { - path: home, - name: "Custom".to_string(), - } - ); - } - - #[test] - fn favorite_display_name() { - assert_eq!( - Favorite::Path(PathBuf::from("/some/dir")).display_name(), - Some("dir".to_string()) - ); - assert_eq!(Favorite::Path(PathBuf::from("/")).display_name(), None); - assert_eq!( - Favorite::Named { - path: PathBuf::from("/some/dir"), - name: "Custom".to_string(), - } - .display_name(), - Some("Custom".to_string()) - ); - } -} diff --git a/src/context_action.rs b/src/context_action.rs deleted file mode 100644 index 34dfb6e..0000000 --- a/src/context_action.rs +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only - -use std::path::PathBuf; - -use serde::{Deserialize, Serialize}; - -use crate::mime_app; -use crate::spawn_detached::spawn_detached; - -#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -pub enum ContextActionSelection { - #[default] - #[serde(alias = "any")] - Any, - #[serde(alias = "files")] - Files, - #[serde(alias = "folders")] - Folders, -} - -#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] -#[serde(default)] -pub struct ContextActionPreset { - pub name: String, - pub confirm: bool, - pub selection: ContextActionSelection, - pub steps: Vec, -} - -impl ContextActionPreset { - pub fn matches_selection(&self, selected: usize, selected_dir: usize) -> bool { - if selected == 0 { - return false; - } - - match self.selection { - ContextActionSelection::Any => true, - ContextActionSelection::Files => selected_dir == 0, - ContextActionSelection::Folders => selected_dir == selected, - } - } - - pub fn run(&self, paths: &[PathBuf]) { - if self.steps.is_empty() { - log::warn!("context action {:?} has no steps", self.name); - return; - } - - for step in &self.steps { - let Some(commands) = mime_app::exec_to_command(step, &self.name, None, paths) else { - log::warn!( - "failed to parse context action {:?}: invalid Exec {:?}", - self.name, - step - ); - return; - }; - - for mut command in commands { - if let Err(err) = spawn_detached(&mut command) { - log::warn!( - "failed to run context action {:?} step {:?}: {}", - self.name, - step, - err - ); - return; - } - } - } - } -} - -pub fn run(actions: &[ContextActionPreset], action: usize, paths: &[PathBuf]) { - if let Some(preset) = actions.get(action) { - preset.run(paths); - } else { - log::warn!("invalid context action index `{action}`"); - } -} diff --git a/src/dialog.rs b/src/dialog.rs index ba2fd15..6f0e5cb 100644 --- a/src/dialog.rs +++ b/src/dialog.rs @@ -1,47 +1,58 @@ // Copyright 2023 System76 // SPDX-License-Identifier: GPL-3.0-only -use cosmic::app::cosmic::Cosmic; -use cosmic::app::{Core, Task, context_drawer}; -use cosmic::cosmic_config::ConfigSet; -use cosmic::iced::core::SmolStr; -use cosmic::iced::core::widget::operation; -use cosmic::iced::futures::{self, SinkExt}; -use cosmic::iced::keyboard::key::{Named, Physical}; -use cosmic::iced::keyboard::{Event as KeyEvent, Key, Modifiers}; -use cosmic::iced::platform_specific::shell::{self as iced_winit, SurfaceIdWrapper}; -use cosmic::iced::widget::scrollable; -use cosmic::iced::widget::scrollable::AbsoluteOffset; -use cosmic::iced::{ - self, Alignment, Event, Length, Size, Subscription, event, mouse, stream, window, +use cosmic::{ + Application, ApplicationExt, Element, + app::{Core, Task, context_drawer, cosmic::Cosmic}, + cosmic_config, cosmic_theme, executor, + iced::{ + self, Alignment, Event, Length, Size, Subscription, + core::SmolStr, + event, + futures::{self, SinkExt}, + keyboard::{Event as KeyEvent, Key, Modifiers, key::Named}, + mouse, stream, + widget::scrollable, + window, + }, + iced_core::widget::operation, + iced_widget::scrollable::AbsoluteOffset, + iced_winit::{self, SurfaceIdWrapper}, + theme, + widget::{ + self, Operation, + menu::{Action as MenuAction, KeyBind, key_bind::Modifier}, + segmented_button, + }, }; -use cosmic::widget::menu::key_bind::Modifier; -use cosmic::widget::menu::{Action as MenuAction, KeyBind}; -use cosmic::widget::{self, Operation, segmented_button}; -use cosmic::{Application, ApplicationExt, Element, cosmic_config, cosmic_theme, executor, theme}; use mime_guess::{Mime, mime}; -use notify_debouncer_full::notify::{self, RecommendedWatcher}; -use notify_debouncer_full::{DebouncedEvent, Debouncer, RecommendedCache, new_debouncer}; +use notify_debouncer_full::{ + DebouncedEvent, Debouncer, RecommendedCache, new_debouncer, + notify::{self, RecommendedWatcher}, +}; use recently_used_xbel::update_recently_used; use rustc_hash::{FxHashMap, FxHashSet}; -use std::any::TypeId; -use std::collections::{HashMap, VecDeque}; -use std::path::PathBuf; -use std::time::{self, Instant}; -use std::{env, fmt, fs}; +use std::{ + any::TypeId, + collections::{HashMap, VecDeque}, + env, fmt, fs, + path::PathBuf, + time::{self, Instant}, +}; -use crate::app::{ - Action, ContextPage, Message as AppMessage, PreviewItem, PreviewKind, REPLACE_BUTTON_ID, +use crate::{ + app::{ + Action, ContextPage, Message as AppMessage, PreviewItem, PreviewKind, REPLACE_BUTTON_ID, + }, + config::{Config, DialogConfig, Favorite, TIME_CONFIG_ID, ThumbCfg, TimeConfig, TypeToSearch}, + fl, home_dir, + key_bind::key_binds, + localize::LANGUAGE_SORTER, + menu, + mounter::{MOUNTERS, MounterItem, MounterItems, MounterKey, MounterMessage}, + tab::{self, ItemMetadata, Location, SearchLocation, Tab}, + zoom::{zoom_in_view, zoom_out_view, zoom_to_default}, }; -use crate::config::{ - Config, DialogConfig, State, TIME_CONFIG_ID, ThumbCfg, TimeConfig, TypeToSearch, -}; -use crate::key_bind::key_binds; -use crate::localize::LANGUAGE_SORTER; -use crate::mounter::{MOUNTERS, MounterItem, MounterItems, MounterKey, MounterMessage}; -use crate::tab::{self, HeadingOptions, ItemMetadata, Location, SearchLocation, Tab}; -use crate::zoom::{zoom_in_view, zoom_out_view, zoom_to_default}; -use crate::{fl, home_dir, menu, mime_icon}; #[derive(Clone, Debug)] pub struct DialogMessage(cosmic::Action); @@ -192,7 +203,7 @@ impl> From for DialogLabel { impl<'a, M: Clone + 'static> From<&'a DialogLabel> for Element<'a, M> { fn from(label: &'a DialogLabel) -> Self { - let mut iced_spans: Vec> = + let mut iced_spans: Vec> = Vec::with_capacity(label.spans.len()); for span in &label.spans { iced_spans.push(cosmic::iced::widget::span(&span.text).underline(span.underline)); @@ -254,7 +265,6 @@ impl Dialog { crate::localize::localize(); let (config_handler, config) = Config::load(); - let (state_handler, state) = State::load(); let mut settings = window::Settings { decorations: false, @@ -289,8 +299,6 @@ impl Dialog { window_id, config_handler, config, - state_handler, - state, }; let (cosmic, cosmic_command) = Cosmic::::init((core, flags)); @@ -445,8 +453,6 @@ struct Flags { #[allow(dead_code)] config_handler: Option, config: Config, - state_handler: Option, - state: State, } /// Messages that are used specifically by our [`App`]. @@ -462,7 +468,7 @@ enum Message { Escape, Filename(String), Filter(usize), - Key(Modifiers, Key, Physical, Option), + Key(Modifiers, Key, Option), ModifiersChanged(Modifiers), MounterItems(MounterKey, MounterItems), Mouse(window::Id, mouse::Button), @@ -476,12 +482,12 @@ enum Message { SearchActivate, SearchClear, SearchInput(String), - Surface(cosmic::surface::Action), + Surface(cosmic::surface::Action), #[allow(clippy::enum_variant_names)] TabMessage(tab::Message), TabRescan( Location, - Option>, + Option, Vec, Option>, ), @@ -509,7 +515,7 @@ impl From for Message { AppMessage::ZoomIn(_entity_opt) => Self::ZoomIn, AppMessage::ZoomOut(_entity_opt) => Self::ZoomOut, AppMessage::NewItem(_entity_opt, true) => Self::NewFolder, - AppMessage::Surface(action) => Self::Surface(action.map(Self::from)), + AppMessage::Surface(action) => Self::Surface(action), unsupported => { log::warn!("{unsupported:?} not supported in dialog mode"); Self::None @@ -549,6 +555,7 @@ struct App { title: String, accept_label: DialogLabel, choices: Vec, + context_menu_window: Option, context_page: ContextPage, dialog_pages: VecDeque, dialog_text_input: widget::Id, @@ -580,7 +587,7 @@ impl App { space_s, space_l, .. - } = theme::spacing(); + } = theme::active().cosmic().spacing; let is_condensed = self.core().is_condensed(); let mut col = widget::column::with_capacity(2).spacing(space_xxs); @@ -588,7 +595,6 @@ impl App { col = col.push( widget::text_input("", filename) .id(self.filename_id.clone()) - .double_click_select_delimiter('.') .on_input(Message::Filename) .on_submit(|_| Message::Save(false)), ); @@ -711,7 +717,7 @@ impl App { match (selected.next(), selected.next()) { // At least two selected items - (Some(_), Some(_)) => Some(self.tab.multi_preview_view(None)), + (Some(_), Some(_)) => Some(self.tab.multi_preview_view()), // Exactly one selected item (Some(item), None) => Some(item.preview_view(None, military_time)), // No selected items @@ -737,17 +743,11 @@ impl App { fn rescan_tab(&self, selection_paths: Option>) -> Task { 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, items)) => { - #[cfg(feature = "gvfs")] - let mut items = items; - #[cfg(not(feature = "gvfs"))] - let items = items; - + Ok((parent_item_opt, mut items)) => { #[cfg(feature = "gvfs")] { let mounter_paths: Box<[_]> = mounter_items @@ -798,7 +798,7 @@ impl App { }; search_location.map(|search_location| { - ( + return ( Location::Search( search_location, term, @@ -806,7 +806,7 @@ impl App { Instant::now(), ), true, - ) + ); }) } None => match &self.tab.location { @@ -836,10 +836,9 @@ impl App { fn update_config(&mut self) -> Task { self.core.window.show_context = self.flags.config.dialog.show_details; - let config = self.flags.config.dialog_tab(); - self.tab.config.view = config.view; + self.tab.config = self.flags.config.dialog_tab(); self.update_nav_model(); - self.update(Message::TabMessage(tab::Message::Config(config))) + self.update(Message::TabMessage(tab::Message::Config(self.tab.config))) } fn with_dialog_config(&mut self, f: F) -> Task { @@ -878,20 +877,31 @@ impl App { } } + fn close_context_menus(&mut self) -> Task { + self.tab.location_context_menu_index = None; + if self.tab.context_menu.is_some() { + return self.update(Message::TabMessage(tab::Message::ContextMenu(None, None))); + } + + Task::none() + } + fn update_nav_model(&mut self) { let mut nav_model = segmented_button::ModelBuilder::default(); - if self.flags.config.show_recents { - nav_model = nav_model.insert(|b| { - b.text(fl!("recents")) - .icon(widget::icon::from_name("document-open-recent-symbolic")) - .data(Location::Recents) - }); - } + nav_model = nav_model.insert(|b| { + b.text(fl!("recents")) + .icon(widget::icon::from_name("document-open-recent-symbolic")) + .data(Location::Recents) + }); for favorite in &self.flags.config.favorites { if let Some(path) = favorite.path_opt() { - let Some(name) = favorite.display_name() else { + let name = if matches!(favorite, Favorite::Home) { + fl!("home") + } else if let Some(file_name) = path.file_name().and_then(|x| x.to_str()) { + file_name.to_string() + } else { continue; }; nav_model = nav_model.insert(move |b| { @@ -1041,7 +1051,8 @@ impl Application for App { None, ); tab.mode = tab::Mode::Dialog(flags.kind.clone()); - (tab.sort_name, tab.sort_direction) = flags.state.dialog_sort_name; + tab.sort_name = tab::HeadingOptions::Modified; + tab.sort_direction = false; let key_binds = key_binds(&tab.mode); @@ -1051,6 +1062,7 @@ impl Application for App { title, accept_label: DialogLabel::from(accept_label), choices: Vec::new(), + context_menu_window: None, context_page: ContextPage::Preview(None, PreviewKind::Selected), dialog_pages: VecDeque::new(), dialog_text_input: widget::Id::new("Dialog Text Input"), @@ -1110,7 +1122,7 @@ impl Application for App { } fn dialog(&self) -> Option> { - let cosmic_theme::Spacing { space_xxs, .. } = theme::spacing(); + let cosmic_theme::Spacing { space_xxs, .. } = theme::active().cosmic().spacing; //TODO: should gallery view just be a dialog? if self.tab.gallery { @@ -1194,9 +1206,7 @@ impl Application for App { .icon(widget::icon::from_name("dialog-question").size(64)) .body(fl!("replace-warning")) .primary_action( - widget::button::suggested(fl!("replace")) - .on_press(Message::DialogComplete) - .id(REPLACE_BUTTON_ID.clone()), + widget::button::suggested(fl!("replace")).on_press(Message::DialogComplete), ) .secondary_action( widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel), @@ -1321,6 +1331,15 @@ impl Application for App { return Task::none(); } + if self.tab.location_context_menu_index.is_some() { + self.tab.location_context_menu_index = None; + return Task::none(); + } + + if self.tab.context_menu.is_some() { + return self.update(Message::TabMessage(tab::Message::ContextMenu(None, None))); + } + if self.tab.edit_location.is_some() { // Close location editing if enabled self.tab.edit_location = None; @@ -1379,10 +1398,7 @@ impl Application for App { Message::Config(config) => { if config != self.flags.config { log::info!("update config"); - // Don't overwrite military time - let military_time = self.flags.config.tab.military_time; self.flags.config = config; - self.flags.config.tab.military_time = military_time; return self.update_config(); } } @@ -1435,16 +1451,16 @@ impl Application for App { } return self.rescan_tab(None); } - Message::Key(modifiers, key, physical_key, text) => { + Message::Key(modifiers, key, text) => { for (key_bind, action) in &self.key_binds { - if key_bind.matches(modifiers, &key, Some(&physical_key)) { + if key_bind.matches(modifiers, &key) { return self.update(Message::from(action.message())); } } // Check key binds from accept label if let Some(key_bind) = &self.accept_label.key_bind_opt - && key_bind.matches(modifiers, &key, Some(&physical_key)) + && key_bind.matches(modifiers, &key) { return self.update(if self.flags.kind.save() { Message::Save(false) @@ -1555,7 +1571,7 @@ impl Application for App { Message::Mouse(window_id, _button) => { // Close context menu when clicking outside. if self.core.main_window_id() == Some(window_id) { - return Task::none(); + return self.close_context_menus(); } } Message::NewFolder => { @@ -1715,7 +1731,7 @@ impl Application for App { ))); } Message::SearchActivate => { - let mut tasks = vec![]; + let mut tasks = vec![self.close_context_menus()]; if self.search_get().is_none() { tasks.push(self.search_set(Some(String::new()))); @@ -1726,7 +1742,7 @@ impl Application for App { return Task::batch(tasks); } Message::SearchClear => { - return self.search_set(None); + return Task::batch([self.close_context_menus(), self.search_set(None)]); } Message::SearchInput(input) => { return self.search_set(Some(input)); @@ -1762,9 +1778,76 @@ impl Application for App { self.rescan_tab(selection_paths), ])); } - tab::Command::Surface(action) => { - let action = action.map(Message::TabMessage); - commands.push(self.update(Message::Surface(action))); + tab::Command::ContextMenu(point_opt, parent_id) => { + #[cfg(feature = "wayland")] + match point_opt { + Some(point) => { + if crate::is_wayland() { + // Open context menu + use cctk::wayland_protocols::xdg::shell::client::xdg_positioner::{ + Anchor, Gravity, + }; + use cosmic::iced_runtime::platform_specific::wayland::popup::{ + SctkPopupSettings, SctkPositioner, + }; + use cosmic::iced::Rectangle; + let window_id = window::Id::unique(); + self.context_menu_window = Some(window_id); + let autosize_id = widget::Id::unique(); + commands.push(self.update(Message::Surface( + cosmic::surface::action::app_popup( + move |app: &mut Self| -> SctkPopupSettings { + let anchor_rect = Rectangle { + x: point.x as i32, + y: point.y as i32, + width: 1, + height: 1, + }; + let positioner = SctkPositioner { + size: None, + anchor_rect, + anchor: Anchor::None, + gravity: Gravity::BottomRight, + reactive: true, + ..Default::default() + }; + SctkPopupSettings { + parent: parent_id + .unwrap_or(app.flags.window_id), + id: window_id, + positioner, + parent_size: None, + grab: true, + close_with_children: false, + input_zone: None, + } + }, + Some(Box::new(move |app: &Self| { + widget::autosize::autosize( + menu::context_menu( + &app.tab, + &app.key_binds, + &app.modifiers, + false, // Paste not used in dialogs + ) + .map(Message::TabMessage) + .map(cosmic::Action::App), + autosize_id.clone(), + ) + .into() + })), + ), + ))); + } + } + None => { + if let Some(window_id) = self.context_menu_window.take() { + commands.push(self.update(Message::Surface( + cosmic::surface::action::destroy_popup(window_id), + ))); + } + } + } } tab::Command::Iced(iced_command) => { commands.push(iced_command.0.map(|tab_message| { @@ -1799,17 +1882,6 @@ impl Application for App { self.auto_scroll_speed = None; } } - tab::Command::SetSort(_, heading_options, direction) => { - self.flags.state.dialog_sort_name = (heading_options, direction); - if let Some(state_handler) = self.flags.state_handler.as_ref() - && let Err(err) = state_handler.set::<&(HeadingOptions, bool)>( - "dialog_sort_name", - &self.flags.state.dialog_sort_name, - ) - { - log::warn!("Failed to save dialog sort name: {err:?}"); - } - } unsupported => { log::warn!("{unsupported:?} not supported in dialog mode"); } @@ -1847,13 +1919,11 @@ impl Application for App { items.retain(|item| { // Directories are always shown item.metadata.is_dir() - // Check for mime type match (first because it is faster) || mimes.iter().any(|filter_mime| { if filter_mime.subtype() == mime::STAR { filter_mime.type_() == item.mime.type_() } else { *filter_mime == item.mime - || mime_icon::is_mime_subclass_of(&item.mime, filter_mime) } }) // Check for glob match (last because it is slower) @@ -1882,16 +1952,6 @@ impl Application for App { if self.search_get().is_some() { return widget::text_input::focus(self.search_id.clone()); } - if let DialogKind::SaveFile { filename } = &self.flags.kind { - return Task::batch([ - widget::text_input::focus(self.filename_id.clone()), - widget::text_input::select_until_last( - self.filename_id.clone(), - filename, - '.', - ), - ]); - } return widget::text_input::focus(self.filename_id.clone()); } } @@ -1930,7 +1990,9 @@ impl Application for App { }); } Message::Surface(action) => { - return cosmic::task::message(cosmic::Action::Surface(action)); + return cosmic::task::message(cosmic::Action::Cosmic( + cosmic::app::Action::Surface(action), + )); } } @@ -1939,7 +2001,7 @@ impl Application for App { /// Creates a view after each update. fn view(&self) -> Element<'_, Message> { - let cosmic_theme::Spacing { space_xxs, .. } = theme::spacing(); + let cosmic_theme::Spacing { space_xxs, .. } = theme::active().cosmic().spacing; let mut col = widget::column::with_capacity(2); @@ -1960,7 +2022,7 @@ impl Application for App { col = col.push( self.tab - .view(&self.key_binds, &self.modifiers, false, &[]) + .view(&self.key_binds, &self.modifiers, false) .map(Message::TabMessage), ); @@ -1978,14 +2040,11 @@ impl Application for App { }, Event::Keyboard(KeyEvent::KeyPressed { key, - physical_key, modifiers, text, .. }) => match status { - event::Status::Ignored => { - Some(Message::Key(modifiers, key, physical_key, text)) - } + event::Status::Ignored => Some(Message::Key(modifiers, key, text)), event::Status::Captured => { if key == Key::Named(Named::Escape) { Some(Message::Escape) diff --git a/src/key_bind.rs b/src/key_bind.rs index d3ddd59..8340580 100644 --- a/src/key_bind.rs +++ b/src/key_bind.rs @@ -1,10 +1,11 @@ -use cosmic::iced::core::keyboard::key::Named; -use cosmic::iced::keyboard::Key; -use cosmic::widget::menu::key_bind::{KeyBind, Modifier}; +use cosmic::{ + iced::keyboard::Key, + iced_core::keyboard::key::Named, + widget::menu::key_bind::{KeyBind, Modifier}, +}; use std::collections::HashMap; -use crate::app::Action; -use crate::tab; +use crate::{app::Action, tab}; //TODO: load from config pub fn key_binds(mode: &tab::Mode) -> HashMap { @@ -30,16 +31,12 @@ pub fn key_binds(mode: &tab::Mode) -> HashMap { bind!([], Key::Named(Named::F5), Reload); bind!([], Key::Named(Named::Home), SelectFirst); bind!([], Key::Named(Named::End), SelectLast); - bind!([], Key::Named(Named::PageDown), ItemPageDown); - bind!([], Key::Named(Named::PageUp), ItemPageUp); bind!([Shift], Key::Named(Named::ArrowDown), ItemDown); bind!([Shift], Key::Named(Named::ArrowLeft), ItemLeft); bind!([Shift], Key::Named(Named::ArrowRight), ItemRight); bind!([Shift], Key::Named(Named::ArrowUp), ItemUp); bind!([Shift], Key::Named(Named::Home), SelectFirst); bind!([Shift], Key::Named(Named::End), SelectLast); - bind!([Shift], Key::Named(Named::PageDown), ItemPageDown); - bind!([Shift], Key::Named(Named::PageUp), ItemPageUp); bind!([Ctrl, Shift], Key::Character("n".into()), NewFolder); bind!([], Key::Named(Named::Enter), Open); bind!([Ctrl], Key::Character(" ".into()), Preview); diff --git a/src/large_image.rs b/src/large_image.rs index 1d78bc5..8686a97 100644 --- a/src/large_image.rs +++ b/src/large_image.rs @@ -1,7 +1,9 @@ use cosmic::widget; use image::ImageReader; -use std::collections::{HashMap, HashSet}; -use std::path::{Path, PathBuf}; +use std::{ + collections::{HashMap, HashSet}, + path::{Path, PathBuf}, +}; /// Bytes per pixel in RGBA format (Red, Green, Blue, Alpha = 4 bytes) pub const RGBA_BYTES_PER_PIXEL: u64 = 4; diff --git a/src/lib.rs b/src/lib.rs index 44b87f6..159af08 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,23 +1,15 @@ // Copyright 2023 System76 // SPDX-License-Identifier: GPL-3.0-only -use cosmic::app::Settings; -use cosmic::iced::Limits; -use std::path::PathBuf; -use std::{env, fs, process}; -use tracing_subscriber::layer::SubscriberExt; -use tracing_subscriber::util::SubscriberInitExt; - -use crate::app::{App, Flags}; -use crate::config::{Config, State}; -use crate::tab::Location; +use cosmic::{app::Settings, iced::Limits}; +use std::{env, fs, path::PathBuf, process}; +use app::{App, Flags}; pub mod app; mod archive; -pub mod channel; pub mod clipboard; +use config::Config; pub mod config; -mod context_action; pub mod dialog; mod key_bind; pub(crate) mod large_image; @@ -30,15 +22,16 @@ mod mounter; mod mouse_area; pub mod operation; mod spawn_detached; +use tab::Location; +mod zoom; + +use crate::config::State; pub mod tab; mod thumbnail_cacher; mod thumbnailer; -pub(crate) mod trash; -mod zoom; pub(crate) type FxOrderMap = ordermap::OrderMap; -#[cfg(feature = "gvfs")] pub(crate) fn err_str(err: T) -> String { err.to_string() } @@ -79,22 +72,7 @@ pub fn is_wayland() -> bool { /// Runs application in desktop mode #[rustfmt::skip] pub fn desktop() -> Result<(), Box> { - let log_format = tracing_subscriber::fmt::format() - .pretty() - .without_time() - .with_line_number(true) - .with_file(true) - .with_target(false) - .with_thread_names(true); - - let log_layer = tracing_subscriber::fmt::Layer::default() - .with_writer(std::io::stderr) - .event_format(log_format); - - tracing_subscriber::registry() - .with(tracing_subscriber::EnvFilter::from_env("RUST_LOG")) - .with(log_layer) - .init(); + env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init(); localize::localize(); @@ -129,21 +107,7 @@ pub fn desktop() -> Result<(), Box> { /// Runs application with these settings #[rustfmt::skip] pub fn main() -> Result<(), Box> { - let log_format = tracing_subscriber::fmt::format() - .pretty() - .with_line_number(true) - .with_file(true) - .with_target(false) - .with_thread_names(true); - - let log_layer = tracing_subscriber::fmt::Layer::default() - .with_writer(std::io::stderr) - .event_format(log_format); - - tracing_subscriber::registry() - .with(tracing_subscriber::EnvFilter::from_default_env()) - .with(log_layer) - .init(); + env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init(); localize::localize(); @@ -193,7 +157,7 @@ pub fn main() -> Result<(), Box> { } if daemonize { - #[cfg(all(unix, not(any(target_os = "macos", target_os = "redox"))))] + #[cfg(all(unix, not(target_os = "redox")))] match fork::daemon(true, true) { Ok(fork::Fork::Child) => (), Ok(fork::Fork::Parent(_child_pid)) => process::exit(0), diff --git a/src/load_image.rs b/src/load_image.rs index 88ceb1b..00e9bdf 100644 --- a/src/load_image.rs +++ b/src/load_image.rs @@ -1,10 +1,11 @@ -use cosmic::iced::{core as iced_core, widget as iced_widget}; +use cosmic::{iced_core, iced_widget}; use iced_core::event::Event; +use iced_core::layout; +use iced_core::mouse; +use iced_core::overlay; +use iced_core::renderer; use iced_core::widget::{Operation, Tree}; -use iced_core::{ - Clipboard, Element, Layout, Length, Rectangle, Shell, Vector, Widget, layout, mouse, overlay, - renderer, -}; +use iced_core::{Clipboard, Element, Layout, Length, Rectangle, Shell, Vector, Widget}; pub fn loaded_image<'a, Message: 'static, Theme>( handle: ::Handle, diff --git a/src/localize.rs b/src/localize.rs index b569807..f561a42 100644 --- a/src/localize.rs +++ b/src/localize.rs @@ -1,10 +1,13 @@ // SPDX-License-Identifier: GPL-3.0-only -use i18n_embed::fluent::{FluentLanguageLoader, fluent_language_loader}; -use i18n_embed::{DefaultLocalizer, LanguageLoader, Localizer}; -use icu::collator::options::CollatorOptions; -use icu::collator::preferences::CollationNumericOrdering; -use icu::collator::{Collator, CollatorBorrowed, CollatorPreferences}; +use i18n_embed::{ + DefaultLocalizer, LanguageLoader, Localizer, + fluent::{FluentLanguageLoader, fluent_language_loader}, +}; +use icu::collator::{ + Collator, CollatorBorrowed, CollatorPreferences, options::CollatorOptions, + preferences::CollationNumericOrdering, +}; use icu::locale::Locale; use rust_embed::RustEmbed; use std::sync::LazyLock; diff --git a/src/main.rs b/src/main.rs index 276cb9b..d4cb5bd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,5 @@ use tikv_jemallocator::Jemalloc; static GLOBAL: Jemalloc = Jemalloc; fn main() -> Result<(), Box> { - let _ = jxl_oxide::integration::register_image_decoding_hook(); cosmic_files::main() } diff --git a/src/menu.rs b/src/menu.rs index 05998a4..d025a0f 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -1,28 +1,48 @@ // SPDX-License-Identifier: GPL-3.0-only -use cosmic::{Element, theme}; -use cosmic::app::Core; -use cosmic::iced::keyboard::Modifiers; -use cosmic::widget::menu::action::MenuAction; -use cosmic::widget::menu::key_bind::KeyBind; -use cosmic::widget::menu::{self, ItemHeight, ItemWidth, MenuBar}; -use cosmic::widget::{self, responsive_menu_bar}; +use cosmic::{ + Element, + app::Core, + iced::{ + Alignment, Background, Border, Length, advanced::widget::text::Style as TextStyle, + keyboard::Modifiers, + }, + theme, + widget::{ + self, Row, button, column, container, divider, + menu::{self, ItemHeight, ItemWidth, MenuBar, key_bind::KeyBind}, + responsive_menu_bar, space, text, + }, +}; use i18n_embed::LanguageLoader; use mime_guess::Mime; -use std::collections::HashMap; -use std::sync::LazyLock; +use std::{collections::HashMap, sync::LazyLock}; -use crate::app::{Action, Message}; -use crate::config::{Config, ContextActionPreset}; -use crate::fl; -use crate::tab::{ - self, HeadingOptions, ItemMetadata, Location, LocationMenuAction, SearchLocation, Tab, +use crate::{ + app::{Action, Message}, + config::Config, + fl, + tab::{self, HeadingOptions, Location, LocationMenuAction, SearchLocation, Tab}, }; -use crate::trash::{Trash, TrashExt}; static MENU_ID: LazyLock = LazyLock::new(|| cosmic::widget::Id::new("responsive-menu")); +macro_rules! menu_button { + ($($x:expr),+ $(,)?) => ( + button::custom( + Row::with_children( + [$(Element::from($x)),+] + ) + .height(Length::Fixed(24.0)) + .align_y(Alignment::Center) + ) + .padding([theme::active().cosmic().spacing.space_xxs, 16]) + .width(Length::Fill) + .class(theme::Button::MenuItem) + ); +} + const fn menu_button_optional( label: String, action: Action, @@ -35,46 +55,72 @@ const fn menu_button_optional( } } -/// A menu action dispatched to the tab the menu was opened on. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct TabAction(pub Action); - -impl MenuAction for TabAction { - type Message = tab::Message; - - fn message(&self) -> tab::Message { - tab::Message::ContextAction(self.0) - } -} - pub fn context_menu<'a>( tab: &Tab, key_binds: &HashMap, modifiers: &Modifiers, clipboard_paste_available: bool, - context_actions: &[ContextActionPreset], -) -> Vec> { - let menu_item = - |label: String, action: Action| menu::Item::Button(label, None, TabAction(action)); - let menu_item_disabled = - |label: String, action: Action| menu::Item::ButtonDisabled(label, None, TabAction(action)); +) -> Element<'a, tab::Message> { + let find_key = |action: &Action| -> String { + for (key_bind, key_action) in key_binds { + if action == key_action { + return key_bind.to_string(); + } + } + String::new() + }; + fn key_style(theme: &cosmic::Theme) -> TextStyle { + let mut color = theme.cosmic().background.component.on; + color.alpha *= 0.75; + TextStyle { + color: Some(color.into()), + } + } + fn disabled_style(theme: &cosmic::Theme) -> TextStyle { + let mut color = theme.cosmic().background.component.on; + color.alpha *= 0.5; + TextStyle { + color: Some(color.into()), + } + } + + let menu_item = |label, action| { + let key = find_key(&action); + menu_button!( + text::body(label), + space::horizontal(), + text::body(key).class(theme::Text::Custom(key_style)) + ) + .on_press(tab::Message::ContextAction(action)) + }; + + let menu_item_disabled = |label, action: Action| { + let key = find_key(&action); + menu_button!( + text::body(label).class(theme::Text::Custom(disabled_style)), + space::horizontal(), + text::body(key).class(theme::Text::Custom(disabled_style)) + ) + }; // Allow paste when clipboard has data and we're in a location that supports it let can_paste = clipboard_paste_available && tab.location.supports_paste(); let (sort_name, sort_direction, _) = tab.sort_options(); - let sort_item = |label: String, variant| { - let entry = menu::Entry::new(label, TabAction(Action::ToggleSort(variant))); - menu::Item::Entry(if sort_name == variant { - let icon_name = if sort_direction { - "view-sort-ascending-symbolic" - } else { - "view-sort-descending-symbolic" - }; - entry.icon(widget::icon::from_name(icon_name).size(14).handle()) - } else { - entry.reserve_icon() - }) + let sort_item = |label, variant| { + menu_item( + format!( + "{} {}", + label, + match (sort_name == variant, sort_direction) { + (true, true) => "\u{2B07}", + (true, false) => "\u{2B06}", + _ => "", + } + ), + Action::ToggleSort(variant), + ) + .into() }; let mut selected_dir = 0; @@ -83,7 +129,6 @@ pub fn context_menu<'a>( let mut selected_desktop_entry = None; let mut selected_types: Vec = vec![]; let mut selected_mount_point = 0; - let mut any_trash_item = false; if let Some(items) = tab.items_opt() { for item in items { if item.selected { @@ -96,17 +141,15 @@ pub fn context_menu<'a>( Some(Location::Trash) | Some(Location::Search(SearchLocation::Trash, ..)) => { selected_trash_only = true } - Some(Location::Path(path)) + Some(Location::Path(path)) => { if selected == 1 - && path.extension().and_then(|s| s.to_str()) == Some("desktop") => - { - selected_desktop_entry = Some(&**path); + && path.extension().and_then(|s| s.to_str()) == Some("desktop") + { + selected_desktop_entry = Some(&**path); + } } _ => (), } - if matches!(&item.metadata, ItemMetadata::Trash { .. }) { - any_trash_item = true; - } selected_types.push(item.mime.clone()); } } @@ -114,14 +157,6 @@ pub fn context_menu<'a>( selected_types.sort_unstable(); selected_types.dedup(); selected_trash_only = selected_trash_only && selected == 1; - let context_action_items = |selected: usize, selected_dir: usize| { - context_actions - .iter() - .enumerate() - .filter(|(_, action)| action.matches_selection(selected, selected_dir)) - .map(|(i, action)| menu_item(action.name.clone(), Action::RunContextAction(i))) - .collect::>() - }; // Parse the desktop entry if it is the only selection #[cfg(feature = "desktop")] let selected_desktop_entry = selected_desktop_entry.and_then(|path| { @@ -135,7 +170,7 @@ pub fn context_menu<'a>( } }); - let mut children: Vec> = Vec::new(); + let mut children: Vec> = Vec::new(); match (&tab.mode, &tab.location) { ( tab::Mode::App | tab::Mode::Desktop, @@ -147,175 +182,142 @@ pub fn context_menu<'a>( | Location::Network(_, _, Some(_)), ) => { if selected_trash_only { - children.push(menu_item(fl!("open"), Action::Open)); - if !Trash::is_empty() { - children.push(menu_item(fl!("empty-trash"), Action::EmptyTrash)); + children.push(menu_item(fl!("open"), Action::Open).into()); + if !trash::os_limited::is_empty().unwrap_or(true) { + children.push(menu_item(fl!("empty-trash"), Action::EmptyTrash).into()); } } else if let Some(entry) = selected_desktop_entry { - children.push(menu_item(fl!("open"), Action::Open)); + children.push(menu_item(fl!("open"), Action::Open).into()); #[cfg(feature = "desktop")] { - children.extend( - entry - .desktop_actions - .into_iter() - .enumerate() - .map(|(i, action)| menu_item(action.name, Action::ExecEntryAction(i))), - ); + children.extend(entry.desktop_actions.into_iter().enumerate().map( + |(i, action)| menu_item(action.name, Action::ExecEntryAction(i)).into(), + )); } - children.push(menu_item(fl!("edit-desktop-entry"), Action::EditDesktopEntry)); - children.push(menu::Item::Divider); - children.push(menu_item(fl!("rename"), Action::Rename)); - children.push(menu_item(fl!("cut"), Action::Cut)); + children.push(divider::horizontal::light().into()); + children.push(menu_item(fl!("rename"), Action::Rename).into()); + children.push(menu_item(fl!("cut"), Action::Cut).into()); if modifiers.shift() && !modifiers.control() { - children.push(menu_item(fl!("copy-path"), Action::CopyPath)); + children.push(menu_item(fl!("copy-path"), Action::CopyPath).into()); } else { - children.push(menu_item(fl!("copy"), Action::Copy)); + children.push(menu_item(fl!("copy"), Action::Copy).into()); } // Should this simply bypass trash and remove the shortcut? - children.push(menu_item(fl!("move-to-trash"), Action::Delete)); - let action_items = context_action_items(selected, selected_dir); - if !action_items.is_empty() { - children.push(menu::Item::Divider); - children.extend(action_items); - } + children.push(menu_item(fl!("move-to-trash"), Action::Delete).into()); } else if selected > 0 { if selected_dir == 1 && selected == 1 || selected_dir == 0 { - children.push(menu_item(fl!("open"), Action::Open)); + children.push(menu_item(fl!("open"), Action::Open).into()); } if selected == 1 { - children.push(menu_item(fl!("menu-open-with"), Action::OpenWith)); + children.push(menu_item(fl!("menu-open-with"), Action::OpenWith).into()); if selected_dir == 1 { - children.push(menu_item(fl!("open-in-terminal"), Action::OpenTerminal)); + children + .push(menu_item(fl!("open-in-terminal"), Action::OpenTerminal).into()); } } - if tab.location.is_recents() || matches!(tab.location, Location::Search(..)) { - children.push(menu_item( - fl!("open-item-location"), - Action::OpenItemLocation, - )); + if tab.location.is_recents() { + children.push( + menu_item(fl!("open-item-location"), Action::OpenItemLocation).into(), + ); } // All selected items are directories if selected == selected_dir && matches!(tab.mode, tab::Mode::App) { - children.push(menu_item(fl!("open-in-new-tab"), Action::OpenInNewTab)); - children.push(menu_item( - fl!("open-in-new-window"), - Action::OpenInNewWindow, - )); + children.push(menu_item(fl!("open-in-new-tab"), Action::OpenInNewTab).into()); + children + .push(menu_item(fl!("open-in-new-window"), Action::OpenInNewWindow).into()); } - let action_items = context_action_items(selected, selected_dir); - if !action_items.is_empty() { - children.push(menu::Item::Divider); - children.extend(action_items); - } - children.push(menu::Item::Divider); + children.push(divider::horizontal::light().into()); if selected_mount_point == 0 { - children.push(menu_item(fl!("rename"), Action::Rename)); - children.push(menu_item(fl!("cut"), Action::Cut)); + children.push(menu_item(fl!("rename"), Action::Rename).into()); + children.push(menu_item(fl!("cut"), Action::Cut).into()); } if modifiers.shift() && !modifiers.control() { - children.push(menu_item(fl!("copy-path"), Action::CopyPath)); + children.push(menu_item(fl!("copy-path"), Action::CopyPath).into()); } else { - children.push(menu_item(fl!("copy"), Action::Copy)); + children.push(menu_item(fl!("copy"), Action::Copy).into()); } if selected_mount_point == 0 { - children.push(menu_item(fl!("move-to"), Action::MoveTo)); + children.push(menu_item(fl!("move-to"), Action::MoveTo).into()); } - children.push(menu_item(fl!("copy-to"), Action::CopyTo)); + children.push(menu_item(fl!("copy-to"), Action::CopyTo).into()); - children.push(menu::Item::Divider); + children.push(divider::horizontal::light().into()); let supported_archive_types = crate::archive::SUPPORTED_ARCHIVE_TYPES; selected_types.retain(|t| supported_archive_types.iter().copied().all(|m| *t != m)); if selected_types.is_empty() { - children.push(menu_item(fl!("extract-here"), Action::ExtractHere)); - children.push(menu_item(fl!("extract-to"), Action::ExtractTo)); + children.push(menu_item(fl!("extract-here"), Action::ExtractHere).into()); + children.push(menu_item(fl!("extract-to"), Action::ExtractTo).into()); } - children.push(menu_item(fl!("compress"), Action::Compress)); - children.push(menu::Item::Divider); + children.push(menu_item(fl!("compress"), Action::Compress).into()); + children.push(divider::horizontal::light().into()); //TODO: Print? - children.push(menu_item(fl!("show-details"), Action::Preview)); - if any_trash_item { - children.push(menu::Item::Divider); - children.push(menu_item( - fl!("restore-from-trash"), - Action::RestoreFromTrash, - )); - children.push(menu::Item::Divider); - children.push(menu_item(fl!("delete-permanently"), Action::Delete)); - } else { - if matches!(tab.mode, tab::Mode::App) { - children.push(menu::Item::Divider); - children.push(menu_item(fl!("add-to-sidebar"), Action::AddToSidebar)); - } - children.push(menu::Item::Divider); - if tab.location.is_recents() { - children.push(menu_item( - fl!("remove-from-recents"), - Action::RemoveFromRecents, - )); - children.push(menu::Item::Divider); - } - if selected_mount_point == 0 { - if modifiers.shift() && !modifiers.control() { - children.push( - menu_item(fl!("delete-permanently"), Action::PermanentlyDelete) - .into(), - ); - } else { - children.push(menu_item(fl!("move-to-trash"), Action::Delete)); - } - } else if selected == 1 { - children.push(menu_item(fl!("eject"), Action::Eject)); + children.push(menu_item(fl!("show-details"), Action::Preview).into()); + if matches!(tab.mode, tab::Mode::App) { + children.push(divider::horizontal::light().into()); + children.push(menu_item(fl!("add-to-sidebar"), Action::AddToSidebar).into()); + } + children.push(divider::horizontal::light().into()); + if tab.location.is_recents() { + children.push( + menu_item(fl!("remove-from-recents"), Action::RemoveFromRecents).into(), + ); + children.push(divider::horizontal::light().into()); + } + if selected_mount_point == 0 { + if modifiers.shift() && !modifiers.control() { + children.push( + menu_item(fl!("delete-permanently"), Action::PermanentlyDelete).into(), + ); + } else { + children.push(menu_item(fl!("move-to-trash"), Action::Delete).into()); } + } else if selected == 1 { + children.push(menu_item(fl!("eject"), Action::Eject).into()); } } else { //TODO: need better designs for menu with no selection //TODO: have things like properties but they apply to the folder? if tab.location != Location::Recents { - children.push(menu_item(fl!("new-folder"), Action::NewFolder)); - children.push(menu_item(fl!("new-file"), Action::NewFile)); - children.push(menu_item(fl!("open-in-terminal"), Action::OpenTerminal)); - children.push(menu::Item::Divider); + children.push(menu_item(fl!("new-folder"), Action::NewFolder).into()); + children.push(menu_item(fl!("new-file"), Action::NewFile).into()); + children.push(menu_item(fl!("open-in-terminal"), Action::OpenTerminal).into()); + children.push(divider::horizontal::light().into()); } if tab.mode.multiple() { - children.push(menu_item(fl!("select-all"), Action::SelectAll)); + children.push(menu_item(fl!("select-all"), Action::SelectAll).into()); } if can_paste { - children.push(menu_item(fl!("paste"), Action::Paste)); + children.push(menu_item(fl!("paste"), Action::Paste).into()); } else { - children.push(menu_item_disabled(fl!("paste"), Action::Paste)); + children.push(menu_item_disabled(fl!("paste"), Action::Paste).into()); } //TODO: only show if cosmic-settings is found? if matches!(tab.mode, tab::Mode::Desktop) { - children.push(menu::Item::Divider); - children.push(menu_item( - fl!("change-wallpaper"), - Action::CosmicSettingsWallpaper, - )); - children.push(menu_item( - fl!("desktop-appearance"), - Action::CosmicSettingsDesktop, - )); - children.push(menu_item( - fl!("display-settings"), - Action::CosmicSettingsDisplays, - )); + children.push(divider::horizontal::light().into()); + children.push( + menu_item(fl!("change-wallpaper"), Action::CosmicSettingsWallpaper).into(), + ); + children.push( + menu_item(fl!("desktop-appearance"), Action::CosmicSettingsDesktop).into(), + ); + children.push( + menu_item(fl!("display-settings"), Action::CosmicSettingsDisplays).into(), + ); } - children.push(menu::Item::Divider); + children.push(divider::horizontal::light().into()); // TODO: Nested menu children.push(sort_item(fl!("sort-by-name"), HeadingOptions::Name)); children.push(sort_item(fl!("sort-by-modified"), HeadingOptions::Modified)); children.push(sort_item(fl!("sort-by-size"), HeadingOptions::Size)); if matches!(tab.location, Location::Desktop(..)) { - children.push(menu::Item::Divider); - children.push(menu_item( - fl!("desktop-view-options"), - Action::DesktopViewOptions, - )); + children.push(divider::horizontal::light().into()); + children.push( + menu_item(fl!("desktop-view-options"), Action::DesktopViewOptions).into(), + ); } } } @@ -330,25 +332,24 @@ pub fn context_menu<'a>( ) => { if selected > 0 { if selected_dir == 1 && selected == 1 || selected_dir == 0 { - children.push(menu_item(fl!("open"), Action::Open)); + children.push(menu_item(fl!("open"), Action::Open).into()); } if matches!(tab.location, Location::Search(..)) || tab.location.is_recents() { - children.push(menu_item( - fl!("open-item-location"), - Action::OpenItemLocation, - )); + children.push( + menu_item(fl!("open-item-location"), Action::OpenItemLocation).into(), + ); } - children.push(menu::Item::Divider); - children.push(menu_item(fl!("show-details"), Action::Preview)); + children.push(divider::horizontal::light().into()); + children.push(menu_item(fl!("show-details"), Action::Preview).into()); } else { if dialog_kind.save() { - children.push(menu_item(fl!("new-folder"), Action::NewFolder)); + children.push(menu_item(fl!("new-folder"), Action::NewFolder).into()); } if tab.mode.multiple() { - children.push(menu_item(fl!("select-all"), Action::SelectAll)); + children.push(menu_item(fl!("select-all"), Action::SelectAll).into()); } if !children.is_empty() { - children.push(menu::Item::Divider); + children.push(divider::horizontal::light().into()); } children.push(sort_item(fl!("sort-by-name"), HeadingOptions::Name)); children.push(sort_item(fl!("sort-by-modified"), HeadingOptions::Modified)); @@ -358,14 +359,14 @@ pub fn context_menu<'a>( (_, Location::Network(..)) => { if selected > 0 { if selected_dir == 1 && selected == 1 || selected_dir == 0 { - children.push(menu_item(fl!("open"), Action::Open)); + children.push(menu_item(fl!("open"), Action::Open).into()); } } else { if tab.mode.multiple() { - children.push(menu_item(fl!("select-all"), Action::SelectAll)); + children.push(menu_item(fl!("select-all"), Action::SelectAll).into()); } if !children.is_empty() { - children.push(menu::Item::Divider); + children.push(divider::horizontal::light().into()); } children.push(sort_item(fl!("sort-by-name"), HeadingOptions::Name)); children.push(sort_item(fl!("sort-by-modified"), HeadingOptions::Modified)); @@ -374,26 +375,18 @@ pub fn context_menu<'a>( } (_, Location::Trash | Location::Search(SearchLocation::Trash, ..)) => { if tab.mode.multiple() { - children.push(menu_item(fl!("select-all"), Action::SelectAll)); - } - if !Trash::is_empty() { - if !children.is_empty() { - children.push(menu::Item::Divider); - } - children.push(menu_item(fl!("empty-trash"), Action::EmptyTrash)); + children.push(menu_item(fl!("select-all"), Action::SelectAll).into()); } if !children.is_empty() { - children.push(menu::Item::Divider); + children.push(divider::horizontal::light().into()); } if selected > 0 { - children.push(menu_item(fl!("show-details"), Action::Preview)); - children.push(menu::Item::Divider); - children.push(menu_item( - fl!("restore-from-trash"), - Action::RestoreFromTrash, - )); - children.push(menu::Item::Divider); - children.push(menu_item(fl!("delete-permanently"), Action::Delete)); + children.push(menu_item(fl!("show-details"), Action::Preview).into()); + children.push(divider::horizontal::light().into()); + children + .push(menu_item(fl!("restore-from-trash"), Action::RestoreFromTrash).into()); + children.push(divider::horizontal::light().into()); + children.push(menu_item(fl!("delete-permanently"), Action::Delete).into()); } else { // TODO: Nested menu children.push(sort_item(fl!("sort-by-name"), HeadingOptions::Name)); @@ -403,11 +396,26 @@ pub fn context_menu<'a>( } } - let key_binds: HashMap = key_binds - .iter() - .map(|(key_bind, action)| (key_bind.clone(), TabAction(*action))) - .collect(); - menu::items(&key_binds, children) + container(column::with_children(children)) + .padding(1) + //TODO: move style to libcosmic + .style(|theme| { + let cosmic = theme.cosmic(); + let component = &cosmic.background.component; + container::Style { + icon_color: Some(component.on.into()), + text_color: Some(component.on.into()), + background: Some(Background::Color(component.base.into())), + border: Border { + radius: cosmic.radius_s().map(|x| x + 1.0).into(), + width: 1.0, + color: component.divider.into(), + }, + ..Default::default() + } + }) + .width(Length::Fixed(360.0)) + .into() } pub fn dialog_menu( @@ -551,7 +559,7 @@ pub fn dialog_menu( ]) .item_height(ItemHeight::Dynamic(40)) .item_width(ItemWidth::Uniform(360)) - .spacing(theme::spacing().space_xxxs.into()) + .spacing(theme::active().cosmic().spacing.space_xxxs.into()) .into() } @@ -606,7 +614,7 @@ pub fn menu_bar<'a>( responsive_menu_bar() .item_height(ItemHeight::Dynamic(40)) .item_width(ItemWidth::Uniform(360)) - .spacing(theme::spacing().space_xxxs.into()) + .spacing(theme::active().cosmic().spacing.space_xxxs.into()) .into_element( core, key_binds, @@ -756,33 +764,50 @@ pub fn menu_bar<'a>( ) } -pub fn location_context_menu(ancestor_index: usize) -> Vec> { +pub fn location_context_menu<'a>(ancestor_index: usize) -> Element<'a, tab::Message> { //TODO: only add some of these when in App mode - menu::items( - &HashMap::new(), - vec![ - menu::Item::Button( - fl!("open-in-new-tab"), - None, + let children = [ + menu_button!(text::body(fl!("open-in-new-tab"))) + .on_press(tab::Message::LocationMenuAction( LocationMenuAction::OpenInNewTab(ancestor_index), - ), - menu::Item::Button( - fl!("open-in-new-window"), - None, + )) + .into(), + menu_button!(text::body(fl!("open-in-new-window"))) + .on_press(tab::Message::LocationMenuAction( LocationMenuAction::OpenInNewWindow(ancestor_index), - ), - menu::Item::Divider, - menu::Item::Button( - fl!("show-details"), - None, + )) + .into(), + divider::horizontal::light().into(), + menu_button!(text::body(fl!("show-details"))) + .on_press(tab::Message::LocationMenuAction( LocationMenuAction::Preview(ancestor_index), - ), - menu::Item::Divider, - menu::Item::Button( - fl!("add-to-sidebar"), - None, + )) + .into(), + divider::horizontal::light().into(), + menu_button!(text::body(fl!("add-to-sidebar"))) + .on_press(tab::Message::LocationMenuAction( LocationMenuAction::AddToSidebar(ancestor_index), - ), - ], - ) + )) + .into(), + ]; + + container(column::with_children(children)) + .padding(1) + .style(|theme| { + let cosmic = theme.cosmic(); + let component = &cosmic.background.component; + container::Style { + icon_color: Some(component.on.into()), + text_color: Some(component.on.into()), + background: Some(Background::Color(component.base.into())), + border: Border { + radius: cosmic.radius_s().map(|x| x + 1.0).into(), + width: 1.0, + color: component.divider.into(), + }, + ..Default::default() + } + }) + .width(Length::Fixed(360.0)) + .into() } diff --git a/src/mime_app.rs b/src/mime_app.rs index 8fd8a60..4a55cdf 100644 --- a/src/mime_app.rs +++ b/src/mime_app.rs @@ -1,171 +1,155 @@ // Copyright 2023 System76 // SPDX-License-Identifier: GPL-3.0-only -use bstr::{BString, ByteSlice, ByteVec}; +#[cfg(feature = "desktop")] +use cosmic::desktop; use cosmic::widget; pub use mime_guess::Mime; -#[cfg(feature = "desktop")] -use notify_debouncer_full::notify; -use rustc_hash::{FxHashMap, FxHashSet}; -use std::ffi::OsStr; -use std::os::unix::ffi::OsStrExt; -use std::path::{Path, PathBuf}; -use std::sync::atomic::AtomicBool; -use std::sync::{Arc, RwLock, atomic}; -use std::time::{self, Instant}; -use std::{fs, io, process}; +use rustc_hash::FxHashMap; +use std::{ + cmp::Ordering, + ffi::OsStr, + fs, io, + path::{Path, PathBuf}, + process, + time::Instant, +}; -#[cfg(feature = "desktop")] -pub async fn watch(mut emitter: impl FnMut() + 'static + Send) { - let watcher_result = notify_debouncer_full::new_debouncer( - time::Duration::from_millis(250), - Some(time::Duration::from_millis(250)), - move |event_res: notify_debouncer_full::DebounceEventResult| { - let Ok(events) = event_res else { - return; - }; - - if events.iter().any(|event| { - event.kind.is_create() || event.kind.is_modify() || event.kind.is_remove() - }) { - emitter(); - } - }, - ); - - if let Ok(mut watcher) = watcher_result { - let system_paths = cosmic_mime_apps::list_paths(); - let local_paths = (|| { - let base_dirs = xdg::BaseDirectories::new(); - let Some(home) = base_dirs.get_config_home() else { - return Err(std::io::Error::other("XDG config home not set")); - }; - - let Ok(desktop) = std::env::var("XDG_CURRENT_DESKTOP") else { - return Err(std::io::Error::other("XDG_CURRENT_DESKTOP unset")); - }; - - let default_mimeapps = home.join("mimeapps.list"); - let desktop_mimeapps = - home.join([&desktop.to_ascii_lowercase(), "-mimeapps.list"].concat()); - - Ok([desktop_mimeapps, default_mimeapps]) - })() - .ok(); - - for path in system_paths - .iter() - .chain(local_paths.as_ref().into_iter().flatten()) - { - _ = watcher.watch(path.as_path(), notify::RecursiveMode::NonRecursive); - } - - std::future::pending().await - } -} +// Supported exec key field codes +const EXEC_HANDLERS: [&str; 4] = ["%f", "%F", "%u", "%U"]; +// Deprecated field codes. The spec advises to ignore these handlers. +const DEPRECATED_HANDLERS: [&str; 6] = ["%d", "%D", "%n", "%N", "%v", "%m"]; pub fn exec_to_command( exec: &str, - entry_name: &str, - entry_path: Option<&Path>, path_opt: &[impl AsRef], ) -> Option> { - let arguments = shlex::split(exec)?; + let args_vec = shlex::split(exec)?; + let program = args_vec.first()?; + // Skip program to make indexing easier + let args_vec = &args_vec[1..]; - if arguments.is_empty() { - tracing::error!("command does not contain any arguments"); - return None; - } - - let mut commands = Vec::new(); - - let paths = path_opt + // Base Command instance(s) + // 1. We may need to launch multiple of the same process. + // 2. Each of those processes will need to be passed args from exec. + // 3. Each of those args may appear in any order. + // 4. Arg order should be preserved. + // + // So, we'll go through exec in two passes. The first pass handles paths (%f etc) and args up + // to the field code followed by the second which passes extra, non-% args to each processes. + // + // While it'd be marginally faster to process everything in one pass, that's problematic: + // 1. path_opt may need to be cloned because it may be moved on each iteration (borrowck + // doesn't know we'll only use it once) + // 2. We have to keep track of which modifier (%f etc) we've used/seen already + // 3. We have to keep track of which processes received non-modifier args which gets messy fast + // 4. `exec` is likely small so looping over it twice is not a big deal + let field_code_pos = args_vec .iter() - .map(AsRef::as_ref) - .map(Some) - // Add a single `None` if no path was given. - .chain(std::iter::repeat_n( - None, - if path_opt.is_empty() { 1 } else { 0 }, - )); + .position(|arg| EXEC_HANDLERS.contains(&arg.as_str())); + let args_handler = field_code_pos.and_then(|i| args_vec.get(i)); + // msrv + // .inspect(|handler| log::trace!("Found paths handler: {handler} for exec: {exec}")); + // Number of args before the field code. + // This won't be an off by one err below because take is not zero indexed. + let field_code_pos = field_code_pos.unwrap_or_default(); + let mut processes = match args_handler.map(String::as_str) { + Some("%f") => { + let mut processes = Vec::with_capacity(path_opt.len()); - for path in paths { - let mut batch_process = false; - let mut args = Vec::with_capacity(arguments.len()); - let mut field_code_used = false; - - for argument in arguments.iter().skip(1) { - let mut new_argument = BString::new(Vec::with_capacity(argument.capacity())); - let mut chars = argument.chars(); - while let Some(char) = chars.next() { - // https://specifications.freedesktop.org/desktop-entry/latest/exec-variables.html - if char == '%' { - match chars.next() { - Some('%') => new_argument.push_char(char), - Some('c') => new_argument.push_str(entry_name), - Some('k') => { - if let Some(path) = entry_path { - new_argument.push_str(path.as_os_str().as_bytes()); - } - } - - // %f and %u behave the same in a file manager. - Some('f' | 'u') => { - if let Some(path) = path - && !field_code_used - { - // TODO: files on remote file systems should be copied to a temporary local file. - batch_process = true; - field_code_used = true; - new_argument.push_str(path.as_bytes()); - } - } - - // %F and %U behave the same in a file manager. - Some('F') | Some('U') => { - if !field_code_used && new_argument.is_empty() { - field_code_used = true; - for path in path_opt.iter().map(AsRef::as_ref) { - args.push(BString::new(path.as_bytes().to_owned())); - } - } - } - - _ => (), - } - } else { - new_argument.push_char(char); + for path in path_opt.iter().map(AsRef::as_ref) { + // TODO: %f and %F need to handle non-file URLs (see spec) + if from_file_or_dir(path).is_none() { + log::warn!("Desktop file expects a file path instead of a URL: {path:?}"); } + + // Passing multiple paths to %f should open an instance per path + let mut process = process::Command::new(program); + process.args( + args_vec + .iter() + .map(AsRef::as_ref) + .take(field_code_pos) + .chain(std::iter::once(path)), + ); + processes.push(process); } - if !new_argument.is_empty() { - args.push(new_argument); - } + processes } + Some("%F") => { + // TODO: %f and %F need to handle non-file URLs (see spec) + for invalid in path_opt + .iter() + .map(AsRef::as_ref) + .filter(|&path| from_file_or_dir(path).is_none()) + { + log::warn!("Desktop file expects a file path instead of a URL: {invalid:?}"); + } - let mut command = process::Command::new(&arguments[0]); + // Launch one instance with all args + let mut process = process::Command::new(program); + process.args( + args_vec + .iter() + .map(OsStr::new) + .take(field_code_pos) + .chain(path_opt.iter().map(AsRef::as_ref)), + ); - for arg in args { - match arg.to_os_str() { - Ok(arg) => { - command.arg(arg); - } - Err(_) => { - tracing::error!("invalid string encoding in command"); + vec![process] + } + Some("%u") => path_opt + .iter() + .map(|path| { + let mut process = process::Command::new(program); + process.args( + args_vec + .iter() + .map(OsStr::new) + .take(field_code_pos) + .chain(std::iter::once(path.as_ref())), + ); + process + }) + .collect(), + Some("%U") => { + let mut process = process::Command::new(program); + process.args( + args_vec + .iter() + .map(OsStr::new) + .take(field_code_pos) + .chain(path_opt.iter().map(AsRef::as_ref)), + ); + vec![process] + } + Some(invalid) => unreachable!("All valid variants were checked; got: {invalid}"), + None => vec![process::Command::new(program)], + }; + + // Pass 2: Add remaining arguments that are not % to each process + for arg in args_vec.iter().skip(field_code_pos) { + match arg.as_str() { + // Consume path field codes or fail on codes we don't handle yet + field_code if arg.starts_with('%') => { + if !EXEC_HANDLERS.contains(&field_code) + && !DEPRECATED_HANDLERS.contains(&field_code) + { + log::warn!("unsupported Exec code {field_code:?} in {exec:?}"); return None; } } - } - - commands.push(command); - - if !batch_process { - break; + arg => { + for process in &mut processes { + process.arg(arg); + } + } } } #[cfg(debug_assertions)] - for command in &commands { + for command in &processes { log::debug!( "Parsed program {} with args: {:?}", command.get_program().to_string_lossy(), @@ -173,14 +157,13 @@ pub fn exec_to_command( ); } - Some(commands) + Some(processes) } -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum MimeAppMatch { - Exact, - Related, - Other, +fn from_file_or_dir(path: impl AsRef) -> Option { + url::Url::from_file_path(&path) + .ok() + .or_else(|| url::Url::from_directory_path(&path).ok()) } #[derive(Clone, Debug)] @@ -189,42 +172,14 @@ pub struct MimeApp { pub path: Option, pub name: String, pub exec: Option, - icon_name: Box, - icon: std::sync::OnceLock, - is_default: Arc>>>, - no_display: Arc, + pub icon: widget::icon::Handle, + pub is_default: bool, } impl MimeApp { //TODO: move to libcosmic, support multiple files pub fn command>(&self, path_opt: &[O]) -> Option> { - exec_to_command( - self.exec.as_deref()?, - &self.name, - self.path.as_deref(), - path_opt, - ) - } - - pub fn is_default(&self, mime: &Mime) -> bool { - self.is_default.read().unwrap().contains(mime.essence_str()) - } - - pub fn no_display(&self) -> bool { - self.no_display.load(atomic::Ordering::Relaxed) - } - - pub fn icon(&self) -> widget::icon::Handle { - self.icon - .get_or_init(|| { - let name = &*self.icon_name; - if name.starts_with('/') { - cosmic::widget::icon::from_path(PathBuf::from(name)) - } else { - cosmic::widget::icon::from_name(name).size(32).handle() - } - }) - .clone() + exec_to_command(self.exec.as_deref()?, path_opt) } } @@ -235,10 +190,38 @@ impl AsRef for MimeApp { } } +#[cfg(feature = "desktop")] +impl From<&desktop::DesktopEntryData> for MimeApp { + fn from(app: &desktop::DesktopEntryData) -> Self { + Self { + id: app.id.clone(), + path: app.path.clone(), + name: app.name.clone(), + exec: app.exec.clone(), + icon: match &app.icon { + desktop::fde::IconSource::Name(name) => { + widget::icon::from_name(name.as_str()).size(32).handle() + } + desktop::fde::IconSource::Path(path) => widget::icon::from_path(path.clone()), + }, + is_default: false, + } + } +} + +#[cfg(feature = "desktop")] +fn filename_eq(path_opt: &Option, filename: &str) -> bool { + path_opt + .as_ref() + .and_then(|path| path.file_name()) + .is_some_and(|x| x == filename) +} + pub struct MimeAppCache { - apps: Vec>, - cache: FxHashMap>>, - terminals: Vec>, + apps: Vec, + cache: FxHashMap>, + icons: FxHashMap>, + terminals: Vec, } impl MimeAppCache { @@ -246,273 +229,154 @@ impl MimeAppCache { let mut mime_app_cache = Self { apps: Vec::new(), cache: FxHashMap::default(), + icons: FxHashMap::default(), terminals: Vec::new(), }; mime_app_cache.reload(); mime_app_cache } - pub fn get_apps_for_mime( - &self, - mime_type: &Mime, - include_other: bool, - ) -> Vec<(&Arc, MimeAppMatch)> { - let mut results = Vec::new(); - let mut dedupe = FxHashSet::default(); - - // start with exact matches - results.extend( - self.get(mime_type) - .iter() - .filter(|&mime_app| dedupe.insert(&mime_app.id)) - .map(|mime_app| (mime_app, MimeAppMatch::Exact)), - ); - - let include_mime = match mime_type.type_().as_str() { - "audio" => Some("video/mp4".parse::().expect("video/mp4 mime")), - "text" => Some(mime_guess::mime::TEXT_PLAIN), - _ => None, - }; - - if let Some(mime) = include_mime { - results.extend( - self.get(&mime) - .iter() - .filter(|&mime_app| dedupe.insert(&mime_app.id)) - .map(|mime_app| (mime_app, MimeAppMatch::Exact)), - ); - } - - // grab matches based off of subclass / parent mime type - if let Some(parent_types) = crate::mime_icon::parent_mime_types(mime_type) { - for parent_type in parent_types { - results.extend( - self.get(&parent_type) - .iter() - .filter(|&mime_app| dedupe.insert(&mime_app.id)) - .map(|mime_app| (mime_app, MimeAppMatch::Related)), - ); - } - } - - if include_other { - results.extend({ - let mut apps = self - .apps() - .iter() - .filter(|mime_app| !mime_app.no_display()) - .filter(|&mime_app| dedupe.insert(&mime_app.id)) - .map(|mime_app| (mime_app, MimeAppMatch::Other)) - .collect::>(); - apps.sort_by(|(a, _), (b, _)| { - crate::localize::LANGUAGE_SORTER.compare(&a.name, &b.name) - }); - apps - }); - } - - results - } - #[cfg(not(feature = "desktop"))] pub fn reload(&mut self) {} - /// Reload mime types and their known app associations and defaults. + // Only available when using desktop feature of libcosmic, which only works on Unix-likes #[cfg(feature = "desktop")] pub fn reload(&mut self) { use crate::localize::LANGUAGE_SORTER; - use crate::mime_icon; - use cosmic::desktop::fde; - use std::borrow::Cow; let start = Instant::now(); self.apps.clear(); self.cache.clear(); + self.icons.clear(); self.terminals.clear(); + //TODO: get proper locale? + let locale = &[]; + + // Load desktop applications by supported mime types + //TODO: hashmap for all apps by id? + let all_apps: Box<[_]> = desktop::load_applications(locale, false, None).collect(); + for app in &all_apps { + //TODO: just collect apps that can be executed with a file argument? + if !app.mime_types.is_empty() { + self.apps.push(MimeApp::from(app)); + } + for mime in &app.mime_types { + let apps = self + .cache + .entry(mime.clone()) + .or_insert_with(|| Vec::with_capacity(1)); + if !apps.iter().any(|x| x.id == app.id) { + apps.push(MimeApp::from(app)); + } + } + for category in &app.categories { + if category == "TerminalEmulator" { + self.terminals.push(MimeApp::from(app)); + break; + } + } + } + let mut list = cosmic_mime_apps::List::default(); + let paths = cosmic_mime_apps::list_paths(); list.load_from_paths(&paths); - let locales = fde::get_languages_from_env(); - let desktop_entries = fde::Iter::new(fde::default_paths()).entries(Some(&locales)); - let mime_icon_cache = mime_icon::MIME_ICON_CACHE.lock().unwrap(); - let shared_mime_info = &mime_icon_cache.shared_mime_info; - let mut aliased_mimes = FxHashMap::default(); - for desktop_entry in desktop_entries { - let name = desktop_entry - .name(&locales) - .unwrap_or_else(|| Cow::Borrowed(desktop_entry.id())); - - let app = Arc::new(MimeApp { - id: desktop_entry.appid.clone(), - path: Some(desktop_entry.path.clone()), - name: name.into(), - exec: desktop_entry.exec().map(String::from), - icon_name: desktop_entry.icon().unwrap_or_default().into(), - icon: std::sync::OnceLock::new(), - is_default: Arc::new(RwLock::default()), - no_display: Arc::new(AtomicBool::new(false)), - }); - - tracing::info!(target: "mime-apps", id = app.id, "detected desktop entry"); - - self.apps.push(app.clone()); - - if desktop_entry - .categories() - .into_iter() - .flatten() - .any(|c| c == "TerminalEmulator") - { - self.terminals.push(app.clone()); - } - - // Cache associations defined by the desktop entry. - let mime_types = desktop_entry.mime_type().unwrap_or_else(Vec::new); - let associated_mime_types = mime_types.iter().filter_map(|m| { - m.parse::().ok().map(|mime| { - if let Some(unaliased) = shared_mime_info.unalias_mime_type(&mime) { - aliased_mimes.insert(unaliased.clone(), mime); - return unaliased; - } - - mime - }) - }); - - for mime in associated_mime_types { - let apps = self.cache.entry(mime.clone()).or_default(); - if apps.iter().all(|cached_app| cached_app.id != app.id) { - apps.push(app.clone()); - } - } - } - - // Cache added associations from mimeapps lists. - for (mut added_mime, added_apps) in &list.added_associations { - let _unaliased; - if let Some(unaliased) = shared_mime_info.unalias_mime_type(added_mime) { - aliased_mimes.insert(unaliased.clone(), added_mime.clone()); - _unaliased = unaliased; - added_mime = &_unaliased; - } - - for added_app in added_apps { - if let Some(app) = self - .apps - .iter() - .find(|cached| cached.id.as_str() == added_app.as_ref()) - { - let apps = self.cache.entry(added_mime.clone()).or_default(); - if apps.iter().all(|cached_app| cached_app.id != app.id) { - apps.push(app.clone()); + for (mime, filenames) in list + .added_associations + .iter() + .chain(list.default_apps.iter()) + { + for filename in filenames { + log::trace!("add {mime}={filename}"); + let apps = self + .cache + .entry(mime.clone()) + .or_insert_with(|| Vec::with_capacity(1)); + if !apps.iter().any(|x| filename_eq(&x.path, filename)) { + if let Some(app) = all_apps.iter().find(|&x| filename_eq(&x.path, filename)) { + apps.push(MimeApp::from(app)); + } else { + log::info!( + "failed to add association for {mime:?}: application {filename:?} not found" + ); } } } } - // Remove associations - for (mut removed_mime, removed_apps) in &list.removed_associations { - let _unaliased; - if let Some(unaliased) = shared_mime_info.unalias_mime_type(removed_mime) { - aliased_mimes.insert(unaliased.clone(), removed_mime.clone()); - _unaliased = unaliased; - removed_mime = &_unaliased; - } - - for removed_app in removed_apps { - if let Some(app) = self - .apps - .iter() - .find(|cached| cached.id.as_str() == removed_app.as_ref()) - && let Some(apps) = self.cache.get_mut(removed_mime) - { - apps.retain(|cached_app| cached_app.id != app.id); + for (mime, filenames) in list.removed_associations.iter() { + for filename in filenames { + log::trace!("remove {mime}={filename}"); + if let Some(apps) = self.cache.get_mut(mime) { + apps.retain(|x| !filename_eq(&x.path, filename)); } } } - // Fetch defaults and sort apps by their default precedence. - for (mime, mut apps) in std::mem::take(&mut self.cache).into_iter() { - let defaults = list.default_app_for(&mime); - let aliased_defaults = aliased_mimes - .get(&mime) - .and_then(|mime| list.default_app_for(mime)); - - let cache = self - .cache - .entry(mime.clone()) - .or_insert_with(|| Vec::with_capacity(apps.len())); - - // Sort cached apps for this mime by default precedence. - for default in defaults - .into_iter() - .flatten() - .chain(aliased_defaults.into_iter().flatten()) - { - let default = default.strip_suffix(".desktop").unwrap_or(default.as_ref()); - let mut found_any = false; - apps.retain(|app| { - let found = app.id.as_str() == default; + for (mime, filenames) in list.default_apps.iter() { + for filename in filenames { + log::trace!("default {mime}={filename}"); + if let Some(apps) = self.cache.get_mut(mime) { + let mut found = false; + for app in apps.iter_mut() { + if filename_eq(&app.path, filename) { + app.is_default = true; + found = true; + } else { + app.is_default = false; + } + } if found { - app.is_default - .write() - .unwrap() - .insert(mime.essence_str().into()); - cache.push(app.clone()); - found_any = true; + break; } - - !found - }); - - if !found_any && let Some(app) = self.apps.iter().find(|app| app.id == default) { - app.is_default - .write() - .unwrap() - .insert(mime.essence_str().into()); - cache.push(app.clone()); + log::debug!( + "failed to set default for {mime:?}: application {filename:?} not found" + ); } } - - // Sort remaining apps by name - apps.sort_by(|a, b| LANGUAGE_SORTER.compare(&a.name, &b.name)); - cache.extend_from_slice(&apps); - - tracing::debug!(target: "mime-apps", mime = mime.essence_str(), apps = ?(cache.iter().map(|app| &*app.id).collect::>()), "mime defaults found") } - let associated: rustc_hash::FxHashSet<&str> = self - .cache - .values() - .flatten() - .map(|app| app.id.as_str()) - .collect(); - for app in &self.apps { - app.no_display.store( - !associated.contains(app.id.as_str()), - atomic::Ordering::Relaxed, - ); + // Sort apps by name + self.apps + .sort_by(|a, b| match (a.is_default, b.is_default) { + (true, false) => Ordering::Less, + (false, true) => Ordering::Greater, + _ => LANGUAGE_SORTER.compare(&a.name, &b.name), + }); + for apps in self.cache.values_mut() { + apps.sort_by(|a, b| match (a.is_default, b.is_default) { + (true, false) => Ordering::Less, + (false, true) => Ordering::Greater, + _ => LANGUAGE_SORTER.compare(&a.name, &b.name), + }); } + // Copy icons to special cache + //TODO: adjust dropdown API so this is no longer needed + self.icons.extend(self.cache.iter().map(|(mime, apps)| { + ( + mime.clone(), + apps.iter().map(|app| app.icon.clone()).collect(), + ) + })); + let elapsed = start.elapsed(); - tracing::info!(target: "mime-apps", "loaded mime app cache in {elapsed:?}"); + log::info!("loaded mime app cache in {elapsed:?}"); } - pub fn apps(&self) -> &[Arc] { + pub fn apps(&self) -> &[MimeApp] { &self.apps } - pub fn get(&self, key: &Mime) -> &[Arc] { + pub fn get(&self, key: &Mime) -> &[MimeApp] { self.cache.get(key).map_or(&[], Vec::as_slice) } - pub fn icons(&self, key: &Mime) -> Vec { - self.cache - .get(key) - .map_or_else(Vec::new, |apps| apps.iter().map(|app| app.icon()).collect()) + pub fn icons(&self, key: &Mime) -> &[widget::icon::Handle] { + self.icons.get(key).map_or(&[], Box::as_ref) } fn get_default_terminal(&self) -> Option { @@ -530,19 +394,14 @@ impl MimeAppCache { .map(|string| string.trim().replace(".desktop", "")) } - pub fn terminal(&self) -> Option<&Arc> { + pub fn terminal(&self) -> Option<&MimeApp> { //TODO: consider rules in https://github.com/Vladimir-csp/xdg-terminal-exec // The current approach works but might not adhere to the spec (yet) // Look for and return preferred terminals //TODO: fallback order beyond cosmic-term? - // 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(), - ]; + let mut preference_order = vec!["com.system76.CosmicTerm".to_string()]; if let Some(id) = self.get_default_terminal() { preference_order.insert(0, id); @@ -616,43 +475,11 @@ impl Default for MimeAppCache { mod tests { use super::exec_to_command; - #[test] - fn keys_within_words() { - let exec = "/usr/bin/foo --option=%f"; - let paths = ["file1"]; - let commands = exec_to_command(exec, "keys_within_words", None, &paths) - .expect("Should parse valid exec"); - - assert_eq!(1, commands.len()); - let command = commands.first().unwrap(); - - assert_eq!("/usr/bin/foo", command.get_program().to_str().unwrap()); - assert_eq!( - "--option=file1", - command.get_args().next().unwrap().to_str().unwrap() - ); - } - - #[test] - fn no_path_f_field_code() { - let exec = "/usr/bin/foo %f"; - let paths: [&str; 0] = []; - let commands = exec_to_command(exec, "no_path_f_field_code", None, &paths) - .expect("Should parse valid exec"); - - assert_eq!(1, commands.len()); - let command = commands.first().unwrap(); - - assert_eq!("/usr/bin/foo", command.get_program().to_str().unwrap()); - assert_eq!(0, command.get_args().len()); - } - #[test] fn one_path_f_field_code() { let exec = "/usr/bin/foo %f"; let paths = ["file1"]; - let commands = exec_to_command(exec, "one_path_f_field_code", None, &paths) - .expect("Should parse valid exec"); + let commands = exec_to_command(exec, &paths).expect("Should parse valid exec"); assert_eq!(1, commands.len()); let command = commands.first().unwrap(); @@ -667,40 +494,31 @@ mod tests { #[test] #[allow(non_snake_case)] fn one_path_F_field_code() { - let exec = "/usr/bin/cosmic-term -w %F"; - let paths = ["/home/user"]; - let commands = exec_to_command(exec, "one_path_F_field_code", None, &paths) - .expect("Should parse valid exec"); + let exec = "/usr/bin/bar %F"; + let paths = ["cat"]; + let commands = exec_to_command(exec, &paths).expect("Should parse valid exec"); assert_eq!(1, commands.len()); let command = commands.first().unwrap(); - let mut args = command.get_args(); - assert_eq!( - "/usr/bin/cosmic-term", - command.get_program().to_str().unwrap() - ); - assert_eq!("-w", args.next().unwrap().to_str().unwrap()); - assert_eq!(paths[0], args.next().unwrap().to_str().unwrap()); + assert_eq!("/usr/bin/bar", command.get_program().to_str().unwrap()); + assert_eq!("cat", command.get_args().next().unwrap().to_str().unwrap()); } #[test] fn one_path_u_field_code() { - let exec = "/usr/bin/cosmic-term -w %u"; - let paths = ["/home/user"]; - let commands = exec_to_command(exec, "one_path_u_field_code", None, &paths) - .expect("Should parse valid exec"); + let exec = "/usr/bin/foobar %u"; + let paths = ["/home/josh/krumpli"]; + let commands = exec_to_command(exec, &paths).expect("Should parse valid exec"); assert_eq!(1, commands.len()); let command = commands.first().unwrap(); - let mut args = command.get_args(); + assert_eq!("/usr/bin/foobar", command.get_program().to_str().unwrap()); assert_eq!( - "/usr/bin/cosmic-term", - command.get_program().to_str().unwrap() + *paths.first().unwrap(), + command.get_args().next().unwrap().to_str().unwrap() ); - assert_eq!("-w", args.next().unwrap().to_str().unwrap()); - assert_eq!(paths[0], args.next().unwrap().to_str().unwrap()); } #[test] @@ -708,8 +526,7 @@ mod tests { fn one_path_U_field_code() { let exec = "/usr/bin/rmrfbye %U"; let paths = ["/"]; - let commands = exec_to_command(exec, "one_path_U_field_code", None, &paths) - .expect("Should parse valid exec"); + let commands = exec_to_command(exec, &paths).expect("Should parse valid exec"); assert_eq!(1, commands.len()); let command = commands.first().unwrap(); @@ -725,8 +542,7 @@ mod tests { "/usr/share/games/psp/miku.iso", "/usr/share/games/psp/eternia.iso", ]; - let commands = exec_to_command(exec, "mult_path_f_field_code", None, &paths) - .expect("Should parse valid exec"); + let commands = exec_to_command(exec, &paths).expect("Should parse valid exec"); assert_eq!(paths.len(), commands.len()); for (command, path) in commands.into_iter().zip(paths.iter()) { @@ -746,8 +562,7 @@ mod tests { "/usr/share/games/doom2/hr.wad", "/usr/share/games/doom2/hrmus.wad", ]; - let commands = exec_to_command(exec, "mult_path_F_field_code", None, &paths) - .expect("Should parse valid exec"); + let commands = exec_to_command(exec, &paths).expect("Should parse valid exec"); assert_eq!(1, commands.len()); let command = commands.first().unwrap(); @@ -769,8 +584,7 @@ mod tests { "https://redox-os.org/", "https://system76.com/", ]; - let commands = exec_to_command(exec, "mult_path_u_field_code", None, &paths) - .expect("Should parse valid exec"); + let commands = exec_to_command(exec, &paths).expect("Should parse valid exec"); assert_eq!(paths.len(), commands.len()); for (command, path) in commands.into_iter().zip(paths.iter()) { @@ -793,8 +607,7 @@ mod tests { "frieren01.mkv", "rtmp://example.org/this/video/doesnt/exist.avi", ]; - let commands = exec_to_command(exec, "mult_path_U_field_code", None, &paths) - .expect("Should parse valid exec"); + let commands = exec_to_command(exec, &paths).expect("Should parse valid exec"); assert_eq!(1, commands.len()); let command = commands.first().unwrap(); @@ -822,8 +635,7 @@ mod tests { "@@u", ]; let paths = ["file1.rs", "file2.rs"]; - let commands = exec_to_command(exec, "flatpak_style_exec", None, &paths) - .expect("Should parse valid exec"); + let commands = exec_to_command(exec, &paths).expect("Should parse valid exec"); assert_eq!(1, commands.len()); let command = commands.first().unwrap(); @@ -846,8 +658,7 @@ mod tests { "file:///usr/share/games/roguelike/mods/mod1", "file:///usr/share/games/roguelike/mods/mod2", ]; - let commands = exec_to_command(exec, "multiple_field_codes", None, &paths) - .expect("Should parse valid exec"); + let commands = exec_to_command(exec, &paths).expect("Should parse valid exec"); assert_eq!(1, commands.len()); let command = commands.first().unwrap(); @@ -880,8 +691,7 @@ mod tests { ]; let paths = ["rust_game_dev.pdf", "superhero_ferris.epub"]; let args_trailing = ["@@"]; - let commands = exec_to_command(exec, "sandwiched_field_code", None, &paths) - .expect("Should parse valid exec"); + let commands = exec_to_command(exec, &paths).expect("Should parse valid exec"); assert_eq!(1, commands.len()); let command = commands.first().unwrap(); diff --git a/src/mime_icon.rs b/src/mime_icon.rs index 26d899d..52b1a27 100644 --- a/src/mime_icon.rs +++ b/src/mime_icon.rs @@ -3,9 +3,11 @@ use cosmic::widget::icon; use mime_guess::Mime; use rustc_hash::FxHashMap; -use std::fs; -use std::path::Path; -use std::sync::{LazyLock, Mutex}; +use std::{ + fs, + path::Path, + sync::{LazyLock, Mutex}, +}; pub const FALLBACK_MIME_ICON: &str = "text-x-generic"; @@ -15,21 +17,20 @@ struct MimeIconKey { size: u16, } -#[derive(Default)] -pub struct MimeIconCache { +struct MimeIconCache { cache: FxHashMap>, - #[cfg(unix)] - pub shared_mime_info: xdg_mime::SharedMimeInfo, + shared_mime_info: xdg_mime::SharedMimeInfo, } impl MimeIconCache { - #[cfg(not(unix))] - pub fn get(&mut self, _key: MimeIconKey) -> Option { - None + pub fn new() -> Self { + Self { + cache: FxHashMap::default(), + shared_mime_info: xdg_mime::SharedMimeInfo::new(), + } } - #[cfg(unix)] - fn get(&mut self, key: MimeIconKey) -> Option { + pub fn get(&mut self, key: MimeIconKey) -> Option { self.cache .entry(key) .or_insert_with_key(|key| { @@ -38,7 +39,7 @@ impl MimeIconCache { return None; } let icon_name = icon_names.remove(0); - let mut named = icon::from_name(icon_name).prefer_svg(true).size(key.size); + let mut named = icon::from_name(icon_name).size(key.size); if !icon_names.is_empty() { let fallback_names = icon_names.into_iter().map(std::borrow::Cow::from).collect(); @@ -49,20 +50,9 @@ impl MimeIconCache { .clone() } } +static MIME_ICON_CACHE: LazyLock> = + LazyLock::new(|| Mutex::new(MimeIconCache::new())); -pub static MIME_ICON_CACHE: LazyLock> = - LazyLock::new(|| Mutex::new(MimeIconCache::default())); - -#[cfg(not(unix))] -pub fn mime_for_path( - path: impl AsRef, - metadata_opt: Option<&fs::Metadata>, - remote: bool, -) -> Mime { - mime_guess::from_path(path).first_or_octet_stream() -} - -#[cfg(unix)] pub fn mime_for_path( path: impl AsRef, metadata_opt: Option<&fs::Metadata>, @@ -72,7 +62,6 @@ pub fn mime_for_path( let mime_icon_cache = MIME_ICON_CACHE.lock().unwrap(); // Try the shared mime info cache first let mut gb = mime_icon_cache.shared_mime_info.guess_mime_type(); - gb.zero_size(false); if remote { if let Some(file_name) = path.file_name().and_then(std::ffi::OsStr::to_str) { gb.file_name(file_name); @@ -107,28 +96,12 @@ pub fn mime_icon(mime: Mime, size: u16) -> icon::Handle { let mut mime_icon_cache = MIME_ICON_CACHE.lock().unwrap(); match mime_icon_cache.get(MimeIconKey { mime, size }) { Some(handle) => handle, - None => icon::from_name(FALLBACK_MIME_ICON) - .prefer_svg(true) - .size(size) - .handle(), + None => icon::from_name(FALLBACK_MIME_ICON).size(size).handle(), } } -#[cfg(not(unix))] -pub fn parent_mime_types(_mime: &Mime) -> Option> { - None -} - -#[cfg(unix)] pub fn parent_mime_types(mime: &Mime) -> Option> { let mime_icon_cache = MIME_ICON_CACHE.lock().unwrap(); + mime_icon_cache.shared_mime_info.get_parents_aliased(mime) } - -pub fn is_mime_subclass_of(mime_type: &Mime, base: &Mime) -> bool { - let mime_icon_cache = MIME_ICON_CACHE.lock().unwrap(); - - mime_icon_cache - .shared_mime_info - .mime_type_subclass(mime_type, base) -} diff --git a/src/mounter/gvfs.rs b/src/mounter/gvfs.rs index b675dd0..cd4ea68 100644 --- a/src/mounter/gvfs.rs +++ b/src/mounter/gvfs.rs @@ -1,32 +1,21 @@ -use cosmic::iced::futures::SinkExt; -use cosmic::iced::{Subscription, stream}; -use cosmic::{Task, widget}; -use gio::glib; -use gio::prelude::*; -use std::any::TypeId; -use std::cell::Cell; -use std::future::pending; -use std::hash::Hash; -use std::path::PathBuf; -use std::sync::Arc; -use tokio::sync::mpsc; +use cosmic::{ + Task, + iced::{Subscription, futures::SinkExt, stream}, + widget, +}; +use gio::{glib, prelude::*}; +use std::{any::TypeId, cell::Cell, future::pending, hash::Hash, path::PathBuf, sync::Arc}; +use tokio::sync::{Mutex, mpsc}; use super::{Mounter, MounterAuth, MounterItem, MounterItems, MounterMessage}; -use crate::config::IconSizes; -use crate::err_str; -use crate::tab::{self, ChecksumState, DirSize, ItemMetadata, ItemThumbnail, Location}; +use crate::{ + config::IconSizes, + err_str, + tab::{self, DirSize, ItemMetadata, ItemThumbnail, Location}, +}; const TARGET_URI_ATTRIBUTE: &str = "standard::target-uri"; -// Attributes requested when listing a network directory. -const SCAN_ATTRIBUTES: &str = "standard::name,\ -standard::display-name,\ -standard::type,\ -standard::size,\ -standard::icon,\ -standard::is-hidden,\ -time::modified"; - fn resolve_uri(uri: &str) -> (String, gio::File) { let file = gio::File::for_uri(uri); // Resolve the target-uri if it exists @@ -128,27 +117,9 @@ fn network_scan(uri: &str, sizes: IconSizes) -> Result, String> { Box::from([]) }; - // `filesystem::remote` belongs to the filesystem namespace, which `enumerate_children` - // never fills in, so it has to be queried once for the directory being listed. - let remote = file - .query_filesystem_info( - gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE, - gio::Cancellable::NONE, - ) - .map(|info| info.boolean(gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE)) - .unwrap_or_else(|err| { - log::warn!("failed to get GIO filesystem info for {uri}: {err}"); - // Assume remote, so per-entry work is skipped rather than retried - true - }); - let mut items = Vec::new(); for info_res in file - .enumerate_children( - SCAN_ATTRIBUTES, - gio::FileQueryInfoFlags::NONE, - gio::Cancellable::NONE, - ) + .enumerate_children("*", gio::FileQueryInfoFlags::NONE, gio::Cancellable::NONE) .map_err(err_str)? { let info = info_res.map_err(err_str)?; @@ -160,17 +131,13 @@ fn network_scan(uri: &str, sizes: IconSizes) -> Result, String> { //TODO: what is the best way to resolve shortcuts? let location = Location::Network(uri, display_name.clone(), file.child(&name).path()); - let metadata = if force_dir { - ItemMetadata::SimpleDir { entries: 0 } - } else { + let metadata = if !force_dir && !info.boolean(gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE) { let mtime = info.attribute_uint64(gio::FILE_ATTRIBUTE_TIME_MODIFIED); let is_dir = matches!(info.file_type(), gio::FileType::Directory); let size_opt = (!is_dir).then_some(info.size() as u64); let mut children_opt = None; - // Counting children costs a directory listing per entry, which is far too - // expensive on a remote filesystem - if is_dir && !remote { + if is_dir { if let Some(path) = file.child(&name).path() { //TODO: calculate children in the background (and make it cancellable?) match std::fs::read_dir(&path) { @@ -190,8 +157,9 @@ fn network_scan(uri: &str, sizes: IconSizes) -> Result, String> { mtime, size_opt, children_opt, - is_dir, } + } else { + ItemMetadata::SimpleDir { entries: 0 } }; let (mime, icon_handle_grid, icon_handle_list, icon_handle_list_condensed) = { @@ -231,7 +199,6 @@ fn network_scan(uri: &str, sizes: IconSizes) -> Result, String> { metadata, hidden, location_opt: Some(location), - image_dimensions: None, mime, icon_handle_grid, icon_handle_list, @@ -246,7 +213,6 @@ fn network_scan(uri: &str, sizes: IconSizes) -> Result, String> { //TODO: scan directory size on gvfs mounts? dir_size: DirSize::NotDirectory, cut: false, - checksums: ChecksumState::default(), }); } Ok(items) @@ -263,10 +229,7 @@ fn dir_info(uri: &str) -> Result<(String, String, Option), glib::Error> Ok((resolved_uri, info.display_name().into(), file.path())) } -fn mount_op( - uri: String, - event_tx: std::sync::Weak>, -) -> gio::MountOperation { +fn mount_op(uri: String, event_tx: mpsc::UnboundedSender) -> gio::MountOperation { let mount_op = gio::MountOperation::new(); mount_op.connect_ask_password( move |mount_op, message, default_user, default_domain, flags| { @@ -289,9 +252,9 @@ fn mount_op( .then_some(false), }; let (auth_tx, mut auth_rx) = mpsc::channel(1); - if let Some(event_tx) = event_tx.upgrade() { - event_tx.send(Event::NetworkAuth(uri.clone(), auth, auth_tx)); - } + event_tx + .send(Event::NetworkAuth(uri.clone(), auth, auth_tx)) + .unwrap(); //TODO: async recv? if let Some(auth) = auth_rx.blocking_recv() { if auth.anonymous_opt == Some(true) { @@ -394,45 +357,37 @@ impl Item { pub struct Gvfs { command_tx: mpsc::UnboundedSender, - event_rx: Arc>, + event_rx: Arc>>, } impl Gvfs { pub fn new() -> Self { //TODO: switch to using gvfs-zbus which will better integrate with async rust let (command_tx, mut command_rx) = mpsc::unbounded_channel(); - let (event_tx, event_rx) = crate::channel::channel(); - let event_tx = Arc::new(event_tx); + let (event_tx, event_rx) = mpsc::unbounded_channel(); std::thread::spawn(move || { let main_loop = glib::MainLoop::new(None, false); main_loop.context().spawn_local(async move { - let event_tx = Arc::downgrade(&event_tx); let monitor = gio::VolumeMonitor::get(); { let event_tx = event_tx.clone(); monitor.connect_mount_changed(move |_monitor, mount| { log::info!("mount changed {}", MountExt::name(mount)); - if let Some(event_tx) = event_tx.upgrade() { - event_tx.send(Event::Changed); - } + event_tx.send(Event::Changed).unwrap(); }); } { let event_tx = event_tx.clone(); monitor.connect_mount_added(move |_monitor, mount| { log::info!("mount added {}", MountExt::name(mount)); - if let Some(event_tx) = event_tx.upgrade() { - event_tx.send(Event::Changed); - } + event_tx.send(Event::Changed).unwrap(); }); } { let event_tx = event_tx.clone(); monitor.connect_mount_removed(move |_monitor, mount| { log::info!("mount removed {}", MountExt::name(mount)); - if let Some(event_tx) = event_tx.upgrade() { - event_tx.send(Event::Changed); - } + event_tx.send(Event::Changed).unwrap(); }); } @@ -440,27 +395,21 @@ impl Gvfs { let event_tx = event_tx.clone(); monitor.connect_volume_changed(move |_monitor, volume| { log::info!("volume changed {}", VolumeExt::name(volume)); - if let Some(event_tx) = event_tx.upgrade() { - event_tx.send(Event::Changed); - } + event_tx.send(Event::Changed).unwrap(); }); } { let event_tx = event_tx.clone(); monitor.connect_volume_added(move |_monitor, volume| { log::info!("volume added {}", VolumeExt::name(volume)); - if let Some(event_tx) = event_tx.upgrade() { - event_tx.send(Event::Changed); - } + event_tx.send(Event::Changed).unwrap(); }); } { let event_tx = event_tx.clone(); monitor.connect_volume_removed(move |_monitor, volume| { log::info!("volume removed {}", VolumeExt::name(volume)); - if let Some(event_tx) = event_tx.upgrade() { - event_tx.send(Event::Changed); - } + event_tx.send(Event::Changed).unwrap(); }); } @@ -470,11 +419,7 @@ impl Gvfs { items_tx.send(items(&monitor, sizes)).await.unwrap(); } Cmd::Rescan => { - let Some(event_tx) = event_tx.upgrade() else { - return; - }; - - event_tx.send(Event::Items(items(&monitor, IconSizes::default()))); + event_tx.send(Event::Items(items(&monitor, IconSizes::default()))).unwrap(); } Cmd::Mount(mounter_item, complete_tx) => { let MounterItem::Gvfs(ref item) = mounter_item else { @@ -526,9 +471,6 @@ impl Gvfs { .ok().map(|info| info.boolean(gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE)) .unwrap_or(true); } - let Some(event_tx) = event_tx.upgrade() else { - return; - }; event_tx.send(Event::MountResult(updated_item, match res { Ok(()) => { _ = complete_tx.send(Ok(())); @@ -540,7 +482,7 @@ impl Gvfs { Some(gio::IOErrorEnum::FailedHandled) => Ok(false), _ => Err(format!("{err}")) }} - })); + })).unwrap(); }, ); break; @@ -556,9 +498,6 @@ impl Gvfs { gio::Cancellable::NONE, move |res| { log::info!("network drive {uri}: result {res:?}"); - let Some(event_tx) = event_tx.upgrade() else { - return; - }; event_tx.send(Event::NetworkResult(uri, match res { Ok(()) => { _ = result_tx.send(Ok(())); @@ -569,7 +508,7 @@ impl Gvfs { Some(gio::IOErrorEnum::FailedHandled) => Ok(false), _ => Err(format!("{err}")) }} - })); + })).unwrap(); } ); } @@ -593,9 +532,6 @@ impl Gvfs { // FIXME sometimes a uri can be mounted and then not recognized as mounted... // seems to be related to uri with a path items_tx.blocking_send(network_scan(&uri, sizes)).unwrap(); - let Some(event_tx) = event_tx.upgrade() else { - return; - }; event_tx.send(Event::NetworkResult(resolved_uri, match res { Ok(()) => { Ok(true) @@ -604,7 +540,7 @@ impl Gvfs { Some(gio::IOErrorEnum::FailedHandled) => Ok(false), _ => Err(format!("{err}")) } - })); + })).unwrap(); } ); } else { @@ -660,7 +596,7 @@ impl Gvfs { }); Self { command_tx, - event_rx: Arc::new(event_rx), + event_rx: Arc::new(Mutex::new(event_rx)), } } } @@ -689,7 +625,7 @@ impl Mounter for Gvfs { ) } - fn network_drive(&self, uri: String) -> Task { + fn network_drive(&self, uri: String) -> Task<()> { let command_tx = self.command_tx.clone(); Task::perform( async move { @@ -698,15 +634,9 @@ impl Mounter for Gvfs { command_tx.send(Cmd::NetworkDrive(uri, res_tx)).unwrap(); res_rx.await }, - |result| match result { - Ok(Ok(())) => true, - Ok(Err(err)) => { + |x| { + if let Err(err) = x { log::error!("{err:?}"); - false - } - Err(err) => { - log::error!("{err:?}"); - false } }, ) @@ -740,7 +670,7 @@ impl Mounter for Gvfs { let event_rx = self.event_rx.clone(); struct Wrapper { command_tx: mpsc::UnboundedSender, - event_rx: Arc>, + event_rx: Arc>>, } impl Hash for Wrapper { fn hash(&self, state: &mut H) { @@ -764,7 +694,7 @@ impl Mounter for Gvfs { MounterMessage, >| async move { command_tx.send(Cmd::Rescan).unwrap(); - while let Some(event) = event_rx.recv().await { + while let Some(event) = event_rx.lock().await.recv().await { match event { Event::Changed => command_tx.send(Cmd::Rescan).unwrap(), Event::Items(items) => { diff --git a/src/mounter/mod.rs b/src/mounter/mod.rs index c6366c9..b97f32a 100644 --- a/src/mounter/mod.rs +++ b/src/mounter/mod.rs @@ -1,13 +1,13 @@ -use cosmic::iced::Subscription; -use cosmic::{Task, widget}; -use std::collections::BTreeMap; -use std::fmt; -use std::path::PathBuf; -use std::sync::{Arc, LazyLock}; +use cosmic::{Task, iced::Subscription, widget}; +use std::{ + collections::BTreeMap, + fmt, + path::PathBuf, + sync::{Arc, LazyLock}, +}; use tokio::sync::mpsc; -use crate::config::IconSizes; -use crate::tab; +use crate::{config::IconSizes, tab}; #[cfg(feature = "gvfs")] mod gvfs; @@ -75,10 +75,10 @@ impl MounterItem { } } - pub fn icon(&self, _symbolic: bool) -> Option { + pub fn icon(&self, symbolic: bool) -> Option { match self { #[cfg(feature = "gvfs")] - Self::Gvfs(item) => item.icon(_symbolic), + Self::Gvfs(item) => item.icon(symbolic), Self::None => unreachable!(), } } @@ -103,7 +103,6 @@ impl MounterItem { pub type MounterItems = Vec; #[derive(Clone, Debug)] -#[allow(dead_code)] pub enum MounterMessage { Items(MounterItems), MountResult(MounterItem, Result), @@ -115,7 +114,7 @@ pub trait Mounter: Send + Sync { fn items(&self, sizes: IconSizes) -> Option; //TODO: send result fn mount(&self, item: MounterItem) -> Task<()>; - fn network_drive(&self, uri: String) -> Task; + fn network_drive(&self, uri: String) -> Task<()>; fn network_scan(&self, uri: &str, sizes: IconSizes) -> Option, String>>; fn dir_info(&self, uri: &str) -> Option<(String, String, Option)>; fn unmount(&self, item: MounterItem) -> Task<()>; diff --git a/src/mouse_area.rs b/src/mouse_area.rs index cbb7bbb..dea4b25 100644 --- a/src/mouse_area.rs +++ b/src/mouse_area.rs @@ -3,17 +3,21 @@ use std::time::Instant; use crate::tab::DOUBLE_CLICK_DURATION; -use cosmic::iced::core::border::Border; -use cosmic::iced::core::event::Event; -use cosmic::iced::core::mouse::{self, click}; -use cosmic::iced::core::renderer::{self, Quad, Renderer as _}; -use cosmic::iced::core::widget::{Operation, Tree, tree}; -use cosmic::iced::core::{ - Clipboard, Color, Layout, Length, Point, Rectangle, Shell, Size, Vector, Widget, layout, - overlay, touch, +use cosmic::{ + Element, Renderer, Theme, + iced_core::{ + Clipboard, Color, Layout, Length, Point, Rectangle, Shell, Size, Vector, Widget, + border::Border, + event::Event, + layout, + mouse::{self, click}, + overlay, + renderer::{self, Quad, Renderer as _}, + touch, + widget::{Operation, Tree, tree}, + }, + widget::Id, }; -use cosmic::widget::Id; -use cosmic::{Element, Renderer, Theme}; /// Emit messages on mouse events. #[allow(missing_debug_implementations)] @@ -214,9 +218,8 @@ impl<'a, Message, F> OnAutoScroll<'a, Message> for F where F: Fn(Option) -> pub trait OnMouseButton<'a, Message>: Fn(Option) -> Message + 'a {} impl<'a, Message, F> OnMouseButton<'a, Message> for F where F: Fn(Option) -> Message + 'a {} -pub trait OnDrag<'a, Message>: Fn(Option) -> Option + 'a {} -impl<'a, Message, F> OnDrag<'a, Message> for F where F: Fn(Option) -> Option + 'a -{} +pub trait OnDrag<'a, Message>: Fn(Option) -> Message + 'a {} +impl<'a, Message, F> OnDrag<'a, Message> for F where F: Fn(Option) -> Message + 'a {} pub trait OnResize<'a, Message>: Fn(Rectangle) -> Message + 'a {} impl<'a, Message, F> OnResize<'a, Message> for F where F: Fn(Rectangle) -> Message + 'a {} @@ -392,7 +395,7 @@ where update( self, - event, + &event, layout, cursor, shell, @@ -485,7 +488,7 @@ where state: &Tree, layout: Layout<'_>, renderer: &Renderer, - dnd_rectangles: &mut cosmic::iced::core::clipboard::DndDestinationRectangles, + dnd_rectangles: &mut cosmic::iced_core::clipboard::DndDestinationRectangles, ) { self.content.as_widget().drag_destinations( &state.children[0], @@ -564,16 +567,6 @@ fn update( state.last_position = position_in; } - if state.drag_initiated.is_some() - && matches!( - event, - Event::Mouse(mouse::Event::CursorMoved { .. }) - | Event::Touch(touch::Event::FingerMoved { .. }) - ) - { - shell.request_redraw(); - } - if let Event::Mouse(mouse::Event::CursorMoved { position }) = event { let virtual_position = Point::new( viewport.x - layout_bounds.x + position.x, @@ -650,7 +643,6 @@ fn update( { state.drag_initiated = None; state.prev_click = None; - shell.request_redraw(); if let Some(message) = widget.on_drag_end.as_ref() { shell.publish(message(cursor.position_in(layout_bounds))); } @@ -670,9 +662,7 @@ fn update( state.prev_click = None; return; } - if state.drag_initiated.take().is_some() { - shell.request_redraw(); - } + state.drag_initiated = None; if let Some(message) = widget.on_release.as_ref() { shell.publish(message(cursor.position_in(layout_bounds))); @@ -799,13 +789,12 @@ fn update( } if let Some((message, drag_rect)) = widget.on_drag.as_ref().zip(state.drag_rect(cursor)) { - let rect_opt = drag_rect.intersection(&layout_bounds).map(|mut rect| { - rect.x -= layout_bounds.x; - rect.y -= layout_bounds.y; - rect - }); - if let Some(message) = message(rect_opt) { - shell.publish(message); - } + shell.publish(message(drag_rect.intersection(&layout_bounds).map( + |mut rect| { + rect.x -= layout_bounds.x; + rect.y -= layout_bounds.y; + rect + }, + ))); } } diff --git a/src/operation/controller.rs b/src/operation/controller.rs index 691316c..f4b26fe 100644 --- a/src/operation/controller.rs +++ b/src/operation/controller.rs @@ -1,11 +1,7 @@ -use atomic_float::AtomicF32; -use num_enum::{IntoPrimitive, TryFromPrimitive}; -use std::sync::Arc; -use std::sync::atomic::{self, AtomicU16}; +use std::sync::{Arc, Mutex}; use tokio::sync::Notify; -#[derive(Clone, Copy, Debug, PartialEq, Eq, IntoPrimitive, TryFromPrimitive)] -#[repr(u16)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ControllerState { Cancelled, Failed, @@ -15,8 +11,8 @@ pub enum ControllerState { #[derive(Debug)] struct ControllerInner { - state: AtomicU16, - progress: AtomicF32, + state: Mutex, + progress: Mutex, notify: Notify, } @@ -31,8 +27,8 @@ impl Default for Controller { Self { primary: true, inner: Arc::new(ControllerInner { - state: AtomicU16::new(ControllerState::Running.into()), - progress: AtomicF32::new(0.0), + state: Mutex::new(ControllerState::Running), + progress: Mutex::new(0.0), notify: Notify::new(), }), } @@ -54,24 +50,19 @@ impl Controller { } pub fn progress(&self) -> f32 { - self.inner.progress.load(atomic::Ordering::Relaxed) + *self.inner.progress.lock().unwrap() } pub fn set_progress(&self, progress: f32) { - self.inner - .progress - .swap(progress, atomic::Ordering::Relaxed); + *self.inner.progress.lock().unwrap() = progress; } pub fn state(&self) -> ControllerState { - ControllerState::try_from(self.inner.state.load(atomic::Ordering::Relaxed)) - .unwrap_or(ControllerState::Failed) + *self.inner.state.lock().unwrap() } pub fn set_state(&self, state: ControllerState) { - self.inner - .state - .store(state.into(), atomic::Ordering::Relaxed); + *self.inner.state.lock().unwrap() = state; self.inner.notify.notify_waiters(); } @@ -95,35 +86,6 @@ impl Controller { self.set_state(ControllerState::Paused); } - /// Returns when the state is paused. - /// - /// Use this to pause futures. - pub async fn until_paused(&self) { - loop { - if matches!(self.state(), ControllerState::Paused) { - return; - } - - self.inner.notify.notified().await; - } - } - - /// Returns when state is neither paused, cancelled, nor failed. - /// - /// Use this to resume futures. - pub async fn until_unpaused(&self) { - loop { - if !matches!( - self.state(), - ControllerState::Paused | ControllerState::Cancelled | ControllerState::Failed - ) { - return; - } - - self.inner.notify.notified().await; - } - } - pub fn unpause(&self) { if !self.is_cancelled() | !self.is_failed() { self.set_state(ControllerState::Running); diff --git a/src/operation/mod.rs b/src/operation/mod.rs index 62dbba6..3aad6fc 100644 --- a/src/operation/mod.rs +++ b/src/operation/mod.rs @@ -1,15 +1,20 @@ -use crate::app::{ArchiveType, DialogPage, Message, REPLACE_BUTTON_ID}; -use crate::config::IconSizes; -use crate::spawn_detached::spawn_detached; -use crate::{archive, fl, tab}; -use cosmic::iced::futures::channel::mpsc::Sender; -use cosmic::iced::futures::{self, SinkExt, StreamExt, stream}; -use std::borrow::Cow; -use std::fmt::Formatter; -use std::fs; -use std::io::{self, Read, Write}; -use std::path::{Path, PathBuf}; -use std::sync::Arc; +use crate::{ + app::{ArchiveType, DialogPage, Message, REPLACE_BUTTON_ID}, + archive, + config::IconSizes, + fl, + spawn_detached::spawn_detached, + tab, +}; +use cosmic::iced::futures::{self, SinkExt, StreamExt, channel::mpsc::Sender, stream}; +use std::{ + borrow::Cow, + fmt::Formatter, + fs, + io::{self, Read, Write}, + path::{Path, PathBuf}, + sync::Arc, +}; use tokio::sync::{Mutex as TokioMutex, mpsc}; use walkdir::WalkDir; use zip::AesMode::Aes256; @@ -17,9 +22,6 @@ use zip::AesMode::Aes256; pub use self::controller::{Controller, ControllerState}; pub mod controller; -pub use notifiers::*; -mod notifiers; - pub use self::reader::OpReader; pub mod reader; @@ -34,7 +36,7 @@ async fn handle_replace( conflict_count: usize, ) -> ReplaceResult { let item_from = match tab::item_from_path(file_from, IconSizes::default()) { - Ok(ok) => Box::new(ok), + Ok(ok) => ok, Err(err) => { log::warn!("{err}"); return ReplaceResult::Cancel; @@ -42,7 +44,7 @@ async fn handle_replace( }; let item_to = match tab::item_from_path(file_to, IconSizes::default()) { - Ok(ok) => Box::new(ok), + Ok(ok) => ok, Err(err) => { log::warn!("{err}"); return ReplaceResult::Cancel; @@ -109,7 +111,7 @@ async fn copy_or_move( ); // Handle duplicate file names by renaming paths - let from_to_pairs_iter = paths + let mut from_to_pairs: Vec<(PathBuf, PathBuf)> = paths .into_iter() .zip(std::iter::repeat(to.as_path())) .filter_map(|(from, to)| { @@ -127,46 +129,36 @@ async fn copy_or_move( //TODO: how to handle from missing file name? None } - }); + }) + .collect(); // Attempt quick and simple renames //TODO: allow rename to be used for directories in recursive context? + if matches!(method, Method::Move { .. }) { + from_to_pairs.retain(|(from, to)| { + //TODO: show replace dialog here? + if to.exists() { + return true; + } - let from_to_pairs: Vec<(PathBuf, PathBuf)> = if matches!(method, Method::Move { .. }) { - from_to_pairs_iter - .map(|(from, to)| async move { - //TODO: show replace dialog here? - if to.exists() { - return Some((from, to)); + //TODO: use compio::fs::rename? + match fs::rename(from, to) { + Ok(()) => { + log::info!("renamed {} to {}", from.display(), to.display()); + false } - - match compio::fs::rename(&from, &to).await { - Ok(()) => { - log::info!("renamed {} to {}", from.display(), to.display()); - None - } - Err(err) => { - log::info!( - "failed to rename {} to {}, fallback to recursive move: {}", - from.display(), - to.display(), - err - ); - Some((from, to)) - } + Err(err) => { + log::info!( + "failed to rename {} to {}, fallback to recursive move: {}", + from.display(), + to.display(), + err + ); + true } - }) - .collect::>() - .fold(Vec::new(), |mut pairs, pair| async move { - if let Some(pair) = pair { - pairs.push(pair); - } - pairs - }) - .await - } else { - from_to_pairs_iter.collect() - }; + } + }); + } let mut context = Context::new(controller.clone()); @@ -224,7 +216,7 @@ pub async fn sync_to_disk( } })) .buffer_unordered(32) - .collect::<()>() + .collect::>() .await; // Sync directories to disk @@ -234,7 +226,7 @@ pub async fn sync_to_disk( } })) .buffer_unordered(16) - .collect::<()>() + .collect::>() .await; } @@ -770,12 +762,13 @@ impl Operation { OperationError::from_err(e, &controller) })?; - if let Ok(modified) = metadata.modified() - && let Some(last_modified) = + if let Ok(modified) = metadata.modified() { + if let Some(last_modified) = archive::system_time_to_zip_date_time(modified) - { - zip_options = - zip_options.last_modified_time(last_modified); + { + zip_options = + zip_options.last_modified_time(last_modified); + } } #[cfg(unix)] @@ -967,13 +960,11 @@ impl Operation { password, } => { let controller_clone = controller.clone(); - compio::runtime::spawn(async move { - let extracted = compio::runtime::spawn_blocking(move || { + compio::runtime::spawn_blocking( + move || -> Result { let controller = controller_clone; let total_paths = paths.len(); let mut op_sel = OperationSelection::default(); - let mut written_files = Vec::new(); - let mut target_dirs = std::collections::HashSet::new(); for (i, path) in paths.iter().enumerate() { futures::executor::block_on(async { controller @@ -997,32 +988,16 @@ impl Operation { op_sel.ignored.push(path.clone()); op_sel.selected.push(new_dir.clone()); - let (files, dirs) = crate::archive::extract( - path, - &new_dir, - &password, - &controller, - )?; - written_files.extend(files); - target_dirs.extend(dirs); + crate::archive::extract(path, &new_dir, &password, &controller)?; } } - Ok::<_, OperationError>((op_sel, written_files, target_dirs)) - }) - .await - .map_err(wrap_compio_spawn_error)??; - - let (op_sel, written_files, target_dirs) = extracted; - if !written_files.is_empty() || !target_dirs.is_empty() { - sync_to_disk(written_files, target_dirs).await; - } - - Ok::<_, OperationError>(op_sel) - }) - .await - .map_err(wrap_compio_spawn_error)? + Ok(op_sel) + }, + ) } + .await + .map_err(wrap_compio_spawn_error)?, Self::Move { paths, to, @@ -1138,9 +1113,7 @@ impl Operation { #[cfg(target_os = "macos")] Self::Restore { .. } => { // TODO: add support for macos - return Err(OperationError::from_msg( - "Restoring from trash is not supported on macos", - )); + return OperationError::from_msg("Restoring from trash is not supported on macos"); } #[cfg(not(target_os = "macos"))] Self::Restore { items } => { @@ -1156,26 +1129,10 @@ impl Operation { paths.push(item.original_path()); - // Items with .trashinfo id use standard restore; sub-items use manual move - if item - .id - .to_str() - .map_or(false, |s| s.ends_with(".trashinfo")) - { - compio::runtime::spawn_blocking(|| trash::os_limited::restore_all([item])) - .await - .map_err(wrap_compio_spawn_error)? - .map_err(|e| OperationError::from_err(e, &controller))?; - } else { - let from = PathBuf::from(&item.id); - let to = item.original_path(); - if let Some(parent) = to.parent() { - std::fs::create_dir_all(parent) - .map_err(|e| OperationError::from_err(e, &controller))?; - } - std::fs::rename(&from, &to) - .map_err(|e| OperationError::from_err(e, &controller))?; - } + compio::runtime::spawn_blocking(|| trash::os_limited::restore_all([item])) + .await + .map_err(wrap_compio_spawn_error)? + .map_err(|e| OperationError::from_err(e, &controller))?; } Ok(OperationSelection { ignored: Vec::new(), @@ -1224,10 +1181,8 @@ impl Operation { .map_err(|s| OperationError::from_state(s, &controller))?; let controller_clone = controller.clone(); - let path_clone = path.clone(); compio::runtime::spawn_blocking(move || -> Result<(), OperationError> { let controller = controller_clone; - let path = path_clone; //TODO: what to do on non-Unix systems? #[cfg(unix)] { @@ -1242,10 +1197,7 @@ impl Operation { .await .map_err(wrap_compio_spawn_error)? .map_err(|e| OperationError::from_err(e, &controller))?; - Ok(OperationSelection { - ignored: Vec::new(), - selected: vec![path], - }) + Ok(OperationSelection::default()) } }; @@ -1272,23 +1224,28 @@ fn wrap_compio_spawn_error(err: Box) -> OperationError #[cfg(test)] mod tests { - use std::fs::{self, File}; - use std::io; - use std::path::PathBuf; + use std::{ + fs::{self, File}, + io, + path::PathBuf, + }; - use cosmic::iced::futures::channel::mpsc; - use cosmic::iced::futures::{StreamExt, future}; + use cosmic::iced::futures::{StreamExt, channel::mpsc, future}; use log::debug; use test_log::test; use tokio::sync; use super::{Controller, Operation, OperationError, OperationSelection, ReplaceResult}; - use crate::app::test_utils::{ - NAME_LEN, NUM_DIRS, NUM_FILES, NUM_HIDDEN, NUM_NESTED, empty_fs, filter_dirs, filter_files, - simple_fs, + use crate::{ + app::{ + DialogPage, Message, + test_utils::{ + NAME_LEN, NUM_DIRS, NUM_FILES, NUM_HIDDEN, NUM_NESTED, empty_fs, filter_dirs, + filter_files, simple_fs, + }, + }, + fl, }; - use crate::app::{DialogPage, Message}; - use crate::fl; /// Simple wrapper around `[Operation::Copy]` pub async fn operation_copy( diff --git a/src/operation/notifiers.rs b/src/operation/notifiers.rs deleted file mode 100644 index 0e27ce9..0000000 --- a/src/operation/notifiers.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2026 System76 -// SPDX-License-Identifier: GPL-3.0-only - -use std::path::{Path, PathBuf}; -use std::sync::{Arc, LazyLock, Mutex}; -use tokio::sync::Notify; - -/// Monitor files which are being written to. -pub struct FileWritingNotifier { - data: Vec, - notify: Arc, -} - -static ACTIVELY_WRITING: LazyLock> = LazyLock::new(|| { - Mutex::new(FileWritingNotifier { - data: Vec::new(), - notify: Arc::new(Notify::new()), - }) -}); - -/// Append path that is being written to. -pub fn actively_writing_add(path: PathBuf) { - ACTIVELY_WRITING.lock().unwrap().data.push(path); -} - -/// Remove path to file that has finished writing and notify waiters. -pub fn actively_writing_remove(path: &Path) { - let mut guard = ACTIVELY_WRITING.lock().unwrap(); - guard.data.retain(|p| p != path); - guard.notify.notify_waiters(); -} - -/// Wait until the actively-writing queue is empty or a file has been removed. -pub async fn actively_writing_tick() { - let notify = (|| { - let guard = ACTIVELY_WRITING.lock().unwrap(); - - if !guard.data.is_empty() { - return Some(guard.notify.clone()); - } - - None - })(); - - if let Some(notify) = notify { - notify.notified().await - } -} - -/// Check if a file is being written to. Avoid thumbnail generation until after it is finished. -pub fn is_actively_writing_to(path: &Path) -> bool { - ACTIVELY_WRITING - .lock() - .unwrap() - .data - .iter() - .any(|p| p == path) -} diff --git a/src/operation/reader.rs b/src/operation/reader.rs index 9f45441..75088df 100644 --- a/src/operation/reader.rs +++ b/src/operation/reader.rs @@ -1,5 +1,4 @@ -use std::path::Path; -use std::{fs, io}; +use std::{fs, io, path::Path}; use crate::operation::OperationError; diff --git a/src/operation/recursive.rs b/src/operation/recursive.rs index eb141b8..a807594 100644 --- a/src/operation/recursive.rs +++ b/src/operation/recursive.rs @@ -1,39 +1,15 @@ -// Copyright 2023 System76 -// SPDX-License-Identifier: GPL-3.0-only - -use super::{Controller, OperationSelection, ReplaceResult, copy_unique_path}; -use crate::operation::{OperationError, sync_to_disk}; -use anyhow::Context as AnyhowContext; use compio::BufResult; use compio::buf::{IntoInner, IoBuf}; -use compio::driver::ToSharedFd; -use compio::driver::op::AsyncifyFd; use compio::io::{AsyncReadAt, AsyncWriteAt}; -use cosmic::iced::futures; -#[cfg(feature = "gvfs")] -use futures::{FutureExt, StreamExt}; -use std::cell::Cell; -use std::error::Error; -use std::fs; use std::future::Future; -use std::ops::ControlFlow; -use std::path::PathBuf; use std::pin::Pin; -use std::rc::Rc; use std::time::Instant; +use std::{cell::Cell, error::Error, fs, ops::ControlFlow, path::PathBuf, rc::Rc}; use walkdir::WalkDir; -#[cfg(feature = "gvfs")] -use gio::prelude::FileExtManual; +use crate::operation::{OperationError, sync_to_disk}; -#[derive(thiserror::Error, Debug)] -pub enum GioCopyError { - #[error("controller state")] - Controller(OperationError), - #[cfg(feature = "gvfs")] - #[error("gio copy failed")] - GLib(#[from] glib::Error), -} +use super::{Controller, OperationSelection, ReplaceResult, copy_unique_path}; pub enum Method { Copy, @@ -337,28 +313,136 @@ impl Op { }) } - async fn run(&mut self, ctx: &mut Context, progress: Progress) -> Result> { + async fn run( + &mut self, + ctx: &mut Context, + mut progress: Progress, + ) -> Result> { if self.skipped.normal.get() || (self.is_cleanup && self.skipped.cleanup.get()) { return Ok(true); } match self.kind { OpKind::Copy => { - crate::operation::actively_writing_add(self.to.clone()); - let result = self.copy(ctx, progress).await; - - if result.is_err() { - _ = compio::fs::remove_file(&self.to).await; + // Remove `to` if overwriting and it is an existing file + if self.to.is_file() { + match ctx.replace(self).await? { + ControlFlow::Continue(to) => { + self.to = to; + } + ControlFlow::Break(ret) => { + return Ok(ret); + } + } } - crate::operation::actively_writing_remove(&self.to); - return result; + let (from_file, metadata, mut to_file) = cosmic::iced::futures::try_join!( + async { + compio::fs::OpenOptions::new() + .read(true) + .open(&self.from) + .await + }, + compio::fs::metadata(&self.from), + // This is atomic and ensures `to` is not created by any other process + async { + compio::fs::OpenOptions::new() + .create_new(true) + .write(true) + .open(&self.to) + .await + } + )?; + + progress.total_bytes = Some(metadata.len()); + (ctx.on_progress)(self, &progress); + if let Err(err) = to_file.set_permissions(metadata.permissions()).await { + // This error is not propagated upwards as some filesystems do not support setting permissions + log::warn!( + "failed to set permissions for {}: {}", + self.to.display(), + err + ); + } + + // Prevent spamming the progress callbacks. + let mut last_progress_update = Instant::now(); + // io_uring/IOCP requires transferring ownership of the buffer to the kernel. + let mut buf_in = std::mem::take(&mut ctx.buf); + // Track where the current read/write position is at. + let mut pos = 0; + + loop { + let BufResult(result, buf_out) = from_file.read_at(buf_in, pos).await; + + let count = match result { + Ok(0) => { + ctx.buf = buf_out; + break; + } + Ok(count) => count, + Err(why) => { + ctx.buf = buf_out; + return Err(why.into()); + } + }; + + let BufResult(result, buf_out_slice) = + to_file.write_at(buf_out.slice(..count), pos).await; + let buf_out = buf_out_slice.into_inner(); + + if let Err(why) = result { + ctx.buf = buf_out; + return Err(why.into()); + } + + progress.current_bytes += count as u64; + pos += count as u64; + + // Avoid spamming progress messages too early. + let current = Instant::now(); + if current.duration_since(last_progress_update).as_millis() > 49 { + last_progress_update = current; + (ctx.on_progress)(self, &progress); + + // Also check if the progress was cancelled. + if let Err(state) = ctx.controller.check().await { + ctx.buf = buf_out; + return Err(OperationError::from_state(state, &ctx.controller).into()); + } + } + + buf_in = buf_out; + } + + let mut times = fs::FileTimes::new(); + { + use std::os::unix::prelude::MetadataExt; + log::info!("{}", metadata.mtime()); + } + if let Ok(time) = dbg!(metadata.modified()) { + times = times.set_modified(time); + } + if let Ok(time) = dbg!(metadata.accessed()) { + times = times.set_accessed(time); + } + //TODO: upstream set_times implementation to compio? + { + use compio::driver::{ToSharedFd, op::AsyncifyFd}; + let op = + AsyncifyFd::new(to_file.to_shared_fd(), move |file: &std::fs::File| { + BufResult(file.set_times(times).map(|_| 0), ()) + }); + match compio::runtime::submit(op).await.0.map(|_| ()) { + Ok(()) => { + log::info!("set times for {} to {:?}", self.to.display(), times); + } + Err(err) => { + log::warn!("failed to set times for {}: {}", self.to.display(), err); + } + } + } } OpKind::Move { cross_device_copy } => { - // Do not clean up if cross_device_copy is set - if cross_device_copy { - self.skipped.cleanup.set(true); - } - // Remove `to` if overwriting and it is an existing file if self.to.is_file() { match ctx.replace(self).await? { @@ -436,286 +520,4 @@ impl Op { } Ok(true) } - - async fn copy( - &mut self, - ctx: &mut Context, - mut progress: Progress, - ) -> Result> { - // Remove `to` if overwriting and it is an existing file - if self.to.is_file() { - match ctx.replace(self).await? { - ControlFlow::Continue(to) => { - self.to = to; - } - ControlFlow::Break(ret) => { - return Ok(ret); - } - } - } - - let (from_file_open_result, metadata, to_file_open_result) = cosmic::iced::futures::join!( - async { - compio::fs::OpenOptions::new() - .read(true) - .open(&self.from) - .await - .with_context(|| format!("failed to open {} for reading", self.from.display(),)) - }, - async { compio::fs::metadata(&self.from).await.ok() }, - // This is atomic and ensures `to` is not created by any other process - async { - compio::fs::OpenOptions::new() - .create_new(true) - .write(true) - .open(&self.to) - .await - } - ); - - let from_file = from_file_open_result?; - - let mut to_file = match to_file_open_result { - Ok(file) => file, - #[cfg(not(feature = "gvfs"))] - Err(why) => { - _ = from_file.close().await; - return Err(why) - .with_context(|| format!("failed to open {} for writing", self.to.display())) - .map_err(Into::into); - } - #[cfg(feature = "gvfs")] - Err(_why) => { - _ = from_file.close().await; - return self - .gio_file_copy(ctx, progress) - .await - .map(|()| true) - .map_err(Into::into); - } - }; - progress.total_bytes = metadata.as_ref().map(|m| m.len()); - (ctx.on_progress)(self, &progress); - - if let Some(metadata) = metadata.as_ref() - && let Err(why) = to_file.set_permissions(metadata.permissions()).await - { - // This error is not propagated upwards as some filesystems do not support setting permissions - if !matches!(why.kind(), std::io::ErrorKind::Unsupported) { - tracing::warn!(?why, "failed to set permissions for {}", self.to.display(),); - } - } - - // Prevent spamming the progress callbacks. - let mut last_progress_update = Instant::now(); - // io_uring/IOCP requires transferring ownership of the buffer to the kernel. - let mut buf_in = std::mem::take(&mut ctx.buf); - // Track where the current read/write position is at. - let mut pos = 0; - - loop { - let BufResult(result, buf_out) = from_file.read_at(buf_in, pos).await; - - let count = match result { - Ok(0) => { - buf_in = buf_out; - break; - } - Ok(count) => count, - Err(why) => { - ctx.buf = buf_out; - tracing::error!("failed to read: {:?}", why); - _ = futures::future::join(from_file.close(), to_file.close()).await; - return Err(why).context("failed to read")?; - } - }; - - let BufResult(result, buf_out_slice) = - to_file.write_at(buf_out.slice(..count), pos).await; - let buf_out = buf_out_slice.into_inner(); - - if let Err(why) = result { - #[cfg(feature = "gvfs")] - if let std::io::ErrorKind::Unsupported = why.kind() { - ctx.buf = buf_out; - _ = futures::future::join(from_file.close(), to_file.close()).await; - return self - .gio_file_copy(ctx, progress) - .await - .map(|_| true) - .map_err(Into::into); - } - - tracing::error!("failed to write: {:?}", why); - ctx.buf = buf_out; - _ = futures::future::join(from_file.close(), to_file.close()).await; - return Err(why).context("failed to write")?; - } - - progress.current_bytes += count as u64; - pos += count as u64; - - // Avoid spamming progress messages too early. - let current = Instant::now(); - if current.duration_since(last_progress_update).as_millis() > 49 { - last_progress_update = current; - (ctx.on_progress)(self, &progress); - - // Also check if the progress was cancelled. - if let Err(state) = ctx.controller.check().await { - ctx.buf = buf_out; - tracing::warn!( - "operation to copy from {:?} to {:?} cancelled", - self.from, - self.to - ); - _ = futures::future::join(from_file.close(), to_file.close()).await; - return Err(OperationError::from_state(state, &ctx.controller).into()); - } - } - - buf_in = buf_out; - } - - ctx.buf = buf_in; - - if let Some(metadata) = metadata.as_ref() { - let mut times = fs::FileTimes::new(); - if let Ok(time) = metadata.modified() { - times = times.set_modified(time); - } - if let Ok(time) = metadata.accessed() { - times = times.set_accessed(time); - } - //TODO: upstream set_times implementation to compio? - let op = AsyncifyFd::new(to_file.to_shared_fd(), move |file: &std::fs::File| { - BufResult(file.set_times(times).map(|_| 0), ()) - }); - match compio::runtime::submit(op).await.0.map(|_| ()) { - Ok(()) => { - tracing::info!("set times for {} to {:?}", self.to.display(), times); - } - Err(why) => { - if !matches!(why.kind(), std::io::ErrorKind::Unsupported) { - tracing::error!(?why, "failed to set times for {}", self.to.display()); - } - } - } - } - - _ = to_file.close().await; - - Ok(true) - } - - /// Fallback mechanism in the event that unsupported I/O error errors occur. - /// Fixes unsupported errors when copying large files over MTP. - /// TODO: Find what Gio.File does to work around this. - #[cfg(feature = "gvfs")] - async fn gio_file_copy( - &self, - ctx: &mut Context, - mut progress: Progress, - ) -> Result<(), GioCopyError> { - _ = compio::fs::remove_file(&self.to).await; - - let from = gio::File::for_path(&self.from); - let to = gio::File::for_path(&self.to); - let (progress_tx, mut progress_rx) = tokio::sync::mpsc::unbounded_channel(); - let (tx, rx) = tokio::sync::oneshot::channel(); - let (pause_tx, mut pause_rx) = tokio::sync::watch::channel(false); - - let task = compio::runtime::spawn_blocking(move || { - let glib_context = glib::MainContext::new(); - let glib_loop = glib::MainLoop::new(Some(&glib_context), false); - glib_context.with_thread_default(move || { - let glib_loop2 = glib_loop.clone(); - glib::MainContext::ref_thread_default().spawn_local(async move { - // Create a future for copying the file with `gio::File`. This also creates a progress stream. - let (gio_copy_fut, mut progress_stream) = from.copy_future( - &to, - gio::FileCopyFlags::OVERWRITE | gio::FileCopyFlags::ALL_METADATA, - glib::Priority::LOW, - ); - - let mut copy_fut = gio_copy_fut - .map(|result| result.map_err(GioCopyError::GLib)) - .fuse(); - - let progress_fut = std::pin::pin!(async { - while let Some((current_bytes, _)) = progress_stream.next().await { - _ = progress_tx.send(current_bytes); - } - - drop(progress_tx); - futures::future::pending::<()>().await; - }); - - let mut progress_fut = progress_fut.fuse(); - let mut pause_rx2 = pause_rx.clone(); - - loop { - let until_paused = std::pin::pin!(pause_rx.wait_for(|paused| *paused)); - futures::select! { - _ = &mut progress_fut => {}, - - result = &mut copy_fut => { - _ = tx.send(result.map(|_| ())); - glib_loop2.quit(); - return; - } - - _ = until_paused.fuse() => { - _ = pause_rx2.wait_for(|paused| !*paused).await; - } - } - } - }); - - glib_loop.run(); - }) - }); - - let mut last_progress_update = Instant::now(); - let mut task = task.fuse(); - let mut rx = rx.fuse(); - - loop { - let until_paused = std::pin::pin!(ctx.controller.until_paused()); - futures::select! { - value = progress_rx.recv().fuse() => { - if let Some(current_bytes) = value { - progress.current_bytes = current_bytes as u64; - let current = Instant::now(); - if current.duration_since(last_progress_update).as_millis() > 49 { - last_progress_update = current; - (ctx.on_progress)(self, &progress); - // Also check if the progress was cancelled. - if let Err(state) = ctx.controller.check().await { - tracing::warn!( - "operation to copy from {:?} to {:?} cancelled", - self.from, - self.to - ); - return Err::<(), GioCopyError>(GioCopyError::Controller( - OperationError::from_state(state, &ctx.controller), - )); - } - } - } - } - - result = rx => return result.unwrap(), - - _ = task => (), - - _ = until_paused.fuse() => { - // Pauses an active copy while the controller state is paused. - _ = pause_tx.send(true); - ctx.controller.until_unpaused().await; - _ = pause_tx.send(false); - } - } - } - } } diff --git a/src/tab.rs b/src/tab.rs index 93f3293..23de5d6 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -1,73 +1,91 @@ -#[cfg(feature = "desktop")] -use cosmic::desktop::fde::{DesktopEntry, get_languages_from_env}; -use cosmic::iced::advanced::graphics; -use cosmic::iced::advanced::text::{self, Paragraph}; -use cosmic::iced::alignment::Vertical; -use cosmic::iced::clipboard::dnd::DndAction; -use cosmic::iced::core::mouse::ScrollDelta; -use cosmic::iced::core::widget::tree; -use cosmic::iced::futures::{self, SinkExt}; -use cosmic::iced::keyboard::Modifiers; -use cosmic::iced::widget::scrollable::{self, AbsoluteOffset, Viewport}; -use cosmic::iced::widget::{rule, stack}; -use cosmic::iced::{ - Alignment, Border, Color, ContentFit, Length, Point, Rectangle, Size, Subscription, Vector, - padding, stream, window, +use chrono::{Datelike, Timelike, Utc}; +use cosmic::{ + Apply, Element, cosmic_theme, + desktop::fde::{DesktopEntry, get_languages_from_env}, + font, + iced::{ + Alignment, Border, Color, ContentFit, Length, Point, Rectangle, Size, Subscription, Vector, + advanced::{ + graphics, + text::{self, Paragraph}, + }, + alignment::{Horizontal, Vertical}, + clipboard::dnd::DndAction, + event, + futures::{self, SinkExt}, + keyboard::Modifiers, + padding, stream, + widget::{ + rule, + scrollable::{self, AbsoluteOffset, Viewport}, + stack, + }, + window, + }, + iced_core::{mouse::ScrollDelta, widget::tree}, + theme, + widget::{ + self, DndDestination, DndSource, Id, RcElementWrapper, Space, Widget, + menu::{action::MenuAction, key_bind::KeyBind}, + space, + }, }; -use cosmic::widget::menu::action::MenuAction; -use cosmic::widget::menu::key_bind::KeyBind; -use cosmic::widget::{self, DndDestination, DndSource, Id, RcElementWrapper, Widget, space}; -use cosmic::{Apply, Element, cosmic_theme, font, theme}; -#[cfg(feature = "desktop")] use i18n_embed::LanguageLoader; -use icu::datetime::input::DateTime; -use icu::datetime::options::TimePrecision; -use icu::datetime::{DateTimeFormatter, DateTimeFormatterPreferences, fieldsets}; -use icu::locale::preferences::extensions::unicode::keywords::HourCycle; -use image::{DynamicImage, ImageReader}; -use jiff_icu::ConvertFrom; +use icu::{ + datetime::{ + DateTimeFormatter, DateTimeFormatterPreferences, fieldsets, + input::{Date, DateTime, Time}, + options::TimePrecision, + }, + locale::preferences::extensions::unicode::keywords::HourCycle, +}; +use image::{DynamicImage, ImageDecoder, ImageReader}; +use jxl_oxide::integration::JxlDecoder; use mime_guess::{Mime, mime}; +use regex::Regex; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; -use sha2::{Digest, Sha256}; -use std::borrow::Cow; -use std::cell::Cell; -use std::cmp::{Ordering, Reverse}; -use std::collections::{BTreeMap, BTreeSet, HashMap}; -use std::error::Error; -use std::fmt::{self, Display}; -use std::fs::{self, File, Metadata}; -use std::hash::Hash; -use std::io::{BufRead, BufReader, Read}; -#[cfg(unix)] -use std::os::unix::fs::MetadataExt; -use std::path::{self, Path, PathBuf}; -use std::sync::{Arc, LazyLock, RwLock, atomic}; -use std::time::{Duration, Instant, SystemTime}; +use std::{ + borrow::Cow, + cell::Cell, + cmp::{Ordering, Reverse}, + collections::{BTreeMap, HashMap}, + error::Error, + fmt::{self, Display}, + fs::{self, File, Metadata}, + hash::Hash, + io::{BufRead, BufReader}, + os::unix::fs::MetadataExt, + path::{self, Path, PathBuf}, + sync::{Arc, LazyLock, RwLock, atomic}, + time::{Duration, Instant, SystemTime}, +}; use tempfile::NamedTempFile; use tokio::sync::mpsc; use trash::{TrashItem, TrashItemMetadata, TrashItemSize}; use walkdir::WalkDir; -use crate::app::{Action, PreviewItem, PreviewKind}; -use crate::clipboard::{ClipboardCopy, ClipboardKind, ClipboardPaste}; -use crate::config::{ - ContextActionPreset, DesktopConfig, ICON_SCALE_MAX, ICON_SIZE_GRID, IconSizes, TabConfig, - ThumbCfg, +use crate::{ + FxOrderMap, + app::{Action, PreviewItem, PreviewKind}, + clipboard::{ClipboardCopy, ClipboardKind, ClipboardPaste}, + config::{DesktopConfig, ICON_SCALE_MAX, ICON_SIZE_GRID, IconSizes, TabConfig, ThumbCfg}, + dialog::DialogKind, + fl, + large_image::{ + LargeImageManager, decode_large_image, exceeds_memory_limit, should_use_dedicated_worker, + should_use_tiling, + }, + localize::{LANGUAGE_SORTER, LOCALE}, + menu, mime_app, + mime_icon::{mime_for_path, mime_icon}, + mounter::MOUNTERS, + mouse_area, + operation::{Controller, OperationError}, + thumbnail_cacher::{CachedThumbnail, ThumbnailCacher, ThumbnailSize}, + thumbnailer::thumbnailer, }; -use crate::dialog::DialogKind; -use crate::large_image::{ - LargeImageManager, decode_large_image, exceeds_memory_limit, should_use_dedicated_worker, - should_use_tiling, -}; -use crate::localize::{LANGUAGE_SORTER, LOCALE}; -use crate::mime_icon::{mime_for_path, mime_icon}; -use crate::mounter::MOUNTERS; -use crate::operation::{Controller, OperationError}; -use crate::thumbnail_cacher::{CachedThumbnail, ThumbnailCacher, ThumbnailSize}; -use crate::thumbnailer::thumbnailer; -use crate::trash::{Trash, TrashExt}; -use crate::{FxOrderMap, fl, menu, mime_app, mouse_area}; +use uzers::{get_group_by_gid, get_user_by_uid}; pub const DOUBLE_CLICK_DURATION: Duration = Duration::from_millis(500); pub const HOVER_DURATION: Duration = Duration::from_millis(1600); @@ -77,11 +95,6 @@ const MAX_SEARCH_LATENCY: Duration = Duration::from_millis(20); const MAX_SEARCH_RESULTS: usize = 200; //TODO: configurable thumbnail size? const THUMBNAIL_SIZE: u32 = (ICON_SIZE_GRID as u32) * (ICON_SCALE_MAX as u32); -/// Maximum bytes of text to pass to the editor for preview; caps shaping work to avoid blocking. -/// Files larger than this get a truncated preview (first N bytes only). -const TEXT_PREVIEW_MAX_BYTES: usize = 256 * 1024; // 256 KiB -/// Maximum file size (bytes) to attempt text preview; files larger than this are skipped entirely. -const TEXT_PREVIEW_MAX_FILE_BYTES: u64 = 8 * 1000 * 1000; // 8 MiB // Thumbnail generation semaphore - limits parallel thumbnail workers // Uses 4 workers for balanced throughput and memory usage @@ -181,9 +194,7 @@ fn button_appearance( appearance.icon_color = Some(Color::from(cosmic.on_bg_component_color())); appearance.text_color = Some(Color::from(cosmic.on_bg_component_color())); if cut { - appearance.text_color = Some(Color::from( - cosmic.background(theme.transparent).component.on_disabled, - )); + appearance.text_color = Some(Color::from(cosmic.background.component.on_disabled)); } else { appearance.text_color = Some(Color::from(cosmic.on_bg_component_color())); } @@ -194,16 +205,12 @@ fn button_appearance( appearance.background = Some(Color::from(cosmic.bg_color()).into()); appearance.icon_color = Some(Color::from(cosmic.on_bg_color())); if cut { - appearance.text_color = Some(Color::from( - cosmic.background(theme.transparent).component.disabled, - )); + appearance.text_color = Some(Color::from(cosmic.background.component.disabled)); } else { appearance.text_color = Some(Color::from(cosmic.on_bg_color())); } } else if cut { - appearance.text_color = Some(Color::from( - cosmic.background(theme.transparent).component.on_disabled, - )); + appearance.text_color = Some(Color::from(cosmic.background.component.on_disabled)); } if focused && accent { appearance.outline_width = 1.0; @@ -282,7 +289,6 @@ fn button_style( pub fn folder_icon(path: &PathBuf, icon_size: u16) -> widget::icon::Handle { widget::icon::from_name(SPECIAL_DIRS.get(path).map_or("folder", |x| *x)) - .prefer_svg(true) .size(icon_size) .handle() } @@ -296,26 +302,6 @@ 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() @@ -458,12 +444,25 @@ impl<'a> FormatTime<'a> { impl Display for FormatTime<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Ok(zoned) = jiff::Zoned::try_from(self.time) else { - return Ok(()); + let datetime = chrono::DateTime::::from(self.time); + let now = chrono::Local::now(); + let icu_datetime = DateTime { + date: Date::try_new_gregorian( + datetime.year(), + datetime.month() as u8, + datetime.day() as u8, + ) + .unwrap(), + time: Time::try_new( + datetime.hour() as u8, + datetime.minute() as u8, + datetime.second() as u8, + 0, + ) + .unwrap(), }; - let now = jiff::Zoned::now(); - let icu_datetime = DateTime::convert_from(zoned.datetime()); - if zoned.date() == now.date() { + + if datetime.date_naive() == now.date_naive() { f.write_str(fl!("today").as_str())?; f.write_str(", ")?; self.time_formatter.format(&icu_datetime).fmt(f) @@ -573,12 +572,6 @@ pub fn fs_kind(_metadata: &Metadata) -> FsKind { FsKind::Local } -#[cfg(not(feature = "desktop"))] -fn get_desktop_file_display_name(_path: &Path) -> Option { - None -} - -#[cfg(feature = "desktop")] fn get_desktop_file_display_name(path: &Path) -> Option { let locales = get_languages_from_env(); let entry = match DesktopEntry::from_path(path, Some(&locales)) { @@ -592,12 +585,6 @@ fn get_desktop_file_display_name(path: &Path) -> Option { entry.name(&locales).map(|s| s.into_owned()) } -#[cfg(not(feature = "desktop"))] -fn get_desktop_file_icon(_path: &Path) -> Option { - None -} - -#[cfg(feature = "desktop")] fn get_desktop_file_icon(path: &Path) -> Option { let entry = match DesktopEntry::from_path::<&str>(path, None) { Ok(ok) => ok, @@ -617,14 +604,10 @@ fn desktop_icon_handle(icon: &str, size: u16) -> widget::icon::Handle { if icon_path.is_absolute() && icon_path.exists() { widget::icon::from_path(icon_path.to_path_buf()) } else { - widget::icon::from_name(icon) - .prefer_svg(true) - .size(size) - .handle() + widget::icon::from_name(icon).size(size).handle() } } -#[cfg(feature = "desktop")] pub fn parse_desktop_file(path: &Path) -> (Option, Option) { let locales = get_languages_from_env(); let entry = match DesktopEntry::from_path(path, Some(&locales)) { @@ -663,41 +646,6 @@ fn display_name_for_file(path: &Path, name: &str, get_from_gvfs: bool, is_deskto Item::display_name(name) } -// Whether a dir lives on a remote filesystem, according to GIO. -#[cfg(feature = "gvfs")] -fn gvfs_dir_is_remote(dir: &Path) -> bool { - static REMOTE_CACHE: LazyLock>> = - LazyLock::new(|| RwLock::new(FxHashMap::default())); - - if let Some(remote) = REMOTE_CACHE.read().unwrap().get(dir) { - return *remote; - } - - let remote = match gio::prelude::FileExt::query_filesystem_info( - &gio::File::for_path(dir), - gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE, - gio::Cancellable::NONE, - ) { - Ok(info) => info.boolean(gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE), - Err(err) => { - log::warn!( - "failed to get GIO filesystem info for {}: {}", - dir.display(), - err - ); - - //Assume remote so that the "expensive tasks" are rather skipped then actually executed. - true - } - }; - - REMOTE_CACHE - .write() - .unwrap() - .insert(dir.to_path_buf(), remote); - remote -} - #[cfg(feature = "gvfs")] pub fn item_from_gvfs_info(path: PathBuf, file_info: gio::FileInfo, sizes: IconSizes) -> Item { let file_name = file_info @@ -705,7 +653,7 @@ pub fn item_from_gvfs_info(path: PathBuf, file_info: gio::FileInfo, sizes: IconS .unwrap_or_default(); let mtime = file_info.attribute_uint64(gio::FILE_ATTRIBUTE_TIME_MODIFIED); let mut is_desktop = false; - let remote = path.parent().is_none_or(gvfs_dir_is_remote); + let remote = file_info.boolean(gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE); let is_dir = matches!(file_info.file_type(), gio::FileType::Directory); let size_opt = (!is_dir).then_some(file_info.size() as u64); @@ -719,9 +667,9 @@ pub fn item_from_gvfs_info(path: PathBuf, file_info: gio::FileInfo, sizes: IconS folder_icon(&path, sizes.list_condensed()), ) } else { - // 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(); + // 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); //TODO: clean this up, implement for trash let icon_name_opt = if mime == "application/x-desktop" { @@ -738,21 +686,28 @@ 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, - icon_handle_grid, - icon_handle_list, - icon_handle_list_condensed, + mime.clone(), + mime_icon(mime.clone(), sizes.grid()), + mime_icon(mime.clone(), sizes.list()), + mime_icon(mime, sizes.list_condensed()), ) } }; - let children_opt = None; + let mut 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); @@ -766,12 +721,8 @@ pub fn item_from_gvfs_info(path: PathBuf, file_info: gio::FileInfo, sizes: IconS mtime, size_opt, children_opt, - is_dir, }, hidden, - image_dimensions: (!remote && mime.type_() == mime::IMAGE) - .then(|| image::image_dimensions(&path).ok()) - .flatten(), location_opt: Some(Location::Path(path)), mime, icon_handle_grid, @@ -790,7 +741,6 @@ pub fn item_from_gvfs_info(path: PathBuf, file_info: gio::FileInfo, sizes: IconS overlaps_drag_rect: false, dir_size, cut: false, - checksums: ChecksumState::default(), } } @@ -808,10 +758,7 @@ 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); @@ -821,7 +768,23 @@ pub fn item_from_entry( #[cfg(feature = "gvfs")] FsKind::Gvfs => { is_gvfs = true; - path.parent().is_none_or(gvfs_dir_is_remote) + let file = gio::File::for_path(&path); + + match gio::prelude::FileExt::query_filesystem_info( + &file, + gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE, + gio::Cancellable::NONE, + ) { + Ok(info) => info.boolean(gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE), + Err(err) => { + log::warn!( + "failed to get GIO filesystem info for {}: {}", + path.display(), + err + ); + true + } + } } #[cfg(not(feature = "gvfs"))] FsKind::Gvfs => { @@ -843,9 +806,7 @@ pub fn item_from_entry( folder_icon(&path, sizes.list_condensed()), ) } else { - // 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(); + let mime = mime_for_path(&path, Some(&metadata), remote); //TODO: clean this up, implement for trash let icon_name_opt = if mime == "application/x-desktop" { is_desktop = true; @@ -861,21 +822,28 @@ 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, - icon_handle_grid, - icon_handle_list, - icon_handle_list_condensed, + mime.clone(), + mime_icon(mime.clone(), sizes.grid()), + mime_icon(mime.clone(), sizes.list()), + mime_icon(mime, sizes.list_condensed()), ) } }; - let children_opt = None; + let mut 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); @@ -890,7 +858,6 @@ pub fn item_from_entry( }, hidden, location_opt: Some(Location::Path(path)), - image_dimensions: None, mime, icon_handle_grid, icon_handle_list, @@ -904,7 +871,6 @@ pub fn item_from_entry( overlaps_drag_rect: false, dir_size, cut: false, - checksums: ChecksumState::default(), } } @@ -917,8 +883,6 @@ pub fn item_from_trash_entry( let name = entry.name.to_string_lossy().into_owned(); let display_name = Item::display_name(&name); - let location = crate::trash::trash_item_path(&entry).map(Location::Path); - let (mime, icon_handle_grid, icon_handle_list, icon_handle_list_condensed) = match metadata.size { trash::TrashItemSize::Entries(_) => ( @@ -946,10 +910,7 @@ pub fn item_from_trash_entry( is_mount_point: false, metadata: ItemMetadata::Trash { metadata, entry }, hidden: false, - location_opt: location, - image_dimensions: (mime.type_() == mime::IMAGE) - .then(|| image::image_dimensions(&original_path).ok()) - .flatten(), + location_opt: None, mime, icon_handle_grid, icon_handle_list, @@ -963,48 +924,9 @@ pub fn item_from_trash_entry( overlaps_drag_rect: false, dir_size: DirSize::NotDirectory, cut: false, - checksums: ChecksumState::default(), } } -fn item_from_trash_child( - path: PathBuf, - name: String, - metadata: fs::Metadata, - sizes: IconSizes, -) -> Option { - let Some(original_path) = crate::trash::original_path_for_trash_child(&path) else { - log::warn!( - "failed to resolve original path for trash item {}, skipping entry", - path.display() - ); - return None; - }; - let Some(original_parent) = original_path.parent() else { - log::warn!( - "trash item {} has no original parent, skipping entry", - path.display() - ); - return None; - }; - let entry = trash::TrashItem { - id: path.as_os_str().to_os_string(), - name: std::ffi::OsString::from(&name), - original_parent: original_parent.to_path_buf(), - time_deleted: 0, - }; - let size = if metadata.is_dir() { - trash::TrashItemSize::Entries(0) - } else { - trash::TrashItemSize::Bytes(metadata.len()) - }; - Some(item_from_trash_entry( - entry, - trash::TrashItemMetadata { size }, - sizes, - )) -} - fn get_filename_from_path(path: &Path) -> Result { Ok(match path.file_name() { Some(name_os) => name_os @@ -1031,10 +953,7 @@ pub fn item_from_path>(path: P, sizes: IconSizes) -> Result Vec { 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")] { @@ -1046,6 +965,7 @@ pub fn scan_path(tab_path: &PathBuf, sizes: IconSizes) -> Vec { // gio crate expects a comma delimited string let attr_string = [ gio::FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME.as_str(), + gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE.as_str(), gio::FILE_ATTRIBUTE_TIME_MODIFIED.as_str(), gio::FILE_ATTRIBUTE_STANDARD_SIZE.as_str(), gio::FILE_ATTRIBUTE_STANDARD_TYPE.as_str(), @@ -1082,7 +1002,6 @@ pub fn scan_path(tab_path: &PathBuf, sizes: IconSizes) -> Vec { if !remote_scannable { match fs::read_dir(tab_path) { Ok(entries) => { - let trash = crate::trash::is_trash_path(tab_path); items = entries .filter_map(|entry_res| { let entry = entry_res @@ -1123,11 +1042,7 @@ pub fn scan_path(tab_path: &PathBuf, sizes: IconSizes) -> Vec { }) .ok()?; - if trash { - item_from_trash_child(path, name, metadata, sizes) - } else { - Some(item_from_entry(path, name, metadata, sizes)) - } + Some(item_from_entry(path, name, metadata, sizes)) }) .collect(); } @@ -1261,7 +1176,135 @@ pub fn scan_search bool + Sync>( } } SearchLocation::Trash => { - Trash::scan_search(callback, ®ex); + trash_helpers::scan_search_trash(callback, ®ex); + } + } +} + +// This config statement is from trash::os_limited, inverted +#[cfg(not(any( + target_os = "windows", + all( + unix, + not(target_os = "macos"), + not(target_os = "ios"), + not(target_os = "android") + ) +)))] +mod trash_helpers { + use super::*; + + pub fn trash_entries() -> usize { + 0 + } + + pub fn trash_icon(icon_size: u16) -> widget::icon::Handle { + widget::icon::from_name("user-trash") + .size(icon_size) + .handle() + } + + pub fn trash_icon_symbolic(icon_size: u16) -> widget::icon::Handle { + widget::icon::from_name("user-trash-symbolic") + .size(icon_size) + .handle() + } + + pub fn scan_trash(_sizes: IconSizes) -> Vec { + log::warn!("viewing trash not supported on this platform"); + Vec::new() + } + + pub fn scan_search_trash bool + Sync>(callback: F, regex: &Regex) {} +} + +// This config statement is from trash::os_limited +#[cfg(any( + target_os = "windows", + all( + unix, + not(target_os = "macos"), + not(target_os = "ios"), + not(target_os = "android") + ) +))] +pub mod trash_helpers { + use super::*; + + pub fn trash_entries() -> usize { + match trash::os_limited::list() { + Ok(entries) => entries.len(), + Err(_err) => 0, + } + } + + pub fn trash_icon(icon_size: u16) -> widget::icon::Handle { + widget::icon::from_name(if trash::os_limited::is_empty().unwrap_or(true) { + "user-trash" + } else { + "user-trash-full" + }) + .size(icon_size) + .handle() + } + + pub fn trash_icon_symbolic(icon_size: u16) -> widget::icon::Handle { + widget::icon::from_name(if trash::os_limited::is_empty().unwrap_or(true) { + "user-trash-symbolic" + } else { + "user-trash-full-symbolic" + }) + .size(icon_size) + .handle() + } + + pub fn scan_trash(sizes: IconSizes) -> Vec { + let entries = match trash::os_limited::list() { + Ok(entry) => entry, + Err(err) => { + log::warn!("failed to read trash items: {err}"); + return Vec::new(); + } + }; + let mut items: Vec<_> = entries + .into_iter() + .filter_map(|entry| { + let metadata = trash::os_limited::metadata(&entry) + .inspect_err(|err| { + log::warn!("failed to get metadata for trash item {entry:?}: {err}") + }) + .ok()?; + Some(item_from_trash_entry(entry, metadata, sizes)) + }) + .collect(); + items.sort_by(|a, b| match (a.metadata.is_dir(), b.metadata.is_dir()) { + (true, false) => Ordering::Less, + (false, true) => Ordering::Greater, + _ => LANGUAGE_SORTER.compare(&a.display_name, &b.display_name), + }); + items + } + + pub fn scan_search_trash bool + Sync>(callback: F, regex: &Regex) { + let entries = match trash::os_limited::list() { + Ok(entries) => entries, + Err(err) => { + log::warn!("failed to read trash items: {err}"); + return; + } + }; + + for entry in entries { + if let Ok(metadata) = trash::os_limited::metadata(&entry).inspect_err(|err| { + log::warn!("failed to get metadata for trash item {entry:?}: {err}") + }) { + let name = entry.name.to_string_lossy(); + if regex.is_match(&name) { + if !callback(SearchItem::Trash(entry, metadata)) { + break; + } + } + } } } } @@ -1297,8 +1340,8 @@ pub fn scan_recents(sizes: IconSizes) -> Vec { .into_iter() .filter_map(|bookmark| { let path = uri_to_path(bookmark.href)?; - let last_edit = bookmark.modified.parse::().ok()?; - let last_visit = bookmark.visited.parse::().ok()?; + let last_edit = bookmark.modified.parse::>().ok()?; + let last_visit = bookmark.visited.parse::>().ok()?; if path.exists() { let file_name = path.file_name()?; @@ -1399,15 +1442,15 @@ pub fn scan_desktop( let display_name = Item::display_name(&name); let metadata = ItemMetadata::SimpleDir { - entries: Trash::entries() as u64, + entries: trash_helpers::trash_entries() as u64, }; let (mime, icon_handle_grid, icon_handle_list, icon_handle_list_condensed) = { ( "inode/directory".parse().unwrap(), - Trash::icon(sizes.grid()), - Trash::icon(sizes.list()), - Trash::icon(sizes.list_condensed()), + trash_helpers::trash_icon(sizes.grid()), + trash_helpers::trash_icon(sizes.list()), + trash_helpers::trash_icon(sizes.list_condensed()), ) }; @@ -1418,7 +1461,6 @@ pub fn scan_desktop( metadata, hidden: false, location_opt: Some(Location::Trash), - image_dimensions: None, mime, icon_handle_grid, icon_handle_list, @@ -1432,7 +1474,6 @@ pub fn scan_desktop( overlaps_drag_rect: false, dir_size: DirSize::NotDirectory, cut: false, - checksums: ChecksumState::default(), }); } @@ -1639,7 +1680,7 @@ impl Location { } } - pub fn scan(&self, sizes: IconSizes) -> (Option>, Vec) { + pub fn scan(&self, sizes: IconSizes) -> (Option, Vec) { let items = match self { Self::Desktop(path, display, desktop_config) => { scan_desktop(path, display, *desktop_config, sizes) @@ -1649,13 +1690,13 @@ impl Location { // Search is done incrementally Vec::new() } - Self::Trash => Trash::scan(sizes), + Self::Trash => trash_helpers::scan_trash(sizes), Self::Recents => scan_recents(sizes), Self::Network(uri, _, _) => scan_network(uri, sizes), }; let parent_item_opt = match self.path_opt() { Some(path) => match item_from_path(path, sizes) { - Ok(item) => Some(Box::new(item)), + Ok(item) => Some(item), Err(err) => { log::warn!("failed to get item for {}: {}", path.display(), err); None @@ -1757,14 +1798,13 @@ impl fmt::Debug for TaskWrapper { #[derive(Debug)] pub enum Command { Action(Action), - Surface(cosmic::surface::Action), AddNetworkDrive, AddToSidebar(PathBuf), AutoScroll(Option), ChangeLocation(String, Location, Option>), + ContextMenu(Option, Option), Delete(Vec), DropFiles(PathBuf, ClipboardPaste), - ClearRecents, EmptyTrash, #[cfg(feature = "desktop")] ExecEntryAction(cosmic::desktop::DesktopEntryData, usize), @@ -1774,10 +1814,8 @@ pub enum Command { OpenInNewWindow(PathBuf), OpenTrash, Preview(PreviewKind), - RunContextAction(usize), SetOpenWith(Mime, String), SetPermissions(PathBuf, u32), - SetMultiplePermissions(Vec<(PathBuf, u32)>), SetSort(String, HeadingOptions, bool), WindowDrag, WindowToggleMaximize, @@ -1792,9 +1830,9 @@ pub enum Message { ClickRelease(Option), Config(TabConfig), ContextAction(Action), - RightClickBackground, - Surface(cosmic::surface::Action), - LocationContextMenuIndex(Option), + ContextMenu(Option, Option), + LocationContextMenuPoint(Option), + LocationContextMenuIndex(Option, Option), LocationMenuAction(LocationMenuAction), Drag(Option), DragEnd, @@ -1804,7 +1842,6 @@ pub enum Message { EditLocationSubmit, EditLocationTab, OpenInNewTab(PathBuf), - ClearRecents, EmptyTrash, #[cfg(feature = "desktop")] ExecEntryAction(Option, usize), @@ -1816,8 +1853,6 @@ pub enum Message { GoPrevious, ItemDown, ItemLeft, - ItemPageDown, - ItemPageUp, ItemRight, ItemUp, Location(Location), @@ -1836,9 +1871,7 @@ pub enum Message { SelectFirst, SelectLast, SetOpenWith(Mime, String), - RunContextAction(usize), SetPermissions(PathBuf, u32), - ShiftPermissions(Option<(PathBuf, u32)>, u32, u32), SetSort(HeadingOptions, bool), TabComplete(PathBuf, Vec<(String, PathBuf)>), Thumbnail(PathBuf, ItemThumbnail), @@ -1854,11 +1887,6 @@ pub enum Message { HighlightDeactivate(usize), HighlightActivate(usize), DirectorySize(PathBuf, DirSize), - #[cfg(feature = "gvfs")] - DirectoryChildren(PathBuf, usize), - Checksums(PathBuf, ChecksumState), - CalculateChecksums(PathBuf), - CopyChecksum(String), ImageDecoded(PathBuf, u32, u32, Vec, Option<(u32, u32)>, u64), // path, width, height, pixels, display_size, generation } @@ -1886,23 +1914,6 @@ pub enum DirSize { Error(String), } -/// Checksums computed for a file. Only SHA256 is currently exposed; see -/// [`calculate_checksums`] for how to add more. -#[derive(Clone, Debug, Default)] -pub struct FileChecksums { - pub sha256: String, -} - -/// State of checksum computation for a file. -#[derive(Clone, Debug, Default)] -pub enum ChecksumState { - #[default] - NotCalculated, - Calculating, - Calculated(FileChecksums), - Error(String), -} - #[derive(Clone, Debug)] pub enum ItemMetadata { Path { @@ -1924,7 +1935,6 @@ pub enum ItemMetadata { mtime: u64, size_opt: Option, children_opt: Option, - is_dir: bool, }, } @@ -1939,7 +1949,7 @@ impl ItemMetadata { Self::SimpleDir { .. } => true, Self::SimpleFile { .. } => false, #[cfg(feature = "gvfs")] - Self::GvfsPath { is_dir, .. } => *is_dir, + Self::GvfsPath { children_opt, .. } => children_opt.is_some(), } } @@ -1948,7 +1958,7 @@ impl ItemMetadata { Self::Path { metadata, .. } => metadata.modified().ok(), #[cfg(feature = "gvfs")] Self::GvfsPath { mtime, .. } => { - SystemTime::UNIX_EPOCH.checked_add(Duration::from_secs(*mtime)) + Some(SystemTime::UNIX_EPOCH + Duration::from_secs(*mtime)) } _ => None, } @@ -2103,26 +2113,55 @@ impl ItemThumbnail { } tried_supported_file = true; - let dyn_img = match image::ImageReader::open(path) - .and_then(image::ImageReader::with_guessed_format) - { - Ok(mut reader) => { - let mut limits = image::Limits::default(); - let max_ram = max_mem * 1000 * 1000 / jobs as u64; - limits.max_alloc = Some(max_ram); - reader.limits(limits); - match reader.decode() { - Ok(reader) => Some(reader), + let dyn_img: Option = match mime.subtype().as_str() { + "jxl" => match File::open(path) { + Ok(file) => match JxlDecoder::new(file) { + Ok(mut decoder) => { + let mut limits = image::Limits::default(); + let max_ram = max_mem * 1000 * 1000 / jobs as u64; + limits.max_alloc = Some(max_ram); + let _ = decoder.set_limits(limits); + match image::DynamicImage::from_decoder(decoder) { + Ok(img) => Some(img), + Err(err) => { + log::warn!("failed to decode jxl {}: {}", path.display(), err); + None + } + } + } Err(err) => { - log::warn!("failed to decode {}: {}", path.display(), err); + log::warn!("failed to create jxl decoder {}: {}", path.display(), err); + None + } + }, + Err(err) => { + log::warn!("failed to open path {}: {}", path.display(), err); + None + } + }, + _ => { + match image::ImageReader::open(path) + .and_then(image::ImageReader::with_guessed_format) + { + Ok(mut reader) => { + let mut limits = image::Limits::default(); + let max_ram = max_mem * 1000 * 1000 / jobs as u64; + limits.max_alloc = Some(max_ram); + reader.limits(limits); + match reader.decode() { + Ok(reader) => Some(reader), + Err(err) => { + log::warn!("failed to decode {}: {}", path.display(), err); + None + } + } + } + Err(err) => { + log::warn!("failed to read {}: {}", path.display(), err); None } } } - Err(err) => { - log::warn!("failed to read {}: {}", path.display(), err); - None - } }; if let Some(dyn_img) = dyn_img { @@ -2192,37 +2231,17 @@ impl ItemThumbnail { log::warn!("failed to read {}: {}", path.display(), err); } } - } else if mime.type_() == mime::TEXT && check_size("text", TEXT_PREVIEW_MAX_FILE_BYTES) { - tried_supported_file = true; - if size > 0 { - // Reuse size from metadata above; cap allocation and read - let read_cap = (size.min(TEXT_PREVIEW_MAX_BYTES as u64)) as usize; - let mut buf = vec![0u8; read_cap]; - match File::open(path).and_then(|f| { - let n = Read::read(&mut f.take(read_cap as u64), &mut buf)?; - buf.truncate(n); - Ok(()) - }) { - Ok(()) => { - let text = match std::str::from_utf8(&buf) { - Ok(s) => s.to_string(), - Err(e) => { - // Use only the valid UTF-8 prefix (slice is guaranteed valid by valid_up_to()) - std::str::from_utf8(&buf[..e.valid_up_to()]) - .unwrap_or("") - .to_string() - } - }; - if !text.is_empty() { - return Self::Text(widget::text_editor::Content::with_text(&text)); - } - } - Err(err) => { - log::warn!("failed to read {}: {}", path.display(), err); - } + } else if mime.type_() == mime::TEXT && check_size("text", 8 * 1000 * 1000) { + /*TODO: fix performance issues, widget::text_editor::Content::with_text forces all text to shape, which blocks rendering + match fs::read_to_string(&path) { + Ok(data) => { + return ItemThumbnail::Text(widget::text_editor::Content::with_text(&data)); + } + Err(err) => { + log::warn!("failed to read {}: {}", path.display(), err); } } - // size == 0: empty file or unknown size; skip read and allocation + */ } // If we weren't able to create a thumbnail, but we should have @@ -2342,7 +2361,6 @@ pub struct Item { pub hidden: bool, pub location_opt: Option, pub mime: Mime, - pub image_dimensions: Option<(u32, u32)>, pub icon_handle_grid: widget::icon::Handle, pub icon_handle_list: widget::icon::Handle, pub icon_handle_list_condensed: widget::icon::Handle, @@ -2355,7 +2373,6 @@ pub struct Item { pub cut: bool, pub overlaps_drag_rect: bool, pub dir_size: DirSize, - pub checksums: ChecksumState, } impl Item { @@ -2370,7 +2387,6 @@ impl Item { ) -> widget::Text<'a, cosmic::Theme, cosmic::Renderer> { widget::text::body(name) .wrapping(text::Wrapping::WordOrGlyph) - .align_x(text::Alignment::Center) .ellipsize(text::Ellipsize::Middle(text::EllipsizeHeightLimit::Lines( 3, ))) @@ -2408,7 +2424,7 @@ impl Item { } fn preview(&self) -> Element<'_, Message> { - let spacing = cosmic::theme::spacing(); + let spacing = cosmic::theme::active().cosmic().spacing; // This loads the image only if thumbnailing worked let icon = widget::icon::icon(self.icon_handle_grid.clone()) .content_fit(ContentFit::Contain) @@ -2426,8 +2442,10 @@ impl Item { widget::image(handle.clone()).into() } ItemThumbnail::Svg(handle) => widget::svg(handle.clone()).into(), - ItemThumbnail::Text(content) => widget::text_editor::text_editor(content) - .style(text_editor_class) + ItemThumbnail::Text(content) => widget::text_editor(content) + .class(cosmic::theme::iced::TextEditor::Custom(Box::new( + text_editor_class, + ))) .width(THUMBNAIL_SIZE as f32) .height(Length::Fixed(THUMBNAIL_SIZE as f32)) .padding(spacing.space_xxs) @@ -2467,9 +2485,9 @@ impl Item { space_xxxs, space_m, .. - } = theme::spacing(); + } = theme::active().cosmic().spacing; - let mut column = widget::column::with_capacity(4).spacing(space_m); + let mut column = widget::column().spacing(space_m); column = column.push( widget::container(self.preview()) @@ -2477,32 +2495,28 @@ impl Item { .max_height(THUMBNAIL_SIZE as f32), ); - let mut details = widget::column::with_capacity(8).spacing(space_xxxs); - details = details.push(widget::selectable_text::heading(self.name.clone())); + let mut details = widget::column().spacing(space_xxxs); + details = details.push(widget::text::heading(self.name.clone())); details = details.push(widget::text::body(fl!( "type", mime = self.mime.to_string() ))); let mut settings = Vec::new(); if let Some(mime_app_cache) = mime_app_cache_opt { - let mime_apps = mime_app_cache.get_apps_for_mime(&self.mime, false); + let mime_apps = mime_app_cache.get(&self.mime); if !mime_apps.is_empty() { - let (names, icons) = mime_apps - .iter() - .map(|(app, _)| (Cow::Owned(app.name.clone()), app.icon())) - .collect::<(Vec<_>, Vec<_>)>(); settings.push( widget::settings::item::builder(fl!("open-with")).control( Element::from( widget::dropdown( - names, - mime_apps.iter().position(|(x, _)| x.is_default(&self.mime)), + mime_apps, + mime_apps.iter().position(|x| x.is_default), move |index| index, ) - .icons(Cow::Owned(icons)), + .icons(Cow::Borrowed(mime_app_cache.icons(&self.mime))), ) - .map(move |index| { - let mime_app = &mime_apps[index].0; + .map(|index| { + let mime_app = &mime_apps[index]; Message::SetOpenWith(self.mime.clone(), mime_app.id.clone()) }), ), @@ -2535,21 +2549,21 @@ impl Item { let time_formatter = time_formatter(military_time); if let Ok(time) = metadata.created() { - details = details.push(widget::selectable_text::body(fl!( + details = details.push(widget::text::body(fl!( "item-created", created = format_time(time, &date_time_formatter, &time_formatter).to_string() ))); } if let Ok(time) = metadata.modified() { - details = details.push(widget::selectable_text::body(fl!( + details = details.push(widget::text::body(fl!( "item-modified", modified = format_time(time, &date_time_formatter, &time_formatter).to_string() ))); } if let Ok(time) = metadata.accessed() { - details = details.push(widget::selectable_text::body(fl!( + details = details.push(widget::text::body(fl!( "item-accessed", accessed = format_time(time, &date_time_formatter, &time_formatter).to_string() ))); @@ -2561,7 +2575,7 @@ impl Item { let mode = metadata.mode(); - let user_name = uzers::get_user_by_uid(metadata.uid()) + let user_name = get_user_by_uid(metadata.uid()) .and_then(|user| user.name().to_str().map(ToOwned::to_owned)) .unwrap_or_default(); let user_path = path.clone(); @@ -2584,7 +2598,7 @@ impl Item { )), ); - let group_name = uzers::get_group_by_gid(metadata.gid()) + let group_name = get_group_by_gid(metadata.gid()) .and_then(|group| group.name().to_str().map(ToOwned::to_owned)) .unwrap_or_default(); let group_path = path.clone(); @@ -2631,61 +2645,13 @@ impl Item { } column = column.push(details); - if let Some(metadata) = self.file_metadata() - && !metadata.is_dir() - && let Some(path) = self.path_opt() - { - let control: Element<'_, Message> = match &self.checksums { - ChecksumState::NotCalculated => widget::button::standard(fl!("calculate")) - .on_press(Message::CalculateChecksums(path.clone())) - .into(), - ChecksumState::Calculating => widget::row::with_capacity(2) - .align_y(Alignment::Center) - .spacing(space_xxxs) - .push(widget::indeterminate_circular().size(16.0)) - .push(widget::text::body(fl!("calculating"))) - .into(), - ChecksumState::Calculated(checksums) => { - let value = checksums.sha256.clone(); - // Middle-ellipsize the digest to fit, full value on hover. - let value_text = widget::tooltip( - widget::text::body(value.clone()) - .font(cosmic::font::mono()) - .width(Length::Fill) - .wrapping(text::Wrapping::None) - .ellipsize(text::Ellipsize::Middle(text::EllipsizeHeightLimit::Lines( - 1, - ))), - widget::text::body(value.clone()), - widget::tooltip::Position::Bottom, - ); - let copy_button = widget::button::icon( - widget::icon::from_name("edit-copy-symbolic").size(16), - ) - .on_press(Message::CopyChecksum(value.clone())) - .tooltip(fl!("copy")); - widget::row::with_capacity(2) - .align_y(Alignment::Center) - .spacing(space_xxxs) - .push(value_text) - .push(copy_button) - .into() - } - ChecksumState::Error(err) => { - widget::text::body(format!("{}: {}", fl!("error"), err)).into() - } - }; - settings.push( - widget::settings::item::builder(fl!("checksum", kind = "SHA256")).control(control), - ); - } - - if let Some(path) = self.path_opt() - && self.selected - { - column = column.push( - widget::button::standard(fl!("open")).on_press(Message::Open(Some(path.clone()))), - ); + if let Some(path) = self.path_opt() { + if self.selected { + column = column.push( + widget::button::standard(fl!("open")) + .on_press(Message::Open(Some(path.clone()))), + ); + } } if !settings.is_empty() { @@ -2698,12 +2664,12 @@ impl Item { } pub fn replace_view(&self, heading: String, military_time: bool) -> Element<'_, Message> { - let cosmic_theme::Spacing { space_xxxs, .. } = theme::spacing(); + let cosmic_theme::Spacing { space_xxxs, .. } = theme::active().cosmic().spacing; - let mut row = widget::row::with_capacity(2).spacing(space_xxxs); + let mut row = widget::row().spacing(space_xxxs); row = row.push(self.preview()); - let mut column = widget::column::with_capacity(3).spacing(space_xxxs); + let mut column = widget::column().spacing(space_xxxs); column = column.push(widget::text::heading(heading)); //TODO: translate! @@ -2820,8 +2786,9 @@ pub struct Tab { pub location: Location, pub location_ancestors: Vec<(Location, String)>, pub location_title: String, - /// Breadcrumb whose context menu is open, drawn as active while it shows + pub location_context_menu_point: Option, pub location_context_menu_index: Option, + pub context_menu: Option, pub mode: Mode, pub scroll_opt: Option, pub size_opt: Cell>, @@ -2837,7 +2804,7 @@ pub struct Tab { pub sort_name: HeadingOptions, pub sort_direction: bool, pub gallery: bool, - pub(crate) parent_item_opt: Option>, + pub(crate) parent_item_opt: Option, pub(crate) items_opt: Option>, pub dnd_hovered: Option<(Location, Instant)>, pub(crate) scrollable_id: widget::Id, @@ -2849,6 +2816,7 @@ pub struct Tab { search_context: Option, date_time_formatter: DateTimeFormatter, time_formatter: DateTimeFormatter, + watch_drag: bool, window_id: Option, large_image_manager: LargeImageManager, } @@ -2875,32 +2843,6 @@ async fn calculate_dir_size(path: &Path, controller: Controller) -> Result Result { - let path = path.to_path_buf(); - tokio::task::spawn_blocking(move || { - let mut file = File::open(&path).map_err(|e| e.to_string())?; - let mut sha256_hasher = Sha256::new(); - - let mut buffer = [0u8; 8192]; - loop { - let bytes_read = file.read(&mut buffer).map_err(|e| e.to_string())?; - if bytes_read == 0 { - break; - } - sha256_hasher.update(&buffer[..bytes_read]); - } - - Ok(FileChecksums { - sha256: format!("{:x}", sha256_hasher.finalize()), - }) - }) - .await - .map_err(|e| e.to_string())? -} - fn folder_name>(path: P) -> (String, bool) { let path = path.as_ref(); let mut found_home = false; @@ -2965,6 +2907,8 @@ impl Tab { location, location_ancestors, location_title, + context_menu: None, + location_context_menu_point: None, location_context_menu_index: None, mode: Mode::App, scroll_opt: None, @@ -2993,6 +2937,7 @@ impl Tab { search_context: None, date_time_formatter: date_time_formatter(config.military_time), time_formatter: time_formatter(config.military_time), + watch_drag: true, window_id, large_image_manager: LargeImageManager::new(), } @@ -3115,7 +3060,7 @@ impl Tab { /// Returns true if an item was selected. pub fn select_by_prefix(&mut self, prefix: &str) -> bool { let prefix_lower = prefix.to_lowercase(); - let focus = self.select_focus.take(); + self.select_focus = None; if let Some(ref mut items) = self.items_opt { // First, deselect all items @@ -3123,104 +3068,18 @@ impl Tab { item.selected = false; } - // Determine the start index of the search. When the index is before the currently focused item, it will be - // considered first, otherwise last. Consider the focused item last when only a single character has been - // typed, so we eagerly switch focus on the first character and stay on the same item as long as the prefix - // matches. - let single_char = prefix_lower.chars().count() == 1; - let start = if single_char { - Self::index_after_focus(focus, self.sort_direction) - } else { - Self::index_before_focus(focus, self.sort_direction) - }; - self.select_focus = Self::select_first_prefix_from_index( - &prefix_lower, - items, - start, - self.sort_direction, - ); - - if self.select_focus.is_some() || single_char { - return self.select_focus.is_some(); + // Find first matching item + for (i, item) in items.iter_mut().enumerate() { + if item.name.to_lowercase().starts_with(&prefix_lower) { + item.selected = true; + self.select_focus = Some(i); + return true; + } } - - let mut chars = prefix_lower.chars(); - let Some(first) = chars.next() else { - log::error!("search term is empty"); - return self.select_focus.is_some(); - }; - - // Check if all entered characters are the same - if !chars.all(|c| c == first) { - return self.select_focus.is_some(); - } - - // Search for a single character when all entered characters are the same. - // This allows cycling through items starting with the same character by repeatedly pressing a key. - let start = Self::index_after_focus(focus, self.sort_direction); - self.select_focus = Self::select_first_prefix_from_index( - &first.to_string(), - items, - start, - self.sort_direction, - ); - - return self.select_focus.is_some(); } false } - fn index_before_focus(current_focus: Option, forward: bool) -> usize { - current_focus.map_or(0, |i| if forward { i } else { i + 1 }) - } - - fn index_after_focus(current_focus: Option, forward: bool) -> usize { - current_focus.map_or(0, |i| if forward { i + 1 } else { i }) - } - - fn select_first_prefix_from_index( - prefix_lower: &str, - items: &mut [Item], - start: usize, - forward: bool, - ) -> Option { - // Order the search item so they begin at `start`. - let Some((until, after)) = items.split_at_mut_checked(start) else { - log::error!( - "invalid start index {start} for items of length {}", - items.len() - ); - return None; - }; - let search_items = after - .iter_mut() - .enumerate() - .map(|(i, item)| (i + start, item)) - .chain(until.iter_mut().enumerate()); - - if forward { - Self::select_first_prefix_match(prefix_lower, search_items) - } else { - Self::select_first_prefix_match(prefix_lower, search_items.rev()) - } - } - - /// Selects the first item in the given iterator whose name starts with the given prefix. - /// - /// The `prefix` must be lowercase. - fn select_first_prefix_match<'a>( - prefix: &str, - items: impl Iterator, - ) -> Option { - for (i, item) in items { - if item.name.to_lowercase().starts_with(prefix) { - item.selected = true; - return Some(i); - } - } - None - } - pub fn select_paths(&mut self, paths: Vec) { self.select_focus = None; if let Some(ref mut items) = self.items_opt { @@ -3317,14 +3176,6 @@ impl Tab { item.pos_opt.get() } - fn dehighlight_all(&mut self) { - if let Some(items) = self.items_opt.as_mut() { - for item in items.iter_mut() { - item.highlighted = false; - } - } - } - pub(crate) fn select_focus_scroll(&mut self) -> Option { let items = self.items_opt.as_ref()?; let item = items.get(self.select_focus?)?; @@ -3360,20 +3211,6 @@ impl Tab { } } - fn rows_per_page(&self) -> usize { - let viewport_height = self.item_view_size_opt.get().map_or(0.0, |s| s.height); - let row_height = self - .select_focus - .and_then(|i| self.items_opt.as_ref()?.get(i)) - .and_then(|item| item.rect_opt.get()) - .map_or(0.0, |r| r.height); - if row_height > 0.0 && viewport_height > 0.0 { - (viewport_height / row_height).floor() as usize - } else { - 1 - } - } - fn select_range_start_pos_opt(&self) -> Option<(usize, usize)> { let items = self.items_opt.as_ref()?; let item = items.get(self.select_range.map(|r| r.0)?)?; @@ -3505,6 +3342,7 @@ impl Tab { self.location = location.normalize(); self.location_ancestors = self.location.ancestors(); self.location_title = self.location.title(); + self.context_menu = None; self.edit_location = None; self.items_opt = None; //TODO: remember scroll by location? @@ -3549,6 +3387,7 @@ impl Tab { let mut history_i_opt = None; let mod_ctrl = modifiers.contains(Modifiers::CTRL) && self.mode.multiple(); let mod_shift = modifiers.contains(Modifiers::SHIFT) && self.mode.multiple(); + let last_context_menu = self.context_menu; match message { Message::AddNetworkDrive => { commands.push(Command::AddNetworkDrive); @@ -3583,6 +3422,8 @@ impl Tab { } if click_i_opt != self.clicked.take() { + self.context_menu = None; + self.location_context_menu_index = None; if let Some(ref mut items) = self.items_opt { for (i, item) in items.iter_mut().enumerate() { if mod_ctrl { @@ -3599,6 +3440,7 @@ impl Tab { } Message::DragEnd => { self.clicked = None; + self.watch_drag = true; } Message::DoubleClick(click_i_opt) => { if let Some(clicked_item) = self @@ -3623,7 +3465,9 @@ impl Tab { } Message::Click(click_i_opt) => { self.selected_clicked = false; + self.context_menu = None; self.edit_location = None; + self.location_context_menu_index = None; if click_i_opt.is_none() { self.clicked = click_i_opt; } @@ -3765,16 +3609,19 @@ impl Tab { } } Message::ContextAction(action) => { + // Close context menu + self.context_menu = None; + commands.push(Command::Action(action)); } - Message::RunContextAction(action) => { - commands.push(Command::RunContextAction(action)); - } - Message::RightClickBackground => { + Message::ContextMenu(point_opt, _) => { self.edit_location = None; + self.context_menu = point_opt; + self.location_context_menu_index = None; //TODO: hack for clearing selecting when right clicking empty space - if self.last_right_click.take().is_none() + if self.context_menu.is_some() + && self.last_right_click.take().is_none() && let Some(ref mut items) = self.items_opt { for item in items.iter_mut() { @@ -3782,13 +3629,17 @@ impl Tab { } } } - Message::Surface(action) => { - commands.push(Command::Surface(action)); + Message::LocationContextMenuPoint(point_opt) => { + self.context_menu = None; + self.location_context_menu_point = point_opt; } - Message::LocationContextMenuIndex(index) => { - self.location_context_menu_index = index; + Message::LocationContextMenuIndex(p, index_opt) => { + self.context_menu = None; + self.location_context_menu_point = p; + self.location_context_menu_index = index_opt; } Message::LocationMenuAction(action) => { + self.location_context_menu_index = None; let path_for_index = |ancestor_index| { self.location .path_opt() @@ -3812,7 +3663,7 @@ impl Tab { match item_from_path(&path, IconSizes::default()) { Ok(item) => { commands.push(Command::Preview(PreviewKind::Custom( - PreviewItem(Box::new(item)), + PreviewItem(item), ))); } Err(err) => { @@ -3838,7 +3689,10 @@ impl Tab { } } Message::Drag(rect_opt) => { + self.watch_drag = false; if let Some(rect) = rect_opt { + self.context_menu = None; + self.location_context_menu_index = None; if self.mode.multiple() { self.select_rect(rect, mod_ctrl, mod_shift); } @@ -3874,28 +3728,6 @@ impl Tab { } Message::EditLocationSubmit => { if let Some(mut edit_location) = self.edit_location.take() { - let typed_opt = match &edit_location.location { - Location::Path(path) => path.to_str().map(str::to_string), - Location::Network(uri, ..) => Some(uri.clone()), - _ => None, - }; - let mut typed_uri = false; - if let Some(typed) = typed_opt { - match typed.trim().parse::() { - Ok(url) if url.scheme() != "file" && url.has_host() => { - let uri = url.as_str().to_string(); - edit_location = - Location::Network(uri.clone(), uri, None).normalize().into(); - typed_uri = true; - } - Err(_) if matches!(edit_location.location, Location::Network(..)) => { - edit_location = - Location::Path(PathBuf::from(typed)).normalize().into(); - } - _ => {} - } - } - // Select first completion if current location does not exist if edit_location.selected.is_none() && edit_location @@ -3911,9 +3743,6 @@ impl Tab { } cd = edit_location.resolve(); - if cd.is_none() && typed_uri { - cd = Some(edit_location.location); - } } } Message::EditLocationTab => { @@ -3924,9 +3753,6 @@ impl Tab { Message::OpenInNewTab(path) => { commands.push(Command::OpenInNewTab(path)); } - Message::ClearRecents => { - commands.push(Command::ClearRecents); - } Message::EmptyTrash => { commands.push(Command::EmptyTrash); } @@ -4032,7 +3858,6 @@ impl Tab { } } Message::ItemDown => { - self.dehighlight_all(); if let Some(edit_location) = &mut self.edit_location { edit_location.select(true); } else if self.gallery { @@ -4074,95 +3899,7 @@ impl Tab { } } } - Message::ItemPageDown => { - self.dehighlight_all(); - if let Some((row, col)) = self.select_focus_pos_opt().or(self.select_last_pos_opt()) - { - if self.select_focus.is_none() { - self.select_position(row, col, mod_shift); - } - - let rows_per_page = self.rows_per_page().max(1); - let target_row = row.saturating_add(rows_per_page); - - if !self.select_position(target_row, col, mod_shift) { - // Fall back to the last item at or before target_row - let best = self.items_opt.as_ref().and_then(|items| { - items - .iter() - .filter_map(|item| item.pos_opt.get()) - .filter(|(r, _)| *r <= target_row) - .max() - }); - if let Some((best_row, best_col)) = best { - self.select_position(best_row, best_col, mod_shift); - } - } - } else { - self.select_position(0, 0, mod_shift); - } - if let Some(offset) = self.select_focus_scroll() { - commands.push(Command::Iced( - scrollable::scroll_to( - self.scrollable_id.clone(), - AbsoluteOffset { - x: Some(offset.x), - y: Some(offset.y), - }, - ) - .into(), - )); - } - if let Some(id) = self.select_focus_id() { - commands.push(Command::Iced(widget::button::focus(id).into())); - } - } - Message::ItemPageUp => { - self.dehighlight_all(); - if let Some((row, col)) = - self.select_focus_pos_opt().or(self.select_first_pos_opt()) - { - if self.select_focus.is_none() { - self.select_position(row, col, mod_shift); - } - - let rows_per_page = self.rows_per_page().max(1); - let target_row = row.saturating_sub(rows_per_page); - - if !self.select_position(target_row, col, mod_shift) { - // Fall back to the first item at or after target_row - let best = self.items_opt.as_ref().and_then(|items| { - items - .iter() - .filter_map(|item| item.pos_opt.get()) - .filter(|(r, _)| *r >= target_row) - .min() - }); - if let Some((best_row, best_col)) = best { - self.select_position(best_row, best_col, mod_shift); - } - } - } else { - self.select_position(0, 0, mod_shift); - } - if let Some(offset) = self.select_focus_scroll() { - commands.push(Command::Iced( - scrollable::scroll_to( - self.scrollable_id.clone(), - AbsoluteOffset { - x: Some(offset.x), - y: Some(offset.y), - }, - ) - .into(), - )); - } - if let Some(id) = self.select_focus_id() { - commands.push(Command::Iced(widget::button::focus(id).into())); - } - } Message::ItemLeft => { - self.dehighlight_all(); if self.gallery { commands.append(&mut self.update(Message::GalleryPrevious, modifiers)); } else { @@ -4221,7 +3958,6 @@ impl Tab { } } Message::ItemRight => { - self.dehighlight_all(); if self.gallery { commands.append(&mut self.update(Message::GalleryNext, modifiers)); } else { @@ -4263,7 +3999,6 @@ impl Tab { } } Message::ItemUp => { - self.dehighlight_all(); if let Some(edit_location) = &mut self.edit_location { edit_location.select(false); } else if self.gallery { @@ -4475,11 +4210,13 @@ impl Tab { } } Message::HighlightDeactivate(i) => { + self.watch_drag = true; if let Some(item) = self.items_opt.as_mut().and_then(|f| f.get_mut(i)) { item.highlighted = false; } } Message::HighlightActivate(i) => { + self.watch_drag = true; if let Some(item) = self.items_opt.as_mut().and_then(|f| f.get_mut(i)) { item.highlighted = true; } @@ -4505,6 +4242,7 @@ impl Tab { } Message::Scroll(viewport) => { self.scroll_opt = Some(viewport.absolute_offset()); + self.watch_drag = true; } Message::ScrollTab(scroll_speed) => { commands.push(Command::Iced( @@ -4647,28 +4385,6 @@ impl Tab { Message::SetPermissions(path, mode) => { commands.push(Command::SetPermissions(path, mode)); } - Message::ShiftPermissions(path_mode_opt, shift, bits) => match path_mode_opt { - Some((path, mode)) => commands.push(Command::SetPermissions( - path, - set_mode_part(mode, shift, bits), - )), - // Shift permissions on all selected items - None => { - let mut permissions = Vec::new(); - for item in self.items_opt().map_or(Vec::new(), |items| { - items.iter().filter(|item| item.selected).collect() - }) { - #[cfg(unix)] - if let (Some(path), Some(mode)) = ( - item.path_opt(), - item.file_metadata().map(|metadata| metadata.mode()), - ) { - permissions.push((path.clone(), set_mode_part(mode, shift, bits))); - } - } - commands.push(Command::SetMultiplePermissions(permissions)); - } - }, Message::SetSort(heading_option, dir) => { if !matches!(self.location, Location::Search(..)) { self.sort_name = heading_option; @@ -4837,66 +4553,6 @@ impl Tab { } } } - #[cfg(feature = "gvfs")] - Message::DirectoryChildren(path, children) => { - if let Some(ref mut items) = self.items_opt { - for item in items.iter_mut() { - if item.path_opt() == Some(&path) { - if let ItemMetadata::GvfsPath { children_opt, .. } = &mut item.metadata - { - *children_opt = Some(children); - } - break; - } - } - } - } - Message::Checksums(path, checksum_state) => { - let location = Location::Path(path); - if let Some(ref mut item) = self.parent_item_opt - && item.location_opt.as_ref() == Some(&location) - { - item.checksums = checksum_state.clone(); - } - if let Some(ref mut items) = self.items_opt { - for item in items.iter_mut() { - if item.location_opt.as_ref() == Some(&location) { - item.checksums = checksum_state; - break; - } - } - } - } - Message::CalculateChecksums(path) => { - let location = Location::Path(path.clone()); - if let Some(ref mut item) = self.parent_item_opt - && item.location_opt.as_ref() == Some(&location) - { - item.checksums = ChecksumState::Calculating; - } - if let Some(ref mut items) = self.items_opt { - for item in items.iter_mut() { - if item.location_opt.as_ref() == Some(&location) { - item.checksums = ChecksumState::Calculating; - break; - } - } - } - commands.push(Command::Iced( - cosmic::Task::future(async move { - match calculate_checksums(&path).await { - Ok(checksums) => { - Message::Checksums(path, ChecksumState::Calculated(checksums)) - } - Err(err) => Message::Checksums(path, ChecksumState::Error(err)), - } - }) - .into(), - )); - } - Message::CopyChecksum(value) => { - commands.push(Command::Iced(cosmic::iced::clipboard::write(value).into())); - } } // Scroll to top if needed @@ -4957,6 +4613,16 @@ impl Tab { } } + // Update context menu popup + if self.context_menu != last_context_menu { + if last_context_menu.is_some() { + commands.push(Command::ContextMenu(None, self.window_id)); + } + if let Some(point) = self.context_menu { + commands.push(Command::ContextMenu(Some(point), self.window_id)); + } + } + commands } @@ -5002,15 +4668,11 @@ impl Tab { ItemMetadata::GvfsPath { size_opt, children_opt, - is_dir, .. - } => { - if *is_dir { - (true, children_opt.unwrap_or_default() as u64) - } else { - (false, size_opt.unwrap_or_default()) - } - } + } => match children_opt { + Some(child_count) => (true, *child_count as u64), + None => (false, size_opt.unwrap_or_default()), + }, }; let (a_is_entry, a_size) = get_size(a.1); let (b_is_entry, b_size) = get_size(b.1); @@ -5133,7 +4795,7 @@ impl Tab { space_xs, space_m, .. - } = theme::spacing(); + } = theme::active().cosmic().spacing; //TODO: display error messages when image not found? let mut name_opt = None; @@ -5189,14 +4851,14 @@ impl Tab { let content: cosmic::Element<'_, Message> = if let Some(error_msg) = error_msg_opt { - widget::column::with_capacity(2) + widget::column() .push(widget::image(image_handle)) .push(widget::text(format!("⚠ {}", error_msg)).size(13)) .padding(space_xs) .align_x(cosmic::iced::Alignment::Center) .into() } else if is_loading { - widget::column::with_capacity(2) + widget::column() .push(widget::image(image_handle)) .push(widget::text("Loading higher resolution...").size(14)) .padding(space_xs) @@ -5219,11 +4881,9 @@ impl Tab { } ItemThumbnail::Text(text) => { element_opt = Some( - widget::container( - widget::text_editor::text_editor(text) - .padding(space_xxs) - .style(text_editor_class), - ) + widget::container(widget::text_editor(text).padding(space_xxs).class( + cosmic::theme::iced::TextEditor::Custom(Box::new(text_editor_class)), + )) .center(Length::Fill) .into(), ); @@ -5333,7 +4993,7 @@ impl Tab { space_s, space_m, .. - } = theme::spacing(); + } = theme::active().cosmic().spacing; let size = self.size_opt.get().unwrap_or(Size::new(0.0, 0.0)); @@ -5414,6 +5074,21 @@ impl Tab { .height(Length::Fixed((space_m + 4).into())) .padding([0, space_xxs]); + let dialog_mode = matches!(self.mode, Mode::Dialog(_)); + let heading_row_compact = widget::row::with_children([ + heading_item(fl!("name"), Length::Fill, HeadingOptions::Name), + if self.location.is_trash() { + heading_item(fl!("trashed-on"), Length::Shrink, HeadingOptions::TrashedOn) + } else { + heading_item(fl!("modified"), Length::Shrink, HeadingOptions::Modified) + }, + heading_item(fl!("size"), Length::Shrink, HeadingOptions::Size), + ]) + .align_y(Alignment::Center) + .spacing(space_s) + .height(Length::Fixed((space_m + 4).into())) + .padding([0, space_xxs]); + let accent_rule = rule::horizontal(1).class(theme::Rule::Custom(Box::new(|theme| rule::Style { color: theme.cosmic().accent_color().into(), @@ -5498,9 +5173,14 @@ impl Tab { let mut column = widget::column::with_capacity(4).padding([0, space_s]); column = column.push(row); column = column.push(accent_rule); - if self.config.view == View::List && !condensed { - column = column.push(heading_row); - column = column.push(heading_rule); + if self.config.view == View::List { + if !condensed { + column = column.push(heading_row); + column = column.push(heading_rule); + } else if dialog_mode { + column = column.push(heading_row_compact); + column = column.push(heading_rule); + } } return column.into(); } @@ -5567,14 +5247,10 @@ impl Tab { } let location = self.location.with_path(ancestor.to_path_buf()); - let mouse_area = crate::mouse_area::MouseArea::new( + let mut mouse_area = crate::mouse_area::MouseArea::new( widget::button::custom(row) .padding(space_xxxs) - .class(if self.location_context_menu_index == Some(index) { - theme::Button::LinkActive - } else { - theme::Button::Link - }) + .class(theme::Button::Link) .on_press(if ancestor == path { Message::EditLocation(Some(self.location.clone().into())) } else { @@ -5582,6 +5258,21 @@ impl Tab { }), ); + if self.location_context_menu_index.is_some() { + mouse_area = mouse_area + .on_right_press(move |point_opt| { + Message::LocationContextMenuIndex(point_opt, None) + }) + .wayland_on_right_press_window_position(); + } else { + mouse_area = mouse_area + .on_right_press_no_capture() + .on_right_press(move |point_opt| { + Message::LocationContextMenuIndex(point_opt, Some(index)) + }) + .wayland_on_right_press_window_position(); + } + let mouse_area = if let Location::Path(_) = &self.location { mouse_area .on_middle_press(move |_| Message::OpenInNewTab(ancestor.to_path_buf())) @@ -5589,16 +5280,7 @@ impl Tab { mouse_area }; - // Each breadcrumb carries the menu for its own ancestor index - let mut context_menu = - widget::context_menu(mouse_area, Some(menu::location_context_menu(index))) - .on_open(Message::LocationContextMenuIndex(Some(index))) - .on_close(Message::LocationContextMenuIndex(None)) - .on_surface_action(Message::Surface); - if let Some(window_id) = self.window_id { - context_menu = context_menu.window_id(window_id); - } - children.push(self.dnd_dest(&location, context_menu)); + children.push(self.dnd_dest(&location, mouse_area)); if found_home || overflow { break; @@ -5644,16 +5326,34 @@ impl Tab { column = column.push(row); column = column.push(accent_rule); - if self.config.view == View::List && !condensed { - column = column.push(heading_row); - column = column.push(heading_rule); + if self.config.view == View::List { + if !condensed { + column = column.push(heading_row); + column = column.push(heading_rule); + } else if dialog_mode { + column = column.push(heading_row_compact); + column = column.push(heading_rule); + } } - column.into() + let mouse_area = crate::mouse_area::MouseArea::new(column) + .on_right_press(Message::LocationContextMenuPoint); + + let mut popover = widget::popover(mouse_area); + if let (Some(point), Some(index)) = ( + self.location_context_menu_point, + self.location_context_menu_index, + ) { + popover = popover + .popup(menu::location_context_menu(index)) + .position(widget::popover::Position::Point(point)); + } + + popover.into() } pub fn empty_view(&self, has_hidden: bool) -> Element<'_, Message> { - let cosmic_theme::Spacing { space_xxs, .. } = theme::spacing(); + let cosmic_theme::Spacing { space_xxs, .. } = theme::active().cosmic().spacing; mouse_area::MouseArea::new(widget::column::with_children([widget::container( match self.mode { @@ -5671,7 +5371,7 @@ impl Tab { }) .into(), ]), - Mode::Desktop => widget::column::with_capacity(0), + Mode::Desktop => widget::column(), } .align_x(Alignment::Center) .spacing(space_xxs), @@ -5693,7 +5393,7 @@ impl Tab { space_xxs, space_xxxs, .. - } = theme::spacing(); + } = theme::active().cosmic().spacing; let TabConfig { show_hidden, @@ -5803,7 +5503,8 @@ impl Tab { widget::button::custom( widget::icon::icon(item.icon_handle_grid.clone()) .content_fit(ContentFit::Contain) - .size(icon_sizes.grid()), + .size(icon_sizes.grid()) + .width(Length::Shrink), ) .padding(space_xxxs) .class(button_style( @@ -5838,13 +5539,18 @@ impl Tab { .height(Length::Fixed(item_height as f32)) .width(Length::Fixed(item_width as f32)); for button in buttons { - column = column.push( - mouse_area::MouseArea::new(button) - .on_right_press_no_capture() - .on_right_press(move |point_opt| { - Message::RightClick(point_opt, Some(i)) - }), - ); + if self.context_menu.is_some() { + column = column.push(button); + } else { + column = column.push( + mouse_area::MouseArea::new(button) + .on_right_press_no_capture() + .wayland_on_right_press_window_position() + .on_right_press(move |point_opt| { + Message::RightClick(point_opt, Some(i)) + }), + ); + } } let column: Element = @@ -5873,7 +5579,7 @@ impl Tab { // Add a spacer if the row is empty, so scroll works if grid_elements[row].is_empty() { grid_elements[row].push(Element::from( - widget::column::with_capacity(0) + widget::column() .width(Length::Fill) .height(Length::Fixed(item_height as f32)), )); @@ -6011,13 +5717,15 @@ impl Tab { Element::from(dnd_grid) }); - let mouse_area = mouse_area::MouseArea::new(column.width(Length::Fill)) + let mut mouse_area = mouse_area::MouseArea::new(column.width(Length::Fill)) .on_press(|_| Message::Click(None)) .on_auto_scroll(Message::AutoScroll) - .on_drag(move |rect_opt| self.on_drag(rect_opt)) .on_drag_end(|_| Message::DragEnd) .show_drag_rect(self.mode.multiple()) .on_release(|_| Message::ClickRelease(None)); + if self.watch_drag { + mouse_area = mouse_area.on_drag(Message::Drag); + } (drag_list, mouse_area.into(), true) } @@ -6031,7 +5739,7 @@ impl Tab { ) { let cosmic_theme::Spacing { space_s, space_xxs, .. - } = theme::spacing(); + } = theme::active().cosmic().spacing; let TabConfig { show_hidden, @@ -6166,21 +5874,17 @@ impl Tab { ItemMetadata::GvfsPath { size_opt, children_opt, - is_dir, .. - } => { - if *is_dir { - // Children are not counted on remote filesystems - match children_opt { - //TODO: translate - Some(1) => "1 item".to_string(), - Some(child_count) => format!("{child_count} items"), - None => String::new(), + } => match children_opt { + Some(child_count) => { + if *child_count == 1 { + format!("{child_count} item") + } else { + format!("{child_count} items") } - } else { - format_size(size_opt.unwrap_or_default()) } - } + None => format_size(size_opt.unwrap_or_default()), + }, }; let row = if condensed { @@ -6247,42 +5951,47 @@ impl Tab { .spacing(space_xxs) }; - let button = - |row| { - let mouse_area = crate::mouse_area::MouseArea::new( - widget::button::custom(row) - .width(Length::Fill) - .id(item.button_id.clone()) - .padding([0, space_xxs]) - .class(button_style( - item.selected, - item.highlighted, - item.cut, - true, - true, - false, - )), - ) - .on_press(move |_| Message::Click(Some(i))) - .on_double_click(move |_| Message::DoubleClick(Some(i))) - .on_release(move |_| Message::ClickRelease(Some(i))) - .on_middle_press(move |_| Message::MiddleClick(i)) - .on_enter(move || Message::HighlightActivate(i)) - .on_exit(move || Message::HighlightDeactivate(i)); + let button = |row| { + let mouse_area = crate::mouse_area::MouseArea::new( + widget::button::custom(row) + .width(Length::Fill) + .id(item.button_id.clone()) + .padding([0, space_xxs]) + .class(button_style( + item.selected, + item.highlighted, + item.cut, + true, + true, + false, + )), + ) + .on_press(move |_| Message::Click(Some(i))) + .on_double_click(move |_| Message::DoubleClick(Some(i))) + .on_release(move |_| Message::ClickRelease(Some(i))) + .on_middle_press(move |_| Message::MiddleClick(i)) + .on_enter(move || Message::HighlightActivate(i)) + .on_exit(move || Message::HighlightDeactivate(i)); - mouse_area.on_right_press_no_capture().on_right_press( - move |point_opt| Message::RightClick(point_opt, Some(i)), - ) - }; + if self.context_menu.is_some() { + mouse_area + } else { + mouse_area + .on_right_press_no_capture() + .wayland_on_right_press_window_position() + .on_right_press(move |point_opt| { + Message::RightClick(point_opt, Some(i)) + }) + } + }; let button_row = button(row.into()); - let button_row: Element<_> = if item.metadata.is_dir() - && let Some(location) = item.location_opt.as_ref() - { - self.dnd_dest(location, button_row) - } else { - button_row.into() - }; + let button_row: Element<_> = + if item.metadata.is_dir() && item.location_opt.is_some() { + self.dnd_dest(item.location_opt.as_ref().unwrap(), button_row) + } else { + button_row.into() + }; if item.selected || !drag_items.is_empty() { let dnd_row = if !item.selected { @@ -6365,7 +6074,7 @@ impl Tab { button_row } else { - widget::column::with_capacity(0) + widget::column() .width(Length::Fill) .height(Length::Fixed(f32::from(row_height))) .into() @@ -6401,14 +6110,16 @@ impl Tab { let drag_col = (!drag_items.is_empty()) .then(|| Element::from(widget::column::with_children(drag_items))); - let mouse_area = mouse_area::MouseArea::new(column.padding([0, space_s])) + let mut mouse_area = mouse_area::MouseArea::new(column.padding([0, space_s])) .with_id(Id::new("list-view")) .on_press(|_| Message::Click(None)) .on_auto_scroll(Message::AutoScroll) - .on_drag(move |rect_opt| self.on_drag(rect_opt)) .on_drag_end(|_| Message::DragEnd) .show_drag_rect(self.mode.multiple()) .on_release(|_| Message::ClickRelease(None)); + if self.watch_drag { + mouse_area = mouse_area.on_drag(Message::Drag); + } (drag_col, mouse_area.into(), true) } @@ -6419,7 +6130,6 @@ impl Tab { modifiers: &'a Modifiers, size: Size, clipboard_paste_available: bool, - context_actions: &'a [ContextActionPreset], ) -> Element<'a, Message> { // Update cached size self.size_opt.set(Some(size)); @@ -6429,7 +6139,7 @@ impl Tab { space_xxs, space_xs, .. - } = theme::spacing(); + } = theme::active().cosmic().spacing; let location_view_opt = if matches!(self.mode, Mode::Desktop) { None @@ -6495,47 +6205,46 @@ impl Tab { .on_back_press(move |_point_opt| Message::GoPrevious) .on_forward_press(move |_point_opt| Message::GoNext) .on_scroll(|delta| respond_to_scroll_direction(delta, modifiers)) - .on_right_press(|_| Message::RightClickBackground); + .on_right_press(move |p| { + Message::ContextMenu( + if self.context_menu.is_some() { None } else { p }, + self.window_id, + ) + }) + .wayland_on_right_press_window_position(); - let items_area: Element<'_, Message> = if can_scroll { - // FIXME: new responsive widget will remove the state from the scrollable - // id_container with custom id forces the state to be extracted in a diff - // pre-processing step - widget::id_container( - widget::scrollable(mouse_area) - .id(self.scrollable_id.clone()) - .on_scroll(Message::Scroll) - .width(Length::Fill) - .height(Length::Fill), - widget::Id::new(format!("{}-scrollable", self.scrollable_id)), - ) - .into() - } else { - mouse_area.into() - }; - - // Wrap the scrollable, not its content, so the popup anchors in window coordinates - let mut context_menu = widget::context_menu( - items_area, - Some(menu::context_menu( - self, - key_binds, - modifiers, - clipboard_paste_available, - context_actions, - )), - ) - .item_width(cosmic::widget::menu::ItemWidth::Uniform(360)) - .on_surface_action(Message::Surface); - if let Some(window_id) = self.window_id { - context_menu = context_menu.window_id(window_id); + let mut popover = widget::popover(mouse_area); + if let Some(point) = self.context_menu + && (!cfg!(feature = "wayland") || !crate::is_wayland()) + { + let context_menu = + menu::context_menu(self, key_binds, modifiers, clipboard_paste_available); + popover = popover + .popup(context_menu) + .position(widget::popover::Position::Point(point)); } let mut tab_column = widget::column::with_capacity(3); if let Some(location_view) = location_view_opt { tab_column = tab_column.push(location_view); } - tab_column = tab_column.push(context_menu); + if can_scroll { + tab_column = tab_column.push( + // FIXME: new responsive widget will remove the state from the scrollable + // id_container with custom id forces the state to be extracted in a diff + // pre-processing step + widget::id_container( + widget::scrollable(popover) + .id(self.scrollable_id.clone()) + .on_scroll(Message::Scroll) + .width(Length::Fill) + .height(Length::Fill), + widget::Id::new(format!("{}-scrollable", self.scrollable_id)), + ), + ); + } else { + tab_column = tab_column.push(popover); + } match &self.location { Location::Trash | Location::Search(SearchLocation::Trash, ..) => { if let Some(items) = self.items_opt() @@ -6555,24 +6264,6 @@ impl Tab { ); } } - Location::Recents | Location::Search(SearchLocation::Recents, ..) => { - if let Some(items) = self.items_opt() - && !items.is_empty() - { - tab_column = tab_column.push( - widget::layer_container(widget::row::with_children([ - widget::space::horizontal().into(), - widget::button::standard(fl!("clear-recents-history")) - .on_press(Message::ClearRecents) - .into(), - ])) - .padding([space_xxs, space_xs]) - .layer(cosmic_theme::Layer::Primary) - .apply(widget::container) - .padding([0, 0, 7, 0]), - ); - } - } Location::Network(uri, _display_name, _path) if uri == "network:///" => { tab_column = tab_column.push( widget::layer_container(widget::row::with_children([ @@ -6600,7 +6291,7 @@ impl Tab { tab_view = tab_view.style(|t| { let mut a = widget::container::Style::default(); let c = t.cosmic(); - a.border = cosmic::iced::core::Border { + a.border = cosmic::iced_core::Border { color: (c.accent_color()).into(), width: 1., radius: c.radius_0().into(), @@ -6631,17 +6322,14 @@ impl Tab { dnd_dest.into() } - pub fn multi_preview_view<'a>( - &'a self, - mime_app_cache_opt: Option<&'a mime_app::MimeAppCache>, - ) -> Element<'a, Message> { + pub fn multi_preview_view<'a>(&'a self) -> Element<'a, Message> { let cosmic_theme::Spacing { space_xxxs, space_m, .. - } = theme::spacing(); + } = theme::active().cosmic().spacing; - let mut column = widget::column::with_capacity(4).spacing(space_m); + let mut column = widget::column().spacing(space_m); let handle = widget::icon::from_name("text-x-generic") .size(IconSizes::default().grid()) @@ -6670,7 +6358,8 @@ impl Tab { if item.selected { item.location_opt .as_ref() - .and_then(Location::path_opt) + .map(Location::path_opt) + .flatten() .is_some() } else { false @@ -6679,7 +6368,7 @@ impl Tab { .collect() }); - let mut details = widget::column::with_capacity(3).spacing(space_xxxs); + let mut details = widget::column().spacing(space_xxxs); details = details.push(widget::text::body(fl!( "items", items = selected_items.len() @@ -6687,11 +6376,6 @@ impl Tab { let mut total_size: u64 = 0; let mut mime_type_counts: BTreeMap = BTreeMap::new(); - let mut user_name: BTreeSet = BTreeSet::new(); - let mut mode_user: BTreeSet = BTreeSet::new(); - let mut group_name: BTreeSet = BTreeSet::new(); - let mut mode_group: BTreeSet = BTreeSet::new(); - let mut mode_other: BTreeSet = BTreeSet::new(); let mut calculating_dir_size = false; let mut dir_size_error: Option = None; @@ -6715,23 +6399,6 @@ impl Tab { } else { total_size = total_size.saturating_add(metadata.len()); } - #[cfg(unix)] - { - let mode = metadata.mode(); - user_name.insert( - uzers::get_user_by_uid(metadata.uid()) - .and_then(|user| user.name().to_str().map(ToOwned::to_owned)) - .unwrap_or_default(), - ); - mode_user.insert(get_mode_part(mode, MODE_SHIFT_USER)); - group_name.insert( - uzers::get_group_by_gid(metadata.gid()) - .and_then(|group| group.name().to_str().map(ToOwned::to_owned)) - .unwrap_or_default(), - ); - mode_group.insert(get_mode_part(mode, MODE_SHIFT_GROUP)); - mode_other.insert(get_mode_part(mode, MODE_SHIFT_OTHER)); - } } } let mut mime_types: Vec<(String, u64)> = mime_type_counts.into_iter().collect(); @@ -6770,126 +6437,6 @@ impl Tab { column = column.push(widget::button::standard(fl!("open")).on_press(Message::Open(None))); - let mut settings = Vec::new(); - // Only allow modifying open-with if all mime types are the same - if mime_types.len() == 1 - && let Some(mime) = mime_types - .first() - .and_then(|(mime, _)| mime.parse::().ok()) - && let Some(mime_app_cache) = mime_app_cache_opt - { - let mime_apps = mime_app_cache.get_apps_for_mime(&mime, false); - if !mime_apps.is_empty() { - let mime_closure = mime.clone(); - let (names, icons) = mime_apps - .iter() - .map(|(app, _)| (Cow::Owned(app.name.clone()), app.icon())) - .collect::<(Vec<_>, Vec<_>)>(); - settings.push( - widget::settings::item::builder(fl!("open-with")).control( - Element::from( - widget::dropdown( - names, - mime_apps.iter().position(|(x, _)| x.is_default(&mime)), - move |index| (index, mime_closure.clone()), - ) - .icons(Cow::Owned(icons)), - ) - .map(move |(index, mime)| { - let mime_app = &mime_apps[index].0; - Message::SetOpenWith(mime, mime_app.id.clone()) - }), - ), - ); - } - } - - #[cfg(unix)] - { - // Only return mode part if it's the only one - fn selected_mode_part(mut modes: BTreeSet) -> Option { - match (modes.pop_first(), modes.pop_first()) { - (Some(mode), None) => Some(mode.try_into().unwrap()), - _ => None, - } - } - - // Convert a limited number of values from a set into a comma separated list - fn join_set(set: BTreeSet) -> String { - let limit = 5; - let mut title = set.into_iter().collect::>(); - if title.len() > limit { - title.truncate(limit); - title.push("...".to_string()); - } - title.join(", ") - } - - let mode_part_user = selected_mode_part(mode_user); - settings.push( - widget::settings::item::builder(join_set(user_name)) - .description(fl!("owner")) - .control( - widget::dropdown( - Cow::Borrowed(MODE_NAMES.as_slice()), - mode_part_user, - move |selected| { - Message::ShiftPermissions( - None, - MODE_SHIFT_USER, - selected.try_into().unwrap(), - ) - }, - ) - .placeholder(fl!("mixed")), - ), - ); - - let mode_part_group = selected_mode_part(mode_group); - settings.push( - widget::settings::item::builder(join_set(group_name)) - .description(fl!("group")) - .control( - widget::dropdown( - Cow::Borrowed(MODE_NAMES.as_slice()), - mode_part_group, - move |selected| { - Message::ShiftPermissions( - None, - MODE_SHIFT_GROUP, - selected.try_into().unwrap(), - ) - }, - ) - .placeholder(fl!("mixed")), - ), - ); - - let mode_part_other = selected_mode_part(mode_other); - settings.push( - widget::settings::item::builder(fl!("other")).control( - widget::dropdown( - Cow::Borrowed(MODE_NAMES.as_slice()), - mode_part_other, - move |selected| { - Message::ShiftPermissions( - None, - MODE_SHIFT_OTHER, - selected.try_into().unwrap(), - ) - }, - ) - .placeholder(fl!("mixed")), - ), - ); - } - - if !settings.is_empty() { - let mut section = widget::settings::section(); - section = section.extend(settings); - column = column.push(section); - } - column.into() } pub fn view<'a>( @@ -6897,17 +6444,10 @@ impl Tab { key_binds: &'a HashMap, modifiers: &'a Modifiers, clipboard_paste_available: bool, - context_actions: &'a [ContextActionPreset], ) -> Element<'a, Message> { widget::responsive(move |size| { widget::id_container( - self.view_responsive( - key_binds, - modifiers, - size, - clipboard_paste_available, - context_actions, - ), + self.view_responsive(key_binds, modifiers, size, clipboard_paste_available), Id::new(format!( "tab-{}-{}", self.scrollable_id, self.location_title @@ -6934,74 +6474,6 @@ impl Tab { Rectangle::new(point, size) }; - // Count the children of visible directories in the background. Doing it while - // scanning costs one directory listing per entry, which stalls remote filesystems. - #[cfg(feature = "gvfs")] - for item in items { - let ItemMetadata::GvfsPath { - children_opt: None, - is_dir: true, - .. - } = &item.metadata - else { - continue; - }; - - // Skip items that are not visible, or have no determined rect - match item.rect_opt.get() { - Some(rect) if rect.intersects(&visible_rect) => {} - _ => continue, - } - - let Some(path) = item.path_opt().cloned() else { - continue; - }; - - struct ChildrenWrapper(PathBuf); - impl Hash for ChildrenWrapper { - fn hash(&self, state: &mut H) { - self.0.hash(state); - } - } - - subscriptions.push(Subscription::run_with( - ChildrenWrapper(path), - |ChildrenWrapper(path)| { - let path = path.clone(); - stream::channel( - 1, - move |mut output: futures::channel::mpsc::Sender<_>| async move { - let message = { - let path = path.clone(); - tokio::task::spawn_blocking(move || { - let children = match fs::read_dir(&path) { - Ok(entries) => entries.count(), - Err(err) => { - log::warn!( - "failed to read directory {}: {}", - path.display(), - err - ); - 0 - } - }; - Message::DirectoryChildren(path, children) - }) - .await - .unwrap() - }; - - if let Err(err) = output.send(message).await { - log::warn!("failed to send directory children: {err}"); - } - - std::future::pending().await - }, - ) - }, - )); - } - for item in items { if item.thumbnail_opt.is_some() { // Skip items that already have a mime type and thumbnail @@ -7040,13 +6512,13 @@ impl Tab { // Determine effective memory budget based on image size let (effective_max_mb, effective_jobs) = if mime.type_() == mime::IMAGE { - match item.image_dimensions { - Some((width, height)) => { + match image::image_dimensions(&path) { + Ok((width, height)) => { let (_use_dedicated, eff_mb, eff_jobs) = should_use_dedicated_worker(width, height, max_mb, max_jobs); (eff_mb, eff_jobs) } - None => (max_mb, max_jobs), + Err(_) => (max_mb, max_jobs), } } else { (max_mb, max_jobs) @@ -7089,15 +6561,11 @@ impl Tab { stream::channel( 1, move |mut output: futures::channel::mpsc::Sender<_>| async move { - while crate::operation::is_actively_writing_to(&path) { - crate::operation::actively_writing_tick().await; - } - let message = { let path = path.clone(); // Acquire semaphore permit - let _permit = THUMB_SEMAPHORE.acquire().await.unwrap(); + _ = THUMB_SEMAPHORE.acquire().await; tokio::task::spawn_blocking(move || { let start = Instant::now(); @@ -7277,8 +6745,9 @@ impl Tab { .await .unwrap(); - let (watch_tx, mut watch_rx) = tokio::sync::watch::channel(true); + let output = Arc::new(tokio::sync::Mutex::new(output)); { + let output = output.clone(); tokio::task::spawn_blocking(move || { scan_search( &search_location, @@ -7288,15 +6757,17 @@ impl Tab { // Don't send if the result is too old if let Some(last_modified) = *last_modified_opt.read().unwrap() - && let SearchItem::Path(_, _, ref metadata) = - search_item { - if let Ok(modified) = metadata.modified() { - if modified < last_modified { + if let SearchItem::Path(_, _, ref metadata) = + search_item + { + if let Ok(modified) = metadata.modified() { + if modified < last_modified { + return true; + } + } else { return true; } - } else { - return true; } } @@ -7306,7 +6777,14 @@ impl Tab { true } else { // Wake up update method - watch_tx.send(false).is_ok() + futures::executor::block_on(async { + output + .lock() + .await + .send(Message::SearchReady(false)) + .await + }) + .is_ok() } } Err(_) => false, @@ -7319,16 +6797,13 @@ impl Tab { search_location, start.elapsed(), ); - }); - } - - while watch_rx.changed().await.is_ok() { - let is_ready = *watch_rx.borrow_and_update(); - let _ = output.send(Message::SearchReady(is_ready)).await; + }) + .await + .unwrap(); } // Send final ready - let _ = output.send(Message::SearchReady(true)).await; + let _ = output.lock().await.send(Message::SearchReady(true)).await; std::future::pending().await }, @@ -7401,19 +6876,6 @@ impl Tab { const fn format_time(&self, time: SystemTime) -> FormatTime<'_> { format_time(time, &self.date_time_formatter, &self.time_formatter) } - - fn on_drag<'a>(&self, rect_opt: Option) -> Option { - let rect = rect_opt?; - // We only want to publish a drag message if the overlapped items of the drag rect change, - // otherwise a view rebuild is triggered on every drag event. - let changed = self.items_opt.as_ref().is_some_and(|items| { - items.iter().any(|item| { - let overlaps = item.rect_opt.get().is_some_and(|r| r.intersects(&rect)); - overlaps != item.overlaps_drag_rect - }) - }); - changed.then_some(Message::Drag(Some(rect))) - } } pub fn respond_to_scroll_direction(delta: ScrollDelta, modifiers: &Modifiers) -> Option { @@ -7440,7 +6902,7 @@ pub fn respond_to_scroll_direction(delta: ScrollDelta, modifiers: &Modifiers) -> fn text_editor_class( theme: &cosmic::Theme, status: cosmic::widget::text_editor::Status, -) -> cosmic::iced::widget::text_editor::Style { +) -> cosmic::iced_widget::text_editor::Style { let cosmic = theme.cosmic(); let container = theme.current_container(); @@ -7453,9 +6915,9 @@ fn text_editor_class( let placeholder = placeholder.into(); match status { - cosmic::iced::widget::text_editor::Status::Active - | cosmic::iced::widget::text_editor::Status::Disabled => { - cosmic::iced::widget::text_editor::Style { + cosmic::iced_widget::text_editor::Status::Active + | cosmic::iced_widget::text_editor::Status::Disabled => { + cosmic::iced_widget::text_editor::Style { background: background.into(), border: cosmic::iced::Border { radius: cosmic.corner_radii.radius_m.into(), @@ -7467,9 +6929,9 @@ fn text_editor_class( selection, } } - cosmic::iced::widget::text_editor::Status::Hovered - | cosmic::iced::widget::text_editor::Status::Focused { .. } => { - cosmic::iced::widget::text_editor::Style { + cosmic::iced_widget::text_editor::Status::Hovered + | cosmic::iced_widget::text_editor::Status::Focused { .. } => { + cosmic::iced_widget::text_editor::Style { background: background.into(), border: cosmic::iced::Border { radius: cosmic.corner_radii.radius_m.into(), @@ -7486,25 +6948,21 @@ fn text_editor_class( #[cfg(test)] mod tests { - use std::path::PathBuf; - use std::{fs, io}; + use std::{fs, io, path::PathBuf}; - use cosmic::iced::mouse::ScrollDelta; - use cosmic::iced::runtime::keyboard::Modifiers; - use cosmic::widget; + use cosmic::{iced::mouse::ScrollDelta, iced_runtime::keyboard::Modifiers, widget}; use log::{debug, trace}; - use mime_guess::mime; use tempfile::TempDir; use test_log::test; - use super::{ - ItemMetadata, ItemThumbnail, Location, Message, Tab, respond_to_scroll_direction, scan_path, + use super::{Location, Message, Tab, respond_to_scroll_direction, scan_path}; + use crate::{ + app::test_utils::{ + NAME_LEN, NUM_DIRS, NUM_FILES, NUM_HIDDEN, NUM_NESTED, assert_eq_tab_path, empty_fs, + eq_path_item, filter_dirs, read_dir_sorted, simple_fs, tab_click_new, + }, + config::{IconSizes, TabConfig, ThumbCfg}, }; - use crate::app::test_utils::{ - NAME_LEN, NUM_DIRS, NUM_FILES, NUM_HIDDEN, NUM_NESTED, assert_eq_tab_path, empty_fs, - eq_path_item, filter_dirs, read_dir_sorted, simple_fs, tab_click_new, - }; - use crate::config::{IconSizes, TabConfig, ThumbCfg}; // Boilerplate for tab tests. Checks if simulated clicks selected items. fn tab_selects_item( @@ -7917,89 +7375,4 @@ mod tests { } } } - - #[test] - fn item_thumbnail_text_preview_small_utf8_returns_text() -> io::Result<()> { - let dir = TempDir::new()?; - let path = dir.path().join("preview.txt"); - fs::write(&path, "Hello, world!")?; - let metadata = fs::metadata(&path)?; - let item_metadata = ItemMetadata::Path { - metadata, - children_opt: None, - }; - let thumb = ItemThumbnail::new( - &path, - item_metadata, - mime::TEXT_PLAIN, - 128, - 100 * 1024 * 1024, - 1, - 8, - ); - assert!( - matches!(thumb, ItemThumbnail::Text(_)), - "small text file should produce Text thumbnail" - ); - Ok(()) - } - - #[test] - fn item_thumbnail_text_preview_empty_file_returns_not_image() -> io::Result<()> { - let dir = TempDir::new()?; - let path = dir.path().join("empty.txt"); - fs::File::create(&path)?; - let metadata = fs::metadata(&path)?; - let item_metadata = ItemMetadata::Path { - metadata, - children_opt: None, - }; - let thumb = ItemThumbnail::new( - &path, - item_metadata, - mime::TEXT_PLAIN, - 128, - 100 * 1024 * 1024, - 1, - 8, - ); - assert!( - matches!(thumb, ItemThumbnail::NotImage), - "empty text file should produce NotImage (no read)" - ); - Ok(()) - } - - #[test] - fn item_thumbnail_text_preview_invalid_utf8_uses_valid_prefix() -> io::Result<()> { - let dir = TempDir::new()?; - let path = dir.path().join("invalid_utf8.txt"); - // Valid UTF-8 "ab" then invalid byte sequence then "c" - fs::write(&path, b"ab\xff\xfe\xfdc")?; - let metadata = fs::metadata(&path)?; - let item_metadata = ItemMetadata::Path { - metadata, - children_opt: None, - }; - let thumb = ItemThumbnail::new( - &path, - item_metadata, - mime::TEXT_PLAIN, - 128, - 100 * 1024 * 1024, - 1, - 8, - ); - match &thumb { - ItemThumbnail::Text(content) => { - // Text editor content may add a trailing newline - assert_eq!(content.text().trim_end(), "ab"); - } - _ => panic!( - "expected Text thumbnail with valid prefix only, got {:?}", - thumb - ), - } - Ok(()) - } } diff --git a/src/thumbnail_cacher.rs b/src/thumbnail_cacher.rs index e42d343..719bfc6 100644 --- a/src/thumbnail_cacher.rs +++ b/src/thumbnail_cacher.rs @@ -1,14 +1,15 @@ use image::DynamicImage; use md5::{Digest, Md5}; use rustc_hash::FxHashMap; -use std::error::Error; -use std::fs::{self, File}; -use std::io::{self, BufReader, BufWriter}; -#[cfg(unix)] -use std::os::unix::fs::PermissionsExt; -use std::path::{Path, PathBuf}; -use std::sync::LazyLock; -use std::time::UNIX_EPOCH; +use std::{ + error::Error, + fs::{self, File}, + io::{self, BufReader, BufWriter}, + os::unix::fs::PermissionsExt, + path::{Path, PathBuf}, + sync::LazyLock, + time::UNIX_EPOCH, +}; use tempfile::NamedTempFile; use url::Url; @@ -92,7 +93,6 @@ impl ThumbnailCacher { } pub fn update_with_temp_file(&self, temp_file: NamedTempFile) -> Result<&Path, Box> { - #[cfg(unix)] fs::set_permissions(temp_file.path(), fs::Permissions::from_mode(0o600))?; self.update_thumbnail_text_metadata(temp_file.path())?; fs::rename(temp_file.path(), &self.thumbnail_path)?; @@ -127,7 +127,6 @@ impl ThumbnailCacher { pub fn create_fail_marker(&self) -> Result<(), Box> { if let Some(dir) = self.thumbnail_fail_marker_path.parent() { fs::create_dir_all(dir)?; - #[cfg(unix)] fs::set_permissions(dir, fs::Permissions::from_mode(0o700))?; } diff --git a/src/thumbnailer.rs b/src/thumbnailer.rs index a31af6b..f996f03 100644 --- a/src/thumbnailer.rs +++ b/src/thumbnailer.rs @@ -1,16 +1,15 @@ // Copyright 2023 System76 // SPDX-License-Identifier: GPL-3.0-only -#[cfg(feature = "desktop")] 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)] @@ -175,25 +174,3 @@ pub fn thumbnailer(mime: &Mime) -> Vec { let thumbnailer_cache = THUMBNAILER_CACHE.lock().unwrap(); thumbnailer_cache.get(mime) } - -#[cfg(all(test, feature = "desktop"))] -mod tests { - use super::Thumbnailer; - use std::path::Path; - - #[test] - fn command_expands_thumbnailer_arguments() { - let thumbnailer = Thumbnailer { - exec: "cosmic-files-thumbnailer %o --size %s %i".to_string(), - }; - - let command = thumbnailer - .command(Path::new("input.exe"), Path::new("output.png"), 128) - .expect("valid thumbnailer command"); - assert_eq!(command.get_program(), "cosmic-files-thumbnailer"); - assert_eq!( - command.get_args().collect::>(), - ["output.png", "--size", "128", "input.exe"] - ); - } -} diff --git a/src/trash.rs b/src/trash.rs deleted file mode 100644 index 1d857f5..0000000 --- a/src/trash.rs +++ /dev/null @@ -1,234 +0,0 @@ -use cosmic::widget; -use regex::Regex; -use std::collections::HashSet; -use std::path::{Path, PathBuf}; -use std::sync::LazyLock; - -use crate::config::IconSizes; -use crate::tab::{Item, SearchItem}; - -fn percent_decode(s: &str) -> Option { - let (mut r, mut b) = (String::with_capacity(s.len()), s.bytes()); - while let Some(c) = b.next() { - if c == b'%' { - let (hi, lo) = (b.next()?, b.next()?); - let h = |x: u8| match x { - b'0'..=b'9' => Some(x - b'0'), - b'a'..=b'f' => Some(x - b'a' + 10), - b'A'..=b'F' => Some(x - b'A' + 10), - _ => None, - }; - r.push((h(hi)? << 4 | h(lo)?) as char); - } else { - r.push(c as char); - } - } - Some(r) -} - -pub trait TrashExt { - fn is_empty() -> bool { - true - } - - fn entries() -> usize { - 0 - } - - fn folders() -> Result, trash::Error> { - Err(trash::Error::Unknown { - description: "reading trash folders not supported on this platform".into(), - }) - } - - fn scan(_sizes: IconSizes) -> Vec { - log::warn!("viewing trash not supported on this platform"); - Vec::new() - } - - fn scan_search bool + Sync>(_callback: F, _regex: &Regex) {} - - fn icon(icon_size: u16) -> widget::icon::Handle { - widget::icon::from_name(if Self::is_empty() { - "user-trash" - } else { - "user-trash-full" - }) - .size(icon_size) - .handle() - } - - fn icon_symbolic(icon_size: u16) -> widget::icon::Handle { - widget::icon::from_name(if Self::is_empty() { - "user-trash-symbolic" - } else { - "user-trash-full-symbolic" - }) - .size(icon_size) - .handle() - } -} - -/// Derive the actual filesystem path of a trashed item from its .trashinfo path -/// (or return the id directly if it's already a filesystem path). -pub fn trash_item_path(item: &trash::TrashItem) -> Option { - let id_path = Path::new(&item.id); - if id_path.extension().map_or(false, |e| e == "trashinfo") { - let trash_root = id_path.parent()?.parent()?; - let file_name = id_path.file_stem()?; - Some(trash_root.join("files").join(file_name)) - } else { - Some(PathBuf::from(&item.id)) - } -} - -/// For a path inside a trash `files/` directory, reconstruct the original path -/// by reading the parent `.trashinfo` file. -/// -/// Given `~/.local/share/Trash/files/folder/sub/file.txt`: -/// - The top-level trashed item is `folder` -/// - Read `~/.local/share/Trash/info/folder.trashinfo` to get the original path -/// - Compute: `/sub/file.txt` -pub fn original_path_for_trash_child(p: &Path) -> Option { - let files = trash_files_dir(p)?; - let root = files.parent()?; - let top = p.strip_prefix(files).ok()?.components().next()?; - let mut trashinfo_name = top.as_os_str().to_os_string(); - trashinfo_name.push(".trashinfo"); - let info = std::fs::read_to_string(root.join("info").join(trashinfo_name)).ok()?; - let orig = percent_decode(info.lines().find_map(|l| l.strip_prefix("Path="))?.trim())?; - let rel = p.strip_prefix(files.join(top)).ok()?; - let mut result = PathBuf::from(&orig); - if !rel.as_os_str().is_empty() { - result.push(rel); - } - Some(result) -} - -static TRASH_FOLDERS: LazyLock> = LazyLock::new(|| { - Trash::folders().unwrap_or_else(|e| { - log::warn!("failed to list trash folders: {}", e); - HashSet::new() - }) -}); - -fn is_trash_root(root: &Path) -> bool { - if TRASH_FOLDERS.is_empty() { - root.join("info").is_dir() - } else { - TRASH_FOLDERS.contains(root) - } -} - -fn trash_files_dir(path: &Path) -> Option<&Path> { - path.ancestors() - .find(|a| a.ends_with("files") && a.parent().is_some_and(is_trash_root)) -} - -/// Check whether a path is inside any trash `files/` directory. -pub fn is_trash_path(path: &Path) -> bool { - trash_files_dir(path).is_some() -} - -pub struct Trash; - -// This config statement is from trash::os_limited -#[cfg(any( - target_os = "windows", - all( - unix, - not(target_os = "macos"), - not(target_os = "ios"), - not(target_os = "android") - ) -))] -impl TrashExt for Trash { - fn is_empty() -> bool { - trash::os_limited::is_empty().unwrap_or(true) - } - - fn entries() -> usize { - match trash::os_limited::list() { - Ok(entries) => entries.len(), - Err(_err) => 0, - } - } - - // Not available on Windows only - #[cfg(not(target_os = "windows"))] - fn folders() -> Result, trash::Error> { - trash::os_limited::trash_folders() - } - - fn scan(sizes: IconSizes) -> Vec { - use crate::localize::LANGUAGE_SORTER; - use crate::tab::item_from_trash_entry; - use std::cmp::Ordering; - - let entries = match trash::os_limited::list() { - Ok(entry) => entry, - Err(err) => { - log::warn!("failed to read trash items: {err}"); - return Vec::new(); - } - }; - let mut items: Vec<_> = entries - .into_iter() - .filter_map(|entry| { - let metadata = trash::os_limited::metadata(&entry) - .inspect_err(|err| { - log::warn!("failed to get metadata for trash item {entry:?}: {err}") - }) - .ok()?; - Some(item_from_trash_entry(entry, metadata, sizes)) - }) - .collect(); - items.sort_by(|a, b| match (a.metadata.is_dir(), b.metadata.is_dir()) { - (true, false) => Ordering::Less, - (false, true) => Ordering::Greater, - _ => LANGUAGE_SORTER.compare(&a.display_name, &b.display_name), - }); - items - } - - fn scan_search bool + Sync>(callback: F, regex: &Regex) { - let entries = match trash::os_limited::list() { - Ok(entries) => entries, - Err(err) => { - log::warn!("failed to read trash items: {err}"); - return; - } - }; - - for entry in entries { - if let Ok(metadata) = trash::os_limited::metadata(&entry).inspect_err(|err| { - log::warn!("failed to get metadata for trash item {entry:?}: {err}") - }) { - let name = entry.name.to_string_lossy(); - if regex.is_match(&name) && !callback(SearchItem::Trash(entry, metadata)) { - break; - } - } - } - } -} - -// This config statement is from trash::os_limited, inverted -#[cfg(not(any( - target_os = "windows", - all( - unix, - not(target_os = "macos"), - not(target_os = "ios"), - not(target_os = "android") - ) -)))] -impl TrashExt for Trash { - fn scan_search bool + Sync>(callback: F, regex: &Regex) { - log::warn!( - "searching trash not supported on this platform for pattern {:?}", - regex.as_str() - ); - drop(callback); - } -} diff --git a/src/zoom.rs b/src/zoom.rs index 89ae5b8..600d0b5 100644 --- a/src/zoom.rs +++ b/src/zoom.rs @@ -1,7 +1,6 @@ use std::num::NonZeroU16; -use crate::config::IconSizes; -use crate::tab::View; +use crate::{config::IconSizes, tab::View}; static DEFAULT_ZOOM: NonZeroU16 = NonZeroU16::new(100).unwrap(); static MIN_ZOOM: NonZeroU16 = NonZeroU16::new(50).unwrap();