diff --git a/Cargo.lock b/Cargo.lock index 40e459e4..786e115c 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.1", + "bitflags 2.9.3", "cc", "cesu8", "jni", @@ -141,9 +141,9 @@ dependencies = [ [[package]] name = "arbitrary" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" [[package]] name = "arc" @@ -267,9 +267,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", @@ -394,9 +394,9 @@ dependencies = [ [[package]] name = "async-std" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730294c1c08c2e0f85759590518f6333f0d5a0a766a27d519c1b244c3dfd8a24" +checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" dependencies = [ "async-channel 1.9.0", "async-global-executor", @@ -426,9 +426,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", @@ -492,9 +492,9 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ea8ef51aced2b9191c08197f55450d830876d9933f8f48a429b354f1d496b42" +checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f" dependencies = [ "arrayvec", ] @@ -579,9 +579,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" @@ -591,9 +591,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" dependencies = [ "serde", ] @@ -634,7 +634,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" dependencies = [ - "objc2 0.6.1", + "objc2 0.6.2", ] [[package]] @@ -734,7 +734,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "log", "polling", "rustix 0.38.44", @@ -773,9 +773,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.32" +version = "1.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" +checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" dependencies = [ "jobserver", "libc", @@ -800,9 +800,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cfg_aliases" @@ -818,7 +818,7 @@ dependencies = [ "log", "reqwest", "serde", - "thiserror 2.0.14", + "thiserror 2.0.16", "tokio", "tracing-subscriber", "webbrowser", @@ -874,18 +874,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", @@ -1061,7 +1061,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "core-foundation 0.10.1", "core-graphics-types 0.2.0", "foreign-types 0.5.0", @@ -1085,7 +1085,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "core-foundation 0.10.1", "libc", ] @@ -1096,7 +1096,7 @@ version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da46a9d5a8905cc538a4a5bceb6a4510de7a51049c5588c0114efce102bcbbe8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "fontdb 0.16.2", "log", "rangemap", @@ -1287,9 +1287,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" @@ -1343,10 +1343,10 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.6.1", "libc", - "objc2 0.6.1", + "objc2 0.6.2", ] [[package]] @@ -1403,7 +1403,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98888c4bbd601524c11a7ed63f814b8825f420514f78e96f752c437ae9cbb5d1" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "bytemuck", "drm-ffi", "drm-fourcc", @@ -1756,9 +1756,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", ] @@ -1979,7 +1979,7 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi 0.14.3+wasi-0.2.4", ] [[package]] @@ -2091,7 +2091,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "gpu-alloc-types", ] @@ -2101,7 +2101,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", ] [[package]] @@ -2122,7 +2122,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "gpu-descriptor-types", "hashbrown", ] @@ -2133,7 +2133,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", ] [[package]] @@ -2378,19 +2378,21 @@ dependencies = [ [[package]] name = "hyper" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", + "futures-core", "h2 0.4.12", "http 1.3.1", "http-body 1.0.1", "httparse", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -2403,7 +2405,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ "http 1.3.1", - "hyper 1.6.0", + "hyper 1.7.0", "hyper-util", "rustls 0.23.31", "rustls-pki-types", @@ -2420,7 +2422,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.6.0", + "hyper 1.7.0", "hyper-util", "native-tls", "tokio", @@ -2441,7 +2443,7 @@ dependencies = [ "futures-util", "http 1.3.1", "http-body 1.0.1", - "hyper 1.6.0", + "hyper 1.7.0", "ipnet", "libc", "percent-encoding", @@ -2494,7 +2496,7 @@ dependencies = [ "iced_widget", "iced_winit", "image", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -2507,7 +2509,7 @@ dependencies = [ "log", "semver", "serde", - "thiserror 2.0.14", + "thiserror 2.0.16", "tokio", ] @@ -2515,7 +2517,7 @@ dependencies = [ name = "iced_core" version = "0.14.0-dev" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "bytes", "dark-light", "glam", @@ -2525,7 +2527,7 @@ dependencies = [ "rustc-hash 2.1.1", "serde", "smol_str", - "thiserror 2.0.14", + "thiserror 2.0.16", "web-time", ] @@ -2570,7 +2572,7 @@ dependencies = [ name = "iced_graphics" version = "0.14.0-dev" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "bytemuck", "cosmic-text", "half", @@ -2582,7 +2584,7 @@ dependencies = [ "lyon_path", "raw-window-handle 0.6.2", "rustc-hash 2.1.1", - "thiserror 2.0.14", + "thiserror 2.0.16", "unicode-segmentation", ] @@ -2610,7 +2612,7 @@ dependencies = [ "iced_tiny_skia", "iced_wgpu", "log", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -2624,7 +2626,7 @@ dependencies = [ "iced_selector", "raw-window-handle 0.6.2", "sipper", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -2645,7 +2647,7 @@ dependencies = [ "nom 8.0.0", "png", "sha2", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[package]] @@ -2668,7 +2670,7 @@ dependencies = [ name = "iced_wgpu" version = "0.14.0-dev" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "bytemuck", "cryoglyph", "futures", @@ -2680,7 +2682,7 @@ dependencies = [ "lyon", "resvg", "rustc-hash 2.1.1", - "thiserror 2.0.14", + "thiserror 2.0.16", "wgpu", ] @@ -2696,7 +2698,7 @@ dependencies = [ "pulldown-cmark", "qrcode", "rustc-hash 2.1.1", - "thiserror 2.0.14", + "thiserror 2.0.16", "unicode-segmentation", "url", ] @@ -2710,7 +2712,7 @@ dependencies = [ "log", "rustc-hash 2.1.1", "sysinfo", - "thiserror 2.0.14", + "thiserror 2.0.16", "tracing", "wasm-bindgen-futures", "web-sys", @@ -2806,9 +2808,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", @@ -2850,9 +2852,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", @@ -2872,9 +2874,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", @@ -2908,11 +2910,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.1", + "bitflags 2.9.3", "cfg-if", "libc", ] @@ -3011,9 +3013,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", @@ -3155,7 +3157,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "libc", "redox_syscall 0.5.17", ] @@ -3362,9 +3364,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", ] @@ -3384,7 +3386,7 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block", "core-graphics-types 0.2.0", "foreign-types 0.5.0", @@ -3479,9 +3481,9 @@ dependencies = [ [[package]] name = "mutate_once" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b" +checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af" [[package]] name = "naga" @@ -3491,7 +3493,7 @@ checksum = "916cbc7cb27db60be930a4e2da243cf4bc39569195f22fd8ee419cd31d5b662c" dependencies = [ "arrayvec", "bit-set", - "bitflags 2.9.1", + "bitflags 2.9.3", "cfg-if", "cfg_aliases", "codespan-reporting", @@ -3505,7 +3507,7 @@ dependencies = [ "once_cell", "rustc-hash 1.1.0", "spirv", - "thiserror 2.0.14", + "thiserror 2.0.16", "unicode-ident", ] @@ -3532,7 +3534,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "jni-sys", "log", "ndk-sys", @@ -3568,7 +3570,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "cfg-if", "cfg_aliases", "libc", @@ -3746,9 +3748,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" +checksum = "561f357ba7f3a2a61563a186a163d0a3a5247e1089524a3981d49adb775078bc" dependencies = [ "objc2-encode", ] @@ -3759,7 +3761,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -3775,9 +3777,9 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.6.1", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-foundation 0.3.1", ] @@ -3787,7 +3789,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", @@ -3811,7 +3813,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3823,9 +3825,9 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "dispatch2", - "objc2 0.6.1", + "objc2 0.6.2", ] [[package]] @@ -3864,7 +3866,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.5.1", "dispatch", "libc", @@ -3877,8 +3879,8 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" dependencies = [ - "bitflags 2.9.1", - "objc2 0.6.1", + "bitflags 2.9.3", + "objc2 0.6.2", "objc2-core-foundation", ] @@ -3900,7 +3902,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3912,7 +3914,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3935,7 +3937,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.5.1", "objc2 0.5.2", "objc2-cloud-kit", @@ -3967,7 +3969,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", @@ -4004,7 +4006,7 @@ version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "libc", "once_cell", "onig_sys", @@ -4043,7 +4045,7 @@ version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "cfg-if", "foreign-types 0.3.2", "libc", @@ -4240,9 +4242,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" @@ -4356,7 +4358,7 @@ checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" dependencies = [ "base64 0.22.1", "indexmap", - "quick-xml 0.38.1", + "quick-xml 0.38.3", "serde", "time", ] @@ -4451,9 +4453,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", ] @@ -4490,9 +4492,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.97" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61789d7719defeb74ea5fe81f2fdfdbd28a803847077cecce2ff14e1472f6f1" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -4542,7 +4544,7 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "getopts", "memchr", "pulldown-cmark-escape", @@ -4594,9 +4596,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9845d9dccf565065824e69f9f235fafba1587031eda353c1f1561cd6a6be78f4" +checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" dependencies = [ "memchr", ] @@ -4751,9 +4753,9 @@ checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -4761,9 +4763,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -4794,7 +4796,7 @@ version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", ] [[package]] @@ -4805,14 +4807,14 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.14", + "thiserror 2.0.16", ] [[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", @@ -4822,9 +4824,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", @@ -4833,9 +4835,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" @@ -4858,7 +4860,7 @@ dependencies = [ "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.6.0", + "hyper 1.7.0", "hyper-rustls", "hyper-tls", "hyper-util", @@ -4936,7 +4938,7 @@ dependencies = [ "dispatch2", "js-sys", "log", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-app-kit 0.3.1", "objc2-core-foundation", "objc2-foundation 0.3.1", @@ -5002,7 +5004,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "errno", "libc", "linux-raw-sys 0.4.15", @@ -5015,7 +5017,7 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "errno", "libc", "linux-raw-sys 0.9.4", @@ -5092,7 +5094,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "bytemuck", "libm", "smallvec", @@ -5175,7 +5177,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -5229,9 +5231,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.142" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ "itoa", "memchr", @@ -5409,7 +5411,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "calloop", "calloop-wayland-source", "cursor-icon", @@ -5538,7 +5540,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.1", + "bitflags 2.9.3", ] [[package]] @@ -5591,7 +5593,7 @@ dependencies = [ "memmap2", "serde", "subsecond-types", - "thiserror 2.0.14", + "thiserror 2.0.16", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -5648,9 +5650,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.104" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -5728,7 +5730,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -5779,15 +5781,15 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.20.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", "rustix 1.0.8", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -5819,11 +5821,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.14" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ - "thiserror-impl 2.0.14", + "thiserror-impl 2.0.16", ] [[package]] @@ -5839,9 +5841,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.14" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", @@ -5960,9 +5962,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -6155,7 +6157,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "bytes", "futures-util", "http 1.3.1", @@ -6379,9 +6381,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", @@ -6580,11 +6582,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]] @@ -6705,7 +6707,7 @@ version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "rustix 1.0.8", "wayland-backend", "wayland-scanner", @@ -6717,7 +6719,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "cursor-icon", "wayland-backend", ] @@ -6739,7 +6741,7 @@ version = "0.32.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "wayland-backend", "wayland-client", "wayland-scanner", @@ -6751,7 +6753,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6764,7 +6766,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6824,7 +6826,7 @@ dependencies = [ "jni", "log", "ndk-context", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-foundation 0.3.1", "url", "web-sys", @@ -6871,7 +6873,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70b6ff82bbf6e9206828e1a3178e851f8c20f1c9028e74dd3a8090741ccd5798" dependencies = [ "arrayvec", - "bitflags 2.9.1", + "bitflags 2.9.3", "cfg-if", "cfg_aliases", "document-features", @@ -6902,7 +6904,7 @@ dependencies = [ "arrayvec", "bit-set", "bit-vec", - "bitflags 2.9.1", + "bitflags 2.9.3", "cfg_aliases", "document-features", "hashbrown", @@ -6916,7 +6918,7 @@ dependencies = [ "raw-window-handle 0.6.2", "rustc-hash 1.1.0", "smallvec", - "thiserror 2.0.14", + "thiserror 2.0.16", "wgpu-core-deps-apple", "wgpu-core-deps-emscripten", "wgpu-core-deps-wasm", @@ -6971,7 +6973,7 @@ dependencies = [ "arrayvec", "ash", "bit-set", - "bitflags 2.9.1", + "bitflags 2.9.3", "block", "bytemuck", "cfg-if", @@ -7001,7 +7003,7 @@ dependencies = [ "raw-window-handle 0.6.2", "renderdoc-sys", "smallvec", - "thiserror 2.0.14", + "thiserror 2.0.16", "wasm-bindgen", "web-sys", "wgpu-types", @@ -7015,11 +7017,11 @@ version = "26.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eca7a8d8af57c18f57d393601a1fb159ace8b2328f1b6b5f80893f7d672c9ae2" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "bytemuck", "js-sys", "log", - "thiserror 2.0.14", + "thiserror 2.0.16", "web-sys", ] @@ -7041,11 +7043,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -7551,7 +7553,7 @@ dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.5.1", "bytemuck", "calloop", @@ -7596,9 +7598,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", ] @@ -7614,13 +7616,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.1", -] +checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" [[package]] name = "writeable" @@ -7672,7 +7671,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "dlib", "log", "once_cell", @@ -7744,9 +7743,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", @@ -7768,7 +7767,7 @@ dependencies = [ "serde_repr", "tracing", "uds_windows", - "windows-sys 0.59.0", + "windows-sys 0.60.2", "winnow", "zbus_macros", "zbus_names", @@ -7777,9 +7776,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", @@ -7914,9 +7913,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", @@ -7929,9 +7928,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", @@ -7942,14 +7941,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", ] diff --git a/core/src/border.rs b/core/src/border.rs index da0aaa28..cf73284c 100644 --- a/core/src/border.rs +++ b/core/src/border.rs @@ -263,3 +263,16 @@ impl From for [f32; 4] { ] } } + +impl std::ops::Mul for Radius { + type Output = Self; + + fn mul(self, scale: f32) -> Self::Output { + Self { + top_left: self.top_left * scale, + top_right: self.top_right * scale, + bottom_right: self.bottom_right * scale, + bottom_left: self.bottom_left * scale, + } + } +} diff --git a/core/src/color.rs b/core/src/color.rs index ad60592a..954aad66 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -197,7 +197,7 @@ impl Color { } /// Returns the relative luminance of the [`Color`]. - /// https://www.w3.org/TR/WCAG21/#dfn-relative-luminance + /// pub fn relative_luminance(self) -> f32 { let linear = self.into_linear(); 0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2] diff --git a/core/src/element.rs b/core/src/element.rs index a3f60127..6a71296c 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -291,7 +291,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -300,7 +300,7 @@ where } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -426,7 +426,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -435,7 +435,7 @@ where } fn operate( - &self, + &mut self, state: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -554,7 +554,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, _limits: &layout::Limits, diff --git a/core/src/layout.rs b/core/src/layout.rs index 98d05602..9938342f 100644 --- a/core/src/layout.rs +++ b/core/src/layout.rs @@ -53,7 +53,7 @@ impl<'a> Layout<'a> { } } - /// Returns an iterator over the [`Layout`] of the children of a [`Node`]. + /// Returns an iterator over the children of this [`Layout`]. pub fn children(self) -> impl DoubleEndedIterator> { self.node.children().iter().map(move |node| { Layout::with_offset( @@ -62,6 +62,19 @@ impl<'a> Layout<'a> { ) }) } + + /// Returns the [`Layout`] of the child at the given index. + /// + /// This can be useful if you ever need to access children out of order + /// for layering purposes. + /// + /// # Panics + /// Panics if index is out of bounds. + pub fn child(self, index: usize) -> Layout<'a> { + let node = &self.node.children()[index]; + + Layout::with_offset(Vector::new(self.position.x, self.position.y), node) + } } /// Produces a [`Node`] with two children nodes one right next to each other. diff --git a/core/src/layout/flex.rs b/core/src/layout/flex.rs index f178a993..0afc6e1b 100644 --- a/core/src/layout/flex.rs +++ b/core/src/layout/flex.rs @@ -68,7 +68,7 @@ pub fn resolve( padding: Padding, spacing: f32, align_items: Alignment, - items: &[Element<'_, Message, Theme, Renderer>], + items: &mut [Element<'_, Message, Theme, Renderer>], trees: &mut [widget::Tree], ) -> Node where @@ -78,13 +78,13 @@ 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 mut fill_main_sum = 0; let mut some_fill_cross = false; - let (mut cross, cross_compress) = match axis { - Axis::Vertical if width == Length::Shrink => (0.0, true), - Axis::Horizontal if height == Length::Shrink => (0.0, true), - _ => (max_cross, false), - }; + let mut cross = if cross_compress { 0.0 } else { max_cross }; let mut available = axis.main(limits.max()) - total_spacing; @@ -95,14 +95,16 @@ where // We lay out non-fluid elements in the main axis. // If we need to compress the cross axis, then we skip any of these elements // that are also fluid in the cross axis. - for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate() { + for (i, (child, tree)) in items.iter_mut().zip(trees.iter_mut()).enumerate() + { let (fill_main_factor, fill_cross_factor) = { let size = child.as_widget().size(); axis.pack(size.width.fill_factor(), size.height.fill_factor()) }; - if fill_main_factor == 0 && (!cross_compress || fill_cross_factor == 0) + if (main_compress || fill_main_factor == 0) + && (!cross_compress || fill_cross_factor == 0) { let (max_width, max_height) = axis.pack( available, @@ -113,11 +115,14 @@ where }, ); - let child_limits = - Limits::new(Size::ZERO, Size::new(max_width, max_height)); + let child_limits = Limits::with_compression( + Size::ZERO, + Size::new(max_width, max_height), + compression, + ); let layout = - child.as_widget().layout(tree, renderer, &child_limits); + child.as_widget_mut().layout(tree, renderer, &child_limits); let size = layout.size(); available -= axis.main(size); @@ -141,7 +146,8 @@ where // We can defer the layout of any elements that have a fixed size in the main axis, // allowing them to use the cross calculations of the next pass. if cross_compress && some_fill_cross { - for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate() + for (i, (child, tree)) in + items.iter_mut().zip(trees.iter_mut()).enumerate() { let (main_size, cross_size) = { let size = child.as_widget().size(); @@ -149,7 +155,9 @@ where axis.pack(size.width, size.height) }; - if main_size.fill_factor() == 0 && cross_size.fill_factor() != 0 { + if (main_compress || main_size.fill_factor() == 0) + && cross_size.fill_factor() != 0 + { if let Length::Fixed(main) = main_size { available -= main; continue; @@ -157,11 +165,14 @@ where let (max_width, max_height) = axis.pack(available, cross); - let child_limits = - Limits::new(Size::ZERO, Size::new(max_width, max_height)); + let child_limits = Limits::with_compression( + Size::ZERO, + Size::new(max_width, max_height), + compression, + ); let layout = - child.as_widget().layout(tree, renderer, &child_limits); + child.as_widget_mut().layout(tree, renderer, &child_limits); let size = layout.size(); available -= axis.main(size); @@ -172,63 +183,59 @@ where } } - let remaining = match axis { - Axis::Horizontal => match width { - Length::Shrink => 0.0, - _ => available.max(0.0), - }, - Axis::Vertical => match height { - Length::Shrink => 0.0, - _ => available.max(0.0), - }, - }; + let remaining = available.max(0.0); - // THIRD PASS + // THIRD PASS (conditional) // We lay out the elements that are fluid in the main axis. // We use the remaining space to evenly allocate space based on fill factors. - for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate() { - let (fill_main_factor, fill_cross_factor) = { - let size = child.as_widget().size(); + if !main_compress { + for (i, (child, tree)) in + items.iter_mut().zip(trees.iter_mut()).enumerate() + { + let (fill_main_factor, fill_cross_factor) = { + let size = child.as_widget().size(); - axis.pack(size.width.fill_factor(), size.height.fill_factor()) - }; - - if fill_main_factor != 0 { - let max_main = - remaining * fill_main_factor as f32 / fill_main_sum as f32; - - let max_main = if max_main.is_nan() { - f32::INFINITY - } else { - max_main + axis.pack(size.width.fill_factor(), size.height.fill_factor()) }; - let min_main = if max_main.is_infinite() { - 0.0 - } else { - max_main - }; + if fill_main_factor != 0 { + let max_main = + remaining * fill_main_factor as f32 / fill_main_sum as f32; - let (min_width, min_height) = axis.pack(min_main, 0.0); - let (max_width, max_height) = axis.pack( - max_main, - if fill_cross_factor == 0 { - max_cross + let max_main = if max_main.is_nan() { + f32::INFINITY } else { - cross - }, - ); + max_main + }; - let child_limits = Limits::new( - Size::new(min_width, min_height), - Size::new(max_width, max_height), - ); + let min_main = if max_main.is_infinite() { + 0.0 + } else { + max_main + }; - let layout = - child.as_widget().layout(tree, renderer, &child_limits); - cross = cross.max(axis.cross(layout.size())); + let (min_width, min_height) = axis.pack(min_main, 0.0); + let (max_width, max_height) = axis.pack( + max_main, + if fill_cross_factor == 0 { + max_cross + } else { + cross + }, + ); - nodes[i] = layout; + let child_limits = Limits::with_compression( + Size::new(min_width, min_height), + Size::new(max_width, max_height), + compression, + ); + + let layout = + child.as_widget_mut().layout(tree, renderer, &child_limits); + cross = cross.max(axis.cross(layout.size())); + + nodes[i] = layout; + } } } @@ -237,7 +244,7 @@ where // These are elements that must be compressed in their cross axis and have // a fixed length in the main axis. if cross_compress && some_fill_cross { - for (i, (child, tree)) in items.iter().zip(trees).enumerate() { + for (i, (child, tree)) in items.iter_mut().zip(trees).enumerate() { let (main_size, cross_size) = { let size = child.as_widget().size(); @@ -255,7 +262,7 @@ where Limits::new(Size::ZERO, Size::new(max_width, max_height)); let layout = - child.as_widget().layout(tree, renderer, &child_limits); + child.as_widget_mut().layout(tree, renderer, &child_limits); let size = layout.size(); cross = cross.max(axis.cross(size)); diff --git a/core/src/layout/limits.rs b/core/src/layout/limits.rs index 7fbc7b9d..888bc3fd 100644 --- a/core/src/layout/limits.rs +++ b/core/src/layout/limits.rs @@ -6,18 +6,34 @@ use crate::{Length, Size}; pub struct Limits { min: Size, max: Size, + compression: Size, } impl Limits { /// No limits pub const NONE: Limits = Limits { min: Size::ZERO, - max: Size::INFINITY, + max: Size::INFINITE, + compression: Size::new(false, false), }; /// Creates new [`Limits`] with the given minimum and maximum [`Size`]. pub const fn new(min: Size, max: Size) -> Limits { - Limits { min, max } + Limits::with_compression(min, max, Size::new(false, false)) + } + + /// Creates new [`Limits`] with the given minimun and maximum [`Size`], and + /// whether fluid lengths should be compressed to intrinsic dimensions. + pub const fn with_compression( + min: Size, + max: Size, + compress: Size, + ) -> Self { + Limits { + min, + max, + compression: compress, + } } /// Returns the minimum [`Size`] of the [`Limits`]. @@ -30,16 +46,25 @@ impl Limits { self.max } + /// Returns the compression of the [`Limits`]. + pub fn compression(&self) -> Size { + self.compression + } + /// Applies a width constraint to the current [`Limits`]. pub fn width(mut self, width: impl Into) -> Limits { match width.into() { - Length::Shrink | Length::Fill | Length::FillPortion(_) => {} + Length::Shrink => { + self.compression.width = true; + } Length::Fixed(amount) => { let new_width = amount.min(self.max.width).max(self.min.width); self.min.width = new_width; self.max.width = new_width; + self.compression.width = false; } + Length::Fill | Length::FillPortion(_) => {} } self @@ -48,14 +73,18 @@ impl Limits { /// Applies a height constraint to the current [`Limits`]. pub fn height(mut self, height: impl Into) -> Limits { match height.into() { - Length::Shrink | Length::Fill | Length::FillPortion(_) => {} + Length::Shrink => { + self.compression.height = true; + } Length::Fixed(amount) => { let new_height = amount.min(self.max.height).max(self.min.height); self.min.height = new_height; self.max.height = new_height; + self.compression.height = false; } + Length::Fill | Length::FillPortion(_) => {} } self @@ -103,7 +132,11 @@ impl Limits { (self.max().height - size.height).max(0.0), ); - Limits { min, max } + Limits { + min, + max, + compression: self.compression, + } } /// Removes the minimum width constraint for the current [`Limits`]. @@ -111,6 +144,7 @@ impl Limits { Limits { min: Size::ZERO, max: self.max, + compression: self.compression, } } @@ -124,21 +158,27 @@ impl Limits { intrinsic_size: Size, ) -> Size { let width = match width.into() { - Length::Fill | Length::FillPortion(_) => self.max.width, + Length::Fill | Length::FillPortion(_) + if !self.compression.width => + { + self.max.width + } Length::Fixed(amount) => { amount.min(self.max.width).max(self.min.width) } - Length::Shrink => { - intrinsic_size.width.min(self.max.width).max(self.min.width) - } + _ => intrinsic_size.width.min(self.max.width).max(self.min.width), }; let height = match height.into() { - Length::Fill | Length::FillPortion(_) => self.max.height, + Length::Fill | Length::FillPortion(_) + if !self.compression.height => + { + self.max.height + } Length::Fixed(amount) => { amount.min(self.max.height).max(self.min.height) } - Length::Shrink => intrinsic_size + _ => intrinsic_size .height .min(self.max.height) .max(self.min.height), diff --git a/core/src/rectangle.rs b/core/src/rectangle.rs index 8adb272f..9f2808dd 100644 --- a/core/src/rectangle.rs +++ b/core/src/rectangle.rs @@ -34,8 +34,11 @@ where } impl Rectangle { - /// A rectangle starting at [`Point::ORIGIN`] with infinite width and height. - pub const INFINITE: Self = Self::new(Point::ORIGIN, Size::INFINITY); + /// A rectangle starting at negative infinity and with infinite width and height. + pub const INFINITE: Self = Self::new( + Point::new(f32::NEG_INFINITY, f32::NEG_INFINITY), + Size::INFINITE, + ); /// Creates a new [`Rectangle`] with its top-left corner in the given /// [`Point`] and with the provided [`Size`]. diff --git a/core/src/renderer.rs b/core/src/renderer.rs index 53f59303..84d48304 100644 --- a/core/src/renderer.rs +++ b/core/src/renderer.rs @@ -60,8 +60,8 @@ pub trait Renderer { /// Fills a [`Quad`] with the provided [`Background`]. fn fill_quad(&mut self, quad: Quad, background: impl Into); - /// Clears all of the recorded primitives in the [`Renderer`]. - fn clear(&mut self); + /// Resets the [`Renderer`] to start drawing in the `new_bounds` from scratch. + fn reset(&mut self, new_bounds: Rectangle); } /// A polygon with four sides. diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index 8105f91a..ff5baa3b 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -16,7 +16,7 @@ impl Renderer for () { fn end_transformation(&mut self) {} - fn clear(&mut self) {} + fn reset(&mut self, _new_bounds: Rectangle) {} fn fill_quad( &mut self, diff --git a/core/src/size.rs b/core/src/size.rs index 9503cf59..539135bc 100644 --- a/core/src/size.rs +++ b/core/src/size.rs @@ -24,7 +24,7 @@ impl Size { pub const UNIT: Size = Size::new(1., 1.); /// A [`Size`] with infinite width and height. - pub const INFINITY: Size = Size::new(f32::INFINITY, f32::INFINITY); + pub const INFINITE: Size = Size::new(f32::INFINITY, f32::INFINITY); /// Returns the minimum of each component of this size and another. pub fn min(self, other: Self) -> Self { diff --git a/core/src/widget.rs b/core/src/widget.rs index 655c010b..3b39ffcc 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -58,7 +58,7 @@ where /// This [`layout::Node`] is used by the runtime to compute the [`Layout`] of the /// user interface. fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -102,7 +102,7 @@ where /// Applies an [`Operation`] to the [`Widget`]. fn operate( - &self, + &mut self, _state: &mut Tree, _layout: Layout<'_>, _renderer: &Renderer, diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index 24966211..c1f1e87e 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -207,7 +207,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -245,7 +245,7 @@ where } fn operate( - &self, + &mut self, _state: &mut Tree, layout: Layout<'_>, _renderer: &Renderer, diff --git a/core/src/window/icon.rs b/core/src/window/icon.rs index 5ef0eed7..fb34ca32 100644 --- a/core/src/window/icon.rs +++ b/core/src/window/icon.rs @@ -35,7 +35,7 @@ pub fn from_rgba( } /// An window icon normally used for the titlebar or taskbar. -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Icon { rgba: Vec, size: Size, @@ -48,6 +48,15 @@ impl Icon { } } +impl std::fmt::Debug for Icon { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Icon") + .field("rgba", &format!("{} pixels", self.rgba.len() / 4)) + .field("size", &self.size) + .finish() + } +} + #[derive(Debug, thiserror::Error)] /// An error produced when using [`from_rgba`] with invalid arguments. pub enum Error { diff --git a/devtools/src/tester/recorder.rs b/devtools/src/tester/recorder.rs index 01b6e2e2..6eae0f61 100644 --- a/devtools/src/tester/recorder.rs +++ b/devtools/src/tester/recorder.rs @@ -134,14 +134,16 @@ where } fn layout( - &self, + &mut self, tree: &mut widget::Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.content - .as_widget() - .layout(&mut tree.children[0], renderer, limits) + self.content.as_widget_mut().layout( + &mut tree.children[0], + renderer, + limits, + ) } fn draw( @@ -201,13 +203,13 @@ where } fn operate( - &self, + &mut self, tree: &mut widget::Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn widget::Operation, ) { - self.content.as_widget().operate( + self.content.as_widget_mut().operate( &mut tree.children[0], layout, renderer, diff --git a/examples/custom_quad/src/main.rs b/examples/custom_quad/src/main.rs index 4ea58511..fdc3c0c2 100644 --- a/examples/custom_quad/src/main.rs +++ b/examples/custom_quad/src/main.rs @@ -175,7 +175,7 @@ mod quad { } fn layout( - &self, + &mut self, _tree: &mut widget::Tree, _renderer: &Renderer, _limits: &layout::Limits, diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs index e5db6139..c44775be 100644 --- a/examples/custom_widget/src/main.rs +++ b/examples/custom_widget/src/main.rs @@ -33,7 +33,7 @@ mod circle { } fn layout( - &self, + &mut self, _tree: &mut widget::Tree, _renderer: &Renderer, _limits: &layout::Limits, diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs index f1025675..1334a52f 100644 --- a/examples/geometry/src/main.rs +++ b/examples/geometry/src/main.rs @@ -27,7 +27,7 @@ mod rainbow { } fn layout( - &self, + &mut self, _tree: &mut widget::Tree, _renderer: &Renderer, limits: &layout::Limits, diff --git a/examples/layout/src/main.rs b/examples/layout/src/main.rs index f477c406..c15a0bfc 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("← Previous") + button(text("← Previous").shaping(text::Shaping::Advanced)) .padding([5, 10]) .on_press(Message::Previous) .into(), ), Some(horizontal_space().into()), (!self.example.is_last()).then_some( - button("Next →") + button(text("Next →").shaping(text::Shaping::Advanced)) .padding([5, 10]) .on_press(Message::Next) .into(), @@ -294,7 +294,7 @@ fn quotes<'a>() -> Element<'a, Message> { fn quote<'a>( content: impl Into>, ) -> Element<'a, Message> { - row![vertical_rule(2), content.into()] + row![vertical_rule(1), content.into()] .spacing(10) .height(Shrink) .into() @@ -313,7 +313,7 @@ fn quotes<'a>() -> Element<'a, Message> { "This is another reply", ), horizontal_rule(1), - "A separator ↑", + text("A separator ↑").shaping(text::Shaping::Advanced), ] .width(Shrink) .spacing(10) diff --git a/examples/loading_spinners/src/circular.rs b/examples/loading_spinners/src/circular.rs index 24293138..c5d6df6f 100644 --- a/examples/loading_spinners/src/circular.rs +++ b/examples/loading_spinners/src/circular.rs @@ -253,7 +253,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, diff --git a/examples/loading_spinners/src/linear.rs b/examples/loading_spinners/src/linear.rs index a6713c7a..e9b4a20e 100644 --- a/examples/loading_spinners/src/linear.rs +++ b/examples/loading_spinners/src/linear.rs @@ -167,7 +167,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, diff --git a/examples/loupe/src/main.rs b/examples/loupe/src/main.rs index c141e758..89bc82da 100644 --- a/examples/loupe/src/main.rs +++ b/examples/loupe/src/main.rs @@ -96,12 +96,12 @@ mod loupe { } fn layout( - &self, + &mut self, tree: &mut widget::Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.content.as_widget().layout(tree, renderer, limits) + self.content.as_widget_mut().layout(tree, renderer, limits) } fn draw( diff --git a/examples/pane_grid/Cargo.toml b/examples/pane_grid/Cargo.toml index fd3e133c..c1756a65 100644 --- a/examples/pane_grid/Cargo.toml +++ b/examples/pane_grid/Cargo.toml @@ -7,4 +7,4 @@ publish = false [dependencies] iced.workspace = true -iced.features = ["debug", "lazy"] +iced.features = ["debug"] diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs index 4fdd4cf9..1cc8a58a 100644 --- a/examples/toast/src/main.rs +++ b/examples/toast/src/main.rs @@ -287,12 +287,12 @@ mod toast { } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.content.as_widget().layout( + self.content.as_widget_mut().layout( &mut tree.children[0], renderer, limits, @@ -343,7 +343,7 @@ mod toast { } fn operate( - &self, + &mut self, state: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -351,7 +351,7 @@ mod toast { ) { operation.container(None, layout.bounds()); operation.traverse(&mut |operation| { - self.content.as_widget().operate( + self.content.as_widget_mut().operate( &mut state.children[0], layout, renderer, @@ -584,12 +584,12 @@ mod toast { operation.container(None, layout.bounds()); operation.traverse(&mut |operation| { self.toasts - .iter() + .iter_mut() .zip(self.state.iter_mut()) .zip(layout.children()) .for_each(|((child, state), layout)| { child - .as_widget() + .as_widget_mut() .operate(state, layout, renderer, operation); }); }); diff --git a/graphics/src/geometry.rs b/graphics/src/geometry.rs index 2b4b45a6..b32387f8 100644 --- a/graphics/src/geometry.rs +++ b/graphics/src/geometry.rs @@ -20,7 +20,7 @@ pub use crate::core::{Image, Svg}; pub use crate::gradient::{self, Gradient}; use crate::cache::Cached; -use crate::core::{self, Size}; +use crate::core::{self, Rectangle}; /// A renderer capable of drawing some [`Self::Geometry`]. pub trait Renderer: core::Renderer { @@ -31,7 +31,7 @@ pub trait Renderer: core::Renderer { type Frame: frame::Backend; /// Creates a new [`Self::Frame`]. - fn new_frame(&self, size: Size) -> Self::Frame; + fn new_frame(&self, bounds: Rectangle) -> Self::Frame; /// Draws the given [`Self::Geometry`]. fn draw_geometry(&mut self, geometry: Self::Geometry); @@ -42,7 +42,7 @@ impl Renderer for () { type Geometry = (); type Frame = (); - fn new_frame(&self, _size: Size) -> Self::Frame {} + fn new_frame(&self, _bounds: Rectangle) -> Self::Frame {} fn draw_geometry(&mut self, _geometry: Self::Geometry) {} } diff --git a/graphics/src/geometry/cache.rs b/graphics/src/geometry/cache.rs index d70cee0b..e17506a3 100644 --- a/graphics/src/geometry/cache.rs +++ b/graphics/src/geometry/cache.rs @@ -1,5 +1,5 @@ use crate::cache::{self, Cached}; -use crate::core::Size; +use crate::core::{Rectangle, Size}; use crate::geometry::{self, Frame}; pub use cache::Group; @@ -17,7 +17,7 @@ where #[derive(Debug, Clone)] struct Data { - bounds: Size, + bounds: Rectangle, geometry: T, } @@ -53,7 +53,7 @@ where /// [`Cache`]. /// /// The closure will only be called when - /// - the bounds have changed since the previous draw call. + /// - the size has changed since the previous draw call. /// - the [`Cache`] is empty or has been explicitly cleared. /// /// Otherwise, the previously stored geometry will be returned. The @@ -62,7 +62,21 @@ where pub fn draw( &self, renderer: &Renderer, - bounds: Size, + size: Size, + draw_fn: impl FnOnce(&mut Frame), + ) -> Renderer::Geometry { + self.draw_with_bounds(renderer, Rectangle::with_size(size), draw_fn) + } + + /// Draws geometry using the provided closure and stores it in the + /// [`Cache`]. + /// + /// Analogous to [`draw`](Self::draw), but takes a clipping [`Rectangle`] instead of + /// a [`Size`]. + pub fn draw_with_bounds( + &self, + renderer: &Renderer, + bounds: Rectangle, draw_fn: impl FnOnce(&mut Frame), ) -> Renderer::Geometry { use std::ops::Deref; @@ -82,7 +96,7 @@ where } }; - let mut frame = Frame::new(renderer, bounds); + let mut frame = Frame::with_bounds(renderer, bounds); draw_fn(&mut frame); let geometry = frame.into_geometry().cache(self.raw.group(), previous); diff --git a/graphics/src/geometry/frame.rs b/graphics/src/geometry/frame.rs index 29b534f8..5372ec16 100644 --- a/graphics/src/geometry/frame.rs +++ b/graphics/src/geometry/frame.rs @@ -16,9 +16,17 @@ where Renderer: geometry::Renderer, { /// Creates a new [`Frame`] with the given dimensions. + /// + /// Any geometry drawn outside of the dimensions will be clipped. + /// If you need further control, use [`with_bounds`](Self::with_bounds). pub fn new(renderer: &Renderer, size: Size) -> Self { + Self::with_bounds(renderer, Rectangle::with_size(size)) + } + + /// Creates a new [`Frame`] with the given clip bounds. + pub fn with_bounds(renderer: &Renderer, bounds: Rectangle) -> Self { Self { - raw: renderer.new_frame(size), + raw: renderer.new_frame(bounds), } } diff --git a/graphics/src/layer.rs b/graphics/src/layer.rs index c9a818fb..31cfe68a 100644 --- a/graphics/src/layer.rs +++ b/graphics/src/layer.rs @@ -9,6 +9,9 @@ pub trait Layer: Default { /// Creates a new [`Layer`] with the given bounds. fn with_bounds(bounds: Rectangle) -> Self; + /// Returns the current bounds of the [`Layer`]. + fn bounds(&self) -> Rectangle; + /// Flushes and settles any pending group of primitives in the [`Layer`]. /// /// This will be called when a [`Layer`] is finished. It allows layers to efficiently @@ -20,6 +23,24 @@ pub trait Layer: Default { /// Clears all the layers contents and resets its bounds. fn reset(&mut self); + + /// Returns the start level of the [`Layer`]. + /// + /// A level is a "sublayer" index inside of a [`Layer`]. + /// + /// A [`Layer`] may draw multiple primitive types in a certain order. + /// The level represents the lowest index of the primitive types it + /// contains. + /// + /// Two layers A and B can therefore be merged if they have the same bounds, + /// and the end level of A is lower or equal than the start level of B. + fn start(&self) -> usize; + + /// Returns the end level of the [`Layer`]. + fn end(&self) -> usize; + + /// Merges a [`Layer`] with the current one. + fn merge(&mut self, _layer: &mut Self); } /// A stack of layers used for drawing. @@ -101,13 +122,6 @@ impl Stack { let _ = self.transformations.pop(); } - /// Returns an iterator over mutable references to the layers in the [`Stack`]. - pub fn iter_mut(&mut self) -> impl Iterator { - self.flush(); - - self.layers[..self.active_count].iter_mut() - } - /// Returns an iterator over immutable references to the layers in the [`Stack`]. pub fn iter(&self) -> impl Iterator { self.layers[..self.active_count].iter() @@ -118,19 +132,82 @@ impl Stack { &self.layers[..self.active_count] } - /// Flushes and settles any primitives in the current layer of the [`Stack`]. + /// Flushes and settles any primitives in the [`Stack`]. pub fn flush(&mut self) { self.layers[self.current].flush(); } + /// Performs layer merging wherever possible. + /// + /// Flushes and settles any primitives in the [`Stack`]. + pub fn merge(&mut self) { + self.flush(); + + // These are the layers left to process + let mut left = self.active_count; + + // There must be at least 2 or more layers to merge + while left > 1 { + // We set our target as the topmost layer left to process + let mut current = left - 1; + let mut target = &self.layers[current]; + let mut target_start = target.start(); + let mut target_index = current; + + // We scan downwards for a contiguous block of mergeable layer candidates + while current > 0 { + let candidate = &self.layers[current - 1]; + let start = candidate.start(); + let end = candidate.end(); + + // We skip empty layers + if end == 0 { + current -= 1; + continue; + } + + // Candidate can be merged if primitive sublayers do not overlap with + // previous targets and the clipping bounds match + if end > target_start || candidate.bounds() != target.bounds() { + break; + } + + // Candidate is not empty and can be merged into + target = candidate; + target_start = start; + target_index = current; + current -= 1; + } + + // We merge all the layers scanned into the target + // + // Since we use `target_index` instead of `current`, we + // deliberately avoid merging into empty layers. + // + // If no candidates were mergeable, this is a no-op. + let (head, tail) = self.layers.split_at_mut(target_index + 1); + let layer = &mut head[target_index]; + + for middle in &mut tail[0..left - target_index - 1] { + layer.merge(middle); + } + + // Empty layers found after the target can be skipped + left = current; + } + } + /// Clears the layers of the [`Stack`], allowing reuse. /// + /// It resizes the base layer bounds to the `new_bounds`. + /// /// This will normally keep layer allocations for future drawing operations. - pub fn clear(&mut self) { + pub fn reset(&mut self, new_bounds: Rectangle) { for layer in self.layers[..self.active_count].iter_mut() { layer.reset(); } + self.layers[0].resize(new_bounds); self.current = 0; self.active_count = 1; self.previous.clear(); diff --git a/renderer/src/fallback.rs b/renderer/src/fallback.rs index 7b87e7e1..b09b29ce 100644 --- a/renderer/src/fallback.rs +++ b/renderer/src/fallback.rs @@ -46,8 +46,8 @@ where delegate!(self, renderer, renderer.fill_quad(quad, background.into())); } - fn clear(&mut self) { - delegate!(self, renderer, renderer.clear()); + fn reset(&mut self, new_bounds: Rectangle) { + delegate!(self, renderer, renderer.reset(new_bounds)); } fn start_layer(&mut self, bounds: Rectangle) { @@ -408,13 +408,13 @@ mod geometry { type Geometry = Geometry; type Frame = Frame; - fn new_frame(&self, size: iced_graphics::core::Size) -> Self::Frame { + fn new_frame(&self, bounds: Rectangle) -> Self::Frame { match self { Self::Primary(renderer) => { - Frame::Primary(renderer.new_frame(size)) + Frame::Primary(renderer.new_frame(bounds)) } Self::Secondary(renderer) => { - Frame::Secondary(renderer.new_frame(size)) + Frame::Secondary(renderer.new_frame(bounds)) } } } diff --git a/runtime/src/task.rs b/runtime/src/task.rs index 731c0be8..34ee1df7 100644 --- a/runtime/src/task.rs +++ b/runtime/src/task.rs @@ -383,6 +383,12 @@ impl Task> { } } +impl Default for Task { + fn default() -> Self { + Self::none() + } +} + impl From<()> for Task { fn from(_value: ()) -> Self { Self::none() diff --git a/runtime/src/user_interface.rs b/runtime/src/user_interface.rs index 631c3883..151a94a7 100644 --- a/runtime/src/user_interface.rs +++ b/runtime/src/user_interface.rs @@ -97,12 +97,12 @@ where cache: Cache, renderer: &mut Renderer, ) -> Self { - let root = root.into(); + let mut root = root.into(); let Cache { mut state } = cache; state.diff(root.as_widget()); - let base = root.as_widget().layout( + let base = root.as_widget_mut().layout( &mut state, renderer, &layout::Limits::new(Size::ZERO, bounds), @@ -234,7 +234,7 @@ where if shell.is_layout_invalid() { drop(maybe_overlay); - self.base = self.root.as_widget().layout( + self.base = self.root.as_widget_mut().layout( &mut self.state, renderer, &layout::Limits::new(Size::ZERO, self.bounds), @@ -335,7 +335,7 @@ where input_method.merge(shell.input_method()); shell.revalidate_layout(|| { - self.base = self.root.as_widget().layout( + self.base = self.root.as_widget_mut().layout( &mut self.state, renderer, &layout::Limits::new(Size::ZERO, self.bounds), @@ -482,10 +482,8 @@ where style: &renderer::Style, cursor: mouse::Cursor, ) { - // TODO: Move to shell level (?) - renderer.clear(); - let viewport = Rectangle::with_size(self.bounds); + renderer.reset(viewport); let base_cursor = match &self.overlay { None @@ -541,7 +539,7 @@ where ) { let viewport = Rectangle::with_size(self.bounds); - self.root.as_widget().operate( + self.root.as_widget_mut().operate( &mut self.state, Layout::new(&self.base), renderer, diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs index af55f2a5..a1c0d8a6 100644 --- a/tiny_skia/src/geometry.rs +++ b/tiny_skia/src/geometry.rs @@ -64,21 +64,14 @@ pub struct Frame { } impl Frame { - pub fn new(size: Size) -> Self { - Self::with_clip(Rectangle::with_size(size)) - } - - pub fn with_clip(clip_bounds: Rectangle) -> Self { + pub fn new(bounds: Rectangle) -> Self { Self { - clip_bounds, + clip_bounds: bounds, stack: Vec::new(), primitives: Vec::new(), images: Vec::new(), text: Vec::new(), - transform: tiny_skia::Transform::from_translate( - clip_bounds.x, - clip_bounds.y, - ), + transform: tiny_skia::Transform::identity(), } } } @@ -238,7 +231,7 @@ impl geometry::frame::Backend for Frame { align_x: text.align_x, align_y: text.align_y, shaping: text.shaping, - clip_bounds: Rectangle::with_size(Size::INFINITY), + clip_bounds: Rectangle::with_size(Size::INFINITE), }); } else { text.draw_with(|path, color| self.fill(&path, color)); @@ -265,7 +258,7 @@ impl geometry::frame::Backend for Frame { } fn draft(&mut self, clip_bounds: Rectangle) -> Self { - Self::with_clip(clip_bounds) + Self::new(clip_bounds) } fn paste(&mut self, frame: Self) { diff --git a/tiny_skia/src/layer.rs b/tiny_skia/src/layer.rs index 24e62ecb..00097f83 100644 --- a/tiny_skia/src/layer.rs +++ b/tiny_skia/src/layer.rs @@ -17,8 +17,8 @@ pub struct Layer { pub bounds: Rectangle, pub quads: Vec<(Quad, Background)>, pub primitives: Vec>, - pub text: Vec>, pub images: Vec, + pub text: Vec>, } impl Layer { @@ -284,6 +284,10 @@ impl graphics::Layer for Layer { } } + fn bounds(&self) -> Rectangle { + self.bounds + } + fn flush(&mut self) {} fn resize(&mut self, bounds: Rectangle) { @@ -298,6 +302,53 @@ impl graphics::Layer for Layer { self.text.clear(); self.images.clear(); } + + fn start(&self) -> usize { + if !self.quads.is_empty() { + return 1; + } + + if !self.primitives.is_empty() { + return 2; + } + + if !self.images.is_empty() { + return 3; + } + + if !self.text.is_empty() { + return 4; + } + + usize::MAX + } + + fn end(&self) -> usize { + if !self.text.is_empty() { + return 4; + } + + if !self.images.is_empty() { + return 3; + } + + if !self.primitives.is_empty() { + return 2; + } + + if !self.quads.is_empty() { + return 1; + } + + 0 + } + + fn merge(&mut self, layer: &mut Self) { + self.quads.append(&mut layer.quads); + self.primitives.append(&mut layer.primitives); + self.text.append(&mut layer.text); + self.images.append(&mut layer.images); + } } #[derive(Debug, Clone)] diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index a222e23c..16a27fc0 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -225,8 +225,8 @@ impl core::Renderer for Renderer { layer.draw_quad(quad, background.into(), transformation); } - fn clear(&mut self) { - self.layers.clear(); + fn reset(&mut self, new_bounds: Rectangle) { + self.layers.reset(new_bounds); } } @@ -293,8 +293,8 @@ impl graphics::geometry::Renderer for Renderer { type Geometry = Geometry; type Frame = geometry::Frame; - fn new_frame(&self, size: core::Size) -> Self::Frame { - geometry::Frame::new(size) + fn new_frame(&self, bounds: Rectangle) -> Self::Frame { + geometry::Frame::new(bounds) } fn draw_geometry(&mut self, geometry: Self::Geometry) { diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index c3c66464..18e26794 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -105,13 +105,8 @@ pub struct Frame { } impl Frame { - /// Creates a new [`Frame`] with the given [`Size`]. - pub fn new(size: Size) -> Frame { - Self::with_clip(Rectangle::with_size(size)) - } - /// Creates a new [`Frame`] with the given clip bounds. - pub fn with_clip(bounds: Rectangle) -> Frame { + pub fn new(bounds: Rectangle) -> Frame { Frame { clip_bounds: bounds, buffers: BufferStack::new(), @@ -120,9 +115,7 @@ impl Frame { text: Vec::new(), transforms: Transforms { previous: Vec::new(), - current: Transform(lyon::math::Transform::translation( - bounds.x, bounds.y, - )), + current: Transform(lyon::math::Transform::identity()), }, fill_tessellator: tessellation::FillTessellator::new(), stroke_tessellator: tessellation::StrokeTessellator::new(), @@ -409,7 +402,7 @@ impl geometry::frame::Backend for Frame { } fn draft(&mut self, clip_bounds: Rectangle) -> Frame { - Frame::with_clip(clip_bounds) + Frame::new(clip_bounds) } fn paste(&mut self, frame: Frame) { diff --git a/wgpu/src/image/null.rs b/wgpu/src/image/null.rs index cfcd53fc..d0d74cfc 100644 --- a/wgpu/src/image/null.rs +++ b/wgpu/src/image/null.rs @@ -11,4 +11,6 @@ impl Batch { pub fn is_empty(&self) -> bool { true } + + pub fn append(&mut self, _batch: &mut Self) {} } diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index 2d8fcee5..cd3a7de7 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -49,11 +49,14 @@ impl Layer { position: [bounds.x, bounds.y], size: [bounds.width, bounds.height], border_color: color::pack(quad.border.color), - border_radius: quad.border.radius.into(), - border_width: quad.border.width, + border_radius: (quad.border.radius * transformation.scale_factor()) + .into(), + border_width: quad.border.width * transformation.scale_factor(), shadow_color: color::pack(quad.shadow.color), - shadow_offset: quad.shadow.offset.into(), - shadow_blur_radius: quad.shadow.blur_radius, + shadow_offset: (quad.shadow.offset * transformation.scale_factor()) + .into(), + shadow_blur_radius: quad.shadow.blur_radius + * transformation.scale_factor(), snap: quad.snap as u32, }; @@ -268,6 +271,10 @@ impl graphics::Layer for Layer { } } + fn bounds(&self) -> Rectangle { + self.bounds + } + fn flush(&mut self) { self.flush_meshes(); self.flush_text(); @@ -288,6 +295,62 @@ impl graphics::Layer for Layer { self.pending_meshes.clear(); self.pending_text.clear(); } + + fn start(&self) -> usize { + if !self.quads.is_empty() { + return 1; + } + + if !self.triangles.is_empty() { + return 2; + } + + if !self.primitives.is_empty() { + return 3; + } + + if !self.images.is_empty() { + return 4; + } + + if !self.text.is_empty() { + return 5; + } + + usize::MAX + } + + fn end(&self) -> usize { + if !self.text.is_empty() { + return 5; + } + + if !self.images.is_empty() { + return 4; + } + + if !self.primitives.is_empty() { + return 3; + } + + if !self.triangles.is_empty() { + return 2; + } + + if !self.quads.is_empty() { + return 1; + } + + 0 + } + + fn merge(&mut self, layer: &mut Self) { + self.quads.append(&mut layer.quads); + self.triangles.append(&mut layer.triangles); + self.primitives.append(&mut layer.primitives); + self.images.append(&mut layer.images); + self.text.append(&mut layer.text); + } } impl Default for Layer { diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 39f31622..8b1e6245 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -311,7 +311,9 @@ impl Renderer { viewport.physical_size(), )); - for layer in self.layers.iter_mut() { + self.layers.merge(); + + for layer in self.layers.iter() { if physical_bounds .intersection(&(layer.bounds * scale_factor)) .and_then(Rectangle::snap) @@ -648,8 +650,8 @@ impl core::Renderer for Renderer { layer.draw_quad(quad, background.into(), transformation); } - fn clear(&mut self) { - self.layers.clear(); + fn reset(&mut self, new_bounds: Rectangle) { + self.layers.reset(new_bounds); } } @@ -759,8 +761,8 @@ impl graphics::geometry::Renderer for Renderer { type Geometry = Geometry; type Frame = geometry::Frame; - fn new_frame(&self, size: core::Size) -> Self::Frame { - geometry::Frame::new(size) + fn new_frame(&self, bounds: Rectangle) -> Self::Frame { + geometry::Frame::new(bounds) } fn draw_geometry(&mut self, geometry: Self::Geometry) { diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs index bf9d8961..31bf69cf 100644 --- a/wgpu/src/quad.rs +++ b/wgpu/src/quad.rs @@ -304,6 +304,12 @@ impl Batch { self.gradients.clear(); self.order.clear(); } + + pub fn append(&mut self, batch: &mut Batch) { + self.solids.append(&mut batch.solids); + self.gradients.append(&mut batch.gradients); + self.order.append(&mut batch.order); + } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] diff --git a/wgpu/src/quad/gradient.rs b/wgpu/src/quad/gradient.rs index cd99c8ca..6a5eed8d 100644 --- a/wgpu/src/quad/gradient.rs +++ b/wgpu/src/quad/gradient.rs @@ -72,8 +72,6 @@ impl Pipeline { ) -> Self { #[cfg(not(target_arch = "wasm32"))] { - use crate::graphics::color; - let layout = device.create_pipeline_layout( &wgpu::PipelineLayoutDescriptor { label: Some("iced_wgpu.quad.gradient.pipeline"), @@ -86,39 +84,17 @@ impl Pipeline { device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some("iced_wgpu.quad.gradient.shader"), source: wgpu::ShaderSource::Wgsl( - std::borrow::Cow::Borrowed( - if color::GAMMA_CORRECTION { - concat!( - include_str!("../shader/quad.wgsl"), - "\n", - include_str!("../shader/vertex.wgsl"), - "\n", - include_str!( - "../shader/quad/gradient.wgsl" - ), - "\n", - include_str!("../shader/color.wgsl"), - "\n", - include_str!("../shader/color/oklab.wgsl") - ) - } else { - concat!( - include_str!("../shader/quad.wgsl"), - "\n", - include_str!("../shader/vertex.wgsl"), - "\n", - include_str!( - "../shader/quad/gradient.wgsl" - ), - "\n", - include_str!("../shader/color.wgsl"), - "\n", - include_str!( - "../shader/color/linear_rgb.wgsl" - ) - ) - }, - ), + std::borrow::Cow::Borrowed(concat!( + include_str!("../shader/quad.wgsl"), + "\n", + include_str!("../shader/vertex.wgsl"), + "\n", + include_str!("../shader/quad/gradient.wgsl"), + "\n", + include_str!("../shader/color.wgsl"), + "\n", + include_str!("../shader/color/linear_rgb.wgsl") + )), ), }); diff --git a/wgpu/src/shader/color/oklab.wgsl b/wgpu/src/shader/color/oklab.wgsl deleted file mode 100644 index 0dc37ba6..00000000 --- a/wgpu/src/shader/color/oklab.wgsl +++ /dev/null @@ -1,26 +0,0 @@ -const to_lms = mat3x4( - vec4(0.4121656120, 0.2118591070, 0.0883097947, 0.0), - vec4(0.5362752080, 0.6807189584, 0.2818474174, 0.0), - vec4(0.0514575653, 0.1074065790, 0.6302613616, 0.0), -); - -const to_rgb = mat3x4( - vec4( 4.0767245293, -3.3072168827, 0.2307590544, 0.0), - vec4(-1.2681437731, 2.6093323231, -0.3411344290, 0.0), - vec4(-0.0041119885, -0.7034763098, 1.7068625689, 0.0), -); - -fn interpolate_color(from_: vec4, to_: vec4, factor: f32) -> vec4 { - // To Oklab - let lms_a = pow(from_ * to_lms, vec3(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0)); - let lms_b = pow(to_ * to_lms, vec3(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0)); - let mixed = mix(lms_a, lms_b, factor); - - // Back to linear RGB - var color = to_rgb * (mixed * mixed * mixed); - - // Alpha interpolation - color.a = mix(from_.a, to_.a, factor); - - return color; -} diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 2c900f54..682f1966 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -827,7 +827,6 @@ mod solid { mod gradient { use crate::Buffer; use crate::graphics::Antialiasing; - use crate::graphics::color; use crate::graphics::mesh; use crate::triangle; @@ -922,35 +921,15 @@ mod gradient { device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some("iced_wgpu.triangle.gradient.shader"), source: wgpu::ShaderSource::Wgsl( - std::borrow::Cow::Borrowed( - if color::GAMMA_CORRECTION { - concat!( - include_str!("shader/triangle.wgsl"), - "\n", - include_str!( - "shader/triangle/gradient.wgsl" - ), - "\n", - include_str!("shader/color.wgsl"), - "\n", - include_str!("shader/color/oklab.wgsl") - ) - } else { - concat!( - include_str!("shader/triangle.wgsl"), - "\n", - include_str!( - "shader/triangle/gradient.wgsl" - ), - "\n", - include_str!("shader/color.wgsl"), - "\n", - include_str!( - "shader/color/linear_rgb.wgsl" - ) - ) - }, - ), + std::borrow::Cow::Borrowed(concat!( + include_str!("shader/triangle.wgsl"), + "\n", + include_str!("shader/triangle/gradient.wgsl"), + "\n", + include_str!("shader/color.wgsl"), + "\n", + include_str!("shader/color/linear_rgb.wgsl") + )), ), }); diff --git a/widget/src/button.rs b/widget/src/button.rs index d9021597..c0e16728 100644 --- a/widget/src/button.rs +++ b/widget/src/button.rs @@ -235,7 +235,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -246,7 +246,7 @@ where self.height, self.padding, |limits| { - self.content.as_widget().layout( + self.content.as_widget_mut().layout( &mut tree.children[0], renderer, limits, @@ -256,7 +256,7 @@ where } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -264,7 +264,7 @@ where ) { operation.container(None, layout.bounds()); operation.traverse(&mut |operation| { - self.content.as_widget().operate( + self.content.as_widget_mut().operate( &mut tree.children[0], layout.children().next().unwrap(), renderer, diff --git a/widget/src/canvas.rs b/widget/src/canvas.rs index 50acade5..b1b6ac13 100644 --- a/widget/src/canvas.rs +++ b/widget/src/canvas.rs @@ -207,7 +207,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/checkbox.rs b/widget/src/checkbox.rs index 42ef89fd..32e81538 100644 --- a/widget/src/checkbox.rs +++ b/widget/src/checkbox.rs @@ -269,7 +269,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -447,7 +447,7 @@ where } fn operate( - &self, + &mut self, _state: &mut Tree, layout: Layout<'_>, _renderer: &Renderer, diff --git a/widget/src/column.rs b/widget/src/column.rs index 51cb8bda..ccf49e20 100644 --- a/widget/src/column.rs +++ b/widget/src/column.rs @@ -206,7 +206,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -222,13 +222,13 @@ where self.padding, self.spacing, self.align, - &self.children, + &mut self.children, &mut tree.children, ) } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -237,12 +237,12 @@ where operation.container(None, layout.bounds()); operation.traverse(&mut |operation| { self.children - .iter() + .iter_mut() .zip(&mut tree.children) .zip(layout.children()) .for_each(|((child, state), layout)| { child - .as_widget() + .as_widget_mut() .operate(state, layout, renderer, operation); }); }); diff --git a/widget/src/combo_box.rs b/widget/src/combo_box.rs index 757deee5..cda3c402 100644 --- a/widget/src/combo_box.rs +++ b/widget/src/combo_box.rs @@ -474,7 +474,7 @@ where } fn layout( - &self, + &mut self, tree: &mut widget::Tree, renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/container.rs b/widget/src/container.rs index 16a827da..dfe26b21 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -258,7 +258,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -272,12 +272,14 @@ where self.padding, self.horizontal_alignment, self.vertical_alignment, - |limits| self.content.as_widget().layout(tree, renderer, limits), + |limits| { + self.content.as_widget_mut().layout(tree, renderer, limits) + }, ) } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -285,7 +287,7 @@ where ) { operation.container(self.id.as_ref(), layout.bounds()); operation.traverse(&mut |operation| { - self.content.as_widget().operate( + self.content.as_widget_mut().operate( tree, layout.children().next().unwrap(), renderer, diff --git a/widget/src/float.rs b/widget/src/float.rs index b61b4319..1ff6ab10 100644 --- a/widget/src/float.rs +++ b/widget/src/float.rs @@ -116,12 +116,12 @@ where } fn layout( - &self, + &mut self, tree: &mut widget::Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.content.as_widget().layout(tree, renderer, limits) + self.content.as_widget_mut().layout(tree, renderer, limits) } fn update( @@ -197,14 +197,14 @@ where } fn operate( - &self, + &mut self, state: &mut widget::Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn widget::Operation, ) { self.content - .as_widget() + .as_widget_mut() .operate(state, layout, renderer, operation); } diff --git a/widget/src/grid.rs b/widget/src/grid.rs index 15ffa738..4b1d9d56 100644 --- a/widget/src/grid.rs +++ b/widget/src/grid.rs @@ -176,7 +176,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -220,10 +220,10 @@ where let mut row_height = 0.0f32; for (i, (child, tree)) in - self.children.iter().zip(&mut tree.children).enumerate() + self.children.iter_mut().zip(&mut tree.children).enumerate() { let node = child - .as_widget() + .as_widget_mut() .layout(tree, renderer, &cell_limits) .move_to((x, y)); @@ -251,7 +251,7 @@ where } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -260,12 +260,12 @@ where operation.container(None, layout.bounds()); operation.traverse(&mut |operation| { self.children - .iter() + .iter_mut() .zip(&mut tree.children) .zip(layout.children()) .for_each(|((child, state), layout)| { child - .as_widget() + .as_widget_mut() .operate(state, layout, renderer, operation); }); }); diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index fbebde3b..a47a6f63 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -6,7 +6,7 @@ use crate::container::{self, Container}; use crate::core; use crate::core::widget::operation::{self, Operation}; use crate::core::window; -use crate::core::{Element, Length, Pixels, Widget}; +use crate::core::{Element, Length, Pixels, Size, Widget}; use crate::float::{self, Float}; use crate::keyed; use crate::overlay; @@ -23,7 +23,9 @@ use crate::text_input::{self, TextInput}; use crate::toggler::{self, Toggler}; use crate::tooltip::{self, Tooltip}; use crate::vertical_slider::{self, VerticalSlider}; -use crate::{Column, Grid, MouseArea, Pin, Row, Sensor, Space, Stack, Themer}; +use crate::{ + Column, Grid, MouseArea, Pin, Responsive, Row, Sensor, Space, Stack, Themer, +}; use std::borrow::Borrow; use std::ops::RangeInclusive; @@ -607,12 +609,12 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.content.as_widget().layout(tree, renderer, limits) + self.content.as_widget_mut().layout(tree, renderer, limits) } fn draw( @@ -631,14 +633,14 @@ where } fn operate( - &self, + &mut self, state: &mut Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn operation::Operation, ) { self.content - .as_widget() + .as_widget_mut() .operate(state, layout, renderer, operation); } @@ -770,18 +772,18 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - let base = self.base.as_widget().layout( + let base = self.base.as_widget_mut().layout( &mut tree.children[0], renderer, limits, ); - let top = self.top.as_widget().layout( + let top = self.top.as_widget_mut().layout( &mut tree.children[1], renderer, &layout::Limits::new(Size::ZERO, base.size()), @@ -832,18 +834,20 @@ where } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn operation::Operation, ) { - let children = [&self.base, &self.top] + let children = [&mut self.base, &mut self.top] .into_iter() .zip(layout.children().zip(&mut tree.children)); for (child, (layout, tree)) in children { - child.as_widget().operate(tree, layout, renderer, operation); + child + .as_widget_mut() + .operate(tree, layout, renderer, operation); } } @@ -2117,3 +2121,18 @@ where { Float::new(content) } + +/// Creates a new [`Responsive`] widget with a closure that produces its +/// contents. +/// +/// The `view` closure will receive the maximum available space for +/// the [`Responsive`] during layout. You can use this [`Size`] to +/// conditionally build the contents. +pub fn responsive<'a, Message, Theme, Renderer>( + f: impl Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a, +) -> Responsive<'a, Message, Theme, Renderer> +where + Renderer: core::Renderer, +{ + Responsive::new(f) +} diff --git a/widget/src/image.rs b/widget/src/image.rs index 80ebd721..3b29e4f5 100644 --- a/widget/src/image.rs +++ b/widget/src/image.rs @@ -59,6 +59,7 @@ pub struct Image { handle: Handle, width: Length, height: Length, + crop: Option>, content_fit: ContentFit, filter_method: FilterMethod, rotation: Rotation, @@ -74,6 +75,7 @@ impl Image { handle: handle.into(), width: Length::Shrink, height: Length::Shrink, + crop: None, content_fit: ContentFit::default(), filter_method: FilterMethod::default(), rotation: Rotation::default(), @@ -145,6 +147,24 @@ impl Image { self.scale = scale.into(); self } + + /// Crops the [`Image`] to the given region described by the [`Rectangle`] in absolute + /// coordinates. + /// + /// Cropping is done before applying any transformation or [`ContentFit`]. In practice, + /// this means that cropping an [`Image`] with this method should produce the same result + /// as cropping it externally (e.g. with an image editor) and creating a new [`Handle`] + /// for the cropped version. + /// + /// However, this method is much more efficient; since it just leverages scissoring during + /// rendering and no image cropping actually takes place. Instead, it reuses the existing + /// image allocations and should be as efficient as not cropping at all! + /// + /// The `region` coordinates will be clamped to the image dimensions, if necessary. + pub fn crop(mut self, region: Rectangle) -> Self { + self.crop = Some(region); + self + } } /// Computes the layout of an [`Image`]. @@ -154,6 +174,7 @@ pub fn layout( handle: &Handle, width: Length, height: Length, + region: Option>, content_fit: ContentFit, rotation: Rotation, expand: bool, @@ -162,16 +183,14 @@ where Renderer: image::Renderer, { // The raw w/h of the underlying image - let image_size = renderer.measure_image(handle); - let image_size = - Size::new(image_size.width as f32, image_size.height as f32); + let image_size = crop(renderer.measure_image(handle), region); // The rotated size of the image let rotated_size = rotation.apply(image_size); // The size to be available to the widget prior to `Shrink`ing let bounds = if expand { - limits.max() + limits.width(width).height(height).max() } else { limits.resolve(width, height, rotated_size) }; @@ -198,6 +217,7 @@ fn drawing_bounds( renderer: &Renderer, bounds: Rectangle, handle: &Handle, + region: Option>, content_fit: ContentFit, rotation: Rotation, scale: f32, @@ -205,8 +225,8 @@ fn drawing_bounds( where Renderer: image::Renderer, { - let Size { width, height } = renderer.measure_image(handle); - let image_size = Size::new(width as f32, height as f32); + let original_size = renderer.measure_image(handle); + let image_size = crop(original_size, region); let rotated_size = rotation.apply(image_size); let adjusted_fit = content_fit.fit(rotated_size, bounds.size()); @@ -217,6 +237,37 @@ where let final_size = image_size * fit_scale * scale; + let (crop_offset, final_size) = if let Some(region) = region { + let x = region.x.min(original_size.width) as f32; + let y = region.y.min(original_size.height) as f32; + let width = image_size.width; + let height = image_size.height; + + let ratio = Vector::new( + original_size.width as f32 / width, + original_size.height as f32 / height, + ); + + let final_size = final_size * ratio; + + let scale = Vector::new( + final_size.width / original_size.width as f32, + final_size.height / original_size.height as f32, + ); + + let offset = match content_fit { + ContentFit::None => Vector::new(x * scale.x, y * scale.y), + _ => Vector::new( + ((original_size.width as f32 - width) / 2.0 - x) * scale.x, + ((original_size.height as f32 - height) / 2.0 - y) * scale.y, + ), + }; + + (offset, final_size) + } else { + (Vector::ZERO, final_size) + }; + let position = match content_fit { ContentFit::None => Point::new( bounds.x + (rotated_size.width - adjusted_fit.width) / 2.0, @@ -228,19 +279,31 @@ where ), }; - Rectangle::new(position, final_size) + Rectangle::new(position + crop_offset, final_size) } fn must_clip(bounds: Rectangle, drawing_bounds: Rectangle) -> bool { drawing_bounds.width > bounds.width || drawing_bounds.height > bounds.height } +fn crop(size: Size, region: Option>) -> Size { + if let Some(region) = region { + Size::new( + region.width.min(size.width) as f32, + region.height.min(size.height) as f32, + ) + } else { + Size::new(size.width as f32, size.height as f32) + } +} + /// Draws an [`Image`] pub fn draw( renderer: &mut Renderer, layout: Layout<'_>, viewport: &Rectangle, handle: &Handle, + crop: Option>, content_fit: ContentFit, filter_method: FilterMethod, rotation: Rotation, @@ -251,8 +314,15 @@ pub fn draw( Handle: Clone, { let bounds = layout.bounds(); - let drawing_bounds = - drawing_bounds(renderer, bounds, handle, content_fit, rotation, scale); + let drawing_bounds = drawing_bounds( + renderer, + bounds, + handle, + crop, + content_fit, + rotation, + scale, + ); if must_clip(bounds, drawing_bounds) { if let Some(bounds) = bounds.intersection(viewport) { @@ -316,7 +386,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -327,6 +397,7 @@ where &self.handle, self.width, self.height, + self.crop, self.content_fit, self.rotation, self.expand, @@ -348,6 +419,7 @@ where layout, viewport, &self.handle, + self.crop, self.content_fit, self.filter_method, self.rotation, diff --git a/widget/src/image/viewer.rs b/widget/src/image/viewer.rs index 811241a9..205f84d5 100644 --- a/widget/src/image/viewer.rs +++ b/widget/src/image/viewer.rs @@ -117,7 +117,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/keyed/column.rs b/widget/src/keyed/column.rs index 40605e14..45525e4a 100644 --- a/widget/src/keyed/column.rs +++ b/widget/src/keyed/column.rs @@ -253,7 +253,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -272,13 +272,13 @@ where self.padding, self.spacing, self.align_items, - &self.children, + &mut self.children, &mut tree.children, ) } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -287,12 +287,12 @@ where operation.container(None, layout.bounds()); operation.traverse(&mut |operation| { self.children - .iter() + .iter_mut() .zip(&mut tree.children) .zip(layout.children()) .for_each(|((child, state), layout)| { child - .as_widget() + .as_widget_mut() .operate(state, layout, renderer, operation); }); }); diff --git a/widget/src/lazy.rs b/widget/src/lazy.rs index 88a648c6..106e239d 100644 --- a/widget/src/lazy.rs +++ b/widget/src/lazy.rs @@ -2,11 +2,9 @@ pub(crate) mod helpers; pub mod component; -pub mod responsive; #[allow(deprecated)] pub use component::Component; -pub use responsive::Responsive; mod cache; @@ -165,27 +163,29 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.with_element(|element| { - element - .as_widget() - .layout(&mut tree.children[0], renderer, limits) + self.with_element_mut(|element| { + element.as_widget_mut().layout( + &mut tree.children[0], + renderer, + limits, + ) }) } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn widget::Operation, ) { - self.with_element(|element| { - element.as_widget().operate( + self.with_element_mut(|element| { + element.as_widget_mut().operate( &mut tree.children[0], layout, renderer, diff --git a/widget/src/lazy/component.rs b/widget/src/lazy/component.rs index eedc1afa..b42ef170 100644 --- a/widget/src/lazy/component.rs +++ b/widget/src/lazy/component.rs @@ -298,15 +298,15 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { let t = tree.state.downcast_mut::>>>(); - self.with_element(|element| { - element.as_widget().layout( + self.with_element_mut(|element| { + element.as_widget_mut().layout( &mut t.borrow_mut().as_mut().unwrap().children[0], renderer, limits, @@ -377,7 +377,7 @@ where } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -386,8 +386,8 @@ where self.rebuild_element_with_operation(layout, operation); let tree = tree.state.downcast_mut::>>>(); - self.with_element(|element| { - element.as_widget().operate( + self.with_element_mut(|element| { + element.as_widget_mut().operate( &mut tree.borrow_mut().as_mut().unwrap().children[0], layout, renderer, diff --git a/widget/src/lazy/helpers.rs b/widget/src/lazy/helpers.rs index 52e690ff..7e7601cb 100644 --- a/widget/src/lazy/helpers.rs +++ b/widget/src/lazy/helpers.rs @@ -1,10 +1,10 @@ -use crate::core::{self, Element, Size}; +use crate::core::{self, Element}; use crate::lazy::component; use std::hash::Hash; #[allow(deprecated)] -pub use crate::lazy::{Component, Lazy, Responsive}; +pub use crate::lazy::{Component, Lazy}; /// Creates a new [`Lazy`] widget with the given data `Dependency` and a /// closure that can turn this data into a widget tree. @@ -41,19 +41,3 @@ where { component::view(component) } - -/// Creates a new [`Responsive`] widget with a closure that produces its -/// contents. -/// -/// The `view` closure will be provided with the current [`Size`] of -/// the [`Responsive`] widget and, therefore, can be used to build the -/// contents of the widget in a responsive way. -#[cfg(feature = "lazy")] -pub fn responsive<'a, Message, Theme, Renderer>( - f: impl Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a, -) -> Responsive<'a, Message, Theme, Renderer> -where - Renderer: core::Renderer, -{ - Responsive::new(f) -} diff --git a/widget/src/lazy/responsive.rs b/widget/src/lazy/responsive.rs deleted file mode 100644 index 6c227a7a..00000000 --- a/widget/src/lazy/responsive.rs +++ /dev/null @@ -1,462 +0,0 @@ -use crate::core::layout::{self, Layout}; -use crate::core::mouse; -use crate::core::overlay; -use crate::core::renderer; -use crate::core::widget; -use crate::core::widget::tree::{self, Tree}; -use crate::core::{ - self, Clipboard, Element, Event, Length, Point, Rectangle, Shell, Size, - Vector, Widget, -}; -use crate::horizontal_space; - -use ouroboros::self_referencing; -use std::cell::{RefCell, RefMut}; -use std::marker::PhantomData; -use std::ops::Deref; - -/// A widget that is aware of its dimensions. -/// -/// A [`Responsive`] widget will always try to fill all the available space of -/// its parent. -#[cfg(feature = "lazy")] -#[allow(missing_debug_implementations)] -pub struct Responsive< - 'a, - Message, - Theme = crate::Theme, - Renderer = crate::Renderer, -> { - view: Box Element<'a, Message, Theme, Renderer> + 'a>, - content: RefCell>, -} - -impl<'a, Message, Theme, Renderer> Responsive<'a, Message, Theme, Renderer> -where - Renderer: core::Renderer, -{ - /// Creates a new [`Responsive`] widget with a closure that produces its - /// contents. - /// - /// The `view` closure will be provided with the current [`Size`] of - /// the [`Responsive`] widget and, therefore, can be used to build the - /// contents of the widget in a responsive way. - pub fn new( - view: impl Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a, - ) -> Self { - Self { - view: Box::new(view), - content: RefCell::new(Content { - size: Size::ZERO, - layout: None, - is_layout_invalid: true, - element: Element::new(horizontal_space().width(0)), - }), - } - } -} - -struct Content<'a, Message, Theme, Renderer> { - size: Size, - layout: Option, - is_layout_invalid: bool, - element: Element<'a, Message, Theme, Renderer>, -} - -impl<'a, Message, Theme, Renderer> Content<'a, Message, Theme, Renderer> -where - Renderer: core::Renderer, -{ - fn layout(&mut self, tree: &mut Tree, renderer: &Renderer) { - if self.layout.is_none() || self.is_layout_invalid { - self.layout = Some(self.element.as_widget().layout( - tree, - renderer, - &layout::Limits::new(Size::ZERO, self.size), - )); - self.is_layout_invalid = false; - } - } - - fn update( - &mut self, - tree: &mut Tree, - new_size: Size, - view: &dyn Fn(Size) -> Element<'a, Message, Theme, Renderer>, - ) { - if self.size != new_size { - self.element = view(new_size); - self.size = new_size; - self.layout = None; - - tree.diff(&self.element); - } else { - let is_tree_empty = - tree.tag == tree::Tag::stateless() && tree.children.is_empty(); - - if is_tree_empty { - self.layout = None; - tree.diff(&self.element); - } - } - } - - fn resolve( - &mut self, - tree: &mut Tree, - renderer: R, - layout: Layout<'_>, - view: &dyn Fn(Size) -> Element<'a, Message, Theme, Renderer>, - f: impl FnOnce( - &mut Tree, - R, - Layout<'_>, - &mut Element<'a, Message, Theme, Renderer>, - ) -> T, - ) -> T - where - R: Deref, - { - self.update(tree, layout.bounds().size(), view); - self.layout(tree, renderer.deref()); - - let content_layout = Layout::with_offset( - layout.position() - Point::ORIGIN, - self.layout.as_ref().unwrap(), - ); - - f(tree, renderer, content_layout, &mut self.element) - } -} - -struct State { - tree: RefCell, -} - -impl Widget - for Responsive<'_, Message, Theme, Renderer> -where - Renderer: core::Renderer, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::() - } - - fn state(&self) -> tree::State { - tree::State::new(State { - tree: RefCell::new(Tree::empty()), - }) - } - - fn size(&self) -> Size { - Size { - width: Length::Fill, - height: Length::Fill, - } - } - - fn layout( - &self, - _tree: &mut Tree, - _renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - layout::Node::new(limits.max()) - } - - fn operate( - &self, - tree: &mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - operation: &mut dyn widget::Operation, - ) { - let state = tree.state.downcast_mut::(); - let mut content = self.content.borrow_mut(); - - content.resolve( - &mut state.tree.borrow_mut(), - renderer, - layout, - &self.view, - |tree, renderer, layout, element| { - element - .as_widget() - .operate(tree, layout, renderer, operation); - }, - ); - } - - fn update( - &mut self, - tree: &mut Tree, - event: &Event, - layout: Layout<'_>, - cursor: mouse::Cursor, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - viewport: &Rectangle, - ) { - let state = tree.state.downcast_mut::(); - let mut content = self.content.borrow_mut(); - - let mut local_messages = vec![]; - let mut local_shell = Shell::new(&mut local_messages); - - content.resolve( - &mut state.tree.borrow_mut(), - renderer, - layout, - &self.view, - |tree, renderer, layout, element| { - element.as_widget_mut().update( - tree, - event, - layout, - cursor, - renderer, - clipboard, - &mut local_shell, - viewport, - ); - }, - ); - - if local_shell.is_layout_invalid() { - content.layout = None; - } - - shell.merge(local_shell, std::convert::identity); - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor: mouse::Cursor, - viewport: &Rectangle, - ) { - let state = tree.state.downcast_ref::(); - let mut content = self.content.borrow_mut(); - - content.resolve( - &mut state.tree.borrow_mut(), - renderer, - layout, - &self.view, - |tree, renderer, layout, element| { - element.as_widget().draw( - tree, renderer, theme, style, layout, cursor, viewport, - ); - }, - ); - } - - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor: mouse::Cursor, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - let state = tree.state.downcast_ref::(); - let mut content = self.content.borrow_mut(); - - content.resolve( - &mut state.tree.borrow_mut(), - renderer, - layout, - &self.view, - |tree, renderer, layout, element| { - element - .as_widget() - .mouse_interaction(tree, layout, cursor, viewport, renderer) - }, - ) - } - - fn overlay<'b>( - &'b mut self, - tree: &'b mut Tree, - layout: Layout<'b>, - renderer: &Renderer, - viewport: &Rectangle, - translation: Vector, - ) -> Option> { - use std::ops::DerefMut; - - let state = tree.state.downcast_ref::(); - - let overlay = OverlayBuilder { - content: self.content.borrow_mut(), - tree: state.tree.borrow_mut(), - types: PhantomData, - overlay_builder: |content: &mut RefMut< - '_, - Content<'_, _, _, _>, - >, - tree| { - content.update(tree, layout.bounds().size(), &self.view); - content.layout(tree, renderer); - - let Content { - element, - layout: content_layout_node, - is_layout_invalid, - .. - } = content.deref_mut(); - - let content_layout = Layout::with_offset( - layout.bounds().position() - Point::ORIGIN, - content_layout_node.as_ref().unwrap(), - ); - - ( - element - .as_widget_mut() - .overlay( - tree, - content_layout, - renderer, - viewport, - translation, - ) - .map(|overlay| { - RefCell::new(overlay::Nested::new(overlay)) - }), - is_layout_invalid, - ) - }, - } - .build(); - - if overlay.with_overlay(|(overlay, _layout)| overlay.is_some()) { - Some(overlay::Element::new(Box::new(overlay))) - } else { - None - } - } -} - -impl<'a, Message, Theme, Renderer> - From> - for Element<'a, Message, Theme, Renderer> -where - Message: 'a, - Theme: 'a, - Renderer: core::Renderer + 'a, -{ - fn from(responsive: Responsive<'a, Message, Theme, Renderer>) -> Self { - Self::new(responsive) - } -} - -#[self_referencing] -struct Overlay<'a, 'b, Message, Theme, Renderer> { - content: RefMut<'a, Content<'b, Message, Theme, Renderer>>, - tree: RefMut<'a, Tree>, - types: PhantomData, - - #[borrows(mut content, mut tree)] - #[not_covariant] - overlay: ( - Option>>, - &'this mut bool, - ), -} - -impl Overlay<'_, '_, Message, Theme, Renderer> { - fn with_overlay_maybe( - &self, - f: impl FnOnce(&mut overlay::Nested<'_, Message, Theme, Renderer>) -> T, - ) -> Option { - self.with_overlay(|(overlay, _layout)| { - overlay.as_ref().map(|nested| (f)(&mut nested.borrow_mut())) - }) - } - - fn with_overlay_mut_maybe( - &mut self, - f: impl FnOnce(&mut overlay::Nested<'_, Message, Theme, Renderer>) -> T, - ) -> Option { - self.with_overlay_mut(|(overlay, _layout)| { - overlay.as_mut().map(|nested| (f)(nested.get_mut())) - }) - } -} - -impl overlay::Overlay - for Overlay<'_, '_, Message, Theme, Renderer> -where - Renderer: core::Renderer, -{ - fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node { - self.with_overlay_maybe(|overlay| overlay.layout(renderer, bounds)) - .unwrap_or_default() - } - - fn draw( - &self, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor: mouse::Cursor, - ) { - let _ = self.with_overlay_maybe(|overlay| { - overlay.draw(renderer, theme, style, layout, cursor); - }); - } - - fn mouse_interaction( - &self, - layout: Layout<'_>, - cursor: mouse::Cursor, - renderer: &Renderer, - ) -> mouse::Interaction { - self.with_overlay_maybe(|overlay| { - overlay.mouse_interaction(layout, cursor, renderer) - }) - .unwrap_or_default() - } - - fn update( - &mut self, - event: &Event, - layout: Layout<'_>, - cursor: mouse::Cursor, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) { - let mut is_layout_invalid = false; - - let _ = self.with_overlay_mut_maybe(|overlay| { - overlay.update(event, layout, cursor, renderer, clipboard, shell); - - is_layout_invalid = shell.is_layout_invalid(); - }); - - if is_layout_invalid { - self.with_overlay_mut(|(_overlay, layout)| { - **layout = true; - }); - } - } - - fn operate( - &mut self, - layout: Layout<'_>, - renderer: &Renderer, - operation: &mut dyn widget::Operation, - ) { - let _ = self.with_overlay_mut_maybe(|overlay| { - overlay.operate(layout, renderer, operation); - }); - } -} diff --git a/widget/src/lib.rs b/widget/src/lib.rs index 4f4db734..c58220a8 100644 --- a/widget/src/lib.rs +++ b/widget/src/lib.rs @@ -13,6 +13,7 @@ mod action; mod column; mod mouse_area; mod pin; +mod responsive; mod space; mod stack; mod themer; @@ -79,6 +80,8 @@ pub use progress_bar::ProgressBar; #[doc(no_inline)] pub use radio::Radio; #[doc(no_inline)] +pub use responsive::Responsive; +#[doc(no_inline)] pub use row::Row; #[doc(no_inline)] pub use rule::Rule; diff --git a/widget/src/mouse_area.rs b/widget/src/mouse_area.rs index fefa68d5..f4a76d3b 100644 --- a/widget/src/mouse_area.rs +++ b/widget/src/mouse_area.rs @@ -190,24 +190,26 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.content - .as_widget() - .layout(&mut tree.children[0], renderer, limits) + self.content.as_widget_mut().layout( + &mut tree.children[0], + renderer, + limits, + ) } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn Operation, ) { - self.content.as_widget().operate( + self.content.as_widget_mut().operate( &mut tree.children[0], layout, renderer, diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs index 741dbf15..c4c5b521 100644 --- a/widget/src/overlay/menu.rs +++ b/widget/src/overlay/menu.rs @@ -369,7 +369,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index e1cbe6c2..5a3c1f70 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -418,7 +418,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -432,20 +432,19 @@ where let children = self .panes - .iter() - .copied() - .zip(&self.contents) + .iter_mut() + .zip(&mut self.contents) .zip(tree.children.iter_mut()) .filter_map(|((pane, content), tree)| { if self .internal .maximized() - .is_some_and(|maximized| maximized != pane) + .is_some_and(|maximized| maximized != *pane) { return Some(layout::Node::new(Size::ZERO)); } - let region = regions.get(&pane)?; + let region = regions.get(pane)?; let size = Size::new(region.width, region.height); let node = content.layout( @@ -462,7 +461,7 @@ where } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -471,15 +470,14 @@ where operation.container(None, layout.bounds()); operation.traverse(&mut |operation| { self.panes - .iter() - .copied() - .zip(&self.contents) + .iter_mut() + .zip(&mut self.contents) .zip(&mut tree.children) .zip(layout.children()) .filter(|(((pane, _), _), _)| { self.internal .maximized() - .is_none_or(|maximized| *pane == maximized) + .is_none_or(|maximized| **pane == maximized) }) .for_each(|(((_, content), state), layout)| { content.operate(state, layout, renderer, operation); diff --git a/widget/src/pane_grid/content.rs b/widget/src/pane_grid/content.rs index 8800d13b..6592694b 100644 --- a/widget/src/pane_grid/content.rs +++ b/widget/src/pane_grid/content.rs @@ -165,12 +165,12 @@ where } pub(crate) fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - if let Some(title_bar) = &self.title_bar { + if let Some(title_bar) = &mut self.title_bar { let max_size = limits.max(); let title_bar_layout = title_bar.layout( @@ -181,7 +181,7 @@ where let title_bar_size = title_bar_layout.size(); - let body_layout = self.body.as_widget().layout( + let body_layout = self.body.as_widget_mut().layout( &mut tree.children[0], renderer, &layout::Limits::new( @@ -201,7 +201,7 @@ where ], ) } else { - self.body.as_widget().layout( + self.body.as_widget_mut().layout( &mut tree.children[0], renderer, limits, @@ -210,13 +210,13 @@ where } pub(crate) fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn widget::Operation, ) { - let body_layout = if let Some(title_bar) = &self.title_bar { + let body_layout = if let Some(title_bar) = &mut self.title_bar { let mut children = layout.children(); title_bar.operate( @@ -231,7 +231,7 @@ where layout }; - self.body.as_widget().operate( + self.body.as_widget_mut().operate( &mut tree.children[0], body_layout, renderer, diff --git a/widget/src/pane_grid/title_bar.rs b/widget/src/pane_grid/title_bar.rs index e47f4a10..7d15bf80 100644 --- a/widget/src/pane_grid/title_bar.rs +++ b/widget/src/pane_grid/title_bar.rs @@ -274,7 +274,7 @@ where } pub(crate) fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -282,7 +282,7 @@ where let limits = limits.shrink(self.padding); let max_size = limits.max(); - let title_layout = self.content.as_widget().layout( + let title_layout = self.content.as_widget_mut().layout( &mut tree.children[0], renderer, &layout::Limits::new(Size::ZERO, max_size), @@ -290,8 +290,8 @@ where let title_size = title_layout.size(); - let node = if let Some(controls) = &self.controls { - let controls_layout = controls.full.as_widget().layout( + let node = if let Some(controls) = &mut self.controls { + let controls_layout = controls.full.as_widget_mut().layout( &mut tree.children[1], renderer, &layout::Limits::new(Size::ZERO, max_size), @@ -300,8 +300,8 @@ where if title_layout.bounds().width + controls_layout.bounds().width > max_size.width { - if let Some(compact) = controls.compact.as_ref() { - let compact_layout = compact.as_widget().layout( + if let Some(compact) = controls.compact.as_mut() { + let compact_layout = compact.as_widget_mut().layout( &mut tree.children[2], renderer, &layout::Limits::new(Size::ZERO, max_size), @@ -369,7 +369,7 @@ where } pub(crate) fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -382,16 +382,16 @@ where let title_layout = children.next().unwrap(); let mut show_title = true; - if let Some(controls) = &self.controls { + if let Some(controls) = &mut self.controls { let controls_layout = children.next().unwrap(); if title_layout.bounds().width + controls_layout.bounds().width > padded.bounds().width { - if let Some(compact) = controls.compact.as_ref() { + if let Some(compact) = controls.compact.as_mut() { let compact_layout = children.next().unwrap(); - compact.as_widget().operate( + compact.as_widget_mut().operate( &mut tree.children[2], compact_layout, renderer, @@ -400,7 +400,7 @@ where } else { show_title = false; - controls.full.as_widget().operate( + controls.full.as_widget_mut().operate( &mut tree.children[1], controls_layout, renderer, @@ -408,7 +408,7 @@ where ); } } else { - controls.full.as_widget().operate( + controls.full.as_widget_mut().operate( &mut tree.children[1], controls_layout, renderer, @@ -418,7 +418,7 @@ where }; if show_title { - self.content.as_widget().operate( + self.content.as_widget_mut().operate( &mut tree.children[0], title_layout, renderer, diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs index 4076d7f7..86dbdaf6 100644 --- a/widget/src/pick_list.rs +++ b/widget/src/pick_list.rs @@ -348,7 +348,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/pin.rs b/widget/src/pin.rs index 539bf2c1..04ed0324 100644 --- a/widget/src/pin.rs +++ b/widget/src/pin.rs @@ -139,7 +139,7 @@ where } fn layout( - &self, + &mut self, tree: &mut widget::Tree, renderer: &Renderer, limits: &layout::Limits, @@ -151,7 +151,7 @@ where let node = self .content - .as_widget() + .as_widget_mut() .layout(tree, renderer, &layout::Limits::new(Size::ZERO, available)) .move_to(self.position); @@ -160,13 +160,13 @@ where } fn operate( - &self, + &mut self, tree: &mut widget::Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn widget::Operation, ) { - self.content.as_widget().operate( + self.content.as_widget_mut().operate( tree, layout.children().next().unwrap(), renderer, diff --git a/widget/src/progress_bar.rs b/widget/src/progress_bar.rs index a9b4eb65..d364f5df 100644 --- a/widget/src/progress_bar.rs +++ b/widget/src/progress_bar.rs @@ -157,7 +157,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/qr_code.rs b/widget/src/qr_code.rs index 07bb0c50..e8a278fd 100644 --- a/widget/src/qr_code.rs +++ b/widget/src/qr_code.rs @@ -136,7 +136,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, _limits: &layout::Limits, diff --git a/widget/src/radio.rs b/widget/src/radio.rs index fab29f56..3bb2e806 100644 --- a/widget/src/radio.rs +++ b/widget/src/radio.rs @@ -290,7 +290,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/responsive.rs b/widget/src/responsive.rs new file mode 100644 index 00000000..a987a430 --- /dev/null +++ b/widget/src/responsive.rs @@ -0,0 +1,208 @@ +use crate::core::layout::{self, Layout}; +use crate::core::mouse; +use crate::core::overlay; +use crate::core::renderer; +use crate::core::widget; +use crate::core::widget::Tree; +use crate::core::{ + self, Clipboard, Element, Event, Length, Rectangle, Shell, Size, Vector, + Widget, +}; +use crate::horizontal_space; + +/// A widget that is aware of its dimensions. +/// +/// A [`Responsive`] widget will always try to fill all the available space of +/// its parent. +#[allow(missing_debug_implementations)] +pub struct Responsive< + 'a, + Message, + Theme = crate::Theme, + Renderer = crate::Renderer, +> { + view: Box Element<'a, Message, Theme, Renderer> + 'a>, + width: Length, + height: Length, + content: Element<'a, Message, Theme, Renderer>, +} + +impl<'a, Message, Theme, Renderer> Responsive<'a, Message, Theme, Renderer> +where + Renderer: core::Renderer, +{ + /// Creates a new [`Responsive`] widget with a closure that produces its + /// contents. + /// + /// The `view` closure will receive the maximum available space for + /// the [`Responsive`] during layout. You can use this [`Size`] to + /// conditionally build the contents. + pub fn new( + view: impl Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a, + ) -> Self { + Self { + view: Box::new(view), + width: Length::Fill, + height: Length::Fill, + content: Element::new(horizontal_space().width(0)), + } + } + + /// Sets the width of the [`Responsive`]. + pub fn width(mut self, width: impl Into) -> Self { + self.width = width.into(); + self + } + + /// Sets the height of the [`Responsive`]. + pub fn height(mut self, height: impl Into) -> Self { + self.height = height.into(); + self + } +} + +impl Widget + for Responsive<'_, Message, Theme, Renderer> +where + Renderer: core::Renderer, +{ + fn diff(&self, _tree: &mut Tree) { + // Diff is deferred to layout + } + + fn size(&self) -> Size { + Size { + width: self.width, + height: self.height, + } + } + + fn layout( + &mut self, + tree: &mut Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + let limits = limits.width(self.width).height(self.height); + let size = limits.max(); + + self.content = (self.view)(size); + tree.diff_children(std::slice::from_ref(&self.content)); + + let node = self.content.as_widget_mut().layout( + &mut tree.children[0], + renderer, + &limits.loose(), + ); + + let size = limits.resolve(self.width, self.height, node.size()); + + layout::Node::with_children(size, vec![node]) + } + + fn update( + &mut self, + tree: &mut Tree, + event: &Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) { + self.content.as_widget_mut().update( + &mut tree.children[0], + event, + layout.children().next().unwrap(), + cursor, + renderer, + clipboard, + shell, + viewport, + ); + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + self.content.as_widget().draw( + &tree.children[0], + renderer, + theme, + style, + layout.children().next().unwrap(), + cursor, + viewport, + ); + } + + fn mouse_interaction( + &self, + tree: &Tree, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + self.content.as_widget().mouse_interaction( + &tree.children[0], + layout.children().next().unwrap(), + cursor, + viewport, + renderer, + ) + } + + fn operate( + &mut self, + tree: &mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn widget::Operation, + ) { + self.content.as_widget_mut().operate( + &mut tree.children[0], + layout.children().next().unwrap(), + renderer, + operation, + ); + } + + fn overlay<'a>( + &'a mut self, + tree: &'a mut Tree, + layout: Layout<'a>, + renderer: &Renderer, + viewport: &Rectangle, + translation: Vector, + ) -> Option> { + self.content.as_widget_mut().overlay( + &mut tree.children[0], + layout.children().next().unwrap(), + renderer, + viewport, + translation, + ) + } +} + +impl<'a, Message, Theme, Renderer> + From> + for Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: 'a, + Renderer: core::Renderer + 'a, +{ + fn from(responsive: Responsive<'a, Message, Theme, Renderer>) -> Self { + Self::new(responsive) + } +} diff --git a/widget/src/row.rs b/widget/src/row.rs index 9baa3bbd..19c6666a 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -208,7 +208,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -222,13 +222,13 @@ where self.padding, self.spacing, self.align, - &self.children, + &mut self.children, &mut tree.children, ) } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -237,12 +237,12 @@ where operation.container(None, layout.bounds()); operation.traverse(&mut |operation| { self.children - .iter() + .iter_mut() .zip(&mut tree.children) .zip(layout.children()) .for_each(|((child, state), layout)| { child - .as_widget() + .as_widget_mut() .operate(state, layout, renderer, operation); }); }); @@ -408,7 +408,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -418,6 +418,7 @@ where .height(self.row.height) .shrink(self.row.padding); + let child_limits = limits.loose(); let spacing = self.row.spacing; let vertical_spacing = self.vertical_spacing.unwrap_or(spacing); let max_width = limits.max().width; @@ -450,11 +451,11 @@ where } }; - for (i, child) in self.row.children.iter().enumerate() { - let node = child.as_widget().layout( + for (i, child) in self.row.children.iter_mut().enumerate() { + let node = child.as_widget_mut().layout( &mut tree.children[i], renderer, - &limits, + &child_limits, ); let child_size = node.size(); @@ -529,7 +530,7 @@ where } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, diff --git a/widget/src/rule.rs b/widget/src/rule.rs index d70109bc..163d2757 100644 --- a/widget/src/rule.rs +++ b/widget/src/rule.rs @@ -112,7 +112,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index 07b56f5d..5320a9cc 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -109,22 +109,12 @@ where class: Theme::default(), last_status: None, } - .validate() + .enclose() } - fn validate(mut self) -> Self { + fn enclose(mut self) -> Self { let size_hint = self.content.as_widget().size_hint(); - debug_assert!( - self.direction.vertical().is_none() || !size_hint.height.is_fill(), - "scrollable content must not fill its vertical scrolling axis" - ); - - debug_assert!( - self.direction.horizontal().is_none() || !size_hint.width.is_fill(), - "scrollable content must not fill its horizontal scrolling axis" - ); - if self.direction.horizontal().is_none() { self.width = self.width.enclose(size_hint.width); } @@ -144,7 +134,7 @@ where /// Sets the [`Direction`] of the [`Scrollable`]. pub fn direction(mut self, direction: impl Into) -> Self { self.direction = direction.into(); - self.validate() + self.enclose() } /// Sets the [`widget::Id`] of the [`Scrollable`]. @@ -419,7 +409,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -435,23 +425,27 @@ where ..Padding::ZERO }, |limits| { - let child_limits = layout::Limits::new( + let is_horizontal = self.direction.horizontal().is_some(); + let is_vertical = self.direction.vertical().is_some(); + + let child_limits = layout::Limits::with_compression( limits.min(), Size::new( - if self.direction.horizontal().is_some() { + if is_horizontal { f32::INFINITY } else { limits.max().width }, - if self.direction.vertical().is_some() { + if is_vertical { f32::INFINITY } else { limits.max().height }, ), + Size::new(is_horizontal, is_vertical), ); - self.content.as_widget().layout( + self.content.as_widget_mut().layout( &mut tree.children[0], renderer, &child_limits, @@ -525,7 +519,7 @@ where } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -548,7 +542,7 @@ where ); operation.traverse(&mut |operation| { - self.content.as_widget().operate( + self.content.as_widget_mut().operate( &mut tree.children[0], layout.children().next().unwrap(), renderer, diff --git a/widget/src/sensor.rs b/widget/src/sensor.rs index da56e0c9..f18a1f1a 100644 --- a/widget/src/sensor.rs +++ b/widget/src/sensor.rs @@ -284,14 +284,16 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.content - .as_widget() - .layout(&mut tree.children[0], renderer, limits) + self.content.as_widget_mut().layout( + &mut tree.children[0], + renderer, + limits, + ) } fn draw( @@ -316,13 +318,13 @@ where } fn operate( - &self, + &mut self, tree: &mut Tree, layout: core::Layout<'_>, renderer: &Renderer, operation: &mut dyn widget::Operation, ) { - self.content.as_widget().operate( + self.content.as_widget_mut().operate( &mut tree.children[0], layout, renderer, diff --git a/widget/src/shader.rs b/widget/src/shader.rs index 6d532e59..cf165e59 100644 --- a/widget/src/shader.rs +++ b/widget/src/shader.rs @@ -77,7 +77,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/slider.rs b/widget/src/slider.rs index 74225436..db6bc175 100644 --- a/widget/src/slider.rs +++ b/widget/src/slider.rs @@ -234,7 +234,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/space.rs b/widget/src/space.rs index 35bb30c4..949ea3ce 100644 --- a/widget/src/space.rs +++ b/widget/src/space.rs @@ -65,7 +65,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/stack.rs b/widget/src/stack.rs index 28290fc2..5b6c8fab 100644 --- a/widget/src/stack.rs +++ b/widget/src/stack.rs @@ -23,6 +23,7 @@ pub struct Stack<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> width: Length, height: Length, children: Vec>, + clip: bool, } impl<'a, Message, Theme, Renderer> Stack<'a, Message, Theme, Renderer> @@ -62,6 +63,7 @@ where width: Length::Shrink, height: Length::Shrink, children, + clip: false, } } @@ -114,6 +116,16 @@ where ) -> Self { children.into_iter().fold(self, Self::push) } + + /// Sets whether the [`Stack`] should clip overflowing content. + /// + /// It has a slight performance overhead during presentation. + /// + /// By default, it is set to `false`. + pub fn clip(mut self, clip: bool) -> Self { + self.clip = clip; + self + } } impl Default for Stack<'_, Message, Renderer> @@ -146,7 +158,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -161,7 +173,7 @@ where )); } - let base = self.children[0].as_widget().layout( + let base = self.children[0].as_widget_mut().layout( &mut tree.children[0], renderer, &limits, @@ -171,18 +183,21 @@ where let limits = layout::Limits::new(Size::ZERO, size); let nodes = std::iter::once(base) - .chain(self.children[1..].iter().zip(&mut tree.children[1..]).map( - |(layer, tree)| { - layer.as_widget().layout(tree, renderer, &limits) - }, - )) + .chain( + self.children[1..] + .iter_mut() + .zip(&mut tree.children[1..]) + .map(|(layer, tree)| { + layer.as_widget_mut().layout(tree, renderer, &limits) + }), + ) .collect(); layout::Node::with_children(size, nodes) } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, @@ -191,12 +206,12 @@ where operation.container(None, layout.bounds()); operation.traverse(&mut |operation| { self.children - .iter() + .iter_mut() .zip(&mut tree.children) .zip(layout.children()) .for_each(|((child, state), layout)| { child - .as_widget() + .as_widget_mut() .operate(state, layout, renderer, operation); }); }); @@ -278,6 +293,12 @@ where viewport: &Rectangle, ) { if let Some(clipped_viewport) = layout.bounds().intersection(viewport) { + let viewport = if self.clip { + &clipped_viewport + } else { + viewport + }; + let layers_below = if cursor.is_over(layout.bounds()) { self.children .iter() @@ -313,26 +334,16 @@ where layout, cursor| { if i > 0 { - renderer.with_layer(clipped_viewport, |renderer| { + renderer.with_layer(*viewport, |renderer| { layer.as_widget().draw( - state, - renderer, - theme, - style, - layout, - cursor, - &clipped_viewport, + state, renderer, theme, style, layout, cursor, + viewport, ); }); } else { layer.as_widget().draw( - state, - renderer, - theme, - style, - layout, - cursor, - &clipped_viewport, + state, renderer, theme, style, layout, cursor, + viewport, ); } }; diff --git a/widget/src/svg.rs b/widget/src/svg.rs index 72ead4f9..ab69b2dc 100644 --- a/widget/src/svg.rs +++ b/widget/src/svg.rs @@ -162,7 +162,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/table.rs b/widget/src/table.rs index 03537f9a..06b47124 100644 --- a/widget/src/table.rs +++ b/widget/src/table.rs @@ -232,7 +232,7 @@ where } fn layout( - &self, + &mut self, tree: &mut widget::Tree, renderer: &Renderer, limits: &layout::Limits, @@ -265,7 +265,7 @@ where let mut y = self.padding_y; for (i, (cell, state)) in - self.cells.iter().zip(&mut tree.children).enumerate() + self.cells.iter_mut().zip(&mut tree.children).enumerate() { let row = i / columns; let column = i % columns; @@ -306,7 +306,7 @@ where ) .width(width); - let layout = cell.as_widget().layout(state, renderer, &limits); + let layout = cell.as_widget_mut().layout(state, renderer, &limits); let size = limits.resolve(width, Length::Shrink, layout.size()); metrics.columns[column] = metrics.columns[column].max(size.width); @@ -344,7 +344,7 @@ where let mut y = self.padding_y; for (i, (cell, state)) in - self.cells.iter().zip(&mut tree.children).enumerate() + self.cells.iter_mut().zip(&mut tree.children).enumerate() { let row = i / columns; let column = i % columns; @@ -396,7 +396,7 @@ where ) .width(width); - let layout = cell.as_widget().layout(state, renderer, &limits); + let layout = cell.as_widget_mut().layout(state, renderer, &limits); let size = limits.resolve( if let Length::Fixed(_) = width { width @@ -581,7 +581,7 @@ where } fn operate( - &self, + &mut self, tree: &mut widget::Tree, layout: Layout<'_>, renderer: &Renderer, @@ -589,11 +589,12 @@ where ) { for ((cell, state), layout) in self .cells - .iter() + .iter_mut() .zip(&mut tree.children) .zip(layout.children()) { - cell.as_widget().operate(state, layout, renderer, operation); + cell.as_widget_mut() + .operate(state, layout, renderer, operation); } } diff --git a/widget/src/text/rich.rs b/widget/src/text/rich.rs index 9e7eaddf..e11f406b 100644 --- a/widget/src/text/rich.rs +++ b/widget/src/text/rich.rs @@ -225,7 +225,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 3657c817..33666935 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -597,7 +597,7 @@ where } fn layout( - &self, + &mut self, tree: &mut widget::Tree, renderer: &Renderer, limits: &layout::Limits, @@ -1051,7 +1051,7 @@ where } fn operate( - &self, + &mut self, tree: &mut widget::Tree, layout: Layout<'_>, _renderer: &Renderer, diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index ea4de989..de47b145 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -295,7 +295,7 @@ where /// /// [`Renderer`]: text::Renderer pub fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -669,7 +669,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, @@ -678,7 +678,7 @@ where } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, _renderer: &Renderer, diff --git a/widget/src/themer.rs b/widget/src/themer.rs index 43de86f4..3b1dcd25 100644 --- a/widget/src/themer.rs +++ b/widget/src/themer.rs @@ -82,23 +82,23 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.content.as_widget().layout(tree, renderer, limits) + self.content.as_widget_mut().layout(tree, renderer, limits) } fn operate( - &self, + &mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn Operation, ) { self.content - .as_widget() + .as_widget_mut() .operate(tree, layout, renderer, operation); } diff --git a/widget/src/toggler.rs b/widget/src/toggler.rs index 13385317..0ad2d22c 100644 --- a/widget/src/toggler.rs +++ b/widget/src/toggler.rs @@ -270,7 +270,7 @@ where } fn layout( - &self, + &mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits, diff --git a/widget/src/tooltip.rs b/widget/src/tooltip.rs index 8ef6280c..4c2c1a2e 100644 --- a/widget/src/tooltip.rs +++ b/widget/src/tooltip.rs @@ -179,14 +179,16 @@ where } fn layout( - &self, + &mut self, tree: &mut widget::Tree, renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - self.content - .as_widget() - .layout(&mut tree.children[0], renderer, limits) + self.content.as_widget_mut().layout( + &mut tree.children[0], + renderer, + limits, + ) } fn update( @@ -202,6 +204,7 @@ where ) { let state = tree.state.downcast_mut::(); + let previous_state = *state; let was_idle = *state == State::Idle; *state = cursor @@ -214,7 +217,9 @@ where if was_idle != is_idle { shell.invalidate_layout(); shell.request_redraw(); - } else if !is_idle && self.position == Position::FollowCursor { + } else if self.position == Position::FollowCursor + && previous_state != *state + { shell.request_redraw(); } @@ -291,7 +296,7 @@ where let tooltip = if let State::Hovered { cursor_position } = *state { Some(overlay::Element::new(Box::new(Overlay { position: layout.position() + translation, - tooltip: &self.tooltip, + tooltip: &mut self.tooltip, state: children.next().unwrap(), cursor_position, content_bounds: layout.bounds(), @@ -363,7 +368,7 @@ where Renderer: text::Renderer, { position: Point, - tooltip: &'b Element<'a, Message, Theme, Renderer>, + tooltip: &'b mut Element<'a, Message, Theme, Renderer>, state: &'b mut widget::Tree, cursor_position: Point, content_bounds: Rectangle, @@ -383,7 +388,7 @@ where fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node { let viewport = Rectangle::with_size(bounds); - let tooltip_layout = self.tooltip.as_widget().layout( + let tooltip_layout = self.tooltip.as_widget_mut().layout( self.state, renderer, &layout::Limits::new( @@ -391,7 +396,7 @@ where if self.snap_within_viewport { viewport.size() } else { - Size::INFINITY + Size::INFINITE }, ) .shrink(Padding::new(self.padding)), @@ -505,7 +510,7 @@ where &defaults, layout.children().next().unwrap(), cursor_position, - &Rectangle::with_size(Size::INFINITY), + &Rectangle::with_size(Size::INFINITE), ); } } diff --git a/widget/src/vertical_slider.rs b/widget/src/vertical_slider.rs index c262eb8e..8e8ca1c0 100644 --- a/widget/src/vertical_slider.rs +++ b/widget/src/vertical_slider.rs @@ -238,7 +238,7 @@ where } fn layout( - &self, + &mut self, _tree: &mut Tree, _renderer: &Renderer, limits: &layout::Limits, diff --git a/winit/src/window.rs b/winit/src/window.rs index e9a058af..f2d12677 100644 --- a/winit/src/window.rs +++ b/winit/src/window.rs @@ -346,7 +346,7 @@ where self.content = Renderer::Paragraph::with_spans(Text { content: &spans, - bounds: Size::INFINITY, + bounds: Size::INFINITE, size: preedit .text_size .unwrap_or_else(|| renderer.default_size()),