From 9994f1f133cb22d5023a10619c14b4fb86524208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 29 Aug 2025 13:19:37 +0200 Subject: [PATCH 01/36] Avoid compressing the cross-axis in `flex` layout --- core/src/layout/flex.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) 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()); From d31a7b6ae85ded56d4d4db986dcf6e73c6753883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 30 Aug 2025 17:45:48 +0200 Subject: [PATCH 02/36] Update `Cargo.lock` --- Cargo.lock | 271 +++++++++++++++++++++++++---------------------------- 1 file changed, 130 insertions(+), 141 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4b7bec86..aada7dc6 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.2", + "bitflags 2.9.3", "cc", "cesu8", "jni", @@ -245,9 +245,9 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.2" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" +checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" dependencies = [ "async-task", "concurrent-queue", @@ -557,9 +557,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bit_field" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" +checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" [[package]] name = "bitflags" @@ -569,9 +569,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.2" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29" +checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" dependencies = [ "serde", ] @@ -703,7 +703,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "log", "polling", "rustix 0.38.44", @@ -742,9 +742,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.33" +version = "1.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" +checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" dependencies = [ "jobserver", "libc", @@ -843,18 +843,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.45" +version = "4.5.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" +checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.44" +version = "4.5.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" +checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41" dependencies = [ "anstyle", "clap_lex", @@ -1030,7 +1030,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "core-foundation 0.10.1", "core-graphics-types 0.2.0", "foreign-types 0.5.0", @@ -1054,7 +1054,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "core-foundation 0.10.1", "libc", ] @@ -1065,7 +1065,7 @@ version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da46a9d5a8905cc538a4a5bceb6a4510de7a51049c5588c0114efce102bcbbe8" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "fontdb 0.16.2", "log", "rangemap", @@ -1256,9 +1256,9 @@ checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "data-url" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" +checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" [[package]] name = "deranged" @@ -1360,7 +1360,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98888c4bbd601524c11a7ed63f814b8825f420514f78e96f752c437ae9cbb5d1" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "bytemuck", "drm-ffi", "drm-fourcc", @@ -1713,9 +1713,9 @@ checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -1897,19 +1897,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.0.8", + "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", ] @@ -1936,7 +1936,7 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi 0.14.3+wasi-0.2.4", ] [[package]] @@ -2048,7 +2048,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "gpu-alloc-types", ] @@ -2058,7 +2058,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", ] [[package]] @@ -2079,7 +2079,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "gpu-descriptor-types", "hashbrown", ] @@ -2090,7 +2090,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", ] [[package]] @@ -2474,7 +2474,7 @@ dependencies = [ name = "iced_core" version = "0.14.0-dev" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "bytes", "dark-light", "glam", @@ -2527,7 +2527,7 @@ dependencies = [ name = "iced_graphics" version = "0.14.0-dev" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "bytemuck", "cosmic-text", "half", @@ -2614,7 +2614,7 @@ dependencies = [ name = "iced_wgpu" version = "0.14.0-dev" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "bytemuck", "cryoglyph", "futures", @@ -2753,9 +2753,9 @@ dependencies = [ [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -2797,9 +2797,9 @@ dependencies = [ [[package]] name = "image-webp" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6970fe7a5300b4b42e62c52efa0187540a5bef546c60edaf554ef595d2e6f0b" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" dependencies = [ "byteorder-lite", "quick-error", @@ -2819,9 +2819,9 @@ checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" [[package]] name = "indexmap" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" dependencies = [ "equivalent", "hashbrown", @@ -2855,11 +2855,11 @@ dependencies = [ [[package]] name = "io-uring" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "cfg-if", "libc", ] @@ -2958,9 +2958,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ "getrandom 0.3.3", "libc", @@ -3102,7 +3102,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "libc", "redox_syscall 0.5.17", ] @@ -3309,9 +3309,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "483758ad303d734cec05e5c12b41d7e93e6a6390c5e9dae6bdeb7c1259012d28" +checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" dependencies = [ "libc", ] @@ -3331,7 +3331,7 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "block", "core-graphics-types 0.2.0", "foreign-types 0.5.0", @@ -3438,7 +3438,7 @@ checksum = "916cbc7cb27db60be930a4e2da243cf4bc39569195f22fd8ee419cd31d5b662c" dependencies = [ "arrayvec", "bit-set", - "bitflags 2.9.2", + "bitflags 2.9.3", "cfg-if", "cfg_aliases", "codespan-reporting", @@ -3479,7 +3479,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "jni-sys", "log", "ndk-sys", @@ -3515,7 +3515,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "cfg-if", "cfg_aliases", "libc", @@ -3549,12 +3549,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]] @@ -3697,7 +3696,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "block2", "libc", "objc2 0.5.2", @@ -3713,7 +3712,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "block2", "objc2 0.5.2", "objc2-core-location", @@ -3737,7 +3736,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3779,7 +3778,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "block2", "dispatch", "libc", @@ -3792,7 +3791,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "objc2 0.6.2", ] @@ -3814,7 +3813,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3826,7 +3825,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3849,7 +3848,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "block2", "objc2 0.5.2", "objc2-cloud-kit", @@ -3881,7 +3880,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "block2", "objc2 0.5.2", "objc2-core-location", @@ -3918,7 +3917,7 @@ version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "libc", "once_cell", "onig_sys", @@ -3957,7 +3956,7 @@ version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "cfg-if", "foreign-types 0.3.2", "libc", @@ -4053,12 +4052,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" @@ -4154,9 +4147,9 @@ checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "phf" @@ -4270,7 +4263,7 @@ checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" dependencies = [ "base64 0.22.1", "indexmap", - "quick-xml 0.38.2", + "quick-xml 0.38.3", "serde", "time", ] @@ -4359,9 +4352,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" dependencies = [ "zerovec", ] @@ -4450,7 +4443,7 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "getopts", "memchr", "pulldown-cmark-escape", @@ -4502,9 +4495,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.38.2" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d200a41a7797e6461bd04e4e95c3347053a731c32c87f066f2f0dda22dbdbba8" +checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" dependencies = [ "memchr", ] @@ -4702,7 +4695,7 @@ version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", ] [[package]] @@ -4718,9 +4711,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" dependencies = [ "aho-corasick", "memchr", @@ -4730,9 +4723,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" dependencies = [ "aho-corasick", "memchr", @@ -4741,9 +4734,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "renderdoc-sys" @@ -4886,7 +4879,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "errno", "libc", "linux-raw-sys 0.4.15", @@ -4899,7 +4892,7 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "errno", "libc", "linux-raw-sys 0.9.4", @@ -4976,7 +4969,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "bytemuck", "libm", "smallvec", @@ -5059,7 +5052,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -5293,7 +5286,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "calloop", "calloop-wayland-source", "cursor-icon", @@ -5422,7 +5415,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.2", + "bitflags 2.9.3", ] [[package]] @@ -5612,7 +5605,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -6039,7 +6032,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "bytes", "futures-util", "http 1.3.1", @@ -6109,9 +6102,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", @@ -6263,9 +6256,9 @@ checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", @@ -6458,11 +6451,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.14.2+wasi-0.2.4" +version = "0.14.3+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] @@ -6583,7 +6576,7 @@ version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "rustix 1.0.8", "wayland-backend", "wayland-scanner", @@ -6595,7 +6588,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "cursor-icon", "wayland-backend", ] @@ -6617,7 +6610,7 @@ version = "0.32.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "wayland-backend", "wayland-client", "wayland-scanner", @@ -6629,7 +6622,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6642,7 +6635,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6749,7 +6742,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70b6ff82bbf6e9206828e1a3178e851f8c20f1c9028e74dd3a8090741ccd5798" dependencies = [ "arrayvec", - "bitflags 2.9.2", + "bitflags 2.9.3", "cfg-if", "cfg_aliases", "document-features", @@ -6780,7 +6773,7 @@ dependencies = [ "arrayvec", "bit-set", "bit-vec", - "bitflags 2.9.2", + "bitflags 2.9.3", "cfg_aliases", "document-features", "hashbrown", @@ -6849,7 +6842,7 @@ dependencies = [ "arrayvec", "ash", "bit-set", - "bitflags 2.9.2", + "bitflags 2.9.3", "block", "bytemuck", "cfg-if", @@ -6893,7 +6886,7 @@ version = "26.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eca7a8d8af57c18f57d393601a1fb159ace8b2328f1b6b5f80893f7d672c9ae2" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "bytemuck", "js-sys", "log", @@ -7429,7 +7422,7 @@ dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.9.2", + "bitflags 2.9.3", "block2", "bytemuck", "calloop", @@ -7474,9 +7467,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] @@ -7492,13 +7485,10 @@ dependencies = [ ] [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.2", -] +checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" [[package]] name = "writeable" @@ -7519,24 +7509,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.0.8", "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" @@ -7550,7 +7540,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.9.2", + "bitflags 2.9.3", "dlib", "log", "once_cell", @@ -7622,9 +7612,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.9.0" +version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb4f9a464286d42851d18a605f7193b8febaf5b0919d71c6399b7b26e5b0aad" +checksum = "67a073be99ace1adc48af593701c8015cd9817df372e14a1a6b0ee8f8bf043be" dependencies = [ "async-broadcast", "async-executor", @@ -7646,7 +7636,7 @@ dependencies = [ "serde_repr", "tracing", "uds_windows", - "windows-sys 0.59.0", + "windows-sys 0.60.2", "winnow", "zbus_macros", "zbus_names", @@ -7655,9 +7645,9 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.9.0" +version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef9859f68ee0c4ee2e8cde84737c78e3f4c54f946f2a38645d0d4c7a95327659" +checksum = "0e80cd713a45a49859dcb648053f63265f4f2851b6420d47a958e5697c68b131" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -7792,9 +7782,9 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.6.0" +version = "5.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91b3680bb339216abd84714172b5138a4edac677e641ef17e1d8cb1b3ca6e6f" +checksum = "999dd3be73c52b1fccd109a4a81e4fcd20fab1d3599c8121b38d04e1419498db" dependencies = [ "endi", "enumflags2", @@ -7807,9 +7797,9 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "5.6.0" +version = "5.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8c68501be459a8dbfffbe5d792acdd23b4959940fc87785fb013b32edbc208" +checksum = "6643fd0b26a46d226bd90d3f07c1b5321fe9bb7f04673cb37ac6d6883885b68e" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -7820,14 +7810,13 @@ dependencies = [ [[package]] name = "zvariant_utils" -version = "3.2.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34" +checksum = "c6949d142f89f6916deca2232cf26a8afacf2b9fdc35ce766105e104478be599" dependencies = [ "proc-macro2", "quote", "serde", - "static_assertions", "syn", "winnow", ] From bc7d64987e7b062497105a5ec1ce8d6f433a9c2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 30 Aug 2025 18:15:12 +0200 Subject: [PATCH 03/36] Add `Auto` strategy to `text::Shaping` --- Cargo.toml | 4 +++ core/Cargo.toml | 2 ++ core/src/text.rs | 29 +++++++++++++++++-- examples/layout/src/main.rs | 6 ++-- .../catppuccin_frappé-tiny-skia.sha256 | 2 +- graphics/src/text.rs | 9 +++++- graphics/src/text/cache.rs | 2 +- graphics/src/text/paragraph.rs | 4 +-- 8 files changed, 47 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index be4070ac..a3ae52c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,6 +69,10 @@ advanced = ["iced_core/advanced", "iced_widget/advanced"] 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 diff --git a/core/Cargo.toml b/core/Cargo.toml index f57aaa4d..cb7ef3ef 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -17,6 +17,8 @@ workspace = true auto-detect-theme = ["dep:dark-light"] advanced = [] crisp = [] +basic-shaping = [] +advanced-shaping = [] [dependencies] bitflags.workspace = true diff --git a/core/src/text.rs b/core/src/text.rs index 0b51f244..e47d9bbd 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/examples/layout/src/main.rs b/examples/layout/src/main.rs index c15a0bfc..f3765d9d 100644 --- a/examples/layout/src/main.rs +++ b/examples/layout/src/main.rs @@ -94,14 +94,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(), @@ -313,7 +313,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/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/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, ); From 6df435adba58b7954d10662392412c1e986e0ac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 30 Aug 2025 18:30:23 +0200 Subject: [PATCH 04/36] Use `Default` implementation of `text::Shaping` --- graphics/src/geometry/text.rs | 2 +- widget/src/overlay/menu.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/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, } From 665c89a062398f8d9ceff734471746f22e1a4f8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 30 Aug 2025 18:34:38 +0200 Subject: [PATCH 05/36] Use `Shaping::Basic` for `icon` helpers in examples --- examples/editor/src/main.rs | 5 ++++- examples/todos/src/main.rs | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/editor/src/main.rs b/examples/editor/src/main.rs index ad2337d8..e6243717 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/todos/src/main.rs b/examples/todos/src/main.rs index 65f4a56c..bd1d9379 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -460,6 +460,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> { From e324b18dff6b59d5ff286c0acbe4135e6e8831c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 30 Aug 2025 18:42:11 +0200 Subject: [PATCH 06/36] Avoid snapshot testing in CI with `--all-features` --- .github/workflows/test.yml | 1 - examples/todos/src/main.rs | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) 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/examples/todos/src/main.rs b/examples/todos/src/main.rs index bd1d9379..ed767e70 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -595,6 +595,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))); From ad0e4c53cf5bcd02c110736853dac214ab35edb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sun, 31 Aug 2025 17:22:08 +0200 Subject: [PATCH 07/36] Stop invalidating layout in `tooltip` widget --- widget/src/tooltip.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) 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(); } From 74b792b6084084f6941ec1c5917293156a60688e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 2 Sep 2025 23:29:22 +0200 Subject: [PATCH 08/36] Use `f32` for `scale_factor` --- core/src/window/screenshot.rs | 4 ++-- devtools/src/lib.rs | 4 ++-- examples/integration/src/main.rs | 6 +++--- examples/multi_window/src/main.rs | 6 +++--- graphics/src/viewport.rs | 10 +++++----- program/src/lib.rs | 20 ++++++++++---------- src/application.rs | 2 +- src/daemon.rs | 2 +- test/src/lib.rs | 2 +- tiny_skia/src/lib.rs | 5 ++--- wgpu/src/lib.rs | 6 +++--- winit/src/conversion.rs | 22 ++++++++++++---------- winit/src/lib.rs | 6 +++++- winit/src/window/state.rs | 12 ++++++------ 14 files changed, 56 insertions(+), 51 deletions(-) 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 45409267..09a2a67b 100644 --- a/devtools/src/lib.rs +++ b/devtools/src/lib.rs @@ -98,7 +98,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) } } @@ -397,7 +397,7 @@ where program.style(self.state(), theme) } - fn scale_factor(&self, program: &P, window: window::Id) -> f64 { + fn scale_factor(&self, program: &P, window: window::Id) -> f32 { program.scale_factor(self.state(), window) } 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/multi_window/src/main.rs b/examples/multi_window/src/main.rs index 77be505b..cb5e6f43 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, } @@ -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); } @@ -146,7 +146,7 @@ impl Example { } } - 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/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 e25cdb22..d479d2f8 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -94,7 +94,7 @@ pub trait Program: Sized { 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 } } @@ -171,7 +171,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) } } @@ -250,7 +250,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) } } @@ -332,7 +332,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) } } @@ -411,7 +411,7 @@ pub fn with_style( 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) } } @@ -422,7 +422,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, @@ -431,7 +431,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; @@ -490,7 +490,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) } } @@ -573,7 +573,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) } } @@ -638,7 +638,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/src/application.rs b/src/application.rs index e735218b..d8f7862b 100644 --- a/src/application.rs +++ b/src/application.rs @@ -387,7 +387,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, > { diff --git a/src/daemon.rs b/src/daemon.rs index 2074cf71..05d276c3 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -232,7 +232,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, > { diff --git a/test/src/lib.rs b/test/src/lib.rs index 636b8173..30b08176 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -462,7 +462,7 @@ where screenshot: window::Screenshot::new( rgba, physical_size, - f64::from(scale_factor), + scale_factor, ), renderer: self.renderer.name(), }) diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index ca00737f..e06c7820 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(); @@ -405,8 +405,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/wgpu/src/lib.rs b/wgpu/src/lib.rs index a24677bd..e55ad9a4 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()); @@ -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(), )); @@ -879,7 +879,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/winit/src/conversion.rs b/winit/src/conversion.rs index 4b9fcc0d..6f22405d 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -13,6 +13,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 +22,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 +139,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 +147,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 +158,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 +314,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)))) } @@ -513,9 +514,9 @@ pub fn modifiers( /// Converts a physical cursor position to 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 +527,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) }; diff --git a/winit/src/lib.rs b/winit/src/lib.rs index 0ad2c507..edfc7783 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -310,6 +310,7 @@ where id, settings, title, + scale_factor, monitor, on_open, } => { @@ -326,6 +327,7 @@ where conversion::window_attributes( settings, &title, + scale_factor, monitor .or(event_loop.primary_monitor()), self.id.clone(), @@ -480,6 +482,7 @@ enum Control { title: String, monitor: Option, on_open: oneshot::Sender, + scale_factor: f32, }, } @@ -1130,6 +1133,7 @@ fn run_action<'a, P, C>( id, settings, title: program.title(id), + scale_factor: program.scale_factor(id), monitor, on_open: channel, }) @@ -1234,7 +1238,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)); } diff --git a/winit/src/window/state.rs b/winit/src/window/state.rs index e17b32a3..c345a846 100644 --- a/winit/src/window/state.rs +++ b/winit/src/window/state.rs @@ -15,7 +15,7 @@ where P::Theme: theme::Base, { title: String, - scale_factor: f64, + scale_factor: f32, viewport: Viewport, viewport_version: u64, cursor_position: Option>, @@ -60,7 +60,7 @@ where Viewport::with_physical_size( Size::new(physical_size.width, physical_size.height), - window.scale_factor() * scale_factor, + window.scale_factor() as f32 * scale_factor, ) }; @@ -99,7 +99,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() } @@ -144,7 +144,7 @@ where 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 +157,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); @@ -208,7 +208,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); From c65bfd1afb399a74eddb3f66853f3cc385a08826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 2 Sep 2025 23:33:42 +0200 Subject: [PATCH 09/36] Apply `scale_factor` to sizing tasks in `window` --- winit/src/lib.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/winit/src/lib.rs b/winit/src/lib.rs index edfc7783..424fb673 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -1194,7 +1194,10 @@ fn run_action<'a, P, C>( winit::dpi::LogicalSize { width: size.width, height: size.height, - }, + } + .to_physical::(f64::from( + window.state.scale_factor(), + )), ); } } @@ -1205,6 +1208,9 @@ fn run_action<'a, P, C>( width: size.width, height: size.height, } + .to_physical::(f64::from( + window.state.scale_factor(), + )) })); } } @@ -1215,6 +1221,9 @@ fn run_action<'a, P, C>( width: size.width, height: size.height, } + .to_physical::(f64::from( + window.state.scale_factor(), + )) })); } } @@ -1225,6 +1234,9 @@ fn run_action<'a, P, C>( width: size.width, height: size.height, } + .to_physical::(f64::from( + window.state.scale_factor(), + )) })); } } From 355f0e09218a8c9c1bed08b04201d32030a4150c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 6 Sep 2025 06:02:11 +0200 Subject: [PATCH 10/36] Fix `SIGSEGV` on exit in some edge cases --- winit/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/winit/src/lib.rs b/winit/src/lib.rs index 424fb673..38d6f3c3 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -422,7 +422,9 @@ where ); } Control::Exit => { + self.process_event(event_loop, Event::Exit); event_loop.exit(); + break; } Control::Crash(error) => { self.error = Some(error); @@ -469,6 +471,7 @@ enum Event { on_open: oneshot::Sender, }, EventLoopAwakened(winit::event::Event), + Exit, } #[derive(Debug)] @@ -1039,6 +1042,7 @@ async fn run_instance

( _ => {} } } + Event::Exit => break, } } From ebe223cb5d9dfdd20e0978969807d6944d670742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 6 Sep 2025 06:16:00 +0200 Subject: [PATCH 11/36] Relax `Send` and `Sync` bounds for `wgpu::Primitive` on Wasm --- wgpu/src/primitive.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs index c8bcb65d..dd877d4d 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,7 +11,7 @@ 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 { +pub trait Primitive: Debug + MaybeSend + MaybeSync + 'static { /// Processes the [`Primitive`], allowing for GPU buffer allocation. fn prepare( &self, @@ -59,9 +60,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,14 +73,14 @@ impl Storage { } /// Inserts the data `T` in to [`Storage`]. - pub fn store(&mut self, data: T) { + pub fn store(&mut self, data: T) { 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 + (pipeline.as_ref() as &dyn Any) .downcast_ref::() .expect("Value with this type does not exist in Storage.") }) @@ -87,9 +89,13 @@ impl Storage { /// 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 + (pipeline.as_mut() as &mut dyn Any) .downcast_mut::() .expect("Value with this type does not exist in Storage.") }) } } + +trait AnyConcurrent: Any + MaybeSend + MaybeSync {} + +impl AnyConcurrent for T where T: Any + MaybeSend + MaybeSync {} From d9f24dddb984307127135d5ef0f1b4cd7af98360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 6 Sep 2025 17:07:24 +0200 Subject: [PATCH 12/36] Add a pure `now` helper to `time` module --- src/time.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/time.rs b/src/time.rs index f32eeb17..6400dc7f 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, +/// this is an "impure" operation (i.e. it's not referentially transparent). +/// +/// You may care about purity if you want to leverage the `time-travel` +/// feature properly. +pub fn now() -> Task { + Task::future(async { Instant::now() }) +} From 6a1cd02b3affbd9d2c9b71d93ceb4c621894ff17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 6 Sep 2025 17:15:27 +0200 Subject: [PATCH 13/36] Clarify "this" in `time::now` docs --- src/time.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/time.rs b/src/time.rs index 6400dc7f..b43ced96 100644 --- a/src/time.rs +++ b/src/time.rs @@ -17,8 +17,8 @@ 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, -/// this is an "impure" operation (i.e. it's not referentially transparent). +/// 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. From 53a98bf7def33dccb17fe6211709401eb10f442d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 6 Sep 2025 20:23:31 +0200 Subject: [PATCH 14/36] Hide `Storage` from `Primitive` for type-safety --- examples/custom_shader/src/scene.rs | 30 ++--- examples/custom_shader/src/scene/pipeline.rs | 7 +- wgpu/src/layer.rs | 4 +- wgpu/src/lib.rs | 6 +- wgpu/src/primitive.rs | 127 ++++++++++++++++--- 5 files changed, 129 insertions(+), 45 deletions(-) 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/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 e55ad9a4..0ff74524 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -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, ); @@ -548,8 +548,8 @@ impl Renderer { .and_then(Rectangle::snap) { instance.primitive.render( - encoder, &primitive_storage, + encoder, frame, &clip_bounds, ); @@ -805,7 +805,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); } } diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs index dd877d4d..eef7cfe9 100644 --- a/wgpu/src/primitive.rs +++ b/wgpu/src/primitive.rs @@ -12,13 +12,32 @@ pub type Batch = Vec; /// A set of methods which allows a [`Primitive`] to be rendered. pub trait Primitive: Debug + MaybeSend + MaybeSync + 'static { - /// Processes the [`Primitive`], allowing for GPU buffer allocation. - fn prepare( + /// 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, ); @@ -26,21 +45,92 @@ pub trait Primitive: Debug + MaybeSend + MaybeSync + 'static { /// Renders the [`Primitive`]. fn render( &self, + 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 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 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 { @@ -48,7 +138,7 @@ impl Instance { pub fn new(bounds: Rectangle, primitive: impl Primitive) -> Self { Instance { bounds, - primitive: Box::new(primitive), + primitive: Box::new(BlackBox { primitive }), } } } @@ -73,26 +163,25 @@ 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.as_ref() as &dyn Any) - .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.as_mut() as &mut dyn Any) - .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) } } From 0d05ea56d3abbc58d2269369955f3fca18023706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 6 Sep 2025 21:17:38 +0200 Subject: [PATCH 15/36] Introduce lightweight `draw` for `wgpu::Primitive` --- wgpu/src/lib.rs | 60 ++++++++++++++++++++++++++--------------- wgpu/src/primitive.rs | 62 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 95 insertions(+), 27 deletions(-) diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 0ff74524..db68748a 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -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,11 +541,30 @@ impl Renderer { .read() .expect("Read primitive storage"); + let mut need_render = Vec::new(); + for instance in &layer.primitives { if let Some(clip_bounds) = (instance.bounds * scale) .intersection(&physical_bounds) .and_then(Rectangle::snap) { + let drawn = instance.primitive.draw( + &primitive_storage, + &mut render_pass, + frame, + &clip_bounds, + ); + + if !drawn { + need_render.push((instance, clip_bounds)); + } + } + } + + if !need_render.is_empty() { + let _ = ManuallyDrop::into_inner(render_pass); + + for (instance, clip_bounds) in need_render { instance.primitive.render( &primitive_storage, encoder, @@ -554,29 +572,29 @@ impl Renderer { &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"))] diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs index eef7cfe9..49081b60 100644 --- a/wgpu/src/primitive.rs +++ b/wgpu/src/primitive.rs @@ -42,14 +42,39 @@ pub trait Primitive: Debug + MaybeSend + MaybeSync + 'static { viewport: &Viewport, ); - /// Renders the [`Primitive`]. + /// Draws the [`Primitive`] in the given [`wgpu::RenderPass`]. + /// + /// When possible, this should be implement over [`render`](Self::render) + /// since reusing the existing render pass should be considerably more + /// efficient than issuing a completely new one. + /// + /// 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<'_>, + _target: &wgpu::TextureView, + _clip_bounds: &Rectangle, + ) -> 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, - renderer: &Self::Renderer, - encoder: &mut wgpu::CommandEncoder, - target: &wgpu::TextureView, - clip_bounds: &Rectangle, - ); + _renderer: &Self::Renderer, + _encoder: &mut wgpu::CommandEncoder, + _target: &wgpu::TextureView, + _clip_bounds: &Rectangle, + ) { + } } pub(crate) trait Stored: @@ -65,6 +90,14 @@ pub(crate) trait Stored: viewport: &Viewport, ); + fn draw( + &self, + storage: &Storage, + render_pass: &mut wgpu::RenderPass<'_>, + target: &wgpu::TextureView, + clip_bounds: &Rectangle, + ) -> bool; + fn render( &self, storage: &Storage, @@ -105,6 +138,23 @@ impl Stored for BlackBox

