diff --git a/Cargo.lock b/Cargo.lock index 1933e29..9d7b1fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,9 +52,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" dependencies = [ "memchr", ] @@ -128,6 +128,17 @@ dependencies = [ "libloading", ] +[[package]] +name = "async-broadcast" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b19760fa2b7301cf235360ffd6d3558b1ed4249edd16d6cca8d690cee265b95" +dependencies = [ + "event-listener", + "futures-core", + "parking_lot 0.12.1", +] + [[package]] name = "async-channel" version = "1.8.0" @@ -139,6 +150,96 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-executor" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b" +dependencies = [ + "async-lock", + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" +dependencies = [ + "async-lock", + "autocfg", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c374dda1ed3e7d8f0d9ba58715f924862c63eae6849c92d3a18e7fbde9e2794" +dependencies = [ + "async-lock", + "autocfg", + "concurrent-queue", + "futures-lite", + "libc", + "log", + "parking", + "polling", + "slab", + "socket2", + "waker-fn", + "windows-sys 0.42.0", +] + +[[package]] +name = "async-lock" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" +dependencies = [ + "event-listener", + "futures-lite", +] + +[[package]] +name = "async-recursion" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b015a331cc64ebd1774ba119538573603427eaace0a1950c423ab971f903796" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-task" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" + +[[package]] +name = "async-trait" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" + [[package]] name = "autocfg" version = "1.1.0" @@ -209,10 +310,25 @@ dependencies = [ ] [[package]] -name = "bumpalo" -version = "3.12.0" +name = "blocking" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" +dependencies = [ + "async-channel", + "async-lock", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "log", +] + +[[package]] +name = "bumpalo" +version = "3.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" [[package]] name = "byte-unit" @@ -225,19 +341,20 @@ dependencies = [ [[package]] name = "bytecheck" -version = "0.6.9" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d11cac2c12b5adc6570dad2ee1b87eff4955dac476fe12d81e5fdd352e52406f" +checksum = "13fe11640a23eb24562225322cd3e452b93a3d4091d62fab69c70542fcd17d1f" dependencies = [ "bytecheck_derive", "ptr_meta", + "simdutf8", ] [[package]] name = "bytecheck_derive" -version = "0.6.9" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e576ebe98e605500b3c8041bb888e966653577172df6dd97398714eb30b9bf" +checksum = "e31225543cb46f81a7e224762764f4a6a0f097b1db0b175f69e8065efaa42de5" dependencies = [ "proc-macro2", "quote", @@ -270,6 +387,12 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + [[package]] name = "calloop" version = "0.10.5" @@ -527,9 +650,12 @@ dependencies = [ "async-channel", "bytecheck", "color-eyre", + "cosmic-settings-page", "cosmic-settings-system", + "cosmic-settings-time", "derive_setters", "dirs", + "downcast-rs", "generator", "i18n-embed", "i18n-embed-fl", @@ -542,6 +668,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "cosmic-settings-page" +version = "0.1.0" +dependencies = [ + "derive_setters", + "downcast-rs", + "generator", + "libcosmic", + "once_cell", + "regex", + "slotmap", +] + [[package]] name = "cosmic-settings-system" version = "0.1.0" @@ -554,6 +693,16 @@ dependencies = [ "sysinfo", ] +[[package]] +name = "cosmic-settings-time" +version = "0.1.0" +dependencies = [ + "icu_calendar", + "icu_timezone", + "timedate-zbus", + "zbus", +] + [[package]] name = "cosmic-text" version = "0.6.0" @@ -795,7 +944,7 @@ dependencies = [ "hashbrown", "lock_api", "once_cell", - "parking_lot_core 0.9.6", + "parking_lot_core 0.9.7", ] [[package]] @@ -807,6 +956,17 @@ dependencies = [ "matches", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "derive_setters" version = "0.1.5" @@ -948,6 +1108,27 @@ dependencies = [ "syn", ] +[[package]] +name = "enumflags2" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e75d4cd21b95383444831539909fbb14b9dc3fdceb2a6f5d36577329a1f55ccb" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "error-code" version = "2.3.1" @@ -1263,6 +1444,21 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" +[[package]] +name = "futures-lite" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + [[package]] name = "futures-macro" version = "0.3.26" @@ -1315,9 +1511,9 @@ dependencies = [ [[package]] name = "generator" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266041a359dfa931b370ef684cceb84b166beb14f7f0421f4a6a3d0c446d12e" +checksum = "f3e123d9ae7c02966b4d892e550bdc32164f05853cd40ab570650ad600596a8a" dependencies = [ "cc", "libc", @@ -1520,6 +1716,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hexf-parse" version = "0.2.1" @@ -1564,9 +1766,9 @@ dependencies = [ [[package]] name = "i18n-embed-fl" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a425b9bbdc2e4cd797a2a79528662cb61894bd36db582e48da2c56c28eb727cd" +checksum = "4b5809e2295beeb55013705c3b947cbbe83b8cadf3c73a1e6dca06381927212a" dependencies = [ "dashmap", "find-crate", @@ -1599,7 +1801,7 @@ dependencies = [ [[package]] name = "iced" version = "0.6.0" -source = "git+https://github.com/pop-os/libcosmic?rev=843919e44f0a00c33c29358359be5b4bfa41ab00#843919e44f0a00c33c29358359be5b4bfa41ab00" +source = "git+https://github.com/pop-os/libcosmic?rev=8232e1d249a467673dbc5b0aa2f2e1665fb18dde#8232e1d249a467673dbc5b0aa2f2e1665fb18dde" dependencies = [ "iced_core", "iced_dyrend", @@ -1617,7 +1819,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.6.2" -source = "git+https://github.com/pop-os/libcosmic?rev=843919e44f0a00c33c29358359be5b4bfa41ab00#843919e44f0a00c33c29358359be5b4bfa41ab00" +source = "git+https://github.com/pop-os/libcosmic?rev=8232e1d249a467673dbc5b0aa2f2e1665fb18dde#8232e1d249a467673dbc5b0aa2f2e1665fb18dde" dependencies = [ "bitflags", "palette", @@ -1627,7 +1829,7 @@ dependencies = [ [[package]] name = "iced_dyrend" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic?rev=843919e44f0a00c33c29358359be5b4bfa41ab00#843919e44f0a00c33c29358359be5b4bfa41ab00" +source = "git+https://github.com/pop-os/libcosmic?rev=8232e1d249a467673dbc5b0aa2f2e1665fb18dde#8232e1d249a467673dbc5b0aa2f2e1665fb18dde" dependencies = [ "iced_glow", "iced_graphics", @@ -1641,7 +1843,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.5.1" -source = "git+https://github.com/pop-os/libcosmic?rev=843919e44f0a00c33c29358359be5b4bfa41ab00#843919e44f0a00c33c29358359be5b4bfa41ab00" +source = "git+https://github.com/pop-os/libcosmic?rev=8232e1d249a467673dbc5b0aa2f2e1665fb18dde#8232e1d249a467673dbc5b0aa2f2e1665fb18dde" dependencies = [ "futures", "log", @@ -1653,7 +1855,7 @@ dependencies = [ [[package]] name = "iced_glow" version = "0.5.1" -source = "git+https://github.com/pop-os/libcosmic?rev=843919e44f0a00c33c29358359be5b4bfa41ab00#843919e44f0a00c33c29358359be5b4bfa41ab00" +source = "git+https://github.com/pop-os/libcosmic?rev=8232e1d249a467673dbc5b0aa2f2e1665fb18dde#8232e1d249a467673dbc5b0aa2f2e1665fb18dde" dependencies = [ "bytemuck", "euclid", @@ -1668,7 +1870,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.5.0" -source = "git+https://github.com/pop-os/libcosmic?rev=843919e44f0a00c33c29358359be5b4bfa41ab00#843919e44f0a00c33c29358359be5b4bfa41ab00" +source = "git+https://github.com/pop-os/libcosmic?rev=8232e1d249a467673dbc5b0aa2f2e1665fb18dde#8232e1d249a467673dbc5b0aa2f2e1665fb18dde" dependencies = [ "bitflags", "bytemuck", @@ -1688,7 +1890,7 @@ dependencies = [ [[package]] name = "iced_lazy" version = "0.3.0" -source = "git+https://github.com/pop-os/libcosmic?rev=843919e44f0a00c33c29358359be5b4bfa41ab00#843919e44f0a00c33c29358359be5b4bfa41ab00" +source = "git+https://github.com/pop-os/libcosmic?rev=8232e1d249a467673dbc5b0aa2f2e1665fb18dde#8232e1d249a467673dbc5b0aa2f2e1665fb18dde" dependencies = [ "iced_native", "ouroboros 0.13.0", @@ -1697,7 +1899,7 @@ dependencies = [ [[package]] name = "iced_native" version = "0.7.0" -source = "git+https://github.com/pop-os/libcosmic?rev=843919e44f0a00c33c29358359be5b4bfa41ab00#843919e44f0a00c33c29358359be5b4bfa41ab00" +source = "git+https://github.com/pop-os/libcosmic?rev=8232e1d249a467673dbc5b0aa2f2e1665fb18dde#8232e1d249a467673dbc5b0aa2f2e1665fb18dde" dependencies = [ "iced_core", "iced_futures", @@ -1710,7 +1912,7 @@ dependencies = [ [[package]] name = "iced_softbuffer" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic?rev=843919e44f0a00c33c29358359be5b4bfa41ab00#843919e44f0a00c33c29358359be5b4bfa41ab00" +source = "git+https://github.com/pop-os/libcosmic?rev=8232e1d249a467673dbc5b0aa2f2e1665fb18dde#8232e1d249a467673dbc5b0aa2f2e1665fb18dde" dependencies = [ "cosmic-text", "iced_graphics", @@ -1725,7 +1927,7 @@ dependencies = [ [[package]] name = "iced_style" version = "0.5.1" -source = "git+https://github.com/pop-os/libcosmic?rev=843919e44f0a00c33c29358359be5b4bfa41ab00#843919e44f0a00c33c29358359be5b4bfa41ab00" +source = "git+https://github.com/pop-os/libcosmic?rev=8232e1d249a467673dbc5b0aa2f2e1665fb18dde#8232e1d249a467673dbc5b0aa2f2e1665fb18dde" dependencies = [ "iced_core", "once_cell", @@ -1735,7 +1937,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.7.0" -source = "git+https://github.com/pop-os/libcosmic?rev=843919e44f0a00c33c29358359be5b4bfa41ab00#843919e44f0a00c33c29358359be5b4bfa41ab00" +source = "git+https://github.com/pop-os/libcosmic?rev=8232e1d249a467673dbc5b0aa2f2e1665fb18dde#8232e1d249a467673dbc5b0aa2f2e1665fb18dde" dependencies = [ "bitflags", "bytemuck", @@ -1755,7 +1957,7 @@ dependencies = [ [[package]] name = "iced_winit" version = "0.6.0" -source = "git+https://github.com/pop-os/libcosmic?rev=843919e44f0a00c33c29358359be5b4bfa41ab00#843919e44f0a00c33c29358359be5b4bfa41ab00" +source = "git+https://github.com/pop-os/libcosmic?rev=8232e1d249a467673dbc5b0aa2f2e1665fb18dde#8232e1d249a467673dbc5b0aa2f2e1665fb18dde" dependencies = [ "iced_futures", "iced_graphics", @@ -1768,6 +1970,73 @@ dependencies = [ "winit", ] +[[package]] +name = "icu_calendar" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee1e8c25ed44743d03e2d58ca1c0226786dc1aac1f9cb27485e2da2de5e0918" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3003f85dccfc0e238ff567693248c59153a46f4e6125ba4020b973cef4d1d335" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", +] + +[[package]] +name = "icu_provider" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dc312a7b6148f7dfe098047ae2494d12d4034f48ade58d4f353000db376e305" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b728b9421e93eff1d9f8681101b78fa745e0748c95c655c83f337044a7e10" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "icu_timezone" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e22da75a450de2d54161838efa9e1a1f5baa7bc1fffdb015f260e0992b01977" +dependencies = [ + "displaydoc", + "icu_calendar", + "icu_locid", + "icu_provider", + "tinystr", + "zerovec", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1842,12 +2111,12 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e" +checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" dependencies = [ "libc", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -1873,9 +2142,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -1930,7 +2199,7 @@ checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libcosmic" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic?rev=843919e44f0a00c33c29358359be5b4bfa41ab00#843919e44f0a00c33c29358359be5b4bfa41ab00" +source = "git+https://github.com/pop-os/libcosmic?rev=8232e1d249a467673dbc5b0aa2f2e1665fb18dde#8232e1d249a467673dbc5b0aa2f2e1665fb18dde" dependencies = [ "apply", "cosmic-theme", @@ -1971,6 +2240,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "litemap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a04a5b2b6f54acba899926491d0a6c59d98012938ca2ab5befb281c034e8f94" + [[package]] name = "locale_config" version = "0.3.0" @@ -2442,9 +2717,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "ordered-float" @@ -2465,6 +2740,16 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "ouroboros" version = "0.13.0" @@ -2514,9 +2799,9 @@ dependencies = [ [[package]] name = "owned_ttf_parser" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5f3c7ca08b6879e7965fb25e24d1f5eeb32ea73f9ad99b3854778a38c57e93" +checksum = "e25e9fb15717794fae58ab55c26e044103aad13186fbb625893f9a3bbcc24228" dependencies = [ "ttf-parser 0.18.1", ] @@ -2552,6 +2837,12 @@ dependencies = [ "syn", ] +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + [[package]] name = "parking_lot" version = "0.11.2" @@ -2570,7 +2861,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.6", + "parking_lot_core 0.9.7", ] [[package]] @@ -2589,15 +2880,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", "redox_syscall 0.2.16", "smallvec", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -2704,6 +2995,20 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "polling" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22122d5ec4f9fe1b3916419b76be1e80bcb93f618d071d2edf841b137b2a2bd6" +dependencies = [ + "autocfg", + "cfg-if", + "libc", + "log", + "wepoll-ffi", + "windows-sys 0.42.0", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2938,9 +3243,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.1" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ "aho-corasick", "memchr", @@ -2949,15 +3254,24 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] [[package]] name = "rend" -version = "0.3.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79af64b4b6362ffba04eef3a4e10829718a4896dac19daa741851c86781edf95" +checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab" dependencies = [ "bytecheck", ] @@ -2995,9 +3309,9 @@ dependencies = [ [[package]] name = "rkyv" -version = "0.7.39" +version = "0.7.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cec2b3485b07d96ddfd3134767b8a447b45ea4eb91448d0a35180ec0ffd5ed15" +checksum = "21499ed91807f07ae081880aabb2ccc0235e9d88011867d984525e9a4c3cfa3e" dependencies = [ "bytecheck", "hashbrown", @@ -3009,9 +3323,9 @@ dependencies = [ [[package]] name = "rkyv_derive" -version = "0.7.39" +version = "0.7.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eaedadc88b53e36dd32d940ed21ae4d850d5916f2581526921f553a72ac34c4" +checksum = "ac1c672430eb41556291981f45ca900a0239ad007242d1cb4b4167af842db666" dependencies = [ "proc-macro2", "quote", @@ -3040,9 +3354,9 @@ dependencies = [ [[package]] name = "rust-embed" -version = "6.4.2" +version = "6.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "283ffe2f866869428c92e0d61c2f35dfb4355293cdfdc48f49e895c15f1333d1" +checksum = "1b68543d5527e158213414a92832d2aab11a84d2571a5eb021ebe22c43aab066" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -3051,9 +3365,9 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "6.3.1" +version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ab23d42d71fb9be1b643fe6765d292c5e14d46912d13f3ae2815ca048ea04d" +checksum = "4d4e0f0ced47ded9a68374ac145edd65a6c1fa13a96447b873660b2a568a0fd7" dependencies = [ "proc-macro2", "quote", @@ -3064,9 +3378,9 @@ dependencies = [ [[package]] name = "rust-embed-utils" -version = "7.3.0" +version = "7.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1669d81dfabd1b5f8e2856b8bbe146c6192b0ba22162edc738ac0a5de18f054" +checksum = "512b0ab6853f7e14e3c8754acb43d6f748bb9ced66aa5915a6553ac8213f7731" dependencies = [ "sha2", "walkdir", @@ -3213,6 +3527,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_repr" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "servo-fontconfig" version = "0.5.1" @@ -3234,6 +3559,17 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.6" @@ -3254,6 +3590,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + [[package]] name = "simplecss" version = "0.2.1" @@ -3322,6 +3664,16 @@ dependencies = [ "wayland-client 0.29.5", ] +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "softbuffer" version = "0.2.0" @@ -3450,6 +3802,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + [[package]] name = "sys-locale" version = "0.2.3" @@ -3465,9 +3829,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.27.7" +version = "0.28.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975fe381e0ecba475d4acff52466906d95b153a40324956552e027b2a9eaa89e" +checksum = "b4c2f3ca6693feb29a89724516f016488e9aafc7f37264f898593ee4b942f31b" dependencies = [ "cfg-if", "core-foundation-sys", @@ -3478,6 +3842,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall 0.2.16", + "remove_dir_all", + "winapi", +] + [[package]] name = "termcolor" version = "1.2.0" @@ -3536,6 +3914,15 @@ dependencies = [ "weezl", ] +[[package]] +name = "timedate-zbus" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "609052c48ae1d9e27999ac4a159843a290bffab5fdc13856889b940aefc185b7" +dependencies = [ + "zbus", +] + [[package]] name = "tiny-skia" version = "0.6.6" @@ -3582,18 +3969,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ac3f5b6856e931e15e07b478e98c8045239829a65f9156d4fa7e7788197a5ef" dependencies = [ "displaydoc", + "zerovec", ] [[package]] name = "tokio" -version = "1.25.0" +version = "1.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" dependencies = [ "autocfg", + "bytes", + "libc", + "mio", "num_cpus", "pin-project-lite", - "windows-sys 0.42.0", + "socket2", + "tracing", + "windows-sys 0.45.0", ] [[package]] @@ -3630,9 +4023,21 @@ checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tracing-core" version = "0.1.30" @@ -3714,6 +4119,16 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "uds_windows" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d" +dependencies = [ + "tempfile", + "winapi", +] + [[package]] name = "unic-langid" version = "0.9.1" @@ -3787,9 +4202,9 @@ checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc" [[package]] name = "unicode-segmentation" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-vo" @@ -3860,6 +4275,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + [[package]] name = "walkdir" version = "2.3.2" @@ -3879,9 +4300,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3889,9 +4310,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", @@ -3904,9 +4325,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" dependencies = [ "cfg-if", "js-sys", @@ -3916,9 +4337,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3926,9 +4347,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", @@ -3939,9 +4360,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "wasm-timer" @@ -4083,9 +4504,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", @@ -4097,6 +4518,15 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" +[[package]] +name = "wepoll-ffi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" +dependencies = [ + "cc", +] + [[package]] name = "wgpu" version = "0.14.2" @@ -4259,15 +4689,11 @@ dependencies = [ [[package]] name = "windows" -version = "0.39.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows_aarch64_msvc 0.39.0", - "windows_i686_gnu 0.39.0", - "windows_i686_msvc 0.39.0", - "windows_x86_64_gnu 0.39.0", - "windows_x86_64_msvc 0.39.0", + "windows-targets 0.48.0", ] [[package]] @@ -4289,117 +4715,168 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.1", "windows_aarch64_msvc 0.42.1", "windows_i686_gnu 0.42.1", "windows_i686_msvc 0.42.1", "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.1", "windows_x86_64_msvc 0.42.1", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.1", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.1", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm 0.42.1", + "windows_x86_64_msvc 0.42.1", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" -[[package]] -name = "windows_aarch64_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" - [[package]] name = "windows_aarch64_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" -[[package]] -name = "windows_i686_gnu" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" - [[package]] name = "windows_i686_gnu" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" -[[package]] -name = "windows_i686_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" - [[package]] name = "windows_i686_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" -[[package]] -name = "windows_x86_64_gnu" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" - [[package]] name = "windows_x86_64_gnu" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" -[[package]] -name = "windows_x86_64_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" - [[package]] name = "windows_x86_64_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "winit" version = "0.27.2" @@ -4441,6 +4918,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "writeable" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d74a687e3b9a7a129db0a8c82b4d464eb9c36f5a66ca68572a7e5f1cfdb5bc" + [[package]] name = "x11-dl" version = "2.21.0" @@ -4507,6 +4990,16 @@ dependencies = [ "dirs", ] +[[package]] +name = "xdg-home" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" +dependencies = [ + "nix 0.26.2", + "winapi", +] + [[package]] name = "xi-unicode" version = "0.3.0" @@ -4537,8 +5030,178 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c03b3e19c937b5b9bd8e52b1c88f30cce5c0d33d676cf174866175bb794ff658" +[[package]] +name = "yoke" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1848075a23a28f9773498ee9a0f2cf58fcbad4f8c0ccf84a210ab33c6ae495de" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca800d73d6b7a7ee54f2608205c98b549fca71c9500c1abcb3abdc7708b4a8cb" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zbus" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29242fa5ec5693629ae74d6eb1f69622a9511f600986d6d9779bccf36ac316e3" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-recursion", + "async-task", + "async-trait", + "byteorder", + "derivative", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix 0.26.2", + "once_cell", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tokio", + "tracing", + "uds_windows", + "winapi", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537793e26e9af85f774801dc52c6f6292352b2b517c5cf0449ffd3735732a53a" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "syn", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f34f314916bd89bdb9934154627fab152f4f28acdda03e7c4c68181b214fe7e3" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + [[package]] name = "zeno" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c110ba09c9b3a43edd4803d570df0da2414fed6e822e22b976a4e3ef50860701" + +[[package]] +name = "zerofrom" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e9355fccf72b04b7deaa99ce7a0f6630530acf34045391b74460fcd714de54" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e8aa86add9ddbd2409c1ed01e033cd457d79b1b1229b64922c25095c595e829" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerovec" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "198f54134cd865f437820aa3b43d0ad518af4e68ee161b444cdd15d8e567c8ea" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2154cb6e2a748163354165e22c6a555effb09ca2d16334767bf66bb404f2206e" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zvariant" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "576cc41e65c7f283e5460f5818073e68fb1f1631502b969ef228c2e03c862efb" +dependencies = [ + "byteorder", + "enumflags2", + "libc", + "serde", + "static_assertions", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fd4aafc0dee96ae7242a24249ce9babf21e1562822f03df650d4e68c20e41ed" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zvariant_utils" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b22993dbc4d128a17a3b6c92f1c63872dd67198537ee728d8b5d7c40640a8b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 9ce0cd0..e4f7da8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,45 +1,9 @@ -[package] -name = "cosmic-settings" -version = "0.1.0" -edition = "2021" -license = "GPL-3.0-only" -rust-version = "1.65.0" +[workspace] +members = ["app", "page", "pages/*"] +default-members = ["app"] -[dependencies] -apply = "0.3.0" -async-channel = "1.8.0" -bytecheck = "0.6.9" -color-eyre = "0.6.2" -derive_setters = "0.1.5" -dirs = "4.0.0" -generator = "0.7.2" -i18n-embed-fl = "0.6.5" -once_cell = "1.17.0" -regex = "1.7.1" -rkyv = { version = "0.7.39", features = ["validation"]} -rust-embed = "6.4.2" -slotmap = "1.0.6" -tokio = "1.25.0" - -[dependencies.cosmic-settings-system] -path = "pages/system" - -[dependencies.i18n-embed] -version = "0.13.8" -features = ["fluent-system", "desktop-requester"] - -[dependencies.libcosmic] +[workspace.dependencies.libcosmic] git = "https://github.com/pop-os/libcosmic" -rev = "843919e44f0a00c33c29358359be5b4bfa41ab00" +rev = "8232e1d249a467673dbc5b0aa2f2e1665fb18dde" default-features = false features = ["debug", "winit", "dyrend", "tokio"] - -[profile.dev] -opt-level = "s" -incremental = true - -[profile.release] -opt-level = "s" -overflow-checks = true -lto = "thin" -incremental = false diff --git a/README.md b/README.md index 560f803..54cb7df 100644 --- a/README.md +++ b/README.md @@ -41,13 +41,10 @@ Translation files may be found in the [i18n directory](./i18n). New translations Licensed under the [GNU Public License 3.0](https://choosealicense.com/licenses/gpl-3.0). - - ### Contribution Any contribution intentionally submitted for inclusion in the work by you shall be licensed under the GNU Public License 3.0 (GPL-3.0). Each source file should have a SPDX copyright notice at the top of the file: ``` -// Copyright {year-created} System76 // SPDX-License-Identifier: GPL-3.0-only ``` diff --git a/app/Cargo.toml b/app/Cargo.toml new file mode 100644 index 0000000..92ae9e2 --- /dev/null +++ b/app/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "cosmic-settings" +version = "0.1.0" +edition = "2021" +license = "GPL-3.0-only" +rust-version = "1.65.0" + +[dependencies] +apply = "0.3.0" +async-channel = "1.8.0" +bytecheck = "0.6.10" +color-eyre = "0.6.2" +cosmic-settings-page = { path = "../page" } +cosmic-settings-system = { path = "../pages/system" } +cosmic-settings-time = { path = "../pages/time" } +derive_setters = "0.1.5" +dirs = "4.0.0" +generator = "0.7.4" +i18n-embed-fl = "0.6.6" +libcosmic = {workspace = true} +once_cell = "1.17.1" +regex = "1.8.1" +rkyv = { version = "0.7.41", features = ["validation"]} +rust-embed = "6.6.1" +slotmap = "1.0.6" +tokio = "1.27.0" +downcast-rs = "1.2.0" + +[dependencies.i18n-embed] +version = "0.13.8" +features = ["fluent-system", "desktop-requester"] + +[profile.release] +opt-level = "s" +overflow-checks = true +lto = "thin" +incremental = false diff --git a/app/README.md b/app/README.md new file mode 100644 index 0000000..e69de29 diff --git a/i18n.toml b/app/i18n.toml similarity index 61% rename from i18n.toml rename to app/i18n.toml index 05c50ba..0be2bb9 100644 --- a/i18n.toml +++ b/app/i18n.toml @@ -1,4 +1,4 @@ fallback_language = "en" [fluent] -assets_dir = "i18n" \ No newline at end of file +assets_dir = "../i18n" \ No newline at end of file diff --git a/src/app.rs b/app/src/app.rs similarity index 83% rename from src/app.rs rename to app/src/app.rs index 421a45c..e57d69e 100644 --- a/src/app.rs +++ b/app/src/app.rs @@ -3,6 +3,8 @@ use apply::Apply; +use cosmic_settings_page::{self as page, section}; + use cosmic::{ iced::widget::{self, column, container, horizontal_space, row}, iced::{self, Application, Command, Length, Subscription}, @@ -19,7 +21,7 @@ use cosmic::{ use crate::{ config::{self, Config}, - page::{self, desktop, section, sound, system, time, Page}, + pages::{desktop, sound, system, time}, widget::{page_title, parent_page_button, search_header, sub_page_button}, }; @@ -34,7 +36,7 @@ pub struct SettingsApp { pub nav_bar_toggled_condensed: bool, pub nav_bar_toggled: bool, pub nav_bar: segmented_button::SingleSelectModel, - pub pages: page::Model, + pub pages: page::Binder, pub scaling_factor: f32, pub search: search::Model, pub search_selections: Vec<(page::Entity, section::Entity)>, @@ -48,10 +50,7 @@ pub struct SettingsApp { #[allow(dead_code)] #[derive(Clone, Debug)] pub enum Message { - About(system::about::Message), Close, - DateAndTime(time::date::Message), - Desktop(desktop::Message), Drag, KeyboardNav(keyboard_nav::Message), Maximize, @@ -59,6 +58,7 @@ pub enum Message { NavBar(segmented_button::Entity), None, Page(page::Entity), + PageMessage(crate::pages::Message), Search(search::Message), ToggleNavBar, ToggleNavBarCondensed, @@ -71,7 +71,7 @@ impl Application for SettingsApp { type Message = Message; type Theme = Theme; - fn new(_flags: ()) -> (Self, Command) { + fn new(_: Self::Flags) -> (Self, Command) { let mut config_path = config::PathManager::new(); let mut app = SettingsApp { @@ -83,7 +83,7 @@ impl Application for SettingsApp { nav_bar: segmented_button::Model::default(), nav_bar_toggled: true, nav_bar_toggled_condensed: false, - pages: page::Model::default(), + pages: page::Binder::default(), title: crate::fl!("app"), scaling_factor: std::env::var("COSMIC_SCALE") .ok() @@ -101,7 +101,7 @@ impl Application for SettingsApp { // app.insert_page::(); // app.insert_page::(); - app.insert_page::(); + let desktop_id = app.insert_page::().id(); // app.insert_page::(); @@ -119,14 +119,14 @@ impl Application for SettingsApp { // app.insert_page::(); // app.insert_page::(); - let mut command = Command::none(); + let active_id = app + .pages + .info + .iter() + .find(|(_id, info)| info.id == *app.config.active_page) + .map_or(desktop_id, |(id, _info)| id); - for (id, info) in app.pages.pages.iter() { - if info.id == &*app.config.active_page { - command = app.activate_page(id); - break; - } - } + let command = app.activate_page(active_id); (app, command) } @@ -195,21 +195,26 @@ impl Application for SettingsApp { self.search_clear(); } Message::None | Message::Search(_) => {} - Message::About(message) => { - if let Some(model) = self.pages.resource_mut::() { - model.update(message); + Message::PageMessage(message) => match message { + crate::pages::Message::About(message) => { + if let Some(page) = self.pages.page_mut::() { + page.update(message); + } } - } - Message::Desktop(message) => { - if let Some(model) = self.pages.resource_mut::() { - model.update(message); + crate::pages::Message::DateAndTime(message) => { + if let Some(page) = self.pages.page_mut::() { + page.update(message); + } } - } - Message::DateAndTime(message) => { - if let Some(model) = self.pages.resource_mut::() { - model.update(message); + crate::pages::Message::Desktop(message) => { + if let Some(page) = self.pages.page_mut::() { + page.update(message); + } } - } + crate::pages::Message::External { .. } => { + todo!("external plugins not supported yet"); + } + }, } ret } @@ -309,7 +314,7 @@ impl SettingsApp { self.active_page = page; if current_page != page { - self.config.active_page = Box::from(self.pages.pages[page].id); + self.config.active_page = Box::from(&*self.pages.info[page].id); self.config_path .config("main", |path| self.config.serialize(path)); } @@ -318,12 +323,15 @@ impl SettingsApp { self.search.state = search::State::Inactive; self.activate_navbar(page); - self.pages.init_page(page).unwrap_or(Command::none()) + self.pages + .page_reload(page) + .unwrap_or(Command::none()) + .map(Message::PageMessage) } /// Activates the navbar item associated with a page. fn activate_navbar(&mut self, mut page: page::Entity) { - if let Some(parent) = self.pages.pages[page].parent { + if let Some(parent) = self.pages.info[page].parent { page = parent; } @@ -333,7 +341,9 @@ impl SettingsApp { } /// Adds a main page to the settings application. - fn insert_page(&mut self) -> page::Insert { + fn insert_page>( + &mut self, + ) -> page::Insert { let id = self.pages.register::

().id(); self.navbar_insert(id); @@ -344,25 +354,24 @@ impl SettingsApp { } fn navbar_insert(&mut self, id: page::Entity) -> segmented_button::SingleSelectEntityMut { - let page = &self.pages.pages[id]; + let page = &self.pages.info[id]; self.nav_bar .insert() .text(page.title.clone()) - .icon(IconSource::from(page.icon_name)) + .icon(IconSource::from(page.icon_name.clone())) .data(id) .with_id(|nav_id| self.pages.data_set(id, nav_id)) } /// Displays the view of a page. fn page_view(&self, content: &[section::Entity]) -> cosmic::Element { - let page = &self.pages.pages[self.active_page]; - + let page = &self.pages.info[self.active_page]; let mut column_widgets = Vec::with_capacity(1); if let Some(parent) = page.parent { column_widgets.push(parent_page_button( - &self.pages.pages[parent], + &self.pages.info[parent], page, Message::Page(parent), )); @@ -371,7 +380,11 @@ impl SettingsApp { column_widgets.reserve_exact(1 + content.len()); for id in content.iter().copied() { let section = &self.pages.sections[id]; - column_widgets.push((section.view_fn)(self, section)); + let model = &self.pages.page[self.active_page]; + + column_widgets.push( + (section.view_fn)(&self.pages, model.as_ref(), section).map(Message::PageMessage), + ); } settings::view_column(column_widgets).into() @@ -418,13 +431,15 @@ impl SettingsApp { let mut current_page = page::Entity::default(); for (page, section) in self.search_selections.iter().copied() { let section = &self.pages.sections[section]; + let model = &self.pages.page[page]; if page != current_page { current_page = page; sections.push(search_header(&self.pages, page)); } - let section = (section.view_fn)(self, section) + let section = (section.view_fn)(&self.pages, model.as_ref(), section) + .map(Message::PageMessage) .apply(iced::widget::container) .padding([0, 0, 0, 48]); @@ -436,13 +451,13 @@ impl SettingsApp { /// Displays the sub-pages view of a page. fn sub_page_view(&self, sub_pages: &[page::Entity]) -> cosmic::Element { - let page = &self.pages.pages[self.active_page]; + let page = &self.pages.info[self.active_page]; let mut column_widgets = Vec::with_capacity(sub_pages.len()); column_widgets.push(page_title(page)); for entity in sub_pages.iter().copied() { - let sub_page = &self.pages.pages[entity]; + let sub_page = &self.pages.info[entity]; column_widgets.push(sub_page_button(entity, sub_page)); } diff --git a/src/config.rs b/app/src/config.rs similarity index 100% rename from src/config.rs rename to app/src/config.rs diff --git a/src/localize.rs b/app/src/localize.rs similarity index 97% rename from src/localize.rs rename to app/src/localize.rs index 263c17f..cd2c281 100644 --- a/src/localize.rs +++ b/app/src/localize.rs @@ -9,7 +9,7 @@ use once_cell::sync::Lazy; use rust_embed::RustEmbed; #[derive(RustEmbed)] -#[folder = "i18n/"] +#[folder = "../i18n/"] struct Localizations; pub static LANGUAGE_LOADER: Lazy = Lazy::new(|| { diff --git a/src/main.rs b/app/src/main.rs similarity index 81% rename from src/main.rs rename to app/src/main.rs index 7cbea88..e953700 100644 --- a/src/main.rs +++ b/app/src/main.rs @@ -16,9 +16,9 @@ pub mod localize; pub mod widget; -pub mod page; +pub mod pages; -use cosmic::{iced::Application, settings}; +use cosmic::iced::Application; use i18n_embed::DesktopLanguageRequester; /// # Errors @@ -35,11 +35,11 @@ pub fn main() -> color_eyre::Result<()> { let requested_languages = DesktopLanguageRequester::requested_languages(); if let Err(error) = localizer.select(&requested_languages) { - eprintln!("error while loading fluent localizations: {}", error); + eprintln!("error while loading fluent localizations: {error}"); } - settings::set_default_icon_theme("Pop"); - let mut settings = settings(); + cosmic::settings::set_default_icon_theme("Pop"); + let mut settings = cosmic::settings(); settings.window.min_size = Some((600, 300)); SettingsApp::run(settings)?; diff --git a/app/src/pages/desktop/appearance.rs b/app/src/pages/desktop/appearance.rs new file mode 100644 index 0000000..9680cd7 --- /dev/null +++ b/app/src/pages/desktop/appearance.rs @@ -0,0 +1,26 @@ +// Copyright 2023 System76 +// SPDX-License-Identifier: GPL-3.0-only + +use cosmic_settings_page::Section; +use cosmic_settings_page::{self as page, section}; +use slotmap::SlotMap; + +#[derive(Default)] +pub struct Page; + +impl page::Page for Page { + fn content( + &self, + sections: &mut SlotMap>, + ) -> Option { + Some(vec![sections.insert(Section::default())]) + } + + fn info(&self) -> page::Info { + page::Info::new("appearance", "preferences-pop-desktop-appearance-symbolic") + .title(fl!("appearance")) + .description(fl!("appearance", "desc")) + } +} + +impl page::AutoBind for Page {} diff --git a/app/src/pages/desktop/dock.rs b/app/src/pages/desktop/dock.rs new file mode 100644 index 0000000..1e2cd97 --- /dev/null +++ b/app/src/pages/desktop/dock.rs @@ -0,0 +1,26 @@ +// Copyright 2023 System76 +// SPDX-License-Identifier: GPL-3.0-only + +use cosmic_settings_page::Section; +use cosmic_settings_page::{self as page, section}; +use slotmap::SlotMap; + +#[derive(Default)] +pub struct Page; + +impl page::Page for Page { + fn content( + &self, + sections: &mut SlotMap>, + ) -> Option { + Some(vec![sections.insert(Section::default())]) + } + + fn info(&self) -> page::Info { + page::Info::new("dock", "preferences-pop-desktop-dock-symbolic") + .title(fl!("dock")) + .description(fl!("dock", "desc")) + } +} + +impl page::AutoBind for Page {} diff --git a/src/page/desktop/mod.rs b/app/src/pages/desktop/mod.rs similarity index 75% rename from src/page/desktop/mod.rs rename to app/src/pages/desktop/mod.rs index e372ccf..d6841ae 100644 --- a/src/page/desktop/mod.rs +++ b/app/src/pages/desktop/mod.rs @@ -8,18 +8,28 @@ pub mod options; pub mod wallpaper; pub mod workspaces; -use crate::page; +use cosmic_settings_page as page; -pub struct Page; +#[derive(Debug, Default)] +#[allow(clippy::struct_excessive_bools)] +pub struct Page { + pub top_left_hot_corner: bool, + pub show_workspaces_button: bool, + pub show_applications_button: bool, + pub show_minimize_button: bool, + pub show_maximize_button: bool, + pub slideshow: bool, + pub same_background: bool, +} -impl page::Page for Page { - type Model = super::Model; - - fn page() -> page::Meta { - page::Meta::new("desktop", "video-display-symbolic").title(fl!("desktop")) +impl page::Page for Page { + fn info(&self) -> page::Info { + page::Info::new("desktop", "video-display-symbolic").title(fl!("desktop")) } +} - fn sub_pages(page: page::Insert) -> page::Insert { +impl page::AutoBind for Page { + fn sub_pages(page: page::Insert) -> page::Insert { page.sub_page::() .sub_page::() .sub_page::() @@ -40,19 +50,7 @@ pub enum Message { TopLeftHotCorner(bool), } -#[derive(Debug, Default)] -#[allow(clippy::struct_excessive_bools)] -pub struct Model { - pub top_left_hot_corner: bool, - pub show_workspaces_button: bool, - pub show_applications_button: bool, - pub show_minimize_button: bool, - pub show_maximize_button: bool, - pub slideshow: bool, - pub same_background: bool, -} - -impl Model { +impl Page { pub fn update(&mut self, message: Message) { match message { Message::SameBackground(value) => self.same_background = value, @@ -66,20 +64,20 @@ impl Model { } } -// impl From for Message { -// fn from(page: Page) -> Message { -// Message::Page(page) +// impl From for Message { +// fn from(page: page::Info) -> Message { +// Message::page::Info(page) // } // } // pub enum Output { -// Page(Page), +// page::Info(page::Info), // } -// impl SubPage for DesktopPage { +// impl Subpage::Info for Desktoppage::Info { // //TODO: translate // fn title(&self) -> &'static str { -// use DesktopPage::*; +// use Desktoppage::Info::*; // match self { // Workspaces => "Workspaces", // Notifications => "Notifications", @@ -88,7 +86,7 @@ impl Model { // //TODO: translate // fn description(&self) -> &'static str { -// use DesktopPage::*; +// use Desktoppage::Info::*; // match self { // Workspaces => "Set workspace number, behavior, and placement.", // Notifications => { @@ -98,18 +96,18 @@ impl Model { // } // fn icon_name(&self) -> &'static str { -// use DesktopPage::*; +// use Desktoppage::Info::*; // match self { // Workspaces => "preferences-pop-desktop-workspaces-symbolic", // Notifications => "preferences-system-notifications-symbolic", // } // } -// fn parent_page(&self) -> Page { -// Page::Desktop(None) +// fn parent_page(&self) -> page::Info { +// page::Info::Desktop(None) // } -// fn into_page(self) -> Page { -// Page::Desktop(Some(self)) +// fn into_page(self) -> page::Info { +// page::Info::Desktop(Some(self)) // } // } diff --git a/app/src/pages/desktop/notifications.rs b/app/src/pages/desktop/notifications.rs new file mode 100644 index 0000000..1a84ad9 --- /dev/null +++ b/app/src/pages/desktop/notifications.rs @@ -0,0 +1,26 @@ +// Copyright 2023 System76 +// SPDX-License-Identifier: GPL-3.0-only + +use cosmic_settings_page::Section; +use cosmic_settings_page::{self as page, section}; +use slotmap::SlotMap; + +#[derive(Default)] +pub struct Page; + +impl page::Page for Page { + fn content( + &self, + sections: &mut SlotMap>, + ) -> Option { + Some(vec![sections.insert(Section::default())]) + } + + fn info(&self) -> page::Info { + page::Info::new("notifications", "preferences-system-notifications-symbolic") + .title(fl!("notifications")) + .description(fl!("notifications", "desc")) + } +} + +impl page::AutoBind for Page {} diff --git a/src/page/desktop/options.rs b/app/src/pages/desktop/options.rs similarity index 71% rename from src/page/desktop/options.rs rename to app/src/pages/desktop/options.rs index f9ad6e4..c1ed3e6 100644 --- a/src/page/desktop/options.rs +++ b/app/src/pages/desktop/options.rs @@ -10,23 +10,19 @@ use cosmic::{ Element, }; +use cosmic_settings_page::Section; +use cosmic_settings_page::{self as page, section}; use slotmap::SlotMap; -use crate::page::{self, section, Content, Section}; - +#[derive(Default)] pub struct Page; -impl page::Page for Page { - type Model = super::Model; - - fn page() -> page::Meta { - page::Meta::new("desktop-options", "video-display-symbolic") - .title(fl!("desktop-options")) - .description(fl!("desktop-options", "desc")) - } - +impl page::Page for Page { #[allow(clippy::too_many_lines)] - fn content(sections: &mut SlotMap) -> Option { + fn content( + &self, + sections: &mut SlotMap>, + ) -> Option { Some(vec![ sections.insert(super_key_action()), sections.insert(hot_corner()), @@ -34,17 +30,25 @@ impl page::Page for Page { sections.insert(window_controls()), ]) } + + fn info(&self) -> page::Info { + page::Info::new("desktop-options", "video-display-symbolic") + .title(fl!("desktop-options")) + .description(fl!("desktop-options", "desc")) + } } -pub fn hot_corner() -> Section { - Section::new() +impl page::AutoBind for Page {} + +pub fn hot_corner() -> Section { + Section::default() .title(fl!("hot-corner")) .descriptions(vec![fl!("hot-corner", "top-left-corner")]) - .view_fn(|app, section| { - let desktop = app - .pages - .resource::() - .expect("desktop model is missing"); + .view::(|binder, _page, section| { + let desktop = binder + .page::() + .expect("desktop page not found"); + let descriptions = §ion.descriptions; settings::view_section(§ion.title) .add(settings::item( @@ -54,24 +58,19 @@ pub fn hot_corner() -> Section { }), )) .apply(Element::from) - .map(crate::Message::Desktop) + .map(crate::pages::Message::Desktop) }) } -pub fn super_key_action() -> Section { - Section::new() +pub fn super_key_action() -> Section { + Section::default() .title(fl!("super-key-action")) .descriptions(vec![ fl!("super-key-action", "launcher"), fl!("super-key-action", "workspaces"), fl!("super-key-action", "applications"), ]) - .view_fn(|app, section| { - let _desktop = app - .pages - .resource::() - .expect("desktop model is missing"); - + .view::(|_binder, _page, section| { let descriptions = §ion.descriptions; settings::view_section(§ion.title) @@ -91,18 +90,17 @@ pub fn super_key_action() -> Section { }) } -pub fn top_panel() -> Section { - Section::new() +pub fn top_panel() -> Section { + Section::default() .title(fl!("top-panel")) .descriptions(vec![ fl!("top-panel", "workspaces"), fl!("top-panel", "applications"), ]) - .view_fn(|app, section| { - let desktop = app - .pages - .resource::() - .expect("desktop model is missing"); + .view::(|binder, _page, section| { + let desktop = binder + .page::() + .expect("desktop page not found"); let descriptions = §ion.descriptions; settings::view_section(§ion.title) @@ -123,22 +121,21 @@ pub fn top_panel() -> Section { ), )) .apply(Element::from) - .map(crate::Message::Desktop) + .map(crate::pages::Message::Desktop) }) } -pub fn window_controls() -> Section { - Section::new() +pub fn window_controls() -> Section { + Section::default() .title(fl!("window-controls")) .descriptions(vec![ fl!("window-controls", "minimize"), fl!("window-controls", "maximize"), ]) - .view_fn(|app, section| { - let desktop = app - .pages - .resource::() - .expect("desktop model is missing"); + .view::(|binder, _page, section| { + let desktop = binder + .page::() + .expect("desktop page not found"); let descriptions = §ion.descriptions; settings::view_section(§ion.title) @@ -159,6 +156,6 @@ pub fn window_controls() -> Section { ), )) .apply(Element::from) - .map(crate::Message::Desktop) + .map(crate::pages::Message::Desktop) }) } diff --git a/src/page/desktop/wallpaper.rs b/app/src/pages/desktop/wallpaper.rs similarity index 78% rename from src/page/desktop/wallpaper.rs rename to app/src/pages/desktop/wallpaper.rs index fcc8fde..96bab09 100644 --- a/src/page/desktop/wallpaper.rs +++ b/app/src/pages/desktop/wallpaper.rs @@ -10,40 +10,43 @@ use cosmic::{ widget::{list_column, settings, toggler}, Element, }; +use cosmic_settings_page::Section; +use cosmic_settings_page::{self as page, section}; use slotmap::SlotMap; -use crate::page::{self, section, Content, Section}; - +#[derive(Default)] pub struct Page; -impl page::Page for Page { - type Model = super::Model; +impl page::Page for Page { + fn content( + &self, + sections: &mut SlotMap>, + ) -> Option { + Some(vec![sections.insert(settings())]) + } - fn page() -> page::Meta { - page::Meta::new("wallpaper", "preferences-desktop-wallpaper-symbolic") + fn info(&self) -> page::Info { + page::Info::new("wallpaper", "preferences-desktop-wallpaper-symbolic") .title(fl!("wallpaper")) .description(fl!("wallpaper", "desc")) } - - fn content(sections: &mut SlotMap) -> Option { - Some(vec![sections.insert(settings())]) - } } -pub fn settings() -> Section { - Section::new() +impl page::AutoBind for Page {} + +pub fn settings() -> Section { + Section::default() .descriptions(vec![ fl!("wallpaper", "same"), fl!("wallpaper", "fit"), fl!("wallpaper", "slide"), fl!("wallpaper", "change"), ]) - .view_fn(|app, section| { + .view::(|binder, _page, section| { + let desktop = binder + .page::() + .expect("desktop page not found"); let descriptions = §ion.descriptions; - let desktop = app - .pages - .resource::() - .expect("desktop model is missing"); let image_paths: Vec = Vec::new(); let mut image_column = Vec::with_capacity(image_paths.len() / 4); @@ -90,6 +93,6 @@ pub fn settings() -> Section { settings::view_column(children) .padding(0) .apply(Element::from) - .map(crate::Message::Desktop) + .map(crate::pages::Message::Desktop) }) } diff --git a/src/page/desktop/workspaces.rs b/app/src/pages/desktop/workspaces.rs similarity index 69% rename from src/page/desktop/workspaces.rs rename to app/src/pages/desktop/workspaces.rs index 2d76a3e..27b788a 100644 --- a/src/page/desktop/workspaces.rs +++ b/app/src/pages/desktop/workspaces.rs @@ -3,42 +3,41 @@ use cosmic::iced::{widget::horizontal_space, Length}; use cosmic::widget::settings; +use cosmic_settings_page::Section; +use cosmic_settings_page::{self as page, section}; use slotmap::SlotMap; -use crate::page::{self, section, Content, Section}; - +#[derive(Default)] pub struct Page; -impl page::Page for Page { - type Model = super::Model; - - fn page() -> page::Meta { - page::Meta::new("workspaces", "preferences-pop-desktop-workspaces-symbolic") - .title(fl!("workspaces")) - .description(fl!("workspaces", "desc")) - } - - fn content(sections: &mut SlotMap) -> Option { +impl page::Page for Page { + fn content( + &self, + sections: &mut SlotMap>, + ) -> Option { Some(vec![ sections.insert(behavior()), sections.insert(multi_behavior()), ]) } + + fn info(&self) -> page::Info { + page::Info::new("workspaces", "preferences-pop-desktop-workspaces-symbolic") + .title(fl!("workspaces")) + .description(fl!("workspaces", "desc")) + } } -fn behavior() -> Section { - Section::new() +impl page::AutoBind for Page {} + +fn behavior() -> Section { + Section::default() .title(fl!("workspaces-behavior")) .descriptions(vec![ fl!("workspaces-behavior", "dynamic"), fl!("workspaces-behavior", "fixed"), ]) - .view_fn(|app, section| { - let _desktop = app - .pages - .resource::() - .expect("desktop model is missing"); - + .view::(|_binder, _page, section| { let descriptions = §ion.descriptions; settings::view_section(§ion.title) @@ -54,18 +53,14 @@ fn behavior() -> Section { }) } -fn multi_behavior() -> Section { - Section::new() +fn multi_behavior() -> Section { + Section::default() .title(fl!("workspaces-multi-behavior")) .descriptions(vec![ fl!("workspaces-multi-behavior", "span"), fl!("workspaces-multi-behavior", "separate"), ]) - .view_fn(|app, section| { - let _desktop = app - .pages - .resource::() - .expect("desktop model is missing"); + .view::(|_binder, _page, section| { let descriptions = §ion.descriptions; settings::view_section(§ion.title) .add(settings::item( diff --git a/app/src/pages/mod.rs b/app/src/pages/mod.rs new file mode 100644 index 0000000..b25646f --- /dev/null +++ b/app/src/pages/mod.rs @@ -0,0 +1,16 @@ +// Copyright 2023 System76 +// SPDX-License-Identifier: GPL-3.0-only + +pub mod desktop; +pub mod networking; +pub mod sound; +pub mod system; +pub mod time; + +#[derive(Clone, Debug)] +pub enum Message { + About(system::about::Message), + DateAndTime(time::date::Message), + Desktop(desktop::Message), + External { id: String, message: Vec }, +} diff --git a/src/page/networking/accounts.rs b/app/src/pages/networking/accounts.rs similarity index 59% rename from src/page/networking/accounts.rs rename to app/src/pages/networking/accounts.rs index 87833fd..6cc790d 100644 --- a/src/page/networking/accounts.rs +++ b/app/src/pages/networking/accounts.rs @@ -1,10 +1,10 @@ // Copyright 2023 System76 // SPDX-License-Identifier: GPL-3.0-only -use crate::page; +use cosmic_settings_page as page; -pub fn page() -> page::Meta { - page::Meta::new("online-accounts", "goa-panel-symbolic") +pub fn info() -> page::Info { + page::Info::new("online-accounts", "goa-panel-symbolic") .title(fl!("online-accounts")) .description(fl!("online-accounts", "desc")) } diff --git a/src/page/networking/mod.rs b/app/src/pages/networking/mod.rs similarity index 100% rename from src/page/networking/mod.rs rename to app/src/pages/networking/mod.rs diff --git a/src/page/networking/wired.rs b/app/src/pages/networking/wired.rs similarity index 57% rename from src/page/networking/wired.rs rename to app/src/pages/networking/wired.rs index f652fdb..4ca33ab 100644 --- a/src/page/networking/wired.rs +++ b/app/src/pages/networking/wired.rs @@ -1,10 +1,10 @@ // Copyright 2023 System76 // SPDX-License-Identifier: GPL-3.0-only -use crate::page; +use cosmic_settings_page as page; -pub fn page() -> page::Meta { - page::Meta::new("wired", "network-workgroup-symbolic") +pub fn info() -> page::Info { + page::Info::new("wired", "network-workgroup-symbolic") .title(fl!("wired")) .description(fl!("wired", "desc")) } diff --git a/src/page/sound.rs b/app/src/pages/sound.rs similarity index 72% rename from src/page/sound.rs rename to app/src/pages/sound.rs index 40afaf0..10fc48f 100644 --- a/src/page/sound.rs +++ b/app/src/pages/sound.rs @@ -1,27 +1,18 @@ // Copyright 2023 System76 // SPDX-License-Identifier: GPL-3.0-only -use crate::page::{self, Content, Section}; use cosmic::{iced, widget::settings}; +use cosmic_settings_page::{self as page, section, Section}; use slotmap::SlotMap; -use super::section; - #[derive(Default)] -pub struct Sound {} - pub struct Page; -impl page::Page for Page { - type Model = Sound; - - fn page() -> page::Meta { - page::Meta::new("sound", "multimedia-volume-control-symbolic") - .title(fl!("sound")) - .description(fl!("sound", "desc")) - } - - fn content(sections: &mut SlotMap) -> Option { +impl page::Page for Page { + fn content( + &self, + sections: &mut SlotMap>, + ) -> Option { Some(vec![ sections.insert(output()), sections.insert(input()), @@ -29,21 +20,24 @@ impl page::Page for Page { sections.insert(applications()), ]) } + + fn info(&self) -> page::Info { + page::Info::new("sound", "multimedia-volume-control-symbolic") + .title(fl!("sound")) + .description(fl!("sound", "desc")) + } } -fn alerts() -> Section { - Section::new() +impl page::AutoBind for Page {} + +fn alerts() -> Section { + Section::default() .title(fl!("sound-alerts")) .descriptions(vec![ fl!("sound-alerts", "volume"), fl!("sound-alerts", "sound"), ]) - .view_fn(|app, section| { - let _sound = app - .pages - .resource::() - .expect("sound model is missing"); - + .view::(|_binder, _page, section| { settings::view_section(§ion.title) .add(settings::item( §ion.descriptions[0], @@ -57,16 +51,11 @@ fn alerts() -> Section { }) } -fn applications() -> Section { - Section::new() +fn applications() -> Section { + Section::default() .title(fl!("sound-applications")) .descriptions(vec![fl!("sound-applications", "desc")]) - .view_fn(|app, section| { - let _sound = app - .pages - .resource::() - .expect("sound model is missing"); - + .view::(|_binder, _page, section| { settings::view_section(§ion.title) .add(settings::item( §ion.descriptions[0], @@ -76,20 +65,15 @@ fn applications() -> Section { }) } -fn input() -> Section { - Section::new() +fn input() -> Section { + Section::default() .title(fl!("sound-input")) .descriptions(vec![ fl!("sound-input", "volume"), fl!("sound-input", "device"), fl!("sound-input", "level"), ]) - .view_fn(|app, section| { - let _sound = app - .pages - .resource::() - .expect("sound model is missing"); - + .view::(|_binder, _page, section| { settings::view_section(§ion.title) .add(settings::item( §ion.descriptions[0], @@ -107,8 +91,8 @@ fn input() -> Section { }) } -fn output() -> Section { - Section::new() +fn output() -> Section { + Section::default() .title(fl!("sound-output")) .descriptions(vec![ fl!("sound-output", "volume"), @@ -117,12 +101,7 @@ fn output() -> Section { fl!("sound-output", "config"), fl!("sound-output", "balance"), ]) - .view_fn(|app, section| { - let _sound = app - .pages - .resource::() - .expect("sound model is missing"); - + .view::(|_binder, _page, section| { settings::view_section(§ion.title) .add(settings::item( §ion.descriptions[0], diff --git a/app/src/pages/system/about.rs b/app/src/pages/system/about.rs new file mode 100644 index 0000000..00421e6 --- /dev/null +++ b/app/src/pages/system/about.rs @@ -0,0 +1,158 @@ +// Copyright 2023 System76 +// SPDX-License-Identifier: GPL-3.0-only + +use cosmic_settings_page::{self as page, section, Section}; + +use cosmic::{ + iced::{ + widget::{horizontal_space, row}, + Length, + }, + widget::{icon, list_column, settings, text}, +}; +use cosmic_settings_system::about::Info; +use slotmap::SlotMap; + +#[derive(Clone, Debug)] +pub enum Message { + Info(Box), +} + +#[derive(Clone, Debug, Default)] +pub struct Page { + info: Info, + // support_page: page::Entity, +} + +impl page::AutoBind for Page {} + +impl page::Page for Page { + fn content( + &self, + sections: &mut SlotMap>, + ) -> Option { + Some(vec![ + sections.insert(distributor_logo()), + sections.insert(device()), + sections.insert(hardware()), + sections.insert(os()), + sections.insert(related()), + ]) + } + + fn info(&self) -> page::Info { + page::Info::new("about", "help-about-symbolic") + .title(fl!("about")) + .description(fl!("about", "desc")) + } + + fn load(&self, _page: page::Entity) -> Option> { + Some(Box::pin(async move { + crate::pages::Message::About(Message::Info(Box::new(Info::load()))) + })) + } +} + +impl Page { + pub fn update(&mut self, message: Message) { + match message { + Message::Info(info) => self.info = *info, + } + } +} + +fn device() -> Section { + Section::default() + .descriptions(vec![fl!("about-device"), fl!("about-device", "desc")]) + .view::(|_binder, page, section| { + let desc = §ion.descriptions; + let device_name = settings::item::builder(&desc[0]) + .description(&desc[1]) + .control(text(&page.info.device_name)); + + list_column().add(device_name).into() + }) +} + +fn distributor_logo() -> Section { + Section::default() + .search_ignore() + .view::(|_binder, _page, _section| { + row!( + horizontal_space(Length::Fill), + icon("distributor-logo", 78), + horizontal_space(Length::Fill), + ) + // Add extra padding to reach 40px from the first section. + .padding([0, 16, 0, 16]) + .into() + }) +} + +fn hardware() -> Section { + Section::default() + .title(fl!("about-hardware")) + .descriptions(vec![ + fl!("about-hardware", "model"), + fl!("about-hardware", "memory"), + fl!("about-hardware", "processor"), + fl!("about-hardware", "graphics"), + fl!("about-hardware", "disk-capacity"), + ]) + .view::(|_binder, page, section| { + let desc = §ion.descriptions; + + let mut sections = settings::view_section(§ion.title) + .add(settings::item(&desc[0], text(&page.info.hardware_model))) + .add(settings::item(&desc[1], text(&page.info.memory))) + .add(settings::item(&desc[2], text(&page.info.processor))); + + for card in &page.info.graphics { + sections = sections.add(settings::item(&desc[3], text(card.as_str()))); + } + + sections + .add(settings::item(&desc[4], text(&page.info.disk_capacity))) + .into() + }) +} + +fn os() -> Section { + Section::default() + .title(fl!("about-os")) + .descriptions(vec![ + fl!("about-os", "os"), + fl!("about-os", "os-architecture"), + fl!("about-os", "desktop-environment"), + fl!("about-os", "windowing-system"), + ]) + .view::(|_binder, page, section| { + let desc = §ion.descriptions; + settings::view_section(§ion.title) + .add(settings::item(&desc[0], text(&page.info.operating_system))) + .add(settings::item(&desc[1], text(&page.info.os_architecture))) + .add(settings::item( + &desc[2], + text(&page.info.desktop_environment), + )) + .add(settings::item(&desc[3], text(&page.info.windowing_system))) + .into() + }) +} + +fn related() -> Section { + Section::default() + .title(fl!("about-related")) + .descriptions(vec![fl!("about-related", "support")]) + .view::(|_binder, _page, section| { + settings::view_section(§ion.title) + .add(settings::item(§ion.descriptions[0], text("TODO"))) + .into() + }) +} + +// fn page(app: &crate::SettingsApp) -> &Page { +// app.pages +// .resource::() +// .expect("missing system->about page") +// } diff --git a/app/src/pages/system/firmware.rs b/app/src/pages/system/firmware.rs new file mode 100644 index 0000000..878e660 --- /dev/null +++ b/app/src/pages/system/firmware.rs @@ -0,0 +1,26 @@ +// Copyright 2023 System76 +// SPDX-License-Identifier: GPL-3.0-only + +use cosmic_settings_page::Section; +use cosmic_settings_page::{self as page, section}; +use slotmap::SlotMap; + +#[derive(Default)] +pub struct Page; + +impl page::Page for Page { + fn content( + &self, + sections: &mut SlotMap>, + ) -> Option { + Some(vec![sections.insert(Section::default())]) + } + + fn info(&self) -> page::Info { + page::Info::new("firmware", "firmware-manager-symbolic") + .title(fl!("firmware")) + .description(fl!("firmware", "desc")) + } +} + +impl page::AutoBind for Page {} diff --git a/app/src/pages/system/mod.rs b/app/src/pages/system/mod.rs new file mode 100644 index 0000000..08e9b6f --- /dev/null +++ b/app/src/pages/system/mod.rs @@ -0,0 +1,25 @@ +// Copyright 2023 System76 +// SPDX-License-Identifier: GPL-3.0-only + +pub mod about; +pub mod firmware; +pub mod users; + +use cosmic_settings_page as page; + +#[derive(Default)] +pub struct Page; + +impl page::Page for Page { + fn info(&self) -> page::Info { + page::Info::new("system", "system-users-symbolic").title(fl!("system")) + } +} + +impl page::AutoBind for Page { + fn sub_pages(page: page::Insert) -> page::Insert { + page.sub_page::() + .sub_page::() + .sub_page::() + } +} diff --git a/app/src/pages/system/users.rs b/app/src/pages/system/users.rs new file mode 100644 index 0000000..4182f4d --- /dev/null +++ b/app/src/pages/system/users.rs @@ -0,0 +1,26 @@ +// Copyright 2023 System76 +// SPDX-License-Identifier: GPL-3.0-only + +use cosmic_settings_page::Section; +use cosmic_settings_page::{self as page, section}; +use slotmap::SlotMap; + +#[derive(Default)] +pub struct Page; + +impl page::Page for Page { + fn content( + &self, + sections: &mut SlotMap>, + ) -> Option { + Some(vec![sections.insert(Section::default())]) + } + + fn info(&self) -> page::Info { + page::Info::new("users", "system-users-symbolic") + .title(fl!("users")) + .description(fl!("users", "desc")) + } +} + +impl page::AutoBind for Page {} diff --git a/src/page/time/date.rs b/app/src/pages/time/date.rs similarity index 66% rename from src/page/time/date.rs rename to app/src/pages/time/date.rs index 6992c36..acc2c8f 100644 --- a/src/page/time/date.rs +++ b/app/src/pages/time/date.rs @@ -1,22 +1,49 @@ // Copyright 2023 System76 // SPDX-License-Identifier: GPL-3.0-only -use crate::page::{self, section, Content, Section}; use apply::Apply; use cosmic::{ iced::{widget::horizontal_space, Length}, widget::settings, }; +use cosmic_settings_page::Section; +use cosmic_settings_page::{self as page, section}; +// use icu::calendar::{DateTime, Gregorian}; + use slotmap::SlotMap; #[derive(Default)] -pub struct Model { +pub struct Page { auto: bool, auto_timezone: bool, military_time: bool, + // info: Option, } -impl Model { +impl page::Page for Page { + fn content( + &self, + sections: &mut SlotMap>, + ) -> Option { + Some(vec![ + sections.insert(date()), + sections.insert(timezone()), + sections.insert(format()), + ]) + } + + fn info(&self) -> page::Info { + page::Info::new("time-date", "preferences-system-time-symbolic") + .title(fl!("time-date")) + .description(fl!("time-date", "desc")) + } + + fn load(&self, _page: page::Entity) -> Option> { + None + } +} + +impl Page { pub fn update(&mut self, message: Message) { match message { Message::Automatic(enable) => self.auto = enable, @@ -33,68 +60,40 @@ pub enum Message { MilitaryTime(bool), } -pub struct Page; +impl page::AutoBind for Page {} -impl page::Page for Page { - type Model = Model; - - fn page() -> page::Meta { - page::Meta::new("time-date", "preferences-system-time-symbolic") - .title(fl!("time-date")) - .description(fl!("time-date", "desc")) - } - - fn content(sections: &mut SlotMap) -> Option { - Some(vec![ - sections.insert(date()), - sections.insert(timezone()), - sections.insert(format()), - ]) - } -} - -fn date() -> Section { - Section::new() +fn date() -> Section { + Section::default() .title(fl!("time-date")) .descriptions(vec![fl!("time-date", "auto"), fl!("time-date")]) - .view_fn(|app, section| { - let model = app - .pages - .resource::() - .expect("time & date model not found"); - + .view::(|_binder, page, section| { settings::view_section(§ion.title) .add( settings::item::builder(§ion.descriptions[0]) - .toggler(model.auto, Message::Automatic), + .toggler(page.auto, Message::Automatic), ) .add(settings::item( §ion.descriptions[1], horizontal_space(Length::Fill), )) .apply(cosmic::Element::from) - .map(crate::Message::DateAndTime) + .map(crate::pages::Message::DateAndTime) }) } -fn format() -> Section { - Section::new() +fn format() -> Section { + Section::default() .title(fl!("time-format")) .descriptions(vec![ fl!("time-format", "twenty-four"), fl!("time-format", "first"), ]) - .view_fn(|app, section| { - let model = app - .pages - .resource::() - .expect("time & date model not found"); - + .view::(|_binder, page, section| { settings::view_section(§ion.title) // 24-hour toggle .add( settings::item::builder(§ion.descriptions[0]) - .toggler(model.military_time, Message::MilitaryTime), + .toggler(page.military_time, Message::MilitaryTime), ) // First day of week .add(settings::item( @@ -102,30 +101,25 @@ fn format() -> Section { horizontal_space(Length::Fill), )) .apply(cosmic::Element::from) - .map(crate::Message::DateAndTime) + .map(crate::pages::Message::DateAndTime) }) } -fn timezone() -> Section { - Section::new() +fn timezone() -> Section { + Section::default() .title(fl!("time-zone")) .descriptions(vec![ fl!("time-zone", "auto"), fl!("time-zone", "auto-info"), fl!("time-zone"), ]) - .view_fn(|app, section| { - let model = app - .pages - .resource::() - .expect("time & date model not found"); - + .view::(|_binder, page, section| { settings::view_section(§ion.title) // Automatic timezone toggle .add( settings::item::builder(§ion.descriptions[0]) .description(§ion.descriptions[1]) - .toggler(model.auto_timezone, Message::AutomaticTimezone), + .toggler(page.auto_timezone, Message::AutomaticTimezone), ) // Time zone select .add( @@ -133,6 +127,6 @@ fn timezone() -> Section { .control(horizontal_space(Length::Fill)), ) .apply(cosmic::Element::from) - .map(crate::Message::DateAndTime) + .map(crate::pages::Message::DateAndTime) }) } diff --git a/app/src/pages/time/mod.rs b/app/src/pages/time/mod.rs new file mode 100644 index 0000000..de021f8 --- /dev/null +++ b/app/src/pages/time/mod.rs @@ -0,0 +1,24 @@ +// Copyright 2023 System76 +// SPDX-License-Identifier: GPL-3.0-only + +use cosmic_settings_page as page; + +pub mod date; +pub mod region; + +#[derive(Default)] +pub struct Page; + +impl page::Page for Page { + fn info(&self) -> page::Info { + page::Info::new("time", "preferences-system-time-symbolic") + .title(fl!("time")) + .description(fl!("time", "desc")) + } +} + +impl page::AutoBind for Page { + fn sub_pages(page: page::Insert) -> page::Insert { + page.sub_page::().sub_page::() + } +} diff --git a/app/src/pages/time/region.rs b/app/src/pages/time/region.rs new file mode 100644 index 0000000..04c1590 --- /dev/null +++ b/app/src/pages/time/region.rs @@ -0,0 +1,26 @@ +// Copyright 2023 System76 +// SPDX-License-Identifier: GPL-3.0-only + +use cosmic_settings_page::Section; +use cosmic_settings_page::{self as page, section}; +use slotmap::SlotMap; + +#[derive(Default)] +pub struct Page; + +impl page::Page for Page { + fn content( + &self, + sections: &mut SlotMap>, + ) -> Option { + Some(vec![sections.insert(Section::default())]) + } + + fn info(&self) -> page::Info { + page::Info::new("time-region", "preferences-desktop-locale-symbolic") + .title(fl!("time-region")) + .description(fl!("time-region", "desc")) + } +} + +impl page::AutoBind for Page {} diff --git a/src/widget/mod.rs b/app/src/widget/mod.rs similarity index 83% rename from src/widget/mod.rs rename to app/src/widget/mod.rs index 5f8fb91..1abfb91 100644 --- a/src/widget/mod.rs +++ b/app/src/widget/mod.rs @@ -9,17 +9,19 @@ use cosmic::iced::{ }; use cosmic::widget::{divider, icon, list, settings, text}; use cosmic::{theme, Element}; - -use crate::page::{self, Meta}; +use cosmic_settings_page as page; #[must_use] -pub fn search_header(pages: &page::Model, page: page::Entity) -> cosmic::Element { - let page_meta = &pages.pages[page]; +pub fn search_header( + pages: &page::Binder, + page: page::Entity, +) -> cosmic::Element { + let page_meta = &pages.info[page]; let mut column_children = Vec::with_capacity(4); if let Some(parent) = page_meta.parent { - let parent_meta = &pages.pages[parent]; + let parent_meta = &pages.info[parent]; column_children.push( text(parent_meta.title.as_str()) @@ -52,7 +54,7 @@ pub fn search_page_link(title: &str) -> Button(page: &Meta) -> Element { +pub fn page_title(page: &page::Info) -> Element { row!( text(page.title.as_str()).size(32), horizontal_space(Length::Fill) @@ -62,8 +64,8 @@ pub fn page_title(page: &Meta) -> Element { #[must_use] pub fn parent_page_button<'a, Message: Clone + 'static>( - parent: &'a Meta, - sub_page: &'a Meta, + parent: &'a page::Info, + sub_page: &'a page::Info, on_press: Message, ) -> Element<'a, Message> { column!( @@ -85,10 +87,10 @@ pub fn parent_page_button<'a, Message: Clone + 'static>( } #[must_use] -pub fn sub_page_button(entity: page::Entity, page: &Meta) -> Element { +pub fn sub_page_button(entity: page::Entity, page: &page::Info) -> Element { settings::item::builder(page.title.as_str()) .description(page.description.as_str()) - .icon(icon(page.icon_name, 20).style(theme::Svg::Symbolic)) + .icon(icon(&*page.icon_name, 20).style(theme::Svg::Symbolic)) .control(row!( horizontal_space(Length::Fill), icon("go-next-symbolic", 20).style(theme::Svg::Symbolic) diff --git a/justfile b/justfile index ebbbd25..10bf363 100644 --- a/justfile +++ b/justfile @@ -15,12 +15,12 @@ rootdir := '' prefix := '/usr' # File paths -bin-src := 'target/release/' + name -bin-dest := rootdir + prefix + '/bin/' + name +bin-src := 'target' / 'release' / name +bin-dest := clean(rootdir / prefix) / 'bin' / name desktop := appid + '.desktop' -desktop-src := 'resources/' + desktop -desktop-dest := rootdir + prefix + '/share/applications/' + desktop +desktop-src := 'resources' / desktop +desktop-dest := clean(rootdir / prefix) / 'share' / 'applications' / desktop [private] help: @@ -34,21 +34,21 @@ clean: clean-dist: clean rm -rf .cargo vendor vendor.tar target -# Compile debug build of cosmic-settings +# Compile with debug profile build-debug *args: cargo build {{args}} -# Compile release build of cosmic-settings +# Compile with release profile build-release *args: (build-debug '--release' args) -# Vendored release build of cosmic-settings +# Compile with a vendored tarball build-vendored *args: vendor-extract (build-release '--frozen --offline' args) -# Run `cargo clippy` +# Check for errors and linter warnings check *args: cargo clippy --all-features {{args}} -- -W clippy::pedantic -# Run `cargo clippy` with json message format +# Runs a check with JSON message format for IDE integration check-json: (check '--message-format=json') # Installation command @@ -67,11 +67,11 @@ install: (install-bin bin-src bin-dest) (install-file desktop-src desktop-dest) # Run the application for testing purposes run *args: - cargo run {{args}} + env RUST_BACKTRACE=full cargo run --release {{args}} # Run `cargo test` test: - cargo test --all-features + cargo test # Uninstalls everything (requires same arguments as given to install) uninstall: @@ -86,10 +86,9 @@ vendor: tar pcf vendor.tar vendor rm -rf vendor -# Extracts vendored dependencies if vendor=1 +# Extracts vendored dependencies [private] vendor-extract: - #!/usr/bin/env sh rm -rf vendor tar pxf vendor.tar diff --git a/page/Cargo.toml b/page/Cargo.toml new file mode 100644 index 0000000..5737aa2 --- /dev/null +++ b/page/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "cosmic-settings-page" +version = "0.1.0" +edition = "2021" + +[dependencies] +derive_setters = "0.1.5" +regex = "1.8.1" +slotmap = "1.0.6" +libcosmic = { workspace = true } +generator = "0.7.4" +downcast-rs = "1.2.0" +once_cell = "1.17.1" diff --git a/page/README.md b/page/README.md new file mode 100644 index 0000000..33091b2 --- /dev/null +++ b/page/README.md @@ -0,0 +1,7 @@ +# cosmic-settings-page + +This module contains the APIs for creating and managing settings pages. + +- A [Page](./src/lib.rs) implements the `Page` and `AutoBind` traits. +- A [Section](./src/section.rs) is a subset of a page, with a view function to generate the UI. +- The [Binder](./src/binder.rs) holds all of the pages, their sections, and additional metadata associated with them diff --git a/page/src/binder.rs b/page/src/binder.rs new file mode 100644 index 0000000..1ef524e --- /dev/null +++ b/page/src/binder.rs @@ -0,0 +1,199 @@ +// Copyright 2023 System76 +// SPDX-License-Identifier: GPL-3.0-only + +use crate::section::{self, Section}; +use crate::{Content, Info, Page}; +use cosmic::iced_native::command::{Action, Command}; +use regex::Regex; +use slotmap::{SecondaryMap, SlotMap, SparseSecondaryMap}; +use std::{ + any::{Any, TypeId}, + collections::HashMap, +}; + +/// All settings pages are registered and managed by the [`Binder`]. +pub struct Binder { + pub info: SlotMap, + pub page: SecondaryMap>>, + pub typed_page_ids: HashMap, + pub resource: HashMap>, + pub storage: HashMap>>, + pub sub_pages: SparseSecondaryMap>, + pub sections: SlotMap>, + pub content: SparseSecondaryMap, +} + +impl Default for Binder { + fn default() -> Self { + Self { + content: SparseSecondaryMap::new(), + info: SlotMap::with_key(), + page: SecondaryMap::new(), + typed_page_ids: HashMap::new(), + resource: HashMap::new(), + sections: SlotMap::with_key(), + storage: HashMap::new(), + sub_pages: SparseSecondaryMap::new(), + } + } +} + +impl Binder { + /// Check if a page exists in the model. + #[must_use] + pub fn contains_item(&self, id: crate::Entity) -> bool { + self.info.contains_key(id) + } + + /// Returns the content of a page, if it has any. + #[must_use] + pub fn content(&self, page: crate::Entity) -> Option<&[section::Entity]> { + self.content.get(page).map(Vec::as_slice) + } + + /// Get an immutable reference to data associated with a page. + #[must_use] + pub fn data(&self, id: crate::Entity) -> Option<&Data> { + self.storage + .get(&TypeId::of::()) + .and_then(|storage| storage.get(id)) + .and_then(|data| data.downcast_ref()) + } + + /// Get a mutable reference to data associated with a page. + pub fn data_mut(&mut self, id: crate::Entity) -> Option<&mut Data> { + self.storage + .get_mut(&TypeId::of::()) + .and_then(|storage| storage.get_mut(id)) + .and_then(|data| data.downcast_mut()) + } + + /// Associates data with the item. + pub fn data_set(&mut self, id: crate::Entity, data: Data) { + if self.contains_item(id) { + self.storage + .entry(TypeId::of::()) + .or_insert_with(SecondaryMap::new) + .insert(id, Box::new(data)); + } + } + + /// Removes a specific data type from the item. + pub fn data_remove(&mut self, id: crate::Entity) { + self.storage + .get_mut(&TypeId::of::()) + .and_then(|storage| storage.remove(id)); + } + + /// Registers a new page in the settings panel. + pub fn register>(&mut self) -> crate::Insert { + let page = P::default(); + + let id = self.register_page(page); + + self.typed_page_ids.insert(TypeId::of::

(), id); + + P::sub_pages(crate::Insert { id, model: self }) + } + + pub fn register_page>(&mut self, page: P) -> crate::Entity { + let id = self.info.insert(page.info()); + + if let Some(content) = page.content(&mut self.sections) { + self.content.insert(id, content); + } + + self.page.insert(id, Box::new(page)); + + id + } + + #[must_use] + pub fn model(&self, id: crate::Entity) -> Option<&dyn Page> { + self.page.get(id).map(AsRef::as_ref) + } + + #[must_use] + pub fn model_mut(&mut self, id: crate::Entity) -> Option<&mut dyn Page> { + self.page.get_mut(id).map(AsMut::as_mut) + } + + /// Obtain a reference to a page by its type ID. + #[must_use] + pub fn page>(&self) -> Option<&P> { + let id = self.typed_page_ids.get(&TypeId::of::

())?; + let page = self.page.get(*id)?; + page.downcast_ref::

() + } + + /// Obtain a reference to a page by its type ID. + #[must_use] + pub fn page_mut>(&mut self) -> Option<&mut P> { + let id = self.typed_page_ids.get(&TypeId::of::

())?; + let page = self.page.get_mut(*id)?; + page.downcast_mut::

() + } + + /// Calls a page's load function to refresh its data. + pub fn page_reload(&mut self, id: crate::Entity) -> Option> { + if let Some(page) = self.page.get(id) { + if let Some(future) = page.load(id) { + return Some(Command::single(Action::Future(future))); + } + } + + None + } + + #[must_use] + pub fn resource(&self) -> Option<&Resource> { + self.resource + .get(&TypeId::of::()) + .and_then(|resource| resource.downcast_ref()) + } + + #[must_use] + pub fn resource_mut(&mut self) -> Option<&mut Resource> { + self.resource + .get_mut(&TypeId::of::()) + .and_then(|resource| resource.downcast_mut()) + } + + #[allow(unused_must_use)] + pub fn resource_register(&mut self) { + self.resource + .entry(TypeId::of::()) + .or_insert_with(|| Box::::default()); + } + + /// Finds content of panels that match the search. + pub fn search<'a>( + &'a self, + rule: &'a Regex, + ) -> impl Iterator + 'a { + generator::Gn::new_scoped_local(|mut s| { + for (page, sections) in self.content.iter() { + for id in sections { + if self.sections[*id].search_matches(rule) { + s.yield_((page, *id)); + } + } + } + + generator::done!(); + }) + } + + /// Returns the sub-pages of a page, if it has any. + pub fn sub_pages(&self, page: crate::Entity) -> Option<&[crate::Entity]> { + self.sub_pages.get(page).map(AsRef::as_ref) + } +} + +pub trait AutoBind: Page + Default + 'static { + /// Attaches sub-pages to the page. + #[allow(clippy::must_use_candidate)] + fn sub_pages(page: crate::Insert) -> crate::Insert { + page + } +} diff --git a/page/src/insert.rs b/page/src/insert.rs new file mode 100644 index 0000000..2b9e574 --- /dev/null +++ b/page/src/insert.rs @@ -0,0 +1,50 @@ +// Copyright 2023 System76 +// SPDX-License-Identifier: GPL-3.0-only + +use super::{AutoBind, Binder, Content, Entity, Info}; + +/// An inserted page which may have additional properties assigned to it. +pub struct Insert<'a, Message> { + pub model: &'a mut Binder, + pub id: Entity, +} + +impl<'a, Message: 'static> Insert<'a, Message> { + #[must_use] + pub fn id(self) -> Entity { + self.id + } + + #[must_use] + pub fn content(self, content: Content) -> Self { + self.model.content.insert(self.id, content); + self + } + + /// Adds a page and associates it with its parent page. + #[allow(clippy::return_self_not_must_use)] + #[allow(clippy::must_use_candidate)] + pub fn sub_page>(self) -> Self { + let sub_page = P::default(); + + let page = self.model.info.insert(Info { + parent: Some(self.id), + ..sub_page.info() + }); + + if let Some(content) = sub_page.content(&mut self.model.sections) { + self.model.content.insert(page, content); + } + + self.model.page.insert(page, Box::new(sub_page)); + + self.model + .sub_pages + .entry(self.id) + .expect("parent page missing") + .and_modify(|v| v.push(page)) + .or_insert_with(|| vec![page]); + + self + } +} diff --git a/page/src/lib.rs b/page/src/lib.rs new file mode 100644 index 0000000..d09540c --- /dev/null +++ b/page/src/lib.rs @@ -0,0 +1,85 @@ +// Copyright 2023 System76 +// SPDX-License-Identifier: GPL-3.0-only + +mod binder; +pub use binder::{AutoBind, Binder}; + +mod insert; +use downcast_rs::{impl_downcast, Downcast}; +pub use insert::Insert; + +pub mod section; +pub use section::Section; + +use derive_setters::Setters; +use slotmap::SlotMap; +use std::{borrow::Cow, future::Future, pin::Pin}; + +slotmap::new_key_type! { + /// The unique ID of a page. + pub struct Entity; +} + +/// A collection of sections which a page may be comprised of. +pub type Content = Vec; + +/// A request by a page to run a command in the background. +pub type Task = Pin + Send>>; + +pub trait Page: Downcast { + /// Information about the page + fn info(&self) -> Info; + + #[must_use] + fn content( + &self, + _sections: &mut SlotMap>, + ) -> Option { + None + } + + #[must_use] + #[allow(unused)] + fn load(&self, page: crate::Entity) -> Option> { + None + } +} + +impl_downcast!(Page); + +/// Information about a page; including its title, icon, and description. +#[derive(Setters)] +#[must_use] +pub struct Info { + /// An identifier that is the same between application runs. + #[setters(skip)] + pub id: Cow<'static, str>, + + /// The icon associated with the page. + #[setters(skip)] + pub icon_name: Cow<'static, str>, + + /// The title of the page. + #[setters(into)] + pub title: String, + + /// A description of the page. + #[setters(into)] + pub description: String, + + /// The parent of the page. + #[setters(strip_option)] + pub parent: Option, +} + +impl Info { + pub fn new(id: impl Into>, icon_name: impl Into>) -> Self { + Self { + title: String::new(), + icon_name: icon_name.into(), + id: id.into(), + description: String::new(), + parent: None, + } + } +} diff --git a/page/src/section.rs b/page/src/section.rs new file mode 100644 index 0000000..e8e4f22 --- /dev/null +++ b/page/src/section.rs @@ -0,0 +1,102 @@ +// Copyright 2023 System76 +// SPDX-License-Identifier: GPL-3.0-only + +use derive_setters::Setters; +use regex::Regex; + +use crate::{Binder, Page}; + +slotmap::new_key_type! { + /// The unique ID of a section of page. + pub struct Entity; +} + +pub type ViewFn = Box< + dyn for<'a> Fn( + &'a Binder, + &'a dyn Page, + &'a Section, + ) -> cosmic::Element<'a, Message>, +>; + +/// A searchable sub-component of a page. +/// +/// Searches can group multiple sections together. +#[derive(Setters)] +#[must_use] +pub struct Section { + #[setters(into)] + pub title: String, + pub descriptions: Vec, + #[setters(skip)] + pub view_fn: ViewFn, + #[setters(bool)] + pub search_ignore: bool, +} + +impl Default for Section { + fn default() -> Self { + Self { + title: String::new(), + descriptions: Vec::new(), + view_fn: Box::new(unimplemented), + search_ignore: false, + } + } +} + +impl Section { + #[must_use] + pub fn search_matches(&self, rule: &Regex) -> bool { + if self.search_ignore { + return false; + } + + if rule.is_match(self.title.as_str()) { + return true; + } + + for description in &self.descriptions { + if rule.is_match(description.as_str()) { + return true; + } + } + + false + } + + /// # Panics + /// + /// Will panic if the `Model` type does not match the page type. + pub fn view>( + mut self, + func: impl for<'a> Fn( + &'a Binder, + &'a Model, + &'a Section, + ) -> cosmic::Element<'a, Message> + + 'static, + ) -> Self { + self.view_fn = Box::new(move |binder, model: &dyn Page, section| { + let model = model.downcast_ref::().unwrap_or_else(|| { + panic!( + "page model type mismatch: expected {}", + std::any::type_name::() + ) + }); + + func(binder, model, section) + }); + self + } +} + +#[must_use] +pub fn unimplemented<'a, Message: 'static>( + _binder: &'a Binder, + _page: &'a dyn Page, + _section: &'a Section, +) -> cosmic::Element<'a, Message> { + cosmic::widget::settings::view_column(vec![cosmic::widget::settings::view_section("").into()]) + .into() +} diff --git a/pages/system/Cargo.lock b/pages/system/Cargo.lock deleted file mode 100644 index 97c3a4b..0000000 --- a/pages/system/Cargo.lock +++ /dev/null @@ -1,279 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bumpalo" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" - -[[package]] -name = "byte-unit" -version = "4.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3348673602e04848647fffaa8e9a861e7b5d5cae6570727b41bde0f722514484" -dependencies = [ - "utf8-width", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "concat-in-place" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b80dba65d26e0c4b692ad0312b837f1177e8175031af57fd1de4f3bc36b430" - -[[package]] -name = "const_format" -version = "0.2.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7309d9b4d3d2c0641e018d449232f2e28f1b22933c137f157d3dbc14228b8c0e" -dependencies = [ - "const_format_proc_macros", -] - -[[package]] -name = "const_format_proc_macros" -version = "0.2.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f47bf7270cf70d370f8f98c1abb6d2d4cf60a6845d30e05bfb90c6568650" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "cosmic-settings-system" -version = "0.1.0" -dependencies = [ - "bumpalo", - "byte-unit", - "concat-in-place", - "const_format", - "memchr", - "sysinfo", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "either" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" - -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "libc" -version = "0.2.139" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] - -[[package]] -name = "ntapi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc51db7b362b205941f71232e56c625156eb9a929f8cf74a428fd5bc094a4afc" -dependencies = [ - "winapi", -] - -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "once_cell" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" - -[[package]] -name = "proc-macro2" -version = "1.0.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rayon" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "sysinfo" -version = "0.27.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975fe381e0ecba475d4acff52466906d95b153a40324956552e027b2a9eaa89e" -dependencies = [ - "cfg-if", - "core-foundation-sys", - "libc", - "ntapi", - "once_cell", - "rayon", - "winapi", -] - -[[package]] -name = "unicode-ident" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "utf8-width" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5190c9442dcdaf0ddd50f37420417d219ae5261bbf5db120d0f9bab996c9cba1" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/pages/system/Cargo.toml b/pages/system/Cargo.toml index bbe4a94..48d3a82 100644 --- a/pages/system/Cargo.toml +++ b/pages/system/Cargo.toml @@ -2,7 +2,7 @@ name = "cosmic-settings-system" version = "0.1.0" edition = "2021" -license = "MPL-2.0" +license = "GPL-3.0-only" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -10,9 +10,9 @@ license = "MPL-2.0" byte-unit = { version = "4.0.18", default-features = false } const_format = "0.2.30" concat-in-place = "1.1.0" -sysinfo = "0.27.7" +sysinfo = "0.28.4" memchr = "2.5.0" [dependencies.bumpalo] -version = "3.12.0" +version = "3.12.1" features = ["collections"] diff --git a/pages/system/src/about.rs b/pages/system/src/about.rs new file mode 100644 index 0000000..61bae4a --- /dev/null +++ b/pages/system/src/about.rs @@ -0,0 +1,210 @@ +// Copyright 2023 System76 +// SPDX-License-Identifier: GPL-3.0-only + +use bumpalo::Bump; +use std::{ffi::OsStr, io::Read}; +use sysinfo::{DiskExt, SystemExt}; + +use concat_in_place::strcat; +use const_format::concatcp; + +const DMI_DIR: &str = "/sys/devices/virtual/dmi/id/"; +const BOARD_NAME: &str = concatcp!(DMI_DIR, "board_name"); +const BOARD_VERSION: &str = concatcp!(DMI_DIR, "board_version"); +const SYS_VENDOR: &str = concatcp!(DMI_DIR, "sys_vendor"); + +const VERSION_IGNORING_PRODUCTS: &[&str] = &["Dev One"]; + +#[must_use] +#[derive(Clone, Debug, Default)] +pub struct Info { + pub desktop_environment: String, + pub device_name: String, + pub disk_capacity: String, + pub graphics: Vec, + pub hardware_model: String, + pub memory: String, + pub operating_system: String, + pub os_architecture: String, + pub processor: String, + pub windowing_system: String, +} + +impl Info { + pub fn load() -> Info { + let mut info = Info::default(); + let mut bump = Bump::with_capacity(8 * 1024); + + architecture(&bump, &mut info.os_architecture); + bump.reset(); + + hardware_model(&bump, &mut info.hardware_model); + bump.reset(); + + operating_system(&bump, &mut info.operating_system); + bump.reset(); + + processor_name(&bump, &mut info.processor); + bump.reset(); + + let mut sys = sysinfo::System::new(); + + sys.refresh_disks_list(); + sys.refresh_disks(); + sys.refresh_memory(); + + let mut total_capacity = 0; + for disk in sys.disks() { + total_capacity += disk.total_space(); + } + + info.disk_capacity = format_size(total_capacity); + + if let Some(name) = sys.host_name() { + info.device_name = name; + } + + info.memory = format_size(sys.total_memory()); + + if let Ok(mut session) = std::env::var("XDG_SESSION_TYPE") { + if let Some(first) = session.get_mut(0..1) { + first.make_ascii_uppercase(); + } + + info.windowing_system = session; + } + + if let Ok(mut session) = std::env::var("DESKTOP_SESSION") { + if let Some(first) = session.get_mut(0..1) { + first.make_ascii_uppercase(); + } + + info.desktop_environment = session; + } + + if let Ok(output) = std::process::Command::new("lspci").output() { + if let Ok(stdout) = std::str::from_utf8(&output.stdout) { + for line in stdout.lines() { + if let Some(pos) = memchr::memmem::find(line.as_bytes(), b"VGA") { + let line = &line[pos + 3..]; + if let Some(pos) = memchr::memmem::find(line.as_bytes(), b": ") { + info.graphics.push(String::from(&line[pos + 2..])); + } + } + } + } + } + + info + } +} + +pub fn architecture(bump: &Bump, arch: &mut String) { + let buffer = &mut bumpalo::collections::Vec::new_in(bump); + if let Some(value) = read_to_string("/proc/sys/kernel/arch", buffer) { + arch.push_str(value.trim()); + } +} + +pub fn hardware_model(bump: &Bump, hardware_model: &mut String) { + let buffer = &mut bumpalo::collections::Vec::new_in(bump); + if let Some(mut sys_vendor) = read_to_string(SYS_VENDOR, buffer) { + sys_vendor = sys_vendor.trim(); + + hardware_model.push_str(sys_vendor); + + let buffer = &mut bumpalo::collections::Vec::new_in(bump); + if let Some(mut name) = read_to_string(BOARD_NAME, buffer) { + name = name.trim(); + + if !name.is_empty() && name != sys_vendor { + // Ensure that the name does not contain the vendor. + name = match name.strip_prefix(sys_vendor) { + Some(stripped) => stripped.trim(), + None => name, + }; + + strcat!(&mut *hardware_model, " " name); + } + + let buffer = &mut bumpalo::collections::Vec::new_in(bump); + if let Some(mut version) = read_to_string(BOARD_VERSION, buffer) { + version = version.trim(); + + if !version.is_empty() && !VERSION_IGNORING_PRODUCTS.contains(&name) { + strcat!(hardware_model, " (" version.trim() ")"); + } + } + } + } +} + +pub fn operating_system(bump: &Bump, operating_system: &mut String) { + let mut buffer = bumpalo::collections::Vec::new_in(bump); + let Some(os_release) = read_to_string("/etc/os-release", &mut buffer) else { + return; + }; + + for line in os_release.lines() { + if let Some(mut value) = line.strip_prefix("PRETTY_NAME=") { + if let Some(v) = value.strip_prefix('"') { + value = v; + } + + if let Some(v) = value.strip_suffix('"') { + value = v; + } + + operating_system.push_str(value.trim()); + break; + } + } +} + +pub fn processor_name(bump: &Bump, name: &mut String) { + if let Some(cpuinfo) = read_to_string( + "/proc/cpuinfo", + &mut bumpalo::collections::Vec::new_in(bump), + ) { + for line in cpuinfo.lines() { + if let Some(info) = line.strip_prefix("model name") { + if let Some(info) = info.trim_start().strip_prefix(':') { + name.push_str(info.trim()); + } + + break; + } + } + } +} + +pub fn read_to_string<'a, P: AsRef>( + path: P, + buffer: &'a mut bumpalo::collections::Vec, +) -> Option<&'a str> { + let mut file = std::fs::File::open(path.as_ref()).ok()?; + + if let Ok(metadata) = file.metadata() { + if let Ok(len) = usize::try_from(metadata.len()) { + buffer.reserve_exact(len); + } + } + + let mut buf = [0; 16 * 1024]; + + loop { + match file.read(&mut buf) { + Ok(0) => break, + Ok(read) => buffer.extend_from_slice(&buf[..read]), + Err(_) => return None, + } + } + + std::str::from_utf8(buffer.as_slice()).ok() +} + +fn format_size(size: u64) -> String { + byte_unit::Byte::from_bytes(size) + .get_appropriate_unit(true) + .to_string() +} diff --git a/pages/system/src/lib.rs b/pages/system/src/lib.rs index c4a48de..2548b98 100644 --- a/pages/system/src/lib.rs +++ b/pages/system/src/lib.rs @@ -1,208 +1,4 @@ // Copyright 2023 System76 -// SPDX-License-Identifier: MPL-2.0 +// SPDX-License-Identifier: GPL-3.0-only -use bumpalo::Bump; -use std::{ffi::OsStr, io::Read}; -use sysinfo::{DiskExt, SystemExt}; - -use concat_in_place::strcat; -use const_format::concatcp; - -const DMI_DIR: &str = "/sys/devices/virtual/dmi/id/"; -const BOARD_NAME: &str = concatcp!(DMI_DIR, "board_name"); -const BOARD_VERSION: &str = concatcp!(DMI_DIR, "board_version"); -const SYS_VENDOR: &str = concatcp!(DMI_DIR, "sys_vendor"); - -#[derive(Clone, Debug, Default)] -pub struct Info { - pub desktop_environment: String, - pub device_name: String, - pub disk_capacity: String, - pub graphics: Vec, - pub hardware_model: String, - pub memory: String, - pub operating_system: String, - pub os_architecture: String, - pub processor: String, - pub windowing_system: String, -} - -impl Info { - pub fn load() -> Info { - let mut info = Info::default(); - let mut bump = Bump::with_capacity(8 * 1024); - - architecture(&bump, &mut info.os_architecture); - bump.reset(); - - hardware_model(&bump, &mut info.hardware_model); - bump.reset(); - - operating_system(&bump, &mut info.operating_system); - bump.reset(); - - processor_name(&bump, &mut info.processor); - bump.reset(); - - let mut sys = sysinfo::System::new(); - - sys.refresh_disks_list(); - sys.refresh_disks(); - sys.refresh_memory(); - - let mut total_capacity = 0; - for disk in sys.disks() { - total_capacity += disk.total_space(); - } - - info.disk_capacity = format_size(total_capacity); - - if let Some(name) = sys.host_name() { - info.device_name = name; - } - - info.memory = format_size(sys.total_memory()); - - if let Ok(mut session) = std::env::var("XDG_SESSION_TYPE") { - if let Some(first) = session.get_mut(0..1) { - first.make_ascii_uppercase(); - } - - info.windowing_system = session; - } - - if let Ok(mut session) = std::env::var("DESKTOP_SESSION") { - if let Some(first) = session.get_mut(0..1) { - first.make_ascii_uppercase(); - } - - info.desktop_environment = session; - } - - if let Ok(output) = std::process::Command::new("lspci").output() { - if let Ok(stdout) = std::str::from_utf8(&output.stdout) { - for line in stdout.lines() { - if let Some(pos) = memchr::memmem::find(line.as_bytes(), b"VGA") { - let line = &line[pos + 3..]; - if let Some(pos) = memchr::memmem::find(line.as_bytes(), b": ") { - info.graphics.push(String::from(&line[pos + 2..])); - } - } - } - } - } - - info - } -} - -pub fn architecture(bump: &Bump, arch: &mut String) { - let buffer = &mut bumpalo::collections::Vec::new_in(bump); - if let Some(value) = read_to_string("/proc/sys/kernel/arch", buffer) { - arch.push_str(value.trim()); - } -} - -pub fn hardware_model(bump: &Bump, hardware_model: &mut String) { - let buffer = &mut bumpalo::collections::Vec::new_in(bump); - if let Some(mut sys_vendor) = read_to_string(SYS_VENDOR, buffer) { - sys_vendor = sys_vendor.trim(); - - hardware_model.push_str(sys_vendor); - - let buffer = &mut bumpalo::collections::Vec::new_in(bump); - if let Some(mut name) = read_to_string(BOARD_NAME, buffer) { - name = name.trim(); - - if !name.is_empty() && name != sys_vendor { - // Ensure that the name does not contain the vendor. - name = match name.strip_prefix(sys_vendor) { - Some(stripped) => stripped.trim(), - None => name, - }; - - strcat!(&mut *hardware_model, " " name); - } - - let buffer = &mut bumpalo::collections::Vec::new_in(bump); - if let Some(mut version) = read_to_string(BOARD_VERSION, buffer) { - version = version.trim(); - - // These have bogus values for their version. - const IGNORE_PRODUCTS: &[&str] = &["Dev One"]; - - if !version.is_empty() && !IGNORE_PRODUCTS.contains(&name) { - strcat!(hardware_model, " (" version.trim() ")"); - } - } - } - } -} - -pub fn operating_system(bump: &Bump, operating_system: &mut String) { - let mut buffer = bumpalo::collections::Vec::new_in(bump); - let Some(os_release) = read_to_string("/etc/os-release", &mut buffer) else { - return; - }; - - for line in os_release.lines() { - if let Some(mut value) = line.strip_prefix("PRETTY_NAME=") { - if let Some(v) = value.strip_prefix('"') { - value = v; - } - - if let Some(v) = value.strip_suffix('"') { - value = v; - } - - operating_system.push_str(value.trim()); - break; - } - } -} - -pub fn processor_name(bump: &Bump, name: &mut String) { - if let Some(cpuinfo) = read_to_string( - "/proc/cpuinfo", - &mut bumpalo::collections::Vec::new_in(bump), - ) { - for line in cpuinfo.lines() { - if let Some(info) = line.strip_prefix("model name") { - if let Some(info) = info.trim_start().strip_prefix(":") { - name.push_str(info.trim()); - } - - break; - } - } - } -} - -pub fn read_to_string<'a, P: AsRef>( - path: P, - buffer: &'a mut bumpalo::collections::Vec, -) -> Option<&'a str> { - let mut file = std::fs::File::open(path.as_ref()).ok()?; - - if let Ok(metadata) = file.metadata() { - buffer.reserve_exact(metadata.len() as usize); - } - - let mut buf = [0; 16 * 1024]; - - loop { - match file.read(&mut buf) { - Ok(0) => break, - Ok(read) => buffer.extend_from_slice(&buf[..read]), - Err(_) => return None, - } - } - - std::str::from_utf8(buffer.as_slice()).ok() -} - -fn format_size(size: u64) -> String { - byte_unit::Byte::from_bytes(size) - .get_appropriate_unit(true) - .to_string() -} +pub mod about; diff --git a/pages/time/Cargo.toml b/pages/time/Cargo.toml new file mode 100644 index 0000000..5e84f3a --- /dev/null +++ b/pages/time/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "cosmic-settings-time" +version = "0.1.0" +edition = "2021" + +[dependencies] +icu_calendar = "1.2.0" +icu_timezone = "1.2.0" +timedate-zbus = "0.1.0" + +[dependencies.zbus] +version = "3.12.0" +default-features = false +features = ["tokio"] diff --git a/pages/time/src/lib.rs b/pages/time/src/lib.rs new file mode 100644 index 0000000..d612e8f --- /dev/null +++ b/pages/time/src/lib.rs @@ -0,0 +1,48 @@ +// Copyright 2023 System76 +// SPDX-License-Identifier: GPL-3.0-only + +use std::time::SystemTime; + +use icu_calendar::{types::IsoSecond, DateTime, Iso}; +use icu_timezone::CustomTimeZone; +use timedate_zbus::TimeDateProxy; + +pub struct Info { + pub can_ntp: bool, + pub timezone: CustomTimeZone, + pub local_time: DateTime, +} + +impl Info { + pub async fn load(proxy: &TimeDateProxy<'_>) -> Option { + let can_ntp = proxy.can_ntp().await.unwrap_or_default(); + + let Ok(timezone) = proxy.timezone() + .await + .unwrap_or_default() + .parse::() else { + return None; + }; + + let Ok(duration) = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) else { + return None; + }; + + let seconds = duration.as_secs(); + + let Ok(iso_seconds) = IsoSecond::try_from((seconds % 60) as u8) else { + return None; + }; + + #[allow(clippy::cast_possible_truncation)] + let mut local_time = DateTime::from_minutes_since_local_unix_epoch((seconds / 60) as i32); + + local_time.time.second = iso_seconds; + + Some(Info { + can_ntp, + timezone, + local_time, + }) + } +} diff --git a/src/page/desktop/appearance.rs b/src/page/desktop/appearance.rs deleted file mode 100644 index 22855f9..0000000 --- a/src/page/desktop/appearance.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2023 System76 -// SPDX-License-Identifier: GPL-3.0-only - -use crate::page::{self, section, Content, Section}; -use slotmap::SlotMap; - -pub struct Page; - -impl page::Page for Page { - type Model = super::Model; - - fn page() -> page::Meta { - page::Meta::new("appearance", "preferences-pop-desktop-appearance-symbolic") - .title(fl!("appearance")) - .description(fl!("appearance", "desc")) - } - - fn content(sections: &mut SlotMap) -> Option { - Some(vec![sections.insert(Section::new())]) - } -} diff --git a/src/page/desktop/dock.rs b/src/page/desktop/dock.rs deleted file mode 100644 index 1ed7388..0000000 --- a/src/page/desktop/dock.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2023 System76 -// SPDX-License-Identifier: GPL-3.0-only - -use slotmap::SlotMap; - -use crate::page::{self, section, Content, Section}; - -pub struct Page; - -impl page::Page for Page { - type Model = super::Model; - - fn page() -> page::Meta { - page::Meta::new("dock", "preferences-pop-desktop-dock-symbolic") - .title(fl!("dock")) - .description(fl!("dock", "desc")) - } - - fn content(sections: &mut SlotMap) -> Option { - Some(vec![sections.insert(Section::new())]) - } -} diff --git a/src/page/desktop/notifications.rs b/src/page/desktop/notifications.rs deleted file mode 100644 index cff02ef..0000000 --- a/src/page/desktop/notifications.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2023 System76 -// SPDX-License-Identifier: GPL-3.0-only - -use slotmap::SlotMap; - -use crate::page::{self, section, Content, Section}; - -pub struct Page; - -impl page::Page for Page { - type Model = super::Model; - - fn page() -> page::Meta { - page::Meta::new("notifications", "preferences-system-notifications-symbolic") - .title(fl!("notifications")) - .description(fl!("notifications", "desc")) - } - - fn content(sections: &mut SlotMap) -> Option { - Some(vec![sections.insert(Section::new())]) - } -} diff --git a/src/page/mod.rs b/src/page/mod.rs deleted file mode 100644 index 725e346..0000000 --- a/src/page/mod.rs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2023 System76 -// SPDX-License-Identifier: GPL-3.0-only - -pub mod desktop; -pub mod networking; -pub mod section; -pub mod time; - -pub use section::Section; -pub mod sound; -pub mod system; - -mod model; - -pub use model::{Insert, Model, PageTask}; - -use derive_setters::Setters; -use slotmap::SlotMap; - -slotmap::new_key_type! { - /// ID of a page - pub struct Entity; -} - -pub trait Page { - type Model: Default + 'static; - - fn page() -> Meta; - - #[must_use] - fn content(_sections: &mut SlotMap) -> Option { - None - } - - /// Attaches sub-pages to the page. - #[allow(clippy::must_use_candidate)] - fn sub_pages(page: Insert) -> Insert { - page - } - - #[must_use] - #[allow(unused)] - fn load(page: Entity) -> PageTask { - Box::pin(async move { crate::Message::None }) - } -} - -#[derive(Setters)] -#[must_use] -pub struct Meta { - /// A unique identity that is the same between application runs. - #[setters(skip)] - pub id: &'static str, - - /// The icon associated with the page. - #[setters(skip)] - pub icon_name: &'static str, - - /// The title of the page. - #[setters(into)] - pub title: String, - - /// A description of the page. - #[setters(into)] - pub description: String, - - /// The parent of the page. - #[setters(strip_option)] - pub parent: Option, -} - -impl Meta { - pub const fn new(id: &'static str, icon_name: &'static str) -> Self { - Self { - title: String::new(), - icon_name, - id, - description: String::new(), - parent: None, - } - } -} - -pub type Content = Vec; diff --git a/src/page/model.rs b/src/page/model.rs deleted file mode 100644 index 1d0bf19..0000000 --- a/src/page/model.rs +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2023 System76 -// SPDX-License-Identifier: GPL-3.0-only - -use std::{ - any::{Any, TypeId}, - collections::HashMap, - future::Future, - pin::Pin, -}; - -use crate::page::{self, section, Content, Meta, Page, Section}; -use cosmic::iced_native::command::{Action, Command}; -use regex::Regex; -use slotmap::{SecondaryMap, SlotMap, SparseSecondaryMap}; - -pub type PageTask = Pin + Send>>; - -pub struct Model { - pub pages: SlotMap, - pub page_load: SecondaryMap PageTask>, - pub resource: HashMap>, - pub storage: HashMap>>, - pub sub_pages: SparseSecondaryMap>, - pub sections: SlotMap, - pub content: SparseSecondaryMap, -} - -impl Default for Model { - fn default() -> Self { - Self { - content: SparseSecondaryMap::new(), - pages: SlotMap::with_key(), - page_load: SecondaryMap::new(), - resource: HashMap::new(), - sections: SlotMap::with_key(), - storage: HashMap::new(), - sub_pages: SparseSecondaryMap::new(), - } - } -} - -impl Model { - /// Check if a page exists in the model. - #[must_use] - pub fn contains_item(&self, id: page::Entity) -> bool { - self.pages.contains_key(id) - } - - /// Returns the content of a page, if it has any. - #[must_use] - pub fn content(&self, page: page::Entity) -> Option<&[section::Entity]> { - self.content.get(page).map(Vec::as_slice) - } - - /// Get an immutable reference to data associated with a page. - #[must_use] - pub fn data(&self, id: page::Entity) -> Option<&Data> { - self.storage - .get(&TypeId::of::()) - .and_then(|storage| storage.get(id)) - .and_then(|data| data.downcast_ref()) - } - - /// Get a mutable reference to data associated with a page. - pub fn data_mut(&mut self, id: page::Entity) -> Option<&mut Data> { - self.storage - .get_mut(&TypeId::of::()) - .and_then(|storage| storage.get_mut(id)) - .and_then(|data| data.downcast_mut()) - } - - /// Associates data with the item. - pub fn data_set(&mut self, id: page::Entity, data: Data) { - if self.contains_item(id) { - self.storage - .entry(TypeId::of::()) - .or_insert_with(SecondaryMap::new) - .insert(id, Box::new(data)); - } - } - - /// Removes a specific data type from the item. - pub fn data_remove(&mut self, id: page::Entity) { - self.storage - .get_mut(&TypeId::of::()) - .and_then(|storage| storage.remove(id)); - } - - pub fn init_page(&mut self, id: page::Entity) -> Option> { - if let Some(func) = self.page_load.get(id).copied() { - return Some(Command::single(Action::Future(func(id)))); - } - - None - } - - // Registers a new page in the settings panel. - pub fn register(&mut self) -> Insert { - let id = self.pages.insert(P::page()); - - self.page_load.insert(id, P::load); - - if let Some(content) = P::content(&mut self.sections) { - self.content.insert(id, content); - } - - self.resource_register::(); - - P::sub_pages(Insert { id, model: self }) - } - - #[must_use] - pub fn resource(&self) -> Option<&Resource> { - self.resource - .get(&TypeId::of::()) - .and_then(|resource| resource.downcast_ref()) - } - - #[must_use] - pub fn resource_mut(&mut self) -> Option<&mut Resource> { - self.resource - .get_mut(&TypeId::of::()) - .and_then(|resource| resource.downcast_mut()) - } - - #[allow(unused_must_use)] - pub fn resource_register(&mut self) { - self.resource - .entry(TypeId::of::()) - .or_insert_with(|| Box::new(Resource::default())); - } - - /// Finds content of panels that match the search. - pub fn search<'a>( - &'a self, - rule: &'a Regex, - ) -> impl Iterator + 'a { - generator::Gn::new_scoped_local(|mut s| { - for (page, sections) in self.content.iter() { - for id in sections { - if self.sections[*id].matches_search(rule) { - s.yield_((page, *id)); - } - } - } - - generator::done!(); - }) - } - - /// Returns the sub-pages of a page, if it has any. - pub fn sub_pages(&self, page: page::Entity) -> Option<&[page::Entity]> { - self.sub_pages.get(page).map(AsRef::as_ref) - } -} - -pub struct Insert<'a> { - pub model: &'a mut Model, - pub id: page::Entity, -} - -impl<'a> Insert<'a> { - #[must_use] - pub fn id(self) -> page::Entity { - self.id - } - - #[must_use] - pub fn content(self, content: Content) -> Self { - self.model.content.insert(self.id, content); - self - } - - /// Adds a page and associates it with its parent page. - #[allow(clippy::return_self_not_must_use)] - #[allow(clippy::must_use_candidate)] - pub fn sub_page(self) -> Self { - let page = self.model.pages.insert(Meta { - parent: Some(self.id), - ..P::page() - }); - - self.model.page_load.insert(page, P::load); - - if let Some(content) = P::content(&mut self.model.sections) { - self.model.content.insert(page, content); - } - - self.model.resource_register::(); - - self.model - .sub_pages - .entry(self.id) - .expect("parent page missing") - .and_modify(|v| v.push(page)) - .or_insert_with(|| vec![page]); - - self - } -} diff --git a/src/page/section.rs b/src/page/section.rs deleted file mode 100644 index 98a1ca3..0000000 --- a/src/page/section.rs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2023 System76 -// SPDX-License-Identifier: GPL-3.0-only - -use derive_setters::Setters; -use regex::Regex; - -use crate::SettingsApp; - -slotmap::new_key_type! { - /// ID of a page section - pub struct Entity; -} - -/// A searchable sub-component of a page. Searches can group multiple sections together. -#[derive(Setters)] -#[must_use] -pub struct Section { - #[setters(into)] - pub title: String, - pub descriptions: Vec, - pub view_fn: for<'a> fn(&'a SettingsApp, &'a Section) -> cosmic::Element<'a, crate::Message>, - #[setters(bool)] - pub search_ignore: bool, -} - -impl Section { - pub const fn new() -> Self { - Self { - title: String::new(), - descriptions: Vec::new(), - view_fn: Self::unimplemented, - search_ignore: false, - } - } - - #[must_use] - pub fn matches_search(&self, rule: &Regex) -> bool { - if self.search_ignore { - return false; - } - - if rule.is_match(self.title.as_str()) { - return true; - } - - for description in &self.descriptions { - if rule.is_match(description.as_str()) { - return true; - } - } - - false - } - - #[must_use] - pub fn unimplemented<'a>( - _app: &'a SettingsApp, - _section: &'a Section, - ) -> cosmic::Element<'a, crate::Message> { - cosmic::widget::settings::view_column(vec![cosmic::widget::settings::view_section("") - .add(crate::widget::unimplemented_page()) - .into()]) - .into() - } -} diff --git a/src/page/system/about.rs b/src/page/system/about.rs deleted file mode 100644 index efa7a73..0000000 --- a/src/page/system/about.rs +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2023 System76 -// SPDX-License-Identifier: GPL-3.0-only - -use crate::{ - page::{self, desktop::wallpaper::settings, Content, Section}, - SettingsApp, -}; -use cosmic::{ - iced::{ - widget::{horizontal_space, row}, - Length, - }, - widget::{icon, list_column, settings, text}, -}; -use cosmic_settings_system::Info; -use slotmap::SlotMap; - -#[derive(Clone, Debug)] -pub enum Message { - Info(Info), -} - -#[derive(Clone, Debug, Default)] -pub struct Model { - info: Info, - support_page: page::Entity, -} - -impl Model { - pub fn update(&mut self, message: Message) { - match message { - Message::Info(info) => self.info = info, - } - } -} - -pub struct Page; - -impl page::Page for Page { - type Model = Model; - - fn page() -> page::Meta { - page::Meta::new("about", "help-about-symbolic") - .title(fl!("about")) - .description(fl!("about", "desc")) - } - - fn content(sections: &mut SlotMap) -> Option { - Some(vec![ - sections.insert(distributor_logo()), - sections.insert(device()), - sections.insert(hardware()), - sections.insert(os()), - sections.insert(related()), - ]) - } - - fn load(_page: page::Entity) -> crate::page::PageTask { - Box::pin(async move { crate::Message::About(Message::Info(Info::load())) }) - } -} - -fn device() -> Section { - Section::new() - .descriptions(vec![fl!("about-device"), fl!("about-device", "desc")]) - .view_fn(|app, section| { - let desc = §ion.descriptions; - let model = model(app); - - let device_name = settings::item::builder(&desc[0]) - .description(&desc[1]) - .control(text(&model.info.device_name)); - - list_column().add(device_name).into() - }) -} - -fn distributor_logo() -> Section { - Section::new().search_ignore().view_fn(|_app, _section| { - row!( - horizontal_space(Length::Fill), - icon("distributor-logo", 78), - horizontal_space(Length::Fill), - ) - // Add extra padding to reach 40px from the first section. - .padding([0, 16, 0, 16]) - .into() - }) -} - -fn hardware() -> Section { - Section::new() - .title(fl!("about-hardware")) - .descriptions(vec![ - fl!("about-hardware", "model"), - fl!("about-hardware", "memory"), - fl!("about-hardware", "processor"), - fl!("about-hardware", "graphics"), - fl!("about-hardware", "disk-capacity"), - ]) - .view_fn(|app, section| { - let desc = §ion.descriptions; - let model = model(app); - - let mut sections = settings::view_section(§ion.title) - .add(settings::item(&desc[0], text(&model.info.hardware_model))) - .add(settings::item(&desc[1], text(&model.info.memory))) - .add(settings::item(&desc[2], text(&model.info.processor))); - - for card in &model.info.graphics { - sections = sections.add(settings::item(&desc[3], text(card.as_str()))); - } - - // .add(settings::item(&desc[3], text(&model.info.graphics))) - sections - .add(settings::item(&desc[4], text(&model.info.disk_capacity))) - .into() - }) -} - -fn os() -> Section { - Section::new() - .title(fl!("about-os")) - .descriptions(vec![ - fl!("about-os", "os"), - fl!("about-os", "os-architecture"), - fl!("about-os", "desktop-environment"), - fl!("about-os", "windowing-system"), - ]) - .view_fn(|app, section| { - let model = model(app); - - let desc = §ion.descriptions; - settings::view_section(§ion.title) - .add(settings::item(&desc[0], text(&model.info.operating_system))) - .add(settings::item(&desc[1], text(&model.info.os_architecture))) - .add(settings::item( - &desc[2], - text(&model.info.desktop_environment), - )) - .add(settings::item(&desc[3], text(&model.info.windowing_system))) - .into() - }) -} - -fn related() -> Section { - Section::new() - .title(fl!("about-related")) - .descriptions(vec![fl!("about-related", "support")]) - .view_fn(|_app, section| { - settings::view_section(§ion.title) - .add(settings::item(§ion.descriptions[0], text("TODO"))) - .into() - }) -} - -fn model(app: &SettingsApp) -> &Model { - app.pages - .resource::() - .expect("missing system->about model") -} diff --git a/src/page/system/firmware.rs b/src/page/system/firmware.rs deleted file mode 100644 index 366cd69..0000000 --- a/src/page/system/firmware.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2023 System76 -// SPDX-License-Identifier: GPL-3.0-only - -use slotmap::SlotMap; - -use crate::page::{self, section, Content, Section}; - -pub struct Page; - -impl page::Page for Page { - type Model = super::Model; - - fn page() -> page::Meta { - page::Meta::new("firmware", "firmware-manager-symbolic") - .title(fl!("firmware")) - .description(fl!("firmware", "desc")) - } - - fn content(sections: &mut SlotMap) -> Option { - Some(vec![sections.insert(Section::new())]) - } -} diff --git a/src/page/system/mod.rs b/src/page/system/mod.rs deleted file mode 100644 index b21d6f1..0000000 --- a/src/page/system/mod.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2023 System76 -// SPDX-License-Identifier: GPL-3.0-only - -pub mod about; -pub mod firmware; -pub mod users; - -use crate::page; - -pub struct Page; - -impl page::Page for Page { - type Model = Model; - - fn page() -> page::Meta { - page::Meta::new("system", "system-users-symbolic").title(fl!("system")) - } - - fn sub_pages(page: page::Insert) -> page::Insert { - page.sub_page::() - .sub_page::() - .sub_page::() - } -} - -#[derive(Default)] -pub struct Model {} diff --git a/src/page/system/users.rs b/src/page/system/users.rs deleted file mode 100644 index 0fa16d8..0000000 --- a/src/page/system/users.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2023 System76 -// SPDX-License-Identifier: GPL-3.0-only - -use slotmap::SlotMap; - -use crate::page::{self, section, Content, Section}; - -pub struct Page; - -impl page::Page for Page { - type Model = super::Model; - - fn page() -> page::Meta { - page::Meta::new("users", "system-users-symbolic") - .title(fl!("users")) - .description(fl!("users", "desc")) - } - - fn content(sections: &mut SlotMap) -> Option { - Some(vec![sections.insert(Section::new())]) - } -} diff --git a/src/page/time/mod.rs b/src/page/time/mod.rs deleted file mode 100644 index 563b365..0000000 --- a/src/page/time/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2023 System76 -// SPDX-License-Identifier: GPL-3.0-only - -use crate::page; - -pub mod date; -pub mod region; - -pub struct Page; - -impl page::Page for Page { - type Model = (); - - fn page() -> page::Meta { - page::Meta::new("time", "preferences-system-time-symbolic") - .title(fl!("time")) - .description(fl!("time", "desc")) - } - - fn sub_pages(page: page::Insert) -> page::Insert { - page.sub_page::().sub_page::() - } -} diff --git a/src/page/time/region.rs b/src/page/time/region.rs deleted file mode 100644 index a17983e..0000000 --- a/src/page/time/region.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2023 System76 -// SPDX-License-Identifier: GPL-3.0-only - -use crate::page::{self, section, Content, Section}; -use slotmap::SlotMap; - -pub struct Page; - -impl page::Page for Page { - type Model = (); - - fn page() -> page::Meta { - page::Meta::new("time-region", "preferences-desktop-locale-symbolic") - .title(fl!("time-region")) - .description(fl!("time-region", "desc")) - } - - fn content(sections: &mut SlotMap) -> Option { - Some(vec![sections.insert(Section::new())]) - } -}