diff --git a/.typos.toml b/.typos.toml index 16659f4c7..97c54d657 100644 --- a/.typos.toml +++ b/.typos.toml @@ -9,6 +9,7 @@ isse = "isse" # part of @IsseW username tye = "tye" # part of @tye-exe username ro = "ro" # read-only, also part of the username @Phen-Ro typ = "typ" # Often used because `type` is a keyword in Rust +wdth = "wdth" # The `wdth` tag is used in variable fonts # I mistype these so often tesalator = "tessellator" @@ -20,6 +21,8 @@ teselation = "tessellation" tessalation = "tessellation" tesselation = "tessellation" +# For consistency +postfix = "suffix" # Use the more common spelling adaptor = "adapter" diff --git a/Cargo.lock b/Cargo.lock index 99e1d7291..d131126e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,47 +20,48 @@ checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" [[package]] name = "accesskit" -version = "0.21.1" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf203f9d3bd8f29f98833d1fbef628df18f759248a547e7e01cfbf63cda36a99" +checksum = "5351dcebb14b579ccab05f288596b2ae097005be7ee50a7c3d4ca9d0d5a66f6a" dependencies = [ "enumn", "serde", + "uuid", ] [[package]] name = "accesskit_atspi_common" -version = "0.14.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29f73a9b855b6f4af4962a94553ef0c092b80cf5e17038724d5e30945d036f69" +checksum = "842fd8203e6dfcf531d24f5bac792088edfba7d6b35844fead191603fb32a260" dependencies = [ "accesskit", "accesskit_consumer", "atspi-common", + "phf 0.13.1", "serde", - "thiserror 1.0.66", "zvariant", ] [[package]] name = "accesskit_consumer" -version = "0.30.1" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd06f5fea9819250fffd4debf926709f3593ac22f8c1541a2573e5ee0ca01cd" +checksum = "53cf47daed85312e763fbf85ceca136e0d7abc68e0a7e12abe11f48172bc3b10" dependencies = [ "accesskit", - "hashbrown 0.15.2", + "hashbrown 0.16.1", ] [[package]] name = "accesskit_macos" -version = "0.22.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fbaf15815f39084e0cb24950c232f0e3634702c2dfbf182ae3b4919a4a1d45" +checksum = "534bc3fdc89a64a1db3c46b33c198fde2b7c3c7d094e5809c8c8bf2970c18243" dependencies = [ "accesskit", "accesskit_consumer", - "hashbrown 0.15.2", + "hashbrown 0.16.1", "objc2 0.5.2", "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", @@ -68,9 +69,9 @@ dependencies = [ [[package]] name = "accesskit_unix" -version = "0.17.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64926a930368d52d95422b822ede15014c04536cabaa2394f99567a1f4788dc6" +checksum = "90e549dd7c6562b6a2ea807b44726e6241707db054a817dc4c7e2b8d3b39bfac" dependencies = [ "accesskit", "accesskit_atspi_common", @@ -86,23 +87,23 @@ dependencies = [ [[package]] name = "accesskit_windows" -version = "0.29.1" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "792991159fa9ba57459de59e12e918bb90c5346fea7d40ac1a11f8632b41e63a" +checksum = "eff7009f1a532e917d66970a1e80c965140c6cfbbabbdde3d64e5431e6c78e21" dependencies = [ "accesskit", "accesskit_consumer", - "hashbrown 0.15.2", + "hashbrown 0.16.1", "static_assertions", - "windows 0.61.1", - "windows-core 0.61.0", + "windows", + "windows-core 0.62.2", ] [[package]] name = "accesskit_winit" -version = "0.29.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9db0ea66997e3f4eae4a5f2c6b6486cf206642639ee629dbbb860ace1dec87" +checksum = "1fe9a94394896352cc4660ca2288bd4ef883d83238853c038b44070c8f134313" dependencies = [ "accesskit", "accesskit_macos", @@ -138,7 +139,7 @@ dependencies = [ "once_cell", "serde", "version_check", - "zerocopy 0.8.27", + "zerocopy", ] [[package]] @@ -150,6 +151,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android-activity" version = "0.6.0" @@ -312,28 +319,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "ashpd" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df" -dependencies = [ - "async-fs", - "async-net", - "enumflags2", - "futures-channel", - "futures-util", - "rand 0.9.2", - "raw-window-handle", - "serde", - "serde_repr", - "url", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "zbus", -] - [[package]] name = "async-broadcast" version = "0.7.1" @@ -371,17 +356,6 @@ dependencies = [ "slab", ] -[[package]] -name = "async-fs" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" -dependencies = [ - "async-lock", - "blocking", - "futures-lite", -] - [[package]] name = "async-io" version = "2.3.4" @@ -412,17 +386,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "async-net" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" -dependencies = [ - "async-io", - "blocking", - "futures-lite", -] - [[package]] name = "async-process" version = "2.3.0" @@ -496,20 +459,19 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "atspi" -version = "0.25.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83247582e7508838caf5f316c00791eee0e15c0bf743e6880585b867e16815c" +checksum = "c77886257be21c9cd89a4ae7e64860c6f0eefca799bb79127913052bd0eefb3d" dependencies = [ "atspi-common", - "atspi-connection", "atspi-proxies", ] [[package]] name = "atspi-common" -version = "0.9.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33dfc05e7cdf90988a197803bf24f5788f94f7c94a69efa95683e8ffe76cfdfb" +checksum = "20c5617155740c98003016429ad13fe43ce7a77b007479350a9f8bf95a29f63d" dependencies = [ "enumflags2", "serde", @@ -521,23 +483,11 @@ dependencies = [ "zvariant", ] -[[package]] -name = "atspi-connection" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4193d51303d8332304056ae0004714256b46b6635a5c556109b319c0d3784938" -dependencies = [ - "atspi-common", - "atspi-proxies", - "futures-lite", - "zbus", -] - [[package]] name = "atspi-proxies" -version = "0.9.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2eebcb9e7e76f26d0bcfd6f0295e1cd1e6f33bedbc5698a971db8dc43d7751c" +checksum = "2230e48787ed3eb4088996eab66a32ca20c0b67bbd4fd6cdfe79f04f1f04c9fc" dependencies = [ "atspi-common", "serde", @@ -698,9 +648,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.8.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "calloop" @@ -1155,18 +1105,18 @@ checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" [[package]] name = "deranged" -version = "0.3.11" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", ] [[package]] name = "dify" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11217d469eafa3b809ad84651eb9797ccbb440b4a916d5d85cb1b994e89787f6" +checksum = "90ce0fb972943b4e88cd03b8f92953df0c71bb05e0bde8e5b684895d808013cc" dependencies = [ "anyhow", "colored", @@ -1611,6 +1561,7 @@ dependencies = [ "ecolor", "emath", "epaint_default_fonts", + "font-types", "log", "mimalloc", "nohash-hasher", @@ -1621,6 +1572,7 @@ dependencies = [ "serde", "similar-asserts", "skrifa", + "smallvec", "vello_cpu", ] @@ -1780,11 +1732,12 @@ checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "font-types" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39a654f404bbcbd48ea58c617c2993ee91d1cb63727a37bf2323a4edeed1b8c5" +checksum = "b1e4d2d0cf79d38430cc9dc9aadec84774bff2e1ba30ae2bf6c16cfce9385a23" dependencies = [ "bytemuck", + "serde", ] [[package]] @@ -1845,32 +1798,23 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", -] - [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[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", @@ -1881,9 +1825,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", @@ -1892,23 +1836,20 @@ dependencies = [ [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", - "futures-io", "futures-macro", "futures-task", - "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -2059,35 +2000,18 @@ dependencies = [ "gl_generator", ] -[[package]] -name = "gpu-alloc" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" -dependencies = [ - "bitflags 2.9.4", - "gpu-alloc-types", -] - -[[package]] -name = "gpu-alloc-types" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" -dependencies = [ - "bitflags 2.9.4", -] - [[package]] name = "gpu-allocator" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" +checksum = "51255ea7cfaadb6c5f1528d43e92a82acb2b96c43365989a28b2d44ee38f8795" dependencies = [ + "ash", + "hashbrown 0.16.1", "log", "presser", - "thiserror 1.0.66", - "windows 0.58.0", + "thiserror 2.0.17", + "windows", ] [[package]] @@ -2132,10 +2056,12 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.2.0", ] @@ -2216,7 +2142,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.0", + "windows-core 0.61.2", ] [[package]] @@ -2381,23 +2307,12 @@ checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" [[package]] name = "indexmap" -version = "2.8.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.15.2", -] - -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags 2.9.4", - "cfg-if", - "libc", + "hashbrown 0.16.1", ] [[package]] @@ -2548,12 +2463,10 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "kittest" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01fd6dd2cce251a360101038acb9334e3a50cd38cd02fefddbf28aa975f043c8" +source = "git+https://github.com/rerun-io/kittest?branch=main#ce7a2f3b12c36021889b50bdff671cec8016b0fb" dependencies = [ "accesskit", "accesskit_consumer", - "parking_lot", ] [[package]] @@ -2568,9 +2481,9 @@ dependencies = [ [[package]] name = "kurbo" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce9729cc38c18d86123ab736fd2e7151763ba226ac2490ec092d1dd148825e32" +checksum = "7564e90fe3c0d5771e1f0bc95322b21baaeaa0d9213fa6a0b61c99f8b17b3bfb" dependencies = [ "arrayvec", "euclid", @@ -2673,9 +2586,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "lz4_flex" @@ -2718,9 +2631,9 @@ dependencies = [ [[package]] name = "metal" -version = "0.32.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605" +checksum = "c7047791b5bc903b8cd963014b355f71dc9864a9a0b727057676c1dcae5cbc15" dependencies = [ "bitflags 2.9.4", "block", @@ -2753,8 +2666,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1706dc14a2e140dec0a7a07109d9a3d5890b81e85bd6c60b906b249a77adf0ca" dependencies = [ "mime", - "phf", - "phf_shared", + "phf 0.11.3", + "phf_shared 0.11.3", "unicase", ] @@ -2795,9 +2708,9 @@ dependencies = [ [[package]] name = "naga" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b2e757b11b47345d44e7760e45458339bc490463d9548cd8651c53ae523153" +checksum = "618f667225063219ddfc61251087db8a9aec3c3f0950c916b614e403486f1135" dependencies = [ "arrayvec", "bit-set", @@ -2806,7 +2719,7 @@ dependencies = [ "cfg_aliases", "codespan-reporting", "half", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "hexf-parse", "indexmap", "libm", @@ -2851,9 +2764,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.29.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ "bitflags 2.9.4", "cfg-if", @@ -2870,9 +2783,9 @@ checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" [[package]] name = "num-conv" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" [[package]] name = "num-traits" @@ -3310,13 +3223,13 @@ checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "peniko" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3c76095c9a636173600478e0373218c7b955335048c2bcd12dc6a79657649d8" +checksum = "9a2b6aadb221872732e87d465213e9be5af2849b0e8cc5300a8ba98fffa2e00a" dependencies = [ "bytemuck", "color", - "kurbo 0.12.0", + "kurbo 0.13.0", "linebender_resource_handle", "smallvec", ] @@ -3333,8 +3246,19 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_macros", - "phf_shared", + "phf_macros 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros 0.13.1", + "phf_shared 0.13.1", + "serde", ] [[package]] @@ -3343,8 +3267,8 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.11.3", + "phf_shared 0.11.3", ] [[package]] @@ -3353,24 +3277,47 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared", + "phf_shared 0.11.3", "rand 0.8.5", ] +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared 0.13.1", +] + [[package]] name = "phf_macros" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.11.3", + "phf_shared 0.11.3", "proc-macro2", "quote", "syn", "unicase", ] +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "phf_shared" version = "0.11.3" @@ -3381,6 +3328,15 @@ dependencies = [ "unicase", ] +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + [[package]] name = "pico-args" version = "0.5.0" @@ -3413,12 +3369,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "piper" version = "0.2.4" @@ -3438,13 +3388,13 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "plist" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" +checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" dependencies = [ "base64", "indexmap", - "quick-xml 0.32.0", + "quick-xml", "serde", "time", ] @@ -3533,11 +3483,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -3548,9 +3498,9 @@ checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" [[package]] name = "proc-macro-crate" -version = "3.2.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ "toml_edit", ] @@ -3634,32 +3584,14 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.32.0" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" -dependencies = [ - "memchr", -] - -[[package]] -name = "quick-xml" -version = "0.36.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" dependencies = [ "memchr", "serde", ] -[[package]] -name = "quick-xml" -version = "0.37.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" -dependencies = [ - "memchr", -] - [[package]] name = "quote" version = "1.0.41" @@ -3762,9 +3694,9 @@ dependencies = [ [[package]] name = "read-fonts" -version = "0.35.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717cf23b488adf64b9d711329542ba34de147df262370221940dfabc2c91358" +checksum = "7b634fabf032fab15307ffd272149b622260f55974d9fad689292a5d33df02e5" dependencies = [ "bytemuck", "font-types", @@ -3850,26 +3782,29 @@ dependencies = [ [[package]] name = "rfd" -version = "0.15.4" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed" +checksum = "20dafead71c16a34e1ff357ddefc8afc11e7d51d6d2b9fbd07eaa48e3e540220" dependencies = [ - "ashpd", "block2 0.6.2", "dispatch2", "js-sys", + "libc", "log", "objc2 0.6.3", "objc2-app-kit 0.3.2", "objc2-core-foundation", "objc2-foundation 0.3.2", + "percent-encoding", "pollster", "raw-window-handle", - "urlencoding", "wasm-bindgen", "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", "web-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4014,12 +3949,6 @@ dependencies = [ "unicode-script", ] -[[package]] -name = "ryu" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" - [[package]] name = "same-file" version = "1.0.6" @@ -4101,14 +4030,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", + "serde_core", + "zmij", ] [[package]] @@ -4124,11 +4054,11 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.9" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -4198,9 +4128,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "skrifa" -version = "0.37.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c31071dedf532758ecf3fed987cdb4bd9509f900e026ab684b4ecb81ea49841" +checksum = "7fbdfe3d2475fbd7ddd1f3e5cf8288a30eb3e5f95832829570cd88115a7434ac" dependencies = [ "bytemuck", "read-fonts", @@ -4229,6 +4159,9 @@ name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] [[package]] name = "smithay-client-toolkit" @@ -4404,6 +4337,14 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "test_background_logic" +version = "0.1.0" +dependencies = [ + "eframe", + "env_logger", +] + [[package]] name = "test_egui_extras_compilation" version = "0.1.0" @@ -4498,30 +4439,30 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" dependencies = [ "num-conv", "time-core", @@ -4590,51 +4531,66 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.47.1" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ - "backtrace", - "io-uring", "libc", "mio", "pin-project-lite", - "slab", "socket2", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "toml" -version = "0.8.20" +version = "1.0.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "d1dfefef6a142e93f346b64c160934eb13b5594b84ab378133ac6815cb2bd57f" dependencies = [ - "serde", + "serde_core", "serde_spanned", - "toml_datetime", - "toml_edit", + "toml_datetime 1.0.0+spec-1.1.0", + "toml_parser", + "winnow", ] [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" dependencies = [ - "serde", + "serde_core", +] + +[[package]] +name = "toml_datetime" +version = "1.0.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" +dependencies = [ + "serde_core", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ "indexmap", - "serde", - "serde_spanned", - "toml_datetime", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.9+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +dependencies = [ "winnow", ] @@ -4764,7 +4720,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d189085656ca1203291e965444e7f6a2723fbdd1dd9f34f8482e79bafd8338a0" dependencies = [ - "phf", + "phf 0.11.3", "unicode_names2_generator", ] @@ -4809,15 +4765,8 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", - "serde", ] -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - [[package]] name = "user_attention" version = "0.1.0" @@ -4866,13 +4815,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] -name = "vello_common" -version = "0.0.4" +name = "uuid" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a235ba928b3109ad9e7696270edb09445a52ae1c7c08e6d31a19b1cdd6cbc24a" +checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" +dependencies = [ + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "vello_common" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd1a4c633ce09e7d713df1a6e036644a125e15e0c169cfb5180ddf5836ca04b" dependencies = [ "bytemuck", "fearless_simd", + "hashbrown 0.16.1", "log", "peniko", "skrifa", @@ -4881,11 +4842,12 @@ dependencies = [ [[package]] name = "vello_cpu" -version = "0.0.4" +version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0bd1fcf9c1814f17a491e07113623d44e3ec1125a9f3401f5e047d6d326da21" +checksum = "0162bfe48aabf6a9fdcd401b628c7d9f260c2cbabb343c70a65feba6f7849edc" dependencies = [ "bytemuck", + "hashbrown 0.16.1", "vello_common", ] @@ -5079,12 +5041,12 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.7" +version = "0.31.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" +checksum = "5423e94b6a63e68e439803a3e153a9252d5ead12fd853334e2ad33997e3889e3" dependencies = [ "proc-macro2", - "quick-xml 0.37.5", + "quick-xml", "quote", ] @@ -5153,16 +5115,17 @@ checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" [[package]] name = "wgpu" -version = "27.0.1" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe68bac7cde125de7a731c3400723cadaaf1703795ad3f4805f187459cd7a77" +checksum = "f9cb534d5ffd109c7d1135f34cdae29e60eab94855a625dcfe1705f8bc7ad79f" dependencies = [ "arrayvec", "bitflags 2.9.4", + "bytemuck", "cfg-if", "cfg_aliases", "document-features", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "js-sys", "log", "naga", @@ -5182,9 +5145,9 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "893764e276cdafec946c7f394f044e283bc8f1e445ab3fea8ad3b6dbc10c0322" +checksum = "8bb4c8b5db5f00e56f1f08869d870a0dff7c8bc7ebc01091fec140b0cf0211a9" dependencies = [ "arrayvec", "bit-set", @@ -5193,7 +5156,7 @@ dependencies = [ "bytemuck", "cfg_aliases", "document-features", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "indexmap", "log", "naga", @@ -5215,45 +5178,45 @@ dependencies = [ [[package]] name = "wgpu-core-deps-apple" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0772ae958e9be0c729561d5e3fd9a19679bcdfb945b8b1a1969d9bfe8056d233" +checksum = "87b7b696b918f337c486bf93142454080a32a37832ba8a31e4f48221890047da" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-emscripten" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06ac3444a95b0813ecfd81ddb2774b66220b264b3e2031152a4a29fda4da6b5" +checksum = "34b251c331f84feac147de3c4aa3aa45112622a95dd7ee1b74384fa0458dbd79" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-wasm" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1027dcf3b027a877e44819df7ceb0e2e98578830f8cd34cd6c3c7c2a7a50b7" +checksum = "12a2cf578ce8d7d50d0e63ddc2345c7dcb599f6eb90b888813406ea78b9b7010" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-windows-linux-android" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71197027d61a71748e4120f05a9242b2ad142e3c01f8c1b47707945a879a03c3" +checksum = "68ca976e72b2c9964eb243e281f6ce7f14a514e409920920dcda12ae40febaae" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-hal" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a753c3dc95e69be3aacfe9c871c5fa2cfa9e35748cdc87de7ba5fc1735b61604" +checksum = "293080d77fdd14d6b08a67c5487dfddbf874534bb7921526db56a7b75d7e3bef" dependencies = [ "android_system_properties", "arrayvec", @@ -5267,10 +5230,9 @@ dependencies = [ "core-graphics-types 0.2.0", "glow", "glutin_wgl_sys", - "gpu-alloc", "gpu-allocator", "gpu-descriptor", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "js-sys", "khronos-egl", "libc", @@ -5294,21 +5256,20 @@ dependencies = [ "wasm-bindgen", "web-sys", "wgpu-types", - "windows 0.58.0", - "windows-core 0.58.0", + "windows", + "windows-core 0.62.2", ] [[package]] name = "wgpu-types" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d67453b02f7adc33c452d17da1c2cad813448221df1547bce9dd4b02d3558538" +checksum = "e18308757e594ed2cd27dddbb16a139c42a683819d32a2e0b1b0167552f5840c" dependencies = [ "bitflags 2.9.4", "bytemuck", "js-sys", "log", - "thiserror 2.0.17", "web-sys", ] @@ -5345,88 +5306,67 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.58.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" -dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows" -version = "0.61.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" dependencies = [ "windows-collections", - "windows-core 0.61.0", + "windows-core 0.62.2", "windows-future", - "windows-link 0.1.3", "windows-numerics", ] [[package]] name = "windows-collections" -version = "0.2.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ - "windows-core 0.61.0", + "windows-core 0.62.2", ] [[package]] name = "windows-core" -version = "0.58.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-implement 0.58.0", - "windows-interface 0.58.0", - "windows-result 0.2.0", - "windows-strings 0.1.0", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-core" -version = "0.61.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" -dependencies = [ - "windows-implement 0.60.0", - "windows-interface 0.59.1", + "windows-implement", + "windows-interface", "windows-link 0.1.3", - "windows-result 0.3.2", - "windows-strings 0.4.0", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] name = "windows-future" -version = "0.2.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ - "windows-core 0.61.0", - "windows-link 0.1.3", + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading", ] [[package]] name = "windows-implement" -version = "0.58.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-implement" -version = "0.60.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", @@ -5435,20 +5375,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.58.0" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", @@ -5469,49 +5398,48 @@ checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-numerics" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-core 0.61.0", "windows-link 0.1.3", ] [[package]] name = "windows-result" -version = "0.2.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] -name = "windows-result" -version = "0.3.2" +name = "windows-strings" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ "windows-link 0.1.3", ] [[package]] name = "windows-strings" -version = "0.1.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-result 0.2.0", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-strings" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" -dependencies = [ - "windows-link 0.1.3", + "windows-link 0.2.1", ] [[package]] @@ -5607,6 +5535,15 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -5859,16 +5796,6 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" -[[package]] -name = "xdg-home" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - [[package]] name = "xkbcommon-dl" version = "0.4.2" @@ -5939,13 +5866,12 @@ dependencies = [ [[package]] name = "zbus" -version = "5.5.0" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59c333f648ea1b647bc95dc1d34807c8e25ed7a6feff3394034dc4776054b236" +checksum = "b622b18155f7a93d1cd2dc8c01d2d6a44e08fb9ebb7b3f9e6ed101488bad6c91" dependencies = [ "async-broadcast", "async-executor", - "async-fs", "async-io", "async-lock", "async-process", @@ -5962,12 +5888,11 @@ dependencies = [ "ordered-stream", "serde", "serde_repr", - "static_assertions", "tracing", "uds_windows", - "windows-sys 0.59.0", + "uuid", + "windows-sys 0.61.2", "winnow", - "xdg-home", "zbus_macros", "zbus_names", "zvariant", @@ -5975,9 +5900,9 @@ dependencies = [ [[package]] name = "zbus-lockstep" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a22426b1bc2aca91de97772506f0655fa373448e6010d79d5d5880915c388409" +checksum = "6998de05217a084b7578728a9443d04ea4cd80f2a0839b8d78770b76ccd45863" dependencies = [ "zbus_xml", "zvariant", @@ -5985,9 +5910,9 @@ dependencies = [ [[package]] name = "zbus-lockstep-macros" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "100ffec29ed51859052f4563061abe35557acb56ba574510571f8398efc70a29" +checksum = "10da05367f3a7b7553c8cdf8fa91aee6b64afebe32b51c95177957efc47ca3a0" dependencies = [ "proc-macro2", "quote", @@ -5999,9 +5924,9 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.5.0" +version = "5.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f325ad10eb0d0a3eb060203494c3b7ec3162a01a59db75d2deee100339709fc0" +checksum = "0bbd5a90dbe8feee5b13def448427ae314ccd26a49cac47905cafefb9ff846f1" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -6014,57 +5939,34 @@ dependencies = [ [[package]] name = "zbus_names" -version = "4.2.0" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" +checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" dependencies = [ "serde", - "static_assertions", "winnow", "zvariant", ] [[package]] name = "zbus_xml" -version = "5.0.2" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589e9a02bfafb9754bb2340a9e3b38f389772684c63d9637e76b1870377bec29" +checksum = "441a0064125265655bccc3a6af6bef56814d9277ac83fce48b1cd7e160b80eac" dependencies = [ - "quick-xml 0.36.2", + "quick-xml", "serde", - "static_assertions", "zbus_names", "zvariant", ] -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive 0.7.35", -] - [[package]] name = "zerocopy" version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ - "zerocopy-derive 0.8.27", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "zerocopy-derive", ] [[package]] @@ -6138,6 +6040,12 @@ dependencies = [ "syn", ] +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + [[package]] name = "zune-core" version = "0.4.12" @@ -6155,15 +6063,13 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.4.0" +version = "5.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2df9ee044893fcffbdc25de30546edef3e32341466811ca18421e3cd6c5a3ac" +checksum = "68b64ef4f40c7951337ddc7023dd03528a57a3ce3408ee9da5e948bd29b232c4" dependencies = [ "endi", "enumflags2", "serde", - "static_assertions", - "url", "winnow", "zvariant_derive", "zvariant_utils", @@ -6171,9 +6077,9 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "5.4.0" +version = "5.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74170caa85b8b84cc4935f2d56a57c7a15ea6185ccdd7eadb57e6edd90f94b2f" +checksum = "484d5d975eb7afb52cc6b929c13d3719a20ad650fea4120e6310de3fc55e415c" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -6184,14 +6090,13 @@ dependencies = [ [[package]] name = "zvariant_utils" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34" +checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" dependencies = [ "proc-macro2", "quote", "serde", - "static_assertions", "syn", "winnow", ] diff --git a/Cargo.toml b/Cargo.toml index 470644bb4..d12b040fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,9 +68,9 @@ egui_glow = { version = "0.33.3", path = "crates/egui_glow", default-features = egui_kittest = { version = "0.33.3", path = "crates/egui_kittest", default-features = false } eframe = { version = "0.33.3", path = "crates/eframe", default-features = false } -accesskit = "0.21.1" -accesskit_consumer = "0.30.1" -accesskit_winit = "0.29.1" +accesskit = "0.24.0" +accesskit_consumer = "0.35.0" +accesskit_winit = "0.32.0" ahash = { version = "0.8.12", default-features = false, features = [ "no-rng", # we don't need DOS-protection, so we let users opt-in to it instead "std", @@ -84,12 +84,13 @@ chrono = { version = "0.4.42", default-features = false } cint = "0.3.1" color-hex = "0.2.0" criterion = { version = "0.7.0", default-features = false } -dify = { version = "0.7.4", default-features = false } +dify = { version = "0.8", default-features = false } directories = "6.0.0" document-features = "0.2.11" ehttp = { version = "0.6.0", default-features = false } enum-map = "2.7.3" env_logger = { version = "0.11.8", default-features = false } +font-types = { version = "0.11.0", default-features = false, features = ["std"] } glow = "0.16.0" glutin = { version = "0.32.3", default-features = false } glutin-winit = { version = "0.5.0", default-features = false } @@ -119,34 +120,36 @@ rand = "0.9.2" raw-window-handle = "0.6.2" rayon = "1.11.0" resvg = { version = "0.45.1", default-features = false } -rfd = "0.15.4" +rfd = "0.17.2" ron = "0.11.0" self_cell = "1.2.1" serde = { version = "1.0.228", features = ["derive"] } similar-asserts = "1.7.0" -skrifa = { version = "0.37.0", default-features = false, features = ["std", "autohint_shaping"] } +skrifa = { version = "0.40.0", default-features = false, features = ["std", "autohint_shaping"] } smallvec = "1.15.1" smithay-clipboard = "0.7.2" static_assertions = "1.1.0" syntect = { version = "5.3.0", default-features = false } tempfile = "3.23.0" thiserror = "2.0.17" -tokio = "1.47.1" -toml = "0.8" +tokio = "1.49" +toml = {version = "1", default-features = false } type-map = "0.5.1" unicode_names2 = { version = "2.0.0", default-features = false } unicode-segmentation = "1.12.0" -vello_cpu = { version = "0.0.4", default-features = false, features = ["std"] } +vello_cpu = { version = "0.0.6", default-features = false, features = ["std", "u8_pipeline", "f32_pipeline"] } wasm-bindgen = "0.2.100" # Keep wasm-bindgen version in sync in: setup_web.sh, Cargo.toml, Cargo.lock, rust.yml wasm-bindgen-futures = "0.4.0" wayland-cursor = { version = "0.31.11", default-features = false } web-sys = "0.3.77" web-time = "1.1.0" # Timekeeping for native and web webbrowser = "1.0.5" -wgpu = { version = "27.0.1", default-features = false, features = ["std"] } +wgpu = { version = "28.0.0", default-features = false, features = ["std"] } windows-sys = "0.61.2" winit = { version = "0.30.12", default-features = false } +[patch.crates-io] +kittest = { git = "https://github.com/rerun-io/kittest", branch = "main" } [workspace.lints.rust] unsafe_code = "deny" diff --git a/crates/eframe/src/epi.rs b/crates/eframe/src/epi.rs index c37dc1cf6..b9a178a1d 100644 --- a/crates/eframe/src/epi.rs +++ b/crates/eframe/src/epi.rs @@ -72,7 +72,8 @@ pub struct CreationContext<'s> { /// The `get_proc_address` wrapper of underlying GL context #[cfg(feature = "glow")] - pub get_proc_address: Option<&'s dyn Fn(&std::ffi::CStr) -> *const std::ffi::c_void>, + pub get_proc_address: + Option *const std::ffi::c_void + Send + Sync>>, /// The underlying WGPU render state. /// diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs index 96a52db88..5b22eb08c 100644 --- a/crates/eframe/src/native/epi_integration.rs +++ b/crates/eframe/src/native/epi_integration.rs @@ -265,6 +265,7 @@ impl EpiIntegration { app: &mut dyn epi::App, viewport_ui_cb: Option<&DeferredViewportUiCallback>, mut raw_input: egui::RawInput, + is_visible: bool, ) -> egui::FullOutput { raw_input.time = Some(self.beginning.elapsed().as_secs_f64()); @@ -275,23 +276,27 @@ impl EpiIntegration { let full_output = self.egui_ctx.run_ui(raw_input, |ui| { if let Some(viewport_ui_cb) = viewport_ui_cb { // Child viewport - profiling::scope!("viewport_callback"); - viewport_ui_cb(ui); + if is_visible { + profiling::scope!("viewport_callback"); + viewport_ui_cb(ui); + } } else { { profiling::scope!("App::logic"); app.logic(ui.ctx(), &mut self.frame); } - { - profiling::scope!("App::update"); - #[expect(deprecated)] - app.update(ui.ctx(), &mut self.frame); - } + if is_visible { + { + profiling::scope!("App::update"); + #[expect(deprecated)] + app.update(ui.ctx(), &mut self.frame); + } - { - profiling::scope!("App::ui"); - app.ui(ui, &mut self.frame); + { + profiling::scope!("App::ui"); + app.ui(ui, &mut self.frame); + } } } }); diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index 1cd49449f..724ddc6d5 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -294,14 +294,15 @@ impl<'app> GlowWinitApp<'app> { // Use latest raw_window_handle for eframe compatibility use raw_window_handle::{HasDisplayHandle as _, HasWindowHandle as _}; - let get_proc_address = |addr: &_| glutin.get_proc_address(addr); + let gl_config = glutin.gl_config.clone(); + let get_proc_address = move |addr: &_| gl_config.display().get_proc_address(addr); let window = glutin.window(ViewportId::ROOT); let cc = CreationContext { egui_ctx: integration.egui_ctx.clone(), integration_info: integration.frame.info().clone(), storage: integration.frame.storage(), gl: Some(gl), - get_proc_address: Some(&get_proc_address), + get_proc_address: Some(Arc::new(get_proc_address)), #[cfg(feature = "wgpu_no_default_features")] wgpu_render_state: None, raw_display_handle: window.display_handle().map(|h| h.as_raw()), @@ -447,12 +448,21 @@ impl WinitApp for GlowWinitApp<'_> { if let Some(viewport) = glutin .focused_viewport .and_then(|viewport| glutin.viewports.get_mut(&viewport)) + && let Some(window) = viewport.window.as_ref() { - if let Some(egui_winit) = viewport.egui_winit.as_mut() { - egui_winit.on_mouse_motion(delta); + if !window.has_focus() + && !viewport + .egui_winit + .as_ref() + .map(|state| state.is_any_pointer_button_down()) + .unwrap_or(false) + { + return Ok(EventResult::Wait); } - if let Some(window) = viewport.window.as_ref() { + if let Some(egui_winit) = viewport.egui_winit.as_mut() + && egui_winit.on_mouse_motion(delta) + { return Ok(EventResult::RepaintNext(window.id())); } } @@ -535,7 +545,7 @@ impl GlowWinitRunning<'_> { } } - let (raw_input, viewport_ui_cb) = { + let (raw_input, viewport_ui_cb, is_visible) = { let mut glutin = self.glutin.borrow_mut(); let egui_ctx = glutin.egui_ctx.clone(); let Some(viewport) = glutin.viewports.get_mut(&viewport_id) else { @@ -546,6 +556,8 @@ impl GlowWinitRunning<'_> { }; egui_winit::update_viewport_info(&mut viewport.info, &egui_ctx, window, false); + let is_visible = viewport.info.visible().unwrap_or(true); + let Some(egui_winit) = viewport.egui_winit.as_mut() else { return Ok(EventResult::Wait); }; @@ -561,7 +573,7 @@ impl GlowWinitRunning<'_> { .map(|(id, viewport)| (*id, viewport.info.clone())) .collect(); - (raw_input, viewport_ui_cb) + (raw_input, viewport_ui_cb, is_visible) }; // HACK: In order to get the right clear_color, the system theme needs to be set, which @@ -577,7 +589,7 @@ impl GlowWinitRunning<'_> { let has_many_viewports = self.glutin.borrow().viewports.len() > 1; let clear_before_update = !has_many_viewports; // HACK: for some reason, an early clear doesn't "take" on Mac with multiple viewports. - if clear_before_update { + if is_visible && clear_before_update { // clear before we call update, so users can paint between clear-color and egui windows: let mut glutin = self.glutin.borrow_mut(); @@ -612,9 +624,12 @@ impl GlowWinitRunning<'_> { // The update function, which could call immediate viewports, // so make sure we don't hold any locks here required by the immediate viewports rendeer. - let full_output = - self.integration - .update(self.app.as_mut(), viewport_ui_cb.as_deref(), raw_input); + let full_output = self.integration.update( + self.app.as_mut(), + viewport_ui_cb.as_deref(), + raw_input, + is_visible, + ); // ------------------------------------------------------------ @@ -657,85 +672,87 @@ impl GlowWinitRunning<'_> { egui_winit.handle_platform_output(&window, platform_output); - let clipped_primitives = integration.egui_ctx.tessellate(shapes, pixels_per_point); + if is_visible { + let clipped_primitives = integration.egui_ctx.tessellate(shapes, pixels_per_point); - { - // We may need to switch contexts again, because of immediate viewports: - frame_timer.pause(); - change_gl_context(current_gl_context, not_current_gl_context, gl_surface); - frame_timer.resume(); - } + { + // We may need to switch contexts again, because of immediate viewports: + frame_timer.pause(); + change_gl_context(current_gl_context, not_current_gl_context, gl_surface); + frame_timer.resume(); + } - let screen_size_in_pixels: [u32; 2] = window.inner_size().into(); + let screen_size_in_pixels: [u32; 2] = window.inner_size().into(); - if !clear_before_update { - painter.clear(screen_size_in_pixels, clear_color); - } + if !clear_before_update { + painter.clear(screen_size_in_pixels, clear_color); + } - painter.paint_and_update_textures( - screen_size_in_pixels, - pixels_per_point, - &clipped_primitives, - &textures_delta, - ); + painter.paint_and_update_textures( + screen_size_in_pixels, + pixels_per_point, + &clipped_primitives, + &textures_delta, + ); - { - for action in viewport.actions_requested.drain(..) { - match action { - ActionRequested::Screenshot(user_data) => { - let screenshot = painter.read_screen_rgba(screen_size_in_pixels); - egui_winit - .egui_input_mut() - .events - .push(egui::Event::Screenshot { - viewport_id, - user_data, - image: screenshot.into(), - }); - } - ActionRequested::Cut => { - egui_winit.egui_input_mut().events.push(egui::Event::Cut); - } - ActionRequested::Copy => { - egui_winit.egui_input_mut().events.push(egui::Event::Copy); - } - ActionRequested::Paste => { - if let Some(contents) = egui_winit.clipboard_text() { - let contents = contents.replace("\r\n", "\n"); - if !contents.is_empty() { - egui_winit - .egui_input_mut() - .events - .push(egui::Event::Paste(contents)); + { + for action in viewport.actions_requested.drain(..) { + match action { + ActionRequested::Screenshot(user_data) => { + let screenshot = painter.read_screen_rgba(screen_size_in_pixels); + egui_winit + .egui_input_mut() + .events + .push(egui::Event::Screenshot { + viewport_id, + user_data, + image: screenshot.into(), + }); + } + ActionRequested::Cut => { + egui_winit.egui_input_mut().events.push(egui::Event::Cut); + } + ActionRequested::Copy => { + egui_winit.egui_input_mut().events.push(egui::Event::Copy); + } + ActionRequested::Paste => { + if let Some(contents) = egui_winit.clipboard_text() { + let contents = contents.replace("\r\n", "\n"); + if !contents.is_empty() { + egui_winit + .egui_input_mut() + .events + .push(egui::Event::Paste(contents)); + } } } } } + + integration.post_rendering(&window); } - integration.post_rendering(&window); - } + { + // vsync - don't count as frame-time: + frame_timer.pause(); + profiling::scope!("swap_buffers"); + let context = current_gl_context.as_ref().ok_or_else(|| { + egui_glow::PainterError::from( + "failed to get current context to swap buffers".to_owned(), + ) + })?; - { - // vsync - don't count as frame-time: - frame_timer.pause(); - profiling::scope!("swap_buffers"); - let context = current_gl_context.as_ref().ok_or_else(|| { - egui_glow::PainterError::from( - "failed to get current context to swap buffers".to_owned(), - ) - })?; + gl_surface.swap_buffers(context)?; + frame_timer.resume(); + } - gl_surface.swap_buffers(context)?; - frame_timer.resume(); - } - - // give it time to settle: - #[cfg(feature = "__screenshot")] - if integration.egui_ctx.cumulative_pass_nr() == 2 - && let Ok(path) = std::env::var("EFRAME_SCREENSHOT_TO") - { - save_screenshot_and_exit(&path, &painter, screen_size_in_pixels); + // give it time to settle: + #[cfg(feature = "__screenshot")] + if integration.egui_ctx.cumulative_pass_nr() == 2 + && let Ok(path) = std::env::var("EFRAME_SCREENSHOT_TO") + { + save_screenshot_and_exit(&path, &painter, screen_size_in_pixels); + } } glutin.handle_viewport_output(event_loop, &integration.egui_ctx, &viewport_output); @@ -812,6 +829,14 @@ impl GlowWinitRunning<'_> { } } + winit::event::WindowEvent::Occluded(is_occluded) => { + if let Some(viewport_id) = viewport_id + && let Some(viewport) = glutin.viewports.get_mut(&viewport_id) + { + viewport.info.occluded = Some(*is_occluded); + } + } + winit::event::WindowEvent::CloseRequested => { if viewport_id == Some(ViewportId::ROOT) && self.integration.should_close() { log::debug!( diff --git a/crates/eframe/src/native/wgpu_integration.rs b/crates/eframe/src/native/wgpu_integration.rs index cb634200a..9d6283808 100644 --- a/crates/eframe/src/native/wgpu_integration.rs +++ b/crates/eframe/src/native/wgpu_integration.rs @@ -454,12 +454,21 @@ impl WinitApp for WgpuWinitApp<'_> { if let Some(viewport) = shared .focused_viewport .and_then(|viewport| shared.viewports.get_mut(&viewport)) + && let Some(window) = viewport.window.as_ref() { - if let Some(egui_winit) = viewport.egui_winit.as_mut() { - egui_winit.on_mouse_motion(delta); + if !window.has_focus() + && !viewport + .egui_winit + .as_ref() + .map(|state| state.is_any_pointer_button_down()) + .unwrap_or(false) + { + return Ok(EventResult::Wait); } - if let Some(window) = viewport.window.as_ref() { + if let Some(egui_winit) = viewport.egui_winit.as_mut() + && egui_winit.on_mouse_motion(delta) + { return Ok(EventResult::RepaintNext(window.id())); } } @@ -564,7 +573,7 @@ impl WgpuWinitRunning<'_> { let mut frame_timer = crate::stopwatch::Stopwatch::new(); frame_timer.start(); - let (viewport_ui_cb, raw_input) = { + let (viewport_ui_cb, raw_input, is_visible) = { profiling::scope!("Prepare"); let mut shared_lock = shared.borrow_mut(); @@ -608,6 +617,8 @@ impl WgpuWinitRunning<'_> { }; egui_winit::update_viewport_info(info, &integration.egui_ctx, window, false); + let is_visible = viewport.info.visible().unwrap_or(true); + { profiling::scope!("set_window"); pollster::block_on(painter.set_window(viewport_id, Some(Arc::clone(window))))?; @@ -628,14 +639,19 @@ impl WgpuWinitRunning<'_> { painter.handle_screenshots(&mut raw_input.events); - (viewport_ui_cb, raw_input) + (viewport_ui_cb, raw_input, is_visible) }; // ------------------------------------------------------------ // Runs the update, which could call immediate viewports, // so make sure we hold no locks here! - let full_output = integration.update(app.as_mut(), viewport_ui_cb.as_deref(), raw_input); + let full_output = integration.update( + app.as_mut(), + viewport_ui_cb.as_deref(), + raw_input, + is_visible, + ); // ------------------------------------------------------------ @@ -676,52 +692,58 @@ impl WgpuWinitRunning<'_> { egui_winit.handle_platform_output(window, platform_output); - let clipped_primitives = egui_ctx.tessellate(shapes, pixels_per_point); + let vsync_secs = if is_visible { + let clipped_primitives = egui_ctx.tessellate(shapes, pixels_per_point); - let mut screenshot_commands = vec![]; - viewport.actions_requested.retain(|cmd| { - if let ActionRequested::Screenshot(info) = cmd { - screenshot_commands.push(info.clone()); - false - } else { - true - } - }); - let vsync_secs = painter.paint_and_update_textures( - viewport_id, - pixels_per_point, - app.clear_color(&egui_ctx.global_style().visuals), - &clipped_primitives, - &textures_delta, - screenshot_commands, - ); + let mut screenshot_commands = vec![]; + viewport.actions_requested.retain(|cmd| { + if let ActionRequested::Screenshot(info) = cmd { + screenshot_commands.push(info.clone()); + false + } else { + true + } + }); + let vsync_secs = painter.paint_and_update_textures( + viewport_id, + pixels_per_point, + app.clear_color(&egui_ctx.global_style().visuals), + &clipped_primitives, + &textures_delta, + screenshot_commands, + ); - for action in viewport.actions_requested.drain(..) { - match action { - ActionRequested::Screenshot { .. } => { - // already handled above - } - ActionRequested::Cut => { - egui_winit.egui_input_mut().events.push(egui::Event::Cut); - } - ActionRequested::Copy => { - egui_winit.egui_input_mut().events.push(egui::Event::Copy); - } - ActionRequested::Paste => { - if let Some(contents) = egui_winit.clipboard_text() { - let contents = contents.replace("\r\n", "\n"); - if !contents.is_empty() { - egui_winit - .egui_input_mut() - .events - .push(egui::Event::Paste(contents)); + for action in viewport.actions_requested.drain(..) { + match action { + ActionRequested::Screenshot { .. } => { + // already handled above + } + ActionRequested::Cut => { + egui_winit.egui_input_mut().events.push(egui::Event::Cut); + } + ActionRequested::Copy => { + egui_winit.egui_input_mut().events.push(egui::Event::Copy); + } + ActionRequested::Paste => { + if let Some(contents) = egui_winit.clipboard_text() { + let contents = contents.replace("\r\n", "\n"); + if !contents.is_empty() { + egui_winit + .egui_input_mut() + .events + .push(egui::Event::Paste(contents)); + } } } } } - } - integration.post_rendering(window); + integration.post_rendering(window); + + vsync_secs + } else { + 0.0 + }; let active_viewports_ids: ViewportIdSet = viewport_output.keys().copied().collect(); @@ -843,6 +865,14 @@ impl WgpuWinitRunning<'_> { } } + winit::event::WindowEvent::Occluded(is_occluded) => { + if let Some(viewport_id) = viewport_id + && let Some(viewport) = shared.viewports.get_mut(&viewport_id) + { + viewport.info.occluded = Some(*is_occluded); + } + } + winit::event::WindowEvent::CloseRequested => { if viewport_id == Some(ViewportId::ROOT) && integration.should_close() { log::debug!( diff --git a/crates/eframe/src/web/app_runner.rs b/crates/eframe/src/web/app_runner.rs index 11654135d..b90b8a5e1 100644 --- a/crates/eframe/src/web/app_runner.rs +++ b/crates/eframe/src/web/app_runner.rs @@ -274,13 +274,21 @@ impl AppRunner { self.app.raw_input_hook(&self.egui_ctx, &mut raw_input); + let is_visible = raw_input + .viewports + .get(&egui::ViewportId::ROOT) + .and_then(|v| v.visible()) + .unwrap_or(true); + let full_output = self.egui_ctx.run_ui(raw_input, |ui| { self.app.logic(ui.ctx(), &mut self.frame); - #[expect(deprecated)] - self.app.update(ui.ctx(), &mut self.frame); + if is_visible { + #[expect(deprecated)] + self.app.update(ui.ctx(), &mut self.frame); - self.app.ui(ui, &mut self.frame); + self.app.ui(ui, &mut self.frame); + } }); let egui::FullOutput { platform_output, @@ -311,8 +319,10 @@ impl AppRunner { } self.handle_platform_output(platform_output); - self.textures_delta.append(textures_delta); - self.clipped_primitives = Some(self.egui_ctx.tessellate(shapes, pixels_per_point)); + if is_visible { + self.textures_delta.append(textures_delta); + self.clipped_primitives = Some(self.egui_ctx.tessellate(shapes, pixels_per_point)); + } } /// Paint the results of the last call to [`Self::logic`]. diff --git a/crates/eframe/src/web/backend.rs b/crates/eframe/src/web/backend.rs index 4814fa99b..e2724fc49 100644 --- a/crates/eframe/src/web/backend.rs +++ b/crates/eframe/src/web/backend.rs @@ -31,11 +31,18 @@ impl WebInput { time: Some(super::now_sec()), ..self.raw.take() }; - raw_input + let viewport = raw_input .viewports .entry(egui::ViewportId::ROOT) - .or_default() - .native_pixels_per_point = Some(super::native_pixels_per_point()); + .or_default(); + viewport.native_pixels_per_point = Some(super::native_pixels_per_point()); + + // A hidden browser tab is effectively occluded. + let hidden = web_sys::window() + .and_then(|w| w.document()) + .is_some_and(|doc| doc.hidden()); + viewport.occluded = Some(hidden); + raw_input } diff --git a/crates/eframe/src/web/web_painter_wgpu.rs b/crates/eframe/src/web/web_painter_wgpu.rs index 264ce6adc..f7adb8fbb 100644 --- a/crates/eframe/src/web/web_painter_wgpu.rs +++ b/crates/eframe/src/web/web_painter_wgpu.rs @@ -268,6 +268,7 @@ impl WebPainter for WebPainterWgpu { label: Some("egui_render"), occlusion_query_set: None, timestamp_writes: None, + multiview_mask: None, }); // Forgetting the pass' lifetime means that we are no longer compile-time protected from @@ -280,15 +281,13 @@ impl WebPainter for WebPainterWgpu { ); } - let mut capture_buffer = None; - - if capture && let Some(capture_state) = &mut self.screen_capture_state { - capture_buffer = Some(capture_state.copy_textures( - &render_state.device, - &output_frame, - &mut encoder, - )); - } + let capture_buffer = if capture + && let Some(capture_state) = &mut self.screen_capture_state + { + Some(capture_state.copy_textures(&render_state.device, &output_frame, &mut encoder)) + } else { + None + }; Some((output_frame, capture_buffer)) }; diff --git a/crates/egui-wgpu/src/capture.rs b/crates/egui-wgpu/src/capture.rs index 58407fdd6..7eb1bd3ca 100644 --- a/crates/egui-wgpu/src/capture.rs +++ b/crates/egui-wgpu/src/capture.rs @@ -47,7 +47,7 @@ impl CaptureState { }, depth_stencil: None, multisample: MultisampleState::default(), - multiview: None, + multiview_mask: None, cache: None, }); @@ -165,6 +165,7 @@ impl CaptureState { depth_stencil_attachment: None, occlusion_query_set: None, timestamp_writes: None, + multiview_mask: None, }); pass.set_pipeline(&self.pipeline); diff --git a/crates/egui-wgpu/src/lib.rs b/crates/egui-wgpu/src/lib.rs index 880ab8f4a..46becf8f7 100644 --- a/crates/egui-wgpu/src/lib.rs +++ b/crates/egui-wgpu/src/lib.rs @@ -185,7 +185,7 @@ impl RenderState { wgpu::Backends::all() }; - instance.enumerate_adapters(backends) + instance.enumerate_adapters(backends).await }; let (adapter, device, queue) = match config.wgpu_setup.clone() { @@ -395,6 +395,10 @@ pub fn adapter_info_summary(info: &wgpu::AdapterInfo) -> String { driver, driver_info, backend, + device_pci_bus_id, + subgroup_min_size, + subgroup_max_size, + transient_saves_memory, } = &info; // Example values: @@ -426,6 +430,13 @@ pub fn adapter_info_summary(info: &wgpu::AdapterInfo) -> String { if *device != 0 { summary += &format!(", device: 0x{device:02X}"); } + if !device_pci_bus_id.is_empty() { + summary += &format!(", pci_bus_id: {device_pci_bus_id:?}"); + } + if *subgroup_min_size != 0 || *subgroup_max_size != 0 { + summary += &format!(", subgroup_size: {subgroup_min_size}..={subgroup_max_size}"); + } + summary += &format!(", transient_saves_memory: {transient_saves_memory}"); summary } diff --git a/crates/egui-wgpu/src/renderer.rs b/crates/egui-wgpu/src/renderer.rs index d3d21f19c..c37802448 100644 --- a/crates/egui-wgpu/src/renderer.rs +++ b/crates/egui-wgpu/src/renderer.rs @@ -353,7 +353,7 @@ impl Renderer { let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("egui_pipeline_layout"), bind_group_layouts: &[&uniform_bind_group_layout, &texture_bind_group_layout], - push_constant_ranges: &[], + immediate_size: 0, }); let depth_stencil = options @@ -426,7 +426,7 @@ impl Renderer { })], compilation_options: wgpu::PipelineCompilationOptions::default() }), - multiview: None, + multiview_mask: None, cache: None, } ) diff --git a/crates/egui-wgpu/src/winit.rs b/crates/egui-wgpu/src/winit.rs index 167d10c79..5fb8d123a 100644 --- a/crates/egui-wgpu/src/winit.rs +++ b/crates/egui-wgpu/src/winit.rs @@ -362,14 +362,13 @@ impl Painter { #[cfg(all(target_os = "macos", feature = "macos-window-resize-jitter-fix"))] { // SAFETY: The cast is checked with if condition. If the used backend is not metal - // it gracefully fails. The pointer casts are valid as it's 1-to-1 type mapping. - // This is how wgpu currently exposes this backend-specific flag. + // it gracefully fails. unsafe { if let Some(hal_surface) = state.surface.as_hal::() { - let raw = - std::ptr::from_ref::(&*hal_surface).cast_mut(); - - (*raw).present_with_transaction = resizing; + hal_surface + .render_layer() + .lock() + .set_presents_with_transaction(resizing); Self::configure_surface( state, @@ -421,12 +420,40 @@ impl Painter { ) -> f32 { profiling::function_scope!(); + /// Guard to ensure that commands are always submitted to the renderer queue + /// so that calls to [`write_buffer()`](https://docs.rs/wgpu/latest/wgpu/struct.Queue.html#method.write_buffer) + /// are completed even if we take a codepath which doesn't submit commands and avoids + /// internal buffers growing indefinitely. + /// + /// This may happen, for example, if no output frame is resolved. + /// See for full context. + struct RendererQueueGuard<'q> { + queue: &'q wgpu::Queue, + commands_submitted: bool, + } + + impl Drop for RendererQueueGuard<'_> { + fn drop(&mut self) { + // Only submit an empty command buffer array if no commands were + // explicitly submitted. + if !self.commands_submitted { + self.queue.submit([]); + } + } + } + let capture = !capture_data.is_empty(); let mut vsync_sec = 0.0; let Some(render_state) = self.render_state.as_mut() else { return vsync_sec; }; + + let mut render_queue_guard = RendererQueueGuard { + queue: &render_state.queue, + commands_submitted: false, + }; + let Some(surface_state) = self.surfaces.get(&viewport_id) else { return vsync_sec; }; @@ -554,6 +581,7 @@ impl Painter { }), timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); // Forgetting the pass' lifetime means that we are no longer compile-time protected from @@ -590,6 +618,9 @@ impl Painter { vsync_sec += start.elapsed().as_secs_f32(); }; + // Ensure that the queue guard does not do unnecessary work when dropped + render_queue_guard.commands_submitted = true; + // Free textures marked for destruction **after** queue submit since they might still be used in the current frame. // Calling `wgpu::Texture::destroy` on a texture that is still in use would invalidate the command buffer(s) it is used in. // However, once we called `wgpu::Queue::submit`, it is up for wgpu to determine how long the underlying gpu resource has to live. diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index 54059cbd6..234a9989b 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -102,7 +102,7 @@ pub struct State { has_sent_ime_enabled: bool, #[cfg(feature = "accesskit")] - accesskit: Option, + pub accesskit: Option, allow_ime: bool, ime_rect_px: Option, @@ -548,23 +548,23 @@ impl State { /// /// | Setup | Events in Order | /// | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | - /// | a-macos15-apple_shuangpin | `Predict("", None)` -> `Commit("测试")` | - /// | b-debian13_gnome48_wayland-fcitx5_shuangpin | `Predict("", None)` -> `Commit("测试")` -> `Predict("", Some(0, 0))` -> `Predict("", None)` (duplicate until `TextEdit` blurred) | - /// | c-windows11-ms_pinyin | `Predict("测试", Some(…))` -> `Predict("", None)` -> `Commit("测试")` -> `Disabled` | + /// | a-macos15-apple_shuangpin | `Preedit("", None)` -> `Commit("测试")` | + /// | b-debian13_gnome48_wayland-fcitx5_shuangpin | `Preedit("", None)` -> `Commit("测试")` -> `Preedit("", Some(0, 0))` -> `Preedit("", None)` (duplicate until `TextEdit` blurred) | + /// | c-windows11-ms_pinyin | `Preedit("测试", Some(…))` -> `Preedit("", None)` -> `Commit("测试")` -> `Disabled` | /// - /// #### Situation: pressed backspace to delete the last character in the prediction + /// #### Situation: pressed backspace to delete the last character in the composition /// /// | Setup | Events in Order | - /// | a-macos15-apple_shuangpin | `Predict("", None)` | - /// | b-debian13_gnome48_wayland-fcitx5_shuangpin | `Predict("", Some(0, 0))` -> `Predict("", None)` (duplicate until `TextEdit` blurred) | - /// | c-windows11-ms_pinyin | `Predict("", Some(0, 0))` -> `Predict("", None)` -> `Commit("")` -> `Disabled` | + /// | a-macos15-apple_shuangpin | `Preedit("", None)` | + /// | b-debian13_gnome48_wayland-fcitx5_shuangpin | `Preedit("", Some(0, 0))` -> `Preedit("", None)` (duplicate until `TextEdit` blurred) | + /// | c-windows11-ms_pinyin | `Preedit("", Some(0, 0))` -> `Preedit("", None)` -> `Commit("")` -> `Disabled` | /// - /// #### Situation: clicked somewhere else while there is an active composition with the prediction "ce" + /// #### Situation: clicked somewhere else while there is an active composition with the pre-edit text "ce" /// /// | Setup | Events in Order | /// | ------------------------------------------- | ------------------------------------------------------------------------------------------------- | /// | a-macos15-apple_shuangpin | nothing emitted | - /// | b-debian13_gnome48_wayland-fcitx5_shuangpin | `Predict("", Some(0, 0))` (duplicate) -> `Predict("", None)` (duplicate until `TextEdit` blurred) | + /// | b-debian13_gnome48_wayland-fcitx5_shuangpin | `Preedit("", Some(0, 0))` (duplicate) -> `Preedit("", None)` (duplicate until `TextEdit` blurred) | /// | c-windows11-ms_pinyin | nothing emitted | fn on_ime(&mut self, ime: &winit::event::Ime) { // // code for inspecting ime events emitted by winit: @@ -610,15 +610,26 @@ impl State { self.ime_event_disable(); } winit::event::Ime::Preedit(_, None) => { - // we need to emit this on macOS, since winit doesn't emit - // `Predict("", Some(0, 0))` before this event on macOS when the - // user deletes the last character in the prediction with the - // backspace key. Without this, only `egui::ImeEvent::Disabled` - // is emitted here, leading to the last character being left in - // TextEdit in such situation. - self.egui_input - .events - .push(egui::Event::Ime(egui::ImeEvent::Preedit(String::new()))); + if cfg!(target_os = "macos") { + // On macOS, when the user presses backspace to delete the + // last character in an IME composition, `winit` only emits + // `winit::event::Ime::Preedit("", None)` without a + // preceding `winit::event::Ime::Preedit("", Some(0, 0))`. + // + // The current implementation of `egui::TextEdit` relies on + // receiving an `egui::ImeEvent::Preedit("")` to remove the + // last character in the composition in this case, so we + // emit it here. + // + // This is guarded to macOS-only, as applying it on other + // platforms is unnecessary and can cause undesired + // behavior. + // See: https://github.com/emilk/egui/pull/7973 + self.egui_input + .events + .push(egui::Event::Ime(egui::ImeEvent::Preedit(String::new()))); + } + self.ime_event_disable(); } } @@ -640,11 +651,27 @@ impl State { self.has_sent_ime_enabled = false; } - pub fn on_mouse_motion(&mut self, delta: (f64, f64)) { + /// Returns `true` if the event was sent to egui. + pub fn on_mouse_motion(&mut self, delta: (f64, f64)) -> bool { + if !self.is_pointer_in_window() && !self.any_pointer_button_down { + return false; + } + self.egui_input.events.push(egui::Event::MouseMoved(Vec2 { x: delta.0 as f32, y: delta.1 as f32, })); + true + } + + /// Returns `true` when the pointer is currently inside the window. + pub fn is_pointer_in_window(&self) -> bool { + self.pointer_pos_in_points.is_some() + } + + /// Returns `true` if any pointer button is currently held down. + pub fn is_any_pointer_button_down(&self) -> bool { + self.any_pointer_button_down } /// Call this when there is a new [`accesskit::ActionRequest`]. diff --git a/crates/egui/src/atomics/atom.rs b/crates/egui/src/atomics/atom.rs index 6425ac724..4db7f12a9 100644 --- a/crates/egui/src/atomics/atom.rs +++ b/crates/egui/src/atomics/atom.rs @@ -1,5 +1,5 @@ -use crate::{AtomKind, FontSelection, Id, SizedAtom, Ui}; -use emath::{NumExt as _, Vec2}; +use crate::{AtomKind, FontSelection, Id, IntoSizedArgs, IntoSizedResult, SizedAtom, Ui}; +use emath::{Align2, NumExt as _, Vec2}; use epaint::text::TextWrapMode; /// A low-level ui building block. @@ -14,6 +14,9 @@ use epaint::text::TextWrapMode; /// ``` #[derive(Clone, Debug)] pub struct Atom<'a> { + /// See [`crate::AtomExt::atom_id`] + pub id: Option, + /// See [`crate::AtomExt::atom_size`] pub size: Option, @@ -26,17 +29,22 @@ pub struct Atom<'a> { /// See [`crate::AtomExt::atom_shrink`] pub shrink: bool, - /// The atom type + /// See [`crate::AtomExt::atom_align`] + pub align: Align2, + + /// The atom type / content pub kind: AtomKind<'a>, } impl Default for Atom<'_> { fn default() -> Self { Atom { + id: None, size: None, max_size: Vec2::INFINITY, grow: false, shrink: false, + align: Align2::CENTER_CENTER, kind: AtomKind::Empty, } } @@ -54,11 +62,27 @@ impl<'a> Atom<'a> { } } - /// Create a [`AtomKind::Custom`] with a specific size. + /// Create an [`AtomKind::Empty`] with a specific size. + /// + /// Example: + /// ``` + /// # use egui::{AtomExt, AtomKind, Atom, Button, Id, __run_test_ui}; + /// # use emath::Vec2; + /// # __run_test_ui(|ui| { + /// let id = Id::new("my_button"); + /// let response = Button::new(("Hi!", Atom::custom(id, Vec2::splat(18.0)))).atom_ui(ui); + /// + /// let rect = response.rect(id); + /// if let Some(rect) = rect { + /// ui.place(rect, Button::new("⏵")); + /// } + /// # }); + /// ``` pub fn custom(id: Id, size: impl Into) -> Self { Atom { size: Some(size.into()), - kind: AtomKind::Custom(id), + kind: AtomKind::Empty, + id: Some(id), ..Default::default() } } @@ -82,19 +106,32 @@ impl<'a> Atom<'a> { wrap_mode = Some(TextWrapMode::Truncate); } - let (intrinsic, kind) = self - .kind - .into_sized(ui, available_size, wrap_mode, fallback_font); + let id = self.id; + + let wrap_mode = wrap_mode.unwrap_or_else(|| ui.wrap_mode()); + let IntoSizedResult { + intrinsic_size, + sized, + } = self.kind.into_sized( + ui, + IntoSizedArgs { + available_size, + wrap_mode, + fallback_font, + }, + ); let size = self .size - .map_or_else(|| kind.size(), |s| s.at_most(self.max_size)); + .map_or_else(|| sized.size(), |s| s.at_most(self.max_size)); SizedAtom { + id, size, - intrinsic_size: intrinsic.at_least(self.size.unwrap_or_default()), + intrinsic_size: intrinsic_size.at_least(self.size.unwrap_or_default()), grow: self.grow, - kind, + align: self.align, + kind: sized, } } } diff --git a/crates/egui/src/atomics/atom_ext.rs b/crates/egui/src/atomics/atom_ext.rs index 6d008b84b..bfe587fae 100644 --- a/crates/egui/src/atomics/atom_ext.rs +++ b/crates/egui/src/atomics/atom_ext.rs @@ -1,10 +1,16 @@ -use crate::{Atom, FontSelection, Ui}; +use crate::{Atom, FontSelection, Id, Ui}; use emath::Vec2; /// A trait for conveniently building [`Atom`]s. /// /// The functions are prefixed with `atom_` to avoid conflicts with e.g. [`crate::RichText::size`]. pub trait AtomExt<'a> { + /// Set the [`Id`] for custom rendering. + /// + /// You can get the [`crate::Rect`] with the [`Id`] from [`crate::AtomLayoutResponse`] and use a + /// [`crate::Painter`] or [`Ui::place`] to add/draw some custom content. + fn atom_id(self, id: Id) -> Atom<'a>; + /// Set the atom to a fixed size. /// /// If [`Atom::grow`] is `true`, this will be the minimum width. @@ -63,12 +69,23 @@ pub trait AtomExt<'a> { let height = ui.fonts_mut(|f| f.row_height(&font_id)); self.atom_max_height(height) } + + /// Sets the [`emath::Align2`] of a single atom within its available space. + /// + /// Defaults to center-center. + fn atom_align(self, align: emath::Align2) -> Atom<'a>; } impl<'a, T> AtomExt<'a> for T where T: Into> + Sized, { + fn atom_id(self, id: Id) -> Atom<'a> { + let mut atom = self.into(); + atom.id = Some(id); + atom + } + fn atom_size(self, size: Vec2) -> Atom<'a> { let mut atom = self.into(); atom.size = Some(size); @@ -104,4 +121,10 @@ where atom.max_size.y = max_height; atom } + + fn atom_align(self, align: emath::Align2) -> Atom<'a> { + let mut atom = self.into(); + atom.align = align; + atom + } } diff --git a/crates/egui/src/atomics/atom_kind.rs b/crates/egui/src/atomics/atom_kind.rs index 10ca3353b..ec2ab8f63 100644 --- a/crates/egui/src/atomics/atom_kind.rs +++ b/crates/egui/src/atomics/atom_kind.rs @@ -1,9 +1,28 @@ -use crate::{FontSelection, Id, Image, ImageSource, SizedAtomKind, Ui, WidgetText}; +use crate::{FontSelection, Image, ImageSource, SizedAtomKind, Ui, WidgetText}; use emath::Vec2; use epaint::text::TextWrapMode; +use std::fmt::Debug; + +/// Args passed when sizing an [`super::Atom`] +pub struct IntoSizedArgs { + pub available_size: Vec2, + pub wrap_mode: TextWrapMode, + pub fallback_font: FontSelection, +} + +/// Result returned when sizing an [`super::Atom`] +pub struct IntoSizedResult<'a> { + pub intrinsic_size: Vec2, + pub sized: SizedAtomKind<'a>, +} + +/// See [`AtomKind::Closure`] +// We need 'static in the result (or need to introduce another lifetime on the enum). +// Otherwise, a single 'static Atom would force the closure to be 'static. +pub type AtomClosure<'a> = Box IntoSizedResult<'static> + 'a>; /// The different kinds of [`crate::Atom`]s. -#[derive(Clone, Default, Debug)] +#[derive(Default)] pub enum AtomKind<'a> { /// Empty, that can be used with [`crate::AtomExt::atom_grow`] to reserve space. #[default] @@ -38,37 +57,57 @@ pub enum AtomKind<'a> { /// default font height, which is convenient for icons. Image(Image<'a>), - /// For custom rendering. + /// A custom closure that produces a sized atom. /// - /// You can get the [`crate::Rect`] with the [`Id`] from [`crate::AtomLayoutResponse`] and use a - /// [`crate::Painter`] or [`Ui::place`] to add/draw some custom content. + /// The vec2 passed in is the available size to this atom. The returned vec2 should be the + /// preferred / intrinsic size. /// - /// Example: - /// ``` - /// # use egui::{AtomExt, AtomKind, Atom, Button, Id, __run_test_ui}; - /// # use emath::Vec2; - /// # __run_test_ui(|ui| { - /// let id = Id::new("my_button"); - /// let response = Button::new(("Hi!", Atom::custom(id, Vec2::splat(18.0)))).atom_ui(ui); - /// - /// let rect = response.rect(id); - /// if let Some(rect) = rect { - /// ui.place(rect, Button::new("⏵")); - /// } - /// # }); - /// ``` - Custom(Id), + /// Note: This api is experimental, expect breaking changes here. + /// When cloning, this will be cloned as [`AtomKind::Empty`]. + Closure(AtomClosure<'a>), +} + +impl Clone for AtomKind<'_> { + fn clone(&self) -> Self { + match self { + AtomKind::Empty => AtomKind::Empty, + AtomKind::Text(text) => AtomKind::Text(text.clone()), + AtomKind::Image(image) => AtomKind::Image(image.clone()), + AtomKind::Closure(_) => { + log::warn!("Cannot clone atom closures"); + AtomKind::Empty + } + } + } +} + +impl Debug for AtomKind<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AtomKind::Empty => write!(f, "AtomKind::Empty"), + AtomKind::Text(text) => write!(f, "AtomKind::Text({text:?})"), + AtomKind::Image(image) => write!(f, "AtomKind::Image({image:?})"), + AtomKind::Closure(_) => write!(f, "AtomKind::Closure()"), + } + } } impl<'a> AtomKind<'a> { + /// See [`Self::Text`] pub fn text(text: impl Into) -> Self { AtomKind::Text(text.into()) } + /// See [`Self::Image`] pub fn image(image: impl Into>) -> Self { AtomKind::Image(image.into()) } + /// See [`Self::Closure`] + pub fn closure(func: impl FnOnce(&Ui, IntoSizedArgs) -> IntoSizedResult<'static> + 'a) -> Self { + AtomKind::Closure(Box::new(func)) + } + /// Turn this [`AtomKind`] into a [`SizedAtomKind`]. /// /// This converts [`WidgetText`] into [`crate::Galley`] and tries to load and size [`Image`]. @@ -76,23 +115,40 @@ impl<'a> AtomKind<'a> { pub fn into_sized( self, ui: &Ui, - available_size: Vec2, - wrap_mode: Option, - fallback_font: FontSelection, - ) -> (Vec2, SizedAtomKind<'a>) { + IntoSizedArgs { + available_size, + wrap_mode, + fallback_font, + }: IntoSizedArgs, + ) -> IntoSizedResult<'a> { match self { AtomKind::Text(text) => { - let wrap_mode = wrap_mode.unwrap_or_else(|| ui.wrap_mode()); let galley = text.into_galley(ui, Some(wrap_mode), available_size.x, fallback_font); - (galley.intrinsic_size(), SizedAtomKind::Text(galley)) + IntoSizedResult { + intrinsic_size: galley.intrinsic_size(), + sized: SizedAtomKind::Text(galley), + } } AtomKind::Image(image) => { let size = image.load_and_calc_size(ui, available_size); let size = size.unwrap_or(Vec2::ZERO); - (size, SizedAtomKind::Image(image, size)) + IntoSizedResult { + intrinsic_size: size, + sized: SizedAtomKind::Image { image, size }, + } } - AtomKind::Custom(id) => (Vec2::ZERO, SizedAtomKind::Custom(id)), - AtomKind::Empty => (Vec2::ZERO, SizedAtomKind::Empty), + AtomKind::Empty => IntoSizedResult { + intrinsic_size: Vec2::ZERO, + sized: SizedAtomKind::Empty { size: None }, + }, + AtomKind::Closure(func) => func( + ui, + IntoSizedArgs { + available_size, + wrap_mode, + fallback_font, + }, + ), } } } diff --git a/crates/egui/src/atomics/atom_layout.rs b/crates/egui/src/atomics/atom_layout.rs index 8132a7dc9..b78f23536 100644 --- a/crates/egui/src/atomics/atom_layout.rs +++ b/crates/egui/src/atomics/atom_layout.rs @@ -38,6 +38,7 @@ pub struct AtomLayout<'a> { fallback_text_color: Option, fallback_font: Option, min_size: Vec2, + max_size: Vec2, wrap_mode: Option, align2: Option, } @@ -59,6 +60,7 @@ impl<'a> AtomLayout<'a> { fallback_text_color: None, fallback_font: None, min_size: Vec2::ZERO, + max_size: Vec2::INFINITY, wrap_mode: None, align2: None, } @@ -113,6 +115,33 @@ impl<'a> AtomLayout<'a> { self } + /// Set the maximum size of the Widget. + /// + /// By default, the size is limited by the available size in the [`Ui`]. + #[inline] + pub fn max_size(mut self, size: Vec2) -> Self { + self.max_size = size; + self + } + + /// Set the maximum width of the Widget. + /// + /// By default, the width is limited by the available width in the [`Ui`]. + #[inline] + pub fn max_width(mut self, width: f32) -> Self { + self.max_size.x = width; + self + } + + /// Set the maximum height of the Widget. + /// + /// By default, the height is limited by the available height in the [`Ui`]. + #[inline] + pub fn max_height(mut self, height: f32) -> Self { + self.max_size.y = height; + self + } + /// Set the [`Id`] used to allocate a [`Response`]. #[inline] pub fn id(mut self, id: Id) -> Self { @@ -161,6 +190,7 @@ impl<'a> AtomLayout<'a> { sense, fallback_text_color, min_size, + mut max_size, wrap_mode, align2, fallback_font, @@ -190,8 +220,16 @@ impl<'a> AtomLayout<'a> { fallback_text_color.unwrap_or_else(|| ui.style().visuals.text_color()); let gap = gap.unwrap_or_else(|| ui.spacing().icon_spacing); + // max_size has no effect in justified layouts. If we'd limit the available size here, + // the content would be sized differently than the frame which would look weird. + if ui.layout().horizontal_justify() { + max_size.x = f32::INFINITY; + } + + let available_size = ui.available_size().at_most(max_size); + // The size available for the content - let available_inner_size = ui.available_size() - frame.total_margin().sum(); + let available_inner_size = available_size - frame.total_margin().sum(); let mut desired_width = 0.0; @@ -321,7 +359,7 @@ impl<'atom> AllocatedAtomLayout<'atom> { pub fn iter_images(&self) -> impl Iterator> { self.iter_kinds().filter_map(|kind| { - if let SizedAtomKind::Image(image, _) = kind { + if let SizedAtomKind::Image { image, size: _ } = kind { Some(image) } else { None @@ -331,7 +369,7 @@ impl<'atom> AllocatedAtomLayout<'atom> { pub fn iter_images_mut(&mut self) -> impl Iterator> { self.iter_kinds_mut().filter_map(|kind| { - if let SizedAtomKind::Image(image, _) = kind { + if let SizedAtomKind::Image { image, size: _ } = kind { Some(image) } else { None @@ -373,8 +411,11 @@ impl<'atom> AllocatedAtomLayout<'atom> { F: FnMut(Image<'atom>) -> Image<'atom>, { self.map_kind(|kind| { - if let SizedAtomKind::Image(image, size) = kind { - SizedAtomKind::Image(f(image), size) + if let SizedAtomKind::Image { image, size } = kind { + SizedAtomKind::Image { + image: f(image), + size, + } } else { kind } @@ -422,25 +463,24 @@ impl<'atom> AllocatedAtomLayout<'atom> { .with_min_x(cursor) .with_max_x(cursor + size.x + growth); cursor = frame.right() + gap; + let rect = sized.align.align_size_within_rect(size, frame); - let align = Align2::CENTER_CENTER; - let rect = align.align_size_within_rect(size, frame); + if let Some(id) = sized.id { + debug_assert!( + !response.custom_rects.iter().any(|(i, _)| *i == id), + "Duplicate custom id" + ); + response.custom_rects.push((id, rect)); + } match sized.kind { SizedAtomKind::Text(galley) => { ui.painter().galley(rect.min, galley, fallback_text_color); } - SizedAtomKind::Image(image, _) => { + SizedAtomKind::Image { image, size: _ } => { image.paint_at(ui, rect); } - SizedAtomKind::Custom(id) => { - debug_assert!( - !response.custom_rects.iter().any(|(i, _)| *i == id), - "Duplicate custom id" - ); - response.custom_rects.push((id, rect)); - } - SizedAtomKind::Empty => {} + SizedAtomKind::Empty { .. } => {} } } @@ -450,7 +490,7 @@ impl<'atom> AllocatedAtomLayout<'atom> { /// Response from a [`AtomLayout::show`] or [`AllocatedAtomLayout::paint`]. /// -/// Use [`AtomLayoutResponse::rect`] to get the response rects from [`AtomKind::Custom`]. +/// Use [`AtomLayoutResponse::rect`] to get the response rects from [`crate::Atom::custom`]. #[derive(Clone, Debug)] pub struct AtomLayoutResponse { pub response: Response, @@ -470,7 +510,7 @@ impl AtomLayoutResponse { self.custom_rects.iter().copied() } - /// Use this together with [`AtomKind::Custom`] to add custom painting / child widgets. + /// Use this together with [`crate::Atom::custom`] to add custom painting / child widgets. /// /// NOTE: Don't `unwrap` rects, they might be empty when the widget is not visible. pub fn rect(&self, id: Id) -> Option { diff --git a/crates/egui/src/atomics/atoms.rs b/crates/egui/src/atomics/atoms.rs index 1db7c63c6..fb04ee2dd 100644 --- a/crates/egui/src/atomics/atoms.rs +++ b/crates/egui/src/atomics/atoms.rs @@ -21,11 +21,26 @@ impl<'a> Atoms<'a> { self.0.push(atom.into()); } + /// Extend the list of atoms by appending more atoms to the right side. + /// + /// If you have weird lifetime issues with this, use [`Self::push_right`] in a loop instead. + pub fn extend_right(&mut self, atoms: Self) { + self.0.extend(atoms.0); + } + /// Insert a new [`Atom`] at the beginning of the list (left side). pub fn push_left(&mut self, atom: impl Into>) { self.0.insert(0, atom.into()); } + /// Extend the list of atoms by prepending more atoms to the left side. + /// + /// If you have weird lifetime issues with this, use [`Self::push_left`] in a loop instead. + pub fn extend_left(&mut self, mut atoms: Self) { + std::mem::swap(&mut atoms.0, &mut self.0); + self.0.extend(atoms.0); + } + /// Concatenate and return the text contents. // TODO(lucasmerlin): It might not always make sense to return the concatenated text, e.g. // in a submenu button there is a right text '⏵' which is now passed to the screen reader. diff --git a/crates/egui/src/atomics/sized_atom.rs b/crates/egui/src/atomics/sized_atom.rs index f1ae0f81b..19c289ab3 100644 --- a/crates/egui/src/atomics/sized_atom.rs +++ b/crates/egui/src/atomics/sized_atom.rs @@ -4,6 +4,8 @@ use emath::Vec2; /// A [`crate::Atom`] which has been sized. #[derive(Clone, Debug)] pub struct SizedAtom<'a> { + pub id: Option, + pub(crate) grow: bool, /// The size of the atom. @@ -15,6 +17,9 @@ pub struct SizedAtom<'a> { /// Intrinsic size of the atom. This is used to calculate `Response::intrinsic_size`. pub intrinsic_size: Vec2, + /// How will the atom be aligned in its available space? + pub align: emath::Align2, + pub kind: SizedAtomKind<'a>, } diff --git a/crates/egui/src/atomics/sized_atom_kind.rs b/crates/egui/src/atomics/sized_atom_kind.rs index ff8da1631..02263adad 100644 --- a/crates/egui/src/atomics/sized_atom_kind.rs +++ b/crates/egui/src/atomics/sized_atom_kind.rs @@ -1,16 +1,20 @@ -use crate::{Id, Image}; +use crate::Image; use emath::Vec2; use epaint::Galley; use std::sync::Arc; /// A sized [`crate::AtomKind`]. -#[derive(Clone, Default, Debug)] +#[derive(Clone, Debug)] pub enum SizedAtomKind<'a> { - #[default] - Empty, + Empty { size: Option }, Text(Arc), - Image(Image<'a>, Vec2), - Custom(Id), + Image { image: Image<'a>, size: Vec2 }, +} + +impl Default for SizedAtomKind<'_> { + fn default() -> Self { + Self::Empty { size: None } + } } impl SizedAtomKind<'_> { @@ -18,8 +22,8 @@ impl SizedAtomKind<'_> { pub fn size(&self) -> Vec2 { match self { SizedAtomKind::Text(galley) => galley.size(), - SizedAtomKind::Image(_, size) => *size, - SizedAtomKind::Empty | SizedAtomKind::Custom(_) => Vec2::ZERO, + SizedAtomKind::Image { image: _, size } => *size, + SizedAtomKind::Empty { size } => size.unwrap_or_default(), } } } diff --git a/crates/egui/src/containers/panel.rs b/crates/egui/src/containers/panel.rs index 6281e6b41..f2a4c3b67 100644 --- a/crates/egui/src/containers/panel.rs +++ b/crates/egui/src/containers/panel.rs @@ -1066,7 +1066,7 @@ impl CentralPanel { id, UiBuilder::new() .layer_id(LayerId::background()) - .max_rect(ctx.available_rect().round_ui()), + .max_rect(ctx.available_rect()), ); panel_ui.set_clip_rect(ctx.content_rect()); diff --git a/crates/egui/src/containers/scroll_area.rs b/crates/egui/src/containers/scroll_area.rs index 8f46223d2..2616fb414 100644 --- a/crates/egui/src/containers/scroll_area.rs +++ b/crates/egui/src/containers/scroll_area.rs @@ -1198,10 +1198,9 @@ impl Prepared { // Clear scroll delta so no parent scroll will use it: ui.input_mut(|input| { if always_scroll_enabled_direction { - input.smooth_scroll_delta()[0] = 0.0; - input.smooth_scroll_delta()[1] = 0.0; + input.smooth_scroll_delta = Vec2::ZERO; } else { - input.smooth_scroll_delta()[d] = 0.0; + input.smooth_scroll_delta[d] = 0.0; } }); diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index e28048edf..3b36150e4 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -798,7 +798,7 @@ impl Context { Id::new((ctx.viewport_id(), "__top_ui")), UiBuilder::new() .layer_id(LayerId::background()) - .max_rect(ctx.available_rect().round_ui()), + .max_rect(ctx.available_rect()), ); { @@ -2616,6 +2616,7 @@ impl ContextImpl { platform_output.accesskit_update = Some(accesskit::TreeUpdate { nodes, tree: Some(accesskit::Tree::new(root_id)), + tree_id: accesskit::TreeId::ROOT, focus: focus_id, }); } @@ -3262,7 +3263,7 @@ impl Context { for (name, data) in &mut font_definitions.font_data { ui.collapsing(name, |ui| { - let mut tweak = data.tweak; + let mut tweak = data.tweak.clone(); if tweak.ui(ui).changed() { Arc::make_mut(data).tweak = tweak; changed = true; diff --git a/crates/egui/src/data/input.rs b/crates/egui/src/data/input.rs index 787867569..a52d40233 100644 --- a/crates/egui/src/data/input.rs +++ b/crates/egui/src/data/input.rs @@ -253,9 +253,28 @@ pub struct ViewportInfo { /// /// This should be the same as [`RawInput::focused`]. pub focused: Option, + + /// Is the window fully occluded (completely covered) by another window? + /// + /// Not all platforms support this. + /// On platforms that don't, this will be `None` or `Some(false)`. + pub occluded: Option, } impl ViewportInfo { + /// Is the window considered visible for rendering purposes? + /// + /// A window is not visible if it is minimized or occluded. + /// When not visible, the UI is not painted and rendering is skipped, + /// but application logic may still be executed by some integrations. + pub fn visible(&self) -> Option { + match (self.minimized, self.occluded) { + (Some(true), _) | (_, Some(true)) => Some(false), + (Some(false), Some(false)) => Some(true), + (_, None) | (None, _) => None, + } + } + /// This viewport has been told to close. /// /// If this is the root viewport, the application will exit @@ -282,6 +301,7 @@ impl ViewportInfo { maximized: self.maximized, fullscreen: self.fullscreen, focused: self.focused, + occluded: self.occluded, } } @@ -298,6 +318,7 @@ impl ViewportInfo { maximized, fullscreen, focused, + occluded, } = self; crate::Grid::new("viewport_info").show(ui, |ui| { @@ -345,6 +366,16 @@ impl ViewportInfo { ui.label(opt_as_str(focused)); ui.end_row(); + ui.label("Occluded:"); + ui.label(opt_as_str(occluded)); + ui.end_row(); + + let visible = self.visible(); + + ui.label("Visible:"); + ui.label(opt_as_str(&visible)); + ui.end_row(); + fn opt_rect_as_string(v: &Option) -> String { v.as_ref().map_or(String::new(), |r| { format!("Pos: {:?}, size: {:?}", r.min, r.size()) diff --git a/crates/egui/src/id.rs b/crates/egui/src/id.rs index 7484930c8..661bdf2bf 100644 --- a/crates/egui/src/id.rs +++ b/crates/egui/src/id.rs @@ -79,7 +79,7 @@ impl Id { self.0.get() } - pub(crate) fn accesskit_id(&self) -> accesskit::NodeId { + pub fn accesskit_id(&self) -> accesskit::NodeId { self.value().into() } diff --git a/crates/egui/src/input_state/mod.rs b/crates/egui/src/input_state/mod.rs index 37faf64c2..b32a75c57 100644 --- a/crates/egui/src/input_state/mod.rs +++ b/crates/egui/src/input_state/mod.rs @@ -661,6 +661,8 @@ impl InputState { if self.pointer.wants_repaint() || self.wheel.unprocessed_wheel_delta.abs().max_elem() > 0.2 || !self.events.is_empty() + || !self.raw.hovered_files.is_empty() + || !self.raw.dropped_files.is_empty() { // Immediate repaint return Some(Duration::ZERO); @@ -869,7 +871,8 @@ impl InputState { let accesskit_id = id.accesskit_id(); self.events.iter().filter_map(move |event| { if let Event::AccessKitActionRequest(request) = event - && request.target == accesskit_id + && request.target_node == accesskit_id + && request.target_tree == accesskit::TreeId::ROOT && request.action == action { return Some(request); @@ -886,7 +889,8 @@ impl InputState { let accesskit_id = id.accesskit_id(); self.events.retain(|event| { if let Event::AccessKitActionRequest(request) = event - && request.target == accesskit_id + && request.target_node == accesskit_id + && request.target_tree == accesskit::TreeId::ROOT { return !consume(request); } diff --git a/crates/egui/src/lib.rs b/crates/egui/src/lib.rs index bc90f0cf9..d86851a1d 100644 --- a/crates/egui/src/lib.rs +++ b/crates/egui/src/lib.rs @@ -685,7 +685,7 @@ pub fn __run_test_ctx(mut run_ui: impl FnMut(&Context)) { } /// For use in tests; especially doctests. -pub fn __run_test_ui(add_contents: impl Fn(&mut Ui)) { +pub fn __run_test_ui(mut add_contents: impl FnMut(&mut Ui)) { let ctx = Context::default(); ctx.set_fonts(FontDefinitions::empty()); // prevent fonts from being loaded (save CPU time) let _ = ctx.run_ui(Default::default(), |ui| { diff --git a/crates/egui/src/memory/mod.rs b/crates/egui/src/memory/mod.rs index 9c91c1322..51ab2cde4 100644 --- a/crates/egui/src/memory/mod.rs +++ b/crates/egui/src/memory/mod.rs @@ -543,22 +543,19 @@ impl Focus { .. } = event && let Some(cardinality) = match key { - crate::Key::ArrowUp => Some(FocusDirection::Up), - crate::Key::ArrowRight => Some(FocusDirection::Right), - crate::Key::ArrowDown => Some(FocusDirection::Down), - crate::Key::ArrowLeft => Some(FocusDirection::Left), + crate::Key::ArrowUp if !modifiers.any() => Some(FocusDirection::Up), + crate::Key::ArrowRight if !modifiers.any() => Some(FocusDirection::Right), + crate::Key::ArrowDown if !modifiers.any() => Some(FocusDirection::Down), + crate::Key::ArrowLeft if !modifiers.any() => Some(FocusDirection::Left), - crate::Key::Tab => { - if modifiers.shift { - Some(FocusDirection::Previous) - } else { - Some(FocusDirection::Next) - } - } - crate::Key::Escape => { + crate::Key::Tab if !modifiers.any() => Some(FocusDirection::Next), + crate::Key::Tab if modifiers.shift_only() => Some(FocusDirection::Previous), + + crate::Key::Escape if !modifiers.any() => { self.focused_widget = None; Some(FocusDirection::None) } + _ => None, } { @@ -567,11 +564,13 @@ impl Focus { if let crate::Event::AccessKitActionRequest(accesskit::ActionRequest { action: accesskit::Action::Focus, - target, + target_node, + target_tree, data: None, }) = event + && *target_tree == accesskit::TreeId::ROOT { - self.id_requested_by_accesskit = Some(*target); + self.id_requested_by_accesskit = Some(*target_node); } } } diff --git a/crates/egui/src/placer.rs b/crates/egui/src/placer.rs index b5f68f72d..8c54204b9 100644 --- a/crates/egui/src/placer.rs +++ b/crates/egui/src/placer.rs @@ -1,4 +1,5 @@ use crate::{Layout, Painter, Pos2, Rect, Region, Vec2, grid, vec2}; +use emath::GuiRounding as _; #[cfg(debug_assertions)] use crate::{Align2, Color32, Stroke}; @@ -92,6 +93,7 @@ impl Placer { } else { self.layout.available_rect_before_wrap(&self.region) } + .round_ui() } /// Amount of space available for a widget. diff --git a/crates/egui/src/style.rs b/crates/egui/src/style.rs index a555b9ace..56a347f0d 100644 --- a/crates/egui/src/style.rs +++ b/crates/egui/src/style.rs @@ -1,7 +1,11 @@ //! egui theme (spacing, colors, etc). use emath::Align; -use epaint::{AlphaFromCoverage, CornerRadius, Shadow, Stroke, TextOptions, text::FontTweak}; +use epaint::{ + AlphaFromCoverage, CornerRadius, Shadow, Stroke, TextOptions, + mutex::Mutex, + text::{FontTweak, Tag}, +}; use std::{collections::BTreeMap, ops::RangeInclusive, sync::Arc}; use crate::{ @@ -2837,7 +2841,7 @@ impl Widget for &mut crate::Frame { impl Widget for &mut FontTweak { fn ui(self, ui: &mut Ui) -> Response { - let original: FontTweak = *self; + let original: FontTweak = self.clone(); let mut response = Grid::new("font_tweak") .num_columns(2) @@ -2847,6 +2851,7 @@ impl Widget for &mut FontTweak { y_offset_factor, y_offset, hinting_override, + coords, } = self; ui.label("Scale"); @@ -2874,6 +2879,50 @@ impl Widget for &mut FontTweak { ui.selectable_value(hinting_override, Some(true), "Enable"); ui.selectable_value(hinting_override, Some(false), "Disable"); }); + ui.end_row(); + + ui.label("coords"); + ui.end_row(); + let mut to_remove = None; + for (i, (tag, value)) in coords.as_mut().iter_mut().enumerate() { + let tag_text = ui.ctx().data_mut(|data| { + let tag = *tag; + Arc::clone(data.get_temp_mut_or_insert_with(ui.id().with(i), move || { + Arc::new(Mutex::new(tag.to_string())) + })) + }); + + let tag_text = &mut *tag_text.lock(); + let response = ui.text_edit_singleline(tag_text); + if response.changed() + && let Ok(new_tag) = Tag::new_checked(tag_text.as_bytes()) + { + *tag = new_tag; + } + // Reset stale text when not actively editing + // (e.g. after an item was removed and indices shifted) + if !response.has_focus() + && Tag::new_checked(tag_text.as_bytes()).ok() != Some(*tag) + { + *tag_text = tag.to_string(); + } + + ui.add(DragValue::new(value)); + if ui.small_button("🗑").clicked() { + to_remove = Some(i); + } + ui.end_row(); + } + if let Some(i) = to_remove { + coords.remove(i); + } + if ui.button("Add coord").clicked() { + coords.push(b"wght", 0.0); + } + if ui.button("Clear coords").clicked() { + coords.clear(); + } + ui.end_row(); if ui.button("Reset").clicked() { *self = Default::default(); diff --git a/crates/egui/src/text_selection/accesskit_text.rs b/crates/egui/src/text_selection/accesskit_text.rs index 974a334d0..650e7e5c0 100644 --- a/crates/egui/src/text_selection/accesskit_text.rs +++ b/crates/egui/src/text_selection/accesskit_text.rs @@ -4,6 +4,26 @@ use crate::{Context, Galley, Id}; use super::{CCursorRange, text_cursor_state::is_word_char}; +/// AccessKit's `word_starts` uses `u8` indices, so text runs cannot exceed this length. +pub(crate) const MAX_CHARS_PER_TEXT_RUN: usize = 255; + +/// Convert a (row, column) layout cursor position to a text run node ID and character index, +/// accounting for rows that are split into multiple text runs. +fn text_run_position(parent_id: Id, row: usize, column: usize) -> accesskit::TextPosition { + // When column lands exactly on a chunk boundary (e.g., 255), it refers to + // the end of the previous chunk, not the start of a new one. + let chunk_index = if column > 0 && column.is_multiple_of(MAX_CHARS_PER_TEXT_RUN) { + column / MAX_CHARS_PER_TEXT_RUN - 1 + } else { + column / MAX_CHARS_PER_TEXT_RUN + }; + let character_index = column - chunk_index * MAX_CHARS_PER_TEXT_RUN; + accesskit::TextPosition { + node: parent_id.with(row).with(chunk_index).accesskit_id(), + character_index, + } +} + /// Update accesskit with the current text state. pub fn update_accesskit_for_text_widget( ctx: &Context, @@ -20,14 +40,8 @@ pub fn update_accesskit_for_text_widget( let anchor = galley.layout_from_cursor(cursor_range.secondary); let focus = galley.layout_from_cursor(cursor_range.primary); builder.set_text_selection(accesskit::TextSelection { - anchor: accesskit::TextPosition { - node: parent_id.with(anchor.row).accesskit_id(), - character_index: anchor.column, - }, - focus: accesskit::TextPosition { - node: parent_id.with(focus.row).accesskit_id(), - character_index: focus.column, - }, + anchor: text_run_position(parent_id, anchor.row, anchor.column), + focus: text_run_position(parent_id, focus.row, focus.column), }); } @@ -40,61 +54,144 @@ pub fn update_accesskit_for_text_widget( return; }; + let mut prev_row_ended_with_newline = true; + for (row_index, row) in galley.rows.iter().enumerate() { - let row_id = parent_id.with(row_index); + let glyph_count = row.glyphs.len(); + let mut value = String::with_capacity(glyph_count); + let mut character_lengths = Vec::::with_capacity(glyph_count); + let mut character_positions = Vec::::with_capacity(glyph_count); + let mut character_widths = Vec::::with_capacity(glyph_count); + let mut word_starts = Vec::::new(); + // For soft-wrapped continuation rows, treat the start as a word + // boundary so the first word character gets a `word_starts` entry. + // Paragraph-starting runs (first row or after a newline) get an + // implicit word start from AccessKit, so they don't need this. + let mut was_at_word_end = !prev_row_ended_with_newline; - ctx.register_accesskit_parent(row_id, parent_id); + for glyph in &row.glyphs { + let is_word_char = is_word_char(glyph.chr); + if is_word_char && was_at_word_end { + word_starts.push(character_lengths.len()); + } + was_at_word_end = !is_word_char; + let old_len = value.len(); + value.push(glyph.chr); + character_lengths.push((value.len() - old_len) as _); + character_positions.push(glyph.pos.x - row.pos.x); + character_widths.push(glyph.advance_width); + } - ctx.accesskit_node_builder(row_id, |builder| { - builder.set_role(accesskit::Role::TextRun); - let rect = global_from_galley * row.rect_without_leading_space(); - builder.set_bounds(accesskit::Rect { - x0: rect.min.x.into(), - y0: rect.min.y.into(), - x1: rect.max.x.into(), - y1: rect.max.y.into(), - }); - builder.set_text_direction(accesskit::TextDirection::LeftToRight); - // TODO(mwcampbell): Set more node fields for the row - // once AccessKit adapters expose text formatting info. + if row.ends_with_newline { + value.push('\n'); + character_lengths.push(1); + character_positions.push(row.size.x); + character_widths.push(0.0); + } - let glyph_count = row.glyphs.len(); - let mut value = String::new(); - value.reserve(glyph_count); - let mut character_lengths = Vec::::with_capacity(glyph_count); - let mut character_positions = Vec::::with_capacity(glyph_count); - let mut character_widths = Vec::::with_capacity(glyph_count); - let mut word_lengths = Vec::::new(); - let mut was_at_word_end = false; - let mut last_word_start = 0usize; + let total_chars = character_lengths.len(); - for glyph in &row.glyphs { - let is_word_char = is_word_char(glyph.chr); - if is_word_char && was_at_word_end { - word_lengths.push((character_lengths.len() - last_word_start) as _); - last_word_start = character_lengths.len(); + if total_chars <= MAX_CHARS_PER_TEXT_RUN { + let run_id = parent_id.with(row_index).with(0usize); + ctx.register_accesskit_parent(run_id, parent_id); + + ctx.accesskit_node_builder(run_id, |builder| { + builder.set_role(accesskit::Role::TextRun); + builder.set_text_direction(accesskit::TextDirection::LeftToRight); + // TODO(mwcampbell): Set more node fields for the row + // once AccessKit adapters expose text formatting info. + + let rect = global_from_galley * row.rect_without_leading_space(); + builder.set_bounds(accesskit::Rect { + x0: rect.min.x.into(), + y0: rect.min.y.into(), + x1: rect.max.x.into(), + y1: rect.max.y.into(), + }); + builder.set_value(value); + builder.set_character_lengths(character_lengths); + + let pos_offset = character_positions.first().copied().unwrap_or(0.0); + for p in &mut character_positions { + *p -= pos_offset; } - was_at_word_end = !is_word_char; - let old_len = value.len(); - value.push(glyph.chr); - character_lengths.push((value.len() - old_len) as _); - character_positions.push(glyph.pos.x - row.pos.x); - character_widths.push(glyph.advance_width); - } + builder.set_character_positions(character_positions); + builder.set_character_widths(character_widths); - if row.ends_with_newline { - value.push('\n'); - character_lengths.push(1); - character_positions.push(row.size.x); - character_widths.push(0.0); - } - word_lengths.push((character_lengths.len() - last_word_start) as _); + let chunk_word_starts: Vec = word_starts.iter().map(|&ws| ws as u8).collect(); + builder.set_word_starts(chunk_word_starts); + }); + } else { + let num_chunks = total_chars.div_ceil(MAX_CHARS_PER_TEXT_RUN); + let mut byte_offset = 0usize; - builder.set_value(value); - builder.set_character_lengths(character_lengths); - builder.set_character_positions(character_positions); - builder.set_character_widths(character_widths); - builder.set_word_lengths(word_lengths); - }); + for chunk_idx in 0..num_chunks { + let char_start = chunk_idx * MAX_CHARS_PER_TEXT_RUN; + let char_end = (char_start + MAX_CHARS_PER_TEXT_RUN).min(total_chars); + + let byte_start = byte_offset; + let chunk_byte_len: usize = character_lengths[char_start..char_end] + .iter() + .map(|&l| l as usize) + .sum(); + let byte_end = byte_start + chunk_byte_len; + byte_offset = byte_end; + + let run_id = parent_id.with(row_index).with(chunk_idx); + ctx.register_accesskit_parent(run_id, parent_id); + + ctx.accesskit_node_builder(run_id, |builder| { + builder.set_role(accesskit::Role::TextRun); + builder.set_text_direction(accesskit::TextDirection::LeftToRight); + // TODO(mwcampbell): Set more node fields for the row + // once AccessKit adapters expose text formatting info. + + if chunk_idx > 0 { + let prev_id = parent_id.with(row_index).with(chunk_idx - 1); + builder.set_previous_on_line(prev_id.accesskit_id()); + } + if chunk_idx + 1 < num_chunks { + let next_id = parent_id.with(row_index).with(chunk_idx + 1); + builder.set_next_on_line(next_id.accesskit_id()); + } + + let row_rect = row.rect_without_leading_space(); + let chunk_x0 = row.pos.x + character_positions[char_start]; + let chunk_x1 = row.pos.x + + character_positions[char_end - 1] + + character_widths[char_end - 1]; + let chunk_rect = emath::Rect::from_min_max( + emath::pos2(chunk_x0, row_rect.min.y), + emath::pos2(chunk_x1, row_rect.max.y), + ); + let rect = global_from_galley * chunk_rect; + builder.set_bounds(accesskit::Rect { + x0: rect.min.x.into(), + y0: rect.min.y.into(), + x1: rect.max.x.into(), + y1: rect.max.y.into(), + }); + builder.set_value(value[byte_start..byte_end].to_owned()); + builder.set_character_lengths(character_lengths[char_start..char_end].to_vec()); + + let pos_offset = character_positions[char_start]; + let chunk_positions: Vec = character_positions[char_start..char_end] + .iter() + .map(|&p| p - pos_offset) + .collect(); + builder.set_character_positions(chunk_positions); + builder.set_character_widths(character_widths[char_start..char_end].to_vec()); + + let chunk_word_starts: Vec = word_starts + .iter() + .filter(|&&ws| ws >= char_start && ws < char_end) + .map(|&ws| (ws - char_start) as u8) + .collect(); + builder.set_word_starts(chunk_word_starts); + }); + } + } + + prev_row_ended_with_newline = row.ends_with_newline; } } diff --git a/crates/egui/src/text_selection/cursor_range.rs b/crates/egui/src/text_selection/cursor_range.rs index a816f5f26..f0445217f 100644 --- a/crates/egui/src/text_selection/cursor_range.rs +++ b/crates/egui/src/text_selection/cursor_range.rs @@ -192,10 +192,13 @@ impl CCursorRange { Event::AccessKitActionRequest(accesskit::ActionRequest { action: accesskit::Action::SetTextSelection, - target, + target_node, + target_tree, data: Some(accesskit::ActionData::SetTextSelection(selection)), }) => { - if _widget_id.accesskit_id() == *target { + if _widget_id.accesskit_id() == *target_node + && *target_tree == accesskit::TreeId::ROOT + { let primary = ccursor_from_accesskit_text_position(_widget_id, galley, &selection.focus); let secondary = @@ -224,18 +227,31 @@ fn ccursor_from_accesskit_text_position( galley: &Galley, position: &accesskit::TextPosition, ) -> Option { + use super::accesskit_text::MAX_CHARS_PER_TEXT_RUN; + let mut total_length = 0usize; for (i, row) in galley.rows.iter().enumerate() { - let row_id = id.with(i); - if row_id.accesskit_id() == position.node { - return Some(CCursor { - index: total_length + position.character_index, - prefer_next_row: !(position.character_index == row.glyphs.len() - && !row.ends_with_newline - && (i + 1) < galley.rows.len()), - }); + let row_chars = row.glyphs.len() + (row.ends_with_newline as usize); + let num_chunks = if row_chars == 0 { + 1 + } else { + row_chars.div_ceil(MAX_CHARS_PER_TEXT_RUN) + }; + + for chunk_idx in 0..num_chunks { + let run_id = id.with(i).with(chunk_idx); + if run_id.accesskit_id() == position.node { + let column = chunk_idx * MAX_CHARS_PER_TEXT_RUN + position.character_index; + return Some(CCursor { + index: total_length + column, + prefer_next_row: !(column == row.glyphs.len() + && !row.ends_with_newline + && (i + 1) < galley.rows.len()), + }); + } } - total_length += row.glyphs.len() + (row.ends_with_newline as usize); + + total_length += row_chars; } None } diff --git a/crates/egui/src/text_selection/visuals.rs b/crates/egui/src/text_selection/visuals.rs index fead390fe..e41d7a436 100644 --- a/crates/egui/src/text_selection/visuals.rs +++ b/crates/egui/src/text_selection/visuals.rs @@ -67,9 +67,7 @@ pub fn paint_text_selection( let first_vertex_index = row .glyphs .get(first_glyph_index) - .map_or(row.visuals.glyph_vertex_range.start, |g| { - g.first_vertex as _ - }); + .map_or(row.visuals.glyph_vertex_range.end, |g| g.first_vertex as _); let last_vertex_index = row .glyphs .get(last_glyph_index) diff --git a/crates/egui/src/ui_builder.rs b/crates/egui/src/ui_builder.rs index adbed444c..0618fb8cc 100644 --- a/crates/egui/src/ui_builder.rs +++ b/crates/egui/src/ui_builder.rs @@ -55,8 +55,8 @@ impl UiBuilder { /// /// This is a shortcut for `.id_salt(my_id).global_scope(true)`. #[inline] - pub fn id(mut self, id: impl Hash) -> Self { - self.id_salt = Some(Id::new(id)); + pub fn id(mut self, id: Id) -> Self { + self.id_salt = Some(id); self.global_scope = true; self } diff --git a/crates/egui/src/widget_text.rs b/crates/egui/src/widget_text.rs index 5d91f4bc1..8bc88344b 100644 --- a/crates/egui/src/widget_text.rs +++ b/crates/egui/src/widget_text.rs @@ -1,5 +1,5 @@ use emath::GuiRounding as _; -use epaint::text::TextFormat; +use epaint::text::{IntoTag, TextFormat, VariationCoords}; use std::fmt::Formatter; use std::{borrow::Cow, sync::Arc}; @@ -34,6 +34,7 @@ pub struct RichText { background_color: Color32, expand_bg: f32, text_color: Option, + coords: VariationCoords, code: bool, strong: bool, weak: bool, @@ -55,6 +56,7 @@ impl Default for RichText { background_color: Default::default(), expand_bg: 1.0, text_color: Default::default(), + coords: Default::default(), code: Default::default(), strong: Default::default(), weak: Default::default(), @@ -196,6 +198,23 @@ impl RichText { self } + /// Add a variation coordinate. + #[inline] + pub fn variation(mut self, tag: impl IntoTag, coord: f32) -> Self { + self.coords.push(tag, coord); + self + } + + /// Override the variation coordinates completely. + #[inline] + pub fn variations( + mut self, + variations: impl IntoIterator, + ) -> Self { + self.coords = VariationCoords::new(variations); + self + } + /// Override the [`TextStyle`]. #[inline] pub fn text_style(mut self, text_style: TextStyle) -> Self { @@ -391,6 +410,7 @@ impl RichText { background_color, expand_bg, text_color: _, // already used by `get_text_color` + coords, code, strong: _, // already used by `get_text_color` weak: _, // already used by `get_text_color` @@ -449,6 +469,7 @@ impl RichText { line_height, color: text_color, background: background_color, + coords, italics, underline, strikethrough, diff --git a/crates/egui/src/widgets/button.rs b/crates/egui/src/widgets/button.rs index c26ae3096..107bcd82c 100644 --- a/crates/egui/src/widgets/button.rs +++ b/crates/egui/src/widgets/button.rs @@ -261,6 +261,13 @@ impl<'a> Button<'a> { self } + /// Set the gap between atoms. + #[inline] + pub fn gap(mut self, gap: f32) -> Self { + self.layout = self.layout.gap(gap); + self + } + /// Show the button and return a [`AtomLayoutResponse`] for painting custom contents. pub fn atom_ui(self, ui: &mut Ui) -> AtomLayoutResponse { let Button { diff --git a/crates/egui/src/widgets/drag_value.rs b/crates/egui/src/widgets/drag_value.rs index 7841fee61..1297b614b 100644 --- a/crates/egui/src/widgets/drag_value.rs +++ b/crates/egui/src/widgets/drag_value.rs @@ -1,11 +1,10 @@ -#![expect(clippy::needless_pass_by_value)] // False positives with `impl ToString` - -use std::{cmp::Ordering, ops::RangeInclusive}; - use crate::{ - Button, CursorIcon, Id, Key, MINUS_CHAR_STR, Modifiers, NumExt as _, Response, RichText, Sense, - TextEdit, TextWrapMode, Ui, Widget, WidgetInfo, emath, text, + Atom, AtomExt as _, AtomKind, Atoms, Button, CursorIcon, Id, IntoAtoms, Key, MINUS_CHAR_STR, + Modifiers, NumExt as _, Response, RichText, Sense, TextEdit, TextWrapMode, Ui, Widget, + WidgetInfo, emath, text, }; +use emath::Vec2; +use std::{cmp::Ordering, ops::RangeInclusive}; // ---------------------------------------------------------------------------- @@ -38,8 +37,7 @@ fn set(get_set_value: &mut GetSetValue<'_>, value: f64) { pub struct DragValue<'a> { get_set_value: GetSetValue<'a>, speed: f64, - prefix: String, - suffix: String, + atoms: Atoms<'a>, range: RangeInclusive, clamp_existing_to_range: bool, min_decimals: usize, @@ -50,6 +48,8 @@ pub struct DragValue<'a> { } impl<'a> DragValue<'a> { + const ATOM_ID: &'static str = "drag_item"; + pub fn new(value: &'a mut Num) -> Self { let slf = Self::from_get_set(move |v: Option| { if let Some(v) = v { @@ -66,11 +66,12 @@ impl<'a> DragValue<'a> { } pub fn from_get_set(get_set_value: impl 'a + FnMut(Option) -> f64) -> Self { + let atoms = Atoms::new(Atom::custom(Id::new(Self::ATOM_ID), Vec2::ZERO).atom_grow(true)); + Self { get_set_value: Box::new(get_set_value), speed: 1.0, - prefix: Default::default(), - suffix: Default::default(), + atoms, range: f64::NEG_INFINITY..=f64::INFINITY, clamp_existing_to_range: true, min_decimals: 0, @@ -164,15 +165,15 @@ impl<'a> DragValue<'a> { /// Show a prefix before the number, e.g. "x: " #[inline] - pub fn prefix(mut self, prefix: impl ToString) -> Self { - self.prefix = prefix.to_string(); + pub fn prefix(mut self, prefix: impl IntoAtoms<'a>) -> Self { + self.atoms.extend_left(prefix.into_atoms()); self } /// Add a suffix to the number, this can be e.g. a unit ("°" or " m") #[inline] - pub fn suffix(mut self, suffix: impl ToString) -> Self { - self.suffix = suffix.to_string(); + pub fn suffix(mut self, suffix: impl IntoAtoms<'a>) -> Self { + self.atoms.extend_right(suffix.into_atoms()); self } @@ -433,8 +434,7 @@ impl Widget for DragValue<'_> { speed, range, clamp_existing_to_range, - prefix, - suffix, + mut atoms, min_decimals, max_decimals, custom_formatter, @@ -442,6 +442,23 @@ impl Widget for DragValue<'_> { update_while_editing, } = self; + let mut prefix_text = String::new(); + let mut suffix_text = String::new(); + let mut past_value = false; + let atom_id = Id::new(Self::ATOM_ID); + for atom in atoms.iter() { + if atom.id == Some(atom_id) { + past_value = true; + } + if let AtomKind::Text(text) = &atom.kind { + if past_value { + suffix_text.push_str(text.text()); + } else { + prefix_text.push_str(text.text()); + } + } + } + let shift = ui.input(|i| i.modifiers.shift_only()); // The widget has the same ID whether it's in edit or button mode. let id = ui.next_auto_id(); @@ -543,8 +560,6 @@ impl Widget for DragValue<'_> { } } - // some clones below are redundant if AccessKit is disabled - #[expect(clippy::redundant_clone)] let mut response = if is_kb_editing { let mut value_text = ui .data_mut(|data| data.remove_temp::(id)) @@ -586,13 +601,20 @@ impl Widget for DragValue<'_> { ui.data_mut(|data| data.insert_temp(id, value_text)); response } else { - let button = Button::new( - RichText::new(format!("{}{}{}", prefix, value_text.clone(), suffix)) - .text_style(text_style), - ) - .wrap_mode(TextWrapMode::Extend) - .sense(Sense::click_and_drag()) - .min_size(ui.spacing().interact_size); // TODO(emilk): find some more generic solution to `min_size` + atoms.map_atoms(|atom| { + if atom.id == Some(atom_id) { + RichText::new(value_text.clone()) + .text_style(text_style.clone()) + .into() + } else { + atom + } + }); + let button = Button::new(atoms) + .wrap_mode(TextWrapMode::Extend) + .sense(Sense::click_and_drag()) + .gap(0.0) + .min_size(ui.spacing().interact_size); // TODO(emilk): find some more generic solution to `min_size` let cursor_icon = if value <= *range.start() { CursorIcon::ResizeEast @@ -607,10 +629,8 @@ impl Widget for DragValue<'_> { if ui.style().explanation_tooltips { response = response.on_hover_text(format!( - "{}{}{}\nDrag to edit or click to enter a value.\nPress 'Shift' while dragging for better control.", - prefix, + "{}\nDrag to edit or click to enter a value.\nPress 'Shift' while dragging for better control.", value as f32, // Show full precision value on-hover. TODO(emilk): figure out f64 vs f32 - suffix )); } @@ -704,7 +724,7 @@ impl Widget for DragValue<'_> { // The value is exposed as a string by the text edit widget // when in edit mode. if !is_kb_editing { - let value_text = format!("{prefix}{value_text}{suffix}"); + let value_text = format!("{prefix_text}{value_text}{suffix_text}"); builder.set_value(value_text); } }); diff --git a/crates/egui/src/widgets/text_edit/builder.rs b/crates/egui/src/widgets/text_edit/builder.rs index fbf25babf..b9fdb1cbe 100644 --- a/crates/egui/src/widgets/text_edit/builder.rs +++ b/crates/egui/src/widgets/text_edit/builder.rs @@ -1066,26 +1066,36 @@ fn events( } => check_for_mutating_key_press(os, &cursor_range, text, galley, modifiers, *key), Event::Ime(ime_event) => { - /// Empty prediction can be produced with [`ImeEvent::Preedit`] - /// or [`ImeEvent::Commit`] when user press backspace or escape - /// during IME, so this function should be called in both cases - /// to clear current text. + /// Both `ImeEvent::Preedit("")` and `ImeEvent::Commit("")` + /// might be emitted from different integrations to signify that + /// the current IME composition should be cleared. /// - /// Example platforms where only `ImeEvent::Preedit("")` of - /// those two events is emitted when the last character in the - /// prediction is deleted: - /// - macOS 15.7.3. - /// - Debian13 with gnome48 and wayland. + /// Example integrations where only `ImeEvent::Preedit("")` of + /// those two events is emitted when the last character is + /// deleted with a backspace: + /// - `egui-winit` on macOS 15.7.3. + /// - `egui-winit` on Debian13 with gnome48 and wayland. /// - /// An example platform where only `ImeEvent::Commit("")` of - /// those two events is emitted when the last character in the - /// prediction is deleted: - /// - Safari 26.2 (on macOS 15.7.3). - fn clear_prediction( + /// An example integration where only `ImeEvent::Commit("")` of + /// those two events is emitted when the last character is + /// deleted with a backspace: + /// - `eframe`'s web integration on Safari 26.2 (on macOS + /// 15.7.3). + /// + /// ## Note + /// + /// The term “pre-edit string” is used by X11 and Wayland, and + /// we use “pre-edit text” and “pre-edit range” here in the + /// same manner. + /// See: + /// + /// We previously referred to “pre-edit text” as “prediction”, + /// which is not standard and can mean different things. + fn clear_preedit_text( text: &mut dyn TextBuffer, - cursor_range: &CCursorRange, + preedit_range: &CCursorRange, ) -> CCursor { - text.delete_selected(cursor_range) + text.delete_selected(preedit_range) } match ime_event { @@ -1094,33 +1104,33 @@ fn events( state.ime_cursor_range = cursor_range; None } - ImeEvent::Preedit(text_mark) => { - if text_mark == "\n" || text_mark == "\r" { + ImeEvent::Preedit(preedit_text) => { + if preedit_text == "\n" || preedit_text == "\r" { None } else { - let mut ccursor = clear_prediction(text, &cursor_range); + let mut ccursor = clear_preedit_text(text, &cursor_range); let start_cursor = ccursor; - if !text_mark.is_empty() { - text.insert_text_at(&mut ccursor, text_mark, char_limit); + if !preedit_text.is_empty() { + text.insert_text_at(&mut ccursor, preedit_text, char_limit); } state.ime_cursor_range = cursor_range; Some(CCursorRange::two(start_cursor, ccursor)) } } - ImeEvent::Commit(prediction) => { - if prediction == "\n" || prediction == "\r" { + ImeEvent::Commit(commit_text) => { + if commit_text == "\n" || commit_text == "\r" { None } else { state.ime_enabled = false; - let mut ccursor = clear_prediction(text, &cursor_range); + let mut ccursor = clear_preedit_text(text, &cursor_range); - if !prediction.is_empty() + if !commit_text.is_empty() && cursor_range.secondary.index == state.ime_cursor_range.secondary.index { - text.insert_text_at(&mut ccursor, prediction, char_limit); + text.insert_text_at(&mut ccursor, commit_text, char_limit); } Some(CCursorRange::one(ccursor)) diff --git a/crates/egui_demo_app/src/accessibility_inspector.rs b/crates/egui_demo_app/src/accessibility_inspector.rs index 138c05ede..db34c85ac 100644 --- a/crates/egui_demo_app/src/accessibility_inspector.rs +++ b/crates/egui_demo_app/src/accessibility_inspector.rs @@ -1,7 +1,7 @@ use std::mem; -use accesskit::{Action, ActionRequest, NodeId}; -use accesskit_consumer::{FilterResult, Node, Tree, TreeChangeHandler}; +use accesskit::{Action, ActionRequest}; +use accesskit_consumer::{FilterResult, Node, NodeId, Tree, TreeChangeHandler}; use eframe::epaint::text::TextWrapMode; use egui::{ @@ -25,7 +25,7 @@ use egui::{ pub struct AccessibilityInspectorPlugin { pub open: bool, tree: Option, - selected_node: Option, + selected_node: Option, queued_action: Option, } @@ -113,13 +113,17 @@ impl AccessibilityInspectorPlugin { Id::new("Accessibility Inspector") } - fn selection_ui(&mut self, ui: &mut Ui, selected_node: Id) { + fn selection_ui(&mut self, ui: &mut Ui, selected_node: NodeId) { ui.separator(); if let Some(tree) = &self.tree - && let Some(node) = tree.state().node_by_id(NodeId::from(selected_node.value())) + && let Some(node) = tree.state().node_by_id(selected_node) { - let node_response = ui.ctx().read_response(selected_node); + // Safety: This is safe since the `accesskit::NodeId` was created from an `egui::Id`. + #[expect(unsafe_code)] + let egui_node_id = unsafe { Id::from_high_entropy_bits(node.locate().0.0) }; + + let node_response = ui.ctx().read_response(egui_node_id); if let Some(widget_response) = node_response { ui.debug_painter().debug_rect( @@ -174,8 +178,10 @@ impl AccessibilityInspectorPlugin { if node.supports_action(action, &|_node| FilterResult::Include) && ui.button(format!("{action:?}")).clicked() { + let (target_node, target_tree) = node.locate(); let action_request = ActionRequest { - target: node.id(), + target_node, + target_tree, action, data: None, }; @@ -188,8 +194,8 @@ impl AccessibilityInspectorPlugin { } } - fn node_ui(ui: &mut Ui, node: &Node<'_>, selected_node: &mut Option) { - if node.id() == Self::id().value().into() + fn node_ui(ui: &mut Ui, node: &Node<'_>, selected_node: &mut Option) { + if node.locate() == (Self::id().value().into(), accesskit::TreeId::ROOT) || node .value() .as_deref() @@ -200,12 +206,12 @@ impl AccessibilityInspectorPlugin { let label = node .label() .or_else(|| node.value()) - .unwrap_or_else(|| node.id().0.to_string()); + .unwrap_or_else(|| node.locate().0.0.to_string()); let label = format!("({:?}) {}", node.role(), label); // Safety: This is safe since the `accesskit::NodeId` was created from an `egui::Id`. #[expect(unsafe_code)] - let egui_node_id = unsafe { Id::from_high_entropy_bits(node.id().0) }; + let egui_node_id = unsafe { Id::from_high_entropy_bits(node.locate().0.0) }; ui.push_id(node.id(), |ui| { let child_count = node.children().len(); @@ -228,7 +234,7 @@ impl AccessibilityInspectorPlugin { collapsing.set_open(!collapsing.is_open()); } let label_response = - ui.selectable_value(selected_node, Some(egui_node_id), label.clone()); + ui.selectable_value(selected_node, Some(node.id()), label.clone()); if label_response.hovered() { let widget_response = ui.ctx().read_response(egui_node_id); diff --git a/crates/egui_demo_app/src/apps/custom3d_wgpu.rs b/crates/egui_demo_app/src/apps/custom3d_wgpu.rs index c88f7638c..fd1d9ae73 100644 --- a/crates/egui_demo_app/src/apps/custom3d_wgpu.rs +++ b/crates/egui_demo_app/src/apps/custom3d_wgpu.rs @@ -41,7 +41,7 @@ impl Custom3d { let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("custom3d"), bind_group_layouts: &[&bind_group_layout], - push_constant_ranges: &[], + immediate_size: 0, }); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { @@ -62,7 +62,7 @@ impl Custom3d { primitive: wgpu::PrimitiveState::default(), depth_stencil: None, multisample: wgpu::MultisampleState::default(), - multiview: None, + multiview_mask: None, cache: None, }); diff --git a/crates/egui_demo_app/src/backend_panel.rs b/crates/egui_demo_app/src/backend_panel.rs index d3953320a..dfc4d116b 100644 --- a/crates/egui_demo_app/src/backend_panel.rs +++ b/crates/egui_demo_app/src/backend_panel.rs @@ -219,6 +219,10 @@ fn integration_ui(ui: &mut egui::Ui, _frame: &mut eframe::Frame) { driver, driver_info, backend, + device_pci_bus_id, + subgroup_min_size, + subgroup_max_size, + transient_saves_memory, } = &info; // Example values: @@ -261,6 +265,19 @@ fn integration_ui(ui: &mut egui::Ui, _frame: &mut eframe::Frame) { ui.label(format!("0x{device:02X}")); ui.end_row(); } + if !device_pci_bus_id.is_empty() { + ui.label("PCI Bus ID:"); + ui.label(device_pci_bus_id.as_str()); + ui.end_row(); + } + if *subgroup_min_size != 0 || *subgroup_max_size != 0 { + ui.label("Subgroup size:"); + ui.label(format!("{subgroup_min_size}..={subgroup_max_size}")); + ui.end_row(); + } + ui.label("Transient saves memory:"); + ui.label(format!("{transient_saves_memory}")); + ui.end_row(); }); }; diff --git a/crates/egui_demo_app/tests/snapshots/clock.png b/crates/egui_demo_app/tests/snapshots/clock.png index c178d21a9..da8425458 100644 --- a/crates/egui_demo_app/tests/snapshots/clock.png +++ b/crates/egui_demo_app/tests/snapshots/clock.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:72c403249fb7e91f225992044153ce500d3971f595fb8ec3a8fb78ecc0e998c7 -size 335365 +oid sha256:80c64fe75dfc412bb33aa977055afcb140e6bfff19128ecf5bd35318ab6773d0 +size 335380 diff --git a/crates/egui_demo_app/tests/snapshots/custom3d.png b/crates/egui_demo_app/tests/snapshots/custom3d.png index 41ace3480..20a933d45 100644 --- a/crates/egui_demo_app/tests/snapshots/custom3d.png +++ b/crates/egui_demo_app/tests/snapshots/custom3d.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:706ad012e52a8c51175b050b985cca88e2cb306b24f618b7391641397d17cd28 -size 92804 +oid sha256:cc55083688d043234c37d22c74635a44b00f4d28c3802c4327c2eaf563c73eed +size 92800 diff --git a/crates/egui_demo_app/tests/snapshots/easymarkeditor.png b/crates/egui_demo_app/tests/snapshots/easymarkeditor.png index b11978ce9..7b370c38e 100644 --- a/crates/egui_demo_app/tests/snapshots/easymarkeditor.png +++ b/crates/egui_demo_app/tests/snapshots/easymarkeditor.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4135662f2b60a10ef8c3b155172d7a3edcf24a625d8286aeaad0614aa8819893 -size 169604 +oid sha256:78d91fa4657cd1cb375487f606b80d418ed6fdbd8a0c0225b9383eead5001563 +size 169682 diff --git a/crates/egui_demo_app/tests/snapshots/imageviewer.png b/crates/egui_demo_app/tests/snapshots/imageviewer.png index 03aa3077a..8d5f688bf 100644 --- a/crates/egui_demo_app/tests/snapshots/imageviewer.png +++ b/crates/egui_demo_app/tests/snapshots/imageviewer.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:509020d8885b718900e534c9948cb95ae88e1eee9e113bdfb77a2f75b9a68f7b -size 96703 +oid sha256:c8b23a5286e5d2dbd8d3eddac6583d981152bd791f74edfa5c712a610f795256 +size 96759 diff --git a/crates/egui_demo_lib/src/demo/text_edit.rs b/crates/egui_demo_lib/src/demo/text_edit.rs index 4ac981807..3ec53a523 100644 --- a/crates/egui_demo_lib/src/demo/text_edit.rs +++ b/crates/egui_demo_lib/src/demo/text_edit.rs @@ -66,7 +66,8 @@ impl crate::View for TextEditDemo { egui::Label::new("Press ctrl+Y to toggle the case of selected text (cmd+Y on Mac)"), ); - if ui.input_mut(|i| i.consume_key(egui::Modifiers::COMMAND, egui::Key::Y)) + if output.response.has_focus() + && ui.input_mut(|i| i.consume_key(egui::Modifiers::COMMAND, egui::Key::Y)) && let Some(text_cursor_range) = output.cursor_range { use egui::TextBuffer as _; diff --git a/crates/egui_demo_lib/tests/misc.rs b/crates/egui_demo_lib/tests/misc.rs index 8abc69d19..d5f6a3a3c 100644 --- a/crates/egui_demo_lib/tests/misc.rs +++ b/crates/egui_demo_lib/tests/misc.rs @@ -59,21 +59,27 @@ fn test_italics() { #[test] fn test_text_selection() { - let mut harness = Harness::builder().build_ui(|ui| { - let visuals = ui.visuals_mut(); - visuals.selection.bg_fill = Color32::LIGHT_GREEN; - visuals.selection.stroke.color = Color32::DARK_BLUE; + let mut results = egui_kittest::SnapshotResults::new(); - ui.label("Some varied ☺ text :)\nAnd it has a second line!"); - }); - harness.run(); - harness.fit_contents(); + for (test_idx, drag_start_x) in [0.2_f32, 0.9].into_iter().enumerate() { + let mut harness = Harness::builder().build_ui(|ui| { + let visuals = ui.visuals_mut(); + visuals.selection.bg_fill = Color32::LIGHT_GREEN; + visuals.selection.stroke.color = Color32::RED; - // Drag to select text: - let label = harness.get_by_role(Role::Label); - harness.drag_at(label.rect().lerp_inside([0.2, 0.25])); - harness.drop_at(label.rect().lerp_inside([0.6, 0.75])); - harness.run(); + ui.label("Some varied ☺ text :)\nAnd it has a second line!"); + }); + harness.run(); + harness.fit_contents(); - harness.snapshot("text_selection"); + // Drag to select text: + let label = harness.get_by_role(Role::Label); + harness.drag_at(label.rect().lerp_inside([drag_start_x, 0.25])); + harness.drop_at(label.rect().lerp_inside([0.6, 0.75])); + harness.run(); + + harness.snapshot(format!("text_selection_{test_idx}")); + + results.extend_harness(&mut harness); + } } diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Clipboard Test.png b/crates/egui_demo_lib/tests/snapshots/demos/Clipboard Test.png index be63a88ea..2549417be 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Clipboard Test.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Clipboard Test.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:52d2233594c4bad348f5479dcfad9576ee5fd7d49faedb6f5ba74b374cdaf3ad +oid sha256:a53262cf5d8507d8eeae8c968767cef462b727879245085673982b850a6da670 size 26977 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png b/crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png index 91200ed0f..f4b7690fc 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:47f6cd15b88df83a9b2d8538e424041e661712f2e85312166a581f69f1254643 -size 26839 +oid sha256:75a9cd9a3315b236c23a53e890de1a821d39c3327813d06df85ba86d2ed50cc7 +size 26887 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png b/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png index 5d4cc4d49..46a5ed1f7 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9302478abb0b86fae1af3af45d91f032272a56a2098405525d08aba4f9534644 -size 76103 +oid sha256:a7601584308bf60820506f842569a3c1daf3c15fa6e715f6b9386b5112dcc92f +size 76076 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Cursor Test.png b/crates/egui_demo_lib/tests/snapshots/demos/Cursor Test.png index dda0e964c..d4c9508f9 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Cursor Test.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Cursor Test.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6927950ccbc5c81d6fbfe0a90ddd79a4306518caced14bb60debd30c7e41d326 +oid sha256:d4e33c7f817100d8414bba245ee7886354b86109f383d59e87a197e39501f0a0 size 62604 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Dancing Strings.png b/crates/egui_demo_lib/tests/snapshots/demos/Dancing Strings.png index f0b9c3892..40153611c 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Dancing Strings.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Dancing Strings.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:366d18457aabf1ebdd42fdbce8819cc67a4f59db85c452623b02ee1d0e8fc50a -size 27817 +oid sha256:93fcc271831167cb077f3de0a9f0e27037f9e5a2ce94e056bd6f1ede9890cb7e +size 27818 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Font Book.png b/crates/egui_demo_lib/tests/snapshots/demos/Font Book.png index 281154b2d..375b0f922 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Font Book.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Font Book.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c5e803659e936268b476690427ef6a6802f477e078dc956a9d1c857b48da868 -size 114409 +oid sha256:3fc2793506ec483c7f124b6206fb18ffb73bec29746f2d9bb5145042ddc45016 +size 114410 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Frame.png b/crates/egui_demo_lib/tests/snapshots/demos/Frame.png index 6efc3f4a9..b8791ed2e 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Frame.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Frame.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5e67baf0696792e50f7ab3121874d055ddee2de0514712aacbf8e135ec4743d -size 25425 +oid sha256:20ea4f93ee50c7a3585aef74c66d7700083ac1c16519b0704b70387849d9d2bc +size 25057 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Grid Test.png b/crates/egui_demo_lib/tests/snapshots/demos/Grid Test.png index c6f5ae26c..b8f0440b6 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Grid Test.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Grid Test.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:55c5fb90736a31fbccd72be5994fc8c62b4b9da9842ad1e6bb795a1e1461a6f8 -size 98780 +oid sha256:1b72a4c0e6d441190a7a156b8bba709e81b6c1fe7b0eacedc1ee7a3bfcf881f6 +size 99297 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png b/crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png index a61363b99..a965901d5 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4080ee1a16eea16c8f4246fe3e760ade7d0289b30d88068d1e49ffb88d88dca -size 18280 +oid sha256:08c40934d4bd2a239bdcc1928d1e5eba56bac03fdded2c85cf47b020d669f07e +size 18281 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/ID Test.png b/crates/egui_demo_lib/tests/snapshots/demos/ID Test.png index c750513c0..abd7c485b 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/ID Test.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/ID Test.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:600d9e0fc193396f36b599e4bfad2547128160d2e56dc2a989cb5f978d5115ae -size 113797 +oid sha256:82878e4150e38fdc4b2e78203c8c661c2d9e716ab32595c298392faf6ba96105 +size 113803 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Interactive Container.png b/crates/egui_demo_lib/tests/snapshots/demos/Interactive Container.png index b57b98a53..d01e4bb7b 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Interactive Container.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Interactive Container.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ae5e843cc9d847b0f3c4092f55b914699adb506cb807b0a97bfc4ec7d94537b -size 22613 +oid sha256:58cd3aba4392332a45f57c7dd90a9b5da386cb396c0c6319e7a7dae71e03ff30 +size 22563 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Layout Test.png b/crates/egui_demo_lib/tests/snapshots/demos/Layout Test.png index 6a2c9cf63..0f50709d3 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Layout Test.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Layout Test.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b0d38cb1eebf3ce7d661d094175b425db2b9eccc5e439b14256c5d801d4454d4 -size 47285 +oid sha256:26ffcf6b71108b82ce15d4cf3f9dd0ce9fe0b9563f02725fef1b74f40e749439 +size 47281 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Manual Layout Test.png b/crates/egui_demo_lib/tests/snapshots/demos/Manual Layout Test.png index e6fd0795d..1ba4655fd 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Manual Layout Test.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Manual Layout Test.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:00e4c7659cd50044d473dd2c138392f78ac7eba27f2b52bae61246f5dc5b2782 -size 23156 +oid sha256:faedf9631149e231d510165215c24fccec50502d58000d5f893aa047a637a68f +size 23148 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png b/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png index a12313e97..f34ac0cd4 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:604f716e687fc26abba92769fe2dae75d850b18598d2e8a9524451ab0f760251 -size 65403 +oid sha256:b6b4c2e55c02fa4caf5f9f8bd2d8c0311cc4cbcf1fc2f568fe112e8e6125c675 +size 65308 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Modals.png b/crates/egui_demo_lib/tests/snapshots/demos/Modals.png index dcf935287..28fe7b683 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Modals.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Modals.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:34de6fd788288174e8e6f1fa48cd49dbc7b14fcf649fe302aed49c8c50178aa8 +oid sha256:3a65927cd8bd8d24e3ffbea8eb421eb22849b27dc77d36f8acd82bf5d5e63959 size 33469 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Painting.png b/crates/egui_demo_lib/tests/snapshots/demos/Painting.png index bbfeed26f..da06d85bf 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Painting.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Painting.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:96760220222bdde8dd1b3d28f089af2892403b78df8d34d3d94dc1a604387083 -size 18241 +oid sha256:9c595ee9b7ada33780178a6a35e26a98055a707f2ff99f6bb36e8db4ed819791 +size 18242 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Popups.png b/crates/egui_demo_lib/tests/snapshots/demos/Popups.png index 32beeba37..0aab4baf8 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Popups.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Popups.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:48d138634343edb251435bf6f9075502b913e806e8b280f3e6012977c13af16f -size 56753 +oid sha256:c218115d305dfa6c9ab883ac6f3a21584b4840b3ba273ea765c8a8381d78935f +size 57181 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Scene.png b/crates/egui_demo_lib/tests/snapshots/demos/Scene.png index 8dae8e626..277f7ab2c 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Scene.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Scene.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:23efb79ca13367f4d8886142d015815c5bdf99c0ed243ece294a7cfd365fd166 -size 33503 +oid sha256:4d10b78f4d80d61a3352d7f2b0ed9b2d93af5f184f2487f6f2afff02a38f4608 +size 33475 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png b/crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png index dc7dda582..5517e03ef 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d85faf6e7fa26741eb720e74695f3c207ea15097b118c3cafe5d52d5d85ea20 -size 23666 +oid sha256:f2ce9062c5d1f0b0861d5df49ae64e56ba0e6501e8bd3f8a92c53aea748be78b +size 23629 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png b/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png index b9f2816a2..24e42cb23 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:875eb687f3a1eed52a6617e532edc5332b0a16296e2b6addac66d5bea0448b14 -size 172605 +oid sha256:b5b965a7c690fd8e8646812513e2417170b687fd37e29d220c29127ba0cc200c +size 172609 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Sliders.png b/crates/egui_demo_lib/tests/snapshots/demos/Sliders.png index 3513155f0..28864a446 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Sliders.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Sliders.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd3573be9ba5818b4edc371095f5c23b084e6c7eaae4f2fd3a6d2de051878c9d -size 118567 +oid sha256:6ffba8bb50b42e47f855f62682f6d5ec10bf67b01d3aa2e843f6bf787f150d0d +size 118562 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Table.png b/crates/egui_demo_lib/tests/snapshots/demos/Table.png index c0470071c..3f72922d2 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Table.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Table.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ed1be0294fb65b11c54c6dc9e4cecb383ace16dad748e3c42f2ed65b2fb05ea8 -size 75509 +oid sha256:931f38ade8373ff79801c05c5d4397f2c5fcfa27022f2e1abe9eb29d561a3aef +size 76022 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Tessellation Test.png b/crates/egui_demo_lib/tests/snapshots/demos/Tessellation Test.png index 99ab541d4..c1b6f506a 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Tessellation Test.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Tessellation Test.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7336c53885add09360df098b6b131323e8ad3ef0ec2b85bf022e78bc4269276a -size 70255 +oid sha256:57bf5220ae8f47485a07e9117abaaad36924d8c6c0f9e278cb05c455f342bff6 +size 70250 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png b/crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png index c86b223dd..e9b302746 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:411dd61eb182a70d46c7fc1fa0f9a4b8aeae88d08b11d5af948c5acccfa9d133 -size 60950 +oid sha256:7c964d07a39ad286a562b53cdfe514d568d91955e6c1ca06a0cb5e45dbe3977e +size 60947 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png b/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png index c63f844ec..843bb93b4 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:78111e33d44a09beb9c1233dd2d5ef10103213a1c1c7df8b5e258d9684f1d93a -size 21810 +oid sha256:718203d31d8b027a7718a66c4712cf1e17b9aea2e870d755bd2c0c346529d4f4 +size 21814 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png b/crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png index 91aeb6b91..81204a347 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e9498a706de403ee7db3603ecc896688e584fede367ed6087cdf10b798a3ab2d +oid sha256:6af5adc42544171c6d85e190c853aca06784c131a373a693a6f7069d4cf1a404 size 13698 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Window Options.png b/crates/egui_demo_lib/tests/snapshots/demos/Window Options.png index c768c6507..78446cca9 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Window Options.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Window Options.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:42385da2eb74d54ba086aed973ade15f2a8d2be0c9281c05e6fb88846137bf81 -size 35870 +oid sha256:2e8e03c2a42e195e6489659053aecb78755d3c218558cb2e9339fa7b6db59405 +size 35875 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Window Resize Test.png b/crates/egui_demo_lib/tests/snapshots/demos/Window Resize Test.png index e8b44a484..a6d103d6d 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Window Resize Test.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Window Resize Test.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9f1b6fa0c48479606539f2d98befe1c9ee881846c0b55d7a53313962d556380d -size 484629 +oid sha256:ad22ea6b6e69fd71416fdae76cbd142d279f8f562e74b77e63b3989be187c57c +size 484631 diff --git a/crates/egui_demo_lib/tests/snapshots/image_kerning/image_dark_x1.png b/crates/egui_demo_lib/tests/snapshots/image_kerning/image_dark_x1.png index aeaa46a34..5cc884a55 100644 --- a/crates/egui_demo_lib/tests/snapshots/image_kerning/image_dark_x1.png +++ b/crates/egui_demo_lib/tests/snapshots/image_kerning/image_dark_x1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:89074b8dab103a419bc3dac743da4d8c47f435fa55b98d8aab71f6c9fb4d39de -size 12370 +oid sha256:c8ea98c65376d9f6ac66d0a9471c4bf3add0904294e7ca1a105458b90654a2e2 +size 12476 diff --git a/crates/egui_demo_lib/tests/snapshots/image_kerning/image_light_x1.png b/crates/egui_demo_lib/tests/snapshots/image_kerning/image_light_x1.png index da72002a0..b223bbb3d 100644 --- a/crates/egui_demo_lib/tests/snapshots/image_kerning/image_light_x1.png +++ b/crates/egui_demo_lib/tests/snapshots/image_kerning/image_light_x1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7bd7b54ff60859e4d4793000bef3adbec4c071063bec6bfdbde62516c4fc3478 -size 12959 +oid sha256:3793a5e83ef9bdffef99bcd8905a094acb69cde356e3a7125a544045296c3926 +size 13070 diff --git a/crates/egui_demo_lib/tests/snapshots/modals_1.png b/crates/egui_demo_lib/tests/snapshots/modals_1.png index 6690a129e..96d31e11d 100644 --- a/crates/egui_demo_lib/tests/snapshots/modals_1.png +++ b/crates/egui_demo_lib/tests/snapshots/modals_1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b53b03212953e12915a0e41bff5f0cdea90f8f866220a01142edaeb915735a34 -size 47077 +oid sha256:941582e2e20a9459db1f2cb7f07fa1930acfdb12cbbe7f96f9aafbeabf8b37f6 +size 47076 diff --git a/crates/egui_demo_lib/tests/snapshots/modals_2.png b/crates/egui_demo_lib/tests/snapshots/modals_2.png index f8eb85d39..3c0a88ee0 100644 --- a/crates/egui_demo_lib/tests/snapshots/modals_2.png +++ b/crates/egui_demo_lib/tests/snapshots/modals_2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:22e5d61a141b5a8663feb8a47371f9259d2a77fdacb1245bce411ffc85ce2cae -size 47716 +oid sha256:2735a021f171f5c95888cda76e8668e1e023588c8c6c7cd382c03d8e31988fe3 +size 48209 diff --git a/crates/egui_demo_lib/tests/snapshots/modals_3.png b/crates/egui_demo_lib/tests/snapshots/modals_3.png index c12f0b9df..28221255f 100644 --- a/crates/egui_demo_lib/tests/snapshots/modals_3.png +++ b/crates/egui_demo_lib/tests/snapshots/modals_3.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:120558ab0c267650744bd078aeace8d4122b3569c5998602f969766131d15c44 -size 43894 +oid sha256:867bef6b55b73d127306a461e115b6f0047d582904999de80aeabae00e60c967 +size 44295 diff --git a/crates/egui_demo_lib/tests/snapshots/modals_backdrop_should_prevent_focusing_lower_area.png b/crates/egui_demo_lib/tests/snapshots/modals_backdrop_should_prevent_focusing_lower_area.png index ef4c45ff1..bb1935741 100644 --- a/crates/egui_demo_lib/tests/snapshots/modals_backdrop_should_prevent_focusing_lower_area.png +++ b/crates/egui_demo_lib/tests/snapshots/modals_backdrop_should_prevent_focusing_lower_area.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af92548b6c8569081a91cb772b73988d9cb342498ddf9c0c86b6963cef8eda9e -size 43985 +oid sha256:936ec8b223ae7f0f32c640c127e1b6b14033bb7d168a4d1f0e6b3bd08a761e36 +size 44055 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.00.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.00.png index b5367f0ed..b48827b6a 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.00.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.00.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db510af76578693c85ce78ca91224758a56f7bbf33db3221c9a4edca08b06600 -size 590547 +oid sha256:fba7387f5deba5e144e2106154b15ab956a50a418857bd34e16b306d7f1a29e4 +size 588252 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.25.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.25.png index 7158a3545..d1286d6a1 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.25.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.25.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cae2b789e8afff23b7545d42a530e6c972d28736bad2bdacbc69f0e7065f85cc -size 740660 +oid sha256:4656f3255d7859c07b269ff655eafe21bdddb949a07aa91477b826f6e2af8c28 +size 740616 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.50.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.50.png index 8b9cb281f..f1892bf26 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.50.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:09d9f567ec371d60881b525ddb462d9135552db97af5921a6eb02aba40e40616 -size 971544 +oid sha256:b18ff644ba5bd0c7f094bf8eac079d8a72bc6918638b1b110002f2f0a7a362cc +size 967860 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.67.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.67.png index 9f5a69154..c32762306 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.67.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.67.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c383dd89fda6094704027074a72085591339a276d60502626d78e8e527b2e10 -size 1076719 +oid sha256:134caff5b8a4969055c32e8f51ca9c6eae1528b84d348691d860913e839de0d9 +size 1076746 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.75.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.75.png index 74760261a..d021a1e71 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.75.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_1.75.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0b4559541cf3259496c760a26f8d83e82179cb7e4576333682c5af49ee4a35a7 -size 1125331 +oid sha256:d731b4ce039315e096113f3c83168165020949e57564e641e778728e35901169 +size 1125286 diff --git a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_2.00.png b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_2.00.png index a85909178..03d4fb69a 100644 --- a/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_2.00.png +++ b/crates/egui_demo_lib/tests/snapshots/rendering_test/dpi_2.00.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:67c8412a1e8fdbfd88f8573797fbf6fbd89c6ce783a074a8e90f7d8d9e67dd57 -size 1366351 +oid sha256:cfac3518220555984d47c9fdfea2202a37102250aefcc2509794f337b3a7baae +size 1361407 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Additive rectangle.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Additive rectangle.png index 0eb5ebd6a..1d3f1785e 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Additive rectangle.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Additive rectangle.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2b7b54a1af0f5cd31bd64f0506e3035dd423314ce3389e61730fa160434fbf3 -size 45074 +oid sha256:cf21fe763e9762bca1b0f486e29a6024efcbc106a7f1ac195104acd0621cf8db +size 45107 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred stroke.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred stroke.png index bd9942eb7..646eaa1e0 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred stroke.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred stroke.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b66a0be67ff2d684a54c2321123521b3ad06dfe5ebffd50e89260d77efcfcc4 -size 86833 +oid sha256:2f09338e652b965cc9ae7bbb261845cd9c15d79f3d15f3c5b5326ef6d163b606 +size 86885 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred.png index 64ddf5c49..e667fc387 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Blurred.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19320291c99a23429b114a59de4636689e281e1e68766abe2aa1e56562128e50 -size 118919 +oid sha256:e298244953653e46875053b12b4fe06ee692cb58fc131233ac4172677f0f8b44 +size 118961 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Minimal rounding.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Minimal rounding.png index 105bbf285..843c3ba3b 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Minimal rounding.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Minimal rounding.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5edf089c00715f1456fe7838e85aadcfc42b6216a3fd95b48d9c21fc8d700cba -size 51371 +oid sha256:6b9b36acf821cca71f97a3c8468fb925561f3bc2030742aef1e3c1d9e69ccc6f +size 51419 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Normal.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Normal.png index 035eb931d..e738e22eb 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Normal.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Normal.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6cd1a10639dcb323bdc3b2c43e0c35665184fc809731ced90088ee9edb9de845 -size 54577 +oid sha256:f5ad7a37546d48fc5426c32534a1c452fd0bf8280346dbe6e67ac26f17f3ba8a +size 54626 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thick stroke, minimal rounding.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thick stroke, minimal rounding.png index 26014a12c..d46e593ba 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thick stroke, minimal rounding.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thick stroke, minimal rounding.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:87e34024f701dc93f4026213ac7eb468a2cd6d3393eb0dbec382bf58007f8e61 -size 55042 +oid sha256:c0b61e9d1c2bcbf891a7acd4f3c1d2bd7524133d8165e7e7984998670de5a085 +size 55090 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin filled.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin filled.png index dcbbba2b6..31a1dd365 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin filled.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin filled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7940ff56796efb27bec66b632ff33aa2ad390c4962a711bf520aee341f035a4 -size 35968 +oid sha256:a2e4975e9328a6d72f2c932daddfbb00cebdb2249aceb53f667d4060a1c0ea8a +size 36006 diff --git a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin stroked.png b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin stroked.png index 0a3d062af..db20010e0 100644 --- a/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin stroked.png +++ b/crates/egui_demo_lib/tests/snapshots/tessellation_test/Thin stroked.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b7bbd16c8aad444f0d11aacf87cf2292d494cc80a1ca46e7e8db86ca3041d35a -size 35931 +oid sha256:ac6f9adeef92be9f69cb288ccafda8d522b8c3cde64352cd5369ae63668240c0 +size 35973 diff --git a/crates/egui_demo_lib/tests/snapshots/text_selection.png b/crates/egui_demo_lib/tests/snapshots/text_selection.png deleted file mode 100644 index 63a4423a3..000000000 --- a/crates/egui_demo_lib/tests/snapshots/text_selection.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0475c5ac04ab8f79b79d43cfdb985f05b61dbe90e81f898a6dc216c308a28841 -size 4707 diff --git a/crates/egui_demo_lib/tests/snapshots/text_selection_0.png b/crates/egui_demo_lib/tests/snapshots/text_selection_0.png new file mode 100644 index 000000000..7930dff48 --- /dev/null +++ b/crates/egui_demo_lib/tests/snapshots/text_selection_0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:344d90928510855dc718a2e36e31a97f084f1163ab750d0217fb8620469b621a +size 5276 diff --git a/crates/egui_demo_lib/tests/snapshots/text_selection_1.png b/crates/egui_demo_lib/tests/snapshots/text_selection_1.png new file mode 100644 index 000000000..8691211cb --- /dev/null +++ b/crates/egui_demo_lib/tests/snapshots/text_selection_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:60449af267336663304e44e254d0984e037bebfa2d1efdf32234cab4374e8c79 +size 5301 diff --git a/crates/egui_demo_lib/tests/snapshots/widget_gallery_dark_x1.png b/crates/egui_demo_lib/tests/snapshots/widget_gallery_dark_x1.png index 112605454..e4d385fce 100644 --- a/crates/egui_demo_lib/tests/snapshots/widget_gallery_dark_x1.png +++ b/crates/egui_demo_lib/tests/snapshots/widget_gallery_dark_x1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bbdc4199dee2ae853b8a240cd84528482dc6762233bd0d1249f2daa296b49487 -size 64172 +oid sha256:c5a45307147f19f2d69a3de1f53e0a73ba4c3368eb25a66b4098fb54cb83822f +size 64203 diff --git a/crates/egui_demo_lib/tests/snapshots/widget_gallery_dark_x2.png b/crates/egui_demo_lib/tests/snapshots/widget_gallery_dark_x2.png index 1b5b60c8a..102cb3650 100644 --- a/crates/egui_demo_lib/tests/snapshots/widget_gallery_dark_x2.png +++ b/crates/egui_demo_lib/tests/snapshots/widget_gallery_dark_x2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f6d38b6b47839d0e4eae530d203c83971fba8a41c9caa3d5b5d89ee7ed582613 -size 150090 +oid sha256:0102aa84db99a6da1db1de3abf67f13c3b571de00e79e7c55805dc0504658d50 +size 150111 diff --git a/crates/egui_demo_lib/tests/snapshots/widget_gallery_light_x1.png b/crates/egui_demo_lib/tests/snapshots/widget_gallery_light_x1.png index ba1029a16..091948af6 100644 --- a/crates/egui_demo_lib/tests/snapshots/widget_gallery_light_x1.png +++ b/crates/egui_demo_lib/tests/snapshots/widget_gallery_light_x1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7ccf0fd192ad7556853a7606fb43adb693194fb864870ba4135c4f59c969b5e -size 59407 +oid sha256:3991cb1f922e0c6712d045b3cd8a1d98165c0fbef7e31b15d587f244e53ec04a +size 59343 diff --git a/crates/egui_demo_lib/tests/snapshots/widget_gallery_light_x2.png b/crates/egui_demo_lib/tests/snapshots/widget_gallery_light_x2.png index 81c7452e6..881f1b0d5 100644 --- a/crates/egui_demo_lib/tests/snapshots/widget_gallery_light_x2.png +++ b/crates/egui_demo_lib/tests/snapshots/widget_gallery_light_x2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4288ee4a0d2229d59c31538179cdda50035a3849f69b400127e1618efe30cdc1 -size 145224 +oid sha256:355d8f08d08011635bf812aea1edeabd69e1ac3c724b521ed243f2b52e9b444b +size 145257 diff --git a/crates/egui_kittest/Cargo.toml b/crates/egui_kittest/Cargo.toml index 33c895617..f922b807e 100644 --- a/crates/egui_kittest/Cargo.toml +++ b/crates/egui_kittest/Cargo.toml @@ -38,7 +38,7 @@ egui.workspace = true eframe = { workspace = true, optional = true } kittest.workspace = true serde.workspace = true -toml.workspace = true +toml = {workspace = true, features = ["parse", "serde"] } # wgpu dependencies egui-wgpu = { workspace = true, optional = true } diff --git a/crates/egui_kittest/README.md b/crates/egui_kittest/README.md index 638c61522..7b97f4e1d 100644 --- a/crates/egui_kittest/README.md +++ b/crates/egui_kittest/README.md @@ -97,12 +97,12 @@ You should add the following to your `.gitignore`: * …have a low resolution to avoid growth in repo size * …have a low comparison threshold to avoid the test passing despite unwanted differences (the default threshold should be fine for most usecases!) -### What do do when CI / another computer produces a different image? +### What to do when CI / another computer produces a different image? The default tolerance settings should be fine for almost all gui comparison tests. However, especially when you're using custom rendering, you may observe images difference with different setups leading to unexpected test failures. -First check whether the difference is due to a change in enabled rendering features, potentially due to difference in hardware (/software renderer) capabilitites. +First check whether the difference is due to a change in enabled rendering features, potentially due to difference in hardware (/software renderer) capabilities. Generally you should carefully enforcing the same set of features for all test runs, but this may happen nonetheless. Once you validated that the differences are miniscule and hard to avoid, you can try to _carefully_ adjust the comparison tolerance setting (`SnapshotOptions::threshold`, TODO([#5683](https://github.com/emilk/egui/issues/5683)): as well as number of pixels allowed to differ) for the specific test. diff --git a/crates/egui_kittest/src/node.rs b/crates/egui_kittest/src/node.rs index 94940ffff..ed68d50ee 100644 --- a/crates/egui_kittest/src/node.rs +++ b/crates/egui_kittest/src/node.rs @@ -98,9 +98,11 @@ impl Node<'_> { /// This will trigger a [`accesskit::Action::Click`] action. /// In contrast to `click()`, this can also click widgets that are not currently visible. pub fn click_accesskit(&self) { + let (target_node, target_tree) = self.accesskit_node.locate(); self.event(egui::Event::AccessKitActionRequest( accesskit::ActionRequest { - target: self.accesskit_node.id(), + target_node, + target_tree, action: accesskit::Action::Click, data: None, }, @@ -119,9 +121,11 @@ impl Node<'_> { } pub fn focus(&self) { + let (target_node, target_tree) = self.accesskit_node.locate(); self.event(egui::Event::AccessKitActionRequest(ActionRequest { action: accesskit::Action::Focus, - target: self.accesskit_node.id(), + target_node, + target_tree, data: None, })); } @@ -162,45 +166,55 @@ impl Node<'_> { /// Scroll the node into view. pub fn scroll_to_me(&self) { + let (target_node, target_tree) = self.accesskit_node.locate(); self.event(egui::Event::AccessKitActionRequest(ActionRequest { action: accesskit::Action::ScrollIntoView, - target: self.accesskit_node.id(), + target_node, + target_tree, data: None, })); } /// Scroll the [`egui::ScrollArea`] containing this node down (100px). pub fn scroll_down(&self) { + let (target_node, target_tree) = self.accesskit_node.locate(); self.event(egui::Event::AccessKitActionRequest(ActionRequest { action: accesskit::Action::ScrollDown, - target: self.accesskit_node.id(), + target_node, + target_tree, data: None, })); } /// Scroll the [`egui::ScrollArea`] containing this node up (100px). pub fn scroll_up(&self) { + let (target_node, target_tree) = self.accesskit_node.locate(); self.event(egui::Event::AccessKitActionRequest(ActionRequest { action: accesskit::Action::ScrollUp, - target: self.accesskit_node.id(), + target_node, + target_tree, data: None, })); } /// Scroll the [`egui::ScrollArea`] containing this node left (100px). pub fn scroll_left(&self) { + let (target_node, target_tree) = self.accesskit_node.locate(); self.event(egui::Event::AccessKitActionRequest(ActionRequest { action: accesskit::Action::ScrollLeft, - target: self.accesskit_node.id(), + target_node, + target_tree, data: None, })); } /// Scroll the [`egui::ScrollArea`] containing this node right (100px). pub fn scroll_right(&self) { + let (target_node, target_tree) = self.accesskit_node.locate(); self.event(egui::Event::AccessKitActionRequest(ActionRequest { action: accesskit::Action::ScrollRight, - target: self.accesskit_node.id(), + target_node, + target_tree, data: None, })); } diff --git a/crates/egui_kittest/src/snapshot.rs b/crates/egui_kittest/src/snapshot.rs index 8134dbdf0..28b5ac986 100644 --- a/crates/egui_kittest/src/snapshot.rs +++ b/crates/egui_kittest/src/snapshot.rs @@ -859,6 +859,7 @@ impl From for Vec { } impl Drop for SnapshotResults { + #[track_caller] fn drop(&mut self) { // Don't panic if we are already panicking (the test probably failed for another reason) if std::thread::panicking() { diff --git a/crates/egui_kittest/tests/snapshots/combobox_closed.png b/crates/egui_kittest/tests/snapshots/combobox_closed.png index 708985b14..073ae79a3 100644 --- a/crates/egui_kittest/tests/snapshots/combobox_closed.png +++ b/crates/egui_kittest/tests/snapshots/combobox_closed.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ca39801faddae7191ed054029263e8eca488d16e1fcbb40fed482d39fc89e8e -size 4520 +oid sha256:00fb02e0cc2c1454d3a3dc0635be24086234c2bc5e2c9fd73741b179622e16d6 +size 4514 diff --git a/crates/egui_kittest/tests/snapshots/combobox_opened.png b/crates/egui_kittest/tests/snapshots/combobox_opened.png index 53a9c8ed1..78e1baaca 100644 --- a/crates/egui_kittest/tests/snapshots/combobox_opened.png +++ b/crates/egui_kittest/tests/snapshots/combobox_opened.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bafe5d7129cd2137b8f7bc9662b894d959b7042c436443f835ecd421a0d9c33f -size 8019 +oid sha256:d8757e2db9a3892d9347495ad59f14d2bd9164a9ba258375a53c9faf8176b597 +size 8016 diff --git a/crates/egui_kittest/tests/snapshots/menu/closed_hovered.png b/crates/egui_kittest/tests/snapshots/menu/closed_hovered.png index d9d542908..a82442e1e 100644 --- a/crates/egui_kittest/tests/snapshots/menu/closed_hovered.png +++ b/crates/egui_kittest/tests/snapshots/menu/closed_hovered.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5b6447dd4bd6489b9b909b8240abe54db16331beaa2fb4656f01f79a08fb25f9 -size 11112 +oid sha256:5f4a038f9acbb12880ba6b681ef7d3ae566045c4474aa31e7c6d746c39a649fc +size 11108 diff --git a/crates/egui_kittest/tests/snapshots/menu/opened.png b/crates/egui_kittest/tests/snapshots/menu/opened.png index 7f9b23b37..eb55bd894 100644 --- a/crates/egui_kittest/tests/snapshots/menu/opened.png +++ b/crates/egui_kittest/tests/snapshots/menu/opened.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4baf110e63fb104f30e9ae06e3601cfb48d7b69aec31636ca470c9ba9f6d44a9 -size 21659 +oid sha256:2965482e0161b4ea99aa5b4ece32261dbe246f86fe43054a754fbd556c7a5896 +size 21666 diff --git a/crates/egui_kittest/tests/snapshots/menu/submenu.png b/crates/egui_kittest/tests/snapshots/menu/submenu.png index 7c1823938..0a78e4e6c 100644 --- a/crates/egui_kittest/tests/snapshots/menu/submenu.png +++ b/crates/egui_kittest/tests/snapshots/menu/submenu.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:93d2f336e216f371e7239b08113e659bad6c30eb299a2d8ea9537ae1c63533f0 -size 28503 +oid sha256:7592ca6213497f686d105a2e686d0c5de364388ddd174cbe8abb425d27ddcab0 +size 28505 diff --git a/crates/egui_kittest/tests/snapshots/menu/subsubmenu.png b/crates/egui_kittest/tests/snapshots/menu/subsubmenu.png index 7e8f3f8e7..84e6ba152 100644 --- a/crates/egui_kittest/tests/snapshots/menu/subsubmenu.png +++ b/crates/egui_kittest/tests/snapshots/menu/subsubmenu.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6bf27cde6a87112a1ae832a65c80a9e34243b6ee368314379f5bf018edf439e5 -size 33239 +oid sha256:3a1adf0903f0fc50323c2d77bbc491c950ab0dae6593c004770ea7961c2c6273 +size 33270 diff --git a/crates/egui_kittest/tests/snapshots/override_text_color_interactive.png b/crates/egui_kittest/tests/snapshots/override_text_color_interactive.png index fb8887d13..4d365c4c2 100644 --- a/crates/egui_kittest/tests/snapshots/override_text_color_interactive.png +++ b/crates/egui_kittest/tests/snapshots/override_text_color_interactive.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8038005841dbf272375388b224dcc9fc1177b5c113d3e6f6dbc2265c88c7e60 -size 19704 +oid sha256:bf8177abaa5920e32ad4618f5296355377f475b0c0f9a95f75cbfbe468415fb8 +size 19696 diff --git a/crates/egui_kittest/tests/snapshots/readme_example.png b/crates/egui_kittest/tests/snapshots/readme_example.png index cb99dfc84..050a4a43e 100644 --- a/crates/egui_kittest/tests/snapshots/readme_example.png +++ b/crates/egui_kittest/tests/snapshots/readme_example.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e86ed66f3ac3a81998eefbed8cb231edc6522174050676b45e9248f9e7f18533 -size 2227 +oid sha256:1dd1f5013587463f002b1becac1560876c462295dbe5dfbb1a9dbce58991e53d +size 2209 diff --git a/crates/egui_kittest/tests/snapshots/test_masking.png b/crates/egui_kittest/tests/snapshots/test_masking.png index a397ceda6..5bf5dd6fa 100644 --- a/crates/egui_kittest/tests/snapshots/test_masking.png +++ b/crates/egui_kittest/tests/snapshots/test_masking.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4216258893fae554f0ab8b3a76ef0905cacb62c70af47fa811ff6f3d99f9f3ab -size 5619 +oid sha256:be0bd449166878ced27eff4966d1741731e926f9baabe8b590375c20103036dd +size 5527 diff --git a/crates/egui_kittest/tests/snapshots/test_shrink.png b/crates/egui_kittest/tests/snapshots/test_shrink.png index 40f2e284d..e4ff540f4 100644 --- a/crates/egui_kittest/tests/snapshots/test_shrink.png +++ b/crates/egui_kittest/tests/snapshots/test_shrink.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21a92c29e27ef0fdec273ea2d94a2b3e74cdf380ec77f4783daeb008bd51db6d -size 2767 +oid sha256:888f8a4d995d718a9a158e563d8ac1434775660b33aebb5f34feea54ffd12600 +size 2830 diff --git a/crates/egui_kittest/tests/snapshots/test_tooltip_hidden.png b/crates/egui_kittest/tests/snapshots/test_tooltip_hidden.png index 40f2e284d..e4ff540f4 100644 --- a/crates/egui_kittest/tests/snapshots/test_tooltip_hidden.png +++ b/crates/egui_kittest/tests/snapshots/test_tooltip_hidden.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21a92c29e27ef0fdec273ea2d94a2b3e74cdf380ec77f4783daeb008bd51db6d -size 2767 +oid sha256:888f8a4d995d718a9a158e563d8ac1434775660b33aebb5f34feea54ffd12600 +size 2830 diff --git a/crates/egui_kittest/tests/snapshots/test_tooltip_shown.png b/crates/egui_kittest/tests/snapshots/test_tooltip_shown.png index 86cc5a717..d6053700b 100644 --- a/crates/egui_kittest/tests/snapshots/test_tooltip_shown.png +++ b/crates/egui_kittest/tests/snapshots/test_tooltip_shown.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f9ca5f8081d677b8bff47813c4eb94319ca03855e780aed834ecc2f3d905a22c -size 4852 +oid sha256:037f3e356d32e1a2c32767460399f919452bff0933e1db7aa113e7e2bdb083f0 +size 4927 diff --git a/crates/emath/src/smart_aim.rs b/crates/emath/src/smart_aim.rs index c1b96ec7b..5d6a3e0fc 100644 --- a/crates/emath/src/smart_aim.rs +++ b/crates/emath/src/smart_aim.rs @@ -2,7 +2,7 @@ use crate::fast_midpoint; -const NUM_DECIMALS: usize = 15; +const NUM_DECIMALS: usize = 16; /// Find the "simplest" number in a closed range [min, max], i.e. the one with the fewest decimal digits. /// @@ -143,6 +143,10 @@ fn from_decimal_string(s: [u8; NUM_DECIMALS]) -> u64 { #[expect(clippy::approx_constant)] #[test] fn test_aim() { + assert_eq!( + best_in_range_f64(0.0799999999999996, 0.09999999999999995), + 0.08, + ); assert_eq!(best_in_range_f64(-0.2, 0.0), 0.0, "Prefer zero"); assert_eq!(best_in_range_f64(-10_004.23, 3.14), 0.0, "Prefer zero"); assert_eq!(best_in_range_f64(-0.2, 100.0), 0.0, "Prefer zero"); diff --git a/crates/epaint/Cargo.toml b/crates/epaint/Cargo.toml index 77facdb3f..c8a05e4d7 100644 --- a/crates/epaint/Cargo.toml +++ b/crates/epaint/Cargo.toml @@ -48,7 +48,7 @@ mint = ["emath/mint"] rayon = ["dep:rayon"] ## Allow serialization using [`serde`](https://docs.rs/serde). -serde = ["dep:serde", "ahash/serde", "emath/serde", "ecolor/serde"] +serde = ["dep:serde", "ahash/serde", "emath/serde", "ecolor/serde", "font-types/serde", "smallvec/serde"] ## Change Vertex layout to be compatible with unity unity = [] @@ -62,12 +62,14 @@ emath.workspace = true ecolor.workspace = true ahash.workspace = true +font-types.workspace = true log.workspace = true nohash-hasher.workspace = true parking_lot.workspace = true # Using parking_lot over std::sync::Mutex gives 50% speedups in some real-world scenarios. profiling.workspace = true self_cell.workspace = true skrifa.workspace = true +smallvec.workspace = true vello_cpu.workspace = true #! ### Optional dependencies diff --git a/crates/epaint/src/text/font.rs b/crates/epaint/src/text/font.rs index 150aca34a..61f9f9f2f 100644 --- a/crates/epaint/src/text/font.rs +++ b/crates/epaint/src/text/font.rs @@ -12,7 +12,7 @@ use vello_cpu::{color, kurbo}; use crate::{ TextOptions, TextureAtlas, text::{ - FontTweak, + FontTweak, VariationCoords, fonts::{Blob, CachedFamily, FontFaceKey}, }, }; @@ -145,8 +145,8 @@ struct GlyphCacheKey(u64); impl nohash_hasher::IsEnabled for GlyphCacheKey {} impl GlyphCacheKey { - fn new(glyph_id: skrifa::GlyphId, metrics: &ScaledMetrics, bin: SubpixelBin) -> Self { - let ScaledMetrics { + fn new(glyph_id: skrifa::GlyphId, metrics: &StyledMetrics, bin: SubpixelBin) -> Self { + let StyledMetrics { pixels_per_point, px_scale_factor, .. @@ -197,10 +197,10 @@ impl FontCell { fn allocate_glyph_uncached( &mut self, atlas: &mut TextureAtlas, - metrics: &ScaledMetrics, + metrics: &StyledMetrics, glyph_info: &GlyphInfo, bin: SubpixelBin, - location: &skrifa::instance::Location, + location: skrifa::instance::LocationRef<'_>, ) -> Option { let glyph_id = glyph_info.id?; @@ -337,8 +337,6 @@ pub struct FontFace { font: FontCell, tweak: FontTweak, - /// Variable font location (for weight axis, etc.) - location: skrifa::instance::Location, glyph_info_cache: ahash::HashMap, glyph_alloc_cache: ahash::HashMap, } @@ -350,7 +348,6 @@ impl FontFace { font_data: Blob, index: u32, tweak: FontTweak, - preferred_weight: Option, ) -> Result> { let font = FontCell::try_new(font_data, |font_data| { let skrifa_font = @@ -396,44 +393,10 @@ impl FontFace { }) })?; - // Use preferred_weight if provided, otherwise try to read from the OS/2 table or fvar default - let weight = preferred_weight.or_else(|| { - // First try OS/2 table - if let Some(w) = font - .borrow_dependent() - .skrifa - .os2() - .ok() - .map(|os2| os2.us_weight_class()) - { - return Some(w); - } - // If no OS/2 or preferred_weight, try to get default from variable font's fvar table - font.borrow_dependent() - .skrifa - .axes() - .iter() - .find(|axis| axis.tag() == skrifa::raw::types::Tag::new(b"wght")) - .map(|axis| axis.default_value() as u16) - }); - - // Create location for variable font with weight axis - // If weight is provided (either from preferred_weight, OS/2, or fvar default), use it - // Otherwise fall back to Location::default() which uses all axis defaults - let location = if let Some(w) = weight { - font.borrow_dependent() - .skrifa - .axes() - .location([("wght", w as f32)]) - } else { - skrifa::instance::Location::default() - }; - Ok(Self { name, font, tweak, - location, glyph_info_cache: Default::default(), glyph_alloc_cache: Default::default(), }) @@ -537,7 +500,7 @@ impl FontFace { #[inline] pub(super) fn pair_kerning_pixels( &self, - metrics: &ScaledMetrics, + metrics: &StyledMetrics, last_glyph_id: skrifa::GlyphId, glyph_id: skrifa::GlyphId, ) -> f32 { @@ -559,7 +522,7 @@ impl FontFace { #[inline] pub fn pair_kerning( &self, - metrics: &ScaledMetrics, + metrics: &StyledMetrics, last_glyph_id: skrifa::GlyphId, glyph_id: skrifa::GlyphId, ) -> f32 { @@ -567,7 +530,12 @@ impl FontFace { } #[inline(always)] - pub fn scaled_metrics(&self, pixels_per_point: f32, font_size: f32) -> ScaledMetrics { + pub fn styled_metrics( + &self, + pixels_per_point: f32, + font_size: f32, + coords: &VariationCoords, + ) -> StyledMetrics { let pt_scale_factor = self.font.px_scale_factor(font_size * self.tweak.scale); let font_data = self.font.borrow_dependent(); let ascent = (font_data.metrics.ascent * pt_scale_factor).round_ui(); @@ -581,20 +549,32 @@ impl FontFace { + self.tweak.y_offset) .round_ui(); - ScaledMetrics { + let axes = font_data.skrifa.axes(); + // Override the default coordinates with ones specified via FontTweak, then the ones specified directly via the + // argument (probably from TextFormat). + let settings = self + .tweak + .coords + .as_ref() + .iter() + .chain(coords.as_ref().iter()); + let location = axes.location(settings); + + StyledMetrics { pixels_per_point, px_scale_factor, scale, y_offset_in_points, ascent, row_height: ascent - descent + line_gap, + location, } } pub fn allocate_glyph( &mut self, atlas: &mut TextureAtlas, - metrics: &ScaledMetrics, + metrics: &StyledMetrics, glyph_info: GlyphInfo, chr: char, h_pos: f32, @@ -628,7 +608,7 @@ impl FontFace { let allocation = self .font - .allocate_glyph_uncached(atlas, metrics, &glyph_info, bin, &self.location) + .allocate_glyph_uncached(atlas, metrics, &glyph_info, bin, (&metrics.location).into()) .unwrap_or_default(); entry.insert(allocation); @@ -665,12 +645,17 @@ impl Font<'_> { }) } - pub fn scaled_metrics(&self, pixels_per_point: f32, font_size: f32) -> ScaledMetrics { + pub fn styled_metrics( + &self, + pixels_per_point: f32, + font_size: f32, + coords: &VariationCoords, + ) -> StyledMetrics { self.cached_family .fonts .first() .and_then(|key| self.fonts_by_id.get(key)) - .map(|font_face| font_face.scaled_metrics(pixels_per_point, font_size)) + .map(|font_face| font_face.styled_metrics(pixels_per_point, font_size, coords)) .unwrap_or_default() } @@ -713,8 +698,8 @@ impl Font<'_> { } /// Metrics for a font at a specific screen-space scale. -#[derive(Clone, Copy, Debug, PartialEq, Default)] -pub struct ScaledMetrics { +#[derive(Clone, Debug, PartialEq, Default)] +pub struct StyledMetrics { /// The DPI part of the screen-space scale. pub pixels_per_point: f32, @@ -738,6 +723,9 @@ pub struct ScaledMetrics { /// /// Returns a value rounded to [`emath::GUI_ROUNDING`]. pub row_height: f32, + + /// Resolved variation coordinates. + pub location: skrifa::instance::Location, } /// Code points that will always be invisible (zero width). diff --git a/crates/epaint/src/text/fonts.rs b/crates/epaint/src/text/fonts.rs index 19876b571..5099e0085 100644 --- a/crates/epaint/src/text/fonts.rs +++ b/crates/epaint/src/text/fonts.rs @@ -10,7 +10,7 @@ use std::{ use crate::{ TextureAtlas, text::{ - Galley, LayoutJob, LayoutSection, TextOptions, + Galley, LayoutJob, LayoutSection, TextOptions, VariationCoords, font::{Font, FontFace, GlyphInfo}, }, }; @@ -125,12 +125,6 @@ pub struct FontData { /// Extra scale and vertical tweak to apply to all text of this font. pub tweak: FontTweak, - - /// The font weight (100-900), if available. - /// Standard values: 100 (Thin), 200 (Extra Light), 300 (Light), 400 (Regular), - /// 500 (Medium), 600 (Semi Bold), 700 (Bold), 800 (Extra Bold), 900 (Black). - /// `None` if the weight could not be determined. - pub weight: Option, } impl FontData { @@ -139,7 +133,6 @@ impl FontData { font: Cow::Borrowed(font), index: 0, tweak: Default::default(), - weight: None, } } @@ -148,43 +141,12 @@ impl FontData { font: Cow::Owned(font), index: 0, tweak: Default::default(), - weight: None, } } pub fn tweak(self, tweak: FontTweak) -> Self { Self { tweak, ..self } } - - /// Set the font weight (100-900). - /// - /// This is typically read automatically from the font file when loaded, - /// but can be overridden manually if needed. - /// - /// Standard weight values: - /// - 100: Thin - /// - 200: Extra Light - /// - 300: Light - /// - 400: Regular/Normal - /// - 500: Medium - /// - 600: Semi Bold - /// - 700: Bold - /// - 800: Extra Bold - /// - 900: Black - /// - /// # Example - /// ``` - /// # use epaint::text::FontData; - /// let font_data = FontData::from_static(include_bytes!("../../../epaint_default_fonts/fonts/Ubuntu-Light.ttf")) - /// .weight(300); // Override to Light weight - /// assert_eq!(font_data.weight, Some(300)); - /// ``` - pub fn weight(self, weight: u16) -> Self { - Self { - weight: Some(weight), - ..self - } - } } impl AsRef<[u8]> for FontData { @@ -196,7 +158,7 @@ impl AsRef<[u8]> for FontData { // ---------------------------------------------------------------------------- /// Extra scale and vertical tweak to apply to all text of a certain font. -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct FontTweak { /// Scale the font's glyphs by this much. @@ -228,6 +190,9 @@ pub struct FontTweak { /// /// `None` means use the global setting. pub hinting_override: Option, + + /// Override the font's default variation coordinates. + pub coords: VariationCoords, } impl Default for FontTweak { @@ -237,6 +202,7 @@ impl Default for FontTweak { y_offset_factor: 0.0, y_offset: 0.0, hinting_override: None, + coords: VariationCoords::default(), } } } @@ -701,7 +667,12 @@ impl FontsView<'_> { pub fn row_height(&mut self, font_id: &FontId) -> f32 { self.fonts .font(&font_id.family) - .scaled_metrics(self.pixels_per_point, font_id.size) + .styled_metrics( + self.pixels_per_point, + font_id.size, + // TODO(valadaptive): use font variation coords when calculating row height + &VariationCoords::default(), + ) .row_height } @@ -807,15 +778,13 @@ impl FontsImpl { let mut fonts_by_id: nohash_hasher::IntMap = Default::default(); let mut fonts_by_name: ahash::HashMap = Default::default(); for (name, font_data) in &definitions.font_data { - let tweak = font_data.tweak; let blob = blob_from_font_data(font_data); let font_face = FontFace::new( options, name.clone(), blob, font_data.index, - tweak, - font_data.weight, + font_data.tweak.clone(), ) .unwrap_or_else(|err| panic!("Error parsing {name:?} TTF/OTF font file: {err}")); let key = FontFaceKey::new(); diff --git a/crates/epaint/src/text/text_layout.rs b/crates/epaint/src/text/text_layout.rs index 5b2400646..0233c1c58 100644 --- a/crates/epaint/src/text/text_layout.rs +++ b/crates/epaint/src/text/text_layout.rs @@ -8,7 +8,7 @@ use crate::{ Color32, Mesh, Stroke, Vertex, stroke::PathStroke, text::{ - font::{ScaledMetrics, is_cjk, is_cjk_break_allowed}, + font::{StyledMetrics, is_cjk, is_cjk_break_allowed}, fonts::FontFaceKey, }, }; @@ -114,7 +114,7 @@ pub fn layout(fonts: &mut FontsImpl, pixels_per_point: f32, job: Arc) let intrinsic_size = calculate_intrinsic_size(point_scale, &job, ¶graphs); let mut elided = false; - let mut rows = rows_from_paragraphs(paragraphs, &job, &mut elided); + let mut rows = rows_from_paragraphs(paragraphs, &job, pixels_per_point, &mut elided); if elided && let Some(last_placed) = rows.last_mut() { let last_row = Arc::make_mut(&mut last_placed.row); replace_last_glyph_with_overflow_character(fonts, pixels_per_point, &job, last_row); @@ -160,7 +160,7 @@ fn layout_section( } = section; let mut font = fonts.font(&format.font_id.family); let font_size = format.font_id.size; - let font_metrics = font.scaled_metrics(pixels_per_point, font_size); + let font_metrics = font.styled_metrics(pixels_per_point, font_size, &format.coords); let line_height = section .format .line_height @@ -178,7 +178,7 @@ fn layout_section( // Optimization: only recompute `ScaledMetrics` when the concrete `FontImpl` changes. let mut current_font = FontFaceKey::INVALID; - let mut current_font_face_metrics = ScaledMetrics::default(); + let mut current_font_face_metrics = StyledMetrics::default(); for chr in job.text[byte_range.clone()].chars() { if job.break_on_newline && chr == '\n' { @@ -192,7 +192,9 @@ fn layout_section( current_font = font_id; current_font_face_metrics = font_face .as_ref() - .map(|font_face| font_face.scaled_metrics(pixels_per_point, font_size)) + .map(|font_face| { + font_face.styled_metrics(pixels_per_point, font_size, &format.coords) + }) .unwrap_or_default(); } @@ -252,11 +254,12 @@ fn calculate_intrinsic_size( ) -> Vec2 { let mut intrinsic_size = Vec2::ZERO; for (idx, paragraph) in paragraphs.iter().enumerate() { - let width = paragraph - .glyphs - .last() - .map(|l| l.max_x()) - .unwrap_or_default(); + // Use the precise cursor position instead of `last_glyph.max_x()`, + // because glyph positions are pixel-snapped but the cursor tracks + // the exact subpixel advance. This ensures that when two galleys are + // placed side-by-side, the gap matches what it would be within a + // single galley. + let width = paragraph.cursor_x_px / point_scale.pixels_per_point; intrinsic_size.x = f32::max(intrinsic_size.x, width); let mut height = paragraph @@ -277,6 +280,7 @@ fn calculate_intrinsic_size( fn rows_from_paragraphs( paragraphs: Vec, job: &LayoutJob, + pixels_per_point: f32, elided: &mut bool, ) -> Vec { let num_paragraphs = paragraphs.len(); @@ -303,8 +307,11 @@ fn rows_from_paragraphs( ends_with_newline: !is_last_paragraph, }); } else { - let paragraph_max_x = paragraph.glyphs.last().unwrap().max_x(); - if paragraph_max_x <= job.effective_wrap_width() { + // Use precise cursor position for width instead of pixel-snapped + // `last_glyph.max_x()`, so that side-by-side galleys have the same + // spacing as characters within a single galley. + let paragraph_width = paragraph.cursor_x_px / pixels_per_point; + if paragraph_width <= job.effective_wrap_width() { // Early-out optimization: the whole paragraph fits on one row. rows.push(PlacedRow { pos: pos2(0.0, f32::NAN), @@ -312,7 +319,7 @@ fn rows_from_paragraphs( section_index_at_start: paragraph.section_index_at_start, glyphs: paragraph.glyphs, visuals: Default::default(), - size: vec2(paragraph_max_x, 0.0), + size: vec2(paragraph_width, 0.0), }), ends_with_newline: !is_last_paragraph, }); @@ -468,7 +475,7 @@ fn replace_last_glyph_with_overflow_character( let mut font_face = font.fonts_by_id.get_mut(&font_id); let font_face_metrics = font_face .as_mut() - .map(|f| f.scaled_metrics(pixels_per_point, font_size)) + .map(|f| f.styled_metrics(pixels_per_point, font_size, §ion.format.coords)) .unwrap_or_default(); let overflow_glyph_x = if let Some(prev_glyph) = row.glyphs.last() { @@ -495,7 +502,9 @@ fn replace_last_glyph_with_overflow_character( let replacement_glyph_width = font_face .as_mut() .and_then(|f| f.glyph_info(overflow_character)) - .map(|i| i.advance_width_unscaled.0 * font_face_metrics.px_scale_factor) + .map(|i| { + i.advance_width_unscaled.0 * font_face_metrics.px_scale_factor / pixels_per_point + }) .unwrap_or_default(); // Check if we're within width budget: @@ -517,7 +526,8 @@ fn replace_last_glyph_with_overflow_character( }) .unwrap_or_default(); - let font_metrics = font.scaled_metrics(pixels_per_point, font_size); + let font_metrics = + font.styled_metrics(pixels_per_point, font_size, §ion.format.coords); let line_height = section .format .line_height @@ -1166,6 +1176,42 @@ mod tests { assert_eq!(row.rect().max.x, row.glyphs.last().unwrap().max_x()); } + #[test] + fn test_truncate_with_pixels_per_point() { + let mut fonts = FontsImpl::new(TextOptions::default(), FontDefinitions::default()); + + for pixels_per_point in [ + 0.33, 0.5, 0.67, 1.0, 1.25, 1.33, 1.5, 1.75, 2.0, 3.0, 4.0, 5.0, + ] { + for ch in ['W', 'A', 'n', 't', 'i'] { + let target_width = 50.0; + let text = (0..20).map(|_| ch).collect::(); + + let mut job = LayoutJob::single_section(text, TextFormat::default()); + job.wrap.max_width = target_width; + job.wrap.max_rows = 1; + let elided_galley = layout(&mut fonts, pixels_per_point, job.into()); + assert!(elided_galley.elided); + + let test_galley = layout( + &mut fonts, + pixels_per_point, + Arc::new(LayoutJob::single_section( + (0..elided_galley.rows[0].char_count_excluding_newline()) + .map(|_| ch) + .chain(std::iter::once('…')) + .collect::(), + TextFormat::default(), + )), + ); + + assert!(elided_galley.size().x >= 0.0); + assert!(elided_galley.size().x <= target_width); + assert!(test_galley.size().x > target_width); + } + } + } + #[test] fn test_empty_row() { let pixels_per_point = 1.0; @@ -1174,7 +1220,7 @@ mod tests { let font_id = FontId::default(); let font_height = fonts .font(&font_id.family) - .scaled_metrics(pixels_per_point, font_id.size) + .styled_metrics(pixels_per_point, font_id.size, &VariationCoords::default()) .row_height; let job = LayoutJob::simple(String::new(), font_id, Color32::WHITE, f32::INFINITY); @@ -1207,7 +1253,7 @@ mod tests { let font_id = FontId::default(); let font_height = fonts .font(&font_id.family) - .scaled_metrics(pixels_per_point, font_id.size) + .styled_metrics(pixels_per_point, font_id.size, &VariationCoords::default()) .row_height; let job = LayoutJob::simple("Hi!\n".to_owned(), font_id, Color32::WHITE, f32::INFINITY); diff --git a/crates/epaint/src/text/text_layout_types.rs b/crates/epaint/src/text/text_layout_types.rs index 3e8a53d9e..d887fb13a 100644 --- a/crates/epaint/src/text/text_layout_types.rs +++ b/crates/epaint/src/text/text_layout_types.rs @@ -1,5 +1,5 @@ -use std::ops::Range; use std::sync::Arc; +use std::{ops::Range, str::FromStr as _}; use super::{ cursor::{CCursor, LayoutCursor}, @@ -7,6 +7,8 @@ use super::{ }; use crate::{Color32, FontId, Mesh, Stroke, text::FontsView}; use emath::{Align, GuiRounding as _, NumExt as _, OrderedFloat, Pos2, Rect, Vec2, pos2, vec2}; +pub use font_types::Tag; +use smallvec::SmallVec; /// Describes the task of laying out text. /// @@ -257,6 +259,107 @@ impl std::hash::Hash for LayoutSection { // ---------------------------------------------------------------------------- +/// Helper trait for all types that can be parsed as a [`font_types::Tag`]. +pub trait IntoTag { + fn into_tag(self) -> font_types::Tag; +} + +impl IntoTag for font_types::Tag { + #[inline(always)] + fn into_tag(self) -> font_types::Tag { + self + } +} + +impl IntoTag for u32 { + #[inline(always)] + fn into_tag(self) -> font_types::Tag { + font_types::Tag::from_u32(self) + } +} + +impl IntoTag for [u8; 4] { + #[inline(always)] + fn into_tag(self) -> font_types::Tag { + font_types::Tag::new_checked(&self).expect("Invalid variation axis tag") + } +} + +impl IntoTag for &[u8; 4] { + #[inline(always)] + fn into_tag(self) -> font_types::Tag { + font_types::Tag::new_checked(self).expect("Invalid variation axis tag") + } +} + +impl IntoTag for &str { + #[inline(always)] + fn into_tag(self) -> font_types::Tag { + font_types::Tag::from_str(self).expect("Invalid variation axis tag") + } +} + +/// List of font variation coordinates by axis tag. If more than one coordinate for a given axis is provided, the last +/// one added is used. +#[derive(Clone, Debug, PartialEq, Default)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub struct VariationCoords(SmallVec<[(font_types::Tag, f32); 2]>); + +impl VariationCoords { + /// Create a list of variation coordinates from a sequence of (tag, value) pairs. + /// + /// ## Example: + /// ``` + /// use epaint::text::VariationCoords; + /// + /// let coords = VariationCoords::new([ + /// (b"wght", 500.0), + /// (b"wdth", 75.0), + /// ]); + /// ``` + pub fn new(values: impl IntoIterator) -> Self { + Self(values.into_iter().map(|(t, c)| (t.into_tag(), c)).collect()) + } + + /// Add a variation coordinate to the list. + #[inline(always)] + pub fn push(&mut self, tag: impl IntoTag, coord: f32) { + self.0.push((tag.into_tag(), coord)); + } + + /// Remove the coordinate at the given index. + pub fn remove(&mut self, index: usize) { + self.0.remove(index); + } + + pub fn clear(&mut self) { + self.0.clear(); + } +} + +impl AsRef<[(font_types::Tag, f32)]> for VariationCoords { + #[inline(always)] + fn as_ref(&self) -> &[(font_types::Tag, f32)] { + &self.0 + } +} + +impl AsMut<[(font_types::Tag, f32)]> for VariationCoords { + fn as_mut(&mut self) -> &mut [(font_types::Tag, f32)] { + &mut self.0 + } +} + +impl std::hash::Hash for VariationCoords { + fn hash(&self, state: &mut H) { + self.0.len().hash(state); + for (tag, coord) in &self.0 { + tag.hash(state); + OrderedFloat(*coord).hash(state); + } + } +} + /// Formatting option for a section of text. #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] @@ -287,6 +390,8 @@ pub struct TextFormat { /// Default: 1.0 pub expand_bg: f32, + pub coords: VariationCoords, + pub italics: bool, pub underline: Stroke, @@ -315,6 +420,7 @@ impl Default for TextFormat { color: Color32::GRAY, background: Color32::TRANSPARENT, expand_bg: 1.0, + coords: VariationCoords::default(), italics: false, underline: Stroke::NONE, strikethrough: Stroke::NONE, @@ -333,6 +439,7 @@ impl std::hash::Hash for TextFormat { color, background, expand_bg, + coords, italics, underline, strikethrough, @@ -346,6 +453,7 @@ impl std::hash::Hash for TextFormat { color.hash(state); background.hash(state); emath::OrderedFloat(*expand_bg).hash(state); + coords.hash(state); italics.hash(state); underline.hash(state); strikethrough.hash(state); @@ -934,7 +1042,7 @@ impl Galley { } /// Returns a 0-width Rect. - fn pos_from_layout_cursor(&self, layout_cursor: &LayoutCursor) -> Rect { + pub fn pos_from_layout_cursor(&self, layout_cursor: &LayoutCursor) -> Rect { let Some(row) = self.rows.get(layout_cursor.row) else { return self.end_pos(); }; diff --git a/crates/epaint_default_fonts/emoji-icon-font.ttf b/crates/epaint_default_fonts/emoji-icon-font.ttf deleted file mode 100644 index 0f29dfed8..000000000 Binary files a/crates/epaint_default_fonts/emoji-icon-font.ttf and /dev/null differ diff --git a/crates/epaint_default_fonts/fonts/emoji-icon-font.ttf b/crates/epaint_default_fonts/fonts/emoji-icon-font.ttf index 806408328..0f29dfed8 100644 Binary files a/crates/epaint_default_fonts/fonts/emoji-icon-font.ttf and b/crates/epaint_default_fonts/fonts/emoji-icon-font.ttf differ diff --git a/deny.toml b/deny.toml index 1b3bd8b12..01377b90b 100644 --- a/deny.toml +++ b/deny.toml @@ -33,6 +33,7 @@ version = 2 ignore = [ "RUSTSEC-2024-0320", # unmaintained yaml-rust pulled in by syntect "RUSTSEC-2024-0436", # unmaintained paste pulled via metal/wgpu, see https://github.com/gfx-rs/metal-rs/issues/349 + "RUSTSEC-2025-0141", # https://rustsec.org/advisories/RUSTSEC-2025-0141 - bincode is unmaintained - https://git.sr.ht/~stygianentity/bincode/tree/v3.0/item/README.md ] [bans] @@ -52,18 +53,18 @@ skip = [ { name = "core-graphics-types" }, # version conflict between winit and wgpu ecosystems { name = "getrandom" }, # ring / rustls (and thus ehttp) still depend on getrandom 0.2 { name = "kurbo" }, # Old version because of resvg - { name = "quick-xml" }, # old version via wayland-scanner { name = "redox_syscall" }, # old version via winit { name = "rustc-hash" }, # Small enough { name = "thiserror" }, # ecosystem is in the process of migrating from 1.x to 2.x { name = "thiserror-impl" }, # same as above - { name = "windows-sys" }, # mostly hopeless to avoid - { name = "zerocopy" }, # Small enough + { name = "toml_datetime" }, # required while eco-system updates to toml 1.0 ] skip-tree = [ { name = "hashbrown" }, # wgpu's naga depends on 0.16, accesskit depends on 0.15 { name = "rfd" }, # example dependency { name = "windows" }, # the ecosystem is currently transitioning from 0.58 to 0.61 + { name = "phf" }, # mime_guess2, unicode_names2 -> 0.11.3; accesskit -> 0.13.1 + { name = "windows-sys" }, # mostly hopeless to avoid ] diff --git a/tests/egui_tests/tests/snapshots/atom_letter_spacing.png b/tests/egui_tests/tests/snapshots/atom_letter_spacing.png new file mode 100644 index 000000000..89fba254e --- /dev/null +++ b/tests/egui_tests/tests/snapshots/atom_letter_spacing.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4aaf541ed0245777c802d31f01edb0cc4e53ebd2f4444e094336c180b98091d3 +size 2221 diff --git a/tests/egui_tests/tests/snapshots/grow_all.png b/tests/egui_tests/tests/snapshots/grow_all.png index 3e5208fe0..89b96aba7 100644 --- a/tests/egui_tests/tests/snapshots/grow_all.png +++ b/tests/egui_tests/tests/snapshots/grow_all.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b91ae9e626d885b049d80dc9421275e147f4a3501c21ff4740b0f59d9c2998b +oid sha256:83c3e19004462b793a5929f60f8b81a795c57529bfc74c6e87890aa4b9b8d939 size 13930 diff --git a/tests/egui_tests/tests/snapshots/layout/atoms_image.png b/tests/egui_tests/tests/snapshots/layout/atoms_image.png index 200ea6476..acfdb810c 100644 --- a/tests/egui_tests/tests/snapshots/layout/atoms_image.png +++ b/tests/egui_tests/tests/snapshots/layout/atoms_image.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2e236f71e26e1a96acf9cd135b5db3a9cb0df374b87c3e283023dd14df193411 -size 369870 +oid sha256:0f65b7221ac74991c526b68ad2469f42801f6083c9acead5bc923fd856a6311d +size 368614 diff --git a/tests/egui_tests/tests/snapshots/layout/atoms_minimal.png b/tests/egui_tests/tests/snapshots/layout/atoms_minimal.png index 3c982b37e..3e37969f7 100644 --- a/tests/egui_tests/tests/snapshots/layout/atoms_minimal.png +++ b/tests/egui_tests/tests/snapshots/layout/atoms_minimal.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:096ec8246969f85cfa0cb8d58731be9aaf82b7dac70dc064ec999b1eed25e1ef -size 368552 +oid sha256:97b26c9abaf655fa5ef0625b8bc61042291a8ea18ecc89ea16abd3be6368c006 +size 367314 diff --git a/tests/egui_tests/tests/snapshots/layout/atoms_multi_grow.png b/tests/egui_tests/tests/snapshots/layout/atoms_multi_grow.png index 664e23a9b..54a8a3e1c 100644 --- a/tests/egui_tests/tests/snapshots/layout/atoms_multi_grow.png +++ b/tests/egui_tests/tests/snapshots/layout/atoms_multi_grow.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0813583ca9658b5f27f3585e59f829b71c86061619d7f61a16cc2ccf0906a322 -size 291213 +oid sha256:47b09261afe84892cdb169cb99ae59c49f671e68b3e99fc170e304de9b2bf526 +size 290633 diff --git a/tests/egui_tests/tests/snapshots/layout/button.png b/tests/egui_tests/tests/snapshots/layout/button.png index 21449927d..635858aba 100644 --- a/tests/egui_tests/tests/snapshots/layout/button.png +++ b/tests/egui_tests/tests/snapshots/layout/button.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e822c2324268d6e6168f9510aa1caec94df38dd0c163afcdecad11f2b1740936 -size 314449 +oid sha256:3cbc6f95073cbbb26729d287e5fe073c76e8bddee7eef95b431a873522234297 +size 313244 diff --git a/tests/egui_tests/tests/snapshots/layout/button_image.png b/tests/egui_tests/tests/snapshots/layout/button_image.png index 4ee6cffa2..79cda64a2 100644 --- a/tests/egui_tests/tests/snapshots/layout/button_image.png +++ b/tests/egui_tests/tests/snapshots/layout/button_image.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:682dd89e15ee289a87a592c93ac2b9ec3172cd4fedcc02072c0516a9ae9ecd64 -size 335687 +oid sha256:f89cc5b17821c9f30f7a086bb37668e4e7913705d42c0678fb0f42c527abb868 +size 334498 diff --git a/tests/egui_tests/tests/snapshots/layout/button_image_shortcut.png b/tests/egui_tests/tests/snapshots/layout/button_image_shortcut.png index 5b74267e1..b244a86dc 100644 --- a/tests/egui_tests/tests/snapshots/layout/button_image_shortcut.png +++ b/tests/egui_tests/tests/snapshots/layout/button_image_shortcut.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e2d22c9e7fd701be1dc1581635cdfa2829e02db9c6f66bf54eac106ebd7344a3 -size 421041 +oid sha256:b7f87fb417453a98e7059535cb68b12549d65f8da7cedf7a48e7154686931e16 +size 419858 diff --git a/tests/egui_tests/tests/snapshots/layout/checkbox.png b/tests/egui_tests/tests/snapshots/layout/checkbox.png index c1e993885..766aedaca 100644 --- a/tests/egui_tests/tests/snapshots/layout/checkbox.png +++ b/tests/egui_tests/tests/snapshots/layout/checkbox.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee91ad31d625930c55ae4ac41011f2018ef11ba20cefe5686b7338671fd6c32e -size 389522 +oid sha256:2f17fe1f7b2cccaa8991559218a7f13f61e459dc8443cf0fe2d24df7e9bd2eea +size 388959 diff --git a/tests/egui_tests/tests/snapshots/layout/checkbox_checked.png b/tests/egui_tests/tests/snapshots/layout/checkbox_checked.png index 4b972d966..8f79ec659 100644 --- a/tests/egui_tests/tests/snapshots/layout/checkbox_checked.png +++ b/tests/egui_tests/tests/snapshots/layout/checkbox_checked.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bcb5e0ec12a4bb7aba8ca8b53622fb2c204411ec66d7745bdb06e01bd1ffc731 -size 417596 +oid sha256:552a4d4933768ea1ee2323e7946f74f9ddd7e2f7b7c6d9f94bb92c8e7dd230a4 +size 416630 diff --git a/tests/egui_tests/tests/snapshots/layout/drag_value.png b/tests/egui_tests/tests/snapshots/layout/drag_value.png index 44bf0bfcb..bfe289a61 100644 --- a/tests/egui_tests/tests/snapshots/layout/drag_value.png +++ b/tests/egui_tests/tests/snapshots/layout/drag_value.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b2cd4d27748e193d4f46ad7a5be6ff411ad3152b4fd546c0dc98dd3bb5333d93 -size 236090 +oid sha256:339772a7974a2136b222697af2dd6e0202295d78e0720645204feb3c291481af +size 263181 diff --git a/tests/egui_tests/tests/snapshots/layout/radio.png b/tests/egui_tests/tests/snapshots/layout/radio.png index d3930768e..2fbd917a8 100644 --- a/tests/egui_tests/tests/snapshots/layout/radio.png +++ b/tests/egui_tests/tests/snapshots/layout/radio.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c15ece11f5c45d4bb89096a4d7146032e109fd9a099f2f37641e2676f7c3e184 -size 327971 +oid sha256:275c5358d3cfcbae7dfbeae4eac6606e2f394023837da492adc85934a972203e +size 325936 diff --git a/tests/egui_tests/tests/snapshots/layout/radio_checked.png b/tests/egui_tests/tests/snapshots/layout/radio_checked.png index c2d12eb98..e95932c0d 100644 --- a/tests/egui_tests/tests/snapshots/layout/radio_checked.png +++ b/tests/egui_tests/tests/snapshots/layout/radio_checked.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5942409a24177f84e067bcb488d8f976a0a6ad432f9f8603be2fdd4269d79efa -size 347946 +oid sha256:8bde6a904873ec2ffd7a194b820f3d76db5cacb3c266f3cb99f1c77ca2bd69fb +size 346473 diff --git a/tests/egui_tests/tests/snapshots/layout/selectable_value.png b/tests/egui_tests/tests/snapshots/layout/selectable_value.png index e2ea0c1f4..fd2daeeb0 100644 --- a/tests/egui_tests/tests/snapshots/layout/selectable_value.png +++ b/tests/egui_tests/tests/snapshots/layout/selectable_value.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c082417d4f65be1efc6c040d2acaf02d899ceaa547ba86f530e1d2e94f4e385 -size 389160 +oid sha256:e0b87d78fce32144f1c694beb637461cb70b9127346c90d0276a877db0700291 +size 387935 diff --git a/tests/egui_tests/tests/snapshots/layout/selectable_value_selected.png b/tests/egui_tests/tests/snapshots/layout/selectable_value_selected.png index 2a2553a30..8ce768dae 100644 --- a/tests/egui_tests/tests/snapshots/layout/selectable_value_selected.png +++ b/tests/egui_tests/tests/snapshots/layout/selectable_value_selected.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7edb1db196e1a6c740503d976f5f8e4dd9d3d4dd07e8391ce77f01f411cae315 -size 402030 +oid sha256:77d4dd1a05771c25af933398d4f118e5e21a31b2e4db66161cf054fb1d7ebe24 +size 400911 diff --git a/tests/egui_tests/tests/snapshots/layout/slider.png b/tests/egui_tests/tests/snapshots/layout/slider.png index b7d9edcd5..83da462b7 100644 --- a/tests/egui_tests/tests/snapshots/layout/slider.png +++ b/tests/egui_tests/tests/snapshots/layout/slider.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8bd1515d5c4045f4cd1b5d0c4f48469bd7e3ce738a95f741e9254e02ea28185 -size 276004 +oid sha256:b4071301c08f980ee26d914e4a4724b3f46f1113c62495483d9b0df980d8cbcd +size 274770 diff --git a/tests/egui_tests/tests/snapshots/layout/text_edit.png b/tests/egui_tests/tests/snapshots/layout/text_edit.png index 379b33806..cfbaefd41 100644 --- a/tests/egui_tests/tests/snapshots/layout/text_edit.png +++ b/tests/egui_tests/tests/snapshots/layout/text_edit.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:61dde59ee92a1c22aba7fd8decf62d88d1ed81c10cd969ce65c451185f7ca58b -size 221618 +oid sha256:30de3e9f9645206e33fa1edd841b48228e154d0ceae962c64c060a66eecd73ba +size 220452 diff --git a/tests/egui_tests/tests/snapshots/layout/text_edit_clip.png b/tests/egui_tests/tests/snapshots/layout/text_edit_clip.png index ccc29355f..8ea5d0b7a 100644 --- a/tests/egui_tests/tests/snapshots/layout/text_edit_clip.png +++ b/tests/egui_tests/tests/snapshots/layout/text_edit_clip.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c2a7ad1a4568f0ed7f203453697982603fad8b7e9852b4193216ebff1624671d -size 384210 +oid sha256:a9f36b8623d2d9c35e337e973f547166f62a5daae757c462b1482babdd42c941 +size 383051 diff --git a/tests/egui_tests/tests/snapshots/layout/text_edit_no_clip.png b/tests/egui_tests/tests/snapshots/layout/text_edit_no_clip.png index 9ac2cefee..e65f04b1c 100644 --- a/tests/egui_tests/tests/snapshots/layout/text_edit_no_clip.png +++ b/tests/egui_tests/tests/snapshots/layout/text_edit_no_clip.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:54a2f4004a71af18ffc42bba723a69855af4913ddedd8185688a59f9967e5a13 -size 509495 +oid sha256:aed677ddda9544258ddc58ed602655f6a62ab2d1d8342accd025593bbcb25e2f +size 506926 diff --git a/tests/egui_tests/tests/snapshots/layout/text_edit_placeholder_clip.png b/tests/egui_tests/tests/snapshots/layout/text_edit_placeholder_clip.png index e74e0f928..0a84f42db 100644 --- a/tests/egui_tests/tests/snapshots/layout/text_edit_placeholder_clip.png +++ b/tests/egui_tests/tests/snapshots/layout/text_edit_placeholder_clip.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ab3a86f34c5cce033903cd67c1070dcc509e385e62e05358e1329968bfb1e95 -size 363693 +oid sha256:1a2734644b2fbb6f42ddab6c65a1f5d073f1f002900bbd814c1edb6184e0a9c0 +size 362521 diff --git a/tests/egui_tests/tests/snapshots/max_width.png b/tests/egui_tests/tests/snapshots/max_width.png index 6534961a8..be50f81db 100644 --- a/tests/egui_tests/tests/snapshots/max_width.png +++ b/tests/egui_tests/tests/snapshots/max_width.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea5546e2e72aa5181edfe260cf5b506a30fea8c3db049c080bafc303223ba95f +oid sha256:df3c1ba38afa30d22106d21a54621c28a0de2b98f77f4d7e398f09089286ef3e size 8367 diff --git a/tests/egui_tests/tests/snapshots/max_width_and_grow.png b/tests/egui_tests/tests/snapshots/max_width_and_grow.png index 54dddf7e8..d49489c41 100644 --- a/tests/egui_tests/tests/snapshots/max_width_and_grow.png +++ b/tests/egui_tests/tests/snapshots/max_width_and_grow.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d65a6c7e855a5476369422577d02f5e2a96814b100d7385f172fa9506189849 +oid sha256:f5919c35a3d736e0c432b3a94d6ab2a2f936f71852b94f2f95475fa6ab5281ad size 8369 diff --git a/tests/egui_tests/tests/snapshots/shrink_first_text.png b/tests/egui_tests/tests/snapshots/shrink_first_text.png index 81680a36a..a623d8b3b 100644 --- a/tests/egui_tests/tests/snapshots/shrink_first_text.png +++ b/tests/egui_tests/tests/snapshots/shrink_first_text.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:77ff29a1441d11f3b13ddaf5f6dd5f2c5781bc418887e1c2eabe00679958cba6 +oid sha256:73b1cc01da110554dd41f4e5134f5d6d34b7e2079d5ac776f40980d616481ffc size 11448 diff --git a/tests/egui_tests/tests/snapshots/shrink_last_text.png b/tests/egui_tests/tests/snapshots/shrink_last_text.png index 6f7b28c16..cda1a2add 100644 --- a/tests/egui_tests/tests/snapshots/shrink_last_text.png +++ b/tests/egui_tests/tests/snapshots/shrink_last_text.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:23923d37e4dd848b043c7118e651ddade82c0df180652d8f0dcb829b1b6245d6 -size 12009 +oid sha256:00e129a40ea9815472ab9d823a1801fbdd268bd58745cad1c1c3dd91309c61fc +size 12010 diff --git a/tests/egui_tests/tests/snapshots/sides/default_long_fit_contents.png b/tests/egui_tests/tests/snapshots/sides/default_long_fit_contents.png index 842f41171..534b55d92 100644 --- a/tests/egui_tests/tests/snapshots/sides/default_long_fit_contents.png +++ b/tests/egui_tests/tests/snapshots/sides/default_long_fit_contents.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3afbf9e4d598907f088d3f09b1cf2b70c682062f1f4b98aa98b997121f763040 -size 8802 +oid sha256:7d7c49df327cdea8cc7d6a0b7278a831574a38e8998dba0733fcae2fd44256a9 +size 8833 diff --git a/tests/egui_tests/tests/snapshots/sides/default_short_fit_contents.png b/tests/egui_tests/tests/snapshots/sides/default_short_fit_contents.png index 099d55cb5..773b93629 100644 --- a/tests/egui_tests/tests/snapshots/sides/default_short_fit_contents.png +++ b/tests/egui_tests/tests/snapshots/sides/default_short_fit_contents.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a0a38c58ae7a30256e9491bfeb1155f2df6bba2a656ed9611fa945cbe2ebdc43 -size 1242 +oid sha256:8284e4828d60a4d65e848d08a95f6fbf681b8632877e9e2961fdb7e4876ffe2c +size 1273 diff --git a/tests/egui_tests/tests/snapshots/sides/shrink_left_long_fit_contents.png b/tests/egui_tests/tests/snapshots/sides/shrink_left_long_fit_contents.png index 842f41171..534b55d92 100644 --- a/tests/egui_tests/tests/snapshots/sides/shrink_left_long_fit_contents.png +++ b/tests/egui_tests/tests/snapshots/sides/shrink_left_long_fit_contents.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3afbf9e4d598907f088d3f09b1cf2b70c682062f1f4b98aa98b997121f763040 -size 8802 +oid sha256:7d7c49df327cdea8cc7d6a0b7278a831574a38e8998dba0733fcae2fd44256a9 +size 8833 diff --git a/tests/egui_tests/tests/snapshots/sides/shrink_left_short_fit_contents.png b/tests/egui_tests/tests/snapshots/sides/shrink_left_short_fit_contents.png index 099d55cb5..773b93629 100644 --- a/tests/egui_tests/tests/snapshots/sides/shrink_left_short_fit_contents.png +++ b/tests/egui_tests/tests/snapshots/sides/shrink_left_short_fit_contents.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a0a38c58ae7a30256e9491bfeb1155f2df6bba2a656ed9611fa945cbe2ebdc43 -size 1242 +oid sha256:8284e4828d60a4d65e848d08a95f6fbf681b8632877e9e2961fdb7e4876ffe2c +size 1273 diff --git a/tests/egui_tests/tests/snapshots/sides/shrink_right_long_fit_contents.png b/tests/egui_tests/tests/snapshots/sides/shrink_right_long_fit_contents.png index 842f41171..534b55d92 100644 --- a/tests/egui_tests/tests/snapshots/sides/shrink_right_long_fit_contents.png +++ b/tests/egui_tests/tests/snapshots/sides/shrink_right_long_fit_contents.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3afbf9e4d598907f088d3f09b1cf2b70c682062f1f4b98aa98b997121f763040 -size 8802 +oid sha256:7d7c49df327cdea8cc7d6a0b7278a831574a38e8998dba0733fcae2fd44256a9 +size 8833 diff --git a/tests/egui_tests/tests/snapshots/sides/shrink_right_short_fit_contents.png b/tests/egui_tests/tests/snapshots/sides/shrink_right_short_fit_contents.png index 099d55cb5..773b93629 100644 --- a/tests/egui_tests/tests/snapshots/sides/shrink_right_short_fit_contents.png +++ b/tests/egui_tests/tests/snapshots/sides/shrink_right_short_fit_contents.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a0a38c58ae7a30256e9491bfeb1155f2df6bba2a656ed9611fa945cbe2ebdc43 -size 1242 +oid sha256:8284e4828d60a4d65e848d08a95f6fbf681b8632877e9e2961fdb7e4876ffe2c +size 1273 diff --git a/tests/egui_tests/tests/snapshots/sides/wrap_left_long_fit_contents.png b/tests/egui_tests/tests/snapshots/sides/wrap_left_long_fit_contents.png index 842f41171..534b55d92 100644 --- a/tests/egui_tests/tests/snapshots/sides/wrap_left_long_fit_contents.png +++ b/tests/egui_tests/tests/snapshots/sides/wrap_left_long_fit_contents.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3afbf9e4d598907f088d3f09b1cf2b70c682062f1f4b98aa98b997121f763040 -size 8802 +oid sha256:7d7c49df327cdea8cc7d6a0b7278a831574a38e8998dba0733fcae2fd44256a9 +size 8833 diff --git a/tests/egui_tests/tests/snapshots/sides/wrap_left_short_fit_contents.png b/tests/egui_tests/tests/snapshots/sides/wrap_left_short_fit_contents.png index 099d55cb5..773b93629 100644 --- a/tests/egui_tests/tests/snapshots/sides/wrap_left_short_fit_contents.png +++ b/tests/egui_tests/tests/snapshots/sides/wrap_left_short_fit_contents.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a0a38c58ae7a30256e9491bfeb1155f2df6bba2a656ed9611fa945cbe2ebdc43 -size 1242 +oid sha256:8284e4828d60a4d65e848d08a95f6fbf681b8632877e9e2961fdb7e4876ffe2c +size 1273 diff --git a/tests/egui_tests/tests/snapshots/sides/wrap_right_long_fit_contents.png b/tests/egui_tests/tests/snapshots/sides/wrap_right_long_fit_contents.png index 842f41171..534b55d92 100644 --- a/tests/egui_tests/tests/snapshots/sides/wrap_right_long_fit_contents.png +++ b/tests/egui_tests/tests/snapshots/sides/wrap_right_long_fit_contents.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3afbf9e4d598907f088d3f09b1cf2b70c682062f1f4b98aa98b997121f763040 -size 8802 +oid sha256:7d7c49df327cdea8cc7d6a0b7278a831574a38e8998dba0733fcae2fd44256a9 +size 8833 diff --git a/tests/egui_tests/tests/snapshots/sides/wrap_right_short_fit_contents.png b/tests/egui_tests/tests/snapshots/sides/wrap_right_short_fit_contents.png index 099d55cb5..773b93629 100644 --- a/tests/egui_tests/tests/snapshots/sides/wrap_right_short_fit_contents.png +++ b/tests/egui_tests/tests/snapshots/sides/wrap_right_short_fit_contents.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a0a38c58ae7a30256e9491bfeb1155f2df6bba2a656ed9611fa945cbe2ebdc43 -size 1242 +oid sha256:8284e4828d60a4d65e848d08a95f6fbf681b8632877e9e2961fdb7e4876ffe2c +size 1273 diff --git a/tests/egui_tests/tests/snapshots/size_max_size.png b/tests/egui_tests/tests/snapshots/size_max_size.png index 12b526287..499259fd4 100644 --- a/tests/egui_tests/tests/snapshots/size_max_size.png +++ b/tests/egui_tests/tests/snapshots/size_max_size.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f2d9b0884adb89f598dd0c7eb421c0c8e8bcdaa1cbca02f4646c777711a005c2 +oid sha256:11a987f7376f8a3174958a8c21bece8bfb7ec284077940d87038271717d2c397 size 8655 diff --git a/tests/egui_tests/tests/snapshots/text_edit_rtl_0.png b/tests/egui_tests/tests/snapshots/text_edit_rtl_0.png index 440e1e818..3b87786e8 100644 --- a/tests/egui_tests/tests/snapshots/text_edit_rtl_0.png +++ b/tests/egui_tests/tests/snapshots/text_edit_rtl_0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ddd940c1ed581471b1448c04296d48b829939025980479a122edfe9b9bd0321e -size 2321 +oid sha256:ce357224c2e1cf32f96b3d075dc070c4d14e9aaca1b8165d0ba98603dff19c1b +size 2324 diff --git a/tests/egui_tests/tests/snapshots/text_edit_rtl_1.png b/tests/egui_tests/tests/snapshots/text_edit_rtl_1.png index d0f6cb316..5d9aada78 100644 --- a/tests/egui_tests/tests/snapshots/text_edit_rtl_1.png +++ b/tests/egui_tests/tests/snapshots/text_edit_rtl_1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a53c074ad4cb1f0e38d6d7144cb661a2b68d809203bfca636ff5a60d8582a651 -size 2288 +oid sha256:d91c715ac66be329cac42ff7c7726348b0ac79d897c414bbde26bb0115781577 +size 2289 diff --git a/tests/egui_tests/tests/snapshots/text_edit_rtl_2.png b/tests/egui_tests/tests/snapshots/text_edit_rtl_2.png index e618bf8dd..cd6d5a621 100644 --- a/tests/egui_tests/tests/snapshots/text_edit_rtl_2.png +++ b/tests/egui_tests/tests/snapshots/text_edit_rtl_2.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a9be0d364374237ea9c3cbfc3703f47f4345d81cecbdcf6c4b49688c4c282ad +oid sha256:5cfdd6255aba92f0253571bde4f22afc8e0fb5fe3cb882946459d623c0a5d89c size 2982 diff --git a/tests/egui_tests/tests/snapshots/visuals/button.png b/tests/egui_tests/tests/snapshots/visuals/button.png index ffe31f06a..e6978c70f 100644 --- a/tests/egui_tests/tests/snapshots/visuals/button.png +++ b/tests/egui_tests/tests/snapshots/visuals/button.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ec81a46d44402d5e709d825e42de99a2f7b9707f77dc5f94e277ae9fd77b6fae -size 10903 +oid sha256:9ed487544a84f9f128af550030bc7fe8a960bc70897b38f7c858440a42b6ce44 +size 11197 diff --git a/tests/egui_tests/tests/snapshots/visuals/button_image.png b/tests/egui_tests/tests/snapshots/visuals/button_image.png index 162712f09..9d48cf0d9 100644 --- a/tests/egui_tests/tests/snapshots/visuals/button_image.png +++ b/tests/egui_tests/tests/snapshots/visuals/button_image.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6a3eb59ff208d106c3808265d4bef10d80b634f74d99476c3541d997b30bc56 -size 11967 +oid sha256:31111fd16534107d74c03b798f53a013741bc1c83cf4bb24e374e4c3cac13af3 +size 11783 diff --git a/tests/egui_tests/tests/snapshots/visuals/button_image_shortcut.png b/tests/egui_tests/tests/snapshots/visuals/button_image_shortcut.png index 4027ef68b..4848b0781 100644 --- a/tests/egui_tests/tests/snapshots/visuals/button_image_shortcut.png +++ b/tests/egui_tests/tests/snapshots/visuals/button_image_shortcut.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:176df42437cc8f6f97bba4b0f9bea72c9359f69e66abfa289d0701814f8ad258 -size 14746 +oid sha256:42cbc8f8740f56ce45c356262d9b872e3973844ce552c6c09e3c07425c3f86b6 +size 14835 diff --git a/tests/egui_tests/tests/snapshots/visuals/button_image_shortcut_selected.png b/tests/egui_tests/tests/snapshots/visuals/button_image_shortcut_selected.png index b742787c0..8c30d3145 100644 --- a/tests/egui_tests/tests/snapshots/visuals/button_image_shortcut_selected.png +++ b/tests/egui_tests/tests/snapshots/visuals/button_image_shortcut_selected.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5003e67baed533e485448b953b616804047b9da25d2758b288c96e65d7f37b0f -size 14323 +oid sha256:11fdd4bde01102e7998defcaa80c1105ec9418152314c74ee028b692b26c6be8 +size 14407 diff --git a/tests/egui_tests/tests/snapshots/visuals/drag_value.png b/tests/egui_tests/tests/snapshots/visuals/drag_value.png index 2b04df5a5..8be9c5e9f 100644 --- a/tests/egui_tests/tests/snapshots/visuals/drag_value.png +++ b/tests/egui_tests/tests/snapshots/visuals/drag_value.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d42e002c3fd34f96d58ddfd4d2f91cf1ac7755ff71b5da315be4bee6bf00e03 -size 8411 +oid sha256:71daf8a33d277075012bf1130d7820574fe0286080154810d8d398c005a65127 +size 9037 diff --git a/tests/egui_tests/tests/snapshots/visuals/radio.png b/tests/egui_tests/tests/snapshots/visuals/radio.png index 4da91f2b3..3762b912a 100644 --- a/tests/egui_tests/tests/snapshots/visuals/radio.png +++ b/tests/egui_tests/tests/snapshots/visuals/radio.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d54388fca5de500aafde4efc73df5cc15a72ce5443c9f527ffb70430c08e60e9 -size 11871 +oid sha256:5e645507bdb755dc8c30e2c3ebaa1ca1a30fb9d52395d3eb70854be8ec8203da +size 11791 diff --git a/tests/egui_tests/tests/snapshots/visuals/radio_checked.png b/tests/egui_tests/tests/snapshots/visuals/radio_checked.png index f91c4a549..9c62da854 100644 --- a/tests/egui_tests/tests/snapshots/visuals/radio_checked.png +++ b/tests/egui_tests/tests/snapshots/visuals/radio_checked.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8aec08c983f71cf4fa81e88ba1751de1253d9ba6d28692b824912ad4764471bc -size 12563 +oid sha256:33fb05197d6ae6f56d79263db49fcbb9534ec5552e4712ee5dfd0c1f76dc745e +size 12483 diff --git a/tests/egui_tests/tests/snapshots/visuals/selectable_value.png b/tests/egui_tests/tests/snapshots/visuals/selectable_value.png index 88069ed9d..2f3192a74 100644 --- a/tests/egui_tests/tests/snapshots/visuals/selectable_value.png +++ b/tests/egui_tests/tests/snapshots/visuals/selectable_value.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79f8fd0df269a45ee5a8cb6ddd1501a562e92de2bec15ab28016ceb2834c3c90 -size 13908 +oid sha256:23d1cddf87ea10d6735403ea0b2a16811d4f92246415633d393c991c3bfab2a1 +size 13716 diff --git a/tests/egui_tests/tests/snapshots/visuals/selectable_value_selected.png b/tests/egui_tests/tests/snapshots/visuals/selectable_value_selected.png index 2ab268abb..66f3df875 100644 --- a/tests/egui_tests/tests/snapshots/visuals/selectable_value_selected.png +++ b/tests/egui_tests/tests/snapshots/visuals/selectable_value_selected.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:966c55de5786c1ad0165e0cca29481d51770e23173b5dda14e40013669d3db6d -size 13889 +oid sha256:e8ac8bbdf9331dbe4244aa2964adf9f49ab8981b899aee9f3200b2799cdf7bc0 +size 13731 diff --git a/tests/egui_tests/tests/snapshots/visuals/text_edit_no_clip.png b/tests/egui_tests/tests/snapshots/visuals/text_edit_no_clip.png index dbfa8856a..f02b65693 100644 --- a/tests/egui_tests/tests/snapshots/visuals/text_edit_no_clip.png +++ b/tests/egui_tests/tests/snapshots/visuals/text_edit_no_clip.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b1533e0ebaf0bd161e651d21ed81d36e8f0be06003357d9e5091ce2db4df5b7d -size 21517 +oid sha256:f6cf861a5c1682add50f9bdee4672e5fcaf882329566097faecab5312ac509b7 +size 21419 diff --git a/tests/egui_tests/tests/test_atoms.rs b/tests/egui_tests/tests/test_atoms.rs index 6f4b694e6..f6e9df14a 100644 --- a/tests/egui_tests/tests/test_atoms.rs +++ b/tests/egui_tests/tests/test_atoms.rs @@ -119,3 +119,24 @@ fn test_button_shortcut_text() { harness.snapshot("button_shortcut"); } + +/// Tests the spacing between galleys. +/// All of these should look the same. +#[test] +fn test_atom_letter_spacing() { + use egui::AtomLayout; + + let mut harness = HarnessBuilder::default().build_ui(|ui| { + ui.add(AtomLayout::new("1.00x").gap(0.0)); + ui.add(AtomLayout::new(("1.00", "x")).gap(0.0)); + ui.horizontal(|ui| { + ui.spacing_mut().item_spacing.x = 0.0; + ui.label("1.00"); + ui.label("x"); + }); + }); + harness.run(); + harness.fit_contents(); + + harness.snapshot("atom_letter_spacing"); +} diff --git a/tests/egui_tests/tests/test_widgets.rs b/tests/egui_tests/tests/test_widgets.rs index a53ad4a0c..5ef98c8a8 100644 --- a/tests/egui_tests/tests/test_widgets.rs +++ b/tests/egui_tests/tests/test_widgets.rs @@ -4,8 +4,9 @@ use egui::accesskit::Role; use egui::load::SizedTexture; use egui::{ Align, AtomExt as _, AtomLayout, Button, Color32, ColorImage, Direction, DragValue, Event, - Grid, IntoAtoms as _, Layout, PointerButton, Response, Slider, Stroke, StrokeKind, TextEdit, - TextWrapMode, TextureHandle, TextureOptions, Ui, UiBuilder, Vec2, Widget as _, include_image, + Grid, IntoAtoms as _, Layout, PointerButton, Response, RichText, Slider, Stroke, StrokeKind, + TextEdit, TextWrapMode, TextureHandle, TextureOptions, Ui, UiBuilder, Vec2, Widget as _, + include_image, }; use egui_kittest::kittest::{Queryable as _, by}; use egui_kittest::{Harness, Node, SnapshotResult, SnapshotResults}; @@ -74,7 +75,11 @@ fn widget_tests() { test_widget( "drag_value", - |ui| DragValue::new(&mut 12.0).ui(ui), + |ui| { + DragValue::new(&mut 12.0) + .suffix(RichText::new(" px").weak().small()) + .ui(ui) + }, &mut results, ); diff --git a/tests/test_background_logic/Cargo.toml b/tests/test_background_logic/Cargo.toml new file mode 100644 index 000000000..92985d5ee --- /dev/null +++ b/tests/test_background_logic/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "test_background_logic" +version = "0.1.0" +license = "MIT OR Apache-2.0" +edition = "2024" +rust-version = "1.92" +publish = false + +[lints] +workspace = true + +[dependencies] +eframe = { workspace = true, features = ["default"] } +env_logger = { workspace = true, features = ["auto-color", "humantime"] } diff --git a/tests/test_background_logic/src/main.rs b/tests/test_background_logic/src/main.rs new file mode 100644 index 000000000..ea80cfa9e --- /dev/null +++ b/tests/test_background_logic/src/main.rs @@ -0,0 +1,64 @@ +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +#![expect(rustdoc::missing_crate_level_docs)] +#![allow(clippy::print_stderr)] + +use std::time::Duration; + +use eframe::egui::{self, ViewportInfo}; + +fn main() { + env_logger::init(); + + let _ = eframe::run_native( + "Background Logic Test", + eframe::NativeOptions { + viewport: egui::ViewportBuilder::default().with_inner_size([400.0, 200.0]), + ..Default::default() + }, + Box::new(|_cc| Ok(Box::new(App))), + ); +} + +struct App; + +impl eframe::App for App { + fn logic(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + eprintln!("App::logic called {}", viewport_info(ctx)); + ctx.request_repaint_after(Duration::from_secs(1)); + } + + fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) { + eprintln!("App::ui called {}", viewport_info(ui.ctx())); + ui.centered_and_justified(|ui| { + ui.heading("Minimize this window"); + }); + } +} + +fn viewport_info(ctx: &egui::Context) -> String { + ctx.input(|i| { + let ViewportInfo { + minimized, + focused, + occluded, + .. + } = i.viewport(); + + let visible = i.viewport().visible(); + + let mut s = String::new(); + + let flags = [ + ("focused", focused), + ("occluded", occluded), + ("minimized", minimized), + ("visible", &visible), + ]; + for (name, value) in flags { + if let Some(value) = value { + s += &format!(" {name}={value}"); + } + } + s + }) +}