{ .prepare(renderer, device, queue, bounds, viewport); } + fn draw( + &self, + storage: &Storage, + render_pass: &mut wgpu::RenderPass<'_>, + target: &wgpu::TextureView, + clip_bounds: &Rectangle, + ) -> 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, target, clip_bounds) + } + fn render( &self, storage: &Storage, From 6fbde6720f688901fd649323206880e576adc3d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 6 Sep 2025 21:19:14 +0200 Subject: [PATCH 16/36] Fix typo in `wgpu::Primitive` docs --- wgpu/src/primitive.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs index 49081b60..592a3375 100644 --- a/wgpu/src/primitive.rs +++ b/wgpu/src/primitive.rs @@ -44,9 +44,9 @@ pub trait Primitive: Debug + MaybeSend + MaybeSync + 'static { /// Draws the [`Primitive`] in the given [`wgpu::RenderPass`]. /// - /// When possible, this should be implement over [`render`](Self::render) + /// When possible, this should be implemented over [`render`](Self::render) /// since reusing the existing render pass should be considerably more - /// efficient than issuing a completely new one. + /// efficient than issuing a new one. /// /// If you have complex composition needs, then you can leverage /// [`render`](Self::render) by returning `false` here. From 949852e5feca419cd27195da600ab02e05a243a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 6 Sep 2025 21:27:48 +0200 Subject: [PATCH 17/36] Remove `target` argument from `Primitive::draw` --- wgpu/src/lib.rs | 1 - wgpu/src/primitive.rs | 6 +----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index db68748a..3e2e3ea8 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -551,7 +551,6 @@ impl Renderer { let drawn = instance.primitive.draw( &primitive_storage, &mut render_pass, - frame, &clip_bounds, ); diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs index 592a3375..b5a73032 100644 --- a/wgpu/src/primitive.rs +++ b/wgpu/src/primitive.rs @@ -56,7 +56,6 @@ pub trait Primitive: Debug + MaybeSend + MaybeSync + 'static { &self, _renderer: &Self::Renderer, _render_pass: &mut wgpu::RenderPass<'_>, - _target: &wgpu::TextureView, _clip_bounds: &Rectangle, ) -> bool { false @@ -94,7 +93,6 @@ pub(crate) trait Stored: &self, storage: &Storage, render_pass: &mut wgpu::RenderPass<'_>, - target: &wgpu::TextureView, clip_bounds: &Rectangle, ) -> bool; @@ -142,7 +140,6 @@ impl Stored for BlackBox

{ &self, storage: &Storage, render_pass: &mut wgpu::RenderPass<'_>, - target: &wgpu::TextureView, clip_bounds: &Rectangle, ) -> bool { let renderer = storage @@ -151,8 +148,7 @@ impl Stored for BlackBox

{ .downcast_ref::() .expect("renderer should have the proper type"); - self.primitive - .draw(renderer, render_pass, target, clip_bounds) + self.primitive.draw(renderer, render_pass, clip_bounds) } fn render( From efae3860bc3e2197eea39d30d756f93fb14c98d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sun, 7 Sep 2025 04:55:45 +0200 Subject: [PATCH 18/36] Set `wgpu` viewport and scissoring before `Primitive::draw` --- wgpu/src/lib.rs | 40 ++++++++++++++++++++++++++++++++++++---- wgpu/src/primitive.rs | 8 ++++---- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 3e2e3ea8..3d8ee265 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -544,22 +544,54 @@ impl Renderer { 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) { - let drawn = instance.primitive.draw( - &primitive_storage, - &mut render_pass, - &clip_bounds, + 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); diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs index b5a73032..dd661e7e 100644 --- a/wgpu/src/primitive.rs +++ b/wgpu/src/primitive.rs @@ -48,6 +48,9 @@ pub trait Primitive: Debug + MaybeSend + MaybeSync + 'static { /// 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. /// @@ -56,7 +59,6 @@ pub trait Primitive: Debug + MaybeSend + MaybeSync + 'static { &self, _renderer: &Self::Renderer, _render_pass: &mut wgpu::RenderPass<'_>, - _clip_bounds: &Rectangle, ) -> bool { false } @@ -93,7 +95,6 @@ pub(crate) trait Stored: &self, storage: &Storage, render_pass: &mut wgpu::RenderPass<'_>, - clip_bounds: &Rectangle, ) -> bool; fn render( @@ -140,7 +141,6 @@ impl Stored for BlackBox

{ &self, storage: &Storage, render_pass: &mut wgpu::RenderPass<'_>, - clip_bounds: &Rectangle, ) -> bool { let renderer = storage .get::

() @@ -148,7 +148,7 @@ impl Stored for BlackBox

{ .downcast_ref::() .expect("renderer should have the proper type"); - self.primitive.draw(renderer, render_pass, clip_bounds) + self.primitive.draw(renderer, render_pass) } fn render( From 9445f5fcdf4e53eaaec4fe1944432b2b333a5c1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 8 Sep 2025 01:24:22 +0200 Subject: [PATCH 19/36] Replace `dark-light` with `mundy` --- Cargo.lock | 737 ++++++++++++++++++++++++++++------------------ Cargo.toml | 6 +- core/Cargo.toml | 6 +- core/src/theme.rs | 19 +- 4 files changed, 466 insertions(+), 302 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aada7dc6..ac9425e7 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,6 +91,15 @@ 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" @@ -190,47 +199,18 @@ 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 = "async-broadcast" 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" @@ -268,21 +248,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" @@ -307,7 +272,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", ] @@ -329,14 +294,14 @@ 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", ] @@ -370,32 +335,6 @@ dependencies = [ "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" @@ -504,6 +443,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "beul" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3c3baedf037b161e69da9200947329f52afa3b6e1cdbe6f344d6aa350fedd2f" + [[package]] name = "bezier_tool" version = "0.1.0" @@ -569,9 +514,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", ] @@ -606,13 +551,22 @@ dependencies = [ "objc2 0.5.2", ] +[[package]] +name = "block2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" +dependencies = [ + "objc2 0.6.2", +] + [[package]] name = "blocking" 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", @@ -703,7 +657,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", @@ -742,10 +696,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", @@ -811,7 +766,7 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -843,18 +798,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", @@ -882,7 +837,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b7f4aaa047ba3c3630b080bb9860894732ff23e2aee290a418909aa6d5df38f" dependencies = [ "objc2 0.5.2", - "objc2-app-kit", + "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", ] @@ -1030,7 +985,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", @@ -1054,7 +1009,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", ] @@ -1065,7 +1020,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", @@ -1234,20 +1189,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", - "async-std", - "objc2 0.5.2", - "objc2-foundation 0.2.2", - "web-sys", - "winreg", -] - [[package]] name = "data-encoding" version = "2.9.0" @@ -1262,9 +1203,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", ] @@ -1297,7 +1238,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -1306,6 +1247,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags 2.9.4", + "objc2 0.6.2", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -1360,7 +1311,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", @@ -1505,12 +1456,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" @@ -1528,7 +1473,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", ] @@ -1573,6 +1518,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" @@ -1589,6 +1554,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" @@ -1936,7 +1907,7 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.3+wasi-0.2.4", + "wasi 0.14.4+wasi-0.2.4", ] [[package]] @@ -1998,18 +1969,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" @@ -2048,7 +2007,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", ] @@ -2058,7 +2017,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]] @@ -2079,7 +2038,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", ] @@ -2090,7 +2049,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]] @@ -2474,12 +2433,12 @@ dependencies = [ name = "iced_core" version = "0.14.0-dev" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "bytes", - "dark-light", "glam", "lilt", "log", + "mundy", "num-traits", "rustc-hash 2.1.1", "serde", @@ -2527,7 +2486,7 @@ dependencies = [ name = "iced_graphics" version = "0.14.0-dev" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "bytemuck", "cosmic-text", "half", @@ -2589,7 +2548,7 @@ version = "0.14.0-dev" dependencies = [ "iced_renderer", "iced_runtime", - "png", + "png 0.17.16", "sha2", "thiserror 1.0.69", ] @@ -2614,7 +2573,7 @@ dependencies = [ name = "iced_wgpu" version = "0.14.0-dev" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "bytemuck", "cryoglyph", "futures", @@ -2774,9 +2733,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", @@ -2784,8 +2743,9 @@ dependencies = [ "exr", "gif", "image-webp", + "moxcms", "num-traits", - "png", + "png 0.18.0", "qoi", "ravif", "rayon", @@ -2859,7 +2819,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", ] @@ -2974,9 +2934,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", @@ -3029,15 +2989,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" @@ -3060,9 +3011,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" @@ -3102,7 +3053,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", ] @@ -3172,12 +3123,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" @@ -3300,11 +3248,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.0.8", ] [[package]] @@ -3331,7 +3279,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", @@ -3390,6 +3338,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" @@ -3424,6 +3382,32 @@ 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", + "beul", + "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" @@ -3438,7 +3422,7 @@ checksum = "916cbc7cb27db60be930a4e2da243cf4bc39569195f22fd8ee419cd31d5b662c" dependencies = [ "arrayvec", "bit-set", - "bitflags 2.9.3", + "bitflags 2.9.4", "cfg-if", "cfg_aliases", "codespan-reporting", @@ -3479,7 +3463,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", @@ -3515,7 +3499,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", @@ -3696,14 +3680,33 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.9.3", - "block2", + "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]] +name = "objc2-app-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" +dependencies = [ + "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]] @@ -3712,20 +3715,31 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.9.3", - "block2", + "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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -3736,31 +3750,76 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.9.3", - "block2", + "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.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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", "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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-contacts", "objc2-foundation 0.2.2", @@ -3778,8 +3837,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.9.3", - "block2", + "bitflags 2.9.4", + "block2 0.5.1", "dispatch", "libc", "objc2 0.5.2", @@ -3791,8 +3850,22 @@ 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", ] [[package]] @@ -3801,9 +3874,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", - "objc2-app-kit", + "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", ] @@ -3813,8 +3886,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.9.3", - "block2", + "bitflags 2.9.4", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -3825,13 +3898,24 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.9.3", - "block2", + "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" @@ -3848,16 +3932,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.9.3", - "block2", + "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", @@ -3869,7 +3953,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -3880,8 +3964,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.9.3", - "block2", + "bitflags 2.9.4", + "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", @@ -3917,7 +4001,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", @@ -3956,7 +4040,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", @@ -4309,6 +4393,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" @@ -4443,7 +4540,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", @@ -4456,6 +4553,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" @@ -4695,7 +4801,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]] @@ -4879,7 +4985,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", @@ -4892,7 +4998,7 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "errno", "libc", "linux-raw-sys 0.9.4", @@ -4969,7 +5075,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", @@ -5052,7 +5158,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", @@ -5286,7 +5392,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", @@ -5322,7 +5428,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", @@ -5380,7 +5486,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", @@ -5415,7 +5521,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]] @@ -5605,7 +5711,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", ] @@ -5736,23 +5842,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", @@ -5762,15 +5870,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", @@ -5787,7 +5895,7 @@ dependencies = [ "bytemuck", "cfg-if", "log", - "png", + "png 0.17.16", "tiny-skia-path", ] @@ -6032,7 +6140,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", @@ -6314,9 +6422,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", @@ -6342,12 +6450,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" @@ -6451,30 +6553,31 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.14.3+wasi-0.2.4" +version = "0.14.4+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" +checksum = "88a5f4a424faf49c3c2c344f166f0662341d470ea185e939657aaff130f0ec4a" 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", @@ -6486,9 +6589,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", @@ -6499,9 +6602,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", @@ -6509,9 +6612,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", @@ -6522,9 +6625,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", ] @@ -6544,9 +6647,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", @@ -6576,7 +6679,7 @@ version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "rustix 1.0.8", "wayland-backend", "wayland-scanner", @@ -6588,7 +6691,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", ] @@ -6610,7 +6713,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", @@ -6622,7 +6725,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", @@ -6635,7 +6738,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", @@ -6667,9 +6770,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", @@ -6742,7 +6845,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", @@ -6773,7 +6876,7 @@ dependencies = [ "arrayvec", "bit-set", "bit-vec", - "bitflags 2.9.3", + "bitflags 2.9.4", "cfg_aliases", "document-features", "hashbrown", @@ -6842,7 +6945,7 @@ dependencies = [ "arrayvec", "ash", "bit-set", - "bitflags 2.9.3", + "bitflags 2.9.4", "block", "bytemuck", "cfg-if", @@ -6886,7 +6989,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", @@ -6912,11 +7015,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]] @@ -6959,6 +7062,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" @@ -6992,11 +7117,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" @@ -7069,13 +7205,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", ] @@ -7104,7 +7256,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]] @@ -7123,7 +7275,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]] @@ -7171,6 +7323,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" @@ -7223,7 +7384,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", @@ -7234,6 +7395,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" @@ -7422,8 +7592,8 @@ dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.9.3", - "block2", + "bitflags 2.9.4", + "block2 0.5.1", "bytemuck", "calloop", "cfg_aliases", @@ -7437,7 +7607,7 @@ dependencies = [ "memmap2", "ndk", "objc2 0.5.2", - "objc2-app-kit", + "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", "objc2-ui-kit", "orbclient", @@ -7474,21 +7644,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" @@ -7540,7 +7700,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", @@ -7626,7 +7786,7 @@ dependencies = [ "async-trait", "blocking", "enumflags2", - "event-listener 5.4.1", + "event-listener", "futures-core", "futures-lite", "hex", @@ -7678,18 +7838,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", @@ -7773,9 +7933,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", ] @@ -7789,7 +7949,6 @@ dependencies = [ "endi", "enumflags2", "serde", - "url", "winnow", "zvariant_derive", "zvariant_utils", diff --git a/Cargo.toml b/Cargo.toml index a3ae52c6..c6088e4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -175,10 +175,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 } @@ -188,6 +187,7 @@ lilt = "0.8" log = "0.4" lyon = "1.0" lyon_path = "1.0" +mundy = "0.2" num-traits = "0.2" ouroboros = "0.18" png = "0.17" @@ -196,8 +196,8 @@ qrcode = { version = "0.13", default-features = false } raw-window-handle = "0.6" resvg = "0.42" 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 cb7ef3ef..a44ca660 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -14,7 +14,7 @@ keywords.workspace = true workspace = true [features] -auto-detect-theme = ["dep:dark-light"] +auto-detect-theme = ["dep:mundy"] advanced = [] crisp = [] basic-shaping = [] @@ -32,8 +32,8 @@ smol_str.workspace = true thiserror.workspace = true web-time.workspace = true -dark-light.workspace = true -dark-light.optional = true +mundy.workspace = true +mundy.optional = true serde.workspace = true serde.optional = true diff --git a/core/src/theme.rs b/core/src/theme.rs index 0adf9ab2..c47f4e2f 100644 --- a/core/src/theme.rs +++ b/core/src/theme.rs @@ -170,16 +170,21 @@ impl Default for Theme { fn default() -> Self { #[cfg(feature = "auto-detect-theme")] { + use crate::time::Duration; 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 - } + let color_scheme = mundy::Preferences::once_blocking( + mundy::Interest::ColorScheme, + Duration::from_millis(100), + ) + .map(|preferences| preferences.color_scheme) + .unwrap_or_default(); + + match color_scheme { + mundy::ColorScheme::Dark => Theme::Dark, + mundy::ColorScheme::Light + | mundy::ColorScheme::NoPreference => Theme::Light, } }); From 5c7ae8a3d64e297993cd9ed8d1e9c988004bd51d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 8 Sep 2025 01:27:01 +0200 Subject: [PATCH 20/36] Enable needed `mundy` features explicitly --- Cargo.lock | 7 ------- Cargo.toml | 4 +++- core/Cargo.toml | 1 + 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac9425e7..b54ce648 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -443,12 +443,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "beul" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3c3baedf037b161e69da9200947329f52afa3b6e1cdbe6f344d6aa350fedd2f" - [[package]] name = "bezier_tool" version = "0.1.0" @@ -3390,7 +3384,6 @@ checksum = "f5f507e52285e981a349f7224e0ac7eaf014e1a9cce399471547c8dfbc018b79" dependencies = [ "android-build", "async-io", - "beul", "cfg-if", "dispatch", "futures-channel", diff --git a/Cargo.toml b/Cargo.toml index c6088e4f..c587c7d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -187,7 +187,6 @@ lilt = "0.8" log = "0.4" lyon = "1.0" lyon_path = "1.0" -mundy = "0.2" num-traits = "0.2" ouroboros = "0.18" png = "0.17" @@ -219,6 +218,9 @@ wgpu = "26.0" window_clipboard = "0.4.1" winit = { git = "https://github.com/iced-rs/winit.git", rev = "11414b6aa45699f038114e61b4ddf5102b2d3b4b" } +mundy.version = "0.2" +mundy.default-features = false + [workspace.lints.rust] rust_2018_idioms = { level = "deny", priority = -1 } missing_debug_implementations = "deny" diff --git a/core/Cargo.toml b/core/Cargo.toml index a44ca660..2b875545 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -34,6 +34,7 @@ web-time.workspace = true mundy.workspace = true mundy.optional = true +mundy.features = ["async-io", "color-scheme"] serde.workspace = true serde.optional = true From 0111f514a11fdda7db69ca0c99ad751194665d17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 8 Sep 2025 05:16:20 +0200 Subject: [PATCH 21/36] Use `winit` to obtain current `theme::Mode` --- Cargo.lock | 477 ++-------------------------- Cargo.toml | 7 +- core/Cargo.toml | 5 - core/src/theme.rs | 52 ++- devtools/src/lib.rs | 22 +- examples/arc/src/main.rs | 2 +- examples/bezier_tool/src/main.rs | 2 +- examples/ferris/src/main.rs | 2 +- examples/game_of_life/src/main.rs | 2 +- examples/layout/src/main.rs | 11 +- examples/multi_window/src/main.rs | 8 +- examples/qr_code/src/main.rs | 9 +- examples/styling/src/main.rs | 43 ++- examples/table/src/main.rs | 2 +- examples/tour/src/main.rs | 24 +- examples/vectorial_text/src/main.rs | 2 +- examples/visible_bounds/src/main.rs | 2 +- program/src/lib.rs | 28 +- src/application.rs | 34 +- src/application/timed.rs | 4 +- src/daemon.rs | 34 +- src/lib.rs | 2 +- widget/src/helpers.rs | 4 +- winit/src/lib.rs | 17 +- winit/src/window/state.rs | 15 +- 25 files changed, 208 insertions(+), 602 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b54ce648..a3be4ad7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -91,15 +91,6 @@ 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" @@ -199,18 +190,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "async-broadcast" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" -dependencies = [ - "event-listener", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - [[package]] name = "async-channel" version = "2.5.0" @@ -306,17 +285,6 @@ dependencies = [ "rustix 1.0.8", ] -[[package]] -name = "async-recursion" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "async-signal" version = "0.2.12" @@ -341,17 +309,6 @@ version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "async-tungstenite" version = "0.25.1" @@ -545,15 +502,6 @@ dependencies = [ "objc2 0.5.2", ] -[[package]] -name = "block2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" -dependencies = [ - "objc2 0.6.2", -] - [[package]] name = "blocking" version = "1.6.2" @@ -831,7 +779,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b7f4aaa047ba3c3630b080bb9860894732ff23e2aee290a418909aa6d5df38f" dependencies = [ "objc2 0.5.2", - "objc2-app-kit 0.2.2", + "objc2-app-kit", "objc2-foundation 0.2.2", ] @@ -1241,16 +1189,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" -[[package]] -name = "dispatch2" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" -dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.2", -] - [[package]] name = "displaydoc" version = "0.2.5" @@ -1362,33 +1300,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "endi" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" - -[[package]] -name = "enumflags2" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" -dependencies = [ - "enumflags2_derive", - "serde", -] - -[[package]] -name = "enumflags2_derive" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "equator" version = "0.4.2" @@ -2182,12 +2093,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - [[package]] name = "hexf-parse" version = "0.2.1" @@ -2432,7 +2337,6 @@ dependencies = [ "glam", "lilt", "log", - "mundy", "num-traits", "rustc-hash 2.1.1", "serde", @@ -3258,15 +3162,6 @@ dependencies = [ "libc", ] -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - [[package]] name = "metal" version = "0.32.0" @@ -3376,31 +3271,6 @@ 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" @@ -3486,19 +3356,6 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" -[[package]] -name = "nix" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" -dependencies = [ - "bitflags 2.9.4", - "cfg-if", - "cfg_aliases", - "libc", - "memoffset", -] - [[package]] name = "nom" version = "7.1.3" @@ -3674,32 +3531,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ "bitflags 2.9.4", - "block2 0.5.1", + "block2", "libc", "objc2 0.5.2", - "objc2-core-data 0.2.2", - "objc2-core-image 0.2.2", + "objc2-core-data", + "objc2-core-image", "objc2-foundation 0.2.2", - "objc2-quartz-core 0.2.2", -] - -[[package]] -name = "objc2-app-kit" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" -dependencies = [ - "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", + "objc2-quartz-core", ] [[package]] @@ -3709,30 +3547,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ "bitflags 2.9.4", - "block2 0.5.1", + "block2", "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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -3744,75 +3571,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ "bitflags 2.9.4", - "block2 0.5.1", + "block2", "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.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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", "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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-contacts", "objc2-foundation 0.2.2", @@ -3831,7 +3613,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.9.4", - "block2 0.5.1", + "block2", "dispatch", "libc", "objc2 0.5.2", @@ -3844,21 +3626,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" dependencies = [ "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", ] [[package]] @@ -3867,9 +3635,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ - "block2 0.5.1", + "block2", "objc2 0.5.2", - "objc2-app-kit 0.2.2", + "objc2-app-kit", "objc2-foundation 0.2.2", ] @@ -3880,7 +3648,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ "bitflags 2.9.4", - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -3892,23 +3660,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ "bitflags 2.9.4", - "block2 0.5.1", + "block2", "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" @@ -3926,15 +3683,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ "bitflags 2.9.4", - "block2 0.5.1", + "block2", "objc2 0.5.2", - "objc2-cloud-kit 0.2.2", - "objc2-core-data 0.2.2", - "objc2-core-image 0.2.2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", "objc2-core-location", "objc2-foundation 0.2.2", "objc2-link-presentation", - "objc2-quartz-core 0.2.2", + "objc2-quartz-core", "objc2-symbols", "objc2-uniform-type-identifiers", "objc2-user-notifications", @@ -3946,7 +3703,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -3958,7 +3715,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ "bitflags 2.9.4", - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", @@ -4095,16 +3852,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "ordered-stream" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" -dependencies = [ - "futures-core", - "pin-project-lite", -] - [[package]] name = "ouroboros" version = "0.18.5" @@ -5215,17 +4962,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_repr" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "serde_spanned" version = "0.6.9" @@ -5479,7 +5215,7 @@ dependencies = [ "memmap2", "objc2 0.5.2", "objc2-foundation 0.2.2", - "objc2-quartz-core 0.2.2", + "objc2-quartz-core", "raw-window-handle 0.6.2", "redox_syscall 0.5.17", "rustix 0.38.44", @@ -6266,17 +6002,6 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" -[[package]] -name = "uds_windows" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" -dependencies = [ - "memoffset", - "tempfile", - "winapi", -] - [[package]] name = "unicase" version = "2.8.1" @@ -7055,28 +6780,6 @@ 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" @@ -7115,17 +6818,6 @@ dependencies = [ "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" @@ -7204,16 +6896,6 @@ 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" @@ -7388,15 +7070,6 @@ 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" @@ -7586,7 +7259,7 @@ dependencies = [ "android-activity", "atomic-waker", "bitflags 2.9.4", - "block2 0.5.1", + "block2", "bytemuck", "calloop", "cfg_aliases", @@ -7600,7 +7273,7 @@ dependencies = [ "memmap2", "ndk", "objc2 0.5.2", - "objc2-app-kit 0.2.2", + "objc2-app-kit", "objc2-foundation 0.2.2", "objc2-ui-kit", "orbclient", @@ -7763,66 +7436,6 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zbus" -version = "5.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a073be99ace1adc48af593701c8015cd9817df372e14a1a6b0ee8f8bf043be" -dependencies = [ - "async-broadcast", - "async-executor", - "async-io", - "async-lock", - "async-process", - "async-recursion", - "async-task", - "async-trait", - "blocking", - "enumflags2", - "event-listener", - "futures-core", - "futures-lite", - "hex", - "nix", - "ordered-stream", - "serde", - "serde_repr", - "tracing", - "uds_windows", - "windows-sys 0.60.2", - "winnow", - "zbus_macros", - "zbus_names", - "zvariant", -] - -[[package]] -name = "zbus_macros" -version = "5.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e80cd713a45a49859dcb648053f63265f4f2851b6420d47a958e5697c68b131" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", - "zbus_names", - "zvariant", - "zvariant_utils", -] - -[[package]] -name = "zbus_names" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" -dependencies = [ - "serde", - "static_assertions", - "winnow", - "zvariant", -] - [[package]] name = "zeno" version = "0.3.3" @@ -7932,43 +7545,3 @@ checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" dependencies = [ "zune-core", ] - -[[package]] -name = "zvariant" -version = "5.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "999dd3be73c52b1fccd109a4a81e4fcd20fab1d3599c8121b38d04e1419498db" -dependencies = [ - "endi", - "enumflags2", - "serde", - "winnow", - "zvariant_derive", - "zvariant_utils", -] - -[[package]] -name = "zvariant_derive" -version = "5.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6643fd0b26a46d226bd90d3f07c1b5321fe9bb7f04673cb37ac6d6883885b68e" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", - "zvariant_utils", -] - -[[package]] -name = "zvariant_utils" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6949d142f89f6916deca2232cf26a8afacf2b9fdc35ce766105e104478be599" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "syn", - "winnow", -] diff --git a/Cargo.toml b/Cargo.toml index c587c7d9..f68a5c96 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"] # Enables the `wgpu` GPU-accelerated renderer backend wgpu = ["iced_renderer/wgpu", "iced_widget/wgpu"] # Enables the `tiny-skia` software renderer backend @@ -67,8 +67,6 @@ highlighter = ["iced_highlighter", "iced_widget/highlighter"] 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 @@ -218,9 +216,6 @@ wgpu = "26.0" window_clipboard = "0.4.1" winit = { git = "https://github.com/iced-rs/winit.git", rev = "11414b6aa45699f038114e61b4ddf5102b2d3b4b" } -mundy.version = "0.2" -mundy.default-features = false - [workspace.lints.rust] rust_2018_idioms = { level = "deny", priority = -1 } missing_debug_implementations = "deny" diff --git a/core/Cargo.toml b/core/Cargo.toml index 2b875545..31296446 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -14,7 +14,6 @@ keywords.workspace = true workspace = true [features] -auto-detect-theme = ["dep:mundy"] advanced = [] crisp = [] basic-shaping = [] @@ -32,10 +31,6 @@ smol_str.workspace = true thiserror.workspace = true web-time.workspace = true -mundy.workspace = true -mundy.optional = true -mundy.features = ["async-io", "color-scheme"] - serde.workspace = true serde.optional = true serde.features = ["derive"] diff --git a/core/src/theme.rs b/core/src/theme.rs index c47f4e2f..3c0f51f9 100644 --- a/core/src/theme.rs +++ b/core/src/theme.rs @@ -166,36 +166,6 @@ impl Theme { } } -impl Default for Theme { - fn default() -> Self { - #[cfg(feature = "auto-detect-theme")] - { - use crate::time::Duration; - use std::sync::LazyLock; - - static DEFAULT: LazyLock = LazyLock::new(|| { - let color_scheme = mundy::Preferences::once_blocking( - mundy::Interest::ColorScheme, - Duration::from_millis(100), - ) - .map(|preferences| preferences.color_scheme) - .unwrap_or_default(); - - match color_scheme { - mundy::ColorScheme::Dark => Theme::Dark, - mundy::ColorScheme::Light - | mundy::ColorScheme::NoPreference => 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 { @@ -261,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 { @@ -273,6 +255,9 @@ pub struct Style { /// The default blank style of a theme. pub trait Base { + /// Returns the default theme for the preferred [`Mode`]. + fn default(preference: Mode) -> Self; + /// Returns the default base [`Style`] of a theme. fn base(&self) -> Style; @@ -285,6 +270,13 @@ pub trait Base { } impl Base for Theme { + fn default(preference: Mode) -> Self { + match preference { + Mode::None | Mode::Light => Self::Light, + Mode::Dark => Self::Dark, + } + } + fn base(&self) -> Style { default(self) } diff --git a/devtools/src/lib.rs b/devtools/src/lib.rs index 09a2a67b..d8ef0729 100644 --- a/devtools/src/lib.rs +++ b/devtools/src/lib.rs @@ -12,7 +12,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::{Alignment::Center, Color, Element, Length::Fill}; @@ -90,7 +90,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) } @@ -307,14 +311,12 @@ where } }; - let theme = program.theme(state, window); - - let derive_theme = move || { + fn derive_theme(theme: &T) -> Theme { theme .palette() .map(|palette| Theme::custom("iced devtools", palette)) - .unwrap_or_default() - }; + .unwrap_or(Theme::Dark) + } let mode = match &self.mode { Mode::None => None, @@ -340,7 +342,7 @@ where } } .map(|mode| { - themer(derive_theme(), Element::from(mode).map(Event::Message)) + themer(derive_theme, Element::from(mode).map(Event::Message)) }); let notification = self @@ -359,7 +361,7 @@ where .push_maybe(mode.map(opaque)) .push_maybe(notification.map(|notification| { themer( - derive_theme(), + derive_theme, bottom_right(opaque( container(notification) .padding(10) @@ -389,7 +391,7 @@ where Subscription::batch([subscription, hotkeys, commands]) } - fn theme(&self, program: &P, window: window::Id) -> P::Theme { + fn theme(&self, program: &P, window: window::Id) -> Option { program.theme(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/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/layout/src/main.rs b/examples/layout/src/main.rs index f3765d9d..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); @@ -116,7 +117,7 @@ impl Layout { .into() } - fn theme(&self) -> Theme { + fn theme(&self) -> Option { self.theme.clone() } } diff --git a/examples/multi_window/src/main.rs b/examples/multi_window/src/main.rs index cb5e6f43..d299648e 100644 --- a/examples/multi_window/src/main.rs +++ b/examples/multi_window/src/main.rs @@ -138,12 +138,8 @@ 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) -> f32 { 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/styling/src/main.rs b/examples/styling/src/main.rs index f28216f9..0117d2dc 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, @@ -38,7 +38,7 @@ 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,20 +46,20 @@ 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 { - Theme::ALL - .last() - .expect("Theme::ALL must not be empty") - .clone() - } else { - Theme::ALL[current - 1].clone() - }; + if let Some(current) = Theme::ALL.iter().position(|candidate| { + self.theme.as_ref() == Some(candidate) + }) { + self.theme = + Some(if matches!(message, Message::NextTheme) { + Theme::ALL[(current + 1) % Theme::ALL.len()].clone() + } else if current == 0 { + Theme::ALL + .last() + .expect("Theme::ALL must not be empty") + .clone() + } else { + Theme::ALL[current - 1].clone() + }); } } } @@ -68,8 +68,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); @@ -190,7 +191,7 @@ impl Styling { }) } - fn theme(&self) -> Theme { + fn theme(&self) -> Option { self.theme.clone() } } @@ -210,9 +211,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/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/tour/src/main.rs b/examples/tour/src/main.rs index 5d009da6..56e14f06 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!(0x0000FF)) + .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 8e5e4a07..fa00a8fc 100644 --- a/examples/visible_bounds/src/main.rs +++ b/examples/visible_bounds/src/main.rs @@ -12,7 +12,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/program/src/lib.rs b/program/src/lib.rs index d479d2f8..3b516770 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -25,7 +25,7 @@ pub trait Program: Sized { type Message: Message + 'static; /// The theme of the program. - type Theme: Default + theme::Base; + type Theme: theme::Base; /// The renderer of the program. type Renderer: Renderer; @@ -86,8 +86,12 @@ 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 { @@ -152,7 +156,7 @@ pub fn with_title( &self, state: &Self::State, window: window::Id, - ) -> Self::Theme { + ) -> Option { self.program.theme(state, window) } @@ -238,7 +242,7 @@ pub fn with_subscription( &self, state: &Self::State, window: window::Id, - ) -> Self::Theme { + ) -> Option { self.program.theme(state, window) } @@ -264,7 +268,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, @@ -273,7 +277,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; @@ -285,7 +289,7 @@ pub fn with_theme( &self, state: &Self::State, window: window::Id, - ) -> Self::Theme { + ) -> Option { (self.theme)(state, window) } @@ -407,7 +411,7 @@ pub fn with_style( &self, state: &Self::State, window: window::Id, - ) -> Self::Theme { + ) -> Option { self.program.theme(state, window) } @@ -478,7 +482,7 @@ pub fn with_scale_factor( &self, state: &Self::State, window: window::Id, - ) -> Self::Theme { + ) -> Option { self.program.theme(state, window) } @@ -561,7 +565,7 @@ pub fn with_executor( &self, state: &Self::State, window: window::Id, - ) -> Self::Theme { + ) -> Option { self.program.theme(state, window) } @@ -628,7 +632,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) } diff --git a/src/application.rs b/src/application.rs index d8f7862b..ef547cea 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() //! } @@ -35,7 +35,7 @@ use crate::shell; use crate::theme; use crate::window; use crate::{ - Element, Executor, Font, Result, Settings, Size, Subscription, Task, + Element, Executor, Font, Result, Settings, Size, Subscription, Task, Theme, }; use iced_debug as debug; @@ -82,7 +82,7 @@ pub fn application( where State: 'static, Message: program::Message + 'static, - Theme: Default + theme::Base, + Theme: theme::Base, Renderer: program::Renderer, { use std::marker::PhantomData; @@ -101,7 +101,7 @@ where for Instance where Message: program::Message + 'static, - Theme: Default + theme::Base, + Theme: theme::Base, Renderer: program::Renderer, Boot: self::Boot, Update: self::Update, @@ -355,13 +355,13 @@ 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| { - debug::hot(|| f(state)) + debug::hot(|| f.theme(state)) }), settings: self.settings, window: self.window, @@ -529,3 +529,25 @@ where self(state).into() } } + +/// TODO +pub trait ThemeFn { + /// TODO + 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 3d158874..2aaf40f3 100644 --- a/src/application/timed.rs +++ b/src/application/timed.rs @@ -30,7 +30,7 @@ pub fn timed( where State: 'static, Message: program::Message + 'static, - Theme: Default + theme::Base + 'static, + Theme: theme::Base + 'static, Renderer: program::Renderer + 'static, { use std::marker::PhantomData; @@ -69,7 +69,7 @@ where > where Message: program::Message + 'static, - Theme: Default + theme::Base + 'static, + Theme: theme::Base + 'static, Renderer: program::Renderer + 'static, Boot: self::Boot, Update: self::Update, diff --git a/src/daemon.rs b/src/daemon.rs index 05d276c3..2a4451e0 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -4,7 +4,9 @@ use crate::program::{self, Program}; use crate::shell; use crate::theme; use crate::window; -use crate::{Element, Executor, Font, Result, Settings, Subscription, Task}; +use crate::{ + Element, Executor, Font, Result, Settings, Subscription, Task, Theme, +}; use iced_debug as debug; @@ -28,7 +30,7 @@ pub fn daemon( where State: 'static, Message: program::Message + 'static, - Theme: Default + theme::Base, + Theme: theme::Base, Renderer: program::Renderer, { use std::marker::PhantomData; @@ -47,7 +49,7 @@ where for Instance where Message: program::Message + 'static, - Theme: Default + theme::Base, + Theme: theme::Base, Renderer: program::Renderer, Boot: application::Boot, Update: application::Update, @@ -202,13 +204,13 @@ 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, move |state, window| { - debug::hot(|| f(state, window)) + debug::hot(|| f.theme(state, window)) }), settings: self.settings, } @@ -314,3 +316,25 @@ where self(state, window).into() } } + +/// TODO +pub trait ThemeFn { + /// TODO + 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 a933d21f..b0b2bb8d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -698,7 +698,7 @@ pub fn run( where State: Default + 'static, Message: program::Message + 'static, - Theme: Default + theme::Base + 'static, + Theme: theme::Base + 'static, Renderer: program::Renderer + 'static, { application(State::default, update, view).run() diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 3b1d0bbe..9e510685 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -2063,7 +2063,7 @@ where /// A widget that applies any `Theme` to its contents. pub fn themer<'a, Message, OldTheme, NewTheme, Renderer>( - new_theme: NewTheme, + to_theme: impl Fn(&OldTheme) -> NewTheme, content: impl Into>, ) -> Themer< 'a, @@ -2077,7 +2077,7 @@ where Renderer: core::Renderer, NewTheme: Clone, { - Themer::new(move |_| new_theme.clone(), content) + Themer::new(to_theme, content) } /// Creates a [`PaneGrid`] with the given [`pane_grid::State`] and view function. diff --git a/winit/src/lib.rs b/winit/src/lib.rs index 38d6f3c3..6f9cea56 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -595,14 +595,7 @@ async fn run_instance

( } } - debug::theme_changed(|| { - if window_manager.is_empty() { - theme::Base::palette(&program.theme(id)) - } else { - None - } - }); - + let is_first = window_manager.is_empty(); let window = window_manager.insert( id, window, @@ -613,6 +606,14 @@ async fn run_instance

( exit_on_close_request, ); + 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( diff --git a/winit/src/window/state.rs b/winit/src/window/state.rs index c345a846..6adc2205 100644 --- a/winit/src/window/state.rs +++ b/winit/src/window/state.rs @@ -21,6 +21,7 @@ where cursor_position: Option>, modifiers: winit::keyboard::ModifiersState, theme: P::Theme, + theme_mode: theme::Mode, style: theme::Style, } @@ -52,7 +53,14 @@ where ) -> Self { let title = program.title(window_id); let scale_factor = program.scale_factor(window_id); - let theme = program.theme(window_id); + let theme_mode = match window.theme() { + None => theme::Mode::None, + Some(winit::window::Theme::Light) => theme::Mode::Light, + Some(winit::window::Theme::Dark) => theme::Mode::Dark, + }; + let theme = program + .theme(window_id) + .unwrap_or_else(|| ::default(theme_mode)); let style = program.style(&theme); let viewport = { @@ -72,6 +80,7 @@ where cursor_position: None, modifiers: winit::keyboard::ModifiersState::default(), theme, + theme_mode, style, } } @@ -216,7 +225,9 @@ where } // Update theme and appearance - self.theme = program.theme(window_id); + self.theme = program.theme(window_id).unwrap_or_else(|| { + ::default(self.theme_mode) + }); self.style = program.style(&self.theme); } } From 4d32e733b705a144cc15bec538d6fc809b4c656f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 8 Sep 2025 05:23:50 +0200 Subject: [PATCH 22/36] Fix `styling` example when system theme is selected --- examples/styling/src/main.rs | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index 0117d2dc..1465d501 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -46,21 +46,26 @@ 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| { + let current = Theme::ALL.iter().position(|candidate| { self.theme.as_ref() == Some(candidate) - }) { - self.theme = - Some(if matches!(message, Message::NextTheme) { - Theme::ALL[(current + 1) % Theme::ALL.len()].clone() - } else if current == 0 { - Theme::ALL - .last() - .expect("Theme::ALL must not be empty") - .clone() - } else { - Theme::ALL[current - 1].clone() - }); - } + }); + + 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() + } + }); } } } From cb9b106ff89cd05cde757ad76369b61d90d46c1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 8 Sep 2025 05:32:23 +0200 Subject: [PATCH 23/36] Allow system theme override with `ICED_THEME` env variable --- core/src/theme.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/core/src/theme.rs b/core/src/theme.rs index 3c0f51f9..5f2fb127 100644 --- a/core/src/theme.rs +++ b/core/src/theme.rs @@ -271,6 +271,24 @@ 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, From 354159bdf4930b25d452c182840648dedcd1192f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 8 Sep 2025 05:40:31 +0200 Subject: [PATCH 24/36] Update `png` to `0.18` --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- test/src/lib.rs | 7 +++++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a3be4ad7..d8d8bcba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2446,7 +2446,7 @@ version = "0.14.0-dev" dependencies = [ "iced_renderer", "iced_runtime", - "png 0.17.16", + "png 0.18.0", "sha2", "thiserror 1.0.69", ] @@ -4907,9 +4907,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", diff --git a/Cargo.toml b/Cargo.toml index f68a5c96..8e952b41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -187,7 +187,7 @@ lyon = "1.0" lyon_path = "1.0" 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" diff --git a/test/src/lib.rs b/test/src/lib.rs index 30b08176..72f9441b 100644 --- a/test/src/lib.rs +++ b/test/src/lib.rs @@ -494,10 +494,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()]) From c1d7819c07e6dd40d405b33484e4c127c1e4db2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 8 Sep 2025 06:01:36 +0200 Subject: [PATCH 25/36] Track window theme in `window::State` --- core/src/window/event.rs | 4 ++++ winit/src/conversion.rs | 14 +++++++++++++ winit/src/window/state.rs | 41 ++++++++++++++++++++++----------------- 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/core/src/window/event.rs b/core/src/window/event.rs index 45d29179..48622b70 100644 --- a/core/src/window/event.rs +++ b/core/src/window/event.rs @@ -1,3 +1,4 @@ +use crate::theme; use crate::time::Instant; use crate::{Point, Size}; @@ -71,4 +72,7 @@ pub enum Event { /// /// - **Wayland:** Not implemented. FilesHoveredLeft, + + /// The theme mode of the window has changed. + ThemeModeChanged(theme::Mode), } diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index 6f22405d..8668d6a3 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}; @@ -318,6 +319,9 @@ pub fn window_event( Some(Event::Window(window::Event::Moved(Point::new(x, y)))) } + WindowEvent::ThemeChanged(theme) => Some(Event::Window( + window::Event::ThemeModeChanged(theme_mode(theme)), + )), _ => None, } } @@ -440,6 +444,16 @@ pub fn mode(mode: Option) -> window::Mode { } } +/// Converts a [`winit`] window theme to 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 [`mouse::Interaction`] to a [`winit`] cursor icon. /// /// [`winit`]: https://github.com/rust-windowing/winit diff --git a/winit/src/window/state.rs b/winit/src/window/state.rs index 6adc2205..65f08d21 100644 --- a/winit/src/window/state.rs +++ b/winit/src/window/state.rs @@ -20,8 +20,8 @@ where viewport_version: u64, cursor_position: Option>, modifiers: winit::keyboard::ModifiersState, - theme: P::Theme, - theme_mode: theme::Mode, + theme: Option, + system_theme: P::Theme, style: theme::Style, } @@ -30,7 +30,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) @@ -53,15 +53,13 @@ where ) -> Self { let title = program.title(window_id); let scale_factor = program.scale_factor(window_id); - let theme_mode = match window.theme() { - None => theme::Mode::None, - Some(winit::window::Theme::Light) => theme::Mode::Light, - Some(winit::window::Theme::Dark) => theme::Mode::Dark, - }; - let theme = program - .theme(window_id) - .unwrap_or_else(|| ::default(theme_mode)); - let style = program.style(&theme); + let theme_mode = window + .theme() + .map(conversion::theme_mode) + .unwrap_or_default(); + let theme = program.theme(window_id); + let system_theme = ::default(theme_mode); + let style = program.style(theme.as_ref().unwrap_or(&system_theme)); let viewport = { let physical_size = window.inner_size(); @@ -80,7 +78,7 @@ where cursor_position: None, modifiers: winit::keyboard::ModifiersState::default(), theme, - theme_mode, + system_theme, style, } } @@ -132,7 +130,7 @@ where /// Returns the current theme of the [`State`]. pub fn theme(&self) -> &P::Theme { - &self.theme + self.theme.as_ref().unwrap_or(&self.system_theme) } /// Returns the current background [`Color`] of the [`State`]. @@ -183,6 +181,15 @@ where WindowEvent::ModifiersChanged(new_modifiers) => { self.modifiers = new_modifiers.state(); } + WindowEvent::ThemeChanged(theme) => { + self.system_theme = ::default( + conversion::theme_mode(*theme), + ); + + if self.theme.is_none() { + window.request_redraw(); + } + } _ => {} } } @@ -225,9 +232,7 @@ where } // Update theme and appearance - self.theme = program.theme(window_id).unwrap_or_else(|| { - ::default(self.theme_mode) - }); - self.style = program.style(&self.theme); + self.theme = program.theme(window_id); + self.style = program.style(self.theme()); } } From ab7eb88951a7e184c444f735986ff1183e7d9d9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 8 Sep 2025 06:07:41 +0200 Subject: [PATCH 26/36] Use lighter color for link in `tour` example --- examples/tour/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 56e14f06..78c329b5 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -220,7 +220,7 @@ impl Tour { "Additionally, this tour can also run on WebAssembly ", "by leveraging ", span("trunk") - .color(color!(0x0000FF)) + .color(color!(0x7777FF)) .underline(true) .font(Font::MONOSPACE) .link(Message::OpenTrunk), From e92c87061d2c62529141106e346c31f2e4a2008e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 8 Sep 2025 09:49:11 +0200 Subject: [PATCH 27/36] Add `linux-theme-detection` feature through `mundy` --- Cargo.lock | 477 ++++++++++++++++++++++++++++++++++++-- Cargo.toml | 7 +- runtime/src/lib.rs | 8 + winit/Cargo.toml | 5 + winit/src/conversion.rs | 29 ++- winit/src/lib.rs | 57 ++++- winit/src/window.rs | 3 +- winit/src/window/state.rs | 30 ++- 8 files changed, 566 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d8d8bcba..fad67bc7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -91,6 +91,15 @@ 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" @@ -190,6 +199,18 @@ dependencies = [ "libloading", ] +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + [[package]] name = "async-channel" version = "2.5.0" @@ -285,6 +306,17 @@ dependencies = [ "rustix 1.0.8", ] +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-signal" version = "0.2.12" @@ -309,6 +341,17 @@ version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-tungstenite" version = "0.25.1" @@ -502,6 +545,15 @@ dependencies = [ "objc2 0.5.2", ] +[[package]] +name = "block2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" +dependencies = [ + "objc2 0.6.2", +] + [[package]] name = "blocking" version = "1.6.2" @@ -779,7 +831,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b7f4aaa047ba3c3630b080bb9860894732ff23e2aee290a418909aa6d5df38f" dependencies = [ "objc2 0.5.2", - "objc2-app-kit", + "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", ] @@ -1189,6 +1241,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags 2.9.4", + "objc2 0.6.2", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -1300,6 +1362,33 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "equator" version = "0.4.2" @@ -2093,6 +2182,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hexf-parse" version = "0.2.1" @@ -2512,6 +2607,7 @@ dependencies = [ "iced_debug", "iced_program", "log", + "mundy", "rustc-hash 2.1.1", "sysinfo", "thiserror 1.0.69", @@ -3162,6 +3258,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "metal" version = "0.32.0" @@ -3271,6 +3376,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" @@ -3356,6 +3486,19 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags 2.9.4", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + [[package]] name = "nom" version = "7.1.3" @@ -3531,13 +3674,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ "bitflags 2.9.4", - "block2", + "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]] +name = "objc2-app-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" +dependencies = [ + "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]] @@ -3547,19 +3709,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ "bitflags 2.9.4", - "block2", + "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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -3571,30 +3744,75 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ "bitflags 2.9.4", - "block2", + "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.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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", "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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-contacts", "objc2-foundation 0.2.2", @@ -3613,7 +3831,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.9.4", - "block2", + "block2 0.5.1", "dispatch", "libc", "objc2 0.5.2", @@ -3626,7 +3844,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" dependencies = [ "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", ] [[package]] @@ -3635,9 +3867,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", - "objc2-app-kit", + "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", ] @@ -3648,7 +3880,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ "bitflags 2.9.4", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -3660,12 +3892,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ "bitflags 2.9.4", - "block2", + "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" @@ -3683,15 +3926,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ "bitflags 2.9.4", - "block2", + "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", @@ -3703,7 +3946,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -3715,7 +3958,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ "bitflags 2.9.4", - "block2", + "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", @@ -3852,6 +4095,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "ouroboros" version = "0.18.5" @@ -4962,6 +5215,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "serde_spanned" version = "0.6.9" @@ -5215,7 +5479,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", @@ -6002,6 +6266,17 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi", +] + [[package]] name = "unicase" version = "2.8.1" @@ -6780,6 +7055,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" @@ -6818,6 +7115,17 @@ dependencies = [ "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" @@ -6896,6 +7204,16 @@ 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" @@ -7070,6 +7388,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" @@ -7259,7 +7586,7 @@ dependencies = [ "android-activity", "atomic-waker", "bitflags 2.9.4", - "block2", + "block2 0.5.1", "bytemuck", "calloop", "cfg_aliases", @@ -7273,7 +7600,7 @@ dependencies = [ "memmap2", "ndk", "objc2 0.5.2", - "objc2-app-kit", + "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", "objc2-ui-kit", "orbclient", @@ -7436,6 +7763,66 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zbus" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a073be99ace1adc48af593701c8015cd9817df372e14a1a6b0ee8f8bf043be" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "nix", + "ordered-stream", + "serde", + "serde_repr", + "tracing", + "uds_windows", + "windows-sys 0.60.2", + "winnow", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e80cd713a45a49859dcb648053f63265f4f2851b6420d47a958e5697c68b131" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" +dependencies = [ + "serde", + "static_assertions", + "winnow", + "zvariant", +] + [[package]] name = "zeno" version = "0.3.3" @@ -7545,3 +7932,43 @@ checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" dependencies = [ "zune-core", ] + +[[package]] +name = "zvariant" +version = "5.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "999dd3be73c52b1fccd109a4a81e4fcd20fab1d3599c8121b38d04e1419498db" +dependencies = [ + "endi", + "enumflags2", + "serde", + "winnow", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6643fd0b26a46d226bd90d3f07c1b5321fe9bb7f04673cb37ac6d6883885b68e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6949d142f89f6916deca2232cf26a8afacf2b9fdc35ce766105e104478be599" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn", + "winnow", +] diff --git a/Cargo.toml b/Cargo.toml index 8e952b41..157c59bc 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", "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 @@ -61,7 +61,7 @@ web-colors = ["iced_renderer/web-colors"] 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 advanced module advanced = ["iced_core/advanced", "iced_widget/advanced"] @@ -77,6 +77,8 @@ strict-assertions = ["iced_renderer/strict-assertions"] 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 @@ -185,6 +187,7 @@ lilt = "0.8" log = "0.4" lyon = "1.0" lyon_path = "1.0" +mundy = { version = "0.2", default-features = false } num-traits = "0.2" ouroboros = "0.18" png = "0.18" diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 457f723c..77e80b8d 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -25,6 +25,7 @@ pub use iced_futures as futures; pub use task::Task; pub use user_interface::UserInterface; +use crate::core::theme; use crate::core::widget; use crate::futures::futures::channel::oneshot; @@ -56,6 +57,9 @@ pub enum Action { /// Run a system action. System(system::Action), + /// Change the default system theme mode of all windows. + ChangeTheme(theme::Mode), + /// Recreate all user interfaces and redraw all windows. Reload, @@ -82,6 +86,7 @@ impl Action { Action::Clipboard(action) => Err(Action::Clipboard(action)), Action::Window(action) => Err(Action::Window(action)), Action::System(action) => Err(Action::System(action)), + Action::ChangeTheme(mode) => Err(Action::ChangeTheme(mode)), Action::Reload => Err(Action::Reload), Action::Exit => Err(Action::Exit), } @@ -106,6 +111,9 @@ where } Action::Window(_) => write!(f, "Action::Window"), Action::System(action) => write!(f, "Action::System({action:?})"), + Action::ChangeTheme(mode) => { + write!(f, "Action::ChangeTheme({mode:?})") + } Action::Reload => write!(f, "Action::Reload"), Action::Exit => write!(f, "Action::Exit"), } diff --git a/winit/Cargo.toml b/winit/Cargo.toml index f2157978..85390a0d 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -23,6 +23,7 @@ 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 @@ -42,3 +43,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 8668d6a3..feef78bd 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -326,7 +326,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 { @@ -339,7 +339,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( @@ -411,7 +411,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( @@ -426,7 +426,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, @@ -434,7 +434,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 { @@ -444,7 +444,7 @@ pub fn mode(mode: Option) -> window::Mode { } } -/// Converts a [`winit`] window theme to a [`theme::Mode`]. +/// 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 { @@ -454,7 +454,18 @@ pub fn theme_mode(theme: winit::window::Theme) -> theme::Mode { } } -/// Converts a [`mouse::Interaction`] to a [`winit`] cursor icon. +/// 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( @@ -525,7 +536,7 @@ 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: f32, @@ -1196,7 +1207,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. +/// Convertions 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 6f9cea56..a2d58820 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -111,7 +111,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 }; @@ -517,6 +517,33 @@ 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 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::ChangeTheme(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 system_theme = theme::Mode::None; + loop { // Empty the queue if possible let event = if let Ok(event) = event_receiver.try_next() { @@ -604,6 +631,7 @@ async fn run_instance

