diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d24f4503..e1280769 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,4 +26,3 @@ jobs: cargo test --verbose --workspace cargo test --verbose --workspace -- --ignored cargo test --verbose --workspace --all-features - cargo test --verbose --workspace --all-features -- --ignored diff --git a/Cargo.lock b/Cargo.lock index d455278b..4d3dde36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,7 +77,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.9.3", + "bitflags 2.9.4", "cc", "cesu8", "jni", @@ -91,18 +91,21 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "android-build" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cac4c64175d504608cf239756339c07f6384a476f97f20a7043f92920b0b8fd" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "android-properties" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -190,24 +193,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "ashpd" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3d60bee1a1d38c2077030f4788e1b4e31058d2e79a8cfc8f2b440bd44db290" -dependencies = [ - "async-fs", - "async-net", - "enumflags2", - "futures-channel", - "futures-util", - "rand 0.8.5", - "serde", - "serde_repr", - "url", - "zbus", -] - [[package]] name = "ashpd" version = "0.11.0" @@ -236,23 +221,12 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" dependencies = [ - "event-listener 5.4.1", + "event-listener", "event-listener-strategy", "futures-core", "pin-project-lite", ] -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener 2.5.3", - "futures-core", -] - [[package]] name = "async-channel" version = "2.5.0" @@ -290,21 +264,6 @@ dependencies = [ "futures-lite", ] -[[package]] -name = "async-global-executor" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" -dependencies = [ - "async-channel 2.5.0", - "async-executor", - "async-io", - "async-lock", - "blocking", - "futures-lite", - "once_cell", -] - [[package]] name = "async-io" version = "2.5.0" @@ -318,7 +277,7 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix 1.0.8", + "rustix 1.1.2", "slab", "windows-sys 0.60.2", ] @@ -329,7 +288,7 @@ version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" dependencies = [ - "event-listener 5.4.1", + "event-listener", "event-listener-strategy", "pin-project-lite", ] @@ -351,16 +310,16 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00" dependencies = [ - "async-channel 2.5.0", + "async-channel", "async-io", "async-lock", "async-signal", "async-task", "blocking", "cfg-if", - "event-listener 5.4.1", + "event-listener", "futures-lite", - "rustix 1.0.8", + "rustix 1.1.2", ] [[package]] @@ -386,38 +345,12 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 1.0.8", + "rustix 1.1.2", "signal-hook-registry", "slab", "windows-sys 0.60.2", ] -[[package]] -name = "async-std" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" -dependencies = [ - "async-channel 1.9.0", - "async-global-executor", - "async-io", - "async-lock", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - [[package]] name = "async-task" version = "4.7.1" @@ -591,9 +524,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.3" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" dependencies = [ "serde", ] @@ -643,7 +576,7 @@ version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" dependencies = [ - "async-channel 2.5.0", + "async-channel", "async-task", "futures-io", "futures-lite", @@ -734,7 +667,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "log", "polling", "rustix 0.38.44", @@ -773,10 +706,11 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.34" +version = "1.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" +checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", @@ -833,16 +767,15 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-link", + "windows-link 0.2.0", ] [[package]] @@ -874,18 +807,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.46" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57" +checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.46" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41" +checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6" dependencies = [ "anstyle", "clap_lex", @@ -1061,7 +994,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "core-foundation 0.10.1", "core-graphics-types 0.2.0", "foreign-types 0.5.0", @@ -1085,7 +1018,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "core-foundation 0.10.1", "libc", ] @@ -1096,7 +1029,7 @@ version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da46a9d5a8905cc538a4a5bceb6a4510de7a51049c5588c0114efce102bcbbe8" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "fontdb 0.16.2", "log", "rangemap", @@ -1265,20 +1198,6 @@ dependencies = [ "iced", ] -[[package]] -name = "dark-light" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e1a09f280e29a8b00bc7e81eca5ac87dca0575639c9422a5fa25a07bb884b8" -dependencies = [ - "ashpd 0.10.3", - "async-std", - "objc2 0.5.2", - "objc2-foundation 0.2.2", - "web-sys", - "winreg", -] - [[package]] name = "data-encoding" version = "2.9.0" @@ -1293,9 +1212,9 @@ checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" dependencies = [ "powerfmt", ] @@ -1328,7 +1247,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -1343,7 +1262,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "block2 0.6.1", "libc", "objc2 0.6.2", @@ -1403,7 +1322,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98888c4bbd601524c11a7ed63f814b8825f420514f78e96f752c437ae9cbb5d1" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "bytemuck", "drm-ffi", "drm-fourcc", @@ -1515,12 +1434,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -1548,12 +1467,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - [[package]] name = "event-listener" version = "5.4.1" @@ -1571,7 +1484,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.4.1", + "event-listener", "pin-project-lite", ] @@ -1616,6 +1529,26 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fax" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" +dependencies = [ + "fax_derive", +] + +[[package]] +name = "fax_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "fdeflate" version = "0.3.7" @@ -1632,6 +1565,12 @@ dependencies = [ "iced", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" + [[package]] name = "flate2" version = "1.1.2" @@ -1940,19 +1879,19 @@ dependencies = [ [[package]] name = "gethostname" -version = "0.4.3" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +checksum = "fc257fdb4038301ce4b9cd1b3b51704509692bb3ff716a410cbd07925d9dae55" dependencies = [ - "libc", - "windows-targets 0.48.5", + "rustix 1.1.2", + "windows-targets 0.52.6", ] [[package]] name = "getopts" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1" +checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" dependencies = [ "unicode-width", ] @@ -1979,7 +1918,7 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.3+wasi-0.2.4", + "wasi 0.14.5+wasi-0.2.4", ] [[package]] @@ -2041,18 +1980,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "gloo-timers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "glow" version = "0.16.0" @@ -2091,7 +2018,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "gpu-alloc-types", ] @@ -2101,7 +2028,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", ] [[package]] @@ -2122,7 +2049,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "gpu-descriptor-types", "hashbrown", ] @@ -2133,7 +2060,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", ] [[package]] @@ -2518,9 +2445,8 @@ dependencies = [ name = "iced_core" version = "0.14.0-dev" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "bytes", - "dark-light", "glam", "lilt", "log", @@ -2571,7 +2497,7 @@ dependencies = [ name = "iced_graphics" version = "0.14.0-dev" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "bytemuck", "cosmic-text", "half", @@ -2644,7 +2570,7 @@ dependencies = [ "iced_runtime", "iced_selector", "nom 8.0.0", - "png", + "png 0.18.0", "sha2", "thiserror 2.0.16", ] @@ -2679,7 +2605,7 @@ dependencies = [ name = "iced_wgpu" version = "0.14.0-dev" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "bytemuck", "cryoglyph", "futures", @@ -2719,6 +2645,7 @@ dependencies = [ "iced_debug", "iced_program", "log", + "mundy", "rustc-hash 2.1.1", "sysinfo", "thiserror 2.0.16", @@ -2838,9 +2765,9 @@ dependencies = [ [[package]] name = "image" -version = "0.25.6" +version = "0.25.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" +checksum = "529feb3e6769d234375c4cf1ee2ce713682b8e76538cb13f9fc23e1400a591e7" dependencies = [ "bytemuck", "byteorder-lite", @@ -2848,8 +2775,9 @@ dependencies = [ "exr", "gif", "image-webp", + "moxcms", "num-traits", - "png", + "png 0.18.0", "qoi", "ravif", "rayon", @@ -2883,9 +2811,9 @@ checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" [[package]] name = "indexmap" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" +checksum = "206a8042aec68fa4a62e8d3f7aa4ceb508177d9324faf261e1959e495b7a1921" dependencies = [ "equivalent", "hashbrown", @@ -2923,7 +2851,7 @@ version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "cfg-if", "libc", ] @@ -3038,9 +2966,9 @@ checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738" dependencies = [ "once_cell", "wasm-bindgen", @@ -3093,15 +3021,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - [[package]] name = "layout" version = "0.1.0" @@ -3124,9 +3043,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lebe" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" +checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" [[package]] name = "libc" @@ -3166,7 +3085,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "libc", "redox_syscall 0.5.17", ] @@ -3200,9 +3119,9 @@ checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7" [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" @@ -3236,12 +3155,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" -dependencies = [ - "value-bag", -] +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "loop9" @@ -3261,9 +3177,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ea4e65087ff52f3862caff188d489f1fab49a0cb09e01b2e3f1a617b10aaed" +checksum = "bfe949189f46fabb938b3a9a0be30fdd93fd8a09260da863399a8cf3db756ec8" [[package]] name = "lyon" @@ -3364,11 +3280,11 @@ checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memfd" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" +checksum = "ad38eb12aea514a0466ea40a80fd8cc83637065948eb4a426e4aa46261175227" dependencies = [ - "rustix 0.38.44", + "rustix 1.1.2", ] [[package]] @@ -3395,7 +3311,7 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "block", "core-graphics-types 0.2.0", "foreign-types 0.5.0", @@ -3454,6 +3370,16 @@ dependencies = [ "iced", ] +[[package]] +name = "moxcms" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd32fa8935aeadb8a8a6b6b351e40225570a37c43de67690383d87ef170cd08" +dependencies = [ + "num-traits", + "pxfm", +] + [[package]] name = "multer" version = "2.1.0" @@ -3488,6 +3414,31 @@ dependencies = [ "voronator", ] +[[package]] +name = "mundy" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5f507e52285e981a349f7224e0ac7eaf014e1a9cce399471547c8dfbc018b79" +dependencies = [ + "android-build", + "async-io", + "cfg-if", + "dispatch", + "futures-channel", + "futures-lite", + "jni", + "ndk-context", + "objc2 0.6.2", + "objc2-app-kit 0.3.1", + "objc2-foundation 0.3.1", + "pin-project-lite", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows 0.61.3", + "zbus", +] + [[package]] name = "mutate_once" version = "0.1.2" @@ -3502,7 +3453,7 @@ checksum = "916cbc7cb27db60be930a4e2da243cf4bc39569195f22fd8ee419cd31d5b662c" dependencies = [ "arrayvec", "bit-set", - "bitflags 2.9.3", + "bitflags 2.9.4", "cfg-if", "cfg_aliases", "codespan-reporting", @@ -3543,7 +3494,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "jni-sys", "log", "ndk-sys", @@ -3579,7 +3530,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "cfg-if", "cfg_aliases", "libc", @@ -3622,12 +3573,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" dependencies = [ - "overload", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -3770,14 +3720,14 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "block2 0.5.1", "libc", "objc2 0.5.2", - "objc2-core-data", - "objc2-core-image", + "objc2-core-data 0.2.2", + "objc2-core-image 0.2.2", "objc2-foundation 0.2.2", - "objc2-quartz-core", + "objc2-quartz-core 0.2.2", ] [[package]] @@ -3786,10 +3736,17 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "block2 0.6.1", + "libc", "objc2 0.6.2", + "objc2-cloud-kit 0.3.1", + "objc2-core-data 0.3.1", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image 0.3.1", "objc2-foundation 0.3.1", + "objc2-quartz-core 0.3.1", ] [[package]] @@ -3798,13 +3755,24 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", ] +[[package]] +name = "objc2-cloud-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17614fdcd9b411e6ff1117dfb1d0150f908ba83a7df81b1f118005fe0a8ea15d" +dependencies = [ + "bitflags 2.9.4", + "objc2 0.6.2", + "objc2-foundation 0.3.1", +] + [[package]] name = "objc2-contacts" version = "0.2.2" @@ -3822,23 +3790,47 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] +[[package]] +name = "objc2-core-data" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291fbbf7d29287518e8686417cf7239c74700fd4b607623140a7d4a3c834329d" +dependencies = [ + "bitflags 2.9.4", + "objc2 0.6.2", + "objc2-foundation 0.3.1", +] + [[package]] name = "objc2-core-foundation" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "dispatch2", "objc2 0.6.2", ] +[[package]] +name = "objc2-core-graphics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" +dependencies = [ + "bitflags 2.9.4", + "dispatch2", + "objc2 0.6.2", + "objc2-core-foundation", + "objc2-io-surface", +] + [[package]] name = "objc2-core-image" version = "0.2.2" @@ -3851,6 +3843,16 @@ dependencies = [ "objc2-metal", ] +[[package]] +name = "objc2-core-image" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79b3dc0cc4386b6ccf21c157591b34a7f44c8e75b064f85502901ab2188c007e" +dependencies = [ + "objc2 0.6.2", + "objc2-foundation 0.3.1", +] + [[package]] name = "objc2-core-location" version = "0.2.2" @@ -3875,7 +3877,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "block2 0.5.1", "dispatch", "libc", @@ -3888,7 +3890,20 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", + "block2 0.6.1", + "libc", + "objc2 0.6.2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c" +dependencies = [ + "bitflags 2.9.4", "objc2 0.6.2", "objc2-core-foundation", ] @@ -3911,7 +3926,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3923,13 +3938,24 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", "objc2-metal", ] +[[package]] +name = "objc2-quartz-core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ffb6a0cd5f182dc964334388560b12a57f7b74b3e2dec5e2722aa2dfb2ccd5" +dependencies = [ + "bitflags 2.9.4", + "objc2 0.6.2", + "objc2-foundation 0.3.1", +] + [[package]] name = "objc2-symbols" version = "0.2.2" @@ -3946,16 +3972,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "block2 0.5.1", "objc2 0.5.2", - "objc2-cloud-kit", - "objc2-core-data", - "objc2-core-image", + "objc2-cloud-kit 0.2.2", + "objc2-core-data 0.2.2", + "objc2-core-image 0.2.2", "objc2-core-location", "objc2-foundation 0.2.2", "objc2-link-presentation", - "objc2-quartz-core", + "objc2-quartz-core 0.2.2", "objc2-symbols", "objc2-uniform-type-identifiers", "objc2-user-notifications", @@ -3978,7 +4004,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", @@ -4015,7 +4041,7 @@ version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "libc", "once_cell", "onig_sys", @@ -4054,7 +4080,7 @@ version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "cfg-if", "foreign-types 0.3.2", "libc", @@ -4150,12 +4176,6 @@ dependencies = [ "syn", ] -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "owned_ttf_parser" version = "0.25.1" @@ -4413,6 +4433,19 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "png" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" +dependencies = [ + "bitflags 2.9.4", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "pokedex" version = "0.1.0" @@ -4435,7 +4468,7 @@ dependencies = [ "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 1.0.8", + "rustix 1.1.2", "windows-sys 0.60.2", ] @@ -4553,7 +4586,7 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "getopts", "memchr", "pulldown-cmark-escape", @@ -4566,6 +4599,15 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae" +[[package]] +name = "pxfm" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55f4fedc84ed39cb7a489322318976425e42a147e2be79d8f878e2884f94e84" +dependencies = [ + "num-traits", +] + [[package]] name = "qoi" version = "0.4.1" @@ -4805,7 +4847,7 @@ version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", ] [[package]] @@ -4942,7 +4984,7 @@ version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed" dependencies = [ - "ashpd 0.11.0", + "ashpd", "block2 0.6.1", "dispatch2", "js-sys", @@ -5013,7 +5055,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "errno", "libc", "linux-raw-sys 0.4.15", @@ -5022,15 +5064,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "errno", "libc", - "linux-raw-sys 0.9.4", - "windows-sys 0.60.2", + "linux-raw-sys 0.11.0", + "windows-sys 0.61.0", ] [[package]] @@ -5103,7 +5145,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "bytemuck", "libm", "smallvec", @@ -5131,11 +5173,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] @@ -5186,7 +5228,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -5195,9 +5237,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", @@ -5420,7 +5462,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "calloop", "calloop-wayland-source", "cursor-icon", @@ -5456,7 +5498,7 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a33bd3e260892199c3ccfc487c88b2da2265080acb316cd920da72fdfd7c599f" dependencies = [ - "async-channel 2.5.0", + "async-channel", "async-executor", "async-fs", "async-io", @@ -5514,7 +5556,7 @@ dependencies = [ "memmap2", "objc2 0.5.2", "objc2-foundation 0.2.2", - "objc2-quartz-core", + "objc2-quartz-core 0.2.2", "raw-window-handle 0.6.2", "redox_syscall 0.5.17", "rustix 0.38.44", @@ -5549,7 +5591,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", ] [[package]] @@ -5739,7 +5781,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -5790,15 +5832,15 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.21.0" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" +checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.8", - "windows-sys 0.60.2", + "rustix 1.1.2", + "windows-sys 0.61.0", ] [[package]] @@ -5870,23 +5912,25 @@ dependencies = [ [[package]] name = "tiff" -version = "0.9.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f" dependencies = [ + "fax", "flate2", - "jpeg-decoder", + "half", + "quick-error", "weezl", + "zune-jpeg", ] [[package]] name = "time" -version = "0.3.41" +version = "0.3.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "83bde6f1ec10e72d583d91623c939f623002284ef622b87de38cfd546cbf2031" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde", @@ -5896,15 +5940,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -5921,7 +5965,7 @@ dependencies = [ "bytemuck", "cfg-if", "log", - "png", + "png 0.17.16", "tiny-skia-path", ] @@ -6166,7 +6210,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "bytes", "futures-util", "http 1.3.1", @@ -6236,9 +6280,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "nu-ansi-term", "sharded-slab", @@ -6336,9 +6380,9 @@ checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "unicode-linebreak" @@ -6454,9 +6498,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.18.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ "getrandom 0.3.3", "js-sys", @@ -6482,12 +6526,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" -[[package]] -name = "value-bag" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" - [[package]] name = "vcpkg" version = "0.2.15" @@ -6591,30 +6629,40 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.14.3+wasi-0.2.4" +version = "0.14.5+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" +checksum = "a4494f6290a82f5fe584817a676a34b9d6763e8d9d18204009fb31dceca98fd4" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.0+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03fa2761397e5bd52002cd7e73110c71af2109aca4e521a9f40473fe685b0a24" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb" dependencies = [ "bumpalo", "log", @@ -6626,9 +6674,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "0ca85039a9b469b38336411d6d6ced91f3fc87109a2a27b0c197663f5144dffe" dependencies = [ "cfg-if", "js-sys", @@ -6639,9 +6687,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6649,9 +6697,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa" dependencies = [ "proc-macro2", "quote", @@ -6662,9 +6710,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1" dependencies = [ "unicode-ident", ] @@ -6684,9 +6732,9 @@ dependencies = [ [[package]] name = "wasmtimer" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8d49b5d6c64e8558d9b1b065014426f35c18de636895d24893dbbd329743446" +checksum = "1c598d6b99ea013e35844697fc4670d08339d5cda15588f193c6beedd12f644b" dependencies = [ "futures", "js-sys", @@ -6704,7 +6752,7 @@ checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" dependencies = [ "cc", "downcast-rs", - "rustix 1.0.8", + "rustix 1.1.2", "scoped-tls", "smallvec", "wayland-sys", @@ -6716,8 +6764,8 @@ version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" dependencies = [ - "bitflags 2.9.3", - "rustix 1.0.8", + "bitflags 2.9.4", + "rustix 1.1.2", "wayland-backend", "wayland-scanner", ] @@ -6728,7 +6776,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "cursor-icon", "wayland-backend", ] @@ -6739,7 +6787,7 @@ version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29" dependencies = [ - "rustix 1.0.8", + "rustix 1.1.2", "wayland-client", "xcursor", ] @@ -6750,7 +6798,7 @@ version = "0.32.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "wayland-backend", "wayland-client", "wayland-scanner", @@ -6762,7 +6810,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6775,7 +6823,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6807,9 +6855,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "77e4b637749ff0d92b8fad63aa1f7cff3cbe125fd49c175cd6345e7272638b12" dependencies = [ "js-sys", "wasm-bindgen", @@ -6882,7 +6930,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70b6ff82bbf6e9206828e1a3178e851f8c20f1c9028e74dd3a8090741ccd5798" dependencies = [ "arrayvec", - "bitflags 2.9.3", + "bitflags 2.9.4", "cfg-if", "cfg_aliases", "document-features", @@ -6913,7 +6961,7 @@ dependencies = [ "arrayvec", "bit-set", "bit-vec", - "bitflags 2.9.3", + "bitflags 2.9.4", "cfg_aliases", "document-features", "hashbrown", @@ -6982,7 +7030,7 @@ dependencies = [ "arrayvec", "ash", "bit-set", - "bitflags 2.9.3", + "bitflags 2.9.4", "block", "bytemuck", "cfg-if", @@ -7026,7 +7074,7 @@ version = "26.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eca7a8d8af57c18f57d393601a1fb159ace8b2328f1b6b5f80893f7d672c9ae2" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "bytemuck", "js-sys", "log", @@ -7052,11 +7100,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -7099,6 +7147,28 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + [[package]] name = "windows-core" version = "0.57.0" @@ -7132,11 +7202,22 @@ checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement 0.60.0", "windows-interface 0.59.1", - "windows-link", + "windows-link 0.1.3", "windows-result 0.3.4", "windows-strings 0.4.2", ] +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", +] + [[package]] name = "windows-implement" version = "0.57.0" @@ -7209,13 +7290,29 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + [[package]] name = "windows-registry" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" dependencies = [ - "windows-link", + "windows-link 0.1.3", "windows-result 0.3.4", "windows-strings 0.4.2", ] @@ -7244,7 +7341,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -7263,7 +7360,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -7311,6 +7408,15 @@ dependencies = [ "windows-targets 0.53.3", ] +[[package]] +name = "windows-sys" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +dependencies = [ + "windows-link 0.2.0", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -7363,7 +7469,7 @@ version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ - "windows-link", + "windows-link 0.1.3", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -7374,6 +7480,15 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -7562,7 +7677,7 @@ dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.9.3", + "bitflags 2.9.4", "block2 0.5.1", "bytemuck", "calloop", @@ -7614,21 +7729,11 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "wit-bindgen" -version = "0.45.0" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" +checksum = "5c573471f125075647d03df72e026074b7203790d41351cd6edc96f46bcccd36" [[package]] name = "writeable" @@ -7649,24 +7754,24 @@ dependencies = [ [[package]] name = "x11rb" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" dependencies = [ "as-raw-xcb-connection", "gethostname", "libc", "libloading", "once_cell", - "rustix 0.38.44", + "rustix 1.1.2", "x11rb-protocol", ] [[package]] name = "x11rb-protocol" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" [[package]] name = "xcursor" @@ -7680,7 +7785,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "dlib", "log", "once_cell", @@ -7752,9 +7857,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.10.0" +version = "5.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a073be99ace1adc48af593701c8015cd9817df372e14a1a6b0ee8f8bf043be" +checksum = "2d07e46d035fb8e375b2ce63ba4e4ff90a7f73cf2ffb0138b29e1158d2eaadf7" dependencies = [ "async-broadcast", "async-executor", @@ -7766,7 +7871,7 @@ dependencies = [ "async-trait", "blocking", "enumflags2", - "event-listener 5.4.1", + "event-listener", "futures-core", "futures-lite", "hex", @@ -7785,9 +7890,9 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.10.0" +version = "5.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e80cd713a45a49859dcb648053f63265f4f2851b6420d47a958e5697c68b131" +checksum = "57e797a9c847ed3ccc5b6254e8bcce056494b375b511b3d6edcec0aeb4defaca" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -7818,18 +7923,18 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", @@ -7913,9 +8018,9 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1f7e205ce79eb2da3cd71c5f55f3589785cb7c79f6a03d1c8d1491bda5d089" +checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" dependencies = [ "zune-core", ] diff --git a/Cargo.toml b/Cargo.toml index a363b5c9..7971c150 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ all-features = true maintenance = { status = "actively-developed" } [features] -default = ["wgpu", "tiny-skia", "crisp", "web-colors", "auto-detect-theme", "thread-pool"] +default = ["wgpu", "tiny-skia", "crisp", "web-colors", "thread-pool", "linux-theme-detection"] # Enables the `wgpu` GPU-accelerated renderer backend wgpu = ["iced_renderer/wgpu", "iced_widget/wgpu"] # Enables the `tiny-skia` software renderer backend @@ -56,14 +56,14 @@ tokio = ["iced_futures/tokio"] # Enables `smol` as the `executor::Default` on native platforms smol = ["iced_futures/smol"] # Enables querying system information -system = ["iced_winit/system"] +sysinfo = ["iced_winit/sysinfo"] # Enables broken "sRGB linear" blending to reproduce color management of the Web web-colors = ["iced_renderer/web-colors"] # Enables pixel snapping for crisp edges by default (can cause jitter!) crisp = ["iced_core/crisp", "iced_widget/crisp"] # Enables the WebGL backend webgl = ["iced_renderer/webgl"] -# Enables syntax highligthing +# Enables syntax highlighting highlighter = ["iced_highlighter", "iced_widget/highlighter"] # Enables the `widget::selector` module selector = ["iced_runtime/selector"] @@ -71,14 +71,18 @@ selector = ["iced_runtime/selector"] advanced = ["iced_core/advanced", "iced_widget/advanced"] # Embeds Fira Sans into the final application; useful for testing and Wasm builds fira-sans = ["iced_renderer/fira-sans"] -# Auto-detects light/dark mode for the built-in theme -auto-detect-theme = ["iced_core/auto-detect-theme"] +# Enables basic text shaping by default +basic-shaping = ["iced_core/basic-shaping"] +# Enables advanced text shaping by default +advanced-shaping = ["iced_core/advanced-shaping"] # Enables strict assertions for debugging purposes at the expense of performance strict-assertions = ["iced_renderer/strict-assertions"] # Redraws on every runtime event, and not only when a widget requests it unconditional-rendering = ["iced_winit/unconditional-rendering"] # Enables support for the `sipper` library sipper = ["iced_runtime/sipper"] +# Enables Linux system theme detection +linux-theme-detection = ["iced_winit/linux-theme-detection"] [dependencies] iced_debug.workspace = true @@ -181,10 +185,9 @@ bytemuck = { version = "1.0", features = ["derive"] } bytes = "1.6" cargo-hot = { package = "cargo-hot-protocol", git = "https://github.com/hecrj/cargo-hot.git", rev = "b8dc518b8640928178a501257e353b73bc06cf47" } cosmic-text = "0.14" -dark-light = "2.0" +cryoglyph = { git = "https://github.com/iced-rs/cryoglyph.git", rev = "453cedec0d2ec563bd7fa87e84a2319bcebb1ba3" } futures = { version = "0.3", default-features = false } glam = "0.25" -cryoglyph = { git = "https://github.com/iced-rs/cryoglyph.git", rev = "453cedec0d2ec563bd7fa87e84a2319bcebb1ba3" } guillotiere = "0.6" half = "2.2" image = { version = "0.25", default-features = false } @@ -194,18 +197,19 @@ lilt = "0.8" log = "0.4" lyon = "1.0" lyon_path = "1.0" +mundy = { version = "0.2", default-features = false } nom = "8" num-traits = "0.2" ouroboros = "0.18" -png = "0.17" +png = "0.18" pulldown-cmark = "0.12" qrcode = { version = "0.13", default-features = false } raw-window-handle = "0.6" resvg = "0.42" rfd = "0.15" rustc-hash = "2.0" -serde = "1.0" semver = "1.0" +serde = "1.0" sha2 = "0.10" sipper = "0.1" smol = "2" diff --git a/core/Cargo.toml b/core/Cargo.toml index f57aaa4d..31296446 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -14,9 +14,10 @@ keywords.workspace = true workspace = true [features] -auto-detect-theme = ["dep:dark-light"] advanced = [] crisp = [] +basic-shaping = [] +advanced-shaping = [] [dependencies] bitflags.workspace = true @@ -30,9 +31,6 @@ smol_str.workspace = true thiserror.workspace = true web-time.workspace = true -dark-light.workspace = true -dark-light.optional = true - serde.workspace = true serde.optional = true serde.features = ["derive"] diff --git a/core/src/layout/flex.rs b/core/src/layout/flex.rs index 0afc6e1b..33dfe6e8 100644 --- a/core/src/layout/flex.rs +++ b/core/src/layout/flex.rs @@ -78,14 +78,19 @@ where let total_spacing = spacing * items.len().saturating_sub(1) as f32; let max_cross = axis.cross(limits.max()); - let compression = limits.compression(); - let (main_compress, cross_compress) = - axis.pack(compression.width, compression.height); + let (main_compress, cross_compress) = { + let compression = limits.compression(); + axis.pack(compression.width, compression.height) + }; + + let compression = { + let (compress_x, compress_y) = axis.pack(main_compress, false); + Size::new(compress_x, compress_y) + }; let mut fill_main_sum = 0; let mut some_fill_cross = false; let mut cross = if cross_compress { 0.0 } else { max_cross }; - let mut available = axis.main(limits.max()) - total_spacing; let mut nodes: Vec = Vec::with_capacity(items.len()); diff --git a/core/src/text.rs b/core/src/text.rs index 525381ef..fdebd03a 100644 --- a/core/src/text.rs +++ b/core/src/text.rs @@ -130,8 +130,17 @@ impl From for alignment::Horizontal { } /// The shaping strategy of some text. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Shaping { + /// Auto-detect the best shaping strategy from the text. + /// + /// This strategy will use [`Basic`](Self::Basic) shaping if the + /// text consists of only ASCII characters; otherwise, it will + /// use [`Advanced`](Self::Advanced) shaping. + /// + /// This is the default, if neither the `basic-shaping` nor `advanced-shaping` + /// features are enabled. + Auto, /// No shaping and no font fallback. /// /// This shaping strategy is very cheap, but it will not display complex @@ -140,8 +149,8 @@ pub enum Shaping { /// You should use this strategy when you have complete control of the text /// and the font you are displaying in your application. /// - /// This is the default. - #[default] + /// This will be the default if the `basic-shaping` feature is enabled and + /// the `advanced-shaping` feature is disabled. Basic, /// Advanced text shaping and font fallback. /// @@ -150,9 +159,23 @@ pub enum Shaping { /// may be needed to display all of the glyphs. /// /// Advanced shaping is expensive! You should only enable it when necessary. + /// + /// This will be the default if the `advanced-shaping` feature is enabled. Advanced, } +impl Default for Shaping { + fn default() -> Self { + if cfg!(feature = "advanced-shaping") { + Self::Advanced + } else if cfg!(feature = "basic-shaping") { + Self::Basic + } else { + Self::Auto + } + } +} + /// The wrapping strategy of some text. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] pub enum Wrapping { diff --git a/core/src/theme.rs b/core/src/theme.rs index 0adf9ab2..1260bca8 100644 --- a/core/src/theme.rs +++ b/core/src/theme.rs @@ -166,31 +166,6 @@ impl Theme { } } -impl Default for Theme { - fn default() -> Self { - #[cfg(feature = "auto-detect-theme")] - { - use std::sync::LazyLock; - - static DEFAULT: LazyLock = LazyLock::new(|| { - match dark_light::detect() - .unwrap_or(dark_light::Mode::Unspecified) - { - dark_light::Mode::Dark => Theme::Dark, - dark_light::Mode::Light | dark_light::Mode::Unspecified => { - Theme::Light - } - } - }); - - DEFAULT.clone() - } - - #[cfg(not(feature = "auto-detect-theme"))] - Theme::Light - } -} - impl fmt::Display for Theme { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -256,6 +231,18 @@ impl fmt::Display for Custom { } } +/// A theme mode, denoting the tone or brightness of a theme. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub enum Mode { + /// No specific tone. + #[default] + None, + /// A mode referring to themes with light tones. + Light, + /// A mode referring to themes with dark tones. + Dark, +} + /// The base style of a theme. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Style { @@ -268,7 +255,13 @@ pub struct Style { /// The default blank style of a theme. pub trait Base { - /// Returns the default base [`Style`] of a theme. + /// Returns the default theme for the preferred [`Mode`]. + fn default(preference: Mode) -> Self; + + /// Returns the [`Mode`] of the theme. + fn mode(&self) -> Mode; + + /// Returns the default base [`Style`] of the theme. fn base(&self) -> Style; /// Returns the color [`Palette`] of the theme. @@ -280,6 +273,39 @@ pub trait Base { } impl Base for Theme { + fn default(preference: Mode) -> Self { + use std::env; + use std::sync::OnceLock; + + static SYSTEM: OnceLock> = OnceLock::new(); + + let system = SYSTEM.get_or_init(|| { + let name = env::var("ICED_THEME").ok()?; + + Theme::ALL + .iter() + .find(|theme| theme.to_string() == name) + .cloned() + }); + + if let Some(system) = system { + return system.clone(); + } + + match preference { + Mode::None | Mode::Light => Self::Light, + Mode::Dark => Self::Dark, + } + } + + fn mode(&self) -> Mode { + if self.extended_palette().is_dark { + Mode::Dark + } else { + Mode::Light + } + } + fn base(&self) -> Style { default(self) } diff --git a/core/src/window/screenshot.rs b/core/src/window/screenshot.rs index 424168bb..5bb9334b 100644 --- a/core/src/window/screenshot.rs +++ b/core/src/window/screenshot.rs @@ -15,7 +15,7 @@ pub struct Screenshot { pub size: Size, /// The scale factor of the [`Screenshot`]. This can be useful when converting between widget /// bounds (which are in logical pixels) to crop screenshots. - pub scale_factor: f64, + pub scale_factor: f32, } impl Debug for Screenshot { @@ -35,7 +35,7 @@ impl Screenshot { pub fn new( bytes: impl Into, size: Size, - scale_factor: f64, + scale_factor: f32, ) -> Self { Self { bytes: bytes.into(), diff --git a/devtools/src/lib.rs b/devtools/src/lib.rs index b859341c..d548b55a 100644 --- a/devtools/src/lib.rs +++ b/devtools/src/lib.rs @@ -11,7 +11,7 @@ mod time_machine; use crate::core::border; use crate::core::keyboard; -use crate::core::theme::{self, Base, Theme}; +use crate::core::theme::{self, Theme}; use crate::core::time::seconds; use crate::core::window; use crate::core::{ @@ -98,7 +98,11 @@ where state.subscription(&self.program) } - fn theme(&self, state: &Self::State, window: window::Id) -> Self::Theme { + fn theme( + &self, + state: &Self::State, + window: window::Id, + ) -> Option { state.theme(&self.program, window) } @@ -106,7 +110,7 @@ where state.style(&self.program, theme) } - fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { + fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 { state.scale_factor(&self.program, window) } } @@ -161,11 +165,11 @@ where show_notification: true, time_machine: TimeMachine::new(), }, - task::blocking(|mut sender| { + Task::batch([task::blocking(|mut sender| { thread::sleep(seconds(2)); let _ = sender.try_send(()); }) - .map(|_| Message::HideNotification), + .map(|_| Message::HideNotification)]), ) } @@ -306,10 +310,7 @@ where let state = self.state(); let view = { - let theme = program.theme(state, window); - - let view: Element<'_, _, Theme, _> = - themer(theme, program.view(state, window)).into(); + let view = program.view(state, window); if self.time_machine.is_rewinding() { view.map(|_| Event::Discard) @@ -318,11 +319,13 @@ where } }; - let theme = program - .theme(state, window) - .palette() - .map(|palette| Theme::custom("iced devtools", palette)) - .unwrap_or_default(); + let theme = || { + program + .theme(state, window) + .as_ref() + .and_then(theme::Base::palette) + .map(|palette| Theme::custom("iced devtools", palette)) + }; let setup = if let Mode::Setup(setup) = &self.mode { let stage: Element<'_, _, Theme, P::Renderer> = match setup { @@ -346,7 +349,7 @@ where } else { None } - .map(|mode| Element::from(mode).map(Event::Message)); + .map(|setup| themer(theme(), Element::from(setup).map(Event::Message))); let notification = self .show_notification @@ -359,20 +362,20 @@ where }) }); - themer( - theme, - stack![view] - .height(Fill) - .push_maybe(setup.map(opaque)) - .push_maybe(notification.map(|notification| { + stack![view] + .height(Fill) + .push_maybe(setup.map(opaque)) + .push_maybe(notification.map(|notification| { + themer( + theme(), bottom_right(opaque( container(notification) .padding(10) .style(container::dark), - )) - })), - ) - .into() + )), + ) + })) + .into() } pub fn subscription(&self, program: &P) -> Subscription> { @@ -394,7 +397,7 @@ where Subscription::batch([subscription, hotkeys, commands]) } - pub fn theme(&self, program: &P, window: window::Id) -> P::Theme { + pub fn theme(&self, program: &P, window: window::Id) -> Option { program.theme(self.state(), window) } @@ -402,7 +405,7 @@ where program.style(self.state(), theme) } - pub fn scale_factor(&self, program: &P, window: window::Id) -> f64 { + pub fn scale_factor(&self, program: &P, window: window::Id) -> f32 { program.scale_factor(self.state(), window) } diff --git a/examples/arc/src/main.rs b/examples/arc/src/main.rs index ce461ab6..6f801d97 100644 --- a/examples/arc/src/main.rs +++ b/examples/arc/src/main.rs @@ -10,7 +10,7 @@ use iced::{Element, Fill, Point, Rectangle, Renderer, Subscription, Theme}; pub fn main() -> iced::Result { iced::application(Arc::new, Arc::update, Arc::view) .subscription(Arc::subscription) - .theme(|_| Theme::Dark) + .theme(Theme::Dark) .run() } diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs index 8b040755..9a525210 100644 --- a/examples/bezier_tool/src/main.rs +++ b/examples/bezier_tool/src/main.rs @@ -4,7 +4,7 @@ use iced::{Element, Theme}; pub fn main() -> iced::Result { iced::application(Example::default, Example::update, Example::view) - .theme(|_| Theme::CatppuccinMocha) + .theme(Theme::CatppuccinMocha) .run() } diff --git a/examples/custom_shader/src/scene.rs b/examples/custom_shader/src/scene.rs index 5fa42188..88f72970 100644 --- a/examples/custom_shader/src/scene.rs +++ b/examples/custom_shader/src/scene.rs @@ -128,26 +128,25 @@ impl Primitive { } impl shader::Primitive for Primitive { - fn prepare( + type Renderer = Pipeline; + + fn initialize( &self, device: &wgpu::Device, queue: &wgpu::Queue, format: wgpu::TextureFormat, - storage: &mut shader::Storage, + ) -> Pipeline { + Pipeline::new(device, queue, format) + } + + fn prepare( + &self, + pipeline: &mut Pipeline, + device: &wgpu::Device, + queue: &wgpu::Queue, _bounds: &Rectangle, viewport: &Viewport, ) { - if !storage.has::() { - storage.store(Pipeline::new( - device, - queue, - format, - viewport.physical_size(), - )); - } - - let pipeline = storage.get_mut::().unwrap(); - // Upload data to GPU pipeline.update( device, @@ -161,14 +160,11 @@ impl shader::Primitive for Primitive { fn render( &self, + pipeline: &Pipeline, encoder: &mut wgpu::CommandEncoder, - storage: &shader::Storage, target: &wgpu::TextureView, clip_bounds: &Rectangle, ) { - // At this point our pipeline should always be initialized - let pipeline = storage.get::().unwrap(); - // Render primitive pipeline.render( target, diff --git a/examples/custom_shader/src/scene/pipeline.rs b/examples/custom_shader/src/scene/pipeline.rs index 7140bcaa..e15587e3 100644 --- a/examples/custom_shader/src/scene/pipeline.rs +++ b/examples/custom_shader/src/scene/pipeline.rs @@ -32,7 +32,6 @@ impl Pipeline { device: &wgpu::Device, queue: &wgpu::Queue, format: wgpu::TextureFormat, - target_size: Size, ) -> Self { //vertices of one cube let vertices = @@ -62,8 +61,8 @@ impl Pipeline { let depth_texture = device.create_texture(&wgpu::TextureDescriptor { label: Some("cubes depth texture"), size: wgpu::Extent3d { - width: target_size.width, - height: target_size.height, + width: 1, + height: 1, depth_or_array_layers: 1, }, mip_level_count: 1, @@ -297,7 +296,7 @@ impl Pipeline { uniforms, uniform_bind_group, vertices, - depth_texture_size: target_size, + depth_texture_size: Size::new(1, 1), depth_view, depth_pipeline, } diff --git a/examples/editor/src/main.rs b/examples/editor/src/main.rs index 622d700b..a687eee4 100644 --- a/examples/editor/src/main.rs +++ b/examples/editor/src/main.rs @@ -326,5 +326,8 @@ fn open_icon<'a, Message>() -> Element<'a, Message> { fn icon<'a, Message>(codepoint: char) -> Element<'a, Message> { const ICON_FONT: Font = Font::with_name("editor-icons"); - text(codepoint).font(ICON_FONT).into() + text(codepoint) + .font(ICON_FONT) + .shaping(text::Shaping::Basic) + .into() } diff --git a/examples/events/src/main.rs b/examples/events/src/main.rs index 57b36f7c..cde7eac2 100644 --- a/examples/events/src/main.rs +++ b/examples/events/src/main.rs @@ -37,7 +37,7 @@ impl Events { } Message::EventOccurred(event) => { if let Event::Window(window::Event::CloseRequested) = event { - window::get_latest().and_then(window::close) + window::latest().and_then(window::close) } else { Task::none() } @@ -47,7 +47,7 @@ impl Events { Task::none() } - Message::Exit => window::get_latest().and_then(window::close), + Message::Exit => window::latest().and_then(window::close), } } diff --git a/examples/exit/src/main.rs b/examples/exit/src/main.rs index 767ff93f..567a0ce6 100644 --- a/examples/exit/src/main.rs +++ b/examples/exit/src/main.rs @@ -20,7 +20,7 @@ enum Message { impl Exit { fn update(&mut self, message: Message) -> Task { match message { - Message::Confirm => window::get_latest().and_then(window::close), + Message::Confirm => window::latest().and_then(window::close), Message::Exit => { self.show_confirm = true; diff --git a/examples/ferris/src/main.rs b/examples/ferris/src/main.rs index add266b5..96b61813 100644 --- a/examples/ferris/src/main.rs +++ b/examples/ferris/src/main.rs @@ -11,7 +11,7 @@ use iced::{ pub fn main() -> iced::Result { iced::application(Image::default, Image::update, Image::view) .subscription(Image::subscription) - .theme(|_| Theme::TokyoNight) + .theme(Theme::TokyoNight) .run() } diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index e6cb4dab..d27ee62e 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -16,7 +16,7 @@ pub fn main() -> iced::Result { iced::application(GameOfLife::default, GameOfLife::update, GameOfLife::view) .subscription(GameOfLife::subscription) - .theme(|_| Theme::Dark) + .theme(Theme::Dark) .centered() .run() } diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs index b3c08879..b98b8273 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration/src/main.rs @@ -67,7 +67,7 @@ pub fn main() -> Result<(), winit::error::EventLoopError> { let physical_size = window.inner_size(); let viewport = Viewport::with_physical_size( Size::new(physical_size.width, physical_size.height), - window.scale_factor(), + window.scale_factor() as f32, ); let clipboard = Clipboard::connect(window.clone()); @@ -212,7 +212,7 @@ pub fn main() -> Result<(), winit::error::EventLoopError> { *viewport = Viewport::with_physical_size( Size::new(size.width, size.height), - window.scale_factor(), + window.scale_factor() as f32, ); surface.configure( @@ -345,7 +345,7 @@ pub fn main() -> Result<(), winit::error::EventLoopError> { // Map window event to iced event if let Some(event) = conversion::window_event( event, - window.scale_factor(), + window.scale_factor() as f32, *modifiers, ) { events.push(event); diff --git a/examples/layout/src/main.rs b/examples/layout/src/main.rs index c15a0bfc..b930bdb0 100644 --- a/examples/layout/src/main.rs +++ b/examples/layout/src/main.rs @@ -19,11 +19,11 @@ pub fn main() -> iced::Result { .run() } -#[derive(Default, Debug)] +#[derive(Debug, Default)] struct Layout { example: Example, explain: bool, - theme: Theme, + theme: Option, } #[derive(Debug, Clone)] @@ -51,7 +51,7 @@ impl Layout { self.explain = explain; } Message::ThemeSelected(theme) => { - self.theme = theme; + self.theme = Some(theme); } } } @@ -74,7 +74,8 @@ impl Layout { horizontal_space(), checkbox("Explain", self.explain) .on_toggle(Message::ExplainToggled), - pick_list(Theme::ALL, Some(&self.theme), Message::ThemeSelected), + pick_list(Theme::ALL, self.theme.as_ref(), Message::ThemeSelected) + .placeholder("Theme"), ] .spacing(20) .align_y(Center); @@ -94,14 +95,14 @@ impl Layout { let controls = row([ (!self.example.is_first()).then_some( - button(text("← Previous").shaping(text::Shaping::Advanced)) + button(text("← Previous")) .padding([5, 10]) .on_press(Message::Previous) .into(), ), Some(horizontal_space().into()), (!self.example.is_last()).then_some( - button(text("Next →").shaping(text::Shaping::Advanced)) + button(text("Next →")) .padding([5, 10]) .on_press(Message::Next) .into(), @@ -116,7 +117,7 @@ impl Layout { .into() } - fn theme(&self) -> Theme { + fn theme(&self) -> Option { self.theme.clone() } } @@ -313,7 +314,7 @@ fn quotes<'a>() -> Element<'a, Message> { "This is another reply", ), horizontal_rule(1), - text("A separator ↑").shaping(text::Shaping::Advanced), + text("A separator ↑"), ] .width(Shrink) .spacing(10) diff --git a/examples/multi_window/src/main.rs b/examples/multi_window/src/main.rs index 3183ecee..a6731023 100644 --- a/examples/multi_window/src/main.rs +++ b/examples/multi_window/src/main.rs @@ -26,7 +26,7 @@ struct Example { struct Window { title: String, scale_input: String, - current_scale: f64, + current_scale: f32, theme: Theme, } @@ -66,7 +66,7 @@ impl Example { return Task::none(); }; - window::get_position(*last_window) + window::position(*last_window) .then(|last_position| { let position = last_position.map_or( window::Position::Default, @@ -113,7 +113,7 @@ impl Example { Message::ScaleChanged(id, scale) => { if let Some(window) = self.windows.get_mut(&id) { window.current_scale = scale - .parse::() + .parse() .unwrap_or(window.current_scale) .clamp(0.5, 5.0); } @@ -138,15 +138,11 @@ impl Example { } } - fn theme(&self, window: window::Id) -> Theme { - if let Some(window) = self.windows.get(&window) { - window.theme.clone() - } else { - Theme::default() - } + fn theme(&self, window: window::Id) -> Option { + Some(self.windows.get(&window)?.theme.clone()) } - fn scale_factor(&self, window: window::Id) -> f64 { + fn scale_factor(&self, window: window::Id) -> f32 { self.windows .get(&window) .map(|window| window.current_scale) diff --git a/examples/qr_code/src/main.rs b/examples/qr_code/src/main.rs index 6b6989d0..fecc4abf 100644 --- a/examples/qr_code/src/main.rs +++ b/examples/qr_code/src/main.rs @@ -20,7 +20,7 @@ struct QRGenerator { data: String, qr_code: Option, total_size: Option, - theme: Theme, + theme: Option, } #[derive(Debug, Clone)] @@ -58,7 +58,7 @@ impl QRGenerator { self.total_size = Some(total_size); } Message::ThemeChanged(theme) => { - self.theme = theme; + self.theme = Some(theme); } } } @@ -78,7 +78,8 @@ impl QRGenerator { let choose_theme = row![ text("Theme:"), - pick_list(Theme::ALL, Some(&self.theme), Message::ThemeChanged,) + pick_list(Theme::ALL, self.theme.as_ref(), Message::ThemeChanged) + .placeholder("Theme") ] .spacing(10) .align_y(Center); @@ -107,7 +108,7 @@ impl QRGenerator { center(content).padding(20).into() } - fn theme(&self) -> Theme { + fn theme(&self) -> Option { self.theme.clone() } } diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index 61285b72..100eca94 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -49,7 +49,7 @@ impl Example { fn update(&mut self, message: Message) -> Task { match message { Message::Screenshot => { - return window::get_latest() + return window::latest() .and_then(window::screenshot) .map(Message::Screenshotted); } diff --git a/examples/styling/snapshots/catppuccin_frappé-tiny-skia.sha256 b/examples/styling/snapshots/catppuccin_frappé-tiny-skia.sha256 index cdc79b78..c7a8077d 100644 --- a/examples/styling/snapshots/catppuccin_frappé-tiny-skia.sha256 +++ b/examples/styling/snapshots/catppuccin_frappé-tiny-skia.sha256 @@ -1 +1 @@ -0650eb2c27c21c5d48e1e00031a52d8471d8a3b4e827ad502c4628914f5c1c13 \ No newline at end of file +129523830df064908cfa911214ba61dadc31d8975425ba3efaae69da9514a3d0 \ No newline at end of file diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index f28216f9..e5805532 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -15,7 +15,7 @@ pub fn main() -> iced::Result { #[derive(Default)] struct Styling { - theme: Theme, + theme: Option, input_value: String, slider_value: f32, checkbox_value: bool, @@ -32,13 +32,14 @@ enum Message { TogglerToggled(bool), PreviousTheme, NextTheme, + ClearTheme, } impl Styling { fn update(&mut self, message: Message) { match message { Message::ThemeChanged(theme) => { - self.theme = theme; + self.theme = Some(theme); } Message::InputChanged(value) => self.input_value = value, Message::ButtonPressed => {} @@ -46,21 +47,29 @@ impl Styling { Message::CheckboxToggled(value) => self.checkbox_value = value, Message::TogglerToggled(value) => self.toggler_value = value, Message::PreviousTheme | Message::NextTheme => { - if let Some(current) = Theme::ALL - .iter() - .position(|candidate| &self.theme == candidate) - { - self.theme = if matches!(message, Message::NextTheme) { - Theme::ALL[(current + 1) % Theme::ALL.len()].clone() - } else if current == 0 { + let current = Theme::ALL.iter().position(|candidate| { + self.theme.as_ref() == Some(candidate) + }); + + self.theme = Some(if matches!(message, Message::NextTheme) { + Theme::ALL[current.map(|current| current + 1).unwrap_or(0) + % Theme::ALL.len()] + .clone() + } else { + let current = current.unwrap_or(0); + + if current == 0 { Theme::ALL .last() .expect("Theme::ALL must not be empty") .clone() } else { Theme::ALL[current - 1].clone() - }; - } + } + }); + } + Message::ClearTheme => { + self.theme = None; } } } @@ -68,8 +77,9 @@ impl Styling { fn view(&self) -> Element<'_, Message> { let choose_theme = column![ text("Theme:"), - pick_list(Theme::ALL, Some(&self.theme), Message::ThemeChanged) - .width(Fill), + pick_list(Theme::ALL, self.theme.as_ref(), Message::ThemeChanged) + .width(Fill) + .placeholder("System"), ] .spacing(10); @@ -186,11 +196,14 @@ impl Styling { keyboard::key::Named::ArrowDown | keyboard::key::Named::ArrowRight, ) => Some(Message::NextTheme), + keyboard::Key::Named(keyboard::key::Named::Space) => { + Some(Message::ClearTheme) + } _ => None, }) } - fn theme(&self) -> Theme { + fn theme(&self) -> Option { self.theme.clone() } } @@ -210,9 +223,7 @@ mod tests { .cloned() .map(|theme| { let mut styling = Styling::default(); - styling.update(Message::ThemeChanged(theme)); - - let theme = styling.theme(); + styling.update(Message::ThemeChanged(theme.clone())); let mut ui = simulator(styling.view()); let snapshot = ui.snapshot(&theme)?; diff --git a/examples/system_information/Cargo.toml b/examples/system_information/Cargo.toml index 55e00d59..6c99e006 100644 --- a/examples/system_information/Cargo.toml +++ b/examples/system_information/Cargo.toml @@ -7,6 +7,6 @@ publish = false [dependencies] iced.workspace = true -iced.features = ["system"] +iced.features = ["sysinfo"] bytesize = "1.1" diff --git a/examples/system_information/src/main.rs b/examples/system_information/src/main.rs index 61bca4c8..4de4ab23 100644 --- a/examples/system_information/src/main.rs +++ b/examples/system_information/src/main.rs @@ -1,5 +1,6 @@ +use iced::system; use iced::widget::{button, center, column, text}; -use iced::{Element, Task, system}; +use iced::{Element, Task}; pub fn main() -> iced::Result { iced::application(Example::new, Example::update, Example::view).run() @@ -26,7 +27,7 @@ impl Example { fn new() -> (Self, Task) { ( Self::Loading, - system::fetch_information().map(Message::InformationReceived), + system::information().map(Message::InformationReceived), ) } diff --git a/examples/table/src/main.rs b/examples/table/src/main.rs index 26a8e621..7a1d6be2 100644 --- a/examples/table/src/main.rs +++ b/examples/table/src/main.rs @@ -8,7 +8,7 @@ use iced::{Center, Element, Fill, Font, Right, Theme}; pub fn main() -> iced::Result { iced::application(Table::new, Table::update, Table::view) - .theme(|_| Theme::CatppuccinMocha) + .theme(Theme::CatppuccinMocha) .run() } diff --git a/examples/todos/snapshots/creates_a_new_task-tiny-skia.sha256 b/examples/todos/snapshots/creates_a_new_task-tiny-skia.sha256 index e6746cf8..f56ef744 100644 --- a/examples/todos/snapshots/creates_a_new_task-tiny-skia.sha256 +++ b/examples/todos/snapshots/creates_a_new_task-tiny-skia.sha256 @@ -1 +1 @@ -99f418007af163f172e163565f166da31015521e1bf7de95fa55cda2fb5a7db5 \ No newline at end of file +0acb67235c6a11014a2d2b825e0a70069bca0c67bee0cdb38a0144fc72b25220 \ No newline at end of file diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 2113c93d..98f6e90f 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -6,7 +6,7 @@ use iced::widget::{ use iced::window; use iced::{ Application, Center, Element, Fill, Font, Function, Preset, Program, - Subscription, Task as Command, + Subscription, Task as Command, Theme, }; use serde::{Deserialize, Serialize}; @@ -156,7 +156,7 @@ impl Todos { operation::focus_next() } } - Message::ToggleFullscreen(mode) => window::get_latest() + Message::ToggleFullscreen(mode) => window::latest() .and_then(move |window| window::set_mode(window, mode)), Message::Loaded(_) => Command::none(), }; @@ -199,7 +199,7 @@ impl Todos { let title = text("todos") .width(Fill) .size(100) - .color([0.5, 0.5, 0.5]) + .style(subtle) .align_x(Center); let input = text_input("What needs to be done?", input_value) @@ -452,7 +452,7 @@ fn empty_message(message: &str) -> Element<'_, Message> { .width(Fill) .size(25) .align_x(Center) - .color([0.7, 0.7, 0.7]), + .style(subtle), ) .height(200) .into() @@ -465,6 +465,7 @@ fn icon(unicode: char) -> Text<'static> { .font(Font::with_name("Iced-Todos-Icons")) .width(20) .align_x(Center) + .shaping(text::Shaping::Basic) } fn edit_icon() -> Text<'static> { @@ -475,6 +476,12 @@ fn delete_icon() -> Text<'static> { icon('\u{F1F8}') } +fn subtle(theme: &Theme) -> text::Style { + text::Style { + color: Some(theme.extended_palette().background.strongest.color), + } +} + // Persistence #[derive(Debug, Clone, Serialize, Deserialize)] struct SavedState { @@ -625,6 +632,7 @@ mod tests { } #[test] + #[ignore] fn it_creates_a_new_task() -> Result<(), Error> { let (mut todos, _command) = Todos::new(); let _command = todos.update(Message::Loaded(Err(LoadError::File))); diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 5d009da6..78c329b5 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -1,11 +1,10 @@ -use iced::border; use iced::widget::{Button, Column, Container, Slider}; use iced::widget::{ button, center_x, center_y, checkbox, column, horizontal_space, image, radio, rich_text, row, scrollable, slider, span, text, text_input, toggler, vertical_space, }; -use iced::{Center, Color, Element, Fill, Font, Pixels, Theme}; +use iced::{Center, Color, Element, Fill, Font, Pixels, color}; pub fn main() -> iced::Result { #[cfg(target_arch = "wasm32")] @@ -201,7 +200,7 @@ impl Tour { Self::container("Welcome!") .push( "This is a simple tour meant to showcase a bunch of \ - widgets that can be easily implemented on top of Iced.", + widgets that come bundled in Iced.", ) .push( "Iced is a cross-platform GUI library for Rust focused on \ @@ -216,28 +215,19 @@ impl Tour { built on top of wgpu, a graphics library supporting Vulkan, \ Metal, DX11, and DX12.", ) - .push({ - let theme = Theme::default(); - let palette = theme.extended_palette(); - + .push( rich_text![ "Additionally, this tour can also run on WebAssembly ", "by leveraging ", span("trunk") - .color(palette.primary.base.color) - .background(palette.background.weakest.color) - .border( - border::rounded(2) - .width(1) - .color(palette.background.weak.color) - ) - .padding([0, 2]) + .color(color!(0x7777FF)) + .underline(true) .font(Font::MONOSPACE) .link(Message::OpenTrunk), "." ] - .on_link_click(std::convert::identity) - }) + .on_link_click(std::convert::identity), + ) .push( "You will need to interact with the UI in order to reach \ the end!", diff --git a/examples/vectorial_text/src/main.rs b/examples/vectorial_text/src/main.rs index d0ba91b9..78349696 100644 --- a/examples/vectorial_text/src/main.rs +++ b/examples/vectorial_text/src/main.rs @@ -11,7 +11,7 @@ pub fn main() -> iced::Result { VectorialText::update, VectorialText::view, ) - .theme(|_| Theme::Dark) + .theme(Theme::Dark) .run() } diff --git a/examples/visible_bounds/src/main.rs b/examples/visible_bounds/src/main.rs index 3930b462..893337e4 100644 --- a/examples/visible_bounds/src/main.rs +++ b/examples/visible_bounds/src/main.rs @@ -13,7 +13,7 @@ use iced::{ pub fn main() -> iced::Result { iced::application(Example::default, Example::update, Example::view) .subscription(Example::subscription) - .theme(|_| Theme::Dark) + .theme(Theme::Dark) .run() } diff --git a/futures/src/event.rs b/futures/src/event.rs index bd75d82c..58bc9df2 100644 --- a/futures/src/event.rs +++ b/futures/src/event.rs @@ -37,6 +37,7 @@ where event: Event::Window(window::Event::RedrawRequested(_)), .. } + | subscription::Event::SystemThemeChanged(_) | subscription::Event::PlatformSpecific(_) => None, subscription::Event::Interaction { window, @@ -66,7 +67,8 @@ where event, status, } => f(event, status, window), - subscription::Event::PlatformSpecific(_) => None, + subscription::Event::SystemThemeChanged(_) + | subscription::Event::PlatformSpecific(_) => None, }) } diff --git a/futures/src/subscription.rs b/futures/src/subscription.rs index e347e81f..338b2622 100644 --- a/futures/src/subscription.rs +++ b/futures/src/subscription.rs @@ -4,6 +4,7 @@ mod tracker; pub use tracker::Tracker; use crate::core::event; +use crate::core::theme; use crate::core::window; use crate::futures::Stream; use crate::{BoxStream, MaybeSend}; @@ -27,6 +28,9 @@ pub enum Event { status: event::Status, }, + /// The system theme has changed. + SystemThemeChanged(theme::Mode), + /// A platform specific event. PlatformSpecific(PlatformSpecific), } @@ -422,7 +426,8 @@ where } } -pub(crate) fn filter_map(id: I, f: F) -> Subscription +/// Creatges a [`Subscription`] from a hashable id and a filter function. +pub fn filter_map(id: I, f: F) -> Subscription where I: Hash + 'static, F: Fn(Event) -> Option + MaybeSend + 'static, diff --git a/graphics/src/compositor.rs b/graphics/src/compositor.rs index 338d648f..e0c8eb46 100644 --- a/graphics/src/compositor.rs +++ b/graphics/src/compositor.rs @@ -59,7 +59,7 @@ pub trait Compositor: Sized { ); /// Returns [`Information`] used by this [`Compositor`]. - fn fetch_information(&self) -> Information; + fn information(&self) -> Information; /// Loads a font from its bytes. fn load_font(&mut self, font: Cow<'static, [u8]>) { @@ -178,7 +178,7 @@ impl Compositor for () { fn load_font(&mut self, _font: Cow<'static, [u8]>) {} - fn fetch_information(&self) -> Information { + fn information(&self) -> Information { Information { adapter: String::from("Null Renderer"), backend: String::from("Null"), diff --git a/graphics/src/geometry/text.rs b/graphics/src/geometry/text.rs index a1c3d7fa..867ae4fc 100644 --- a/graphics/src/geometry/text.rs +++ b/graphics/src/geometry/text.rs @@ -176,7 +176,7 @@ impl Default for Text { font: Font::default(), align_x: Alignment::Default, align_y: alignment::Vertical::Top, - shaping: Shaping::Basic, + shaping: Shaping::default(), } } } diff --git a/graphics/src/text.rs b/graphics/src/text.rs index e00b3ac7..9f932661 100644 --- a/graphics/src/text.rs +++ b/graphics/src/text.rs @@ -323,8 +323,15 @@ fn to_align(alignment: Alignment) -> Option { } /// Converts some [`Shaping`] strategy to a [`cosmic_text::Shaping`] strategy. -pub fn to_shaping(shaping: Shaping) -> cosmic_text::Shaping { +pub fn to_shaping(shaping: Shaping, text: &str) -> cosmic_text::Shaping { match shaping { + Shaping::Auto => { + if text.is_ascii() { + cosmic_text::Shaping::Basic + } else { + cosmic_text::Shaping::Advanced + } + } Shaping::Basic => cosmic_text::Shaping::Basic, Shaping::Advanced => cosmic_text::Shaping::Advanced, } diff --git a/graphics/src/text/cache.rs b/graphics/src/text/cache.rs index 9bb66362..a7fc5ddd 100644 --- a/graphics/src/text/cache.rs +++ b/graphics/src/text/cache.rs @@ -55,7 +55,7 @@ impl Cache { font_system, key.content, &text::to_attributes(key.font), - text::to_shaping(key.shaping), + text::to_shaping(key.shaping, key.content), ); let bounds = text::align(&mut buffer, font_system, key.align_x); diff --git a/graphics/src/text/paragraph.rs b/graphics/src/text/paragraph.rs index f2c6f404..ea2b30d2 100644 --- a/graphics/src/text/paragraph.rs +++ b/graphics/src/text/paragraph.rs @@ -88,7 +88,7 @@ impl core::text::Paragraph for Paragraph { font_system.raw(), text.content, &text::to_attributes(text.font), - text::to_shaping(text.shaping), + text::to_shaping(text.shaping, text.content), ); let min_bounds = @@ -158,7 +158,7 @@ impl core::text::Paragraph for Paragraph { (span.text.as_ref(), attrs.metadata(i)) }), &text::to_attributes(text.font), - text::to_shaping(text.shaping), + cosmic_text::Shaping::Advanced, None, ); diff --git a/graphics/src/viewport.rs b/graphics/src/viewport.rs index dc8e21d3..ea4cd8b2 100644 --- a/graphics/src/viewport.rs +++ b/graphics/src/viewport.rs @@ -5,19 +5,19 @@ use crate::core::{Size, Transformation}; pub struct Viewport { physical_size: Size, logical_size: Size, - scale_factor: f64, + scale_factor: f32, projection: Transformation, } impl Viewport { /// Creates a new [`Viewport`] with the given physical dimensions and scale /// factor. - pub fn with_physical_size(size: Size, scale_factor: f64) -> Viewport { + pub fn with_physical_size(size: Size, scale_factor: f32) -> Viewport { Viewport { physical_size: size, logical_size: Size::new( - (size.width as f64 / scale_factor) as f32, - (size.height as f64 / scale_factor) as f32, + size.width as f32 / scale_factor, + size.height as f32 / scale_factor, ), scale_factor, projection: Transformation::orthographic(size.width, size.height), @@ -45,7 +45,7 @@ impl Viewport { } /// Returns the scale factor of the [`Viewport`]. - pub fn scale_factor(&self) -> f64 { + pub fn scale_factor(&self) -> f32 { self.scale_factor } diff --git a/program/src/lib.rs b/program/src/lib.rs index 3c49659f..aa51d959 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -32,7 +32,7 @@ pub trait Program: Sized { type Message: Send + 'static; /// The theme of the program. - type Theme: Default + theme::Base; + type Theme: theme::Base; /// The renderer of the program. type Renderer: Renderer; @@ -97,15 +97,19 @@ pub trait Program: Sized { Subscription::none() } - fn theme(&self, _state: &Self::State, _window: window::Id) -> Self::Theme { - ::default() + fn theme( + &self, + _state: &Self::State, + _window: window::Id, + ) -> Option { + None } fn style(&self, _state: &Self::State, theme: &Self::Theme) -> theme::Style { theme::Base::base(theme) } - fn scale_factor(&self, _state: &Self::State, _window: window::Id) -> f64 { + fn scale_factor(&self, _state: &Self::State, _window: window::Id) -> f32 { 1.0 } @@ -175,7 +179,7 @@ pub fn with_title( &self, state: &Self::State, window: window::Id, - ) -> Self::Theme { + ) -> Option { self.program.theme(state, window) } @@ -194,7 +198,7 @@ pub fn with_title( self.program.style(state, theme) } - fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { + fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 { self.program.scale_factor(state, window) } } @@ -269,7 +273,7 @@ pub fn with_subscription( &self, state: &Self::State, window: window::Id, - ) -> Self::Theme { + ) -> Option { self.program.theme(state, window) } @@ -281,7 +285,7 @@ pub fn with_subscription( self.program.style(state, theme) } - fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { + fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 { self.program.scale_factor(state, window) } } @@ -295,7 +299,7 @@ pub fn with_subscription( /// Decorates a [`Program`] with the given theme function. pub fn with_theme( program: P, - f: impl Fn(&P::State, window::Id) -> P::Theme, + f: impl Fn(&P::State, window::Id) -> Option, ) -> impl Program { struct WithTheme { program: P, @@ -304,7 +308,7 @@ pub fn with_theme( impl Program for WithTheme where - F: Fn(&P::State, window::Id) -> P::Theme, + F: Fn(&P::State, window::Id) -> Option, { type State = P::State; type Message = P::Message; @@ -316,7 +320,7 @@ pub fn with_theme( &self, state: &Self::State, window: window::Id, - ) -> Self::Theme { + ) -> Option { (self.theme)(state, window) } @@ -371,7 +375,7 @@ pub fn with_theme( self.program.style(state, theme) } - fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { + fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 { self.program.scale_factor(state, window) } } @@ -454,11 +458,11 @@ pub fn with_style( &self, state: &Self::State, window: window::Id, - ) -> Self::Theme { + ) -> Option { self.program.theme(state, window) } - fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { + fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 { self.program.scale_factor(state, window) } } @@ -469,7 +473,7 @@ pub fn with_style( /// Decorates a [`Program`] with the given scale factor function. pub fn with_scale_factor( program: P, - f: impl Fn(&P::State, window::Id) -> f64, + f: impl Fn(&P::State, window::Id) -> f32, ) -> impl Program { struct WithScaleFactor { program: P, @@ -478,7 +482,7 @@ pub fn with_scale_factor( impl Program for WithScaleFactor where - F: Fn(&P::State, window::Id) -> f64, + F: Fn(&P::State, window::Id) -> f32, { type State = P::State; type Message = P::Message; @@ -533,7 +537,7 @@ pub fn with_scale_factor( &self, state: &Self::State, window: window::Id, - ) -> Self::Theme { + ) -> Option { self.program.theme(state, window) } @@ -545,7 +549,7 @@ pub fn with_scale_factor( self.program.style(state, theme) } - fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { + fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 { (self.scale_factor)(state, window) } } @@ -624,7 +628,7 @@ pub fn with_executor( &self, state: &Self::State, window: window::Id, - ) -> Self::Theme { + ) -> Option { self.program.theme(state, window) } @@ -636,7 +640,7 @@ pub fn with_executor( self.program.style(state, theme) } - fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { + fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 { self.program.scale_factor(state, window) } } @@ -697,7 +701,7 @@ impl Instance

{ } /// Returns the current theme of the [`Instance`]. - pub fn theme(&self, window: window::Id) -> P::Theme { + pub fn theme(&self, window: window::Id) -> Option { self.program.theme(&self.state, window) } @@ -707,7 +711,7 @@ impl Instance

{ } /// Returns the current scale factor of the [`Instance`]. - pub fn scale_factor(&self, window: window::Id) -> f64 { + pub fn scale_factor(&self, window: window::Id) -> f32 { self.program.scale_factor(&self.state, window) } } diff --git a/renderer/src/fallback.rs b/renderer/src/fallback.rs index b09b29ce..5e775e38 100644 --- a/renderer/src/fallback.rs +++ b/renderer/src/fallback.rs @@ -312,8 +312,8 @@ where delegate!(self, compositor, compositor.load_font(font)); } - fn fetch_information(&self) -> compositor::Information { - delegate!(self, compositor, compositor.fetch_information()) + fn information(&self) -> compositor::Information { + delegate!(self, compositor, compositor.information()) } fn present( diff --git a/runtime/src/system.rs b/runtime/src/system.rs index 8b0ec2d8..09f112f5 100644 --- a/runtime/src/system.rs +++ b/runtime/src/system.rs @@ -1,11 +1,20 @@ //! Access the native system. +use crate::core::theme; use crate::futures::futures::channel::oneshot; +use crate::futures::subscription::{self, Subscription}; +use crate::task::{self, Task}; /// An operation to be performed on the system. #[derive(Debug)] pub enum Action { - /// Query system information and produce `T` with the result. - QueryInformation(oneshot::Sender), + /// Send available system information. + GetInformation(oneshot::Sender), + + /// Send the current system theme mode. + GetTheme(oneshot::Sender), + + /// Notify to the runtime that the system theme has changed. + NotifyTheme(theme::Mode), } /// Contains information about the system (e.g. system name, processor, memory, graphics adapter). @@ -37,3 +46,29 @@ pub struct Information { /// Model information for the active graphics adapter pub graphics_adapter: String, } + +/// Returns available system information. +pub fn information() -> Task { + task::oneshot(|channel| { + crate::Action::System(Action::GetInformation(channel)) + }) +} + +/// Returns the current system theme. +pub fn theme() -> Task { + task::oneshot(|sender| crate::Action::System(Action::GetTheme(sender))) +} + +/// Subscribes to system theme changes. +pub fn theme_changes() -> Subscription { + #[derive(Hash)] + struct ThemeChanges; + + subscription::filter_map(ThemeChanges, |event| { + let subscription::Event::SystemThemeChanged(mode) = event else { + return None; + }; + + Some(mode) + }) +} diff --git a/runtime/src/window.rs b/runtime/src/window.rs index ccd8721b..dde540b8 100644 --- a/runtime/src/window.rs +++ b/runtime/src/window.rs @@ -266,12 +266,12 @@ pub fn close(id: Id) -> Task { } /// Gets the window [`Id`] of the oldest window. -pub fn get_oldest() -> Task> { +pub fn oldest() -> Task> { task::oneshot(|channel| crate::Action::Window(Action::GetOldest(channel))) } /// Gets the window [`Id`] of the latest window. -pub fn get_latest() -> Task> { +pub fn latest() -> Task> { task::oneshot(|channel| crate::Action::Window(Action::GetLatest(channel))) } @@ -315,14 +315,14 @@ pub fn set_resize_increments(id: Id, increments: Option) -> Task { } /// Get the window's size in logical dimensions. -pub fn get_size(id: Id) -> Task { +pub fn size(id: Id) -> Task { task::oneshot(move |channel| { crate::Action::Window(Action::GetSize(id, channel)) }) } /// Gets the maximized state of the window with the given [`Id`]. -pub fn get_maximized(id: Id) -> Task { +pub fn is_maximized(id: Id) -> Task { task::oneshot(move |channel| { crate::Action::Window(Action::GetMaximized(id, channel)) }) @@ -334,7 +334,7 @@ pub fn maximize(id: Id, maximized: bool) -> Task { } /// Gets the minimized state of the window with the given [`Id`]. -pub fn get_minimized(id: Id) -> Task> { +pub fn is_minimized(id: Id) -> Task> { task::oneshot(move |channel| { crate::Action::Window(Action::GetMinimized(id, channel)) }) @@ -346,14 +346,14 @@ pub fn minimize(id: Id, minimized: bool) -> Task { } /// Gets the position in logical coordinates of the window with the given [`Id`]. -pub fn get_position(id: Id) -> Task> { +pub fn position(id: Id) -> Task> { task::oneshot(move |channel| { crate::Action::Window(Action::GetPosition(id, channel)) }) } /// Gets the scale factor of the window with the given [`Id`]. -pub fn get_scale_factor(id: Id) -> Task { +pub fn scale_factor(id: Id) -> Task { task::oneshot(move |channel| { crate::Action::Window(Action::GetScaleFactor(id, channel)) }) @@ -365,7 +365,7 @@ pub fn move_to(id: Id, position: Point) -> Task { } /// Gets the current [`Mode`] of the window. -pub fn get_mode(id: Id) -> Task { +pub fn mode(id: Id) -> Task { task::oneshot(move |channel| { crate::Action::Window(Action::GetMode(id, channel)) }) @@ -426,7 +426,7 @@ pub fn show_system_menu(id: Id) -> Task { /// Gets an identifier unique to the window, provided by the underlying windowing system. This is /// not to be confused with [`Id`]. -pub fn get_raw_id(id: Id) -> Task { +pub fn raw_id(id: Id) -> Task { task::oneshot(|channel| { crate::Action::Window(Action::GetRawId(id, channel)) }) diff --git a/src/application.rs b/src/application.rs index e2de21b5..143dfcc4 100644 --- a/src/application.rs +++ b/src/application.rs @@ -7,7 +7,7 @@ //! //! pub fn main() -> iced::Result { //! iced::application(u64::default, update, view) -//! .theme(|_| Theme::Dark) +//! .theme(Theme::Dark) //! .centered() //! .run() //! } @@ -36,7 +36,8 @@ use crate::shell; use crate::theme; use crate::window; use crate::{ - Element, Executor, Font, Preset, Result, Settings, Size, Subscription, Task, + Element, Executor, Font, Preset, Result, Settings, Size, Subscription, + Task, Theme, }; use iced_debug as debug; @@ -76,14 +77,14 @@ pub use timed::timed; /// } /// ``` pub fn application( - boot: impl Boot, - update: impl Update, - view: impl for<'a> View<'a, State, Message, Theme, Renderer>, + boot: impl BootFn, + update: impl UpdateFn, + view: impl for<'a> ViewFn<'a, State, Message, Theme, Renderer>, ) -> Application> where State: 'static, Message: Send + 'static, - Theme: Default + theme::Base, + Theme: theme::Base, Renderer: program::Renderer, { use std::marker::PhantomData; @@ -102,11 +103,11 @@ where for Instance where Message: Send + 'static, - Theme: Default + theme::Base, + Theme: theme::Base, Renderer: program::Renderer, - Boot: self::Boot, - Update: self::Update, - View: for<'a> self::View<'a, State, Message, Theme, Renderer>, + Boot: self::BootFn, + Update: self::UpdateFn, + View: for<'a> self::ViewFn<'a, State, Message, Theme, Renderer>, { type State = State; type Message = Message; @@ -334,10 +335,10 @@ impl Application

{ } } - /// Sets the [`Title`] of the [`Application`]. + /// Sets the title of the [`Application`]. pub fn title( self, - title: impl Title, + title: impl TitleFn, ) -> Application< impl Program, > { @@ -369,12 +370,14 @@ impl Application

{ /// Sets the theme logic of the [`Application`]. pub fn theme( self, - f: impl Fn(&P::State) -> P::Theme, + f: impl ThemeFn, ) -> Application< impl Program, > { Application { - raw: program::with_theme(self.raw, move |state, _window| f(state)), + raw: program::with_theme(self.raw, move |state, _window| { + f.theme(state) + }), settings: self.settings, window: self.window, presets: self.presets, @@ -399,7 +402,7 @@ impl Application

{ /// Sets the scale factor of the [`Application`]. pub fn scale_factor( self, - f: impl Fn(&P::State) -> f64, + f: impl Fn(&P::State) -> f32, ) -> Application< impl Program, > { @@ -497,7 +500,7 @@ impl Program for Application

{ &self, state: &Self::State, window: iced_core::window::Id, - ) -> Self::Theme { + ) -> Option { debug::hot(|| self.raw.theme(state, window)) } @@ -505,7 +508,7 @@ impl Program for Application

{ debug::hot(|| self.raw.style(state, theme)) } - fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { + fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 { debug::hot(|| self.raw.scale_factor(state, window)) } @@ -522,12 +525,12 @@ impl Program for Application

{ /// In practice, this means that [`application`] can both take /// simple functions like `State::default` and more advanced ones /// that return a [`Task`]. -pub trait Boot { +pub trait BootFn { /// Initializes the [`Application`] state. fn boot(&self) -> (State, Task); } -impl Boot for T +impl BootFn for T where T: Fn() -> C, C: IntoBoot, @@ -561,18 +564,18 @@ impl IntoBoot for (State, Task) { /// any closure `Fn(&State) -> String`. /// /// This trait allows the [`application`] builder to take any of them. -pub trait Title { +pub trait TitleFn { /// Produces the title of the [`Application`]. fn title(&self, state: &State) -> String; } -impl Title for &'static str { +impl TitleFn for &'static str { fn title(&self, _state: &State) -> String { self.to_string() } } -impl Title for T +impl TitleFn for T where T: Fn(&State) -> String, { @@ -585,18 +588,18 @@ where /// /// This trait allows the [`application`] builder to take any closure that /// returns any `Into>`. -pub trait Update { +pub trait UpdateFn { /// Processes the message and updates the state of the [`Application`]. fn update(&self, state: &mut State, message: Message) -> Task; } -impl Update for () { +impl UpdateFn for () { fn update(&self, _state: &mut State, _message: Message) -> Task { Task::none() } } -impl Update for T +impl UpdateFn for T where T: Fn(&mut State, Message) -> C, C: Into>, @@ -610,13 +613,13 @@ where /// /// This trait allows the [`application`] builder to take any closure that /// returns any `Into>`. -pub trait View<'a, State, Message, Theme, Renderer> { +pub trait ViewFn<'a, State, Message, Theme, Renderer> { /// Produces the widget of the [`Application`]. fn view(&self, state: &'a State) -> Element<'a, Message, Theme, Renderer>; } impl<'a, T, State, Message, Theme, Renderer, Widget> - View<'a, State, Message, Theme, Renderer> for T + ViewFn<'a, State, Message, Theme, Renderer> for T where T: Fn(&'a State) -> Widget, State: 'static, @@ -626,3 +629,35 @@ where self(state).into() } } + +/// The theme logic of some [`Application`]. +/// +/// Any implementors of this trait can be provided as an argument to +/// [`Application::theme`]. +/// +/// `iced` provides two implementors: +/// - the built-in [`Theme`] itself +/// - and any `Fn(&State) -> impl Into>`. +pub trait ThemeFn { + /// Returns the theme of the [`Application`] for the current state. + /// + /// If `None` is returned, `iced` will try to use a theme that + /// matches the system color scheme. + fn theme(&self, state: &State) -> Option; +} + +impl ThemeFn for Theme { + fn theme(&self, _state: &State) -> Option { + Some(self.clone()) + } +} + +impl ThemeFn for F +where + F: Fn(&State) -> T, + T: Into>, +{ + fn theme(&self, state: &State) -> Option { + (self)(state).into() + } +} diff --git a/src/application/timed.rs b/src/application/timed.rs index ade98a18..099b3dc4 100644 --- a/src/application/timed.rs +++ b/src/application/timed.rs @@ -1,5 +1,5 @@ //! An [`Application`] that receives an [`Instant`] in update logic. -use crate::application::{Application, Boot, View}; +use crate::application::{Application, BootFn, ViewFn}; use crate::program; use crate::theme; use crate::time::Instant; @@ -20,17 +20,17 @@ use iced_debug as debug; /// /// [`comet`]: https://github.com/iced-rs/comet pub fn timed( - boot: impl Boot, - update: impl Update, + boot: impl BootFn, + update: impl UpdateFn, subscription: impl Fn(&State) -> Subscription, - view: impl for<'a> View<'a, State, Message, Theme, Renderer>, + view: impl for<'a> ViewFn<'a, State, Message, Theme, Renderer>, ) -> Application< impl Program, > where State: 'static, Message: Send + 'static, - Theme: Default + theme::Base + 'static, + Theme: theme::Base + 'static, Renderer: program::Renderer + 'static, { use std::marker::PhantomData; @@ -69,12 +69,12 @@ where > where Message: Send + 'static, - Theme: Default + theme::Base + 'static, + Theme: theme::Base + 'static, Renderer: program::Renderer + 'static, - Boot: self::Boot, - Update: self::Update, + Boot: self::BootFn, + Update: self::UpdateFn, Subscription: Fn(&State) -> self::Subscription, - View: for<'a> self::View<'a, State, Message, Theme, Renderer>, + View: for<'a> self::ViewFn<'a, State, Message, Theme, Renderer>, { type State = State; type Message = (Message, Instant); @@ -157,9 +157,9 @@ where /// The update logic of some timed [`Application`]. /// -/// This is like [`application::Update`](super::Update), +/// This is like [`application::UpdateFn`](super::UpdateFn), /// but it also takes an [`Instant`]. -pub trait Update { +pub trait UpdateFn { /// Processes the message and updates the state of the [`Application`]. fn update( &self, @@ -169,7 +169,7 @@ pub trait Update { ) -> impl Into>; } -impl Update for () { +impl UpdateFn for () { fn update( &self, _state: &mut State, @@ -179,7 +179,7 @@ impl Update for () { } } -impl Update for T +impl UpdateFn for T where T: Fn(&mut State, Message, Instant) -> C, C: Into>, diff --git a/src/daemon.rs b/src/daemon.rs index c58d8033..d22fcabd 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -7,6 +7,7 @@ use crate::theme; use crate::window; use crate::{ Element, Executor, Font, Preset, Result, Settings, Subscription, Task, + Theme, }; use iced_debug as debug; @@ -24,14 +25,14 @@ use std::borrow::Cow; /// /// [`exit`]: crate::exit pub fn daemon( - boot: impl application::Boot, - update: impl application::Update, - view: impl for<'a> View<'a, State, Message, Theme, Renderer>, + boot: impl application::BootFn, + update: impl application::UpdateFn, + view: impl for<'a> ViewFn<'a, State, Message, Theme, Renderer>, ) -> Daemon> where State: 'static, Message: Send + 'static, - Theme: Default + theme::Base, + Theme: theme::Base, Renderer: program::Renderer, { use std::marker::PhantomData; @@ -50,11 +51,11 @@ where for Instance where Message: Send + 'static, - Theme: Default + theme::Base, + Theme: theme::Base, Renderer: program::Renderer, - Boot: application::Boot, - Update: application::Update, - View: for<'a> self::View<'a, State, Message, Theme, Renderer>, + Boot: application::BootFn, + Update: application::UpdateFn, + View: for<'a> self::ViewFn<'a, State, Message, Theme, Renderer>, { type State = State; type Message = Message; @@ -183,10 +184,10 @@ impl Daemon

{ self } - /// Sets the [`Title`] of the [`Daemon`]. + /// Sets the title of the [`Daemon`]. pub fn title( self, - title: impl Title, + title: impl TitleFn, ) -> Daemon< impl Program, > { @@ -216,12 +217,14 @@ impl Daemon

{ /// Sets the theme logic of the [`Daemon`]. pub fn theme( self, - f: impl Fn(&P::State, window::Id) -> P::Theme, + f: impl ThemeFn, ) -> Daemon< impl Program, > { Daemon { - raw: program::with_theme(self.raw, f), + raw: program::with_theme(self.raw, move |state, window| { + f.theme(state, window) + }), settings: self.settings, presets: self.presets, } @@ -244,7 +247,7 @@ impl Daemon

{ /// Sets the scale factor of the [`Daemon`]. pub fn scale_factor( self, - f: impl Fn(&P::State, window::Id) -> f64, + f: impl Fn(&P::State, window::Id) -> f32, ) -> Daemon< impl Program, > { @@ -338,7 +341,7 @@ impl Program for Daemon

{ &self, state: &Self::State, window: iced_core::window::Id, - ) -> Self::Theme { + ) -> Option { debug::hot(|| self.raw.theme(state, window)) } @@ -346,7 +349,7 @@ impl Program for Daemon

{ debug::hot(|| self.raw.style(state, theme)) } - fn scale_factor(&self, state: &Self::State, window: window::Id) -> f64 { + fn scale_factor(&self, state: &Self::State, window: window::Id) -> f32 { debug::hot(|| self.raw.scale_factor(state, window)) } @@ -361,18 +364,18 @@ impl Program for Daemon

{ /// any closure `Fn(&State, window::Id) -> String`. /// /// This trait allows the [`daemon`] builder to take any of them. -pub trait Title { +pub trait TitleFn { /// Produces the title of the [`Daemon`]. fn title(&self, state: &State, window: window::Id) -> String; } -impl Title for &'static str { +impl TitleFn for &'static str { fn title(&self, _state: &State, _window: window::Id) -> String { self.to_string() } } -impl Title for T +impl TitleFn for T where T: Fn(&State, window::Id) -> String, { @@ -385,7 +388,7 @@ where /// /// This trait allows the [`daemon`] builder to take any closure that /// returns any `Into>`. -pub trait View<'a, State, Message, Theme, Renderer> { +pub trait ViewFn<'a, State, Message, Theme, Renderer> { /// Produces the widget of the [`Daemon`]. fn view( &self, @@ -395,7 +398,7 @@ pub trait View<'a, State, Message, Theme, Renderer> { } impl<'a, T, State, Message, Theme, Renderer, Widget> - View<'a, State, Message, Theme, Renderer> for T + ViewFn<'a, State, Message, Theme, Renderer> for T where T: Fn(&'a State, window::Id) -> Widget, State: 'static, @@ -409,3 +412,35 @@ where self(state, window).into() } } + +/// The theme logic of some [`Daemon`]. +/// +/// Any implementors of this trait can be provided as an argument to +/// [`Daemon::theme`]. +/// +/// `iced` provides two implementors: +/// - the built-in [`Theme`] itself +/// - and any `Fn(&State, window::Id) -> impl Into>`. +pub trait ThemeFn { + /// Returns the theme of the [`Daemon`] for the current state and window. + /// + /// If `None` is returned, `iced` will try to use a theme that + /// matches the system color scheme. + fn theme(&self, state: &State, window: window::Id) -> Option; +} + +impl ThemeFn for Theme { + fn theme(&self, _state: &State, _window: window::Id) -> Option { + Some(self.clone()) + } +} + +impl ThemeFn for F +where + F: Fn(&State, window::Id) -> T, + T: Into>, +{ + fn theme(&self, state: &State, window: window::Id) -> Option { + (self)(state, window).into() + } +} diff --git a/src/lib.rs b/src/lib.rs index d2772e62..c72da926 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -589,11 +589,12 @@ pub mod mouse { }; } -#[cfg(feature = "system")] pub mod system { //! Retrieve system information. - pub use crate::runtime::system::Information; - pub use crate::shell::system::*; + pub use crate::runtime::system::{theme, theme_changes}; + + #[cfg(feature = "sysinfo")] + pub use crate::runtime::system::{Information, information}; } pub mod overlay { @@ -691,14 +692,14 @@ pub type Result = std::result::Result<(), Error>; /// } /// ``` pub fn run( - update: impl application::Update + 'static, - view: impl for<'a> application::View<'a, State, Message, Theme, Renderer> + update: impl application::UpdateFn + 'static, + view: impl for<'a> application::ViewFn<'a, State, Message, Theme, Renderer> + 'static, ) -> Result where State: Default + 'static, Message: Send + message::MaybeDebug + message::MaybeClone + 'static, - Theme: Default + theme::Base + 'static, + Theme: theme::Base + 'static, Renderer: program::Renderer + 'static, { application(State::default, update, view).run() diff --git a/src/time.rs b/src/time.rs index f32eeb17..b43ced96 100644 --- a/src/time.rs +++ b/src/time.rs @@ -11,3 +11,17 @@ pub use crate::core::time::*; ))) )] pub use iced_futures::backend::default::time::*; + +use crate::Task; + +/// Returns a [`Task`] that produces the current [`Instant`] +/// by calling [`Instant::now`]. +/// +/// While you can call [`Instant::now`] directly in your application; +/// that renders your application "impure" (i.e. no referential transparency). +/// +/// You may care about purity if you want to leverage the `time-travel` +/// feature properly. +pub fn now() -> Task { + Task::future(async { Instant::now() }) +} diff --git a/test/src/emulator.rs b/test/src/emulator.rs index c77863ee..ade88e23 100644 --- a/test/src/emulator.rs +++ b/test/src/emulator.rs @@ -379,7 +379,7 @@ impl Emulator

{ program.view(&self.state, self.window) } - pub fn theme(&self, program: &P) -> P::Theme { + pub fn theme(&self, program: &P) -> Option { program.theme(&self.state, self.window) } diff --git a/test/src/simulator.rs b/test/src/simulator.rs index 93b933a5..68923dd7 100644 --- a/test/src/simulator.rs +++ b/test/src/simulator.rs @@ -18,6 +18,7 @@ use crate::{Error, Selector}; use std::borrow::Cow; use std::env; use std::fs; +use std::io; use std::path::{Path, PathBuf}; use std::sync::Arc; @@ -235,7 +236,7 @@ where screenshot: window::Screenshot::new( rgba, physical_size, - f64::from(scale_factor), + scale_factor, ), renderer: self.renderer.name(), }) @@ -267,10 +268,13 @@ impl Snapshot { if path.exists() { let file = fs::File::open(&path)?; - let decoder = png::Decoder::new(file); + let decoder = png::Decoder::new(io::BufReader::new(file)); let mut reader = decoder.read_info()?; - let mut bytes = vec![0; reader.output_buffer_size()]; + let n = reader + .output_buffer_size() + .expect("snapshot should fit in memory"); + let mut bytes = vec![0; n]; let info = reader.next_frame(&mut bytes)?; Ok(self.screenshot.bytes == bytes[..info.buffer_size()]) diff --git a/tester/src/lib.rs b/tester/src/lib.rs index 8e7593d6..80d3d960 100644 --- a/tester/src/lib.rs +++ b/tester/src/lib.rs @@ -566,12 +566,16 @@ impl Tester

{ let viewport = container( scrollable( container(match &self.state { - State::Empty => horizontal_space().into(), - State::Idle { state } => Element::from(themer( - program.theme(state, window), - program.view(state, window), - )) - .map(Tick::Program), + State::Empty => Element::from(horizontal_space()), + State::Idle { state } => { + let theme = program.theme(state, window); + + themer( + theme, + program.view(state, window).map(Tick::Program), + ) + .into() + } State::Recording { emulator } => { let theme = emulator.theme(program); let view = emulator.view(program).map(Tick::Program); diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index 16a27fc0..e5739ea7 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -72,7 +72,7 @@ impl Renderer { damage: &[Rectangle], background_color: Color, ) { - let scale_factor = viewport.scale_factor() as f32; + let scale_factor = viewport.scale_factor(); self.layers.flush(); @@ -404,8 +404,7 @@ impl renderer::Headless for Renderer { scale_factor: f32, background_color: Color, ) -> Vec { - let viewport = - Viewport::with_physical_size(size, f64::from(scale_factor)); + let viewport = Viewport::with_physical_size(size, scale_factor); window::compositor::screenshot(self, &viewport, background_color) } diff --git a/tiny_skia/src/window/compositor.rs b/tiny_skia/src/window/compositor.rs index 321a003f..02cace72 100644 --- a/tiny_skia/src/window/compositor.rs +++ b/tiny_skia/src/window/compositor.rs @@ -100,7 +100,7 @@ impl crate::graphics::Compositor for Compositor { surface.layer_stack.clear(); } - fn fetch_information(&self) -> Information { + fn information(&self) -> Information { Information { adapter: String::from("CPU"), backend: String::from("tiny-skia"), diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index cd3a7de7..5ddb8461 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -235,13 +235,13 @@ impl Layer { pub fn draw_primitive( &mut self, bounds: Rectangle, - primitive: Box, + primitive: impl Primitive, transformation: Transformation, ) { let bounds = bounds * transformation; self.primitives - .push(primitive::Instance { bounds, primitive }); + .push(primitive::Instance::new(bounds, primitive)); } fn flush_meshes(&mut self) { diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 8b1e6245..f49fa1db 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -302,7 +302,7 @@ impl Renderer { encoder: &mut wgpu::CommandEncoder, viewport: &Viewport, ) { - let scale_factor = viewport.scale_factor() as f32; + let scale_factor = viewport.scale_factor(); self.text_viewport .update(&self.engine.queue, viewport.physical_size()); @@ -365,10 +365,10 @@ impl Renderer { for instance in &layer.primitives { instance.primitive.prepare( + &mut primitive_storage, &self.engine.device, &self.engine.queue, self.engine.format, - &mut primitive_storage, &instance.bounds, viewport, ); @@ -464,7 +464,7 @@ impl Renderer { #[cfg(any(feature = "svg", feature = "image"))] let image_cache = self.image_cache.borrow(); - let scale_factor = viewport.scale_factor() as f32; + let scale_factor = viewport.scale_factor(); let physical_bounds = Rectangle::::from(Rectangle::with_size( viewport.physical_size(), )); @@ -534,7 +534,6 @@ impl Renderer { if !layer.primitives.is_empty() { let render_span = debug::render(debug::Primitive::Shader); - let _ = ManuallyDrop::into_inner(render_pass); let primitive_storage = self .engine @@ -542,41 +541,91 @@ impl Renderer { .read() .expect("Read primitive storage"); + let mut need_render = Vec::new(); + for instance in &layer.primitives { + let bounds = instance.bounds * scale; + if let Some(clip_bounds) = (instance.bounds * scale) .intersection(&physical_bounds) .and_then(Rectangle::snap) { + render_pass.set_viewport( + bounds.x, + bounds.y, + bounds.width, + bounds.height, + 0.0, + 1.0, + ); + + render_pass.set_scissor_rect( + clip_bounds.x, + clip_bounds.y, + clip_bounds.width, + clip_bounds.height, + ); + + let drawn = instance + .primitive + .draw(&primitive_storage, &mut render_pass); + + if !drawn { + need_render.push((instance, clip_bounds)); + } + } + } + + render_pass.set_viewport( + 0.0, + 0.0, + viewport.physical_width() as f32, + viewport.physical_height() as f32, + 0.0, + 1.0, + ); + + render_pass.set_scissor_rect( + 0, + 0, + viewport.physical_width(), + viewport.physical_height(), + ); + + if !need_render.is_empty() { + let _ = ManuallyDrop::into_inner(render_pass); + + for (instance, clip_bounds) in need_render { instance.primitive.render( - encoder, &primitive_storage, + encoder, frame, &clip_bounds, ); } + + render_pass = ManuallyDrop::new(encoder.begin_render_pass( + &wgpu::RenderPassDescriptor { + label: Some("iced_wgpu render pass"), + color_attachments: &[Some( + wgpu::RenderPassColorAttachment { + view: frame, + depth_slice: None, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }, + }, + )], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }, + )); } render_span.finish(); - - render_pass = ManuallyDrop::new(encoder.begin_render_pass( - &wgpu::RenderPassDescriptor { - label: Some("iced_wgpu render pass"), - color_attachments: &[Some( - wgpu::RenderPassColorAttachment { - view: frame, - depth_slice: None, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }, - }, - )], - depth_stencil_attachment: None, - timestamp_writes: None, - occlusion_query_set: None, - }, - )); } #[cfg(any(feature = "svg", feature = "image"))] @@ -804,7 +853,7 @@ impl graphics::geometry::Renderer for Renderer { impl primitive::Renderer for Renderer { fn draw_primitive(&mut self, bounds: Rectangle, primitive: impl Primitive) { let (layer, transformation) = self.layers.current_mut(); - layer.draw_primitive(bounds, Box::new(primitive), transformation); + layer.draw_primitive(bounds, primitive, transformation); } } @@ -878,7 +927,7 @@ impl renderer::Headless for Renderer { background_color: Color, ) -> Vec { self.screenshot( - &Viewport::with_physical_size(size, f64::from(scale_factor)), + &Viewport::with_physical_size(size, scale_factor), background_color, ) } diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs index c8bcb65d..dd661e7e 100644 --- a/wgpu/src/primitive.rs +++ b/wgpu/src/primitive.rs @@ -1,6 +1,7 @@ //! Draw custom primitives. use crate::core::{self, Rectangle}; use crate::graphics::Viewport; +use crate::graphics::futures::{MaybeSend, MaybeSync}; use rustc_hash::FxHashMap; use std::any::{Any, TypeId}; @@ -10,36 +11,172 @@ use std::fmt::Debug; pub type Batch = Vec; /// A set of methods which allows a [`Primitive`] to be rendered. -pub trait Primitive: Debug + Send + Sync + 'static { - /// Processes the [`Primitive`], allowing for GPU buffer allocation. - fn prepare( +pub trait Primitive: Debug + MaybeSend + MaybeSync + 'static { + /// The shared renderer of this [`Primitive`]. + /// + /// Normally, this will contain a bunch of [`wgpu`] state; like + /// a rendering pipeline, buffers, and textures. + /// + /// All instances of this [`Primitive`] type will share the same + /// [`Renderer`]. + type Renderer: MaybeSend + MaybeSync; + + /// Initializes the [`Renderer`](Self::Renderer) of the [`Primitive`]. + /// + /// This will only be called once, when the first [`Primitive`] of this kind + /// is encountered. + fn initialize( &self, device: &wgpu::Device, queue: &wgpu::Queue, format: wgpu::TextureFormat, - storage: &mut Storage, + ) -> Self::Renderer; + + /// Processes the [`Primitive`], allowing for GPU buffer allocation. + fn prepare( + &self, + renderer: &mut Self::Renderer, + device: &wgpu::Device, + queue: &wgpu::Queue, bounds: &Rectangle, viewport: &Viewport, ); - /// Renders the [`Primitive`]. + /// Draws the [`Primitive`] in the given [`wgpu::RenderPass`]. + /// + /// When possible, this should be implemented over [`render`](Self::render) + /// since reusing the existing render pass should be considerably more + /// efficient than issuing a new one. + /// + /// The viewport and scissor rect of the render pass provided is set + /// to the bounds and clip bounds of the [`Primitive`], respectively. + /// + /// If you have complex composition needs, then you can leverage + /// [`render`](Self::render) by returning `false` here. + /// + /// By default, it does nothing and returns `false`. + fn draw( + &self, + _renderer: &Self::Renderer, + _render_pass: &mut wgpu::RenderPass<'_>, + ) -> bool { + false + } + + /// Renders the [`Primitive`], using the given [`wgpu::CommandEncoder`]. + /// + /// This will only be called if [`draw`](Self::draw) returns `false`. + /// + /// By default, it does nothing. fn render( &self, - encoder: &mut wgpu::CommandEncoder, + _renderer: &Self::Renderer, + _encoder: &mut wgpu::CommandEncoder, + _target: &wgpu::TextureView, + _clip_bounds: &Rectangle, + ) { + } +} + +pub(crate) trait Stored: + Debug + MaybeSend + MaybeSync + 'static +{ + fn prepare( + &self, + storage: &mut Storage, + device: &wgpu::Device, + queue: &wgpu::Queue, + format: wgpu::TextureFormat, + bounds: &Rectangle, + viewport: &Viewport, + ); + + fn draw( + &self, storage: &Storage, + render_pass: &mut wgpu::RenderPass<'_>, + ) -> bool; + + fn render( + &self, + storage: &Storage, + encoder: &mut wgpu::CommandEncoder, target: &wgpu::TextureView, clip_bounds: &Rectangle, ); } +#[derive(Debug)] +struct BlackBox { + primitive: P, +} + +impl Stored for BlackBox

{ + fn prepare( + &self, + storage: &mut Storage, + device: &wgpu::Device, + queue: &wgpu::Queue, + format: wgpu::TextureFormat, + bounds: &Rectangle, + viewport: &Viewport, + ) { + if !storage.has::

() { + storage.store::( + self.primitive.initialize(device, queue, format), + ); + } + + let renderer = storage + .get_mut::

() + .expect("renderer should be initialized") + .downcast_mut::() + .expect("renderer should have the proper type"); + + self.primitive + .prepare(renderer, device, queue, bounds, viewport); + } + + fn draw( + &self, + storage: &Storage, + render_pass: &mut wgpu::RenderPass<'_>, + ) -> bool { + let renderer = storage + .get::

() + .expect("renderer should be initialized") + .downcast_ref::() + .expect("renderer should have the proper type"); + + self.primitive.draw(renderer, render_pass) + } + + fn render( + &self, + storage: &Storage, + encoder: &mut wgpu::CommandEncoder, + target: &wgpu::TextureView, + clip_bounds: &Rectangle, + ) { + let renderer = storage + .get::

() + .expect("renderer should be initialized") + .downcast_ref::() + .expect("renderer should have the proper type"); + + self.primitive + .render(renderer, encoder, target, clip_bounds); + } +} + #[derive(Debug)] /// An instance of a specific [`Primitive`]. pub struct Instance { /// The bounds of the [`Instance`]. - pub bounds: Rectangle, + pub(crate) bounds: Rectangle, /// The [`Primitive`] to render. - pub primitive: Box, + pub(crate) primitive: Box, } impl Instance { @@ -47,7 +184,7 @@ impl Instance { pub fn new(bounds: Rectangle, primitive: impl Primitive) -> Self { Instance { bounds, - primitive: Box::new(primitive), + primitive: Box::new(BlackBox { primitive }), } } } @@ -59,9 +196,10 @@ pub trait Renderer: core::Renderer { } /// Stores custom, user-provided types. -#[derive(Default, Debug)] +#[derive(Default)] +#[allow(missing_debug_implementations)] pub struct Storage { - pipelines: FxHashMap>, + pipelines: FxHashMap>, } impl Storage { @@ -71,25 +209,28 @@ impl Storage { } /// Inserts the data `T` in to [`Storage`]. - pub fn store(&mut self, data: T) { + pub fn store( + &mut self, + data: D, + ) { let _ = self.pipelines.insert(TypeId::of::(), Box::new(data)); } /// Returns a reference to the data with type `T` if it exists in [`Storage`]. - pub fn get(&self) -> Option<&T> { - self.pipelines.get(&TypeId::of::()).map(|pipeline| { - pipeline - .downcast_ref::() - .expect("Value with this type does not exist in Storage.") - }) + pub fn get(&self) -> Option<&dyn Any> { + self.pipelines + .get(&TypeId::of::()) + .map(|pipeline| pipeline.as_ref() as &dyn Any) } /// Returns a mutable reference to the data with type `T` if it exists in [`Storage`]. - pub fn get_mut(&mut self) -> Option<&mut T> { - self.pipelines.get_mut(&TypeId::of::()).map(|pipeline| { - pipeline - .downcast_mut::() - .expect("Value with this type does not exist in Storage.") - }) + pub fn get_mut(&mut self) -> Option<&mut dyn Any> { + self.pipelines + .get_mut(&TypeId::of::()) + .map(|pipeline| pipeline.as_mut() as &mut dyn Any) } } + +trait AnyConcurrent: Any + MaybeSend + MaybeSync {} + +impl AnyConcurrent for T where T: Any + MaybeSend + MaybeSync {} diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs index a8b161a8..c912ef1d 100644 --- a/wgpu/src/window/compositor.rs +++ b/wgpu/src/window/compositor.rs @@ -332,7 +332,7 @@ impl graphics::Compositor for Compositor { ); } - fn fetch_information(&self) -> compositor::Information { + fn information(&self) -> compositor::Information { let information = self.adapter.get_info(); compositor::Information { diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index a47a6f63..fb26945d 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -4,6 +4,7 @@ use crate::checkbox::{self, Checkbox}; use crate::combo_box::{self, ComboBox}; use crate::container::{self, Container}; use crate::core; +use crate::core::theme; use crate::core::widget::operation::{self, Operation}; use crate::core::window; use crate::core::{Element, Length, Pixels, Size, Widget}; @@ -2050,10 +2051,11 @@ where /// A widget that applies any `Theme` to its contents. pub fn themer<'a, Message, Theme, Renderer>( - theme: Theme, + theme: Option, content: impl Into>, ) -> Themer<'a, Message, Theme, Renderer> where + Theme: theme::Base, Renderer: core::Renderer, { Themer::new(theme, content) diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs index c4c5b521..6f8cc00b 100644 --- a/widget/src/overlay/menu.rs +++ b/widget/src/overlay/menu.rs @@ -73,7 +73,7 @@ where padding: Padding::ZERO, text_size: None, text_line_height: text::LineHeight::default(), - text_shaping: text::Shaping::Basic, + text_shaping: text::Shaping::default(), font: None, class, } diff --git a/widget/src/themer.rs b/widget/src/themer.rs index 3b1dcd25..67b6c65b 100644 --- a/widget/src/themer.rs +++ b/widget/src/themer.rs @@ -3,6 +3,7 @@ use crate::core::layout; use crate::core::mouse; use crate::core::overlay; use crate::core::renderer; +use crate::core::theme; use crate::core::widget::Operation; use crate::core::widget::tree::{self, Tree}; use crate::core::{ @@ -20,7 +21,7 @@ where Renderer: crate::core::Renderer, { content: Element<'a, Message, Theme, Renderer>, - theme: Theme, + theme: Option, text_color: Option Color>, background: Option Background>, } @@ -32,7 +33,7 @@ where /// Creates an empty [`Themer`] that applies the given `Theme` /// to the provided `content`. pub fn new( - theme: Theme, + theme: Option, content: impl Into>, ) -> Self { Self { @@ -59,6 +60,8 @@ where impl Widget for Themer<'_, Message, Theme, Renderer> where + Theme: theme::Base, + AnyTheme: theme::Base, Renderer: crate::core::Renderer, { fn tag(&self) -> tree::Tag { @@ -135,17 +138,20 @@ where &self, tree: &Tree, renderer: &mut Renderer, - _theme: &AnyTheme, + theme: &AnyTheme, style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, viewport: &Rectangle, ) { + let default_theme = theme::Base::default(theme.mode()); + let theme = self.theme.as_ref().unwrap_or(&default_theme); + if let Some(background) = self.background { container::draw_background( renderer, &container::Style { - background: Some(background(&self.theme)), + background: Some(background(theme)), ..container::Style::default() }, layout.bounds(), @@ -154,21 +160,15 @@ where let style = if let Some(text_color) = self.text_color { renderer::Style { - text_color: text_color(&self.theme), + text_color: text_color(theme), } } else { *style }; - self.content.as_widget().draw( - tree, - renderer, - &self.theme, - &style, - layout, - cursor, - viewport, - ); + self.content + .as_widget() + .draw(tree, renderer, theme, &style, layout, cursor, viewport); } fn overlay<'b>( @@ -180,7 +180,7 @@ where translation: Vector, ) -> Option> { struct Overlay<'a, Message, Theme, Renderer> { - theme: &'a Theme, + theme: &'a Option, content: overlay::Element<'a, Message, Theme, Renderer>, } @@ -188,6 +188,8 @@ where overlay::Overlay for Overlay<'_, Message, Theme, Renderer> where + Theme: theme::Base, + AnyTheme: theme::Base, Renderer: crate::core::Renderer, { fn layout( @@ -201,14 +203,17 @@ where fn draw( &self, renderer: &mut Renderer, - _theme: &AnyTheme, + theme: &AnyTheme, style: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, ) { + let default_theme = theme::Base::default(theme.mode()); + let theme = self.theme.as_ref().unwrap_or(&default_theme); + self.content .as_overlay() - .draw(renderer, self.theme, style, layout, cursor); + .draw(renderer, theme, style, layout, cursor); } fn update( @@ -280,7 +285,8 @@ impl<'a, Message, Theme, Renderer, AnyTheme> for Element<'a, Message, AnyTheme, Renderer> where Message: 'a, - Theme: 'a, + Theme: theme::Base + 'a, + AnyTheme: theme::Base, Renderer: 'a + crate::core::Renderer, { fn from( diff --git a/widget/src/tooltip.rs b/widget/src/tooltip.rs index 4c2c1a2e..3f81270f 100644 --- a/widget/src/tooltip.rs +++ b/widget/src/tooltip.rs @@ -214,11 +214,9 @@ where let is_idle = *state == State::Idle; - if was_idle != is_idle { - shell.invalidate_layout(); - shell.request_redraw(); - } else if self.position == Position::FollowCursor - && previous_state != *state + if was_idle != is_idle + || (self.position == Position::FollowCursor + && previous_state != *state) { shell.request_redraw(); } diff --git a/winit/Cargo.toml b/winit/Cargo.toml index 8827d9bb..ebac340b 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -16,12 +16,13 @@ workspace = true [features] default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"] debug = ["iced_debug/enable"] -system = ["sysinfo"] +sysinfo = ["dep:sysinfo"] x11 = ["winit/x11"] wayland = ["winit/wayland"] wayland-dlopen = ["winit/wayland-dlopen"] wayland-csd-adwaita = ["winit/wayland-csd-adwaita"] unconditional-rendering = [] +linux-theme-detection = ["dep:mundy", "mundy/async-io", "mundy/color-scheme"] [dependencies] iced_debug.workspace = true @@ -41,3 +42,7 @@ sysinfo.optional = true web-sys.workspace = true web-sys.features = ["Document", "Window", "HtmlCanvasElement"] wasm-bindgen-futures.workspace = true + +[target.'cfg(target_os = "linux")'.dependencies] +mundy.workspace = true +mundy.optional = true diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index 4b9fcc0d..250918ab 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -5,6 +5,7 @@ use crate::core::input_method; use crate::core::keyboard; use crate::core::mouse; +use crate::core::theme; use crate::core::touch; use crate::core::window; use crate::core::{Event, Point, Size}; @@ -13,6 +14,7 @@ use crate::core::{Event, Point, Size}; pub fn window_attributes( settings: window::Settings, title: &str, + scale_factor: f32, primary_monitor: Option, _id: Option, ) -> winit::window::WindowAttributes { @@ -21,8 +23,8 @@ pub fn window_attributes( attributes = attributes .with_title(title) .with_inner_size(winit::dpi::LogicalSize { - width: settings.size.width, - height: settings.size.height, + width: settings.size.width * scale_factor, + height: settings.size.height * scale_factor, }) .with_maximized(settings.maximized) .with_fullscreen( @@ -138,7 +140,7 @@ pub fn window_attributes( /// Converts a winit window event into an iced event. pub fn window_event( event: winit::event::WindowEvent, - scale_factor: f64, + scale_factor: f32, modifiers: winit::keyboard::ModifiersState, ) -> Option { use winit::event::Ime; @@ -146,7 +148,7 @@ pub fn window_event( match event { WindowEvent::Resized(new_size) => { - let logical_size = new_size.to_logical(scale_factor); + let logical_size = new_size.to_logical(f64::from(scale_factor)); Some(Event::Window(window::Event::Resized(Size { width: logical_size.width, @@ -157,7 +159,7 @@ pub fn window_event( Some(Event::Window(window::Event::CloseRequested)) } WindowEvent::CursorMoved { position, .. } => { - let position = position.to_logical::(scale_factor); + let position = position.to_logical::(f64::from(scale_factor)); Some(Event::Mouse(mouse::Event::CursorMoved { position: Point::new(position.x as f32, position.y as f32), @@ -313,7 +315,7 @@ pub fn window_event( } WindowEvent::Moved(position) => { let winit::dpi::LogicalPosition { x, y } = - position.to_logical(scale_factor); + position.to_logical(f64::from(scale_factor)); Some(Event::Window(window::Event::Moved(Point::new(x, y)))) } @@ -321,7 +323,7 @@ pub fn window_event( } } -/// Converts a [`window::Level`] to a [`winit`] window level. +/// Converts a [`window::Level`] into a [`winit`] window level. /// /// [`winit`]: https://github.com/rust-windowing/winit pub fn window_level(level: window::Level) -> winit::window::WindowLevel { @@ -334,7 +336,7 @@ pub fn window_level(level: window::Level) -> winit::window::WindowLevel { } } -/// Converts a [`window::Position`] to a [`winit`] logical position for a given monitor. +/// Converts a [`window::Position`] into a [`winit`] logical position for a given monitor. /// /// [`winit`]: https://github.com/rust-windowing/winit pub fn position( @@ -406,7 +408,7 @@ pub fn position( } } -/// Converts a [`window::Mode`] to a [`winit`] fullscreen mode. +/// Converts a [`window::Mode`] into a [`winit`] fullscreen mode. /// /// [`winit`]: https://github.com/rust-windowing/winit pub fn fullscreen( @@ -421,7 +423,7 @@ pub fn fullscreen( } } -/// Converts a [`window::Mode`] to a visibility flag. +/// Converts a [`window::Mode`] into a visibility flag. pub fn visible(mode: window::Mode) -> bool { match mode { window::Mode::Windowed | window::Mode::Fullscreen => true, @@ -429,7 +431,7 @@ pub fn visible(mode: window::Mode) -> bool { } } -/// Converts a [`winit`] fullscreen mode to a [`window::Mode`]. +/// Converts a [`winit`] fullscreen mode into a [`window::Mode`]. /// /// [`winit`]: https://github.com/rust-windowing/winit pub fn mode(mode: Option) -> window::Mode { @@ -439,7 +441,28 @@ pub fn mode(mode: Option) -> window::Mode { } } -/// Converts a [`mouse::Interaction`] to a [`winit`] cursor icon. +/// Converts a [`winit`] window theme into a [`theme::Mode`]. +/// +/// [`winit`]: https://github.com/rust-windowing/winit +pub fn theme_mode(theme: winit::window::Theme) -> theme::Mode { + match theme { + winit::window::Theme::Light => theme::Mode::Light, + winit::window::Theme::Dark => theme::Mode::Dark, + } +} + +/// Converts a [`theme::Mode`] into a window theme. +/// +/// [`winit`]: https://github.com/rust-windowing/winit +pub fn window_theme(mode: theme::Mode) -> Option { + match mode { + theme::Mode::None => None, + theme::Mode::Light => Some(winit::window::Theme::Light), + theme::Mode::Dark => Some(winit::window::Theme::Dark), + } +} + +/// Converts a [`mouse::Interaction`] into a [`winit`] cursor icon. /// /// [`winit`]: https://github.com/rust-windowing/winit pub fn mouse_interaction( @@ -510,12 +533,12 @@ pub fn modifiers( result } -/// Converts a physical cursor position to a logical `Point`. +/// Converts a physical cursor position into a logical `Point`. pub fn cursor_position( position: winit::dpi::PhysicalPosition, - scale_factor: f64, + scale_factor: f32, ) -> Point { - let logical_position = position.to_logical(scale_factor); + let logical_position = position.to_logical(f64::from(scale_factor)); Point::new(logical_position.x, logical_position.y) } @@ -526,11 +549,12 @@ pub fn cursor_position( /// [`iced`]: https://github.com/iced-rs/iced/tree/0.12 pub fn touch_event( touch: winit::event::Touch, - scale_factor: f64, + scale_factor: f32, ) -> touch::Event { let id = touch::Finger(touch.id); let position = { - let location = touch.location.to_logical::(scale_factor); + let location = + touch.location.to_logical::(f64::from(scale_factor)); Point::new(location.x as f32, location.y as f32) }; @@ -1180,7 +1204,7 @@ pub fn icon(icon: window::Icon) -> Option { winit::window::Icon::from_rgba(pixels, size.width, size.height).ok() } -/// Convertions some [`input_method::Purpose`] to its `winit` counterpart. +/// Converts some [`input_method::Purpose`] into its `winit` counterpart. pub fn ime_purpose( purpose: input_method::Purpose, ) -> winit::window::ImePurpose { diff --git a/winit/src/lib.rs b/winit/src/lib.rs index 459f7215..8be0e924 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -29,9 +29,6 @@ pub use winit; pub mod clipboard; pub mod conversion; -#[cfg(feature = "system")] -pub mod system; - mod error; mod proxy; mod window; @@ -53,6 +50,7 @@ use crate::futures::futures::{Future, StreamExt}; use crate::futures::subscription; use crate::futures::{Executor, Runtime}; use crate::graphics::{Compositor, compositor}; +use crate::runtime::system; use crate::runtime::user_interface::{self, UserInterface}; use crate::runtime::{Action, Task}; @@ -109,7 +107,7 @@ where let (_id, open) = runtime::window::open(window_settings); - open.then(move |_| task.take().unwrap_or(Task::none())) + open.then(move |_| task.take().unwrap_or_else(Task::none)) } else { task }; @@ -124,6 +122,7 @@ where let (event_sender, event_receiver) = mpsc::unbounded(); let (control_sender, control_receiver) = mpsc::unbounded(); + let (system_theme_sender, system_theme_receiver) = oneshot::channel(); let instance = Box::pin(run_instance::

( program, @@ -134,6 +133,7 @@ where is_daemon, graphics_settings, settings.fonts, + system_theme_receiver, )); let context = task::Context::from_waker(task::noop_waker_ref()); @@ -145,6 +145,7 @@ where sender: mpsc::UnboundedSender>>, receiver: mpsc::UnboundedReceiver, error: Option, + system_theme: Option>, #[cfg(target_arch = "wasm32")] canvas: Option, @@ -157,6 +158,7 @@ where sender: event_sender, receiver: control_receiver, error: None, + system_theme: Some(system_theme_sender), #[cfg(target_arch = "wasm32")] canvas: None, @@ -169,10 +171,15 @@ where where F: Future, { - fn resumed( - &mut self, - _event_loop: &winit::event_loop::ActiveEventLoop, - ) { + fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { + if let Some(sender) = self.system_theme.take() { + let _ = sender.send( + event_loop + .system_theme() + .map(conversion::theme_mode) + .unwrap_or_default(), + ); + } } fn new_events( @@ -307,6 +314,7 @@ where id, settings, title, + scale_factor, monitor, on_open, } => { @@ -323,6 +331,7 @@ where conversion::window_attributes( settings, &title, + scale_factor, monitor .or(event_loop.primary_monitor()), self.id.clone(), @@ -417,7 +426,9 @@ where ); } Control::Exit => { + self.process_event(event_loop, Event::Exit); event_loop.exit(); + break; } Control::Crash(error) => { self.error = Some(error); @@ -464,6 +475,7 @@ enum Event { on_open: oneshot::Sender, }, EventLoopAwakened(winit::event::Event), + Exit, } #[derive(Debug)] @@ -477,6 +489,7 @@ enum Control { title: String, monitor: Option, on_open: oneshot::Sender, + scale_factor: f32, }, } @@ -489,6 +502,7 @@ async fn run_instance

( is_daemon: bool, graphics_settings: graphics::Settings, default_fonts: Vec>, + mut _system_theme: oneshot::Receiver, ) where P: Program + 'static, P::Theme: theme::Base, @@ -508,6 +522,38 @@ async fn run_instance

( let mut user_interfaces = ManuallyDrop::new(FxHashMap::default()); let mut clipboard = Clipboard::unconnected(); + #[cfg(all(feature = "linux-theme-detection", target_os = "linux"))] + let mut system_theme = { + let to_mode = |color_scheme| match color_scheme { + mundy::ColorScheme::NoPreference => theme::Mode::None, + mundy::ColorScheme::Light => theme::Mode::Light, + mundy::ColorScheme::Dark => theme::Mode::Dark, + }; + + runtime.run( + mundy::Preferences::stream(mundy::Interest::ColorScheme) + .map(move |preferences| { + Action::System(system::Action::NotifyTheme(to_mode( + preferences.color_scheme, + ))) + }) + .boxed(), + ); + + mundy::Preferences::once_blocking( + mundy::Interest::ColorScheme, + core::time::Duration::from_millis(200), + ) + .map(|preferences| to_mode(preferences.color_scheme)) + .unwrap_or_default() + }; + + #[cfg(not(all(feature = "linux-theme-detection", target_os = "linux")))] + let mut system_theme = + _system_theme.try_recv().ok().flatten().unwrap_or_default(); + + log::info!("System theme: {system_theme:?}"); + loop { // Empty the queue if possible let event = if let Ok(event) = event_receiver.try_next() { @@ -586,14 +632,20 @@ async fn run_instance

( } } - debug::theme_changed(|| { - if window_manager.is_empty() { - theme::Base::palette(&program.theme(id)) - } else { - None - } - }); + let window_theme = window + .theme() + .map(conversion::theme_mode) + .unwrap_or_default(); + if system_theme != window_theme { + system_theme = window_theme; + + runtime.broadcast(subscription::Event::SystemThemeChanged( + window_theme, + )); + } + + let is_first = window_manager.is_empty(); let window = window_manager.insert( id, window, @@ -602,8 +654,21 @@ async fn run_instance

( .as_mut() .expect("Compositor must be initialized"), exit_on_close_request, + system_theme, ); + window.raw.set_theme(conversion::window_theme( + window.state.theme_mode(), + )); + + debug::theme_changed(|| { + if is_first { + theme::Base::palette(window.state.theme()) + } else { + None + } + }); + let logical_size = window.state.logical_size(); let _ = user_interfaces.insert( @@ -686,6 +751,7 @@ async fn run_instance

( run_action( action, &program, + &mut runtime, &mut compositor, &mut events, &mut messages, @@ -695,6 +761,7 @@ async fn run_instance

( &mut window_manager, &mut ui_caches, &mut is_window_opening, + &mut system_theme, ); actions += 1; } @@ -853,11 +920,24 @@ async fn run_instance

( continue; }; - if matches!( - window_event, - winit::event::WindowEvent::Resized(_) - ) { - window.raw.request_redraw(); + match window_event { + winit::event::WindowEvent::Resized(_) => { + window.raw.request_redraw(); + } + winit::event::WindowEvent::ThemeChanged(theme) => { + let mode = conversion::theme_mode(theme); + + if mode != system_theme { + system_theme = mode; + + runtime.broadcast( + subscription::Event::SystemThemeChanged( + mode, + ), + ); + } + } + _ => {} } if matches!( @@ -870,6 +950,7 @@ async fn run_instance

( id, )), &program, + &mut runtime, &mut compositor, &mut events, &mut messages, @@ -879,9 +960,14 @@ async fn run_instance

( &mut window_manager, &mut ui_caches, &mut is_window_opening, + &mut system_theme, ); } else { - window.state.update(&window.raw, &window_event); + window.state.update( + &program, + &window.raw, + &window_event, + ); if let Some(event) = conversion::window_event( window_event, @@ -1033,6 +1119,7 @@ async fn run_instance

( _ => {} } } + Event::Exit => break, } } @@ -1085,6 +1172,7 @@ fn update( fn run_action<'a, P, C>( action: Action, program: &'a program::Instance

, + runtime: &mut Runtime, Action>, compositor: &mut Option, events: &mut Vec<(window::Id, core::Event)>, messages: &mut Vec, @@ -1097,13 +1185,13 @@ fn run_action<'a, P, C>( window_manager: &mut WindowManager, ui_caches: &mut FxHashMap, is_window_opening: &mut bool, + system_theme: &mut theme::Mode, ) where P: Program, C: Compositor + 'static, P::Theme: theme::Base, { use crate::runtime::clipboard; - use crate::runtime::system; use crate::runtime::window; match action { @@ -1127,6 +1215,7 @@ fn run_action<'a, P, C>( id, settings, title: program.title(id), + scale_factor: program.scale_factor(id), monitor, on_open: channel, }) @@ -1187,7 +1276,10 @@ fn run_action<'a, P, C>( winit::dpi::LogicalSize { width: size.width, height: size.height, - }, + } + .to_physical::(f64::from( + window.state.scale_factor(), + )), ); } } @@ -1198,6 +1290,9 @@ fn run_action<'a, P, C>( width: size.width, height: size.height, } + .to_physical::(f64::from( + window.state.scale_factor(), + )) })); } } @@ -1208,6 +1303,9 @@ fn run_action<'a, P, C>( width: size.width, height: size.height, } + .to_physical::(f64::from( + window.state.scale_factor(), + )) })); } } @@ -1218,6 +1316,9 @@ fn run_action<'a, P, C>( width: size.width, height: size.height, } + .to_physical::(f64::from( + window.state.scale_factor(), + )) })); } } @@ -1231,7 +1332,7 @@ fn run_action<'a, P, C>( let size = window .raw .inner_size() - .to_logical(window.raw.scale_factor()); + .to_logical(f64::from(window.state.scale_factor())); let _ = channel.send(Size::new(size.width, size.height)); } @@ -1398,21 +1499,44 @@ fn run_action<'a, P, C>( } }, Action::System(action) => match action { - system::Action::QueryInformation(_channel) => { - #[cfg(feature = "system")] + system::Action::GetInformation(_channel) => { + #[cfg(feature = "sysinfo")] { if let Some(compositor) = compositor { - let graphics_info = compositor.fetch_information(); + let graphics_info = compositor.information(); let _ = std::thread::spawn(move || { - let information = - crate::system::information(graphics_info); + let information = system_information(graphics_info); let _ = _channel.send(information); }); } } } + system::Action::GetTheme(channel) => { + let _ = channel.send(*system_theme); + } + system::Action::NotifyTheme(mode) => { + if mode != *system_theme { + *system_theme = mode; + + runtime.broadcast(subscription::Event::SystemThemeChanged( + mode, + )); + } + + let Some(theme) = conversion::window_theme(mode) else { + return; + }; + + for (_id, window) in window_manager.iter_mut() { + window.state.update( + program, + &window.raw, + &winit::event::WindowEvent::ThemeChanged(theme), + ); + } + } }, Action::Widget(operation) => { let mut current_operation = Some(operation); @@ -1521,3 +1645,37 @@ pub fn user_force_quit( _ => false, } } + +#[cfg(feature = "sysinfo")] +fn system_information( + graphics: compositor::Information, +) -> system::Information { + use sysinfo::{Process, System}; + + let mut system = System::new_all(); + system.refresh_all(); + + let cpu_brand = system + .cpus() + .first() + .map(|cpu| cpu.brand().to_string()) + .unwrap_or_default(); + + let memory_used = sysinfo::get_current_pid() + .and_then(|pid| system.process(pid).ok_or("Process not found")) + .map(Process::memory) + .ok(); + + system::Information { + system_name: System::name(), + system_kernel: System::kernel_version(), + system_version: System::long_os_version(), + system_short_version: System::os_version(), + cpu_brand, + cpu_cores: system.physical_core_count(), + memory_total: system.total_memory(), + memory_used, + graphics_adapter: graphics.adapter, + graphics_backend: graphics.backend, + } +} diff --git a/winit/src/system.rs b/winit/src/system.rs deleted file mode 100644 index 0b476773..00000000 --- a/winit/src/system.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! Access the native system. -use crate::graphics::compositor; -use crate::runtime::system::{Action, Information}; -use crate::runtime::{self, Task}; - -/// Query for available system information. -pub fn fetch_information() -> Task { - runtime::task::oneshot(|channel| { - runtime::Action::System(Action::QueryInformation(channel)) - }) -} - -pub(crate) fn information( - graphics_info: compositor::Information, -) -> Information { - use sysinfo::{Process, System}; - let mut system = System::new_all(); - system.refresh_all(); - - let cpu_brand = system - .cpus() - .first() - .map(|cpu| cpu.brand().to_string()) - .unwrap_or_default(); - - let memory_used = sysinfo::get_current_pid() - .and_then(|pid| system.process(pid).ok_or("Process not found")) - .map(Process::memory) - .ok(); - - Information { - system_name: System::name(), - system_kernel: System::kernel_version(), - system_version: System::long_os_version(), - system_short_version: System::os_version(), - cpu_brand, - cpu_cores: system.physical_core_count(), - memory_total: system.total_memory(), - memory_used, - graphics_adapter: graphics_info.adapter, - graphics_backend: graphics_info.backend, - } -} diff --git a/winit/src/window.rs b/winit/src/window.rs index f2d12677..419c50e1 100644 --- a/winit/src/window.rs +++ b/winit/src/window.rs @@ -55,8 +55,9 @@ where program: &program::Instance

, compositor: &mut C, exit_on_close_request: bool, + system_theme: theme::Mode, ) -> &mut Window { - let state = State::new(program, id, &window); + let state = State::new(program, id, &window, system_theme); let viewport_version = state.viewport_version(); let physical_size = state.physical_size(); let surface = compositor.create_surface( diff --git a/winit/src/window/state.rs b/winit/src/window/state.rs index e17b32a3..4c3cb403 100644 --- a/winit/src/window/state.rs +++ b/winit/src/window/state.rs @@ -9,18 +9,20 @@ use winit::window::Window; use std::fmt::{Debug, Formatter}; -/// The state of a multi-windowed [`Program`]. +/// The state of the window of a [`Program`]. pub struct State where P::Theme: theme::Base, { title: String, - scale_factor: f64, + scale_factor: f32, viewport: Viewport, viewport_version: u64, cursor_position: Option>, modifiers: winit::keyboard::ModifiersState, - theme: P::Theme, + theme: Option, + theme_mode: theme::Mode, + default_theme: P::Theme, style: theme::Style, } @@ -29,7 +31,7 @@ where P::Theme: theme::Base, { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("multi_window::State") + f.debug_struct("window::State") .field("title", &self.title) .field("scale_factor", &self.scale_factor) .field("viewport", &self.viewport) @@ -49,18 +51,22 @@ where program: &program::Instance

, window_id: window::Id, window: &Window, + system_theme: theme::Mode, ) -> Self { let title = program.title(window_id); let scale_factor = program.scale_factor(window_id); let theme = program.theme(window_id); - let style = program.style(&theme); + let theme_mode = + theme.as_ref().map(theme::Base::mode).unwrap_or_default(); + let default_theme = ::default(system_theme); + let style = program.style(theme.as_ref().unwrap_or(&default_theme)); let viewport = { let physical_size = window.inner_size(); Viewport::with_physical_size( Size::new(physical_size.width, physical_size.height), - window.scale_factor() * scale_factor, + window.scale_factor() as f32 * scale_factor, ) }; @@ -72,6 +78,8 @@ where cursor_position: None, modifiers: winit::keyboard::ModifiersState::default(), theme, + theme_mode, + default_theme, style, } } @@ -99,7 +107,7 @@ where } /// Returns the current scale factor of the [`Viewport`] of the [`State`]. - pub fn scale_factor(&self) -> f64 { + pub fn scale_factor(&self) -> f32 { self.viewport.scale_factor() } @@ -123,7 +131,12 @@ where /// Returns the current theme of the [`State`]. pub fn theme(&self) -> &P::Theme { - &self.theme + self.theme.as_ref().unwrap_or(&self.default_theme) + } + + /// Returns the current [`theme::Mode`] of the [`State`]. + pub fn theme_mode(&self) -> theme::Mode { + self.theme_mode } /// Returns the current background [`Color`] of the [`State`]. @@ -137,14 +150,19 @@ where } /// Processes the provided window event and updates the [`State`] accordingly. - pub fn update(&mut self, window: &Window, event: &WindowEvent) { + pub fn update( + &mut self, + program: &program::Instance

, + window: &Window, + event: &WindowEvent, + ) { match event { WindowEvent::Resized(new_size) => { let size = Size::new(new_size.width, new_size.height); self.viewport = Viewport::with_physical_size( size, - window.scale_factor() * self.scale_factor, + window.scale_factor() as f32 * self.scale_factor, ); self.viewport_version = self.viewport_version.wrapping_add(1); @@ -157,7 +175,7 @@ where self.viewport = Viewport::with_physical_size( size, - new_scale_factor * self.scale_factor, + *new_scale_factor as f32 * self.scale_factor, ); self.viewport_version = self.viewport_version.wrapping_add(1); @@ -174,6 +192,16 @@ where WindowEvent::ModifiersChanged(new_modifiers) => { self.modifiers = new_modifiers.state(); } + WindowEvent::ThemeChanged(theme) => { + self.default_theme = ::default( + conversion::theme_mode(*theme), + ); + + if self.theme.is_none() { + self.style = program.style(&self.default_theme); + window.request_redraw(); + } + } _ => {} } } @@ -182,7 +210,7 @@ where /// window. /// /// Normally, a [`Program`] should be synchronized with its [`State`] - /// and window after calling [`State::update`]. + /// and window after calling [`Program::update`]. pub fn synchronize( &mut self, program: &program::Instance

, @@ -208,7 +236,7 @@ where { self.viewport = Viewport::with_physical_size( Size::new(new_size.width, new_size.height), - window.scale_factor() * new_scale_factor, + window.scale_factor() as f32 * new_scale_factor, ); self.viewport_version = self.viewport_version.wrapping_add(1); @@ -217,6 +245,45 @@ where // Update theme and appearance self.theme = program.theme(window_id); - self.style = program.style(&self.theme); + self.style = program.style(self.theme()); + + let new_mode = self + .theme + .as_ref() + .map(theme::Base::mode) + .unwrap_or_default(); + + if self.theme_mode != new_mode { + #[cfg(not(target_os = "linux"))] + { + window.set_theme(conversion::window_theme(new_mode)); + + // Assume the old mode matches the system one + // We will be notified otherwise + if new_mode == theme::Mode::None { + self.default_theme = + ::default(self.theme_mode); + + if self.theme.is_none() { + self.style = program.style(&self.default_theme); + } + } + } + + #[cfg(target_os = "linux")] + { + // mundy always notifies system theme changes, so we + // just restore the default theme mode. + let new_mode = if new_mode == theme::Mode::None { + theme::Base::mode(&self.default_theme) + } else { + new_mode + }; + + window.set_theme(conversion::window_theme(new_mode)); + } + + self.theme_mode = new_mode; + } } }