diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1e988195..d24f4503 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macOS-latest] - rust: [stable, beta, "1.85"] + rust: [stable, beta, "1.88"] steps: - uses: hecrj/setup-rust-action@v2 with: @@ -23,5 +23,7 @@ jobs: sudo apt-get install -y libxkbcommon-dev libgtk-3-dev - name: Run tests run: | + cargo test --verbose --workspace cargo test --verbose --workspace -- --ignored + cargo test --verbose --workspace --all-features cargo test --verbose --workspace --all-features -- --ignored diff --git a/Cargo.lock b/Cargo.lock index 5aab4f9c..ecdcd3e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "ab_glyph" -version = "0.2.29" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3672c180e71eeaaac3a541fbbc5f5ad4def8b747c595ad30d674e43049f7b0" +checksum = "e074464580a518d16a7126262fffaaa47af89d4099d4cb403f8ed938ba12ee7d" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "ab_glyph_rasterizer" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" +checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "addr2line" @@ -29,9 +29,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "ahash" @@ -63,9 +63,12 @@ checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" [[package]] name = "aligned-vec" -version = "0.5.0" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] [[package]] name = "android-activity" @@ -83,7 +86,7 @@ dependencies = [ "log", "ndk", "ndk-context", - "ndk-sys 0.6.0+11769913", + "ndk-sys", "num_enum", "thiserror 1.0.69", ] @@ -123,9 +126,9 @@ checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "approx" @@ -216,11 +219,14 @@ dependencies = [ "enumflags2", "futures-channel", "futures-util", - "rand 0.9.1", + "rand 0.9.2", "raw-window-handle 0.6.2", "serde", "serde_repr", "url", + "wayland-backend", + "wayland-client", + "wayland-protocols", "zbus", ] @@ -230,7 +236,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" dependencies = [ - "event-listener 5.4.0", + "event-listener 5.4.1", "event-listener-strategy", "futures-core", "pin-project-lite", @@ -249,9 +255,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" dependencies = [ "concurrent-queue", "event-listener-strategy", @@ -275,9 +281,9 @@ dependencies = [ [[package]] name = "async-fs" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +checksum = "09f7e37c0ed80b2a977691c47dae8625cfb21e205827106c64f7c588766b2e50" dependencies = [ "async-lock", "blocking", @@ -290,7 +296,7 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-executor", "async-io", "async-lock", @@ -301,9 +307,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3" +checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" dependencies = [ "async-lock", "cfg-if", @@ -312,19 +318,18 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix 1.0.7", + "rustix 1.0.8", "slab", - "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "async-lock" -version = "3.4.0" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" dependencies = [ - "event-listener 5.4.0", + "event-listener 5.4.1", "event-listener-strategy", "pin-project-lite", ] @@ -342,21 +347,20 @@ dependencies = [ [[package]] name = "async-process" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc" +checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-io", "async-lock", "async-signal", "async-task", "blocking", "cfg-if", - "event-listener 5.4.0", + "event-listener 5.4.1", "futures-lite", - "rustix 1.0.7", - "tracing", + "rustix 1.0.8", ] [[package]] @@ -372,9 +376,9 @@ dependencies = [ [[package]] name = "async-signal" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7605a4e50d4b06df3898d5a70bf5fde51ed9059b0434b73105193bc27acce0d" +checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1" dependencies = [ "async-io", "async-lock", @@ -382,10 +386,10 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 1.0.7", + "rustix 1.0.8", "signal-hook-registry", "slab", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -468,9 +472,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "av1-grain" @@ -488,9 +492,9 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98922d6a4cfbcb08820c69d8eeccc05bb1f29bfa06b4f5b1dbfe9a868bd7608e" +checksum = "2ea8ef51aced2b9191c08197f55450d830876d9933f8f48a429b354f1d496b42" dependencies = [ "arrayvec", ] @@ -635,11 +639,11 @@ dependencies = [ [[package]] name = "blocking" -version = "1.6.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-task", "futures-io", "futures-lite", @@ -660,9 +664,9 @@ checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "by_address" @@ -672,18 +676,18 @@ checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" [[package]] name = "bytemuck" -version = "1.23.0" +version = "1.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" +checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.9.3" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" +checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" dependencies = [ "proc-macro2", "quote", @@ -769,9 +773,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.25" +version = "1.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951" +checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" dependencies = [ "jobserver", "libc", @@ -796,9 +800,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "cfg_aliases" @@ -814,7 +818,7 @@ dependencies = [ "log", "reqwest", "serde", - "thiserror 2.0.12", + "thiserror 2.0.14", "tokio", "tracing-subscriber", "webbrowser", @@ -870,18 +874,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.39" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.39" +version = "4.5.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" dependencies = [ "anstyle", "clap_lex", @@ -889,15 +893,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "clipboard-win" -version = "5.4.0" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" dependencies = [ "error-code", ] @@ -943,10 +947,11 @@ dependencies = [ [[package]] name = "codespan-reporting" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ + "serde", "termcolor", "unicode-width", ] @@ -1127,9 +1132,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -1197,14 +1202,14 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "cryoglyph" version = "0.1.0" -source = "git+https://github.com/iced-rs/cryoglyph.git?rev=a456d1c17bbcf33afcca41d9e5e299f9f1193819#a456d1c17bbcf33afcca41d9e5e299f9f1193819" +source = "git+https://github.com/iced-rs/cryoglyph.git?rev=453cedec0d2ec563bd7fa87e84a2319bcebb1ba3#453cedec0d2ec563bd7fa87e84a2319bcebb1ba3" dependencies = [ "cosmic-text", "etagere", @@ -1323,7 +1328,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1332,18 +1337,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" -[[package]] -name = "dispatch2" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a0d569e003ff27784e0e14e4a594048698e0c0f0b66cabcb51511be55a7caa0" -dependencies = [ - "bitflags 2.9.1", - "block2 0.6.1", - "libc", - "objc2 0.6.1", -] - [[package]] name = "dispatch2" version = "0.3.0" @@ -1351,6 +1344,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ "bitflags 2.9.1", + "block2 0.6.1", + "libc", "objc2 0.6.1", ] @@ -1473,9 +1468,9 @@ checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" [[package]] name = "enumflags2" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" dependencies = [ "enumflags2_derive", "serde", @@ -1483,9 +1478,29 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", @@ -1500,12 +1515,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1541,9 +1556,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "5.4.0" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" dependencies = [ "concurrent-queue", "parking", @@ -1556,7 +1571,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.4.0", + "event-listener 5.4.1", "pin-project-lite", ] @@ -1619,9 +1634,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", "miniz_oxide", @@ -1799,9 +1814,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ "fastrand", "futures-core", @@ -1935,9 +1950,9 @@ dependencies = [ [[package]] name = "getopts" -version = "0.2.21" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1" dependencies = [ "unicode-width", ] @@ -1951,7 +1966,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -1969,9 +1984,9 @@ dependencies = [ [[package]] name = "gif" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" dependencies = [ "color_quant", "weezl", @@ -2159,9 +2174,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" dependencies = [ "bytes", "fnv", @@ -2178,9 +2193,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", @@ -2203,13 +2218,14 @@ checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", + "num-traits", ] [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "foldhash", ] @@ -2252,9 +2268,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -2268,15 +2284,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" -[[package]] -name = "home" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "http" version = "0.2.12" @@ -2355,14 +2362,14 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.3.26", + "h2 0.3.27", "http 0.2.12", "http-body 0.4.6", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -2378,7 +2385,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.10", + "h2 0.4.12", "http 1.3.1", "http-body 1.0.1", "httparse", @@ -2391,14 +2398,14 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.6" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a01595e11bdcec50946522c32dde3fc6914743000a68b93000965f2f02406d" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ "http 1.3.1", "hyper 1.6.0", "hyper-util", - "rustls 0.23.27", + "rustls 0.23.31", "rustls-pki-types", "tokio", "tokio-rustls 0.26.2", @@ -2423,9 +2430,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ "base64 0.22.1", "bytes", @@ -2439,7 +2446,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.6.0", "system-configuration", "tokio", "tower-service", @@ -2488,7 +2495,7 @@ dependencies = [ "iced_widget", "iced_winit", "image", - "thiserror 2.0.12", + "thiserror 2.0.14", ] [[package]] @@ -2501,7 +2508,7 @@ dependencies = [ "log", "semver", "serde", - "thiserror 2.0.12", + "thiserror 2.0.14", "tokio", ] @@ -2519,7 +2526,7 @@ dependencies = [ "rustc-hash 2.1.1", "serde", "smol_str", - "thiserror 2.0.12", + "thiserror 2.0.14", "web-time", ] @@ -2543,7 +2550,7 @@ dependencies = [ "iced_test", "iced_widget", "log", - "rfd 0.15.3", + "rfd 0.15.4", ] [[package]] @@ -2576,7 +2583,7 @@ dependencies = [ "lyon_path", "raw-window-handle 0.6.2", "rustc-hash 2.1.1", - "thiserror 2.0.12", + "thiserror 2.0.14", "unicode-segmentation", ] @@ -2604,7 +2611,7 @@ dependencies = [ "iced_tiny_skia", "iced_wgpu", "log", - "thiserror 2.0.12", + "thiserror 2.0.14", ] [[package]] @@ -2617,7 +2624,7 @@ dependencies = [ "iced_futures", "raw-window-handle 0.6.2", "sipper", - "thiserror 2.0.12", + "thiserror 2.0.14", ] [[package]] @@ -2630,7 +2637,7 @@ dependencies = [ "nom 8.0.0", "png", "sha2", - "thiserror 2.0.12", + "thiserror 2.0.14", ] [[package]] @@ -2665,7 +2672,7 @@ dependencies = [ "lyon", "resvg", "rustc-hash 2.1.1", - "thiserror 2.0.12", + "thiserror 2.0.14", "wgpu", ] @@ -2682,7 +2689,7 @@ dependencies = [ "pulldown-cmark", "qrcode", "rustc-hash 2.1.1", - "thiserror 2.0.12", + "thiserror 2.0.14", "unicode-segmentation", "url", ] @@ -2696,7 +2703,7 @@ dependencies = [ "log", "rustc-hash 2.1.1", "sysinfo", - "thiserror 2.0.12", + "thiserror 2.0.14", "tracing", "wasm-bindgen-futures", "web-sys", @@ -2836,9 +2843,9 @@ dependencies = [ [[package]] name = "image-webp" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f" +checksum = "f6970fe7a5300b4b42e62c52efa0187540a5bef546c60edaf554ef595d2e6f0b" dependencies = [ "byteorder-lite", "quick-error", @@ -2858,9 +2865,9 @@ checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown", @@ -2892,6 +2899,17 @@ dependencies = [ "syn", ] +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + [[package]] name = "ipnet" version = "2.11.0" @@ -2996,9 +3014,9 @@ dependencies = [ [[package]] name = "jpeg-decoder" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" +checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" [[package]] name = "js-sys" @@ -3048,11 +3066,12 @@ dependencies = [ [[package]] name = "kurbo" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1077d333efea6170d9ccb96d3c3026f300ca0773da4938cc4c811daa6df68b0c" +checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" dependencies = [ "arrayvec", + "euclid", "smallvec", ] @@ -3093,15 +3112,15 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libfuzzer-sys" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75" +checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" dependencies = [ "arbitrary", "cc", @@ -3114,7 +3133,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.53.0", + "windows-targets 0.53.3", ] [[package]] @@ -3125,13 +3144,13 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" dependencies = [ "bitflags 2.9.1", "libc", - "redox_syscall 0.5.12", + "redox_syscall 0.5.17", ] [[package]] @@ -3175,9 +3194,9 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "litrs" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" +checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" [[package]] name = "loading_spinners" @@ -3224,9 +3243,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.12.5" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +checksum = "86ea4e65087ff52f3862caff188d489f1fab49a0cb09e01b2e3f1a617b10aaed" [[package]] name = "lyon" @@ -3321,9 +3340,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memfd" @@ -3336,9 +3355,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.9.5" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +checksum = "483758ad303d734cec05e5c12b41d7e93e6a6390c5e9dae6bdeb7c1259012d28" dependencies = [ "libc", ] @@ -3354,13 +3373,13 @@ dependencies = [ [[package]] name = "metal" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e" +checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605" dependencies = [ "bitflags 2.9.1", "block", - "core-graphics-types 0.1.3", + "core-graphics-types 0.2.0", "foreign-types 0.5.0", "log", "objc", @@ -3391,9 +3410,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", "simd-adler32", @@ -3406,7 +3425,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.59.0", ] @@ -3459,24 +3478,28 @@ checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b" [[package]] name = "naga" -version = "24.0.0" +version = "26.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e380993072e52eef724eddfcde0ed013b0c023c3f0417336ed041aa9f076994e" +checksum = "916cbc7cb27db60be930a4e2da243cf4bc39569195f22fd8ee419cd31d5b662c" dependencies = [ "arrayvec", "bit-set", "bitflags 2.9.1", + "cfg-if", "cfg_aliases", "codespan-reporting", + "half", + "hashbrown", "hexf-parse", "indexmap", + "libm", "log", + "num-traits", + "once_cell", "rustc-hash 1.1.0", "spirv", - "strum", - "termcolor", - "thiserror 2.0.12", - "unicode-xid", + "thiserror 2.0.14", + "unicode-ident", ] [[package]] @@ -3505,7 +3528,7 @@ dependencies = [ "bitflags 2.9.1", "jni-sys", "log", - "ndk-sys 0.6.0+11769913", + "ndk-sys", "num_enum", "raw-window-handle 0.6.2", "thiserror 1.0.69", @@ -3517,15 +3540,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" -[[package]] -name = "ndk-sys" -version = "0.5.0+25.2.9519653" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" -dependencies = [ - "jni-sys", -] - [[package]] name = "ndk-sys" version = "0.6.0+11769913" @@ -3667,18 +3681,19 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" dependencies = [ "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -3802,7 +3817,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ "bitflags 2.9.1", - "dispatch2 0.3.0", + "dispatch2", "objc2 0.6.1", ] @@ -4076,9 +4091,9 @@ dependencies = [ [[package]] name = "ordered-float" -version = "4.6.0" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +checksum = "e2c1f9f56e534ac6a9b8a4600bdf0f530fb393b5f393e7b4d03489c3cf0c3f01" dependencies = [ "num-traits", ] @@ -4125,9 +4140,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "owned_ttf_parser" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4" +checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" dependencies = [ "ttf-parser 0.25.1", ] @@ -4199,7 +4214,7 @@ checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.12", + "redox_syscall 0.5.17", "smallvec", "windows-targets 0.52.6", ] @@ -4328,13 +4343,13 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plist" -version = "1.7.1" +version = "1.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d" +checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" dependencies = [ "base64 0.22.1", "indexmap", - "quick-xml 0.32.0", + "quick-xml 0.38.1", "serde", "time", ] @@ -4394,17 +4409,16 @@ dependencies = [ [[package]] name = "polling" -version = "3.8.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" +checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 1.0.7", - "tracing", - "windows-sys 0.59.0", + "rustix 1.0.8", + "windows-sys 0.60.2", ] [[package]] @@ -4413,6 +4427,21 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "potential_utf" version = "0.1.2" @@ -4454,9 +4483,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "d61789d7719defeb74ea5fe81f2fdfdbd28a803847077cecce2ff14e1472f6f1" dependencies = [ "unicode-ident", ] @@ -4476,18 +4505,18 @@ dependencies = [ [[package]] name = "profiling" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" dependencies = [ "profiling-procmacros", ] [[package]] name = "profiling-procmacros" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" +checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" dependencies = [ "quote", "syn", @@ -4549,18 +4578,18 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.32.0" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" dependencies = [ "memchr", ] [[package]] name = "quick-xml" -version = "0.37.5" +version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +checksum = "9845d9dccf565065824e69f9f235fafba1587031eda353c1f1561cd6a6be78f4" dependencies = [ "memchr", ] @@ -4576,9 +4605,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" @@ -4593,9 +4622,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -4647,9 +4676,9 @@ checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" [[package]] name = "rangemap" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" +checksum = "f93e7e49bb0bf967717f7bd674458b3d6b0c5f48ec7e3038166026a69fc22223" [[package]] name = "rav1e" @@ -4688,9 +4717,9 @@ dependencies = [ [[package]] name = "ravif" -version = "0.11.12" +version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6a5f31fcf7500f9401fea858ea4ab5525c99f2322cfcee732c0e6c74208c0c6" +checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b" dependencies = [ "avif-serialize", "imgref", @@ -4754,22 +4783,22 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ "bitflags 2.9.1", ] [[package]] name = "redox_users" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.12", + "thiserror 2.0.14", ] [[package]] @@ -4809,16 +4838,16 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "reqwest" -version = "0.12.19" +version = "0.12.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f8e5513d63f2e5b386eb5106dc67eaf3f84e95258e210489136b8b92ad6119" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2 0.4.10", + "h2 0.4.12", "http 1.3.1", "http-body 1.0.1", "http-body-util", @@ -4826,12 +4855,10 @@ dependencies = [ "hyper-rustls", "hyper-tls", "hyper-util", - "ipnet", "js-sys", "log", "mime", "native-tls", - "once_cell", "percent-encoding", "pin-project-lite", "rustls-pki-types", @@ -4893,13 +4920,13 @@ dependencies = [ [[package]] name = "rfd" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80c844748fdc82aae252ee4594a89b6e7ebef1063de7951545564cbc4e57075d" +checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed" dependencies = [ "ashpd 0.11.0", "block2 0.6.1", - "dispatch2 0.2.0", + "dispatch2", "js-sys", "log", "objc2 0.6.1", @@ -4917,9 +4944,9 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.50" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" dependencies = [ "bytemuck", ] @@ -4946,9 +4973,9 @@ checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -4977,15 +5004,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ "bitflags 2.9.1", "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -5004,13 +5031,13 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.27" +version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ "once_cell", "rustls-pki-types", - "rustls-webpki 0.103.3", + "rustls-webpki 0.103.4", "subtle", "zeroize", ] @@ -5037,9 +5064,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.3" +version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "ring", "rustls-pki-types", @@ -5048,9 +5075,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rustybuzz" @@ -5195,9 +5222,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" dependencies = [ "itoa", "memchr", @@ -5218,9 +5245,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] @@ -5284,9 +5311,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -5343,12 +5370,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "slider" @@ -5368,9 +5392,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smithay-client-toolkit" @@ -5414,7 +5438,7 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a33bd3e260892199c3ccfc487c88b2da2265080acb316cd920da72fdfd7c599f" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-executor", "async-fs", "async-io", @@ -5444,6 +5468,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "softbuffer" version = "0.4.6" @@ -5464,7 +5498,7 @@ dependencies = [ "objc2-foundation 0.2.2", "objc2-quartz-core", "raw-window-handle 0.6.2", - "redox_syscall 0.5.12", + "redox_syscall 0.5.17", "rustix 0.38.44", "tiny-xlib", "wasm-bindgen", @@ -5528,28 +5562,6 @@ dependencies = [ "float-cmp", ] -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "rustversion", - "syn", -] - [[package]] name = "styling" version = "0.1.0" @@ -5561,9 +5573,9 @@ dependencies = [ [[package]] name = "subsecond" -version = "0.7.0-alpha.1" +version = "0.7.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c5b40acd555d02d9a0b5bf4080dbf2cd085d5e2eb2ae7851cb14b9bf5af15c" +checksum = "b14ed4d86ab065ffbfdb994fd3e44daf5244b02cb643bd52949d74b703f36605" dependencies = [ "js-sys", "libc", @@ -5572,7 +5584,7 @@ dependencies = [ "memmap2", "serde", "subsecond-types", - "thiserror 2.0.12", + "thiserror 2.0.14", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -5580,9 +5592,9 @@ dependencies = [ [[package]] name = "subsecond-types" -version = "0.7.0-alpha.1" +version = "0.7.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bedadae58a56e137ac970c38c44bff38cee24400fef64c37d5a188a065b1ec1f" +checksum = "275920a8a5634e47e12253971db85946798795bbe4d9dfc1debf23533d823983" dependencies = [ "serde", ] @@ -5612,7 +5624,7 @@ version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" dependencies = [ - "kurbo 0.11.2", + "kurbo 0.11.3", "siphasher", ] @@ -5629,9 +5641,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.101" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -5745,6 +5757,13 @@ dependencies = [ "iced", ] +[[package]] +name = "table" +version = "0.1.0" +dependencies = [ + "iced", +] + [[package]] name = "target-lexicon" version = "0.12.16" @@ -5760,7 +5779,7 @@ dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.7", + "rustix 1.0.8", "windows-sys 0.59.0", ] @@ -5793,11 +5812,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.14", ] [[package]] @@ -5813,9 +5832,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227" dependencies = [ "proc-macro2", "quote", @@ -5824,12 +5843,11 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] @@ -5973,19 +5991,21 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.1" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "pin-project-lite", "signal-hook-registry", - "socket2", + "slab", + "socket2 0.6.0", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6026,7 +6046,7 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ - "rustls 0.23.27", + "rustls 0.23.31", "tokio", ] @@ -6044,9 +6064,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", @@ -6057,9 +6077,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", @@ -6069,18 +6089,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.26" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", @@ -6166,9 +6186,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", @@ -6177,9 +6197,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -6334,15 +6354,9 @@ checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" [[package]] name = "unicode-width" -version = "0.1.14" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" [[package]] name = "untrusted" @@ -6392,7 +6406,7 @@ dependencies = [ "flate2", "fontdb 0.18.0", "imagesize", - "kurbo 0.11.2", + "kurbo 0.11.3", "log", "pico-args", "roxmltree", @@ -6422,22 +6436,22 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" dependencies = [ "getrandom 0.3.3", "js-sys", - "rand 0.9.1", + "rand 0.9.2", "serde", "wasm-bindgen", ] [[package]] name = "v_frame" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" dependencies = [ "aligned-vec", "num-traits", @@ -6553,9 +6567,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -6652,9 +6666,9 @@ dependencies = [ [[package]] name = "wasmtimer" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0048ad49a55b9deb3953841fa1fc5858f0efbcb7a18868c899a360269fac1b23" +checksum = "d8d49b5d6c64e8558d9b1b065014426f35c18de636895d24893dbbd329743446" dependencies = [ "futures", "js-sys", @@ -6666,13 +6680,13 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" +checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" dependencies = [ "cc", "downcast-rs", - "rustix 0.38.44", + "rustix 1.0.8", "scoped-tls", "smallvec", "wayland-sys", @@ -6680,12 +6694,12 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.10" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" +checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" dependencies = [ "bitflags 2.9.1", - "rustix 0.38.44", + "rustix 1.0.8", "wayland-backend", "wayland-scanner", ] @@ -6703,20 +6717,20 @@ dependencies = [ [[package]] name = "wayland-cursor" -version = "0.31.10" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a65317158dec28d00416cb16705934070aef4f8393353d41126c54264ae0f182" +checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29" dependencies = [ - "rustix 0.38.44", + "rustix 1.0.8", "wayland-client", "xcursor", ] [[package]] name = "wayland-protocols" -version = "0.32.8" +version = "0.32.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" +checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" dependencies = [ "bitflags 2.9.1", "wayland-backend", @@ -6726,9 +6740,9 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fd38cdad69b56ace413c6bcc1fbf5acc5e2ef4af9d5f8f1f9570c0c83eae175" +checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032" dependencies = [ "bitflags 2.9.1", "wayland-backend", @@ -6739,9 +6753,9 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf" +checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" dependencies = [ "bitflags 2.9.1", "wayland-backend", @@ -6752,9 +6766,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" +checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" dependencies = [ "proc-macro2", "quick-xml 0.37.5", @@ -6763,9 +6777,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" +checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" dependencies = [ "dlib", "log", @@ -6795,12 +6809,11 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5df295f8451142f1856b1bd86a606dfe9587d439bc036e319c827700dbd555e" +checksum = "aaf4f3c0ba838e82b4e5ccc4157003fb8c324ee24c058470ffb82820becbde98" dependencies = [ "core-foundation 0.10.1", - "home", "jni", "log", "ndk-context", @@ -6816,14 +6829,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.0", + "webpki-roots 1.0.2", ] [[package]] name = "webpki-roots" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" dependencies = [ "rustls-pki-types", ] @@ -6846,18 +6859,21 @@ checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" [[package]] name = "wgpu" -version = "24.0.5" +version = "26.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b0b3436f0729f6cdf2e6e9201f3d39dc95813fad61d826c1ed07918b4539353" +checksum = "70b6ff82bbf6e9206828e1a3178e851f8c20f1c9028e74dd3a8090741ccd5798" dependencies = [ "arrayvec", "bitflags 2.9.1", + "cfg-if", "cfg_aliases", "document-features", + "hashbrown", "js-sys", "log", "naga", "parking_lot", + "portable-atomic", "profiling", "raw-window-handle 0.6.2", "smallvec", @@ -6872,34 +6888,77 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "24.0.5" +version = "26.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f0aa306497a238d169b9dc70659105b4a096859a34894544ca81719242e1499" +checksum = "d5f62f1053bd28c2268f42916f31588f81f64796e2ff91b81293515017ca8bd9" dependencies = [ "arrayvec", + "bit-set", "bit-vec", "bitflags 2.9.1", "cfg_aliases", "document-features", + "hashbrown", "indexmap", "log", "naga", "once_cell", "parking_lot", + "portable-atomic", "profiling", "raw-window-handle 0.6.2", "rustc-hash 1.1.0", "smallvec", - "thiserror 2.0.12", + "thiserror 2.0.14", + "wgpu-core-deps-apple", + "wgpu-core-deps-emscripten", + "wgpu-core-deps-wasm", + "wgpu-core-deps-windows-linux-android", "wgpu-hal", "wgpu-types", ] [[package]] -name = "wgpu-hal" -version = "24.0.4" +name = "wgpu-core-deps-apple" +version = "26.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f112f464674ca69f3533248508ee30cb84c67cf06c25ff6800685f5e0294e259" +checksum = "18ae5fbde6a4cbebae38358aa73fcd6e0f15c6144b67ef5dc91ded0db125dbdf" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-emscripten" +version = "26.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7670e390f416006f746b4600fdd9136455e3627f5bd763abf9a65daa216dd2d" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-wasm" +version = "26.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03b9f9e1a50686d315fc6debe4980cc45cd37b0e919351917df494e8fdc8885" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-windows-linux-android" +version = "26.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "720a5cb9d12b3d337c15ff0e24d3e97ed11490ff3f7506e7f3d98c68fa5d6f14" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-hal" +version = "26.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df2c64ac282a91ad7662c90bc4a77d4a2135bc0b2a2da5a4d4e267afc034b9e" dependencies = [ "android_system_properties", "arrayvec", @@ -6908,13 +6967,15 @@ dependencies = [ "bitflags 2.9.1", "block", "bytemuck", + "cfg-if", "cfg_aliases", - "core-graphics-types 0.1.3", + "core-graphics-types 0.2.0", "glow", "glutin_wgl_sys", "gpu-alloc", "gpu-allocator", "gpu-descriptor", + "hashbrown", "js-sys", "khronos-egl", "libc", @@ -6922,18 +6983,18 @@ dependencies = [ "log", "metal", "naga", - "ndk-sys 0.5.0+25.2.9519653", + "ndk-sys", "objc", - "once_cell", "ordered-float", "parking_lot", + "portable-atomic", + "portable-atomic-util", "profiling", "range-alloc", "raw-window-handle 0.6.2", "renderdoc-sys", - "rustc-hash 1.1.0", "smallvec", - "thiserror 2.0.12", + "thiserror 2.0.14", "wasm-bindgen", "web-sys", "wgpu-types", @@ -6943,13 +7004,15 @@ dependencies = [ [[package]] name = "wgpu-types" -version = "24.0.0" +version = "26.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50ac044c0e76c03a0378e7786ac505d010a873665e2d51383dcff8dd227dc69c" +checksum = "eca7a8d8af57c18f57d393601a1fb159ace8b2328f1b6b5f80893f7d672c9ae2" dependencies = [ "bitflags 2.9.1", + "bytemuck", "js-sys", "log", + "thiserror 2.0.14", "web-sys", ] @@ -7124,15 +7187,15 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-registry" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bab093bdd303a1240bb99b8aba8ea8a69ee19d34c9e2ef9594e708a4878820" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" dependencies = [ "windows-link", "windows-result 0.3.4", @@ -7221,6 +7284,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -7269,10 +7341,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.0" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ + "windows-link", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -7516,9 +7589,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.10" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] @@ -7582,9 +7655,9 @@ checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" [[package]] name = "xcursor" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" +checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" [[package]] name = "xkbcommon-dl" @@ -7607,9 +7680,9 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" +checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" [[package]] name = "xmlwriter" @@ -7664,9 +7737,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.7.1" +version = "5.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a7c7cee313d044fca3f48fa782cb750c79e4ca76ba7bc7718cd4024cdf6f68" +checksum = "4bb4f9a464286d42851d18a605f7193b8febaf5b0919d71c6399b7b26e5b0aad" dependencies = [ "async-broadcast", "async-executor", @@ -7678,7 +7751,7 @@ dependencies = [ "async-trait", "blocking", "enumflags2", - "event-listener 5.4.0", + "event-listener 5.4.1", "futures-core", "futures-lite", "hex", @@ -7697,9 +7770,9 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.7.1" +version = "5.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17e7e5eec1550f747e71a058df81a9a83813ba0f6a95f39c4e218bdc7ba366a" +checksum = "ef9859f68ee0c4ee2e8cde84737c78e3f4c54f946f2a38645d0d4c7a95327659" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -7730,18 +7803,18 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", @@ -7788,9 +7861,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", @@ -7825,18 +7898,18 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.4.16" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4a518c0ea2576f4da876349d7f67a7be489297cd77c2cf9e04c2e05fcd3974" +checksum = "fc1f7e205ce79eb2da3cd71c5f55f3589785cb7c79f6a03d1c8d1491bda5d089" dependencies = [ "zune-core", ] [[package]] name = "zvariant" -version = "5.5.3" +version = "5.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d30786f75e393ee63a21de4f9074d4c038d52c5b1bb4471f955db249f9dffb1" +checksum = "d91b3680bb339216abd84714172b5138a4edac677e641ef17e1d8cb1b3ca6e6f" dependencies = [ "endi", "enumflags2", @@ -7849,9 +7922,9 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "5.5.3" +version = "5.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75fda702cd42d735ccd48117b1630432219c0e9616bf6cb0f8350844ee4d9580" +checksum = "3a8c68501be459a8dbfffbe5d792acdd23b4959940fc87785fb013b32edbc208" dependencies = [ "proc-macro-crate", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index ce426151..de46980a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ all-features = true maintenance = { status = "actively-developed" } [features] -default = ["wgpu", "tiny-skia", "web-colors", "auto-detect-theme", "thread-pool"] +default = ["wgpu", "tiny-skia", "crisp", "web-colors", "auto-detect-theme", "thread-pool"] # Enables the `wgpu` GPU-accelerated renderer backend wgpu = ["iced_renderer/wgpu", "iced_widget/wgpu"] # Enables the `tiny-skia` software renderer backend @@ -149,7 +149,7 @@ repository = "https://github.com/iced-rs/iced" homepage = "https://iced.rs" categories = ["gui"] keywords = ["gui", "ui", "graphics", "interface", "widgets"] -rust-version = "1.85" +rust-version = "1.88" [workspace.dependencies] iced = { version = "0.14.0-dev", path = "." } @@ -178,7 +178,7 @@ cosmic-text = "0.14" dark-light = "2.0" futures = { version = "0.3", default-features = false } glam = "0.25" -cryoglyph = { git = "https://github.com/iced-rs/cryoglyph.git", rev = "a456d1c17bbcf33afcca41d9e5e299f9f1193819" } +cryoglyph = { git = "https://github.com/iced-rs/cryoglyph.git", rev = "453cedec0d2ec563bd7fa87e84a2319bcebb1ba3" } guillotiere = "0.6" half = "2.2" image = { version = "0.25", default-features = false } @@ -217,7 +217,7 @@ wasm-bindgen-futures = "0.4" wasmtimer = "0.4.1" web-sys = "0.3.69" web-time = "1.1" -wgpu = "24.0" +wgpu = "26.0" window_clipboard = "0.4.1" winit = { git = "https://github.com/iced-rs/winit.git", rev = "11414b6aa45699f038114e61b4ddf5102b2d3b4b" } diff --git a/benches/wgpu.rs b/benches/wgpu.rs index c8792b7a..35696abc 100644 --- a/benches/wgpu.rs +++ b/benches/wgpu.rs @@ -32,16 +32,15 @@ pub fn wgpu_benchmark(c: &mut Criterion) { )) .expect("request adapter"); - let (device, queue) = executor::block_on(adapter.request_device( - &wgpu::DeviceDescriptor { + let (device, queue) = + executor::block_on(adapter.request_device(&wgpu::DeviceDescriptor { label: None, required_features: wgpu::Features::empty(), required_limits: wgpu::Limits::default(), memory_hints: wgpu::MemoryHints::MemoryUsage, - }, - None, - )) - .expect("request device"); + trace: wgpu::Trace::Off, + })) + .expect("request device"); c.bench_function("wgpu — canvas (light)", |b| { benchmark(b, &adapter, &device, &queue, |_| scene(10)); @@ -140,7 +139,7 @@ fn benchmark<'a>( &viewport, ); - let _ = device.poll(wgpu::Maintain::WaitForSubmissionIndex(submission)); + let _ = device.poll(wgpu::PollType::WaitForSubmissionIndex(submission)); i += 1; }); diff --git a/core/src/color.rs b/core/src/color.rs index a280c795..ad60592a 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -90,12 +90,12 @@ impl Color { } } - Self { - r: gamma_component(r), - g: gamma_component(g), - b: gamma_component(b), + Self::new( + gamma_component(r), + gamma_component(g), + gamma_component(b), a, - } + ) } /// Parses a [`Color`] from a hex string. @@ -195,6 +195,13 @@ impl Color { ..self } } + + /// 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] + } } impl From<[f32; 3]> for Color { diff --git a/core/src/element.rs b/core/src/element.rs index 9d083d79..a3f60127 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -532,3 +532,49 @@ where ) } } + +impl<'a, T, Message, Theme, Renderer> From> + for Element<'a, Message, Theme, Renderer> +where + T: Into, + Renderer: crate::Renderer, +{ + fn from(element: Option) -> Self { + struct Void; + + impl Widget for Void + where + Renderer: crate::Renderer, + { + fn size(&self) -> Size { + Size { + width: Length::Fixed(0.0), + height: Length::Fixed(0.0), + } + } + + fn layout( + &self, + _tree: &mut Tree, + _renderer: &Renderer, + _limits: &layout::Limits, + ) -> layout::Node { + layout::Node::new(Size::ZERO) + } + + fn draw( + &self, + _tree: &Tree, + _renderer: &mut Renderer, + _theme: &Theme, + _style: &renderer::Style, + _layout: Layout<'_>, + _cursor: mouse::Cursor, + _viewport: &Rectangle, + ) { + } + } + + element.map(T::into).unwrap_or_else(|| Element::new(Void)) + } +} diff --git a/core/src/layout/node.rs b/core/src/layout/node.rs index 0c0f90fb..6fb5d395 100644 --- a/core/src/layout/node.rs +++ b/core/src/layout/node.rs @@ -52,22 +52,22 @@ impl Node { /// Aligns the [`Node`] in the given space. pub fn align( mut self, - horizontal_alignment: Alignment, - vertical_alignment: Alignment, + align_x: Alignment, + align_y: Alignment, space: Size, ) -> Self { - self.align_mut(horizontal_alignment, vertical_alignment, space); + self.align_mut(align_x, align_y, space); self } /// Mutable reference version of [`Self::align`]. pub fn align_mut( &mut self, - horizontal_alignment: Alignment, - vertical_alignment: Alignment, + align_x: Alignment, + align_y: Alignment, space: Size, ) { - match horizontal_alignment { + match align_x { Alignment::Start => {} Alignment::Center => { self.bounds.x += (space.width - self.bounds.width) / 2.0; @@ -77,7 +77,7 @@ impl Node { } } - match vertical_alignment { + match align_y { Alignment::Start => {} Alignment::Center => { self.bounds.y += (space.height - self.bounds.height) / 2.0; diff --git a/core/src/length.rs b/core/src/length.rs index 363833c4..073465ae 100644 --- a/core/src/length.rs +++ b/core/src/length.rs @@ -57,6 +57,7 @@ impl Length { /// Adapts the [`Length`] so it can contain the other [`Length`] and /// match its fluidity. + #[inline] pub fn enclose(self, other: Length) -> Self { match (self, other) { (Length::Shrink, Length::Fill | Length::FillPortion(_)) => other, diff --git a/core/src/size.rs b/core/src/size.rs index 95089236..9503cf59 100644 --- a/core/src/size.rs +++ b/core/src/size.rs @@ -1,4 +1,4 @@ -use crate::{Radians, Vector}; +use crate::{Length, Radians, Vector}; /// An amount of space in 2 dimensions. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] @@ -66,6 +66,15 @@ impl Size { } } +impl Size { + /// Returns true if either `width` or `height` are 0-sized. + #[inline] + pub fn is_void(&self) -> bool { + matches!(self.width, Length::Fixed(0.0)) + || matches!(self.height, Length::Fixed(0.0)) + } +} + impl From<[T; 2]> for Size { fn from([width, height]: [T; 2]) -> Self { Size { width, height } diff --git a/core/src/theme/palette.rs b/core/src/theme/palette.rs index d9ac6402..88c843b0 100644 --- a/core/src/theme/palette.rs +++ b/core/src/theme/palette.rs @@ -28,7 +28,7 @@ impl Palette { text: Color::BLACK, primary: color!(0x5865F2), success: color!(0x12664f), - warning: color!(0xffc14e), + warning: color!(0xb77e33), danger: color!(0xc3423f), }; @@ -453,9 +453,15 @@ pub struct Background { /// The weakest version of the base background color. pub weakest: Pair, /// A weaker version of the base background color. + pub weaker: Pair, + /// A weak version of the base background color. pub weak: Pair, - /// A stronger version of the base background color. + /// A neutral version of the base background color, between weak and strong. + pub neutral: Pair, + /// A strong version of the base background color. pub strong: Pair, + /// A stronger version of the base background color. + pub stronger: Pair, /// The strongest version of the base background color. pub strongest: Pair, } @@ -464,15 +470,21 @@ impl Background { /// Generates a set of [`Background`] colors from the base and text colors. pub fn new(base: Color, text: Color) -> Self { let weakest = deviate(base, 0.03); - let weak = muted(deviate(base, 0.1)); - let strong = muted(deviate(base, 0.2)); - let strongest = muted(deviate(base, 0.3)); + let weaker = deviate(base, 0.07); + let weak = deviate(base, 0.1); + let neutral = deviate(base, 0.125); + let strong = deviate(base, 0.15); + let stronger = deviate(base, 0.175); + let strongest = deviate(base, 0.20); Self { base: Pair::new(base, text), weakest: Pair::new(weakest, text), + weaker: Pair::new(weaker, text), weak: Pair::new(weak, text), + neutral: Pair::new(neutral, text), strong: Pair::new(strong, text), + stronger: Pair::new(stronger, text), strongest: Pair::new(strongest, text), } } @@ -517,9 +529,11 @@ pub struct Secondary { impl Secondary { /// Generates a set of [`Secondary`] colors from the base and text colors. pub fn generate(base: Color, text: Color) -> Self { - let base = mix(base, text, 0.2); - let weak = mix(base, text, 0.1); - let strong = mix(base, text, 0.3); + let factor = if is_dark(base) { 0.2 } else { 0.4 }; + + let weak = mix(deviate(base, 0.1), text, factor); + let strong = mix(deviate(base, 0.3), text, factor); + let base = mix(deviate(base, 0.2), text, factor); Self { base: Pair::new(base, text), @@ -604,53 +618,55 @@ impl Danger { } } -struct Hsl { - h: f32, - s: f32, +struct Oklch { l: f32, + c: f32, + h: f32, a: f32, } fn darken(color: Color, amount: f32) -> Color { - let mut hsl = to_hsl(color); + let mut oklch = to_oklch(color); - hsl.l = if hsl.l - amount < 0.0 { + // We try to bump the chroma a bit for more colorful palettes + if oklch.c > 0.0 && oklch.c < (1.0 - oklch.l) / 2.0 { + // Formula empirically and cluelessly derived + oklch.c *= 1.0 + (0.2 / oklch.c).min(100.0) * amount; + } + + oklch.l = if oklch.l - amount < 0.0 { 0.0 } else { - hsl.l - amount + oklch.l - amount }; - from_hsl(hsl) + from_oklch(oklch) } fn lighten(color: Color, amount: f32) -> Color { - let mut hsl = to_hsl(color); + let mut oklch = to_oklch(color); - hsl.l = if hsl.l + amount > 1.0 { + // We try to bump the chroma a bit for more colorful palettes + // Formula empirically and cluelessly derived + oklch.c *= 1.0 + 2.0 * amount / oklch.l.max(0.05); + + oklch.l = if oklch.l + amount > 1.0 { 1.0 } else { - hsl.l + amount + oklch.l + amount }; - from_hsl(hsl) + from_oklch(oklch) } fn deviate(color: Color, amount: f32) -> Color { if is_dark(color) { lighten(color, amount) } else { - darken(color, amount * 0.8) + darken(color, amount) } } -fn muted(color: Color) -> Color { - let mut hsl = to_hsl(color); - - hsl.s = hsl.s.min(0.5); - - from_hsl(hsl) -} - fn mix(a: Color, b: Color, factor: f32) -> Color { let b_amount = factor.clamp(0.0, 1.0); let a_amount = 1.0 - b_amount; @@ -680,6 +696,12 @@ fn readable(background: Color, text: Color) -> Color { return candidate; } + let candidate = improve(text, 0.2); + + if is_readable(background, candidate) { + return candidate; + } + let white_contrast = relative_contrast(background, Color::WHITE); let black_contrast = relative_contrast(background, Color::BLACK); @@ -691,85 +713,71 @@ fn readable(background: Color, text: Color) -> Color { } fn is_dark(color: Color) -> bool { - to_hsl(color).l < 0.6 + to_oklch(color).l < 0.6 } fn is_readable(a: Color, b: Color) -> bool { - relative_contrast(a, b) >= 7.0 + relative_contrast(a, b) >= 6.0 } // https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio fn relative_contrast(a: Color, b: Color) -> f32 { - let lum_a = relative_luminance(a); - let lum_b = relative_luminance(b); + let lum_a = a.relative_luminance(); + let lum_b = b.relative_luminance(); (lum_a.max(lum_b) + 0.05) / (lum_a.min(lum_b) + 0.05) } -// https://www.w3.org/TR/WCAG21/#dfn-relative-luminance -fn relative_luminance(color: Color) -> f32 { - let linear = color.into_linear(); - 0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2] +// https://en.wikipedia.org/wiki/Oklab_color_space#Conversions_between_color_spaces +fn to_oklch(color: Color) -> Oklch { + let [r, g, b, alpha] = color.into_linear(); + + // linear RGB → LMS + let l = 0.41222146 * r + 0.53633255 * g + 0.051445995 * b; + let m = 0.2119035 * r + 0.6806995 * g + 0.10739696 * b; + let s = 0.08830246 * r + 0.28171885 * g + 0.6299787 * b; + + // Nonlinear transform (cube root) + let l_ = l.cbrt(); + let m_ = m.cbrt(); + let s_ = s.cbrt(); + + // LMS → Oklab + let l = 0.21045426 * l_ + 0.7936178 * m_ - 0.004072047 * s_; + let a = 1.9779985 * l_ - 2.4285922 * m_ + 0.4505937 * s_; + let b = 0.025904037 * l_ + 0.78277177 * m_ - 0.80867577 * s_; + + // Oklab → Oklch + let c = (a * a + b * b).sqrt(); + let h = b.atan2(a); // radians + + Oklch { l, c, h, a: alpha } } -// https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB -fn to_hsl(color: Color) -> Hsl { - let x_max = color.r.max(color.g).max(color.b); - let x_min = color.r.min(color.g).min(color.b); - let c = x_max - x_min; - let l = x_max.midpoint(x_min); +// https://en.wikipedia.org/wiki/Oklab_color_space#Conversions_between_color_spaces +fn from_oklch(oklch: Oklch) -> Color { + let Oklch { l, c, h, a: alpha } = oklch; - let h = if c == 0.0 { - 0.0 - } else if x_max == color.r { - 60.0 * ((color.g - color.b) / c).rem_euclid(6.0) - } else if x_max == color.g { - 60.0 * (((color.b - color.r) / c) + 2.0) - } else { - // x_max == color.b - 60.0 * (((color.r - color.g) / c) + 4.0) - }; + let a = c * h.cos(); + let b = c * h.sin(); - let s = if l == 0.0 || l == 1.0 { - 0.0 - } else { - (x_max - l) / l.min(1.0 - l) - }; + // Oklab → LMS (nonlinear) + let l_ = l + 0.39633778 * a + 0.21580376 * b; + let m_ = l - 0.105561346 * a - 0.06385417 * b; + let s_ = l - 0.08948418 * a - 1.2914855 * b; - Hsl { - h, - s, - l, - a: color.a, - } -} - -// https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB -fn from_hsl(hsl: Hsl) -> Color { - let c = (1.0 - (2.0 * hsl.l - 1.0).abs()) * hsl.s; - let h = hsl.h / 60.0; - let x = c * (1.0 - (h.rem_euclid(2.0) - 1.0).abs()); - - let (r1, g1, b1) = if h < 1.0 { - (c, x, 0.0) - } else if h < 2.0 { - (x, c, 0.0) - } else if h < 3.0 { - (0.0, c, x) - } else if h < 4.0 { - (0.0, x, c) - } else if h < 5.0 { - (x, 0.0, c) - } else { - // h < 6.0 - (c, 0.0, x) - }; - - let m = hsl.l - (c / 2.0); - - Color { - r: r1 + m, - g: g1 + m, - b: b1 + m, - a: hsl.a, - } + // Cubing back + let l = l_ * l_ * l_; + let m = m_ * m_ * m_; + let s = s_ * s_ * s_; + + let r = 4.0767417 * l - 3.3077116 * m + 0.23096994 * s; + let g = -1.268438 * l + 2.6097574 * m - 0.34131938 * s; + let b = -0.0041960863 * l - 0.7034186 * m + 1.7076147 * s; + + Color::from_linear_rgba( + r.clamp(0.0, 1.0), + g.clamp(0.0, 1.0), + b.clamp(0.0, 1.0), + alpha, + ) } diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs index b5ed9bf0..24966211 100644 --- a/core/src/widget/text.rs +++ b/core/src/widget/text.rs @@ -441,7 +441,7 @@ pub fn primary(theme: &Theme) -> Style { /// Text conveying some secondary information, like a footnote. pub fn secondary(theme: &Theme) -> Style { Style { - color: Some(theme.extended_palette().secondary.strong.color), + color: Some(theme.extended_palette().secondary.base.color), } } @@ -452,6 +452,13 @@ pub fn success(theme: &Theme) -> Style { } } +/// Text conveying some mildly negative information, like a warning. +pub fn warning(theme: &Theme) -> Style { + Style { + color: Some(theme.palette().warning), + } +} + /// Text conveying some negative information, like an error. pub fn danger(theme: &Theme) -> Style { Style { diff --git a/devtools/src/lib.rs b/devtools/src/lib.rs index fa002c8e..209eb100 100644 --- a/devtools/src/lib.rs +++ b/devtools/src/lib.rs @@ -425,7 +425,7 @@ where None }; - let content = row![view].push_maybe(sidebar); + let content = row![view, sidebar]; themer( theme, diff --git a/examples/custom_shader/src/scene/pipeline.rs b/examples/custom_shader/src/scene/pipeline.rs index 567ab00b..7140bcaa 100644 --- a/examples/custom_shader/src/scene/pipeline.rs +++ b/examples/custom_shader/src/scene/pipeline.rs @@ -369,6 +369,7 @@ impl Pipeline { color_attachments: &[Some( wgpu::RenderPassColorAttachment { view: target, + depth_slice: None, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Load, @@ -568,6 +569,7 @@ impl DepthPipeline { label: Some("cubes.pipeline.depth_pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: target, + depth_slice: None, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Load, diff --git a/examples/editor/src/main.rs b/examples/editor/src/main.rs index abad1abe..ad2337d8 100644 --- a/examples/editor/src/main.rs +++ b/examples/editor/src/main.rs @@ -119,10 +119,10 @@ impl Editor { let mut text = self.content.text(); - if let Some(ending) = self.content.line_ending() { - if !text.ends_with(ending.as_str()) { - text.push_str(ending.as_str()); - } + if let Some(ending) = self.content.line_ending() + && !text.ends_with(ending.as_str()) + { + text.push_str(ending.as_str()); } Task::perform( diff --git a/examples/gallery/src/civitai.rs b/examples/gallery/src/civitai.rs index 1102acae..b24b1031 100644 --- a/examples/gallery/src/civitai.rs +++ b/examples/gallery/src/civitai.rs @@ -29,7 +29,7 @@ impl Image { .get("https://civitai.com/api/v1/images") .query(&[ ("sort", "Most Reactions"), - ("period", "Week"), + ("period", "Month"), ("nsfw", "None"), ("limit", &Image::LIMIT.to_string()), ]) diff --git a/examples/gallery/src/main.rs b/examples/gallery/src/main.rs index a40df370..14d20103 100644 --- a/examples/gallery/src/main.rs +++ b/examples/gallery/src/main.rs @@ -10,7 +10,7 @@ use iced::animation; use iced::time::{Instant, milliseconds}; use iced::widget::{ button, container, float, grid, horizontal_space, image, mouse_area, - opaque, pop, scrollable, stack, + opaque, scrollable, sensor, stack, }; use iced::window; use iced::{ @@ -257,7 +257,7 @@ fn card<'a>( .style(button::text) .into() } else { - pop(card) + sensor(card) .on_show(|_| Message::ImagePoppedIn(metadata.id)) .into() } diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs index 16794662..b3c08879 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration/src/main.rs @@ -96,17 +96,14 @@ pub fn main() -> Result<(), winit::error::EventLoopError> { let capabilities = surface.get_capabilities(&adapter); let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - required_features: adapter_features - & wgpu::Features::default(), - required_limits: wgpu::Limits::default(), - memory_hints: - wgpu::MemoryHints::MemoryUsage, - }, - None, - ) + .request_device(&wgpu::DeviceDescriptor { + label: None, + required_features: adapter_features + & wgpu::Features::default(), + required_limits: wgpu::Limits::default(), + memory_hints: wgpu::MemoryHints::MemoryUsage, + trace: wgpu::Trace::Off, + }) .await .expect("Request device"); diff --git a/examples/integration/src/scene.rs b/examples/integration/src/scene.rs index 7ba551aa..4e4757fa 100644 --- a/examples/integration/src/scene.rs +++ b/examples/integration/src/scene.rs @@ -24,6 +24,7 @@ impl Scene { label: None, color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: target, + depth_slice: None, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear({ diff --git a/examples/markdown/src/main.rs b/examples/markdown/src/main.rs index 98d19595..b7195055 100644 --- a/examples/markdown/src/main.rs +++ b/examples/markdown/src/main.rs @@ -6,7 +6,7 @@ use iced::highlighter; use iced::time::{self, Instant, milliseconds}; use iced::widget::{ self, button, center_x, container, horizontal_space, hover, image, - markdown, pop, right, row, scrollable, text_editor, toggler, + markdown, right, row, scrollable, sensor, text_editor, toggler, }; use iced::window; use iced::{ @@ -267,7 +267,7 @@ impl<'a> markdown::Viewer<'a, Message> for CustomViewer<'a> { ) .into() } else { - pop(horizontal_space()) + sensor(horizontal_space()) .key_ref(url.as_str()) .delay(milliseconds(500)) .on_show(|_size| Message::ImageShown(url.clone())) diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index c44fc1f1..f94e83ca 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -71,11 +71,10 @@ impl Example { } } Message::FocusAdjacent(direction) => { - if let Some(pane) = self.focus { - if let Some(adjacent) = self.panes.adjacent(pane, direction) - { - self.focus = Some(adjacent); - } + if let Some(pane) = self.focus + && let Some(adjacent) = self.panes.adjacent(pane, direction) + { + self.focus = Some(adjacent); } } Message::Clicked(pane) => { @@ -106,14 +105,12 @@ impl Example { } } Message::CloseFocused => { - if let Some(pane) = self.focus { - if let Some(Pane { is_pinned, .. }) = self.panes.get(pane) { - if !is_pinned { - if let Some((_, sibling)) = self.panes.close(pane) { - self.focus = Some(sibling); - } - } - } + if let Some(pane) = self.focus + && let Some(Pane { is_pinned, .. }) = self.panes.get(pane) + && !is_pinned + && let Some((_, sibling)) = self.panes.close(pane) + { + self.focus = Some(sibling); } } } @@ -276,13 +273,13 @@ fn view_content<'a>( button( "Split vertically", Message::Split(pane_grid::Axis::Vertical, pane), - ) + ), + if total_panes > 1 && !is_pinned { + Some(button("Close", Message::Close(pane)).style(button::danger)) + } else { + None + } ] - .push_maybe(if total_panes > 1 && !is_pinned { - Some(button("Close", Message::Close(pane)).style(button::danger)) - } else { - None - }) .spacing(5) .max_width(160); @@ -300,7 +297,7 @@ fn view_controls<'a>( is_pinned: bool, is_maximized: bool, ) -> Element<'a, Message> { - let row = row![].spacing(5).push_maybe(if total_panes > 1 { + let maximize = if total_panes > 1 { let (content, message) = if is_maximized { ("Restore", Message::Restore) } else { @@ -315,7 +312,7 @@ fn view_controls<'a>( ) } else { None - }); + }; let close = button(text("Close").size(14)) .style(button::danger) @@ -326,7 +323,7 @@ fn view_controls<'a>( None }); - row.push(close).into() + row![maximize, close].spacing(5).into() } mod style { diff --git a/examples/qr_code/src/main.rs b/examples/qr_code/src/main.rs index 20217615..6b6989d0 100644 --- a/examples/qr_code/src/main.rs +++ b/examples/qr_code/src/main.rs @@ -88,18 +88,18 @@ impl QRGenerator { input, row![toggle_total_size, choose_theme] .spacing(20) - .align_y(Center) + .align_y(Center), + self.total_size.map(|total_size| { + slider(Self::SIZE_RANGE, total_size, Message::TotalSizeChanged) + }), + self.qr_code.as_ref().map(|data| { + if let Some(total_size) = self.total_size { + qr_code(data).total_size(total_size) + } else { + qr_code(data).cell_size(10.0) + } + }) ] - .push_maybe(self.total_size.map(|total_size| { - slider(Self::SIZE_RANGE, total_size, Message::TotalSizeChanged) - })) - .push_maybe(self.qr_code.as_ref().map(|data| { - if let Some(total_size) = self.total_size { - qr_code(data).total_size(total_size) - } else { - qr_code(data).cell_size(10.0) - } - })) .width(700) .spacing(20) .align_x(Center); diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index 1f18e285..61285b72 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -158,15 +158,15 @@ impl Example { .spacing(10) .align_y(Center); - let crop_controls = - column![crop_origin_controls, crop_dimension_controls] - .push_maybe( - self.crop_error - .as_ref() - .map(|error| text!("Crop error! \n{error}")), - ) - .spacing(10) - .align_x(Center); + let crop_controls = column![ + crop_origin_controls, + crop_dimension_controls, + self.crop_error + .as_ref() + .map(|error| text!("Crop error! \n{error}")), + ] + .spacing(10) + .align_x(Center); let controls = { let save_result = @@ -208,8 +208,8 @@ impl Example { ] .spacing(10) .align_x(Center), + save_result.map(text) ] - .push_maybe(save_result.map(text)) .spacing(40) }; diff --git a/examples/styling/snapshots/catppuccin_frappé-tiny-skia.sha256 b/examples/styling/snapshots/catppuccin_frappé-tiny-skia.sha256 index 3309ffc7..cdc79b78 100644 --- a/examples/styling/snapshots/catppuccin_frappé-tiny-skia.sha256 +++ b/examples/styling/snapshots/catppuccin_frappé-tiny-skia.sha256 @@ -1 +1 @@ -30570747bb062e9f7730cdd58be961c84bcf4711a6983185bff6d903e8d29e9c \ No newline at end of file +0650eb2c27c21c5d48e1e00031a52d8471d8a3b4e827ad502c4628914f5c1c13 \ No newline at end of file diff --git a/examples/styling/snapshots/catppuccin_latte-tiny-skia.sha256 b/examples/styling/snapshots/catppuccin_latte-tiny-skia.sha256 index bf351053..f7b72157 100644 --- a/examples/styling/snapshots/catppuccin_latte-tiny-skia.sha256 +++ b/examples/styling/snapshots/catppuccin_latte-tiny-skia.sha256 @@ -1 +1 @@ -d5a086a08544f98087189bd4ece8815e5290722a07cd580b933f1bf77a040c52 \ No newline at end of file +ae1da92064373838152ac163072ee68135f530e0fef8146a01aea1df5cfdb494 \ No newline at end of file diff --git a/examples/styling/snapshots/catppuccin_macchiato-tiny-skia.sha256 b/examples/styling/snapshots/catppuccin_macchiato-tiny-skia.sha256 index ec379562..e5900859 100644 --- a/examples/styling/snapshots/catppuccin_macchiato-tiny-skia.sha256 +++ b/examples/styling/snapshots/catppuccin_macchiato-tiny-skia.sha256 @@ -1 +1 @@ -30e523961db89a3ee97ad1eac09e727ecb3dec485faa362534a9f5ad083b32dd \ No newline at end of file +8466dc0975c0bc7c06ed3c45df51e99b9d384394f8c3689b15231a872ba1262f \ No newline at end of file diff --git a/examples/styling/snapshots/catppuccin_mocha-tiny-skia.sha256 b/examples/styling/snapshots/catppuccin_mocha-tiny-skia.sha256 index 86583521..c20f8b1a 100644 --- a/examples/styling/snapshots/catppuccin_mocha-tiny-skia.sha256 +++ b/examples/styling/snapshots/catppuccin_mocha-tiny-skia.sha256 @@ -1 +1 @@ -bce5427d5105f68e1d7fa18a34fcc551cb78c2fefd9a583ba44686331133436d \ No newline at end of file +1c8f13cfb5d0bbbeb24b80ed35671e8a93d208d79ac4dbd069fe65c4a53c50c2 \ No newline at end of file diff --git a/examples/styling/snapshots/dark-tiny-skia.sha256 b/examples/styling/snapshots/dark-tiny-skia.sha256 index 5453059b..e5fc3bc2 100644 --- a/examples/styling/snapshots/dark-tiny-skia.sha256 +++ b/examples/styling/snapshots/dark-tiny-skia.sha256 @@ -1 +1 @@ -c8a7edbd5a8bbf559134b84253e14e65340f4ffe3e22c272b21c8438e47ffaf7 \ No newline at end of file +a1d30652db2cce98b5b86e8e29d776e2fc9091056aff8861cd54fa061161ed47 \ No newline at end of file diff --git a/examples/styling/snapshots/dracula-tiny-skia.sha256 b/examples/styling/snapshots/dracula-tiny-skia.sha256 index d4b41911..e55502f3 100644 --- a/examples/styling/snapshots/dracula-tiny-skia.sha256 +++ b/examples/styling/snapshots/dracula-tiny-skia.sha256 @@ -1 +1 @@ -63d646b22d3dffbb56dac2e3f345090bd26625a388dd6cc142359f2a7ac9c8df \ No newline at end of file +8c01615169803510f1cd4d051721b415adc7147672238aff1275fa3741edb507 \ No newline at end of file diff --git a/examples/styling/snapshots/ferra-tiny-skia.sha256 b/examples/styling/snapshots/ferra-tiny-skia.sha256 index 2e2c2eab..611cf302 100644 --- a/examples/styling/snapshots/ferra-tiny-skia.sha256 +++ b/examples/styling/snapshots/ferra-tiny-skia.sha256 @@ -1 +1 @@ -d26f55674cbd96bc3b534ffdd098a13199718ef9c5ffe8ece0882ddab714b776 \ No newline at end of file +0b10823a1d218c145214ff2dcf751584669a3ca1d3e777a2cd618479a809523e \ No newline at end of file diff --git a/examples/styling/snapshots/gruvbox_dark-tiny-skia.sha256 b/examples/styling/snapshots/gruvbox_dark-tiny-skia.sha256 index 8249f9c3..11614eea 100644 --- a/examples/styling/snapshots/gruvbox_dark-tiny-skia.sha256 +++ b/examples/styling/snapshots/gruvbox_dark-tiny-skia.sha256 @@ -1 +1 @@ -482c44c13d4ff3de19e71f3dddf93bbee170e54e2d353e818811069de28e18ed \ No newline at end of file +26bc668c55650c6c25a14f76feeb1d1f78a96835aaac7a5f57b48b838cb28b14 \ No newline at end of file diff --git a/examples/styling/snapshots/gruvbox_light-tiny-skia.sha256 b/examples/styling/snapshots/gruvbox_light-tiny-skia.sha256 index 0277b685..a4ed62b2 100644 --- a/examples/styling/snapshots/gruvbox_light-tiny-skia.sha256 +++ b/examples/styling/snapshots/gruvbox_light-tiny-skia.sha256 @@ -1 +1 @@ -6738cc4fc6eb8a5d406c613a4b0f08c0e8dcd2c1a5444445eebd3888f9303841 \ No newline at end of file +228eb8d64eed2f3726d27490b88a4519e36979a0ccfb0db8e164c5e5296b0739 \ No newline at end of file diff --git a/examples/styling/snapshots/kanagawa_dragon-tiny-skia.sha256 b/examples/styling/snapshots/kanagawa_dragon-tiny-skia.sha256 index 7995a848..94daa9f4 100644 --- a/examples/styling/snapshots/kanagawa_dragon-tiny-skia.sha256 +++ b/examples/styling/snapshots/kanagawa_dragon-tiny-skia.sha256 @@ -1 +1 @@ -0a918c52538fc4848aa0c68d8f2d6f4c981ed68971dd9c725f0093a39ef7f353 \ No newline at end of file +d579b14db1650e907f925302f23c53ebaba370aef6410cfa48fef70ab3138d1f \ No newline at end of file diff --git a/examples/styling/snapshots/kanagawa_lotus-tiny-skia.sha256 b/examples/styling/snapshots/kanagawa_lotus-tiny-skia.sha256 index b68211a1..d17b3745 100644 --- a/examples/styling/snapshots/kanagawa_lotus-tiny-skia.sha256 +++ b/examples/styling/snapshots/kanagawa_lotus-tiny-skia.sha256 @@ -1 +1 @@ -de3e1a2c21e1a86d76ca99989c73e8a2596ef627bba95d246fab8f02d56bd0af \ No newline at end of file +896072b46221f83e1edaa37574436af6474969625f5c1a41cc5ddc2e20823cee \ No newline at end of file diff --git a/examples/styling/snapshots/kanagawa_wave-tiny-skia.sha256 b/examples/styling/snapshots/kanagawa_wave-tiny-skia.sha256 index d9848230..b7530850 100644 --- a/examples/styling/snapshots/kanagawa_wave-tiny-skia.sha256 +++ b/examples/styling/snapshots/kanagawa_wave-tiny-skia.sha256 @@ -1 +1 @@ -3418ea4eb0f7786607ef02e7db4bc97309530f2f7c08f8aea15c768a13a09ca4 \ No newline at end of file +60e1c95159caddb8bd7b8360e32ffd75472be37c4fcbd8ad23dabd0d000a4ec1 \ No newline at end of file diff --git a/examples/styling/snapshots/light-tiny-skia.sha256 b/examples/styling/snapshots/light-tiny-skia.sha256 index e9864b81..aea0e467 100644 --- a/examples/styling/snapshots/light-tiny-skia.sha256 +++ b/examples/styling/snapshots/light-tiny-skia.sha256 @@ -1 +1 @@ -c8474e02a9df23f123816a489c1ea7ae6cb994a0eca429592dfe6d933de1beee \ No newline at end of file +2e3c4ea86b5bd968b8ec77a7ec7b5b7ae29d5ee8e4b68a216c1fa11d92c015bc \ No newline at end of file diff --git a/examples/styling/snapshots/moonfly-tiny-skia.sha256 b/examples/styling/snapshots/moonfly-tiny-skia.sha256 index 6456da10..c67427a3 100644 --- a/examples/styling/snapshots/moonfly-tiny-skia.sha256 +++ b/examples/styling/snapshots/moonfly-tiny-skia.sha256 @@ -1 +1 @@ -02095fd09c078be02dc41e29e55de25e8a79e6ad4293aa7e430257a9016dfb3d \ No newline at end of file +79ffced2a78689bac1a40ab154a478b4fff87154ee4a8bbf023d922c86b7d53b \ No newline at end of file diff --git a/examples/styling/snapshots/nightfly-tiny-skia.sha256 b/examples/styling/snapshots/nightfly-tiny-skia.sha256 index f2ef8296..ad8fc9cb 100644 --- a/examples/styling/snapshots/nightfly-tiny-skia.sha256 +++ b/examples/styling/snapshots/nightfly-tiny-skia.sha256 @@ -1 +1 @@ -d82588a2aba3e7211f25b85ebb812a42dfa59137dd4b59d26f5f60d5b28e537f \ No newline at end of file +17c632cbef607502ed2c438f409a1c9bee382d9084c38772021f1f2a4ad3908c \ No newline at end of file diff --git a/examples/styling/snapshots/nord-tiny-skia.sha256 b/examples/styling/snapshots/nord-tiny-skia.sha256 index 3905db54..9e373c8e 100644 --- a/examples/styling/snapshots/nord-tiny-skia.sha256 +++ b/examples/styling/snapshots/nord-tiny-skia.sha256 @@ -1 +1 @@ -d6b73545929cc7794c1a918f069b5326ef129bed8f9ad2cd001be7d078a2b6a0 \ No newline at end of file +359f7f2c1d7f87e6e0eb80a9a28f70f033d6321ba028d32bc372030b718ed481 \ No newline at end of file diff --git a/examples/styling/snapshots/oxocarbon-tiny-skia.sha256 b/examples/styling/snapshots/oxocarbon-tiny-skia.sha256 index 363fcf31..b2afc484 100644 --- a/examples/styling/snapshots/oxocarbon-tiny-skia.sha256 +++ b/examples/styling/snapshots/oxocarbon-tiny-skia.sha256 @@ -1 +1 @@ -0ec7251c69755becd678b7aec398a275edf31cc077960723cd6b9364e8678548 \ No newline at end of file +a908d8f154f2baf67455380b5d8b39003c08ba0c80f39e71d4bcd2377bc784fc \ No newline at end of file diff --git a/examples/styling/snapshots/solarized_dark-tiny-skia.sha256 b/examples/styling/snapshots/solarized_dark-tiny-skia.sha256 index 05f529b9..6075ad58 100644 --- a/examples/styling/snapshots/solarized_dark-tiny-skia.sha256 +++ b/examples/styling/snapshots/solarized_dark-tiny-skia.sha256 @@ -1 +1 @@ -4a15c475d45cf8eb0ccd6727cf6e493bd8c22454610b167a632a2328308faed1 \ No newline at end of file +8d6c2bab1f6e9a8db1e2acc8eb76334170e046b709a36dd4ad4d86f8d47346a4 \ No newline at end of file diff --git a/examples/styling/snapshots/solarized_light-tiny-skia.sha256 b/examples/styling/snapshots/solarized_light-tiny-skia.sha256 index 159f3279..5862204a 100644 --- a/examples/styling/snapshots/solarized_light-tiny-skia.sha256 +++ b/examples/styling/snapshots/solarized_light-tiny-skia.sha256 @@ -1 +1 @@ -49a41af93e89aab0a4e352e9cedfba3c6e18caf4267955c9d362bad40264a165 \ No newline at end of file +2010df2e80bfc72e7e9274de07b77dc4843485f6be38266fdfb7a4f129d75da1 \ No newline at end of file diff --git a/examples/styling/snapshots/tokyo_night-tiny-skia.sha256 b/examples/styling/snapshots/tokyo_night-tiny-skia.sha256 index fbcce8d7..e3bab4b1 100644 --- a/examples/styling/snapshots/tokyo_night-tiny-skia.sha256 +++ b/examples/styling/snapshots/tokyo_night-tiny-skia.sha256 @@ -1 +1 @@ -8fcd80d4569dafdac4b4452b8ca8ab0cdceeb755f3c83d374ccd5ed4d0e8d43d \ No newline at end of file +74812d50467787ce39a33ad6bc89411d7b8bc0b13e1bbd45838fcc27c75aee98 \ No newline at end of file diff --git a/examples/styling/snapshots/tokyo_night_light-tiny-skia.sha256 b/examples/styling/snapshots/tokyo_night_light-tiny-skia.sha256 index eba9720a..d533a94c 100644 --- a/examples/styling/snapshots/tokyo_night_light-tiny-skia.sha256 +++ b/examples/styling/snapshots/tokyo_night_light-tiny-skia.sha256 @@ -1 +1 @@ -c37a32784c769c046f3aa881914b121af373b8c6e175ced89304d15b626a653a \ No newline at end of file +b04218ee65cd446b142596a2cd9ff69d5267969af86026a4ff394f3c13a4d842 \ No newline at end of file diff --git a/examples/styling/snapshots/tokyo_night_storm-tiny-skia.sha256 b/examples/styling/snapshots/tokyo_night_storm-tiny-skia.sha256 index ac5f0ad2..84c3f058 100644 --- a/examples/styling/snapshots/tokyo_night_storm-tiny-skia.sha256 +++ b/examples/styling/snapshots/tokyo_night_storm-tiny-skia.sha256 @@ -1 +1 @@ -533d25575e8bf1111036fb082b424d0d0e60947a7da8428ab8c71e0bda01469e \ No newline at end of file +e1cbe8742f000921c86924056e9a45f95ee2a2a973743bf9f37fee65baccfb9b \ No newline at end of file diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index 934b9f5d..f28216f9 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -1,10 +1,10 @@ use iced::keyboard; use iced::widget::{ - button, center, checkbox, column, container, horizontal_rule, pick_list, - progress_bar, row, scrollable, slider, text, text_input, toggler, - vertical_rule, vertical_space, + button, center_x, center_y, checkbox, column, container, horizontal_rule, + pick_list, progress_bar, row, scrollable, slider, text, text_input, + toggler, vertical_rule, vertical_space, }; -use iced::{Center, Element, Fill, Subscription, Theme}; +use iced::{Center, Element, Fill, Shrink, Subscription, Theme}; pub fn main() -> iced::Result { iced::application(Styling::default, Styling::update, Styling::view) @@ -78,38 +78,64 @@ impl Styling { .padding(10) .size(20); - let styled_button = |label| { - button(text(label).width(Fill).center()) - .padding(10) - .on_press(Message::ButtonPressed) - }; + let buttons = { + let styles = [ + ("Primary", button::primary as fn(&Theme, _) -> _), + ("Secondary", button::secondary), + ("Success", button::success), + ("Warning", button::warning), + ("Danger", button::danger), + ]; - let primary = styled_button("Primary"); - let success = styled_button("Success").style(button::success); - let warning = styled_button("Warning").style(button::warning); - let danger = styled_button("Danger").style(button::danger); + let styled_button = + |label| button(text(label).width(Fill).center()).padding(10); + + column![ + row(styles.into_iter().map(|(name, style)| styled_button( + name + ) + .on_press(Message::ButtonPressed) + .style(style) + .into())) + .spacing(10) + .align_y(Center), + row(styles.into_iter().map(|(name, style)| styled_button( + name + ) + .style(style) + .into())) + .spacing(10) + .align_y(Center), + ] + .spacing(10) + }; let slider = || slider(0.0..=100.0, self.slider_value, Message::SliderChanged); let progress_bar = || progress_bar(0.0..=100.0, self.slider_value); - let scrollable = scrollable(column![ + let scroll_me = scrollable(column![ "Scroll me!", vertical_space().height(800), "You did it!" ]) .width(Fill) - .height(100); + .height(Fill); - let checkbox = checkbox("Check me!", self.checkbox_value) + let check = checkbox("Check me!", self.checkbox_value) .on_toggle(Message::CheckboxToggled); - let toggler = toggler(self.toggler_value) + let check_disabled = checkbox("Disabled", self.checkbox_value); + + let toggle = toggler(self.toggler_value) .label("Toggle me!") .on_toggle(Message::TogglerToggled) .spacing(10); + let disabled_toggle = + toggler(self.toggler_value).label("Disabled").spacing(10); + let card = { container( column![ @@ -128,18 +154,17 @@ impl Styling { choose_theme, horizontal_rule(1), text_input, - row![primary, success, warning, danger] - .spacing(10) - .align_y(Center), + buttons, slider(), progress_bar(), row![ - scrollable, - row![vertical_rule(1), column![checkbox, toggler].spacing(20)] - .spacing(20) + scroll_me, + vertical_rule(1), + column![check, check_disabled, toggle, disabled_toggle] + .spacing(10) ] .spacing(10) - .height(100) + .height(Shrink) .align_y(Center), card ] @@ -147,7 +172,9 @@ impl Styling { .padding(20) .max_width(600); - center(content).into() + center_y(scrollable(center_x(content)).spacing(10)) + .padding(10) + .into() } fn subscription(&self) -> Subscription { diff --git a/examples/table/Cargo.toml b/examples/table/Cargo.toml new file mode 100644 index 00000000..339c2891 --- /dev/null +++ b/examples/table/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "table" +version = "0.1.0" +authors = ["Héctor Ramón Jiménez "] +edition = "2024" +publish = false + +[dependencies] +iced.workspace = true +iced.features = ["debug"] diff --git a/examples/table/src/main.rs b/examples/table/src/main.rs new file mode 100644 index 00000000..26a8e621 --- /dev/null +++ b/examples/table/src/main.rs @@ -0,0 +1,252 @@ +use iced::font; +use iced::time::{Duration, hours, minutes}; +use iced::widget::{ + center_x, center_y, column, container, row, scrollable, slider, table, + text, tooltip, +}; +use iced::{Center, Element, Fill, Font, Right, Theme}; + +pub fn main() -> iced::Result { + iced::application(Table::new, Table::update, Table::view) + .theme(|_| Theme::CatppuccinMocha) + .run() +} + +struct Table { + events: Vec, + padding: (f32, f32), + separator: (f32, f32), +} + +#[derive(Debug, Clone)] +enum Message { + PaddingChanged(f32, f32), + SeparatorChanged(f32, f32), +} + +impl Table { + fn new() -> Self { + Self { + events: Event::list(), + padding: (10.0, 5.0), + separator: (1.0, 1.0), + } + } + + fn update(&mut self, message: Message) { + match message { + Message::PaddingChanged(x, y) => self.padding = (x, y), + Message::SeparatorChanged(x, y) => self.separator = (x, y), + } + } + + fn view(&self) -> Element<'_, Message> { + let table = { + let bold = |header| { + text(header).font(Font { + weight: font::Weight::Bold, + ..Font::DEFAULT + }) + }; + + let columns = [ + table::column(bold("Name"), |event: &Event| text(&event.name)), + table::column(bold("Time"), |event: &Event| { + let minutes = event.duration.as_secs() / 60; + + text!("{minutes} min").style(if minutes > 90 { + text::warning + } else { + text::default + }) + }) + .align_x(Right) + .align_y(Center), + table::column(bold("Price"), |event: &Event| { + if event.price > 0.0 { + text!("${:.2}", event.price).style( + if event.price > 100.0 { + text::warning + } else { + text::default + }, + ) + } else { + text("Free").style(text::success).width(Fill).center() + } + }) + .align_x(Right) + .align_y(Center), + table::column(bold("Rating"), |event: &Event| { + text!("{:.2}", event.rating).style(if event.rating > 4.7 { + text::success + } else if event.rating < 2.0 { + text::danger + } else { + text::default + }) + }) + .align_x(Right) + .align_y(Center), + ]; + + table(columns, &self.events) + .padding_x(self.padding.0) + .padding_y(self.padding.1) + .separator_x(self.separator.0) + .separator_y(self.separator.1) + }; + + let controls = { + let labeled_slider = + |label, + range: std::ops::RangeInclusive, + (x, y), + on_change: fn(f32, f32) -> Message| { + row![ + text(label).font(Font::MONOSPACE).size(14).width(100), + tooltip( + slider(range.clone(), x, move |x| on_change(x, y)), + text!("{x:.0}px").font(Font::MONOSPACE).size(10), + tooltip::Position::Left + ), + tooltip( + slider(range, y, move |y| on_change(x, y)), + text!("{y:.0}px").font(Font::MONOSPACE).size(10), + tooltip::Position::Right + ), + ] + .spacing(10) + .align_y(Center) + }; + + column![ + labeled_slider( + "Padding", + 0.0..=30.0, + self.padding, + Message::PaddingChanged + ), + labeled_slider( + "Separator", + 0.0..=5.0, + self.separator, + Message::SeparatorChanged + ) + ] + .spacing(10) + .width(400) + }; + + column![ + center_y(scrollable(center_x(table)).spacing(10)).padding(10), + center_x(controls).padding(10).style(container::dark) + ] + .into() + } +} + +struct Event { + name: String, + duration: Duration, + price: f32, + rating: f32, +} + +impl Event { + fn list() -> Vec { + vec![ + Event { + name: "Get lost in a hacker bookstore".to_owned(), + duration: hours(2), + price: 0.0, + rating: 4.9, + }, + Event { + name: "Buy vintage synth at Noisebridge flea market".to_owned(), + duration: hours(1), + price: 150.0, + rating: 4.8, + }, + Event { + name: "Eat a questionable hot dog at 2AM".to_owned(), + duration: minutes(20), + price: 5.0, + rating: 1.7, + }, + Event { + name: "Ride the MUNI for the story".to_owned(), + duration: minutes(60), + price: 3.0, + rating: 4.1, + }, + Event { + name: "Scream into the void from Twin Peaks".to_owned(), + duration: minutes(40), + price: 0.0, + rating: 4.9, + }, + Event { + name: "Buy overpriced coffee and feel things".to_owned(), + duration: minutes(25), + price: 6.5, + rating: 4.5, + }, + Event { + name: "Attend an underground robot poetry slam".to_owned(), + duration: hours(1), + price: 12.0, + rating: 4.8, + }, + Event { + name: "Browse cursed tech at a retro computer fair".to_owned(), + duration: hours(2), + price: 10.0, + rating: 4.7, + }, + Event { + name: "Try to order at a secret ramen place with no sign" + .to_owned(), + duration: minutes(50), + price: 14.0, + rating: 4.6, + }, + Event { + name: "Join a spontaneous rooftop drone rave".to_owned(), + duration: hours(3), + price: 0.0, + rating: 4.9, + }, + Event { + name: "Sketch a stranger at Dolores Park".to_owned(), + duration: minutes(45), + price: 0.0, + rating: 4.4, + }, + Event { + name: "Visit the Museum of Obsolete APIs".to_owned(), + duration: hours(1), + price: 9.99, + rating: 4.2, + }, + Event { + name: "Chase the last working payphone".to_owned(), + duration: minutes(35), + price: 0.25, + rating: 4.0, + }, + Event { + name: "Trade zines with a punk on BART".to_owned(), + duration: minutes(30), + price: 3.5, + rating: 4.7, + }, + Event { + name: "Get a tattoo of the Git logo".to_owned(), + duration: hours(1), + price: 200.0, + rating: 4.6, + }, + ] + } +} diff --git a/examples/todos/snapshots/creates_a_new_task-tiny-skia.sha256 b/examples/todos/snapshots/creates_a_new_task-tiny-skia.sha256 index f62b2f69..e6746cf8 100644 --- a/examples/todos/snapshots/creates_a_new_task-tiny-skia.sha256 +++ b/examples/todos/snapshots/creates_a_new_task-tiny-skia.sha256 @@ -1 +1 @@ -0e355b080ad33905145e9f70a3b29e2481197c8fc8f42491acd5358238ebbd5f \ No newline at end of file +99f418007af163f172e163565f166da31015521e1bf7de95fa55cda2fb5a7db5 \ No newline at end of file diff --git a/examples/todos/snapshots/creates_a_new_task-wgpu.sha256 b/examples/todos/snapshots/creates_a_new_task-wgpu.sha256 deleted file mode 100644 index 6dd6ccbe..00000000 --- a/examples/todos/snapshots/creates_a_new_task-wgpu.sha256 +++ /dev/null @@ -1 +0,0 @@ -804a1bb6d49e3b3158463202960447d9e7820b967280f41dd0c34c00d3edf2c3 \ No newline at end of file diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 984cf272..5d009da6 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -142,17 +142,17 @@ impl Tour { } fn view(&self) -> Element<'_, Message> { - let controls = - row![] - .push_maybe(self.screen.previous().is_some().then(|| { - padded_button("Back") - .on_press(Message::BackPressed) - .style(button::secondary) - })) - .push(horizontal_space()) - .push_maybe(self.can_continue().then(|| { - padded_button("Next").on_press(Message::NextPressed) - })); + let controls = row![ + self.screen.previous().is_some().then(|| { + padded_button("Back") + .on_press(Message::BackPressed) + .style(button::secondary) + }), + horizontal_space(), + self.can_continue().then(|| { + padded_button("Next").on_press(Message::NextPressed) + }) + ]; let screen = match self.screen { Screen::Welcome => self.welcome(), diff --git a/examples/websocket/src/main.rs b/examples/websocket/src/main.rs index 40e37ad6..055455de 100644 --- a/examples/websocket/src/main.rs +++ b/examples/websocket/src/main.rs @@ -119,11 +119,11 @@ impl WebSocket { let mut button = button(text("Send").height(40).align_y(Center)) .padding([0, 20]); - if matches!(self.state, State::Connected(_)) { - if let Some(message) = echo::Message::new(&self.new_message) { - input = input.on_submit(Message::Send(message.clone())); - button = button.on_press(Message::Send(message)); - } + if matches!(self.state, State::Connected(_)) + && let Some(message) = echo::Message::new(&self.new_message) + { + input = input.on_submit(Message::Send(message.clone())); + button = button.on_press(Message::Send(message)); } row![input, button].spacing(10).align_y(Center) diff --git a/graphics/src/text.rs b/graphics/src/text.rs index d2514c90..e00b3ac7 100644 --- a/graphics/src/text.rs +++ b/graphics/src/text.rs @@ -176,7 +176,7 @@ impl FontSystem { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct Version(u32); -/// A weak reference to a [`cosmic-text::Buffer`] that can be drawn. +/// A weak reference to a [`cosmic_text::Buffer`] that can be drawn. #[derive(Debug, Clone)] pub struct Raw { /// A weak reference to a [`cosmic_text::Buffer`]. diff --git a/graphics/src/text/editor.rs b/graphics/src/text/editor.rs index 4afd7b12..72a6fc68 100644 --- a/graphics/src/text/editor.rs +++ b/graphics/src/text/editor.rs @@ -321,10 +321,11 @@ impl editor::Editor for Editor { ); // Deselect if selection matches cursor position - if let Some((start, end)) = editor.selection_bounds() { - if start.line == end.line && start.index == end.index { - editor.set_selection(cosmic_text::Selection::None); - } + if let Some((start, end)) = editor.selection_bounds() + && start.line == end.line + && start.index == end.index + { + editor.set_selection(cosmic_text::Selection::None); } } Action::SelectWord => { @@ -438,10 +439,11 @@ impl editor::Editor for Editor { ); // Deselect if selection matches cursor position - if let Some((start, end)) = editor.selection_bounds() { - if start.line == end.line && start.index == end.index { - editor.set_selection(cosmic_text::Selection::None); - } + if let Some((start, end)) = editor.selection_bounds() + && start.line == end.line + && start.index == end.index + { + editor.set_selection(cosmic_text::Selection::None); } } Action::Scroll { lines } => { diff --git a/wgpu/src/color.rs b/wgpu/src/color.rs index 74a452f9..52fe8463 100644 --- a/wgpu/src/color.rs +++ b/wgpu/src/color.rs @@ -184,6 +184,7 @@ pub fn convert( label: Some("iced_wgpu.offscreen.blit.render_pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { view, + depth_slice: None, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Load, diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs index 83777807..470c39e2 100644 --- a/wgpu/src/image/raster.rs +++ b/wgpu/src/image/raster.rs @@ -99,10 +99,8 @@ impl Cache { self.map.retain(|k, memory| { let retain = hits.contains(k); - if !retain { - if let Memory::Device(entry) = memory { - atlas.remove(entry); - } + if !retain && let Memory::Device(entry) = memory { + atlas.remove(entry); } retain diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 13ed671d..39f31622 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -284,7 +284,7 @@ impl Renderer { let _ = self .engine .device - .poll(wgpu::Maintain::WaitForSubmissionIndex(index)); + .poll(wgpu::PollType::WaitForSubmissionIndex(index)); let mapped_buffer = slice.get_mapped_range(); @@ -426,6 +426,7 @@ impl Renderer { label: Some("iced_wgpu render pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: frame, + depth_slice: None, resolve_target: None, ops: wgpu::Operations { load: match clear_color { @@ -514,6 +515,7 @@ impl Renderer { color_attachments: &[Some( wgpu::RenderPassColorAttachment { view: frame, + depth_slice: None, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Load, @@ -560,6 +562,7 @@ impl Renderer { color_attachments: &[Some( wgpu::RenderPassColorAttachment { view: frame, + depth_slice: None, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Load, @@ -830,21 +833,20 @@ impl renderer::Headless for Renderer { force_fallback_adapter: false, compatible_surface: None, }) - .await?; + .await + .ok()?; let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - label: Some("iced_wgpu [headless]"), - required_features: wgpu::Features::empty(), - required_limits: wgpu::Limits { - max_bind_groups: 2, - ..wgpu::Limits::default() - }, - memory_hints: wgpu::MemoryHints::MemoryUsage, + .request_device(&wgpu::DeviceDescriptor { + label: Some("iced_wgpu [headless]"), + required_features: wgpu::Features::empty(), + required_limits: wgpu::Limits { + max_bind_groups: 2, + ..wgpu::Limits::default() }, - None, - ) + memory_hints: wgpu::MemoryHints::MemoryUsage, + trace: wgpu::Trace::Off, + }) .await .ok()?; diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 78d16e5a..2c900f54 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -335,6 +335,7 @@ fn render<'a>( label: Some("iced_wgpu.triangle.render_pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: target, + depth_slice: None, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Load, diff --git a/wgpu/src/triangle/msaa.rs b/wgpu/src/triangle/msaa.rs index d0449489..8ca49461 100644 --- a/wgpu/src/triangle/msaa.rs +++ b/wgpu/src/triangle/msaa.rs @@ -167,6 +167,7 @@ impl Pipeline { label: Some("iced_wgpu.triangle.render_pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: &targets.attachment, + depth_slice: None, resolve_target: Some(&targets.resolve), ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), @@ -340,6 +341,7 @@ impl State { label: Some("iced_wgpu::triangle::msaa render pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: target, + depth_slice: None, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Load, diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs index c729ddd0..a8b161a8 100644 --- a/wgpu/src/window/compositor.rs +++ b/wgpu/src/window/compositor.rs @@ -93,10 +93,10 @@ impl Compositor { force_fallback_adapter: false, }; - let adapter = instance - .request_adapter(&adapter_options) - .await - .ok_or(Error::NoAdapterFound(format!("{adapter_options:?}")))?; + let adapter = + instance.request_adapter(&adapter_options).await.map_err( + |_error| Error::NoAdapterFound(format!("{adapter_options:?}")), + )?; log::info!("Selected: {:#?}", adapter.get_info()); @@ -162,17 +162,15 @@ impl Compositor { for required_limits in limits { let result = adapter - .request_device( - &wgpu::DeviceDescriptor { - label: Some( - "iced_wgpu::window::compositor device descriptor", - ), - required_features: wgpu::Features::empty(), - required_limits: required_limits.clone(), - memory_hints: wgpu::MemoryHints::MemoryUsage, - }, - None, - ) + .request_device(&wgpu::DeviceDescriptor { + label: Some( + "iced_wgpu::window::compositor device descriptor", + ), + required_features: wgpu::Features::empty(), + required_limits: required_limits.clone(), + memory_hints: wgpu::MemoryHints::MemoryUsage, + trace: wgpu::Trace::Off, + }) .await; match result { diff --git a/widget/src/button.rs b/widget/src/button.rs index 85f16f59..ac4a27cb 100644 --- a/widget/src/button.rs +++ b/widget/src/button.rs @@ -688,6 +688,50 @@ pub fn text(theme: &Theme, status: Status) -> Style { } } +/// A button using background shades. +pub fn background(theme: &Theme, status: Status) -> Style { + let palette = theme.extended_palette(); + let base = styled(palette.background.base); + + match status { + Status::Active => base, + Status::Pressed => Style { + background: Some(Background::Color( + palette.background.strong.color, + )), + ..base + }, + Status::Hovered => Style { + background: Some(Background::Color(palette.background.weak.color)), + ..base + }, + Status::Disabled => disabled(base), + } +} + +/// A subtle button using weak background shades. +pub fn subtle(theme: &Theme, status: Status) -> Style { + let palette = theme.extended_palette(); + let base = styled(palette.background.weakest); + + match status { + Status::Active => base, + Status::Pressed => Style { + background: Some(Background::Color( + palette.background.strong.color, + )), + ..base + }, + Status::Hovered => Style { + background: Some(Background::Color( + palette.background.weaker.color, + )), + ..base + }, + Status::Disabled => disabled(base), + } +} + fn styled(pair: palette::Pair) -> Style { Style { background: Some(Background::Color(pair.color)), diff --git a/widget/src/checkbox.rs b/widget/src/checkbox.rs index a38f5945..42ef89fd 100644 --- a/widget/src/checkbox.rs +++ b/widget/src/checkbox.rs @@ -320,11 +320,9 @@ where | Event::Touch(touch::Event::FingerPressed { .. }) => { let mouse_over = cursor.is_over(layout.bounds()); - if mouse_over { - if let Some(on_toggle) = &self.on_toggle { - shell.publish((on_toggle)(!self.is_checked)); - shell.capture_event(); - } + if mouse_over && let Some(on_toggle) = &self.on_toggle { + shell.publish((on_toggle)(!self.is_checked)); + shell.capture_event(); } } _ => {} @@ -556,23 +554,23 @@ pub fn primary(theme: &Theme, status: Status) -> Style { match status { Status::Active { is_checked } => styled( - palette.primary.strong.text, - palette.background.strongest.color, + palette.background.strong.color, palette.background.base, + palette.primary.base.text, palette.primary.base, is_checked, ), Status::Hovered { is_checked } => styled( - palette.primary.strong.text, - palette.background.strongest.color, + palette.background.strong.color, palette.background.weak, + palette.primary.base.text, palette.primary.strong, is_checked, ), Status::Disabled { is_checked } => styled( - palette.primary.strong.text, palette.background.weak.color, - palette.background.weak, + palette.background.weaker, + palette.primary.base.text, palette.background.strong, is_checked, ), @@ -585,23 +583,23 @@ pub fn secondary(theme: &Theme, status: Status) -> Style { match status { Status::Active { is_checked } => styled( - palette.background.base.text, - palette.background.strongest.color, + palette.background.strong.color, palette.background.base, + palette.background.base.text, palette.background.strong, is_checked, ), Status::Hovered { is_checked } => styled( - palette.background.base.text, - palette.background.strongest.color, + palette.background.strong.color, palette.background.weak, + palette.background.base.text, palette.background.strong, is_checked, ), Status::Disabled { is_checked } => styled( - palette.background.strong.color, palette.background.weak.color, palette.background.weak, + palette.background.base.text, palette.background.weak, is_checked, ), @@ -614,23 +612,23 @@ pub fn success(theme: &Theme, status: Status) -> Style { match status { Status::Active { is_checked } => styled( - palette.success.base.text, palette.background.weak.color, palette.background.base, + palette.success.base.text, palette.success.base, is_checked, ), Status::Hovered { is_checked } => styled( - palette.success.base.text, - palette.background.strongest.color, + palette.background.strong.color, palette.background.weak, + palette.success.base.text, palette.success.strong, is_checked, ), Status::Disabled { is_checked } => styled( - palette.success.base.text, palette.background.weak.color, palette.background.weak, + palette.success.base.text, palette.success.weak, is_checked, ), @@ -643,23 +641,23 @@ pub fn danger(theme: &Theme, status: Status) -> Style { match status { Status::Active { is_checked } => styled( - palette.danger.base.text, - palette.background.strongest.color, + palette.background.strong.color, palette.background.base, + palette.danger.base.text, palette.danger.base, is_checked, ), Status::Hovered { is_checked } => styled( - palette.danger.base.text, - palette.background.strongest.color, + palette.background.strong.color, palette.background.weak, + palette.danger.base.text, palette.danger.strong, is_checked, ), Status::Disabled { is_checked } => styled( - palette.danger.base.text, palette.background.weak.color, palette.background.weak, + palette.danger.base.text, palette.danger.weak, is_checked, ), @@ -667,27 +665,25 @@ pub fn danger(theme: &Theme, status: Status) -> Style { } fn styled( - icon_color: Color, border_color: Color, base: palette::Pair, + icon_color: Color, accent: palette::Pair, is_checked: bool, ) -> Style { + let (background, border) = if is_checked { + (accent, accent.color) + } else { + (base, border_color) + }; + Style { - background: Background::Color(if is_checked { - accent.color - } else { - base.color - }), + background: Background::Color(background.color), icon_color, border: Border { radius: 2.0.into(), width: 1.0, - color: if is_checked { - accent.color - } else { - border_color - }, + color: border, }, text_color: None, } diff --git a/widget/src/column.rs b/widget/src/column.rs index 4ab56c89..6c126048 100644 --- a/widget/src/column.rs +++ b/widget/src/column.rs @@ -145,23 +145,13 @@ where let child = child.into(); let child_size = child.as_widget().size_hint(); - self.width = self.width.enclose(child_size.width); - self.height = self.height.enclose(child_size.height); - - self.children.push(child); - self - } - - /// Adds an element to the [`Column`], if `Some`. - pub fn push_maybe( - self, - child: Option>>, - ) -> Self { - if let Some(child) = child { - self.push(child) - } else { - self + if !child_size.is_void() { + self.width = self.width.enclose(child_size.width); + self.height = self.height.enclose(child_size.height); + self.children.push(child); } + + self } /// Extends the [`Column`] with the given children. diff --git a/widget/src/combo_box.rs b/widget/src/combo_box.rs index 27f3db56..757deee5 100644 --- a/widget/src/combo_box.rs +++ b/widget/src/combo_box.rs @@ -602,17 +602,16 @@ where if is_focused { self.state.with_inner(|state| { - if !started_focused { - if let Some(on_option_hovered) = &mut self.on_option_hovered - { - let hovered_option = menu.hovered_option.unwrap_or(0); + if !started_focused + && let Some(on_option_hovered) = &mut self.on_option_hovered + { + let hovered_option = menu.hovered_option.unwrap_or(0); - if let Some(option) = - state.filtered_options.options.get(hovered_option) - { - shell.publish(on_option_hovered(option.clone())); - published_message_to_shell = true; - } + if let Some(option) = + state.filtered_options.options.get(hovered_option) + { + shell.publish(on_option_hovered(option.clone())); + published_message_to_shell = true; } } @@ -625,12 +624,11 @@ where let shift_modifier = modifiers.shift(); match (named_key, shift_modifier) { (key::Named::Enter, _) => { - if let Some(index) = &menu.hovered_option { - if let Some(option) = + if let Some(index) = &menu.hovered_option + && let Some(option) = state.filtered_options.options.get(*index) - { - menu.new_selection = Some(option.clone()); - } + { + menu.new_selection = Some(option.clone()); } shell.capture_event(); @@ -653,21 +651,19 @@ where if let Some(on_option_hovered) = &mut self.on_option_hovered - { - if let Some(option) = + && let Some(option) = menu.hovered_option.and_then(|index| { state .filtered_options .options .get(index) }) - { - // Notify the selection - shell.publish((on_option_hovered)( - option.clone(), - )); - published_message_to_shell = true; - } + { + // Notify the selection + shell.publish((on_option_hovered)( + option.clone(), + )); + published_message_to_shell = true; } shell.capture_event(); @@ -701,21 +697,19 @@ where if let Some(on_option_hovered) = &mut self.on_option_hovered - { - if let Some(option) = + && let Some(option) = menu.hovered_option.and_then(|index| { state .filtered_options .options .get(index) }) - { - // Notify the selection - shell.publish((on_option_hovered)( - option.clone(), - )); - published_message_to_shell = true; - } + { + // Notify the selection + shell.publish((on_option_hovered)( + option.clone(), + )); + published_message_to_shell = true; } shell.capture_event(); diff --git a/widget/src/container.rs b/widget/src/container.rs index e925816c..af789fec 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -714,7 +714,7 @@ pub fn bordered_box(theme: &Theme) -> Style { border: Border { width: 1.0, radius: 5.0.into(), - color: palette.background.strong.color, + color: palette.background.weak.color, }, ..Style::default() } diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 429a597c..60627c50 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -25,11 +25,13 @@ 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, Pop, Row, Space, Stack, Themer}; +use crate::{Column, Grid, MouseArea, Pin, Row, Sensor, Space, Stack, Themer}; use std::borrow::Borrow; use std::ops::RangeInclusive; +pub use crate::table::table; + /// Creates a [`Column`] with the given children. /// /// Columns distribute their children vertically. @@ -988,17 +990,19 @@ where }) } -/// Creates a new [`Pop`] widget. +/// Creates a new [`Sensor`] widget. +/// +/// A [`Sensor`] widget can generate messages when its contents are shown, +/// hidden, or resized. /// -/// A [`Pop`] widget can generate messages when it pops in and out of view. /// It can even notify you with anticipation at a given distance! -pub fn pop<'a, Message, Theme, Renderer>( +pub fn sensor<'a, Message, Theme, Renderer>( content: impl Into>, -) -> Pop<'a, (), Message, Theme, Renderer> +) -> Sensor<'a, (), Message, Theme, Renderer> where Renderer: core::Renderer, { - Pop::new(content) + Sensor::new(content) } /// Creates a new [`Scrollable`] with the provided content. diff --git a/widget/src/lib.rs b/widget/src/lib.rs index f39f081a..d08a92f9 100644 --- a/widget/src/lib.rs +++ b/widget/src/lib.rs @@ -26,13 +26,14 @@ pub mod keyed; pub mod overlay; pub mod pane_grid; pub mod pick_list; -pub mod pop; pub mod progress_bar; pub mod radio; pub mod row; pub mod rule; pub mod scrollable; +pub mod sensor; pub mod slider; +pub mod table; pub mod text; pub mod text_editor; pub mod text_input; @@ -73,8 +74,6 @@ pub use pick_list::PickList; #[doc(no_inline)] pub use pin::Pin; #[doc(no_inline)] -pub use pop::Pop; -#[doc(no_inline)] pub use progress_bar::ProgressBar; #[doc(no_inline)] pub use radio::Radio; @@ -85,6 +84,8 @@ pub use rule::Rule; #[doc(no_inline)] pub use scrollable::Scrollable; #[doc(no_inline)] +pub use sensor::Sensor; +#[doc(no_inline)] pub use slider::Slider; #[doc(no_inline)] pub use space::Space; diff --git a/widget/src/markdown.rs b/widget/src/markdown.rs index be88c5f9..35e7549c 100644 --- a/widget/src/markdown.rs +++ b/widget/src/markdown.rs @@ -43,6 +43,7 @@ //! } //! } //! ``` +use crate::core::alignment; use crate::core::border; use crate::core::font::{self, Font}; use crate::core::padding; @@ -107,13 +108,17 @@ impl Content { let mut leftover = std::mem::take(&mut self.state.leftover); leftover.push_str(markdown); + let input = if leftover.trim_end().ends_with('|') { + leftover.trim_end().trim_end_matches('|') + } else { + leftover.as_str() + }; + // Pop the last item let _ = self.items.pop(); // Re-parse last item and new text - for (item, source, broken_links) in - parse_with(&mut self.state, &leftover) - { + for (item, source, broken_links) in parse_with(&mut self.state, input) { if !broken_links.is_empty() { let _ = self.incomplete.insert( self.items.len(), @@ -127,6 +132,8 @@ impl Content { self.items.push(item); } + self.state.leftover.push_str(&leftover[input.len()..]); + // Re-parse incomplete sections if new references are available if !self.incomplete.is_empty() { self.incomplete.retain(|index, section| { @@ -215,6 +222,29 @@ pub enum Item { Quote(Vec), /// A horizontal separator. Rule, + /// A table. + Table { + /// The columns of the table. + columns: Vec, + /// The rows of the table. + rows: Vec, + }, +} + +/// The column of a table. +#[derive(Debug, Clone)] +pub struct Column { + /// The header of the column. + pub header: Vec, + /// The alignment of the column. + pub alignment: pulldown_cmark::Alignment, +} + +/// The row of a table. +#[derive(Debug, Clone)] +pub struct Row { + /// The cells of the row. + cells: Vec>, } /// A bunch of parsed Markdown text. @@ -462,6 +492,12 @@ fn parse_with<'a>( enum Scope { List(List), Quote(Vec), + Table { + alignment: Vec, + columns: Vec, + rows: Vec, + current: Vec, + }, } struct List { @@ -479,7 +515,6 @@ fn parse_with<'a>( let mut emphasis = false; let mut strikethrough = false; let mut metadata = false; - let mut table = false; let mut code_block = false; let mut link = None; let mut image = None; @@ -535,6 +570,9 @@ fn parse_with<'a>( Scope::Quote(items) => { items.push(item); } + Scope::Table { current, .. } => { + current.push(item); + } } None @@ -555,21 +593,19 @@ fn parse_with<'a>( #[allow(clippy::drain_collect)] parser.filter_map(move |(event, source)| match event { pulldown_cmark::Event::Start(tag) => match tag { - pulldown_cmark::Tag::Strong if !metadata && !table => { + pulldown_cmark::Tag::Strong if !metadata => { strong = true; None } - pulldown_cmark::Tag::Emphasis if !metadata && !table => { + pulldown_cmark::Tag::Emphasis if !metadata => { emphasis = true; None } - pulldown_cmark::Tag::Strikethrough if !metadata && !table => { + pulldown_cmark::Tag::Strikethrough if !metadata => { strikethrough = true; None } - pulldown_cmark::Tag::Link { dest_url, .. } - if !metadata && !table => - { + pulldown_cmark::Tag::Link { dest_url, .. } if !metadata => { match Url::parse(&dest_url) { Ok(url) if url.scheme() == "http" @@ -584,13 +620,13 @@ fn parse_with<'a>( } pulldown_cmark::Tag::Image { dest_url, title, .. - } if !metadata && !table => { + } if !metadata => { image = Url::parse(&dest_url) .ok() .map(|url| (url, title.into_string())); None } - pulldown_cmark::Tag::List(first_item) if !metadata && !table => { + pulldown_cmark::Tag::List(first_item) if !metadata => { let prev = if spans.is_empty() { None } else { @@ -616,7 +652,7 @@ fn parse_with<'a>( None } - pulldown_cmark::Tag::BlockQuote(_kind) if !metadata && !table => { + pulldown_cmark::Tag::BlockQuote(_kind) if !metadata => { let prev = if spans.is_empty() { None } else { @@ -634,7 +670,7 @@ fn parse_with<'a>( } pulldown_cmark::Tag::CodeBlock( pulldown_cmark::CodeBlockKind::Fenced(language), - ) if !metadata && !table => { + ) if !metadata => { #[cfg(feature = "highlighter")] { highlighter = Some({ @@ -672,38 +708,54 @@ fn parse_with<'a>( metadata = true; None } - pulldown_cmark::Tag::Table(_) => { - table = true; + pulldown_cmark::Tag::Table(alignment) => { + stack.push(Scope::Table { + columns: Vec::with_capacity(alignment.len()), + alignment, + current: Vec::new(), + rows: Vec::new(), + }); + + None + } + pulldown_cmark::Tag::TableHead => { + strong = true; + None + } + pulldown_cmark::Tag::TableRow => { + let Scope::Table { rows, .. } = stack.last_mut()? else { + return None; + }; + + rows.push(Row { cells: Vec::new() }); None } _ => None, }, pulldown_cmark::Event::End(tag) => match tag { - pulldown_cmark::TagEnd::Heading(level) if !metadata && !table => { - produce( - state.borrow_mut(), - &mut stack, - Item::Heading(level, Text::new(spans.drain(..).collect())), - source, - ) - } - pulldown_cmark::TagEnd::Strong if !metadata && !table => { + pulldown_cmark::TagEnd::Heading(level) if !metadata => produce( + state.borrow_mut(), + &mut stack, + Item::Heading(level, Text::new(spans.drain(..).collect())), + source, + ), + pulldown_cmark::TagEnd::Strong if !metadata => { strong = false; None } - pulldown_cmark::TagEnd::Emphasis if !metadata && !table => { + pulldown_cmark::TagEnd::Emphasis if !metadata => { emphasis = false; None } - pulldown_cmark::TagEnd::Strikethrough if !metadata && !table => { + pulldown_cmark::TagEnd::Strikethrough if !metadata => { strikethrough = false; None } - pulldown_cmark::TagEnd::Link if !metadata && !table => { + pulldown_cmark::TagEnd::Link if !metadata => { link = None; None } - pulldown_cmark::TagEnd::Paragraph if !metadata && !table => { + pulldown_cmark::TagEnd::Paragraph if !metadata => { if spans.is_empty() { None } else { @@ -715,7 +767,7 @@ fn parse_with<'a>( ) } } - pulldown_cmark::TagEnd::Item if !metadata && !table => { + pulldown_cmark::TagEnd::Item if !metadata => { if spans.is_empty() { None } else { @@ -727,7 +779,7 @@ fn parse_with<'a>( ) } } - pulldown_cmark::TagEnd::List(_) if !metadata && !table => { + pulldown_cmark::TagEnd::List(_) if !metadata => { let scope = stack.pop()?; let Scope::List(list) = scope else { @@ -744,9 +796,7 @@ fn parse_with<'a>( source, ) } - pulldown_cmark::TagEnd::BlockQuote(_kind) - if !metadata && !table => - { + pulldown_cmark::TagEnd::BlockQuote(_kind) if !metadata => { let scope = stack.pop()?; let Scope::Quote(quote) = scope else { @@ -760,7 +810,7 @@ fn parse_with<'a>( source, ) } - pulldown_cmark::TagEnd::Image if !metadata && !table => { + pulldown_cmark::TagEnd::Image if !metadata => { let (url, title) = image.take()?; let alt = Text::new(spans.drain(..).collect()); @@ -774,7 +824,7 @@ fn parse_with<'a>( source, ) } - pulldown_cmark::TagEnd::CodeBlock if !metadata && !table => { + pulldown_cmark::TagEnd::CodeBlock if !metadata => { code_block = false; #[cfg(feature = "highlighter")] @@ -798,12 +848,60 @@ fn parse_with<'a>( None } pulldown_cmark::TagEnd::Table => { - table = false; + let scope = stack.pop()?; + + let Scope::Table { columns, rows, .. } = scope else { + return None; + }; + + produce( + state.borrow_mut(), + &mut stack, + Item::Table { columns, rows }, + source, + ) + } + pulldown_cmark::TagEnd::TableHead => { + strong = false; + None + } + pulldown_cmark::TagEnd::TableCell => { + if !spans.is_empty() { + let _ = produce( + state.borrow_mut(), + &mut stack, + Item::Paragraph(Text::new(spans.drain(..).collect())), + source, + ); + } + + let Scope::Table { + alignment, + columns, + rows, + current, + } = stack.last_mut()? + else { + return None; + }; + + if columns.len() < alignment.len() { + columns.push(Column { + header: std::mem::take(current), + alignment: alignment[columns.len()], + }); + } else { + rows.last_mut() + .expect("table row") + .cells + .push(std::mem::take(current)); + } + None } _ => None, }, - pulldown_cmark::Event::Text(text) if !metadata && !table => { + pulldown_cmark::Event::Text(text) if !metadata => { if code_block { code.push_str(&text); @@ -844,7 +942,7 @@ fn parse_with<'a>( None } - pulldown_cmark::Event::Code(code) if !metadata && !table => { + pulldown_cmark::Event::Code(code) if !metadata => { let span = Span::Standard { text: code.into_string(), strong, @@ -857,7 +955,7 @@ fn parse_with<'a>( spans.push(span); None } - pulldown_cmark::Event::SoftBreak if !metadata && !table => { + pulldown_cmark::Event::SoftBreak if !metadata => { spans.push(Span::Standard { text: String::from(" "), strikethrough, @@ -868,7 +966,7 @@ fn parse_with<'a>( }); None } - pulldown_cmark::Event::HardBreak if !metadata && !table => { + pulldown_cmark::Event::HardBreak if !metadata => { spans.push(Span::Standard { text: String::from("\n"), strikethrough, @@ -974,8 +1072,8 @@ impl Style { Self { inline_code_padding: padding::left(1).right(1), inline_code_highlight: Highlight { - background: color!(0x111).into(), - border: border::rounded(2), + background: color!(0x111111).into(), + border: border::rounded(4), }, inline_code_color: Color::WHITE, link_color: palette.primary, @@ -1113,6 +1211,7 @@ where } => viewer.ordered_list(settings, *start, items), Item::Quote(quote) => viewer.quote(settings, quote), Item::Rule => viewer.rule(settings), + Item::Table { columns, rows } => viewer.table(settings, columns, rows), } } @@ -1222,9 +1321,14 @@ where Theme: Catalog + 'a, Renderer: core::text::Renderer + 'a, { + let digits = ((start + items.len() as u64).max(1) as f32).log10().ceil(); + column(items.iter().enumerate().map(|(i, items)| { row![ - text!("{}.", i as u64 + start).size(settings.text_size), + text!("{}.", i as u64 + start) + .size(settings.text_size) + .align_x(alignment::Horizontal::Right) + .width(settings.text_size * ((digits / 2.0).ceil() + 1.0)), view_with( items, Settings { @@ -1238,7 +1342,6 @@ where .into() })) .spacing(settings.spacing * 0.75) - .padding([0.0, settings.spacing.0]) .into() } @@ -1313,6 +1416,80 @@ where horizontal_rule(2).into() } +/// Displays a table using the default look. +pub fn table<'a, Message, Theme, Renderer>( + viewer: &impl Viewer<'a, Message, Theme, Renderer>, + settings: Settings, + columns: &'a [Column], + rows: &'a [Row], +) -> Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: Catalog + 'a, + Renderer: core::text::Renderer + 'a, +{ + use crate::table; + + let table = table( + columns.iter().enumerate().map(move |(i, column)| { + table::column( + items(viewer, settings, &column.header), + move |row: &Row| { + if let Some(cells) = row.cells.get(i) { + items(viewer, settings, cells) + } else { + text("").into() + } + }, + ) + .align_x(match column.alignment { + pulldown_cmark::Alignment::None + | pulldown_cmark::Alignment::Left => { + alignment::Horizontal::Left + } + pulldown_cmark::Alignment::Center => { + alignment::Horizontal::Center + } + pulldown_cmark::Alignment::Right => { + alignment::Horizontal::Right + } + }) + }), + rows, + ) + .padding_x(settings.spacing.0) + .padding_y(settings.spacing.0 / 2.0) + .separator_x(0); + + scrollable(table) + .direction(scrollable::Direction::Horizontal( + scrollable::Scrollbar::default(), + )) + .spacing(settings.spacing.0 / 2.0) + .into() +} + +/// Displays a column of items with the default look. +pub fn items<'a, Message, Theme, Renderer>( + viewer: &impl Viewer<'a, Message, Theme, Renderer>, + settings: Settings, + items: &'a [Item], +) -> Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: Catalog + 'a, + Renderer: core::text::Renderer + 'a, +{ + column( + items + .iter() + .enumerate() + .map(|(i, content)| item(viewer, settings, content, i)), + ) + .spacing(settings.spacing.0) + .into() +} + /// A view strategy to display a Markdown [`Item`]. pub trait Viewer<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> where @@ -1429,6 +1606,18 @@ where ) -> Element<'a, Message, Theme, Renderer> { rule() } + + /// Displays a table. + /// + /// By default, it calls [`table`]. + fn table( + &self, + settings: Settings, + columns: &'a [Column], + rows: &'a [Row], + ) -> Element<'a, Message, Theme, Renderer> { + table(self, settings, columns, rows) + } } #[derive(Debug, Clone, Copy)] @@ -1446,7 +1635,11 @@ where /// The theme catalog of Markdown items. pub trait Catalog: - container::Catalog + scrollable::Catalog + rule::Catalog + text::Catalog + container::Catalog + + scrollable::Catalog + + rule::Catalog + + text::Catalog + + crate::table::Catalog { /// The styling class of a Markdown code block. fn code_block<'a>() -> ::Class<'a>; diff --git a/widget/src/mouse_area.rs b/widget/src/mouse_area.rs index e17ccd97..fefa68d5 100644 --- a/widget/src/mouse_area.rs +++ b/widget/src/mouse_area.rs @@ -377,24 +377,24 @@ fn update( shell.capture_event(); } - if let Some(position) = cursor_position { - if let Some(message) = widget.on_double_click.as_ref() { - let new_click = mouse::Click::new( - position, - mouse::Button::Left, - state.previous_click, - ); + if let Some(position) = cursor_position + && let Some(message) = widget.on_double_click.as_ref() + { + let new_click = mouse::Click::new( + position, + mouse::Button::Left, + state.previous_click, + ); - if new_click.kind() == mouse::click::Kind::Double { - shell.publish(message.clone()); - } - - state.previous_click = Some(new_click); - - // Even if this is not a double click, but the press is nevertheless - // processed by us and should not be popup to parent widgets. - shell.capture_event(); + if new_click.kind() == mouse::click::Kind::Double { + shell.publish(message.clone()); } + + state.previous_click = Some(new_click); + + // Even if this is not a double click, but the press is nevertheless + // processed by us and should not be popup to parent widgets. + shell.capture_event(); } } Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs index 7778768a..741dbf15 100644 --- a/widget/src/overlay/menu.rs +++ b/widget/src/overlay/menu.rs @@ -407,13 +407,12 @@ where ) { match event { Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => { - if cursor.is_over(layout.bounds()) { - if let Some(index) = *self.hovered_option { - if let Some(option) = self.options.get(index) { - shell.publish((self.on_selected)(option.clone())); - shell.capture_event(); - } - } + if cursor.is_over(layout.bounds()) + && let Some(index) = *self.hovered_option + && let Some(option) = self.options.get(index) + { + shell.publish((self.on_selected)(option.clone())); + shell.capture_event(); } } Event::Mouse(mouse::Event::CursorMoved { .. }) => { @@ -431,19 +430,16 @@ where let new_hovered_option = (cursor_position.y / option_height) as usize; - if *self.hovered_option != Some(new_hovered_option) { - if let Some(option) = + if *self.hovered_option != Some(new_hovered_option) + && let Some(option) = self.options.get(new_hovered_option) + { + if let Some(on_option_hovered) = self.on_option_hovered { - if let Some(on_option_hovered) = - self.on_option_hovered - { - shell - .publish(on_option_hovered(option.clone())); - } - - shell.request_redraw(); + shell.publish(on_option_hovered(option.clone())); } + + shell.request_redraw(); } *self.hovered_option = Some(new_hovered_option); @@ -464,11 +460,11 @@ where *self.hovered_option = Some((cursor_position.y / option_height) as usize); - if let Some(index) = *self.hovered_option { - if let Some(option) = self.options.get(index) { - shell.publish((self.on_selected)(option.clone())); - shell.capture_event(); - } + if let Some(index) = *self.hovered_option + && let Some(option) = self.options.get(index) + { + shell.publish((self.on_selected)(option.clone())); + shell.capture_event(); } } } diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index db60fabc..158c265b 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -593,56 +593,47 @@ where Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) | Event::Touch(touch::Event::FingerLifted { .. }) | Event::Touch(touch::Event::FingerLost { .. }) => { - if let Some((pane, origin)) = action.picked_pane() { - if let Some(on_drag) = on_drag { - if let Some(cursor_position) = cursor.position() { - if cursor_position.distance(origin) - > DRAG_DEADBAND_DISTANCE - { - let event = if let Some(edge) = - in_edge(layout, cursor_position) + if let Some((pane, origin)) = action.picked_pane() + && let Some(on_drag) = on_drag + && let Some(cursor_position) = cursor.position() + { + if cursor_position.distance(origin) > DRAG_DEADBAND_DISTANCE + { + let event = if let Some(edge) = + in_edge(layout, cursor_position) + { + DragEvent::Dropped { + pane, + target: Target::Edge(edge), + } + } else { + let dropped_region = self + .panes + .iter() + .copied() + .zip(&self.contents) + .zip(layout.children()) + .find_map(|(target, layout)| { + layout_region(layout, cursor_position) + .map(|region| (target, region)) + }); + + match dropped_region { + Some(((target, _), region)) + if pane != target => { DragEvent::Dropped { pane, - target: Target::Edge(edge), + target: Target::Pane(target, region), } - } else { - let dropped_region = self - .panes - .iter() - .copied() - .zip(&self.contents) - .zip(layout.children()) - .find_map(|(target, layout)| { - layout_region( - layout, - cursor_position, - ) - .map(|region| (target, region)) - }); - - match dropped_region { - Some(((target, _), region)) - if pane != target => - { - DragEvent::Dropped { - pane, - target: Target::Pane( - target, region, - ), - } - } - _ => DragEvent::Canceled { pane }, - } - }; - - shell.publish(on_drag(event)); - } else { - shell.publish(on_drag(DragEvent::Canceled { - pane, - })); + } + _ => DragEvent::Canceled { pane }, } - } + }; + + shell.publish(on_drag(event)); + } else { + shell.publish(on_drag(DragEvent::Canceled { pane })); } } @@ -660,34 +651,33 @@ where bounds.size(), ); - if let Some((axis, rectangle, _)) = splits.get(&split) { - if let Some(cursor_position) = cursor.position() { - let ratio = match axis { - Axis::Horizontal => { - let position = cursor_position.y - - bounds.y - - rectangle.y; + if let Some((axis, rectangle, _)) = splits.get(&split) + && let Some(cursor_position) = cursor.position() + { + let ratio = match axis { + Axis::Horizontal => { + let position = cursor_position.y + - bounds.y + - rectangle.y; - (position / rectangle.height) - .clamp(0.0, 1.0) - } - Axis::Vertical => { - let position = cursor_position.x - - bounds.x - - rectangle.x; + (position / rectangle.height) + .clamp(0.0, 1.0) + } + Axis::Vertical => { + let position = cursor_position.x + - bounds.x + - rectangle.x; - (position / rectangle.width) - .clamp(0.0, 1.0) - } - }; + (position / rectangle.width).clamp(0.0, 1.0) + } + }; - shell.publish(on_resize(ResizeEvent { - split, - ratio, - })); + shell.publish(on_resize(ResizeEvent { + split, + ratio, + })); - shell.capture_event(); - } + shell.capture_event(); } } else if action.picked_pane().is_some() { shell.request_redraw(); @@ -889,24 +879,23 @@ where viewport, ); - if picked_pane.is_some() && pane_in_edge.is_none() { - if let Some(region) = + if picked_pane.is_some() + && pane_in_edge.is_none() + && let Some(region) = cursor.position().and_then(|cursor_position| { layout_region(pane_layout, cursor_position) }) - { - let bounds = - layout_region_bounds(pane_layout, region); + { + let bounds = layout_region_bounds(pane_layout, region); - renderer.fill_quad( - renderer::Quad { - bounds, - border: style.hovered_region.border, - ..renderer::Quad::default() - }, - style.hovered_region.background, - ); - } + renderer.fill_quad( + renderer::Quad { + bounds, + border: style.hovered_region.border, + ..renderer::Quad::default() + }, + style.hovered_region.background, + ); } } _ => { @@ -937,64 +926,62 @@ where } // Render picked pane last - if let Some(((content, tree), origin, layout)) = render_picked_pane { - if let Some(cursor_position) = cursor.position() { - let bounds = layout.bounds(); + if let Some(((content, tree), origin, layout)) = render_picked_pane + && let Some(cursor_position) = cursor.position() + { + let bounds = layout.bounds(); - let translation = - cursor_position - Point::new(origin.x, origin.y); + let translation = cursor_position - Point::new(origin.x, origin.y); - renderer.with_translation(translation, |renderer| { - renderer.with_layer(bounds, |renderer| { - content.draw( - tree, - renderer, - theme, - defaults, - layout, - pane_cursor, - viewport, - ); - }); + renderer.with_translation(translation, |renderer| { + renderer.with_layer(bounds, |renderer| { + content.draw( + tree, + renderer, + theme, + defaults, + layout, + pane_cursor, + viewport, + ); }); - } + }); } - if picked_pane.is_none() { - if let Some((axis, split_region, is_picked)) = picked_split { - let highlight = if is_picked { - style.picked_split - } else { - style.hovered_split - }; + if picked_pane.is_none() + && let Some((axis, split_region, is_picked)) = picked_split + { + let highlight = if is_picked { + style.picked_split + } else { + style.hovered_split + }; - renderer.fill_quad( - renderer::Quad { - bounds: match axis { - Axis::Horizontal => Rectangle { - x: split_region.x, - y: (split_region.y - + (split_region.height - highlight.width) - / 2.0) - .round(), - width: split_region.width, - height: highlight.width, - }, - Axis::Vertical => Rectangle { - x: (split_region.x - + (split_region.width - highlight.width) - / 2.0) - .round(), - y: split_region.y, - width: highlight.width, - height: split_region.height, - }, + renderer.fill_quad( + renderer::Quad { + bounds: match axis { + Axis::Horizontal => Rectangle { + x: split_region.x, + y: (split_region.y + + (split_region.height - highlight.width) + / 2.0) + .round(), + width: split_region.width, + height: highlight.width, + }, + Axis::Vertical => Rectangle { + x: (split_region.x + + (split_region.width - highlight.width) / 2.0) + .round(), + y: split_region.y, + width: highlight.width, + height: split_region.height, }, - ..renderer::Quad::default() }, - highlight.color, - ); - } + ..renderer::Quad::default() + }, + highlight.color, + ); } } @@ -1086,15 +1073,15 @@ fn click_pane<'a, Message, T>( shell.publish(on_click(pane)); } - if let Some(on_drag) = &on_drag { - if content.can_be_dragged_at(layout, cursor_position) { - *action = state::Action::Dragging { - pane, - origin: cursor_position, - }; + if let Some(on_drag) = &on_drag + && content.can_be_dragged_at(layout, cursor_position) + { + *action = state::Action::Dragging { + pane, + origin: cursor_position, + }; - shell.publish(on_drag(DragEvent::Picked { pane })); - } + shell.publish(on_drag(DragEvent::Picked { pane })); } } } diff --git a/widget/src/pane_grid/state.rs b/widget/src/pane_grid/state.rs index d999434f..84c971bf 100644 --- a/widget/src/pane_grid/state.rs +++ b/widget/src/pane_grid/state.rs @@ -219,14 +219,14 @@ impl State { pane: Pane, swap: bool, ) { - if let Some((state, _)) = self.close(pane) { - if let Some((new_pane, _)) = self.split(axis, target, state) { - // Ensure new node corresponds to original closed `Pane` for state continuity - self.relabel(new_pane, pane); + if let Some((state, _)) = self.close(pane) + && let Some((new_pane, _)) = self.split(axis, target, state) + { + // Ensure new node corresponds to original closed `Pane` for state continuity + self.relabel(new_pane, pane); - if swap { - self.swap(target, pane); - } + if swap { + self.swap(target, pane); } } } @@ -257,13 +257,12 @@ impl State { pane: Pane, inverse: bool, ) { - if let Some((state, _)) = self.close(pane) { - if let Some((new_pane, _)) = + if let Some((state, _)) = self.close(pane) + && let Some((new_pane, _)) = self.split_node(axis, None, state, inverse) - { - // Ensure new node corresponds to original closed `Pane` for state continuity - self.relabel(new_pane, pane); - } + { + // Ensure new node corresponds to original closed `Pane` for state continuity + self.relabel(new_pane, pane); } } diff --git a/widget/src/pane_grid/title_bar.rs b/widget/src/pane_grid/title_bar.rs index 5cd53b50..e47f4a10 100644 --- a/widget/src/pane_grid/title_bar.rs +++ b/widget/src/pane_grid/title_bar.rs @@ -174,38 +174,28 @@ where let title_layout = children.next().unwrap(); let mut show_title = true; - if let Some(controls) = &self.controls { - if show_controls || self.always_show_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() { - let compact_layout = children.next().unwrap(); + if let Some(controls) = &self.controls + && (show_controls || self.always_show_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() { + let compact_layout = children.next().unwrap(); - compact.as_widget().draw( - &tree.children[2], - renderer, - theme, - &inherited_style, - compact_layout, - cursor, - viewport, - ); - } else { - show_title = false; - - controls.full.as_widget().draw( - &tree.children[1], - renderer, - theme, - &inherited_style, - controls_layout, - cursor, - viewport, - ); - } + compact.as_widget().draw( + &tree.children[2], + renderer, + theme, + &inherited_style, + compact_layout, + cursor, + viewport, + ); } else { + show_title = false; + controls.full.as_widget().draw( &tree.children[1], renderer, @@ -216,6 +206,16 @@ where viewport, ); } + } else { + controls.full.as_widget().draw( + &tree.children[1], + renderer, + theme, + &inherited_style, + controls_layout, + cursor, + viewport, + ); } } diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs index 517ff35d..4076d7f7 100644 --- a/widget/src/pick_list.rs +++ b/widget/src/pick_list.rs @@ -899,7 +899,7 @@ pub fn default(theme: &Theme, status: Status) -> Style { let active = Style { text_color: palette.background.weak.text, background: palette.background.weak.color.into(), - placeholder_color: palette.background.strong.color, + placeholder_color: palette.secondary.base.color, handle_color: palette.background.weak.text, border: Border { radius: 2.0.into(), diff --git a/widget/src/row.rs b/widget/src/row.rs index a51106d1..101e51d8 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -136,23 +136,13 @@ where let child = child.into(); let child_size = child.as_widget().size_hint(); - self.width = self.width.enclose(child_size.width); - self.height = self.height.enclose(child_size.height); - - self.children.push(child); - self - } - - /// Adds an element to the [`Row`], if `Some`. - pub fn push_maybe( - self, - child: Option>>, - ) -> Self { - if let Some(child) = child { - self.push(child) - } else { - self + if !child_size.is_void() { + self.width = self.width.enclose(child_size.width); + self.height = self.height.enclose(child_size.height); + self.children.push(child); } + + self } /// Extends the [`Row`] with the given children. @@ -170,6 +160,7 @@ where Wrapping { row: self, vertical_spacing: None, + align_x: alignment::Horizontal::Left, } } } @@ -378,6 +369,7 @@ pub struct Wrapping< > { row: Row<'a, Message, Theme, Renderer>, vertical_spacing: Option, + align_x: alignment::Horizontal, } impl Wrapping<'_, Message, Theme, Renderer> { @@ -386,6 +378,15 @@ impl Wrapping<'_, Message, Theme, Renderer> { self.vertical_spacing = Some(amount.into().0); self } + + /// Sets the horizontal alignment of the wrapping [`Row`]. + pub fn align_x( + mut self, + align_x: impl Into, + ) -> Self { + self.align_x = align_x.into(); + self + } } impl Widget @@ -433,9 +434,9 @@ where Alignment::End => 1.0, }; - let align = |row_start: std::ops::Range, - row_height: f32, - children: &mut Vec| { + let align_y = |row_start: std::ops::Range, + row_height: f32, + children: &mut Vec| { if align_factor != 0.0 { for node in &mut children[row_start] { let height = node.size().height; @@ -460,7 +461,7 @@ where if x != 0.0 && x + child_size.width > max_width { intrinsic_size.width = intrinsic_size.width.max(x - spacing); - align(row_start..i, row_height, &mut children); + align_y(row_start..i, row_height, &mut children); y += row_height + vertical_spacing; x = 0.0; @@ -483,7 +484,42 @@ where } intrinsic_size.height = y + row_height; - align(row_start..children.len(), row_height, &mut children); + align_y(row_start..children.len(), row_height, &mut children); + + let align_factor = match self.align_x { + alignment::Horizontal::Left => 0.0, + alignment::Horizontal::Center => 2.0, + alignment::Horizontal::Right => 1.0, + }; + + if align_factor != 0.0 { + let total_width = intrinsic_size.width; + + let mut row_start = 0; + + for i in 0..children.len() { + let bounds = children[i].bounds(); + let row_width = bounds.x + bounds.width; + + let next_x = children + .get(i + 1) + .map(|node| node.bounds().x) + .unwrap_or_default(); + + if next_x == 0.0 { + let translation = Vector::new( + (total_width - row_width) / align_factor, + 0.0, + ); + + for node in &mut children[row_start..=i] { + node.translate_mut(translation); + } + + row_start = i + 1; + } + } + } let size = limits.resolve(self.row.width, self.row.height, intrinsic_size); diff --git a/widget/src/rule.rs b/widget/src/rule.rs index 2bb893b2..d70109bc 100644 --- a/widget/src/rule.rs +++ b/widget/src/rule.rs @@ -134,7 +134,7 @@ where let style = theme.style(&self.class); let bounds = if self.is_horizontal { - let line_y = (bounds.y + (bounds.height / 2.0)).round(); + let line_y = bounds.y.round(); let (offset, line_width) = style.fill_mode.fill(bounds.width); let line_x = bounds.x + offset; @@ -146,7 +146,7 @@ where height: bounds.height, } } else { - let line_x = (bounds.x + (bounds.width / 2.0)).round(); + let line_x = bounds.x.round(); let (offset, line_height) = style.fill_mode.fill(bounds.height); let line_y = bounds.y + offset; @@ -300,3 +300,15 @@ pub fn default(theme: &Theme) -> Style { snap: true, } } + +/// A [`Rule`] styling using the weak background color. +pub fn weak(theme: &Theme) -> Style { + let palette = theme.extended_palette(); + + Style { + color: palette.background.weak.color, + radius: 0.0.into(), + fill_mode: FillMode::Full, + snap: true, + } +} diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index 01c4ef7c..baff578e 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -798,12 +798,11 @@ where }, ); - if !had_input_method { - if let InputMethod::Enabled { position, .. } = + if !had_input_method + && let InputMethod::Enabled { position, .. } = shell.input_method_mut() - { - *position = *position - translation; - } + { + *position = *position - translation; } }; @@ -1091,23 +1090,22 @@ where ); } - if let Some(scroller) = scrollbar.scroller { - if scroller.bounds.width > 0.0 - && scroller.bounds.height > 0.0 - && (style.scroller.color != Color::TRANSPARENT - || (style.scroller.border.color - != Color::TRANSPARENT - && style.scroller.border.width > 0.0)) - { - renderer.fill_quad( - renderer::Quad { - bounds: scroller.bounds, - border: style.scroller.border, - ..renderer::Quad::default() - }, - style.scroller.color, - ); - } + if let Some(scroller) = scrollbar.scroller + && scroller.bounds.width > 0.0 + && scroller.bounds.height > 0.0 + && (style.scroller.color != Color::TRANSPARENT + || (style.scroller.border.color + != Color::TRANSPARENT + && style.scroller.border.width > 0.0)) + { + renderer.fill_quad( + renderer::Quad { + bounds: scroller.bounds, + border: style.scroller.border, + ..renderer::Quad::default() + }, + style.scroller.color, + ); } }; @@ -2069,7 +2067,7 @@ pub fn default(theme: &Theme, status: Status) -> Style { background: Some(palette.background.weak.color.into()), border: border::rounded(2), scroller: Scroller { - color: palette.background.strong.color, + color: palette.background.strongest.color, border: border::rounded(2), }, }; diff --git a/widget/src/pop.rs b/widget/src/sensor.rs similarity index 89% rename from widget/src/pop.rs rename to widget/src/sensor.rs index 8c6236bc..da56e0c9 100644 --- a/widget/src/pop.rs +++ b/widget/src/sensor.rs @@ -16,7 +16,7 @@ use crate::core::{ /// /// It can even notify you with anticipation at a given distance! #[allow(missing_debug_implementations)] -pub struct Pop< +pub struct Sensor< 'a, Key, Message, @@ -32,11 +32,11 @@ pub struct Pop< delay: Duration, } -impl<'a, Message, Theme, Renderer> Pop<'a, (), Message, Theme, Renderer> +impl<'a, Message, Theme, Renderer> Sensor<'a, (), Message, Theme, Renderer> where Renderer: core::Renderer, { - /// Creates a new [`Pop`] widget with the given content. + /// Creates a new [`Sensor`] widget with the given content. pub fn new( content: impl Into>, ) -> Self { @@ -52,7 +52,8 @@ where } } -impl<'a, Key, Message, Theme, Renderer> Pop<'a, Key, Message, Theme, Renderer> +impl<'a, Key, Message, Theme, Renderer> + Sensor<'a, Key, Message, Theme, Renderer> where Key: self::Key, Renderer: core::Renderer, @@ -82,17 +83,17 @@ where self } - /// Sets the key of the [`Pop`] widget, for continuity. + /// Sets the key of the [`Sensor`] widget, for continuity. /// - /// If the key changes, the [`Pop`] widget will trigger again. + /// If the key changes, the [`Sensor`] widget will trigger again. pub fn key( self, key: K, - ) -> Pop<'a, impl self::Key, Message, Theme, Renderer> + ) -> Sensor<'a, impl self::Key, Message, Theme, Renderer> where K: Clone + PartialEq + 'static, { - Pop { + Sensor { content: self.content, key: OwnedKey(key), on_show: self.on_show, @@ -103,18 +104,18 @@ where } } - /// Sets the key of the [`Pop`] widget, for continuity; using a reference. + /// Sets the key of the [`Sensor`], for continuity; using a reference. /// - /// If the key changes, the [`Pop`] widget will trigger again. + /// If the key changes, the [`Sensor`] will trigger again. pub fn key_ref( self, key: &'a K, - ) -> Pop<'a, &'a K, Message, Theme, Renderer> + ) -> Sensor<'a, &'a K, Message, Theme, Renderer> where K: ToOwned + PartialEq + ?Sized, K::Owned: 'static, { - Pop { + Sensor { content: self.content, key, on_show: self.on_show, @@ -158,7 +159,7 @@ struct State { } impl Widget - for Pop<'_, Key, Message, Theme, Renderer> + for Sensor<'_, Key, Message, Theme, Renderer> where Key: self::Key, Renderer: core::Renderer, @@ -212,7 +213,16 @@ where let distance = top_left_distance.min(bottom_right_distance); - if state.has_popped_in { + if self.on_show.is_none() { + if let Some(on_resize) = &self.on_resize { + let size = bounds.size(); + + if Some(size) != state.last_size { + state.last_size = Some(size); + shell.publish(on_resize(size)); + } + } + } else if state.has_popped_in { if distance <= self.anticipate.0 { if let Some(on_resize) = &self.on_resize { let size = bounds.size(); @@ -226,7 +236,7 @@ where state.has_popped_in = false; state.should_notify_at = Some((false, *now + self.delay)); } - } else if self.on_show.is_some() && distance <= self.anticipate.0 { + } else if distance <= self.anticipate.0 { let size = bounds.size(); state.has_popped_in = true; @@ -356,7 +366,7 @@ where } impl<'a, Key, Message, Theme, Renderer> - From> + From> for Element<'a, Message, Theme, Renderer> where Message: 'a, @@ -364,7 +374,7 @@ where Renderer: core::Renderer + 'a, Theme: 'a, { - fn from(pop: Pop<'a, Key, Message, Theme, Renderer>) -> Self { + fn from(pop: Sensor<'a, Key, Message, Theme, Renderer>) -> Self { Element::new(pop) } } diff --git a/widget/src/table.rs b/widget/src/table.rs new file mode 100644 index 00000000..03537f9a --- /dev/null +++ b/widget/src/table.rs @@ -0,0 +1,727 @@ +//! Display tables. +use crate::core; +use crate::core::alignment; +use crate::core::layout; +use crate::core::mouse; +use crate::core::overlay; +use crate::core::renderer; +use crate::core::widget; +use crate::core::{ + Alignment, Background, Element, Layout, Length, Pixels, Rectangle, Size, + Widget, +}; + +/// Creates a new [`Table`] with the given columns and rows. +/// +/// Columns can be created using the [`column()`] function, while rows can be any +/// iterator over some data type `T`. +pub fn table<'a, 'b, T, Message, Theme, Renderer>( + columns: impl IntoIterator>, + rows: impl IntoIterator, +) -> Table<'a, Message, Theme, Renderer> +where + T: Clone, + Theme: Catalog, + Renderer: core::Renderer, +{ + Table::new(columns, rows) +} + +/// Creates a new [`Column`] with the given header and view function. +/// +/// The view function will be called for each row in a [`Table`] and it must +/// produce the resulting contents of a cell. +pub fn column<'a, 'b, T, E, Message, Theme, Renderer>( + header: impl Into>, + view: impl Fn(T) -> E + 'b, +) -> Column<'a, 'b, T, Message, Theme, Renderer> +where + T: 'a, + E: Into>, +{ + Column { + header: header.into(), + view: Box::new(move |data| view(data).into()), + width: Length::Shrink, + align_x: alignment::Horizontal::Left, + align_y: alignment::Vertical::Top, + } +} + +/// A grid-like visual representation of data distributed in columns and rows. +#[allow(missing_debug_implementations)] +pub struct Table<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> +where + Theme: Catalog, +{ + columns: Vec, + cells: Vec>, + width: Length, + height: Length, + padding_x: f32, + padding_y: f32, + separator_x: f32, + separator_y: f32, + class: Theme::Class<'a>, +} + +struct Column_ { + width: Length, + align_x: alignment::Horizontal, + align_y: alignment::Vertical, +} + +impl<'a, Message, Theme, Renderer> Table<'a, Message, Theme, Renderer> +where + Theme: Catalog, + Renderer: core::Renderer, +{ + /// Creates a new [`Table`] with the given columns and rows. + /// + /// Columns can be created using the [`column()`] function, while rows can be any + /// iterator over some data type `T`. + pub fn new<'b, T>( + columns: impl IntoIterator< + Item = Column<'a, 'b, T, Message, Theme, Renderer>, + >, + rows: impl IntoIterator, + ) -> Self + where + T: Clone, + { + let columns = columns.into_iter(); + let rows = rows.into_iter(); + + let mut width = Length::Shrink; + let mut height = Length::Shrink; + + let mut cells = Vec::with_capacity( + columns.size_hint().0 * (1 + rows.size_hint().0), + ); + + let (mut columns, views): (Vec<_>, Vec<_>) = columns + .map(|column| { + width = width.enclose(column.width); + + cells.push(column.header); + + ( + Column_ { + width: column.width, + align_x: column.align_x, + align_y: column.align_y, + }, + column.view, + ) + }) + .collect(); + + for row in rows { + for view in &views { + let cell = view(row.clone()); + let size_hint = cell.as_widget().size_hint(); + + height = height.enclose(size_hint.height); + + cells.push(cell); + } + } + + if width == Length::Shrink + && let Some(first) = columns.first_mut() + { + first.width = Length::Fill; + } + + Self { + columns, + cells, + width, + height, + padding_x: 10.0, + padding_y: 5.0, + separator_x: 1.0, + separator_y: 1.0, + class: Theme::default(), + } + } + + /// Sets the width of the [`Table`]. + pub fn width(mut self, width: impl Into) -> Self { + self.width = width.into(); + self + } + + /// Sets the padding of the cells of the [`Table`]. + pub fn padding(self, padding: impl Into) -> Self { + let padding = padding.into(); + + self.padding_x(padding).padding_y(padding) + } + + /// Sets the horizontal padding of the cells of the [`Table`]. + pub fn padding_x(mut self, padding: impl Into) -> Self { + self.padding_x = padding.into().0; + self + } + + /// Sets the vertical padding of the cells of the [`Table`]. + pub fn padding_y(mut self, padding: impl Into) -> Self { + self.padding_y = padding.into().0; + self + } + + /// Sets the thickness of the line separator between the cells of the [`Table`]. + pub fn separator(self, separator: impl Into) -> Self { + let separator = separator.into(); + + self.separator_x(separator).separator_y(separator) + } + + /// Sets the thickness of the horizontal line separator between the cells of the [`Table`]. + pub fn separator_x(mut self, separator: impl Into) -> Self { + self.separator_x = separator.into().0; + self + } + + /// Sets the thickness of the vertical line separator between the cells of the [`Table`]. + pub fn separator_y(mut self, separator: impl Into) -> Self { + self.separator_y = separator.into().0; + self + } +} + +struct Metrics { + columns: Vec, + rows: Vec, +} + +impl<'a, Message, Theme, Renderer> Widget + for Table<'a, Message, Theme, Renderer> +where + Theme: Catalog, + Renderer: core::Renderer, +{ + fn size(&self) -> Size { + Size { + width: self.width, + height: self.height, + } + } + + fn tag(&self) -> widget::tree::Tag { + widget::tree::Tag::of::() + } + + fn state(&self) -> widget::tree::State { + widget::tree::State::new(Metrics { + columns: Vec::new(), + rows: Vec::new(), + }) + } + + fn children(&self) -> Vec { + self.cells + .iter() + .map(|cell| widget::Tree::new(cell.as_widget())) + .collect() + } + + fn diff(&self, state: &mut widget::Tree) { + state.diff_children(&self.cells); + } + + fn layout( + &self, + tree: &mut widget::Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + let metrics = tree.state.downcast_mut::(); + let columns = self.columns.len(); + let rows = self.cells.len() / columns; + + let limits = limits.width(self.width).height(self.height); + let available = limits.max(); + let table_fluid = self.width.fluid(); + + let mut cells = Vec::with_capacity(self.cells.len()); + cells.resize(self.cells.len(), layout::Node::default()); + + metrics.columns = vec![0.0; self.columns.len()]; + metrics.rows = vec![0.0; rows]; + + let mut column_factors = vec![0; self.columns.len()]; + let mut total_row_factors = 0; + let mut total_fluid_height = 0.0; + let mut row_factor = 0; + + let spacing_x = self.padding_x * 2.0 + self.separator_x; + let spacing_y = self.padding_y * 2.0 + self.separator_y; + + // FIRST PASS + // Lay out non-fluid cells + let mut x = self.padding_x; + let mut y = self.padding_y; + + for (i, (cell, state)) in + self.cells.iter().zip(&mut tree.children).enumerate() + { + let row = i / columns; + let column = i % columns; + + let width = self.columns[column].width; + let size = cell.as_widget().size(); + + if column == 0 { + x = self.padding_x; + + if row > 0 { + y += metrics.rows[row - 1] + spacing_y; + + if row_factor != 0 { + total_fluid_height += metrics.rows[row - 1]; + total_row_factors += row_factor; + + row_factor = 0; + } + } + } + + let width_factor = width.fill_factor(); + let height_factor = size.height.fill_factor(); + + if width_factor != 0 || height_factor != 0 || size.width.is_fill() { + column_factors[column] = + column_factors[column].max(width_factor); + + row_factor = row_factor.max(height_factor); + + continue; + } + + let limits = layout::Limits::new( + Size::ZERO, + Size::new(available.width - x, available.height - y), + ) + .width(width); + + let layout = cell.as_widget().layout(state, renderer, &limits); + let size = limits.resolve(width, Length::Shrink, layout.size()); + + metrics.columns[column] = metrics.columns[column].max(size.width); + metrics.rows[row] = metrics.rows[row].max(size.height); + cells[i] = layout; + + x += size.width + spacing_x; + } + + // SECOND PASS + // Lay out fluid cells, using metrics from the first pass as limits + let left = Size::new( + available.width + - metrics + .columns + .iter() + .enumerate() + .filter(|(i, _)| column_factors[*i] == 0) + .map(|(_, width)| width) + .sum::(), + available.height - total_fluid_height, + ); + + let width_unit = (left.width + - spacing_x * self.columns.len().saturating_sub(1) as f32 + - self.padding_x * 2.0) + / column_factors.iter().sum::() as f32; + + let height_unit = (left.height + - spacing_y * rows.saturating_sub(1) as f32 + - self.padding_y * 2.0) + / total_row_factors as f32; + + let mut x = self.padding_x; + let mut y = self.padding_y; + + for (i, (cell, state)) in + self.cells.iter().zip(&mut tree.children).enumerate() + { + let row = i / columns; + let column = i % columns; + + let size = cell.as_widget().size(); + + let width = self.columns[column].width; + let width_factor = width.fill_factor(); + let height_factor = size.height.fill_factor(); + + if column == 0 { + x = self.padding_x; + + if row > 0 { + y += metrics.rows[row - 1] + spacing_y; + } + } + + if width_factor == 0 + && size.width.fill_factor() == 0 + && size.height.fill_factor() == 0 + { + continue; + } + + let max_width = if width_factor == 0 { + if size.width.is_fill() { + metrics.columns[column] + } else { + (available.width - x).max(0.0) + } + } else { + width_unit * width_factor as f32 + }; + + let max_height = if height_factor == 0 { + if size.height.is_fill() { + metrics.rows[row] + } else { + (available.height - y).max(0.0) + } + } else { + height_unit * height_factor as f32 + }; + + let limits = layout::Limits::new( + Size::ZERO, + Size::new(max_width, max_height), + ) + .width(width); + + let layout = cell.as_widget().layout(state, renderer, &limits); + let size = limits.resolve( + if let Length::Fixed(_) = width { + width + } else { + table_fluid + }, + Length::Shrink, + layout.size(), + ); + + metrics.columns[column] = metrics.columns[column].max(size.width); + metrics.rows[row] = metrics.rows[row].max(size.height); + cells[i] = layout; + + x += size.width + spacing_x; + } + + // THIRD PASS + // Position each cell + let mut x = self.padding_x; + let mut y = self.padding_y; + + for (i, cell) in cells.iter_mut().enumerate() { + let row = i / columns; + let column = i % columns; + + if column == 0 { + x = self.padding_x; + + if row > 0 { + y += metrics.rows[row - 1] + spacing_y; + } + } + + let Column_ { + align_x, align_y, .. + } = &self.columns[column]; + + cell.move_to_mut((x, y)); + cell.align_mut( + Alignment::from(*align_x), + Alignment::from(*align_y), + Size::new(metrics.columns[column], metrics.rows[row]), + ); + + x += metrics.columns[column] + spacing_x; + } + + let intrinsic = limits.resolve( + self.width, + self.height, + Size::new( + x - spacing_x + self.padding_x, + y + metrics + .rows + .last() + .copied() + .map(|height| height + self.padding_y) + .unwrap_or_default(), + ), + ); + + layout::Node::with_children(intrinsic, cells) + } + + fn update( + &mut self, + tree: &mut widget::Tree, + event: &core::Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn core::Clipboard, + shell: &mut core::Shell<'_, Message>, + viewport: &Rectangle, + ) { + for ((cell, state), layout) in self + .cells + .iter_mut() + .zip(&mut tree.children) + .zip(layout.children()) + { + cell.as_widget_mut().update( + state, event, layout, cursor, renderer, clipboard, shell, + viewport, + ); + } + } + + fn draw( + &self, + tree: &widget::Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + for ((cell, state), layout) in + self.cells.iter().zip(&tree.children).zip(layout.children()) + { + cell.as_widget() + .draw(state, renderer, theme, style, layout, cursor, viewport); + } + + let bounds = layout.bounds(); + let metrics = tree.state.downcast_ref::(); + let style = theme.style(&self.class); + + if self.separator_x > 0.0 { + let mut x = self.padding_x; + + for width in + &metrics.columns[..metrics.columns.len().saturating_sub(1)] + { + x += width + self.padding_x; + + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: bounds.x + x, + y: bounds.y, + width: self.separator_x, + height: bounds.height, + }, + snap: true, + ..renderer::Quad::default() + }, + style.separator_x, + ); + + x += self.separator_x + self.padding_x; + } + } + + if self.separator_y > 0.0 { + let mut y = self.padding_y; + + for height in &metrics.rows[..metrics.rows.len().saturating_sub(1)] + { + y += height + self.padding_y; + + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: bounds.x, + y: bounds.y + y, + width: bounds.width, + height: self.separator_y, + }, + snap: true, + ..renderer::Quad::default() + }, + style.separator_y, + ); + + y += self.separator_y + self.padding_y; + } + } + } + + fn mouse_interaction( + &self, + tree: &widget::Tree, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + self.cells + .iter() + .zip(&tree.children) + .zip(layout.children()) + .map(|((cell, state), layout)| { + cell.as_widget().mouse_interaction( + state, layout, cursor, viewport, renderer, + ) + }) + .max() + .unwrap_or_default() + } + + fn operate( + &self, + tree: &mut widget::Tree, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn widget::Operation, + ) { + for ((cell, state), layout) in self + .cells + .iter() + .zip(&mut tree.children) + .zip(layout.children()) + { + cell.as_widget().operate(state, layout, renderer, operation); + } + } + + fn overlay<'b>( + &'b mut self, + state: &'b mut widget::Tree, + layout: Layout<'b>, + renderer: &Renderer, + viewport: &Rectangle, + translation: core::Vector, + ) -> Option> { + overlay::from_children( + &mut self.cells, + state, + layout, + renderer, + viewport, + translation, + ) + } +} + +impl<'a, Message, Theme, Renderer> From> + for Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: Catalog + 'a, + Renderer: core::Renderer + 'a, +{ + fn from(table: Table<'a, Message, Theme, Renderer>) -> Self { + Element::new(table) + } +} + +/// A vertical visualization of some data with a header. +#[allow(missing_debug_implementations)] +pub struct Column< + 'a, + 'b, + T, + Message, + Theme = crate::Theme, + Renderer = crate::Renderer, +> { + header: Element<'a, Message, Theme, Renderer>, + view: Box Element<'a, Message, Theme, Renderer> + 'b>, + width: Length, + align_x: alignment::Horizontal, + align_y: alignment::Vertical, +} + +impl<'a, 'b, T, Message, Theme, Renderer> + Column<'a, 'b, T, Message, Theme, Renderer> +{ + /// Sets the width of the [`Column`]. + pub fn width(mut self, width: impl Into) -> Self { + self.width = width.into(); + self + } + + /// Sets the alignment for the horizontal axis of the [`Column`]. + pub fn align_x( + mut self, + alignment: impl Into, + ) -> Self { + self.align_x = alignment.into(); + self + } + + /// Sets the alignment for the vertical axis of the [`Column`]. + pub fn align_y( + mut self, + alignment: impl Into, + ) -> Self { + self.align_y = alignment.into(); + self + } +} + +/// The appearance of a [`Table`]. +#[derive(Debug, Clone, Copy)] +pub struct Style { + /// The background color of the horizontal line separator between cells. + pub separator_x: Background, + /// The background color of the vertical line separator between cells. + pub separator_y: Background, +} + +/// The theme catalog of a [`Table`]. +pub trait Catalog { + /// The item class of the [`Catalog`]. + type Class<'a>; + + /// The default class produced by the [`Catalog`]. + fn default<'a>() -> Self::Class<'a>; + + /// The [`Style`] of a class with the given status. + fn style(&self, class: &Self::Class<'_>) -> Style; +} + +/// A styling function for a [`Table`]. +pub type StyleFn<'a, Theme> = Box Style + 'a>; + +impl From