( .as_mut() .expect("Compositor must be initialized"), exit_on_close_request, + system_theme, ); debug::theme_changed(|| { @@ -891,7 +919,11 @@ async fn run_instance

( &mut is_window_opening, ); } 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, @@ -1465,6 +1497,27 @@ fn run_action<'a, P, C>( let _ = channel.send(Ok(())); } } + Action::ChangeTheme(mode) => { + let Some(theme) = conversion::window_theme(mode) else { + return; + }; + + for (id, window) in window_manager.iter_mut() { + window.raw.set_theme(Some(theme)); + window.state.update( + program, + &window.raw, + &winit::event::WindowEvent::ThemeChanged(theme), + ); + + events.push(( + id, + core::Event::Window(core::window::Event::ThemeModeChanged( + mode, + )), + )); + } + } Action::Reload => { for (id, window) in window_manager.iter_mut() { let Some(ui) = interfaces.remove(&id) else { 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 65f08d21..b3f9af3c 100644 --- a/winit/src/window/state.rs +++ b/winit/src/window/state.rs @@ -21,7 +21,7 @@ where cursor_position: Option>, modifiers: winit::keyboard::ModifiersState, theme: Option, - system_theme: P::Theme, + default_theme: P::Theme, style: theme::Style, } @@ -50,16 +50,18 @@ 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_mode = window .theme() .map(conversion::theme_mode) - .unwrap_or_default(); + .unwrap_or(system_theme); + + let title = program.title(window_id); + let scale_factor = program.scale_factor(window_id); let theme = program.theme(window_id); - let system_theme = ::default(theme_mode); - let style = program.style(theme.as_ref().unwrap_or(&system_theme)); + let default_theme = ::default(theme_mode); + let style = program.style(theme.as_ref().unwrap_or(&default_theme)); let viewport = { let physical_size = window.inner_size(); @@ -78,7 +80,7 @@ where cursor_position: None, modifiers: winit::keyboard::ModifiersState::default(), theme, - system_theme, + default_theme, style, } } @@ -130,7 +132,7 @@ where /// Returns the current theme of the [`State`]. pub fn theme(&self) -> &P::Theme { - self.theme.as_ref().unwrap_or(&self.system_theme) + self.theme.as_ref().unwrap_or(&self.default_theme) } /// Returns the current background [`Color`] of the [`State`]. @@ -144,7 +146,12 @@ 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); @@ -182,9 +189,10 @@ where self.modifiers = new_modifiers.state(); } WindowEvent::ThemeChanged(theme) => { - self.system_theme = ::default( + self.default_theme = ::default( conversion::theme_mode(*theme), ); + self.style = program.style(self.theme()); if self.theme.is_none() { window.request_redraw(); @@ -198,7 +206,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

, From 74425d5cb1ddc12637c4f11adbdc258d693b3dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 8 Sep 2025 10:12:04 +0200 Subject: [PATCH 28/36] Set window theme to match color scheme of active theme --- core/src/theme.rs | 13 ++++++++++++- winit/src/conversion.rs | 2 +- winit/src/lib.rs | 4 ++++ winit/src/window/state.rs | 27 +++++++++++++++++++++++---- 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/core/src/theme.rs b/core/src/theme.rs index 5f2fb127..1260bca8 100644 --- a/core/src/theme.rs +++ b/core/src/theme.rs @@ -258,7 +258,10 @@ pub trait Base { /// Returns the default theme for the preferred [`Mode`]. fn default(preference: Mode) -> Self; - /// Returns the default base [`Style`] of a theme. + /// 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. @@ -295,6 +298,14 @@ impl Base for Theme { } } + fn mode(&self) -> Mode { + if self.extended_palette().is_dark { + Mode::Dark + } else { + Mode::Light + } + } + fn base(&self) -> Style { default(self) } diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index feef78bd..654cfb54 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -1207,7 +1207,7 @@ pub fn icon(icon: window::Icon) -> Option { winit::window::Icon::from_rgba(pixels, size.width, size.height).ok() } -/// Convertions some [`input_method::Purpose`] into 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 a2d58820..970366c7 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -634,6 +634,10 @@ async fn run_instance

( 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()) diff --git a/winit/src/window/state.rs b/winit/src/window/state.rs index b3f9af3c..683b5352 100644 --- a/winit/src/window/state.rs +++ b/winit/src/window/state.rs @@ -9,7 +9,7 @@ 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, @@ -21,6 +21,7 @@ where cursor_position: Option>, modifiers: winit::keyboard::ModifiersState, theme: Option, + theme_mode: theme::Mode, default_theme: P::Theme, style: theme::Style, } @@ -52,7 +53,7 @@ where window: &Window, system_theme: theme::Mode, ) -> Self { - let theme_mode = window + let system_theme = window .theme() .map(conversion::theme_mode) .unwrap_or(system_theme); @@ -60,7 +61,9 @@ where let title = program.title(window_id); let scale_factor = program.scale_factor(window_id); let theme = program.theme(window_id); - let default_theme = ::default(theme_mode); + 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 = { @@ -80,6 +83,7 @@ where cursor_position: None, modifiers: winit::keyboard::ModifiersState::default(), theme, + theme_mode, default_theme, style, } @@ -135,6 +139,11 @@ where 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`]. pub fn background_color(&self) -> Color { self.style.background_color @@ -192,9 +201,9 @@ where self.default_theme = ::default( conversion::theme_mode(*theme), ); - self.style = program.style(self.theme()); if self.theme.is_none() { + self.style = program.style(&self.default_theme); window.request_redraw(); } } @@ -242,5 +251,15 @@ where // Update theme and appearance self.theme = program.theme(window_id); self.style = program.style(self.theme()); + + if let Some(theme) = &self.theme { + let new_mode = theme::Base::mode(theme); + + if self.theme_mode != new_mode { + window.set_theme(conversion::window_theme(new_mode)); + + self.theme_mode = new_mode; + } + } } } From 7fedb0cc9ba9f1262d14402e043617cd5c6899e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 8 Sep 2025 11:03:45 +0200 Subject: [PATCH 29/36] Fix edge cases when restoring system theme --- examples/styling/src/main.rs | 7 +++++++ winit/src/window/state.rs | 37 ++++++++++++++++++++++++++++++++---- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index 1465d501..e5805532 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -32,6 +32,7 @@ enum Message { TogglerToggled(bool), PreviousTheme, NextTheme, + ClearTheme, } impl Styling { @@ -67,6 +68,9 @@ impl Styling { } }); } + Message::ClearTheme => { + self.theme = None; + } } } @@ -192,6 +196,9 @@ impl Styling { keyboard::key::Named::ArrowDown | keyboard::key::Named::ArrowRight, ) => Some(Message::NextTheme), + keyboard::Key::Named(keyboard::key::Named::Space) => { + Some(Message::ClearTheme) + } _ => None, }) } diff --git a/winit/src/window/state.rs b/winit/src/window/state.rs index 683b5352..4bc28cd4 100644 --- a/winit/src/window/state.rs +++ b/winit/src/window/state.rs @@ -252,14 +252,43 @@ where self.theme = program.theme(window_id); self.style = program.style(self.theme()); - if let Some(theme) = &self.theme { - let new_mode = theme::Base::mode(theme); + let new_mode = self + .theme + .as_ref() + .map(theme::Base::mode) + .unwrap_or_default(); - if self.theme_mode != new_mode { + if self.theme_mode != new_mode { + #[cfg(not(target_os = "linux"))] + { window.set_theme(conversion::window_theme(new_mode)); - self.theme_mode = 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; } } } From b5974a23ccff58f13c3bf9db54fd8b60cb9c9f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 8 Sep 2025 11:22:13 +0200 Subject: [PATCH 30/36] Remove hardcoded colors from `todos` example --- .../snapshots/creates_a_new_task-tiny-skia.sha256 | 2 +- examples/todos/src/main.rs | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) 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 ed767e70..9c57487c 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -5,7 +5,7 @@ use iced::widget::{ }; use iced::window; use iced::{ - Center, Element, Fill, Font, Function, Subscription, Task as Command, + Center, Element, Fill, Font, Function, Subscription, Task as Command, Theme, }; use serde::{Deserialize, Serialize}; @@ -194,7 +194,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) @@ -447,7 +447,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() @@ -471,6 +471,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 { From 323b17d45849a5096b92437a53e92509436b7494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 8 Sep 2025 12:05:39 +0200 Subject: [PATCH 31/36] Write documentation for new `ThemeFn` traits --- src/application.rs | 14 ++++++++++++-- src/daemon.rs | 14 ++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/application.rs b/src/application.rs index ef547cea..dfcc4c00 100644 --- a/src/application.rs +++ b/src/application.rs @@ -530,9 +530,19 @@ where } } -/// TODO +/// 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 { - /// TODO + /// 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; } diff --git a/src/daemon.rs b/src/daemon.rs index 2a4451e0..1e4143db 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -317,9 +317,19 @@ where } } -/// TODO +/// 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 { - /// TODO + /// 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; } From 9518573fcef7ee0bc4276c87e7d5ad1ca9b4c9b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 8 Sep 2025 12:29:10 +0200 Subject: [PATCH 32/36] Add `Fn` suffix to `application` and `daemon` traits --- src/application.rs | 36 ++++++++++++++++++------------------ src/application/timed.rs | 22 +++++++++++----------- src/daemon.rs | 26 +++++++++++++------------- src/lib.rs | 4 ++-- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/application.rs b/src/application.rs index dfcc4c00..ef15a34a 100644 --- a/src/application.rs +++ b/src/application.rs @@ -75,9 +75,9 @@ 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, @@ -103,9 +103,9 @@ where Message: program::Message + 'static, 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; @@ -320,10 +320,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, > { @@ -425,12 +425,12 @@ impl 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, @@ -464,18 +464,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, { @@ -488,18 +488,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>, @@ -513,13 +513,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, diff --git a/src/application/timed.rs b/src/application/timed.rs index 2aaf40f3..32b2f100 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,10 +20,10 @@ 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, > @@ -71,10 +71,10 @@ where Message: program::Message + '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); @@ -148,9 +148,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, @@ -160,7 +160,7 @@ pub trait Update { ) -> impl Into>; } -impl Update for () { +impl UpdateFn for () { fn update( &self, _state: &mut State, @@ -170,7 +170,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 1e4143db..08a7d939 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -23,9 +23,9 @@ 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, @@ -51,9 +51,9 @@ where Message: program::Message + 'static, 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; @@ -171,10 +171,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, > { @@ -268,18 +268,18 @@ impl 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, { @@ -292,7 +292,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, @@ -302,7 +302,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, diff --git a/src/lib.rs b/src/lib.rs index b0b2bb8d..4ced8d30 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -691,8 +691,8 @@ 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 From 09c604c92db07f5e9ed2f6657702a2d68f47836e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 8 Sep 2025 14:32:24 +0200 Subject: [PATCH 33/36] Add `theme` and `theme_changes` functions to `system` --- Cargo.toml | 2 +- core/src/window/event.rs | 4 - examples/events/src/main.rs | 4 +- examples/exit/src/main.rs | 2 +- examples/multi_window/src/main.rs | 2 +- examples/screenshot/src/main.rs | 2 +- examples/system_information/Cargo.toml | 2 +- examples/system_information/src/main.rs | 5 +- examples/todos/src/main.rs | 2 +- futures/src/event.rs | 4 +- futures/src/subscription.rs | 7 +- graphics/src/compositor.rs | 4 +- renderer/src/fallback.rs | 4 +- runtime/src/lib.rs | 8 -- runtime/src/system.rs | 39 +++++- runtime/src/window.rs | 18 +-- src/lib.rs | 7 +- tiny_skia/src/window/compositor.rs | 2 +- wgpu/src/window/compositor.rs | 2 +- winit/Cargo.toml | 2 +- winit/src/conversion.rs | 3 - winit/src/lib.rs | 151 +++++++++++++++++------- winit/src/system.rs | 7 -- winit/src/window/state.rs | 5 - 24 files changed, 186 insertions(+), 102 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 157c59bc..a92f7778 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ 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!) diff --git a/core/src/window/event.rs b/core/src/window/event.rs index 48622b70..45d29179 100644 --- a/core/src/window/event.rs +++ b/core/src/window/event.rs @@ -1,4 +1,3 @@ -use crate::theme; use crate::time::Instant; use crate::{Point, Size}; @@ -72,7 +71,4 @@ pub enum Event { /// /// - **Wayland:** Not implemented. FilesHoveredLeft, - - /// The theme mode of the window has changed. - ThemeModeChanged(theme::Mode), } 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/multi_window/src/main.rs b/examples/multi_window/src/main.rs index d299648e..42082f21 100644 --- a/examples/multi_window/src/main.rs +++ b/examples/multi_window/src/main.rs @@ -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, 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/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/todos/src/main.rs b/examples/todos/src/main.rs index 9c57487c..31f20ab6 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -151,7 +151,7 @@ impl Todos { widget::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(), }; 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/renderer/src/fallback.rs b/renderer/src/fallback.rs index d8d04d45..d018a242 100644 --- a/renderer/src/fallback.rs +++ b/renderer/src/fallback.rs @@ -313,8 +313,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/lib.rs b/runtime/src/lib.rs index 77e80b8d..457f723c 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -25,7 +25,6 @@ pub use iced_futures as futures; pub use task::Task; pub use user_interface::UserInterface; -use crate::core::theme; use crate::core::widget; use crate::futures::futures::channel::oneshot; @@ -57,9 +56,6 @@ pub enum Action { /// Run a system action. System(system::Action), - /// Change the default system theme mode of all windows. - ChangeTheme(theme::Mode), - /// Recreate all user interfaces and redraw all windows. Reload, @@ -86,7 +82,6 @@ impl Action { Action::Clipboard(action) => Err(Action::Clipboard(action)), Action::Window(action) => Err(Action::Window(action)), Action::System(action) => Err(Action::System(action)), - Action::ChangeTheme(mode) => Err(Action::ChangeTheme(mode)), Action::Reload => Err(Action::Reload), Action::Exit => Err(Action::Exit), } @@ -111,9 +106,6 @@ where } Action::Window(_) => write!(f, "Action::Window"), Action::System(action) => write!(f, "Action::System({action:?})"), - Action::ChangeTheme(mode) => { - write!(f, "Action::ChangeTheme({mode:?})") - } Action::Reload => write!(f, "Action::Reload"), Action::Exit => write!(f, "Action::Exit"), } 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/lib.rs b/src/lib.rs index 4ced8d30..8dfad67d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -587,11 +587,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 { 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/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/winit/Cargo.toml b/winit/Cargo.toml index 85390a0d..85f235f7 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -16,7 +16,7 @@ workspace = true [features] default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"] debug = ["iced_debug/enable"] -system = ["sysinfo"] +sysinfo = ["dep:sysinfo"] program = [] x11 = ["winit/x11"] wayland = ["winit/wayland"] diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index 654cfb54..250918ab 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -319,9 +319,6 @@ pub fn window_event( Some(Event::Window(window::Event::Moved(Point::new(x, y)))) } - WindowEvent::ThemeChanged(theme) => Some(Event::Window( - window::Event::ThemeModeChanged(theme_mode(theme)), - )), _ => None, } } diff --git a/winit/src/lib.rs b/winit/src/lib.rs index 970366c7..90a4529b 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}; @@ -126,6 +124,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, @@ -136,6 +135,7 @@ where is_daemon, graphics_settings, settings.fonts, + system_theme_receiver, )); let context = task::Context::from_waker(task::noop_waker_ref()); @@ -147,6 +147,7 @@ where sender: mpsc::UnboundedSender>>, receiver: mpsc::UnboundedReceiver, error: Option, + system_theme: Option>, #[cfg(target_arch = "wasm32")] canvas: Option, @@ -159,6 +160,7 @@ where sender: event_sender, receiver: control_receiver, error: None, + system_theme: Some(system_theme_sender), #[cfg(target_arch = "wasm32")] canvas: None, @@ -172,10 +174,15 @@ where Message: std::fmt::Debug, 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( @@ -498,6 +505,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, @@ -518,7 +526,7 @@ async fn run_instance

( let mut clipboard = Clipboard::unconnected(); #[cfg(all(feature = "linux-theme-detection", target_os = "linux"))] - let system_theme = { + let mut system_theme = { let to_mode = |color_scheme| match color_scheme { mundy::ColorScheme::NoPreference => theme::Mode::None, mundy::ColorScheme::Light => theme::Mode::Light, @@ -528,7 +536,9 @@ async fn run_instance

( runtime.run( mundy::Preferences::stream(mundy::Interest::ColorScheme) .map(move |preferences| { - Action::ChangeTheme(to_mode(preferences.color_scheme)) + Action::System(system::Action::NotifyTheme(to_mode( + preferences.color_scheme, + ))) }) .boxed(), ); @@ -542,7 +552,10 @@ async fn run_instance

( }; #[cfg(not(all(feature = "linux-theme-detection", target_os = "linux")))] - let system_theme = theme::Mode::None; + 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 @@ -728,6 +741,7 @@ async fn run_instance

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

( &mut window_manager, &mut ui_caches, &mut is_window_opening, + &mut system_theme, ); actions += 1; } @@ -895,11 +910,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!( @@ -912,6 +940,7 @@ async fn run_instance

( id, )), &program, + &mut runtime, &mut compositor, &mut events, &mut messages, @@ -921,6 +950,7 @@ async fn run_instance

( &mut window_manager, &mut ui_caches, &mut is_window_opening, + &mut system_theme, ); } else { window.state.update( @@ -1132,6 +1162,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, @@ -1144,13 +1175,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 { @@ -1458,21 +1489,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); @@ -1501,27 +1555,6 @@ fn run_action<'a, P, C>( let _ = channel.send(Ok(())); } } - Action::ChangeTheme(mode) => { - let Some(theme) = conversion::window_theme(mode) else { - return; - }; - - for (id, window) in window_manager.iter_mut() { - window.raw.set_theme(Some(theme)); - window.state.update( - program, - &window.raw, - &winit::event::WindowEvent::ThemeChanged(theme), - ); - - events.push(( - id, - core::Event::Window(core::window::Event::ThemeModeChanged( - mode, - )), - )); - } - } Action::Reload => { for (id, window) in window_manager.iter_mut() { let Some(ui) = interfaces.remove(&id) else { @@ -1602,3 +1635,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 index 0b476773..ec13c0f3 100644 --- a/winit/src/system.rs +++ b/winit/src/system.rs @@ -3,13 +3,6 @@ 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 { diff --git a/winit/src/window/state.rs b/winit/src/window/state.rs index 4bc28cd4..4c3cb403 100644 --- a/winit/src/window/state.rs +++ b/winit/src/window/state.rs @@ -53,11 +53,6 @@ where window: &Window, system_theme: theme::Mode, ) -> Self { - let system_theme = window - .theme() - .map(conversion::theme_mode) - .unwrap_or(system_theme); - let title = program.title(window_id); let scale_factor = program.scale_factor(window_id); let theme = program.theme(window_id); From 1df16356ed4d1cfc125e4e0eb972d970a84697c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 8 Sep 2025 14:35:17 +0200 Subject: [PATCH 34/36] Remove `system` module leftover from `iced_winit` --- winit/src/system.rs | 36 ------------------------------------ 1 file changed, 36 deletions(-) delete mode 100644 winit/src/system.rs diff --git a/winit/src/system.rs b/winit/src/system.rs deleted file mode 100644 index ec13c0f3..00000000 --- a/winit/src/system.rs +++ /dev/null @@ -1,36 +0,0 @@ -//! Access the native system. -use crate::graphics::compositor; -use crate::runtime::system::{Action, Information}; -use crate::runtime::{self, Task}; - -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, - } -} From 04f53b5f624cbe00bad2cb719bb7cb9a45ab1a70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 10 Sep 2025 18:08:01 +0200 Subject: [PATCH 35/36] Broadcast potential theme changes on window creation --- winit/src/lib.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/winit/src/lib.rs b/winit/src/lib.rs index 90a4529b..4b7f02bd 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -647,6 +647,20 @@ async fn run_instance

( system_theme, ); + let window_theme = window + .raw + .theme() + .map(conversion::theme_mode) + .unwrap_or_default(); + + if system_theme != window_theme { + system_theme = window_theme; + + runtime.broadcast(subscription::Event::SystemThemeChanged( + window_theme, + )); + } + window.raw.set_theme(conversion::window_theme( window.state.theme_mode(), )); From 25b965d26a532cb9335c2672530090fcb3e7deae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 11 Sep 2025 02:46:44 +0200 Subject: [PATCH 36/36] Update system theme before creating `window::State` --- winit/src/lib.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/winit/src/lib.rs b/winit/src/lib.rs index 4b7f02bd..413bdab3 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -635,20 +635,7 @@ async fn run_instance

( } } - let is_first = window_manager.is_empty(); - let window = window_manager.insert( - id, - window, - &program, - compositor - .as_mut() - .expect("Compositor must be initialized"), - exit_on_close_request, - system_theme, - ); - let window_theme = window - .raw .theme() .map(conversion::theme_mode) .unwrap_or_default(); @@ -661,6 +648,18 @@ async fn run_instance

( )); } + let is_first = window_manager.is_empty(); + let window = window_manager.insert( + id, + window, + &program, + compositor + .as_mut() + .expect("Compositor must be initialized"), + exit_on_close_request, + system_theme, + ); + window.raw.set_theme(conversion::window_theme( window.state.theme_mode(), ));