diff --git a/Cargo.lock b/Cargo.lock index ebed273..5162b2e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -544,7 +544,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -579,7 +579,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -823,7 +823,7 @@ checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -913,9 +913,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.22" +version = "1.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9540e661f81799159abee814118cc139a2004b3a3aa3ea37724a1b66530b90e0" +checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" dependencies = [ "jobserver", "libc", @@ -1212,7 +1212,7 @@ dependencies = [ [[package]] name = "cosmic-config" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic.git#cf6a35474e7f3b02032b56db1e3035040a93e1ef" +source = "git+https://github.com/pop-os/libcosmic.git#e645dee2f0b399485b756ac7d85b2c557b2a0e21" dependencies = [ "atomicwrites", "cosmic-config-derive", @@ -1231,7 +1231,7 @@ dependencies = [ [[package]] name = "cosmic-config-derive" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic.git#cf6a35474e7f3b02032b56db1e3035040a93e1ef" +source = "git+https://github.com/pop-os/libcosmic.git#e645dee2f0b399485b756ac7d85b2c557b2a0e21" dependencies = [ "quote", "syn 1.0.109", @@ -1340,7 +1340,7 @@ dependencies = [ [[package]] name = "cosmic-theme" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic.git#cf6a35474e7f3b02032b56db1e3035040a93e1ef" +source = "git+https://github.com/pop-os/libcosmic.git#e645dee2f0b399485b756ac7d85b2c557b2a0e21" dependencies = [ "almost", "cosmic-config", @@ -1496,7 +1496,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1507,7 +1507,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1517,7 +1517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core 0.9.10", @@ -1563,7 +1563,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1575,7 +1575,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1665,7 +1665,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -1784,7 +1784,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2098,7 +2098,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2270,7 +2270,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2458,7 +2458,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2486,8 +2486,8 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -2573,7 +2573,7 @@ checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" dependencies = [ "bitflags 2.6.0", "gpu-descriptor-types", - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -2621,6 +2621,12 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "hassle-rs" version = "0.11.0" @@ -2740,7 +2746,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.77", + "syn 2.0.79", "unic-langid", ] @@ -2754,7 +2760,7 @@ dependencies = [ "i18n-config", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -2783,7 +2789,7 @@ dependencies = [ [[package]] name = "iced" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#cf6a35474e7f3b02032b56db1e3035040a93e1ef" +source = "git+https://github.com/pop-os/libcosmic.git#e645dee2f0b399485b756ac7d85b2c557b2a0e21" dependencies = [ "dnd", "iced_accessibility", @@ -2802,7 +2808,7 @@ dependencies = [ [[package]] name = "iced_accessibility" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic.git#cf6a35474e7f3b02032b56db1e3035040a93e1ef" +source = "git+https://github.com/pop-os/libcosmic.git#e645dee2f0b399485b756ac7d85b2c557b2a0e21" dependencies = [ "accesskit", "accesskit_unix", @@ -2812,7 +2818,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#cf6a35474e7f3b02032b56db1e3035040a93e1ef" +source = "git+https://github.com/pop-os/libcosmic.git#e645dee2f0b399485b756ac7d85b2c557b2a0e21" dependencies = [ "bitflags 2.6.0", "dnd", @@ -2834,7 +2840,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#cf6a35474e7f3b02032b56db1e3035040a93e1ef" +source = "git+https://github.com/pop-os/libcosmic.git#e645dee2f0b399485b756ac7d85b2c557b2a0e21" dependencies = [ "futures", "iced_core", @@ -2847,7 +2853,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#cf6a35474e7f3b02032b56db1e3035040a93e1ef" +source = "git+https://github.com/pop-os/libcosmic.git#e645dee2f0b399485b756ac7d85b2c557b2a0e21" dependencies = [ "bitflags 2.6.0", "bytemuck", @@ -2871,7 +2877,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#cf6a35474e7f3b02032b56db1e3035040a93e1ef" +source = "git+https://github.com/pop-os/libcosmic.git#e645dee2f0b399485b756ac7d85b2c557b2a0e21" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -2883,7 +2889,7 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#cf6a35474e7f3b02032b56db1e3035040a93e1ef" +source = "git+https://github.com/pop-os/libcosmic.git#e645dee2f0b399485b756ac7d85b2c557b2a0e21" dependencies = [ "dnd", "iced_accessibility", @@ -2897,7 +2903,7 @@ dependencies = [ [[package]] name = "iced_sctk" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic.git#cf6a35474e7f3b02032b56db1e3035040a93e1ef" +source = "git+https://github.com/pop-os/libcosmic.git#e645dee2f0b399485b756ac7d85b2c557b2a0e21" dependencies = [ "enum-repr", "float-cmp", @@ -2924,7 +2930,7 @@ dependencies = [ [[package]] name = "iced_style" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#cf6a35474e7f3b02032b56db1e3035040a93e1ef" +source = "git+https://github.com/pop-os/libcosmic.git#e645dee2f0b399485b756ac7d85b2c557b2a0e21" dependencies = [ "iced_core", "once_cell", @@ -2934,7 +2940,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#cf6a35474e7f3b02032b56db1e3035040a93e1ef" +source = "git+https://github.com/pop-os/libcosmic.git#e645dee2f0b399485b756ac7d85b2c557b2a0e21" dependencies = [ "bytemuck", "cosmic-text", @@ -2951,7 +2957,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#cf6a35474e7f3b02032b56db1e3035040a93e1ef" +source = "git+https://github.com/pop-os/libcosmic.git#e645dee2f0b399485b756ac7d85b2c557b2a0e21" dependencies = [ "as-raw-xcb-connection", "bitflags 2.6.0", @@ -2980,7 +2986,7 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#cf6a35474e7f3b02032b56db1e3035040a93e1ef" +source = "git+https://github.com/pop-os/libcosmic.git#e645dee2f0b399485b756ac7d85b2c557b2a0e21" dependencies = [ "dnd", "iced_accessibility", @@ -2998,7 +3004,7 @@ dependencies = [ [[package]] name = "iced_winit" version = "0.12.0" -source = "git+https://github.com/pop-os/libcosmic.git#cf6a35474e7f3b02032b56db1e3035040a93e1ef" +source = "git+https://github.com/pop-os/libcosmic.git#e645dee2f0b399485b756ac7d85b2c557b2a0e21" dependencies = [ "dnd", "iced_accessibility", @@ -3175,7 +3181,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -3204,7 +3210,7 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata 0.4.7", + "regex-automata 0.4.8", "same-file", "walkdir", "winapi-util", @@ -3236,12 +3242,12 @@ checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.0", ] [[package]] @@ -3514,7 +3520,7 @@ checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libcosmic" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic.git#cf6a35474e7f3b02032b56db1e3035040a93e1ef" +source = "git+https://github.com/pop-os/libcosmic.git#e645dee2f0b399485b756ac7d85b2c557b2a0e21" dependencies = [ "apply", "ashpd 0.9.1", @@ -3588,9 +3594,9 @@ dependencies = [ [[package]] name = "liblzma-sys" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63117d31458acdb7b406f6c60090aa8e1e7cd6e283f8ee02ce585ed68c53fe39" +checksum = "6630cb23edeb2e563cd6c30d4117554c69646871455843c33ddcb1d9aef82ecf" dependencies = [ "cc", "libc", @@ -3622,7 +3628,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.6.0", "libc", - "redox_syscall 0.5.6", + "redox_syscall 0.5.7", ] [[package]] @@ -3690,7 +3696,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -3880,7 +3886,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", - "simd-adler32", ] [[package]] @@ -3890,6 +3895,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -4182,7 +4188,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4294,9 +4300,12 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] [[package]] name = "open" @@ -4331,7 +4340,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" dependencies = [ "dlv-list", - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -4365,7 +4374,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4405,7 +4414,7 @@ dependencies = [ "by_address", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4457,7 +4466,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.6", + "redox_syscall 0.5.7", "smallvec", "windows-targets 0.52.6", ] @@ -4520,7 +4529,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -4569,15 +4578,15 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "png" -version = "0.17.13" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" dependencies = [ "bitflags 1.3.2", "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.7.4", + "miniz_oxide 0.8.0", ] [[package]] @@ -4617,6 +4626,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + [[package]] name = "powerfmt" version = "0.2.0" @@ -4868,9 +4883,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355ae415ccd3a04315d3f8246e86d67689ea74d88d915576e1589a351062a13b" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags 2.6.0", ] @@ -4888,14 +4903,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -4909,13 +4924,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -4926,9 +4941,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "renderdoc-sys" @@ -5029,7 +5044,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.77", + "syn 2.0.79", "walkdir", ] @@ -5203,7 +5218,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -5227,7 +5242,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -5560,9 +5575,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", @@ -5577,7 +5592,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -5643,15 +5658,15 @@ dependencies = [ [[package]] name = "temp-dir" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f227968ec00f0e5322f9b8173c7a0cbcff6181a0a5b28e9892491c286277231" +checksum = "bc1ee6eef34f12f765cb94725905c6312b6610ab2b0940889cfe58dae7bc3c72" [[package]] name = "tempfile" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand 2.1.1", @@ -5688,7 +5703,7 @@ checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -5708,7 +5723,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -5864,7 +5879,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -5951,7 +5966,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -6082,9 +6097,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-bidi-mirroring" @@ -6133,9 +6148,9 @@ dependencies = [ [[package]] name = "unicode-properties" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" [[package]] name = "unicode-script" @@ -6360,7 +6375,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-shared", ] @@ -6394,7 +6409,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6821,7 +6836,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -6843,7 +6858,7 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -7310,7 +7325,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "synstructure", ] @@ -7418,7 +7433,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "zvariant_utils 2.1.0", ] @@ -7468,7 +7483,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -7488,7 +7503,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "synstructure", ] @@ -7509,7 +7524,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -7531,7 +7546,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] [[package]] @@ -7664,7 +7679,7 @@ dependencies = [ "proc-macro-crate 3.2.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", "zvariant_utils 2.1.0", ] @@ -7687,5 +7702,5 @@ checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.79", ] diff --git a/i18n/ar/cosmic_files.ftl b/i18n/ar/cosmic_files.ftl index 920647c..3e5e906 100644 --- a/i18n/ar/cosmic_files.ftl +++ b/i18n/ar/cosmic_files.ftl @@ -70,15 +70,6 @@ properties = الخصائص ## Settings settings = الإعدادات -settings-tab = لسان -settings-show-hidden = أظهر الملفّات والمجلدات المخفية -default-view = العرض المبدئي -icon-size-list = حجم الأيقونات (لائحة) -icon-size-grid = حجم الأيقونات (شبكة) -sorting-name = رتّب حسب -direction = الاتّجاه -ascending = تصاعديًا -descending = تنازليًا ### Appearance appearance = المظهر diff --git a/i18n/be/cosmic_files.ftl b/i18n/be/cosmic_files.ftl index ceb1267..646ba1d 100644 --- a/i18n/be/cosmic_files.ftl +++ b/i18n/be/cosmic_files.ftl @@ -170,15 +170,6 @@ show-details = Паказаць дэталі ## Settings settings = Налады -settings-tab = Укладка -settings-show-hidden = Паказаць схаваныя файлы -default-view = Выгляд па змаўчанні -icon-size-list = Памер значка (спіс) -icon-size-grid = Памер значка (сетка) -sorting-name = Сартаваць -direction = Напрамак -ascending = Па ўзрастанні -descending = Па ўбыванні ### Appearance appearance = Выгляд diff --git a/i18n/cs/cosmic_files.ftl b/i18n/cs/cosmic_files.ftl index 02a9563..6021795 100644 --- a/i18n/cs/cosmic_files.ftl +++ b/i18n/cs/cosmic_files.ftl @@ -70,15 +70,6 @@ properties = Vlastnosti ## Settings settings = Nastavení -settings-tab = Karta -settings-show-hidden = Zobrazit skryté soubory -default-view = Výchozí zobrazení -icon-size-list = Velikost ikon (seznam) -icon-size-grid = Velikost ikon (mřížka) -sorting-name = Třídit podle -direction = Směr -ascending = Vzestupně -descending = Sestupně ### Appearance appearance = Vzhled diff --git a/i18n/de/cosmic_files.ftl b/i18n/de/cosmic_files.ftl index beba282..2cf40bf 100644 --- a/i18n/de/cosmic_files.ftl +++ b/i18n/de/cosmic_files.ftl @@ -7,7 +7,7 @@ home = Benutzerordner networks = Netzwerke notification-in-progress = Dateioperationen sind im Gange. trash = Papierkorb -recents = Aktuelle +recents = Zuletzt benutzt undo = Rückgängig today = Heute @@ -169,15 +169,6 @@ properties = Eigenschaften ## Einstellungen settings = Einstellungen -settings-tab = Tab -settings-show-hidden = Versteckte Dateien anzeigen -default-view = Standardansicht -icon-size-list = Symbolgröße (Liste) -icon-size-grid = Symbolgröße (Raster) -sorting-name = Sortieren nach -direction = Richtung -ascending = Aufsteigend -descending = Absteigend ### Aussehen appearance = Aussehen @@ -238,4 +229,4 @@ sort-z-a = Z-A sort-newest-first = Neueste zuerst sort-oldest-first = Älteste zuerst sort-smallest-to-largest = Kleinste bis größte -sort-largest-to-smallest = Größte bis kleinste \ No newline at end of file +sort-largest-to-smallest = Größte bis kleinste diff --git a/i18n/en/cosmic_files.ftl b/i18n/en/cosmic_files.ftl index e7579d1..3e90ef7 100644 --- a/i18n/en/cosmic_files.ftl +++ b/i18n/en/cosmic_files.ftl @@ -51,6 +51,10 @@ open-multiple-folders = Open multiple folders save = Save save-file = Save file +## Open With Dialog +open-with-title = How do you want to open "{$name}"? +browse-store = Browse {$store} + ## Rename Dialog rename-file = Rename file rename-folder = Rename folder @@ -66,6 +70,11 @@ apply-to-all = Apply to all keep-both = Keep both skip = Skip +## Set as Executable and Launch Dialog +set-executable-and-launch = Set as executable and launch +set-executable-and-launch-description = Do you want to set "{$name}" as executable and launch it? +set-and-launch = Set and launch + ## Metadata Dialog owner = Owner group = Group @@ -140,6 +149,8 @@ extracted = Extracted {$items} {$items -> [one] item *[other] items } from {$from} to {$to} +setting-executable-and-launching = Setting "{$name}" as executable and launching +set-executable-and-launched = Set "{$name}" as executable and launched moving = Moving {$items} {$items -> [one] item *[other] items @@ -169,15 +180,6 @@ show-details = Show details ## Settings settings = Settings -settings-tab = Tab -settings-show-hidden = Show hidden files -default-view = Default view -icon-size-list = Icon size (list) -icon-size-grid = Icon size (grid) -sorting-name = Sort by -direction = Direction -ascending = Ascending -descending = Descending ### Appearance appearance = Appearance @@ -201,6 +203,11 @@ sort-by-modified = Sort by modified sort-by-size = Sort by size sort-by-trashed = Sort by delete time +## Desktop +change-wallpaper = Change wallpaper... +desktop-appearance = Desktop appearance... +display-settings = Display settings... + # Menu ## File @@ -208,7 +215,6 @@ file = File new-tab = New tab new-window = New window rename = Rename... -menu-show-details = Show details... close-tab = Close tab quit = Quit @@ -228,6 +234,7 @@ grid-view = Grid view list-view = List view show-hidden-files = Show hidden files list-directories-first = List directories first +gallery-preview = Gallery preview menu-settings = Settings... menu-about = About COSMIC Files... diff --git a/i18n/es-419/cosmic_files.ftl b/i18n/es-419/cosmic_files.ftl index 07b5e7c..faf16ab 100644 --- a/i18n/es-419/cosmic_files.ftl +++ b/i18n/es-419/cosmic_files.ftl @@ -109,15 +109,6 @@ properties = Propiedades ## Settings settings = Configuración -settings-tab = Pestaña -settings-show-hidden = Mostrar archivos ocultos -default-view = Vista predeterminada -icon-size-list = Tamaño de icono (lista) -icon-size-grid = Tamaño de icono (cuadrícula) -sorting-name = Ordenar por -direction = Dirección -ascending = Ascendente -descending = Descendente ### Appearance appearance = Apariencia diff --git a/i18n/es/cosmic_files.ftl b/i18n/es/cosmic_files.ftl index 4cc352b..3b9a3ac 100644 --- a/i18n/es/cosmic_files.ftl +++ b/i18n/es/cosmic_files.ftl @@ -70,15 +70,6 @@ properties = Propiedades ## Settings settings = Configuración -settings-tab = Tab -default-view = Vista por defecto -settings-show-hidden = Mostrar archivos escondidos -icon-size-list = Tamaño de iconos (lista) -icon-size-grid = Tamaño de iconos (grilla) -sorting-name = Ordenar por -direction = Dirección -ascending = Ascendiente -descending = Descendiente ### Appearance appearance = Apariencia diff --git a/i18n/fi/cosmic_files.ftl b/i18n/fi/cosmic_files.ftl index 30c7285..0b234d3 100644 --- a/i18n/fi/cosmic_files.ftl +++ b/i18n/fi/cosmic_files.ftl @@ -60,14 +60,6 @@ properties = Ominaisuudet ## Settings settings = Asetukset -settings-tab = Välilehti -settings-show-hidden = Näytä piilotiedostot -icon-size-list = Kuvakkeen koko (luettelo) -icon-size-grid = Kuvakkeen koko (ruudukko) -sorting-name = Järjestä -direction = Suunta -ascending = Nouseva -descending = Laskeva ### Appearance appearance = Ulkoasu diff --git a/i18n/fr/cosmic_files.ftl b/i18n/fr/cosmic_files.ftl index 275eb04..bd6fded 100644 --- a/i18n/fr/cosmic_files.ftl +++ b/i18n/fr/cosmic_files.ftl @@ -169,15 +169,6 @@ show-details = Afficher les détails ## Settings settings = Paramètres -settings-tab = Onglet -settings-show-hidden = Afficher les fichiers cachés -default-view = Vue par défaut -icon-size-list = Taille des icônes (liste) -icon-size-grid = Taille des icônes (grille) -sorting-name = Trier par -direction = Direction -ascending = Ascendant -descending = Descendant ### Appearance appearance = Apparence diff --git a/i18n/hi/cosmic_files.ftl b/i18n/hi/cosmic_files.ftl index a4365f7..a32798f 100644 --- a/i18n/hi/cosmic_files.ftl +++ b/i18n/hi/cosmic_files.ftl @@ -169,15 +169,6 @@ show-details = विवरण दिखाएँ ## Settings settings = सेटिंग्स -settings-tab = टैब -settings-show-hidden = छिपी हुई फाइलें दिखाएँ -default-view = डिफ़ॉल्ट दृश्य -icon-size-list = आइकॉन आकार (सूची) -icon-size-grid = आइकॉन आकार (ग्रिड) -sorting-name = क्रमबद्ध द्वारा -direction = दिशा -ascending = आरोही -descending = अवरोही ### Appearance appearance = रूप diff --git a/i18n/hu/cosmic_files.ftl b/i18n/hu/cosmic_files.ftl index e8bcb27..c352cf8 100644 --- a/i18n/hu/cosmic_files.ftl +++ b/i18n/hu/cosmic_files.ftl @@ -110,15 +110,6 @@ properties = Tulajdonságok ## Settings settings = Beállítások -settings-tab = Lap -settings-show-hidden = Rejtett fájlok mutatása -default-view = Alapértelmezett nézet -icon-size-list = Ikon méret (lista) -icon-size-grid = Ikon méret (rács) -sorting-name = Rendezés -direction = Irány -ascending = Növekvő -descending = Csökkenő ### Appearance appearance = Kinézet diff --git a/i18n/it/cosmic_files.ftl b/i18n/it/cosmic_files.ftl index 3dbcd4f..5616158 100644 --- a/i18n/it/cosmic_files.ftl +++ b/i18n/it/cosmic_files.ftl @@ -14,6 +14,7 @@ today = Oggi # List view name = Nome modified = Modificato +trashed-on = Spostato nel cestino size = Dimensioni # Dialogs @@ -169,15 +170,6 @@ show-details = Mostra dettagli ## Settings settings = Impostazioni -settings-tab = Schede -settings-show-hidden = Mostra file nascosti -default-view = Vista predefinita -icon-size-list = Dimensioni icone (lista) -icon-size-grid = Dimensioni icone (griglia) -sorting-name = Ordina per -direction = Direzione -ascending = Ascendente -descending = Discendente ### Appearance appearance = Aspetto @@ -199,6 +191,7 @@ remove-from-sidebar = Rimuovi da sidebar sort-by-name = Ordina per nome sort-by-modified = Ordina per ultima modifica sort-by-size = Ordina per dimensione +sort-by-trashed = Ordina per cancellazione # Menu @@ -220,7 +213,7 @@ select-all = Seleziona tutti ## View zoom-in = Zoom in -default-size = Default size +default-size = Default zoom-out = Zoom out view = Vista grid-view = Griglia diff --git a/i18n/ja/cosmic_files.ftl b/i18n/ja/cosmic_files.ftl index 2bc0656..c7e1e0c 100644 --- a/i18n/ja/cosmic_files.ftl +++ b/i18n/ja/cosmic_files.ftl @@ -136,15 +136,6 @@ properties = プロパティ ## Settings settings = 設定 -settings-tab = タブ -settings-show-hidden = 隠しファイルを表示 -default-view = デフォルトの表示 -icon-size-list = アイコンサイズ(リスト) -icon-size-grid = アイコンサイズ(グリッド) -sorting-name = 並べ替え -direction = 順 -ascending = 昇順 -descending = 降順 ### Appearance appearance = 外観 diff --git a/i18n/kn/cosmic_fiiles.ftl b/i18n/kn/cosmic_fiiles.ftl index 78ca417..e88ed69 100644 --- a/i18n/kn/cosmic_fiiles.ftl +++ b/i18n/kn/cosmic_fiiles.ftl @@ -169,15 +169,6 @@ show-details = ವಿವರಗಳನ್ನು ತೋರಿಸಿ ## Settings settings = ಸೆಟ್ಟಿಂಗ್‌ಗಳು -settings-tab = ಟ್ಯಾಬ್ -settings-show-hidden = ಮರೆಮಾಡಲಾದ ಫೈಲ್‌ಗಳನ್ನು ತೋರಿಸಿ -default-view = ಸ್ಥೂಲ ದೃಶ್ಯ -icon-size-list = ಐಕಾನ್ ಗಾತ್ರ (ಪಟ್ಟಿ) -icon-size-grid = ಐಕಾನ್ ಗಾತ್ರ (ಗ್ರೀಡ್) -sorting-name = ಇದರಿಂದ ಶ್ರೇಣೀಬದ್ಧಗೊಳಿಸಿ -direction = ದಿಕ್ಕು -ascending = ಆರೋಹಣ -descending = ಅವರೋಹಣ ### Appearance appearance = ನೋಟ diff --git a/i18n/ko/cosmic_files.ftl b/i18n/ko/cosmic_files.ftl index 2cf12c3..c41b8fb 100644 --- a/i18n/ko/cosmic_files.ftl +++ b/i18n/ko/cosmic_files.ftl @@ -60,14 +60,6 @@ properties = 정보 ## Settings settings = 설정 -settings-tab = 탭 -settings-show-hidden = 숨겨진 파일 표시 -icon-size-list = 아이콘 크기 (목록) -icon-size-grid = 아이콘 크기 (그리드) -sorting-name = 정렬 -direction = 경로 -ascending = 오름차순 -descending = 내림차순 ### Appearance appearance = 모양새 diff --git a/i18n/pl/cosmic_files.ftl b/i18n/pl/cosmic_files.ftl index 472c45e..ae648e1 100644 --- a/i18n/pl/cosmic_files.ftl +++ b/i18n/pl/cosmic_files.ftl @@ -174,15 +174,6 @@ show-details = Pokaż szczegóły ## Settings settings = Ustawienia -settings-tab = Karta -settings-show-hidden = Pokaż ukryte pliki -default-view = Domyślny widok -icon-size-list = Rozmiar ikon (lista) -icon-size-grid = Rozmiar ikon (siatka) -sorting-name = Uszereguj według -direction = Kierunek -ascending = Rosnąco -descending = Malejąco ### Appearance appearance = Wygląd diff --git a/i18n/pt-BR/cosmic_files.ftl b/i18n/pt-BR/cosmic_files.ftl index 96a4f16..195ae69 100644 --- a/i18n/pt-BR/cosmic_files.ftl +++ b/i18n/pt-BR/cosmic_files.ftl @@ -169,15 +169,6 @@ show-details = Mostrar detalhes ## Settings settings = Configurações -settings-tab = Aba -settings-show-hidden = Mostrar arquivos ocultos -default-view = Visualização padrão -icon-size-list = Tamanho do ícone (lista) -icon-size-grid = Tamanho do ícone (grade) -sorting-name = Ordenar por -direction = Classificar -ascending = Crescente -descending = Decrescente ### Appearance appearance = Aparência diff --git a/i18n/pt/cosmic_files.ftl b/i18n/pt/cosmic_files.ftl index c584ced..e8d0f1d 100644 --- a/i18n/pt/cosmic_files.ftl +++ b/i18n/pt/cosmic_files.ftl @@ -130,15 +130,6 @@ show-details = Mostrar detalhes ## Settings settings = Definições -settings-tab = Separador -settings-show-hidden = Mostrar ficheiros ocultos -default-view = Vista predefinida -icon-size-list = Tamanho dos ícones (lista) -icon-size-grid = Tamanho dos ícones (grelha) -sorting-name = Ordenar por -direction = Direção -ascending = Ascendente -descending = Descendente ### Appearance appearance = Aparência diff --git a/i18n/ru/cosmic_files.ftl b/i18n/ru/cosmic_files.ftl index f83cbfe..c7d257a 100644 --- a/i18n/ru/cosmic_files.ftl +++ b/i18n/ru/cosmic_files.ftl @@ -1,17 +1,27 @@ cosmic-files = Файлы COSMIC empty-folder = Пустая папка empty-folder-hidden = Пустая папка (со скрытыми элементами) +no-results = Ничего не найдено filesystem = Файловая система home = Домашняя папка trash = Корзина +networks = Сеть +notification-in-progress = Выполняются файловые операции. +recents = Недавние документы +undo = Отменить +today = Сегодня # List view name = Имя modified = Изменено +trashed-on = Удалено size = Размер # Dialogs +## Compress Dialog +create-archive = Создать архив + ## Empty Trash Dialog empty-trash = Очистить корзину empty-trash-warning = Вы уверены, что хотите навсегда удалить все элементы в корзине? @@ -34,6 +44,7 @@ open-file = Открыть файл open-folder = Открыть папку open-in-new-tab = Открыть в новой вкладке open-in-new-window = Открыть в новом окне +open-item-location = Открыть расположение элемента open-multiple-files = Открыть несколько файлов open-multiple-folders = Открыть несколько папок save = Сохранить @@ -47,37 +58,120 @@ rename-folder = Переименовать папку replace = Заменить replace-title = {$filename} уже существует в данном каталоге. replace-warning = Действительно ли Вы хотите заменить файл на тот, что Вы сохраняете? Замена перезапишет все данные файла. +replace-warning-operation = Хотите заменить? Замена приведёт к перезаписи содержимого. +original-file = Оригинальный файл +replace-with = Заменить на +apply-to-all = Применить ко всем +keep-both = Сохранить оба +skip = Пропустить + +## Metadata Dialog +owner = Владелец +group = Группа +other = Остальные +read = Чтение +write = Запись +execute = Выполнение # Context Pages ## About git-description = Git коммит {$hash} от {$date} +## Add Network Drive +add-network-drive = Добавить сетевой диск +connect = Подключиться +connect-anonymously = Подключиться анонимно +connecting = Подключение... +domain = Домен +enter-server-address = Введите адрес сервера +network-drive-description = + Адреса серверов включают префикс протокола и адрес. + Пример: ssh://192.168.0.1, ftp://[2001:db8::1] +### Make sure to keep the comma which separates the columns +network-drive-schemes = + Доступные протоколы,Префикс + AppleTalk,afp:// + File Transfer Protocol,ftp:// or ftps:// + Network File System,nfs:// + Server Message Block,smb:// + SSH File Transfer Protocol,sftp:// or ssh:// + WebDav,dav:// or davs:// +network-drive-error = Невозможно получить доступ к сетевому диску +password = Пароль +remember-password = Запомнить пароль +try-again = Попробовать еще раз +username = Имя пользователя + ## Operations operations = Операции +edit-history = История редактирования +history = История +no-history = В истории нет записей. pending = В ожидании failed = Сбой complete = Завершено +compressing = Сжатие {$items} {$items -> + [one] элемента + *[other] элементов + } из {$from} в {$to} +compressed = Сжат {$items} {$items -> + [one] элемент + *[other] элементы + } из {$from} в {$to} copy_noun = Копирование +creating = Создание {$name} в {$parent} +created = Создан {$name} в {$parent} +copying = Копирование {$items} {$items -> + [one] элемент + *[other] элементов + } из {$from} в {$to} +copied = Скопировано {$items} {$items -> + [one] элемент + *[other] элементов + } из {$from} в {$to} +emptying-trash = Очистка {trash} +emptied-trash = Очищена {trash} +extracting = Извлечение {$items} {$items -> + [one] элемента + *[other] элементов + } из {$from} в {$to} +extracted = Извлечено {$items} {$items -> + [one] элемент + *[other] элементы + } из {$from} в {$to} +moving = Перемещение {$items} {$items -> + [one] элемента + *[other] элементов + } из {$from} в {$to} +moved = Перемещено {$items} {$items -> + [one] элемент + *[other] элементы + } из {$from} в {$to} +renaming = Переименование {$from} в {$to} +renamed = Переименован {$from} в {$to} +restoring = Восстановление {$items} {$items -> + [one] элемента + *[other] элементов + } из {trash} +restored = Восстановлено {$items} {$items -> + [one] элемент + *[other] элементов + } из {trash} +unknown-folder = неизвестная папка ## Open with open-with = Открыть с помощью default-app = {$name} (по умолчанию) +## Show details +show-details = Показать подробности + ## Properties properties = Свойства ## Settings settings = Параметры -settings-tab = Вкладка -settings-show-hidden = Показывать скрытые файлы -default-view = Вид по умолчанию -icon-size-list = Размер иконок (список) -icon-size-grid = Размер иконок (сетка) -sorting-name = Отсортировать -direction = Направление -ascending = По возрастанию -descending = По убыванию ### Appearance appearance = Оформление @@ -88,6 +182,8 @@ light = Светлая # Context menu add-to-sidebar = Добавить на боковую панель +compress = Сжать +extract-here = Извлечь new-file = Новый файл new-folder = Новая папка open-in-terminal = Открыть в терминале @@ -97,6 +193,7 @@ remove-from-sidebar = Убрать из боковой панели sort-by-name = Разместить по имени sort-by-modified = Разместить по дате изменения sort-by-size = Разместить по размеру +sort-by-trashed = Разместить по дате удаления # Menu @@ -105,6 +202,7 @@ file = Файл new-tab = Новая вкладка new-window = Новое окно rename = Переименовать +menu-show-details = Показать подробности close-tab = Закрыть вкладку quit = Завершить @@ -126,3 +224,12 @@ show-hidden-files = Показывать скрытые файлы list-directories-first = Показывать сначала папки menu-settings = Параметры... menu-about = О приложении Файлы COSMIC... + +## Sort +sort = Сортировка +sort-a-z = По алфавиту (от А до Я) +sort-z-a = По алфавиту (от Я до А) +sort-newest-first = Сначала новые +sort-oldest-first = Сначала старые +sort-smallest-to-largest = От наименьшего к большему +sort-largest-to-smallest = От большего к меньшему \ No newline at end of file diff --git a/i18n/sk/cosmic_files.ftl b/i18n/sk/cosmic_files.ftl index 7b942b8..e7f33a0 100644 --- a/i18n/sk/cosmic_files.ftl +++ b/i18n/sk/cosmic_files.ftl @@ -116,15 +116,6 @@ properties = Podrobnosti ## Settings settings = Nastavenia -settings-tab = Karta -settings-show-hidden = Zobraziť skryté súbory -default-view = Predvolené zobrazenie -icon-size-list = Veľkosť ikon (zoznam) -icon-size-grid = Veľkosť ikon (mriežka) -sorting-name = Zoradiť podľa -direction = Smer -ascending = Vzostupný -descending = Zostupný ### Appearance appearance = Vzhľad diff --git a/i18n/sv/cosmic_files.ftl b/i18n/sv/cosmic_files.ftl index fac61b1..5a85b9c 100644 --- a/i18n/sv/cosmic_files.ftl +++ b/i18n/sv/cosmic_files.ftl @@ -31,15 +31,6 @@ properties = Egenskaper ## Settings settings = Inställningar -settings-tab = Flik -settings-show-hidden = Visa dolda filer -default-view = Standardvy -icon-size-list = Ikonstorlek (lista) -icon-size-grid = Ikonstorlek (rutnät) -sorting-name = Sortera efter -direction = Riktning -ascending = Stigande -descending = Fallande ### Appearance appearance = Utseende diff --git a/i18n/uk/cosmic_files.ftl b/i18n/uk/cosmic_files.ftl index 1f93011..3359e9e 100644 --- a/i18n/uk/cosmic_files.ftl +++ b/i18n/uk/cosmic_files.ftl @@ -114,15 +114,6 @@ properties = Властивості ## Settings settings = Налаштування -settings-tab = Вкладки -settings-show-hidden = Показувати приховані файли -default-view = Типовий режим перегляду -icon-size-list = Розмір піктограм (для списку) -icon-size-grid = Розмір піктограм (для ґратки) -sorting-name = Сортувати за -direction = Напрям -ascending = Зростання -descending = Спадання ### Appearance appearance = Зовнішній вигляд diff --git a/i18n/zh-CN/cosmic_files.ftl b/i18n/zh-CN/cosmic_files.ftl index 244f476..fedfff3 100644 --- a/i18n/zh-CN/cosmic_files.ftl +++ b/i18n/zh-CN/cosmic_files.ftl @@ -111,15 +111,6 @@ show-details = 显示详情 ## Settings settings = 设置 -settings-tab = 标签页 -settings-show-hidden = 显示隐藏文件 -default-view = 默认视图 -icon-size-list = 图标大小(列表) -icon-size-grid = 图标大小(网格) -sorting-name = 排序方式 -direction = 方向 -ascending = 升序 -descending = 降序 ### Appearance appearance = 外观 diff --git a/i18n/zh-TW/cosmic_files.ftl b/i18n/zh-TW/cosmic_files.ftl index 112503d..a87b9f7 100644 --- a/i18n/zh-TW/cosmic_files.ftl +++ b/i18n/zh-TW/cosmic_files.ftl @@ -167,15 +167,6 @@ show-details = 顯示詳細資料 ## Settings settings = 設定 -settings-tab = 分頁 -settings-show-hidden = 顯示隱藏檔案 -default-view = 預設檢視 -icon-size-list = 圖示大小(列表) -icon-size-grid = 圖示大小(網格) -sorting-name = 排序依據 -direction = 排序方向 -ascending = 升序 -descending = 降序 ### Appearance appearance = 外觀 diff --git a/src/app.rs b/src/app.rs index e47c5fd..6200689 100644 --- a/src/app.rs +++ b/src/app.rs @@ -45,6 +45,7 @@ use std::{ collections::{BTreeMap, HashMap, HashSet, VecDeque}, env, fmt, fs, future::pending, + io, num::NonZeroU16, path::PathBuf, process, @@ -92,9 +93,13 @@ pub enum Action { Compress, Copy, Cut, + CosmicSettingsAppearance, + CosmicSettingsDisplays, + CosmicSettingsWallpaper, EditHistory, EditLocation, ExtractHere, + Gallery, HistoryNext, HistoryPrevious, ItemDown, @@ -144,9 +149,15 @@ impl Action { Action::Compress => Message::Compress(entity_opt), Action::Copy => Message::Copy(entity_opt), Action::Cut => Message::Cut(entity_opt), + Action::CosmicSettingsAppearance => Message::CosmicSettings("appearance"), + Action::CosmicSettingsDisplays => Message::CosmicSettings("displays"), + Action::CosmicSettingsWallpaper => Message::CosmicSettings("wallpaper"), Action::EditHistory => Message::ToggleContextPage(ContextPage::EditHistory), - Action::EditLocation => Message::EditLocation(entity_opt), + Action::EditLocation => { + Message::TabMessage(entity_opt, tab::Message::EditLocationToggle) + } Action::ExtractHere => Message::ExtractHere(entity_opt), + Action::Gallery => Message::TabMessage(entity_opt, tab::Message::GalleryToggle), Action::HistoryNext => Message::TabMessage(entity_opt, tab::Message::GoNext), Action::HistoryPrevious => Message::TabMessage(entity_opt, tab::Message::GoPrevious), Action::ItemDown => Message::TabMessage(entity_opt, tab::Message::ItemDown), @@ -162,11 +173,9 @@ impl Action { Action::OpenInNewWindow => Message::OpenInNewWindow(entity_opt), Action::OpenItemLocation => Message::OpenItemLocation(entity_opt), Action::OpenTerminal => Message::OpenTerminal(entity_opt), - Action::OpenWith => Message::ToggleContextPage(ContextPage::OpenWith), + Action::OpenWith => Message::OpenWithDialog(entity_opt), Action::Paste => Message::Paste(entity_opt), - Action::Preview => { - Message::ToggleContextPage(ContextPage::Preview(entity_opt, PreviewKind::Selected)) - } + Action::Preview => Message::ToggleShowDetails, Action::Rename => Message::Rename(entity_opt), Action::RestoreFromTrash => Message::RestoreFromTrash(entity_opt), Action::SearchActivate => Message::SearchActivate, @@ -179,15 +188,9 @@ impl Action { Action::TabNew => Message::TabNew, Action::TabNext => Message::TabNext, Action::TabPrev => Message::TabPrev, - Action::TabViewGrid => { - Message::TabMessage(entity_opt, tab::Message::View(tab::View::Grid)) - } - Action::TabViewList => { - Message::TabMessage(entity_opt, tab::Message::View(tab::View::List)) - } - Action::ToggleFoldersFirst => { - Message::TabMessage(entity_opt, tab::Message::ToggleFoldersFirst) - } + Action::TabViewGrid => Message::TabView(entity_opt, tab::View::Grid), + Action::TabViewList => Message::TabView(entity_opt, tab::View::List), + Action::ToggleFoldersFirst => Message::ToggleFoldersFirst, Action::ToggleShowHidden => { Message::TabMessage(entity_opt, tab::Message::ToggleShowHidden) } @@ -196,9 +199,9 @@ impl Action { } Action::WindowClose => Message::WindowClose, Action::WindowNew => Message::WindowNew, - Action::ZoomDefault => Message::TabMessage(entity_opt, tab::Message::ZoomDefault), - Action::ZoomIn => Message::TabMessage(entity_opt, tab::Message::ZoomIn), - Action::ZoomOut => Message::TabMessage(entity_opt, tab::Message::ZoomOut), + Action::ZoomDefault => Message::ZoomDefault(entity_opt), + Action::ZoomIn => Message::ZoomIn(entity_opt), + Action::ZoomOut => Message::ZoomOut(entity_opt), Action::Recents => Message::Recents, } } @@ -256,13 +259,13 @@ pub enum Message { Compress(Option), Config(Config), Copy(Option), + CosmicSettings(&'static str), Cut(Option), DialogCancel, DialogComplete, DialogPush(DialogPage), DialogUpdate(DialogPage), DialogUpdateComplete(DialogPage), - EditLocation(Option), ExtractHere(Option), Key(Modifiers, Key), LaunchUrl(String), @@ -283,16 +286,17 @@ pub enum Message { NotifyEvents(Vec), NotifyWatcher(WatcherWrapper), OpenTerminal(Option), - OpenWith(PathBuf, mime_app::MimeApp), OpenInNewTab(Option), OpenInNewWindow(Option), OpenItemLocation(Option), + OpenWithBrowse, + OpenWithDialog(Option), + OpenWithSelection(usize), Paste(Option), PasteContents(PathBuf, ClipboardPaste), PendingComplete(u64), PendingError(u64, String), PendingProgress(u64, f32), - Preview(Entity, PreviewKind, time::Duration), RescanTrash, Rename(Option), ReplaceResult(ReplaceResult), @@ -310,12 +314,18 @@ pub enum Message { TabMessage(Option, tab::Message), TabNew, TabRescan(Entity, Location, Vec, Option), + TabView(Option, tab::View), ToggleContextPage(ContextPage), + ToggleShowDetails, + ToggleFoldersFirst, Undo(usize), UndoTrash(widget::ToastId, Arc<[PathBuf]>), UndoTrashStart(Vec), WindowClose, WindowNew, + ZoomDefault(Option), + ZoomIn(Option), + ZoomOut(Option), DndHoverLocTimeout(Location), DndHoverTabTimeout(Entity), DndEnterNav(Entity), @@ -336,7 +346,6 @@ pub enum ContextPage { About, EditHistory, NetworkDrive, - OpenWith, Preview(Option, PreviewKind), Settings, } @@ -347,7 +356,6 @@ impl ContextPage { Self::About => String::new(), Self::EditHistory => fl!("edit-history"), Self::NetworkDrive => fl!("add-network-drive"), - Self::OpenWith => fl!("open-with"), Self::Preview(..) => String::default(), Self::Settings => fl!("settings"), } @@ -406,6 +414,13 @@ pub enum DialogPage { name: String, dir: bool, }, + OpenWith { + path: PathBuf, + mime: mime_guess::Mime, + apps: Vec, + selected: usize, + store_opt: Option, + }, RenameItem { from: PathBuf, parent: PathBuf, @@ -419,6 +434,9 @@ pub enum DialogPage { apply_to_all: bool, tx: mpsc::Sender, }, + SetExecutableAndLaunch { + path: PathBuf, + }, } pub struct FavoriteIndex(usize); @@ -457,9 +475,6 @@ pub struct App { config: Config, mode: Mode, app_themes: Vec, - default_view: Vec, - sort_by_names: Vec, - sort_direction: Vec, context_page: ContextPage, dialog_pages: VecDeque, dialog_text_input: widget::Id, @@ -475,7 +490,6 @@ pub struct App { pending_operations: BTreeMap, complete_operations: BTreeMap, failed_operations: BTreeMap, - preview_opt: Option<(Entity, PreviewKind, time::Instant)>, search_active: bool, search_id: widget::Id, search_input: String, @@ -620,7 +634,20 @@ impl App { fn update_config(&mut self) -> Command { self.update_nav_model(); - cosmic::app::command::set_theme(self.config.app_theme.theme()) + // Tabs are collected first to placate the borrowck + let tabs: Vec<_> = self.tab_model.iter().collect(); + // Update main conf and each tab with the new config + let commands: Vec<_> = std::iter::once(cosmic::app::command::set_theme( + self.config.app_theme.theme(), + )) + .chain(tabs.into_iter().map(|entity| { + self.update(Message::TabMessage( + Some(entity), + tab::Message::Config(self.config.tab), + )) + })) + .collect(); + Command::batch(commands) } fn activate_nav_model_location(&mut self, location: &Location) { @@ -897,24 +924,6 @@ impl App { .into() } - fn open_with(&self) -> Element { - let mut children = Vec::new(); - let entity = self.tab_model.active(); - if let Some(tab) = self.tab_model.data::(entity) { - if let Some(items) = tab.items_opt() { - for item in items.iter() { - if item.selected { - children.push(item.open_with_view(tab.config.icon_sizes)); - // Only show one property view to avoid issues like hangs when generating - // preview images on thousands of files - break; - } - } - } - } - widget::settings::view_column(children).into() - } - fn edit_history(&self) -> Element { let mut children = Vec::new(); @@ -922,7 +931,7 @@ impl App { let progress_bar_height = Length::Fixed(4.0); if !self.pending_operations.is_empty() { - let mut section = widget::settings::view_section(fl!("pending")); + let mut section = widget::settings::section().title(fl!("pending")); for (_id, (op, progress)) in self.pending_operations.iter().rev() { section = section.add(widget::column::with_children(vec![ widget::text(op.pending_text()).into(), @@ -935,7 +944,7 @@ impl App { } if !self.failed_operations.is_empty() { - let mut section = widget::settings::view_section(fl!("failed")); + let mut section = widget::settings::section().title(fl!("failed")); for (_id, (op, error)) in self.failed_operations.iter().rev() { section = section.add(widget::column::with_children(vec![ widget::text(op.pending_text()).into(), @@ -946,7 +955,7 @@ impl App { } if !self.complete_operations.is_empty() { - let mut section = widget::settings::view_section(fl!("complete")); + let mut section = widget::settings::section().title(fl!("complete")); for (_id, op) in self.complete_operations.iter().rev() { section = section.add(widget::text(op.completed_text())); } @@ -1001,133 +1010,27 @@ impl App { fn settings(&self) -> Element { // TODO: Should dialog be updated here too? - widget::settings::view_column(vec![ - widget::settings::view_section(fl!("appearance")) - .add({ - let app_theme_selected = match self.config.app_theme { - AppTheme::Dark => 1, - AppTheme::Light => 2, - AppTheme::System => 0, - }; - widget::settings::item::builder(fl!("theme")).control(widget::dropdown( - &self.app_themes, - Some(app_theme_selected), - move |index| { - Message::AppTheme(match index { - 1 => AppTheme::Dark, - 2 => AppTheme::Light, - _ => AppTheme::System, - }) - }, - )) - }) - .add({ - let tab_config = self.config.tab.clone(); - widget::settings::item::builder(fl!("default-view")).control(widget::dropdown( - &self.default_view, - match tab_config.view { - tab::View::Grid => Some(0), - tab::View::List => Some(1), - }, - move |index| { - Message::TabConfig(TabConfig { - view: match index { - 0 => tab::View::Grid, - _ => tab::View::List, - }, - ..tab_config - }) - }, - )) - }) - .add({ - let tab_config = self.config.tab.clone(); - let list: u16 = tab_config.icon_sizes.list.into(); - widget::settings::item::builder(fl!("icon-size-list")) - .description(format!("{}%", list)) - .control( - widget::slider(50..=500, list, move |list| { - Message::TabConfig(TabConfig { - icon_sizes: IconSizes { - list: NonZeroU16::new(list).unwrap(), - ..tab_config.icon_sizes - }, - ..tab_config - }) - }) - .step(25u16), - ) - }) - .add({ - let tab_config = self.config.tab.clone(); - let grid: u16 = tab_config.icon_sizes.grid.into(); - widget::settings::item::builder(fl!("icon-size-grid")) - .description(format!("{}%", grid)) - .control( - widget::slider(50..=500, grid, move |grid| { - Message::TabConfig(TabConfig { - icon_sizes: IconSizes { - grid: NonZeroU16::new(grid).unwrap(), - ..tab_config.icon_sizes - }, - ..tab_config - }) - }) - .step(25u16), - ) - }) - .add({ - let tab_config = self.config.tab; - let sort_by_selected = tab_config.sort_name as _; - - widget::settings::item::builder(fl!("sorting-name")).control(widget::dropdown( - &self.sort_by_names, - Some(sort_by_selected), - move |index| { - Message::TabConfig(TabConfig { - sort_name: match index { - 0 => HeadingOptions::Name, - 1 => HeadingOptions::Modified, - 2 => HeadingOptions::Size, - _ => HeadingOptions::Name, - }, - ..tab_config - }) - }, - )) - }) - .add({ - let tab_config = self.config.tab; - // Ascending is true. Descending is false - let direction = tab_config.sort_direction.into(); - - widget::settings::item::builder(fl!("direction")).control(widget::dropdown( - &self.sort_direction, - Some(direction), - move |index| { - Message::TabConfig(TabConfig { - sort_direction: index == 1, - ..tab_config - }) - }, - )) - }) - .into(), - widget::settings::view_section(fl!("settings-tab")) - .add({ - let tab_config = self.config.tab.clone(); - widget::settings::item::builder(fl!("settings-show-hidden")).toggler( - tab_config.show_hidden, - move |show_hidden| { - Message::TabConfig(TabConfig { - show_hidden, - ..tab_config - }) - }, - ) - }) - .into(), - ]) + widget::settings::view_column(vec![widget::settings::section() + .title(fl!("appearance")) + .add({ + let app_theme_selected = match self.config.app_theme { + AppTheme::Dark => 1, + AppTheme::Light => 2, + AppTheme::System => 0, + }; + widget::settings::item::builder(fl!("theme")).control(widget::dropdown( + &self.app_themes, + Some(app_theme_selected), + move |index| { + Message::AppTheme(match index { + 1 => AppTheme::Dark, + 2 => AppTheme::Light, + _ => AppTheme::System, + }) + }, + )) + }) + .into()]) .into() } } @@ -1158,7 +1061,9 @@ impl Application for App { fn init(mut core: Core, flags: Self::Flags) -> (Self, Command) { core.window.context_is_overlay = false; match flags.mode { - Mode::App => {} + Mode::App => { + core.window.show_context = flags.config.show_details; + } Mode::Desktop => { core.window.content_container = false; core.window.show_window_menu = false; @@ -1172,6 +1077,11 @@ impl Application for App { let app_themes = vec![fl!("match-desktop"), fl!("dark"), fl!("light")]; + let key_binds = key_binds(&match flags.mode { + Mode::App => tab::Mode::App, + Mode::Desktop => tab::Mode::Desktop, + }); + let mut app = App { core, nav_bar_context_id: segmented_button::Entity::null(), @@ -1181,13 +1091,10 @@ impl Application for App { config: flags.config, mode: flags.mode, app_themes, - default_view: vec![fl!("grid-view"), fl!("list-view")], - sort_by_names: HeadingOptions::names(), - sort_direction: vec![fl!("descending"), fl!("ascending")], - context_page: ContextPage::Settings, + context_page: ContextPage::Preview(None, PreviewKind::Selected), dialog_pages: VecDeque::new(), dialog_text_input: widget::Id::unique(), - key_binds: key_binds(), + key_binds, modifiers: Modifiers::empty(), mounters: mounters(), mounter_items: HashMap::new(), @@ -1199,7 +1106,6 @@ impl Application for App { pending_operations: BTreeMap::new(), complete_operations: BTreeMap::new(), failed_operations: BTreeMap::new(), - preview_opt: None, search_active: false, search_id: widget::Id::unique(), search_input: String::new(), @@ -1348,6 +1254,19 @@ impl Application for App { Some(Message::WindowClose) } + fn on_context_drawer(&mut self) -> Command { + match self.context_page { + ContextPage::Preview(_, _) => { + // Persist state of preview page + if self.core.window.show_context != self.config.show_details { + return self.update(Message::ToggleShowDetails); + } + } + _ => {} + } + Command::none() + } + fn on_escape(&mut self) -> Command { let entity = self.tab_model.active(); @@ -1429,7 +1348,10 @@ impl Application for App { Message::AddToSidebar(entity_opt) => { let mut favorites = self.config.favorites.clone(); for path in self.selected_paths(entity_opt) { - favorites.push(Favorite::from_path(path)); + let favorite = Favorite::from_path(path); + if !favorites.iter().any(|f| f == &favorite) { + favorites.push(favorite); + } } config_set!(favorites, favorites); return self.update_config(); @@ -1458,8 +1380,10 @@ impl Application for App { Message::Config(config) => { if config != self.config { log::info!("update config"); - //TODO: update syntax theme by clearing tabs, only if needed + // Show details is preserved for existing instances + let show_details = self.config.show_details; self.config = config; + self.config.show_details = show_details; return self.update_config(); } } @@ -1476,6 +1400,17 @@ impl Application for App { Message::CloseToast(id) => { self.toasts.remove(id); } + Message::CosmicSettings(arg) => { + //TODO: use special settings URL scheme instead? + let mut command = process::Command::new("cosmic-settings"); + command.arg(arg); + match spawn_detached(&mut command) { + Ok(()) => {} + Err(err) => { + log::warn!("failed to run cosmic-settings {}: {}", arg, err) + } + } + } Message::DialogCancel => { self.dialog_pages.pop_front(); } @@ -1504,8 +1439,8 @@ impl Application for App { log::warn!("TODO: retry operation {}", id); } DialogPage::NetworkAuth { - mounter_key, - uri, + mounter_key: _, + uri: _, auth, auth_tx, } => { @@ -1518,9 +1453,9 @@ impl Application for App { ); } DialogPage::NetworkError { - mounter_key, + mounter_key: _, uri, - error, + error: _, } => { //TODO: re-use mounter_key? return Command::batch([ @@ -1536,6 +1471,41 @@ impl Application for App { Operation::NewFile { path } }); } + DialogPage::OpenWith { + path, + apps, + selected, + .. + } => { + if let Some(app) = apps.get(selected) { + if let Some(mut command) = app.command(Some(path.clone().into())) { + match spawn_detached(&mut command) { + Ok(()) => { + let _ = recently_used_xbel::update_recently_used( + &path, + App::APP_ID.to_string(), + "cosmic-files".to_string(), + None, + ); + } + Err(err) => { + log::warn!( + "failed to open {:?} with {:?}: {}", + path, + app.id, + err + ) + } + } + } else { + log::warn!( + "failed to open {:?} with {:?}: failed to get command", + path, + app.id + ); + } + } + } DialogPage::RenameItem { from, parent, name, .. } => { @@ -1545,6 +1515,9 @@ impl Application for App { DialogPage::Replace { .. } => { log::warn!("replace dialog should be completed with replace result"); } + DialogPage::SetExecutableAndLaunch { path } => { + self.operation(Operation::SetExecutableAndLaunch { path }); + } } } } @@ -1562,21 +1535,6 @@ impl Application for App { self.update(Message::DialogComplete), ]); } - Message::EditLocation(entity_opt) => { - let entity = entity_opt.unwrap_or_else(|| self.tab_model.active()); - if let Some(location) = self.tab_model.data::(entity).and_then(|tab| { - if tab.edit_location.is_none() { - Some(tab.location.clone()) - } else { - None - } - }) { - return self.update(Message::TabMessage( - Some(entity), - tab::Message::EditLocation(Some(location)), - )); - } - } Message::ExtractHere(entity_opt) => { let paths = self.selected_paths(entity_opt); if let Some(current_path) = paths.get(0) { @@ -1863,28 +1821,6 @@ impl Application for App { } } } - Message::OpenWith(path, app) => { - if let Some(mut command) = app.command(Some(path.clone())) { - match spawn_detached(&mut command) { - Ok(()) => { - let _ = recently_used_xbel::update_recently_used( - &path, - App::APP_ID.to_string(), - "cosmic-files".to_string(), - None, - ); - } - Err(err) => { - log::warn!("failed to open {:?} with {:?}: {}", path, app.id, err) - } - } - } else { - log::warn!("failed to get command for {:?}", app.id); - } - - // Close Open With context view - self.set_show_context(false); - } Message::OpenInNewTab(entity_opt) => { return Command::batch(self.selected_paths(entity_opt).into_iter().filter_map( |path| { @@ -1926,6 +1862,68 @@ impl Application for App { }, )) } + Message::OpenWithBrowse => match self.dialog_pages.pop_front() { + Some(DialogPage::OpenWith { + mime, store_opt, .. + }) => { + if let Some(app) = store_opt { + let url = format!("mime:///{mime}"); + if let Some(mut command) = app.command(Some(url.clone().into())) { + match spawn_detached(&mut command) { + Ok(()) => {} + Err(err) => { + log::warn!( + "failed to open {:?} with {:?}: {}", + url, + app.id, + err + ) + } + } + } else { + log::warn!( + "failed to open {:?} with {:?}: failed to get command", + url, + app.id + ); + } + } + } + Some(dialog_page) => { + self.dialog_pages.push_front(dialog_page); + } + None => {} + }, + Message::OpenWithDialog(entity_opt) => { + let entity = entity_opt.unwrap_or_else(|| self.tab_model.active()); + if let Some(tab) = self.tab_model.data::(entity) { + if let Some(items) = tab.items_opt() { + for item in items { + if !item.selected { + continue; + } + let Some(path) = item.path_opt() else { + continue; + }; + return self.update(Message::DialogPush(DialogPage::OpenWith { + path: path.to_path_buf(), + mime: item.mime.clone(), + apps: item.open_with.clone(), + selected: 0, + store_opt: "x-scheme-handler/mime" + .parse::() + .ok() + .and_then(|mime| mime_app::mime_apps(&mime).first().cloned()), + })); + } + } + } + } + Message::OpenWithSelection(index) => { + if let Some(DialogPage::OpenWith { selected, .. }) = self.dialog_pages.front_mut() { + *selected = index; + } + } 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) { @@ -2031,17 +2029,6 @@ impl Application for App { } return self.update_notification(); } - Message::Preview(entity, kind, timeout) => { - if self - .preview_opt - .as_ref() - .is_some_and(|(e, k, i)| *e == entity && *k == kind && i.elapsed() > timeout) - { - self.context_page = ContextPage::Preview(Some(entity), kind); - self.set_show_context(true); - self.set_context_title(self.context_page.title()); - } - } Message::RescanTrash => { // Update trash icon if empty/full let maybe_entity = self.nav_model.iter().find(|&entity| { @@ -2246,23 +2233,23 @@ impl Application for App { } Message::TabConfig(config) => { if config != self.config.tab { - // Tabs are collected first to placate the borrowck - let tabs: Vec<_> = self.tab_model.iter().collect(); - // Update main conf and each tab with the new config - let commands: Vec<_> = std::iter::once(self.update_config()) - .chain(tabs.into_iter().map(|entity| { - let config = config.clone(); - self.update(Message::TabMessage( - Some(entity), - tab::Message::Config(config), - )) - })) - .collect(); - config_set!(tab, config); - return Command::batch(commands); + return self.update_config(); } } + Message::ToggleShowDetails => { + let show_details = !self.config.show_details; + //TODO: move to update_config? + self.context_page = ContextPage::Preview(None, PreviewKind::Selected); + self.core.window.show_context = show_details; + config_set!(show_details, show_details); + return self.update_config(); + } + Message::ToggleFoldersFirst => { + let mut config = self.config.tab; + config.folders_first = !config.folders_first; + return self.update(Message::TabConfig(config)); + } Message::TabMessage(entity_opt, tab_message) => { let entity = entity_opt.unwrap_or_else(|| self.tab_model.active()); @@ -2314,7 +2301,8 @@ impl Application for App { } tab::Command::OpenFile(path) => { let mut found_desktop_exec = false; - if mime_icon::mime_for_path(&path) == "application/x-desktop" { + let mime = mime_icon::mime_for_path(&path); + if mime == "application/x-desktop" { match freedesktop_entry_parser::parse_entry(&path) { Ok(entry) => { match entry.section("Desktop Entry").attr("Exec") { @@ -2347,7 +2335,28 @@ impl Application for App { Err(err) => { log::warn!("failed to parse {:?}: {}", path, err); } - }; + } + } else if mime == "application/x-executable" + || mime == "application/vnd.appimage" + { + let mut command = std::process::Command::new(&path); + match spawn_detached(&mut command) { + Ok(()) => {} + Err(err) => match err.kind() { + io::ErrorKind::PermissionDenied => { + // If permission is denied, try marking as executable, then running + self.dialog_pages.push_back( + DialogPage::SetExecutableAndLaunch { + path: path.clone(), + }, + ); + } + _ => { + log::warn!("failed to execute {:?}: {}", path, err); + } + }, + } + found_desktop_exec = true; } if !found_desktop_exec { match open::that_detached(&path) { @@ -2379,22 +2388,10 @@ impl Application for App { log::error!("failed to get current executable path: {}", err); } }, - tab::Command::Preview(kind, mut timeout) => { - self.preview_opt = Some((entity, kind.clone(), Instant::now())); - if self.core.window.show_context { - // If the context window is already open, immediately show the preview - timeout = time::Duration::new(0, 0) - }; - commands.push(Command::perform( - async move { - tokio::time::sleep(timeout).await; - message::app(Message::Preview(entity, kind, timeout)) - }, - |x| x, - )); - } - tab::Command::PreviewCancel => { - self.preview_opt = None; + tab::Command::Preview(kind) => { + self.context_page = ContextPage::Preview(Some(entity), kind); + self.set_show_context(true); + self.set_context_title(self.context_page.title()); } tab::Command::WindowDrag => { commands.push(window::drag(self.main_window_id())); @@ -2427,6 +2424,15 @@ impl Application for App { _ => (), } } + Message::TabView(entity_opt, view) => { + let entity = entity_opt.unwrap_or_else(|| self.tab_model.active()); + if let Some(tab) = self.tab_model.data_mut::(entity) { + tab.config.view = view; + } + let mut config = self.config.tab; + config.view = view; + return self.update(Message::TabConfig(config)); + } Message::ToggleContextPage(context_page) => { //TODO: ensure context menus are closed if self.context_page == context_page { @@ -2437,8 +2443,8 @@ impl Application for App { self.context_page = context_page; self.set_context_title(self.context_page.title()); } - Message::Undo(id) => { - // TODO; + Message::Undo(_id) => { + // TODO: undo } Message::UndoTrash(id, recently_trashed) => { self.toasts.remove(id); @@ -2495,6 +2501,65 @@ impl Application for App { log::error!("failed to get current executable path: {}", err); } }, + Message::ZoomDefault(entity_opt) => { + let entity = entity_opt.unwrap_or_else(|| self.tab_model.active()); + let mut config = self.config.tab; + if let Some(tab) = self.tab_model.data::(entity) { + match tab.config.view { + tab::View::List => config.icon_sizes.list = 100.try_into().unwrap(), + tab::View::Grid => config.icon_sizes.grid = 100.try_into().unwrap(), + } + } + return self.update(Message::TabConfig(config)); + } + Message::ZoomIn(entity_opt) => { + let entity = entity_opt.unwrap_or_else(|| self.tab_model.active()); + let zoom_in = |size: &mut NonZeroU16, min: u16, max: u16| { + let mut step = min; + while step <= max { + if size.get() < step { + *size = step.try_into().unwrap(); + break; + } + step += 25; + } + if size.get() > step { + *size = step.try_into().unwrap(); + } + }; + let mut config = self.config.tab; + if let Some(tab) = self.tab_model.data::(entity) { + match tab.config.view { + tab::View::List => zoom_in(&mut config.icon_sizes.list, 50, 500), + tab::View::Grid => zoom_in(&mut config.icon_sizes.grid, 50, 500), + } + } + return self.update(Message::TabConfig(config)); + } + Message::ZoomOut(entity_opt) => { + let entity = entity_opt.unwrap_or_else(|| self.tab_model.active()); + let zoom_out = |size: &mut NonZeroU16, min: u16, max: u16| { + let mut step = max; + while step >= min { + if size.get() > step { + *size = step.try_into().unwrap(); + break; + } + step -= 25; + } + if size.get() < step { + *size = step.try_into().unwrap(); + } + }; + let mut config = self.config.tab; + if let Some(tab) = self.tab_model.data::(entity) { + match tab.config.view { + tab::View::List => zoom_out(&mut config.icon_sizes.list, 50, 500), + tab::View::Grid => zoom_out(&mut config.icon_sizes.grid, 50, 500), + } + } + return self.update(Message::TabConfig(config)); + } Message::DndEnterNav(entity) => { if let Some(location) = self.nav_model.data::(entity) { self.nav_dnd_hover = Some((location.clone(), Instant::now())); @@ -2775,7 +2840,6 @@ impl Application for App { ContextPage::About => self.about(), ContextPage::EditHistory => self.edit_history(), ContextPage::NetworkDrive => self.network_drive(), - ContextPage::OpenWith => self.open_with(), ContextPage::Preview(entity_opt, kind) => self.preview(entity_opt, kind), ContextPage::Settings => self.settings(), }) @@ -3009,8 +3073,8 @@ impl Application for App { )) } DialogPage::NetworkError { - mounter_key, - uri, + mounter_key: _, + uri: _, error, } => widget::dialog(fl!("network-drive-error")) .body(error) @@ -3089,6 +3153,67 @@ impl Application for App { .spacing(space_xxs), ) } + DialogPage::OpenWith { + path, + apps, + selected, + store_opt, + .. + } => { + let name = match path.file_name() { + Some(file_name) => file_name.to_str(), + None => path.as_os_str().to_str(), + }; + + let mut column = widget::list_column(); + for (i, app) in apps.iter().enumerate() { + column = column.add( + widget::button::custom( + widget::row::with_children(vec![ + widget::icon(app.icon.clone()).size(32).into(), + if app.is_default { + widget::text(fl!("default-app", name = app.name.as_str())) + .into() + } else { + widget::text(app.name.to_string()).into() + }, + widget::horizontal_space(Length::Fill).into(), + if *selected == i { + widget::icon::from_name("checkbox-checked-symbolic") + .size(16) + .into() + } else { + widget::horizontal_space(Length::Fixed(16.0)).into() + }, + ]) + .spacing(space_s) + .height(Length::Fixed(32.0)) + .align_items(Alignment::Center), + ) + .width(Length::Fill) + .style(theme::Button::MenuItem) + .on_press(Message::OpenWithSelection(i)), + ); + } + + let mut dialog = widget::dialog(fl!("open-with-title", name = name)) + .primary_action( + widget::button::suggested(fl!("open")).on_press(Message::DialogComplete), + ) + .secondary_action( + widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel), + ) + .control(column); + + if let Some(app) = store_opt { + dialog = dialog.control( + widget::button::link(fl!("browse-store", store = app.name.as_str())) + .on_press(Message::OpenWithBrowse), + ); + } + + dialog + } DialogPage::RenameItem { from, parent, @@ -3214,13 +3339,39 @@ impl Application for App { ) } } + DialogPage::SetExecutableAndLaunch { path } => { + let name = match path.file_name() { + Some(file_name) => file_name.to_str(), + None => path.as_os_str().to_str(), + }; + widget::dialog(fl!("set-executable-and-launch")) + .primary_action( + widget::button::text(fl!("set-and-launch")) + .style(theme::Button::Suggested) + .on_press(Message::DialogComplete), + ) + .secondary_action( + widget::button::text(fl!("cancel")) + .style(theme::Button::Standard) + .on_press(Message::DialogCancel), + ) + .control(widget::text::text(fl!( + "set-executable-and-launch-description", + name = name + ))) + } }; Some(dialog.into()) } fn header_start(&self) -> Vec> { - vec![menu::menu_bar(self.tab_model.active_data::(), &self.key_binds).into()] + vec![menu::menu_bar( + self.tab_model.active_data::(), + &self.config, + &self.key_binds, + ) + .into()] } fn header_end(&self) -> Vec> { @@ -3311,7 +3462,7 @@ impl Application for App { content } - fn view_window(&self, id: WindowId) -> Element { + fn view_window(&self, _id: WindowId) -> Element { //TODO: distinct views per window? self.view_main().map(|message| match message { app::Message::App(app) => app, @@ -3848,19 +3999,6 @@ pub(crate) mod test_utils { ); } - pub fn assert_zoom_affects_item_size(tab: &mut Tab, message: tab::Message, should_zoom: bool) { - let grid_icon_size = tab.config.icon_sizes.grid; - let list_icon_size = tab.config.icon_sizes.list; - - debug!("Emitting {:?}", message); - tab.update(message, Modifiers::empty()); - - let grid_size_changed = grid_icon_size != tab.config.icon_sizes.grid; - let list_size_changed = list_icon_size != tab.config.icon_sizes.list; - - assert_eq!(grid_size_changed || list_size_changed, should_zoom); - } - /// Assert that tab's items are equal to a path's entries. pub fn assert_eq_tab_path_contents(tab: &Tab, path: &Path) { let Location::Path(ref tab_path) = tab.location else { diff --git a/src/config.rs b/src/config.rs index e75569b..b2da776 100644 --- a/src/config.rs +++ b/src/config.rs @@ -9,10 +9,7 @@ use cosmic::{ }; use serde::{Deserialize, Serialize}; -use crate::{ - app::App, - tab::{HeadingOptions, View}, -}; +use crate::{app::App, tab::View}; pub const CONFIG_VERSION: u64 = 1; @@ -96,6 +93,7 @@ impl Favorite { pub struct Config { pub app_theme: AppTheme, pub favorites: Vec, + pub show_details: bool, pub tab: TabConfig, } @@ -141,6 +139,7 @@ impl Default for Config { Favorite::Pictures, Favorite::Videos, ], + show_details: false, tab: TabConfig::default(), } } @@ -158,9 +157,6 @@ pub struct TabConfig { pub folders_first: bool, /// Show hidden files and folders pub show_hidden: bool, - /// Sorter - pub sort_name: HeadingOptions, - pub sort_direction: bool, /// Icon zoom pub icon_sizes: IconSizes, } @@ -171,8 +167,6 @@ impl Default for TabConfig { view: View::List, folders_first: true, show_hidden: false, - sort_name: HeadingOptions::Name, - sort_direction: true, icon_sizes: IconSizes::default(), } } diff --git a/src/dialog.rs b/src/dialog.rs index 7564e4f..e719918 100644 --- a/src/dialog.rs +++ b/src/dialog.rs @@ -11,7 +11,7 @@ use cosmic::{ iced::{ event, futures::{self, SinkExt}, - keyboard::{Event as KeyEvent, Modifiers}, + keyboard::{Event as KeyEvent, Key, Modifiers}, subscription::{self, Subscription}, window, Alignment, Event, Length, Size, }, @@ -42,6 +42,7 @@ use crate::{ app::{Action, ContextPage, Message as AppMessage, PreviewItem, PreviewKind}, config::{Config, Favorite, IconSizes, TabConfig}, fl, home_dir, + key_bind::key_binds, localize::LANGUAGE_SORTER, menu, mounter::{mounters, MounterItem, MounterItems, MounterKey, MounterMessage, Mounters}, @@ -309,13 +310,13 @@ enum Message { DialogUpdate(DialogPage), Filename(String), Filter(usize), + Key(Modifiers, Key), Modifiers(Modifiers), MounterItems(MounterKey, MounterItems), NewFolder, NotifyEvents(Vec), NotifyWatcher(WatcherWrapper), Open, - Preview(PreviewKind, time::Duration), Save(bool), SearchActivate, SearchClear, @@ -323,12 +324,15 @@ enum Message { SearchSubmit, TabMessage(tab::Message), TabRescan(Vec), + ToggleShowDetails, } impl From for Message { fn from(app_message: AppMessage) -> Message { match app_message { + AppMessage::SearchActivate => Message::SearchActivate, AppMessage::TabMessage(_entity_opt, tab_message) => Message::TabMessage(tab_message), + AppMessage::ToggleShowDetails => Message::ToggleShowDetails, unsupported => { log::warn!("{unsupported:?} not supported in dialog mode"); Message::None @@ -378,7 +382,6 @@ struct App { mounters: Mounters, mounter_items: HashMap, nav_model: segmented_button::SingleSelectModel, - preview_opt: Option<(PreviewKind, time::Instant)>, result_opt: Option, search_active: bool, search_id: widget::Id, @@ -683,6 +686,7 @@ impl Application for App { core.window.show_close = false; core.window.show_maximize = false; core.window.show_minimize = false; + core.window.show_context = true; let title = flags.kind.title(); let accept_label = flags.kind.accept_label(); @@ -698,12 +702,14 @@ impl Application for App { let tab_config = TabConfig { view: tab::View::List, folders_first: false, - sort_name: tab::HeadingOptions::Modified, - sort_direction: false, ..Default::default() }; let mut tab = Tab::new(location, tab_config); tab.mode = tab::Mode::Dialog(flags.kind.clone()); + tab.sort_name = tab::HeadingOptions::Modified; + tab.sort_direction = false; + + let key_binds = key_binds(&tab.mode); let mut app = App { core, @@ -711,7 +717,7 @@ impl Application for App { title, accept_label, choices: Vec::new(), - context_page: ContextPage::Settings, + context_page: ContextPage::Preview(None, PreviewKind::Selected), dialog_pages: VecDeque::new(), dialog_text_input: widget::Id::unique(), filters: Vec::new(), @@ -721,13 +727,12 @@ impl Application for App { mounters: mounters(), mounter_items: HashMap::new(), nav_model: segmented_button::ModelBuilder::default().build(), - preview_opt: None, result_opt: None, search_active: false, search_id: widget::Id::unique(), search_input: String::new(), tab, - key_binds: HashMap::new(), + key_binds, watcher_opt: None, }; @@ -1058,6 +1063,13 @@ impl Application for App { } return self.rescan_tab(); } + Message::Key(modifiers, key) => { + for (key_bind, action) in self.key_binds.iter() { + if key_bind.matches(modifiers, &key) { + return self.update(Message::from(action.message())); + } + } + } Message::Modifiers(modifiers) => { self.modifiers = modifiers; } @@ -1233,17 +1245,6 @@ impl Application for App { } } } - Message::Preview(kind, timeout) => { - if self - .preview_opt - .as_ref() - .is_some_and(|(k, i)| *k == kind && i.elapsed() > timeout) - { - self.context_page = ContextPage::Preview(None, kind); - self.set_show_context(true); - self.set_context_title(self.context_page.title()); - } - } Message::Save(replace) => { if let DialogKind::SaveFile { filename } = &self.flags.kind { if !filename.is_empty() { @@ -1341,22 +1342,10 @@ impl Application for App { commands.push(self.update(Message::Open)); } } - tab::Command::Preview(kind, mut timeout) => { - self.preview_opt = Some((kind.clone(), time::Instant::now())); - if self.core.window.show_context { - // If the context window is already open, immediately show the preview - timeout = time::Duration::new(0, 0) - }; - commands.push(Command::perform( - async move { - tokio::time::sleep(timeout).await; - message::app(Message::Preview(kind, timeout)) - }, - |x| x, - )); - } - tab::Command::PreviewCancel => { - self.preview_opt = None; + tab::Command::Preview(kind) => { + self.context_page = ContextPage::Preview(None, kind); + self.set_show_context(true); + self.set_context_title(self.context_page.title()); } tab::Command::WindowDrag => { commands.push(window::drag(self.main_window_id())); @@ -1437,6 +1426,15 @@ impl Application for App { // Reset focus on location change return widget::text_input::focus(self.filename_id.clone()); } + Message::ToggleShowDetails => match self.context_page { + ContextPage::Preview(_, _) => { + self.core.window.show_context = !self.core.window.show_context; + } + _ => { + self.context_page = ContextPage::Preview(None, PreviewKind::Selected); + self.core.window.show_context = true; + } + }, } Command::none() @@ -1465,7 +1463,11 @@ impl Application for App { fn subscription(&self) -> Subscription { struct WatcherSubscription; let mut subscriptions = vec![ - event::listen_with(|event, _status| match event { + event::listen_with(|event, status| match event { + Event::Keyboard(KeyEvent::KeyPressed { key, modifiers, .. }) => match status { + event::Status::Ignored => Some(Message::Key(modifiers, key)), + event::Status::Captured => None, + }, Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => { Some(Message::Modifiers(modifiers)) } diff --git a/src/key_bind.rs b/src/key_bind.rs index efb9a75..a364b9f 100644 --- a/src/key_bind.rs +++ b/src/key_bind.rs @@ -5,10 +5,10 @@ use cosmic::{ }; use std::collections::HashMap; -use crate::app::Action; +use crate::{app::Action, tab}; //TODO: load from config -pub fn key_binds() -> HashMap { +pub fn key_binds(mode: &tab::Mode) -> HashMap { let mut key_binds = HashMap::new(); macro_rules! bind { @@ -23,46 +23,58 @@ pub fn key_binds() -> HashMap { }}; } - bind!([Ctrl], Key::Character("d".into()), AddToSidebar); - bind!([Ctrl], Key::Character("c".into()), Copy); - bind!([Ctrl], Key::Character("x".into()), Cut); - bind!([Ctrl], Key::Character("l".into()), EditLocation); - bind!([Alt], Key::Named(Named::ArrowRight), HistoryNext); - bind!([Alt], Key::Named(Named::ArrowLeft), HistoryPrevious); - bind!([], Key::Named(Named::Backspace), HistoryPrevious); - // Catch arrow keys + // Common keys + bind!([], Key::Named(Named::Space), Gallery); bind!([], Key::Named(Named::ArrowDown), ItemDown); bind!([], Key::Named(Named::ArrowLeft), ItemLeft); bind!([], Key::Named(Named::ArrowRight), ItemRight); bind!([], Key::Named(Named::ArrowUp), ItemUp); - // We also need to catch these when shift is held 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!([Alt], Key::Named(Named::ArrowUp), LocationUp); - bind!([], Key::Named(Named::Delete), MoveToTrash); bind!([Ctrl, Shift], Key::Character("n".into()), NewFolder); bind!([], Key::Named(Named::Enter), Open); - bind!([Ctrl], Key::Named(Named::Enter), OpenInNewTab); - bind!([Shift], Key::Named(Named::Enter), OpenInNewWindow); - bind!([Ctrl], Key::Character("v".into()), Paste); - bind!([], Key::Named(Named::Space), Preview); - bind!([], Key::Named(Named::F2), Rename); - bind!([Ctrl], Key::Character("f".into()), SearchActivate); - bind!([Ctrl], Key::Character("a".into()), SelectAll); - bind!([Ctrl], Key::Character(",".into()), Settings); - bind!([Ctrl], Key::Character("w".into()), TabClose); - bind!([Ctrl], Key::Character("t".into()), TabNew); - bind!([Ctrl], Key::Named(Named::Tab), TabNext); - bind!([Ctrl, Shift], Key::Named(Named::Tab), TabPrev); + bind!([Ctrl], Key::Named(Named::Space), Preview); bind!([Ctrl], Key::Character("h".into()), ToggleShowHidden); - bind!([Ctrl], Key::Character("q".into()), WindowClose); - bind!([Ctrl], Key::Character("n".into()), WindowNew); + bind!([Ctrl], Key::Character("a".into()), SelectAll); bind!([Ctrl], Key::Character("=".into()), ZoomIn); bind!([Ctrl], Key::Character("+".into()), ZoomIn); bind!([Ctrl], Key::Character("0".into()), ZoomDefault); bind!([Ctrl], Key::Character("-".into()), ZoomOut); + // App-only keys + if matches!(mode, tab::Mode::App) { + bind!([Ctrl], Key::Character("d".into()), AddToSidebar); + bind!([Ctrl], Key::Named(Named::Enter), OpenInNewTab); + bind!([Ctrl], Key::Character(",".into()), Settings); + bind!([Ctrl], Key::Character("w".into()), TabClose); + bind!([Ctrl], Key::Character("t".into()), TabNew); + bind!([Ctrl], Key::Named(Named::Tab), TabNext); + bind!([Ctrl, Shift], Key::Named(Named::Tab), TabPrev); + bind!([Ctrl], Key::Character("q".into()), WindowClose); + bind!([Ctrl], Key::Character("n".into()), WindowNew); + } + + // App and desktop only keys + if matches!(mode, tab::Mode::App | tab::Mode::Desktop) { + bind!([Ctrl], Key::Character("c".into()), Copy); + bind!([Ctrl], Key::Character("x".into()), Cut); + bind!([], Key::Named(Named::Delete), MoveToTrash); + bind!([Shift], Key::Named(Named::Enter), OpenInNewWindow); + bind!([Ctrl], Key::Character("v".into()), Paste); + bind!([], Key::Named(Named::F2), Rename); + } + + // App and dialog only keys + if matches!(mode, tab::Mode::App | tab::Mode::Dialog(_)) { + bind!([Ctrl], Key::Character("l".into()), EditLocation); + bind!([Alt], Key::Named(Named::ArrowRight), HistoryNext); + bind!([Alt], Key::Named(Named::ArrowLeft), HistoryPrevious); + bind!([], Key::Named(Named::Backspace), HistoryPrevious); + bind!([Alt], Key::Named(Named::ArrowUp), LocationUp); + bind!([Ctrl], Key::Character("f".into()), SearchActivate); + } + key_binds } diff --git a/src/lib.rs b/src/lib.rs index 37183ca..1e7309f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,7 @@ // Copyright 2023 System76 // SPDX-License-Identifier: GPL-3.0-only -use cosmic::{ - app::{Application, Settings}, - iced::Limits, -}; +use cosmic::{app::Settings, iced::Limits}; use std::{env, fs, path::PathBuf, process}; use app::{App, Flags}; diff --git a/src/menu.rs b/src/menu.rs index 46c54f8..4a2551e 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -15,7 +15,7 @@ use std::collections::HashMap; use crate::{ app::{Action, Message}, - config::TabConfig, + config::Config, fl, tab::{self, HeadingOptions, Location, LocationMenuAction, Tab}, }; @@ -58,17 +58,12 @@ pub fn context_menu<'a>( .on_press(tab::Message::ContextAction(action)) }; - let TabConfig { - sort_name, - sort_direction, - .. - } = tab.config; let sort_item = |label, variant| { menu_item( format!( "{} {}", label, - match (sort_name == variant, sort_direction) { + match (tab.sort_name == variant, tab.sort_direction) { (true, true) => "\u{2B07}", (true, false) => "\u{2B06}", _ => "", @@ -119,7 +114,7 @@ pub fn context_menu<'a>( ); } // All selected items are directories - if selected == selected_dir { + if selected == selected_dir && matches!(tab.mode, tab::Mode::App) { children.push(menu_item(fl!("open-in-new-tab"), Action::OpenInNewTab).into()); children .push(menu_item(fl!("open-in-new-window"), Action::OpenInNewWindow).into()); @@ -156,8 +151,10 @@ pub fn context_menu<'a>( //TODO: Print? children.push(menu_item(fl!("show-details"), Action::Preview).into()); - children.push(divider::horizontal::light().into()); - children.push(menu_item(fl!("add-to-sidebar"), Action::AddToSidebar).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()); children.push(menu_item(fl!("move-to-trash"), Action::MoveToTrash).into()); } else { @@ -171,6 +168,22 @@ pub fn context_menu<'a>( children.push(menu_item(fl!("select-all"), Action::SelectAll).into()); } children.push(menu_item(fl!("paste"), Action::Paste).into()); + + //TODO: only show if cosmic-settings is found? + if matches!(tab.mode, tab::Mode::Desktop) { + children.push(divider::horizontal::light().into()); + children.push( + menu_item(fl!("change-wallpaper"), Action::CosmicSettingsWallpaper).into(), + ); + children.push( + menu_item(fl!("desktop-appearance"), Action::CosmicSettingsAppearance) + .into(), + ); + children.push( + menu_item(fl!("display-settings"), Action::CosmicSettingsDisplays).into(), + ); + } + children.push(divider::horizontal::light().into()); // TODO: Nested menu children.push(sort_item(fl!("sort-by-name"), HeadingOptions::Name)); @@ -191,6 +204,8 @@ pub fn context_menu<'a>( menu_item(fl!("open-item-location"), Action::OpenItemLocation).into(), ); } + 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).into()); @@ -273,7 +288,7 @@ pub fn dialog_menu<'a>( let sort_item = |label, sort, dir| { menu::Item::CheckBox( label, - tab.config.sort_name == sort && tab.config.sort_direction == dir, + tab.sort_name == sort && tab.sort_direction == dir, Action::SetSort(sort, dir), ) }; @@ -303,7 +318,7 @@ pub fn dialog_menu<'a>( ), ), menu::Tree::with_children( - widget::button::icon(widget::icon::from_name(if tab.config.sort_direction { + widget::button::icon(widget::icon::from_name(if tab.sort_direction { "view-sort-ascending-symbolic" } else { "view-sort-descending-symbolic" @@ -355,13 +370,14 @@ pub fn dialog_menu<'a>( pub fn menu_bar<'a>( tab_opt: Option<&Tab>, + config: &Config, key_binds: &HashMap, ) -> Element<'a, Message> { let sort_item = |label, sort, dir| { menu::Item::CheckBox( label, tab_opt.map_or(false, |tab| { - tab.config.sort_name == sort && tab.config.sort_direction == dir + tab.sort_name == sort && tab.sort_direction == dir }), Action::SetSort(sort, dir), ) @@ -412,8 +428,6 @@ pub fn menu_bar<'a>( menu::Item::Divider, menu_button_optional(fl!("rename"), Action::Rename, selected > 0), menu::Item::Divider, - menu_button_optional(fl!("menu-show-details"), Action::Preview, selected > 0), - menu::Item::Divider, menu_button_optional(fl!("add-to-sidebar"), Action::AddToSidebar, selected > 0), menu::Item::Divider, menu_button_optional(fl!("move-to-trash"), Action::MoveToTrash, selected > 0), @@ -467,6 +481,9 @@ pub fn menu_bar<'a>( tab_opt.map_or(false, |tab| tab.config.folders_first), Action::ToggleFoldersFirst, ), + menu::Item::CheckBox(fl!("show-details"), config.show_details, Action::Preview), + menu::Item::Divider, + menu_button_optional(fl!("gallery-preview"), Action::Gallery, selected > 0), menu::Item::Divider, menu::Item::Button(fl!("menu-settings"), Action::Settings), menu::Item::Divider, diff --git a/src/mime_app.rs b/src/mime_app.rs index 268152f..9e11011 100644 --- a/src/mime_app.rs +++ b/src/mime_app.rs @@ -7,10 +7,11 @@ use cosmic::widget; pub use mime_guess::Mime; use once_cell::sync::Lazy; use std::{ - cmp::Ordering, collections::HashMap, env, path::PathBuf, process, sync::Mutex, time::Instant, + cmp::Ordering, collections::HashMap, env, ffi::OsString, path::PathBuf, process, sync::Mutex, + time::Instant, }; -pub fn exec_to_command(exec: &str, path_opt: Option) -> Option { +pub fn exec_to_command(exec: &str, path_opt: Option) -> Option { let args_vec: Vec = shlex::split(exec)?; let mut args = args_vec.iter(); let mut command = process::Command::new(args.next()?); @@ -46,7 +47,7 @@ pub struct MimeApp { impl MimeApp { //TODO: move to libcosmic, support multiple files - pub fn command(&self, path_opt: Option) -> Option { + pub fn command(&self, path_opt: Option) -> Option { exec_to_command(self.exec.as_deref()?, path_opt) } } @@ -60,7 +61,9 @@ impl From<&desktop::DesktopEntryData> for MimeApp { name: app.name.clone(), exec: app.exec.clone(), icon: match &app.icon { - desktop::IconSource::Name(name) => widget::icon::from_name(name.as_str()).handle(), + desktop::IconSource::Name(name) => { + widget::icon::from_name(name.as_str()).size(32).handle() + } desktop::IconSource::Path(path) => widget::icon::from_path(path.clone()), }, is_default: false, diff --git a/src/mouse_area.rs b/src/mouse_area.rs index b9b86c1..3d2ecb8 100644 --- a/src/mouse_area.rs +++ b/src/mouse_area.rs @@ -13,7 +13,7 @@ use cosmic::{ Modifiers, }, layout, - mouse::{self, click, Event as MouseEvent}, + mouse::{self, click}, overlay, renderer::{self, Quad, Renderer as _}, touch, @@ -614,13 +614,11 @@ fn update( } } - if let Some(message) = widget.on_scroll.as_ref() { + if let Some(on_scroll) = widget.on_scroll.as_ref() { if let Event::Mouse(mouse::Event::WheelScrolled { delta }) = event { - if let Some(on_scroll) = widget.on_scroll.as_ref() { - if let Some(message) = on_scroll(delta.clone(), state.modifiers) { - shell.publish(message); - return event::Status::Captured; - } + if let Some(message) = on_scroll(delta.clone(), state.modifiers) { + shell.publish(message); + return event::Status::Captured; } } } diff --git a/src/operation.rs b/src/operation.rs index bb59e1a..2226f12 100644 --- a/src/operation.rs +++ b/src/operation.rs @@ -4,10 +4,7 @@ use std::{ fs, io::{self, Read, Write}, path::{Path, PathBuf}, - sync::{ - atomic::{self, AtomicU64}, - Arc, - }, + sync::Arc, }; use tokio::sync::{mpsc, Mutex}; use walkdir::WalkDir; @@ -17,6 +14,7 @@ use crate::{ config::IconSizes, err_str, fl, mime_icon::mime_for_path, + spawn_detached::spawn_detached, tab, }; @@ -179,6 +177,10 @@ pub enum Operation { Restore { paths: Vec, }, + /// Set executable and launch + SetExecutableAndLaunch { + path: PathBuf, + }, } async fn copy_or_move( @@ -329,56 +331,71 @@ async fn copy_or_move( } fn copy_unique_path(from: &Path, to: &Path) -> PathBuf { + // List of compound extensions to check + const COMPOUND_EXTENSIONS: &[&str] = &[ + ".tar.gz", + ".tar.bz2", + ".tar.xz", + ".tar.zst", + ".tar.lz", + ".tar.lzma", + ".tar.sz", + ".tar.lzo", + ".tar.br", + ".tar.Z", + ".tar.pz", + ]; + let mut to = to.to_owned(); if let Some(file_name) = from.file_name().and_then(|name| name.to_str()) { - let is_dir = from.is_dir(); - let (stem, ext) = if !is_dir { - match from.extension().and_then(|e| e.to_str()) { - Some(ext) => { - let stem = from - .file_stem() - .and_then(|s| s.to_str()) - .unwrap_or(file_name); - (stem.to_string(), Some(ext.to_string())) - } - None => (file_name.to_string(), None), - } - } else { + let (stem, ext) = if from.is_dir() { (file_name.to_string(), None) + } else { + let file_name = file_name.to_string(); + COMPOUND_EXTENSIONS + .iter() + .find(|&&ext| file_name.ends_with(ext)) + .map(|&ext| { + ( + file_name.strip_suffix(ext).unwrap().to_string(), + Some(ext[1..].to_string()), + ) + }) + .unwrap_or_else(|| { + from.file_stem() + .and_then(|s| s.to_str()) + .map(|stem| { + ( + stem.to_string(), + from.extension() + .and_then(|e| e.to_str()) + .map(|e| e.to_string()), + ) + }) + .unwrap_or((file_name, None)) + }) }; - let mut n = 0u32; - loop { + for n in 0.. { let new_name = if n == 0 { file_name.to_string() } else { - if is_dir { - format!("{} ({} {})", file_name, fl!("copy_noun"), n) - } else { - match &ext { - Some(ext) => format!("{} ({} {}).{}", stem, fl!("copy_noun"), n, ext), - None => format!("{} ({} {})", stem, fl!("copy_noun"), n), - } + match ext { + Some(ref ext) => format!("{} ({} {}).{}", stem, fl!("copy_noun"), n, ext), + None => format!("{} ({} {})", stem, fl!("copy_noun"), n), } }; - to = to.join(new_name); + to = to.join(&new_name); if !matches!(to.try_exists(), Ok(true)) { - break to; + break; } // Continue if a copy with index exists to.pop(); - - n = if let Some(n) = n.checked_add(1) { - n - } else { - break to; - }; } - } else { - to } + to } fn file_name<'a>(path: &'a Path) -> Cow<'a, str> { @@ -461,6 +478,9 @@ impl Operation { fl!("renaming", from = file_name(from), to = file_name(to)) } Self::Restore { paths } => fl!("restoring", items = paths.len()), + Self::SetExecutableAndLaunch { path } => { + fl!("setting-executable-and-launching", name = file_name(path)) + } } } @@ -509,6 +529,9 @@ impl Operation { ), Self::Rename { from, to } => fl!("renamed", from = file_name(from), to = file_name(to)), Self::Restore { paths } => fl!("restored", items = paths.len()), + Self::SetExecutableAndLaunch { path } => { + fl!("set-executable-and-launched", name = file_name(path)) + } } } @@ -829,6 +852,33 @@ impl Operation { .await; } } + Self::SetExecutableAndLaunch { path } => { + tokio::task::spawn_blocking(move || -> io::Result<()> { + //TODO: what to do on non-Unix systems? + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let mut perms = fs::metadata(&path)?.permissions(); + let current_mode = perms.mode(); + let new_mode = current_mode | 0o111; + perms.set_mode(new_mode); + fs::set_permissions(&path, perms)?; + } + + let mut command = std::process::Command::new(path); + spawn_detached(&mut command)?; + + Ok(()) + }) + .await + .map_err(err_str)? + .map_err(err_str)?; + let _ = msg_tx + .lock() + .await + .send(Message::PendingProgress(id, 100.0)) + .await; + } } let _ = msg_tx diff --git a/src/tab.rs b/src/tab.rs index fd78a09..9aa3ae0 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -9,7 +9,7 @@ use cosmic::{ clipboard::dnd::DndAction, event, futures::SinkExt, - keyboard::{self, Modifiers}, + keyboard::Modifiers, subscription::{self, Subscription}, //TODO: export in cosmic::widget widget::{ @@ -39,7 +39,6 @@ use cosmic::{ use chrono::{DateTime, Utc}; use mime_guess::{mime, Mime}; use once_cell::sync::Lazy; -use recently_used_xbel::{Error, RecentlyUsed}; use serde::{Deserialize, Serialize}; use std::{ borrow::Cow, @@ -48,10 +47,8 @@ use std::{ collections::HashMap, fmt::{self, Display}, fs::{self, Metadata}, - num::NonZeroU16, os::unix::fs::MetadataExt, path::{Path, PathBuf}, - process, sync::{Arc, Mutex}, time::{Duration, Instant, SystemTime}, }; @@ -548,7 +545,7 @@ pub fn scan_search(tab_path: &PathBuf, term: &str, sizes: IconSizes) -> Vec Vec { } pub fn scan_network(uri: &str, mounters: Mounters, sizes: IconSizes) -> Vec { - for (key, mounter) in mounters.iter() { + for (_key, mounter) in mounters.iter() { match mounter.network_scan(uri, sizes) { Some(Ok(items)) => return items, Some(Err(err)) => { @@ -824,8 +821,7 @@ pub enum Command { OpenFile(PathBuf), OpenInNewTab(PathBuf), OpenInNewWindow(PathBuf), - Preview(PreviewKind, Duration), - PreviewCancel, + Preview(PreviewKind), WindowDrag, WindowToggleMaximize, } @@ -845,11 +841,13 @@ pub enum Message { LocationMenuAction(LocationMenuAction), Drag(Option), EditLocation(Option), + EditLocationToggle, OpenInNewTab(PathBuf), EmptyTrash, Gallery(bool), GalleryPrevious, GalleryNext, + GalleryToggle, GoNext, GoPrevious, ItemDown, @@ -866,7 +864,6 @@ pub enum Message { SelectAll, SetSort(HeadingOptions, bool), Thumbnail(PathBuf, ItemThumbnail), - ToggleFoldersFirst, ToggleShowHidden, View(View), ToggleSort(HeadingOptions), @@ -876,7 +873,6 @@ pub enum Message { DndLeave(Location), WindowDrag, WindowToggleMaximize, - ZoomDefault, ZoomIn, ZoomOut, } @@ -1086,50 +1082,6 @@ impl Item { } } - pub fn open_with_view(&self, sizes: IconSizes) -> Element { - let cosmic_theme::Spacing { - space_xs, - space_xxxs, - .. - } = theme::active().cosmic().spacing; - - let mut column = widget::column().spacing(space_xxxs); - - column = column.push(widget::row::with_children(vec![ - widget::horizontal_space(Length::Fill).into(), - self.preview(sizes), - widget::horizontal_space(Length::Fill).into(), - ])); - - column = column.push(widget::text::heading(&self.name)); - - column = column.push(widget::text(format!("Type: {}", self.mime))); - - if let Some(Location::Path(path)) = &self.location_opt { - for app in self.open_with.iter() { - column = column.push( - widget::button::custom( - widget::row::with_children(vec![ - widget::icon(app.icon.clone()).into(), - if app.is_default { - widget::text(fl!("default-app", name = app.name.as_str())).into() - } else { - widget::text(&app.name).into() - }, - ]) - .spacing(space_xs), - ) - //TODO: do not clone so much? - .on_press(app::Message::OpenWith(path.clone(), app.clone())) - .padding(space_xs) - .width(Length::Fill), - ); - } - } - - column.into() - } - pub fn preview_view(&self, sizes: IconSizes) -> Element<'static, app::Message> { let cosmic_theme::Spacing { space_xxxs, @@ -1151,7 +1103,7 @@ impl Item { ); if self.mime.type_() == mime::IMAGE { - if let Some(path) = self.path_opt() { + if let Some(_path) = self.path_opt() { row = row.push( widget::button::icon(widget::icon::from_name("view-fullscreen-symbolic")) .on_press(app::Message::TabMessage(None, Message::Gallery(true))), @@ -1373,6 +1325,8 @@ pub struct Tab { pub history_i: usize, pub history: Vec, pub config: TabConfig, + pub sort_name: HeadingOptions, + pub sort_direction: bool, pub gallery: bool, pub(crate) items_opt: Option>, pub dnd_hovered: Option<(Location, Instant)>, @@ -1421,6 +1375,8 @@ impl Tab { history_i: 0, history, config, + sort_name: HeadingOptions::Name, + sort_direction: true, gallery: false, items_opt: None, scrollable_id: widget::Id::unique(), @@ -1752,7 +1708,6 @@ 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_select_focus = self.select_focus; match message { Message::AddNetworkDrive => { commands.push(Command::AddNetworkDrive); @@ -1806,9 +1761,6 @@ impl Tab { } else { log::warn!("no item for click index {:?}", click_i_opt); } - - // Cancel any preview timers - commands.push(Command::PreviewCancel); } Message::Click(click_i_opt) => { self.selected_clicked = false; @@ -1826,9 +1778,7 @@ impl Tab { if let Some(range) = self.select_range { let min = range.0.min(range.1); let max = range.0.max(range.1); - if self.config.sort_name == HeadingOptions::Name - && self.config.sort_direction - { + if self.sort_name == HeadingOptions::Name && self.sort_direction { // A default/unsorted tab's view is consistent with how the // Items are laid out internally (items_opt), so Items can be // linearly selected @@ -1935,7 +1885,10 @@ impl Tab { } } Message::Config(config) => { + // View is preserved for existing tabs + let view = self.config.view; self.config = config; + self.config.view = view; } Message::ContextAction(action) => { // Close context menu @@ -1989,11 +1942,9 @@ impl Tab { //TODO: blocking code, run in command match item_from_path(&path, IconSizes::default()) { Ok(item) => { - // Show preview instantly - commands.push(Command::Preview( - PreviewKind::Custom(PreviewItem(item)), - Duration::new(0, 0), - )); + commands.push(Command::Preview(PreviewKind::Custom( + PreviewItem(item), + ))); } Err(err) => { log::warn!("failed to get item from path {:?}: {}", path, err); @@ -2023,6 +1974,13 @@ impl Tab { } self.edit_location = edit_location; } + Message::EditLocationToggle => { + if self.edit_location.is_none() { + self.edit_location = Some(self.location.clone()); + } else { + self.edit_location = None; + } + } Message::OpenInNewTab(path) => { commands.push(Command::OpenInNewTab(path)); } @@ -2071,6 +2029,16 @@ impl Tab { commands.push(Command::Iced(widget::button::focus(id))); } } + Message::GalleryToggle => { + if let Some(indices) = self.column_sort() { + for (_, item) in indices.iter() { + if item.selected && item.mime.type_() == mime::IMAGE { + self.gallery = !self.gallery; + break; + } + } + } + } Message::GoNext => { if let Some(history_i) = self.history_i.checked_add(1) { if let Some(location) = self.history.get(history_i) { @@ -2344,8 +2312,8 @@ impl Tab { } } Message::SetSort(heading_option, dir) => { - self.config.sort_name = heading_option; - self.config.sort_direction = dir; + self.sort_name = heading_option; + self.sort_direction = dir; } Message::Thumbnail(path, thumbnail) => { if let Some(ref mut items) = self.items_opt { @@ -2378,21 +2346,20 @@ impl Tab { } } } - Message::ToggleFoldersFirst => self.config.folders_first = !self.config.folders_first, Message::ToggleShowHidden => self.config.show_hidden = !self.config.show_hidden, Message::View(view) => { self.config.view = view; } Message::ToggleSort(heading_option) => { - let heading_sort = if self.config.sort_name == heading_option { - !self.config.sort_direction + let heading_sort = if self.sort_name == heading_option { + !self.sort_direction } else { // Default modified to descending, and others to ascending. heading_option != HeadingOptions::Modified }; - self.config.sort_direction = heading_sort; - self.config.sort_name = heading_option; + self.sort_direction = heading_sort; + self.sort_name = heading_option; } Message::Drop(Some((to, mut from))) => { self.dnd_hovered = None; @@ -2441,9 +2408,6 @@ impl Tab { |x| x, ))); } - - // Clear preview timer - commands.push(Command::PreviewCancel); } Message::DndLeave(loc) => { if Some(&loc) == self.dnd_hovered.as_ref().map(|(l, _)| l) { @@ -2456,65 +2420,11 @@ impl Tab { Message::WindowToggleMaximize => { commands.push(Command::WindowToggleMaximize); } - Message::ZoomDefault => match self.config.view { - View::List => self.config.icon_sizes.list = 100.try_into().unwrap(), - View::Grid => self.config.icon_sizes.grid = 100.try_into().unwrap(), - }, Message::ZoomIn => { - let zoom_in = |size: &mut NonZeroU16, min: u16, max: u16| { - let mut step = min; - while step <= max { - if size.get() < step { - *size = step.try_into().unwrap(); - break; - } - step += 25; - } - if size.get() > step { - *size = step.try_into().unwrap(); - } - }; - match self.config.view { - View::List => zoom_in(&mut self.config.icon_sizes.list, 50, 500), - View::Grid => zoom_in(&mut self.config.icon_sizes.grid, 50, 500), - } + commands.push(Command::Action(Action::ZoomIn)); } Message::ZoomOut => { - let zoom_out = |size: &mut NonZeroU16, min: u16, max: u16| { - let mut step = max; - while step >= min { - if size.get() > step { - *size = step.try_into().unwrap(); - break; - } - step -= 25; - } - if size.get() < step { - *size = step.try_into().unwrap(); - } - }; - match self.config.view { - View::List => zoom_out(&mut self.config.icon_sizes.list, 50, 500), - View::Grid => zoom_out(&mut self.config.icon_sizes.grid, 50, 500), - } - } - } - - // Update preview timer - //TODO: make this configurable - if last_select_focus != self.select_focus { - if let Some(index) = self.select_focus { - if let Some(ref items) = self.items_opt { - if let Some(item) = items.get(index) { - if let Some(location) = item.location_opt.clone() { - // Show preview after double click timeout - commands.push(Command::Preview( - PreviewKind::Location(location), - DOUBLE_CLICK_DURATION, - )); - } - } - } + commands.push(Command::Action(Action::ZoomOut)); } } @@ -2568,8 +2478,8 @@ impl Tab { } }; let mut items: Vec<_> = self.items_opt.as_ref()?.iter().enumerate().collect(); - let heading_sort = self.config.sort_direction; - match self.config.sort_name { + let heading_sort = self.sort_direction; + match self.sort_name { HeadingOptions::Size => { items.sort_by(|a, b| { // entries take precedence over size @@ -2871,12 +2781,6 @@ impl Tab { .. } = theme::active().cosmic().spacing; - let TabConfig { - sort_name, - sort_direction, - .. - } = self.config; - let size = self.size_opt.get().unwrap_or(Size::new(0.0, 0.0)); let mut row = widget::row::with_capacity(5) @@ -2919,7 +2823,7 @@ impl Tab { .spacing(space_xxs) .width(width); row = row.push(widget::text::heading(name)); - match (sort_name == msg, sort_direction) { + match (self.sort_name == msg, self.sort_direction) { (true, true) => { row = row.push(widget::icon::from_name("pan-down-symbolic").size(16)); } @@ -3891,7 +3795,6 @@ impl Tab { mouse_area = mouse_area.on_right_press(Message::ContextMenu); } - let should_propogate_events = true; let mut popover = widget::popover(mouse_area); if let Some(point) = self.context_menu { @@ -3931,7 +3834,7 @@ impl Tab { } } } - Location::Network(uri, display_name) if uri == "network:///" => { + Location::Network(uri, _display_name) if uri == "network:///" => { tab_column = tab_column.push( widget::layer_container(widget::row::with_children(vec![ widget::horizontal_space(Length::Fill).into(), @@ -4109,9 +4012,8 @@ mod tests { use super::{respond_to_scroll_direction, scan_path, Location, Message, Tab}; use crate::{ app::test_utils::{ - assert_eq_tab_path, assert_zoom_affects_item_size, empty_fs, eq_path_item, filter_dirs, - read_dir_sorted, simple_fs, tab_click_new, 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, NAME_LEN, NUM_DIRS, NUM_FILES, NUM_HIDDEN, NUM_NESTED, }, config::{IconSizes, TabConfig}, }; @@ -4344,29 +4246,6 @@ mod tests { Ok(()) } - #[test] - fn tab_zoom_in_increases_item_view_size() -> io::Result<()> { - let fs = simple_fs(0, NUM_NESTED, NUM_DIRS, 0, NAME_LEN)?; - let path = fs.path(); - - let mut tab = Tab::new(Location::Path(path.into()), TabConfig::default()); - - let should_affect_size = true; - assert_zoom_affects_item_size(&mut tab, Message::ZoomIn, should_affect_size); - Ok(()) - } - - fn tab_zoom_out_decreases_item_view_size() -> io::Result<()> { - let fs = simple_fs(0, NUM_NESTED, NUM_DIRS, 0, NAME_LEN)?; - let path = fs.path(); - - let mut tab = Tab::new(Location::Path(path.into()), TabConfig::default()); - - let should_affect_size = true; - assert_zoom_affects_item_size(&mut tab, Message::ZoomOut, should_affect_size); - Ok(()) - } - #[test] fn tab_scroll_up_with_ctrl_modifier_zooms() -> io::Result<()> { let message_maybe = diff --git a/src/thumbnailer.rs b/src/thumbnailer.rs index 18a3bca..ac2df57 100644 --- a/src/thumbnailer.rs +++ b/src/thumbnailer.rs @@ -3,15 +3,7 @@ use mime_guess::Mime; use once_cell::sync::Lazy; -use std::{ - cmp::Ordering, - collections::HashMap, - env, fs, - path::{Path, PathBuf}, - process, - sync::Mutex, - time::Instant, -}; +use std::{collections::HashMap, fs, path::Path, process, sync::Mutex, time::Instant}; #[derive(Clone, Debug)] pub struct Thumbnailer { @@ -75,8 +67,6 @@ impl ThumbnailerCache { #[cfg(feature = "desktop")] pub fn reload(&mut self) { - use crate::localize::LANGUAGE_SORTER; - let start = Instant::now(); self.cache.clear();