From 91a1e6f23e4cb52c2c01dd5d454f9854f03c793e Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 3 Feb 2026 11:05:37 +0100 Subject: [PATCH 01/31] Ignore that bincode is unmaintained --- deny.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/deny.toml b/deny.toml index 1b3bd8b12..e07d476fa 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] From 67d87233fff7ea97b363c009c8212359f1d88cf6 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 3 Feb 2026 14:48:04 +0100 Subject: [PATCH 02/31] use #[track_caller] in kitdiff --- crates/egui_kittest/src/snapshot.rs | 1 + 1 file changed, 1 insertion(+) 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() { From e33050f14bafbb7421c3fc2997d648b3c5ac7c16 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 4 Feb 2026 16:59:12 +0100 Subject: [PATCH 03/31] Update bytes crate --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a99c0d281..d774fa806 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -698,9 +698,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" From f1cde5aab495824da6cf8e9d3c4b87a8d007a6f1 Mon Sep 17 00:00:00 2001 From: Roman Popov Date: Wed, 4 Feb 2026 08:52:39 -0800 Subject: [PATCH 04/31] Fix `CentralPanel::show_inside_dyn` to round `panel_rect` (#7868) While using CentralPanel::show_inside_dyn inside egui_tiles I've noticed that sometimes it renders "Unaligned" message with certain tile positions. Match SidePanel behavior by calling round_ui() on the panel rect. image --------- Co-authored-by: Emil Ernerfeldt --- crates/egui/src/containers/panel.rs | 2 +- crates/egui/src/context.rs | 2 +- crates/egui/src/placer.rs | 2 ++ crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png | 4 ++-- 4 files changed, 6 insertions(+), 4 deletions(-) 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/context.rs b/crates/egui/src/context.rs index e28048edf..2665d5edd 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()), ); { 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_demo_lib/tests/snapshots/demos/Misc Demos.png b/crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png index a12313e97..95c172d26 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:56b44d26946770c0878e11e3197633697ad339a7e8fcffe7279a6b4c45cd3582 +size 65384 From 3cd52881b4237ca362a21b2c975adde51a7df35a Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 10 Feb 2026 11:57:43 +0100 Subject: [PATCH 05/31] Update crate --- Cargo.lock | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d774fa806..9173deaff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1155,9 +1155,9 @@ 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", ] @@ -2870,9 +2870,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" @@ -4490,30 +4490,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", From 64a96ef3917d4e305a79fa5f460af5fc73eabcf8 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 10 Feb 2026 12:24:30 +0100 Subject: [PATCH 06/31] Stop ctrl+arrow etc from moving focus (#7897) Previously any pressing of arrow keys would move the focus, but now we check that there are no modifier keys pressed down --- crates/egui/src/memory/mod.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/crates/egui/src/memory/mod.rs b/crates/egui/src/memory/mod.rs index 9c91c1322..fbc8e6f68 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, } { From 08f3fd2dc1051c47315437877a01effe6fd84e15 Mon Sep 17 00:00:00 2001 From: Lucas Meurer Date: Tue, 17 Feb 2026 11:08:18 +0100 Subject: [PATCH 07/31] Fix scroll area not consuming scroll events (#7904) This fixes scrolling in a nested scroll area also scrolling the outer scroll area https://github.com/user-attachments/assets/ade40b1e-c974-4806-8045-881bea590d4a --- crates/egui/src/containers/scroll_area.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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; } }); From e0bac4e2604bdb3937ff8e846b218bdb5cb1dd36 Mon Sep 17 00:00:00 2001 From: Lucas Meurer Date: Tue, 24 Feb 2026 11:07:55 +0100 Subject: [PATCH 08/31] Pass in an explicit id in `UiBuilder`, to avoid wrapping passed in ids with Id::new() (#7925) I was really confused why I couldn't find the response for my ui with explicit id. Turns out the id I passed in was wrapped by `Id::new` --- crates/egui/src/ui_builder.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/egui/src/ui_builder.rs b/crates/egui/src/ui_builder.rs index 686fdcb47..87786a726 100644 --- a/crates/egui/src/ui_builder.rs +++ b/crates/egui/src/ui_builder.rs @@ -54,8 +54,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 } From fd257b2e95972f2bfe08cbde710f248076a08ee6 Mon Sep 17 00:00:00 2001 From: Lucas Meurer Date: Tue, 24 Feb 2026 14:27:45 +0100 Subject: [PATCH 09/31] Use `FnMut` in `__run_test_ui` (#7933) Matches the `__run_test_ctx` implementation --- crates/egui/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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| { From c89a4d1b38460cd58e25db5761355b837e57b719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uma=C4=B5o?= <107099960+umajho@users.noreply.github.com> Date: Thu, 26 Feb 2026 17:09:43 +0800 Subject: [PATCH 10/31] Fix TextEdit demo consuming cmd+Y while not being focused (#7846) The demo should check whether its `TextEdit` is focused before consuming cmd+Y, in case there are other demos that do the same and come after it. (I found this while experimenting with my own PoC binding and replicating this demo.) | | | |-|-| | before | ![before](https://github.com/user-attachments/assets/7b89b511-473d-43a2-82d2-7c4e732b2f23) | | after | ![after](https://github.com/user-attachments/assets/0b82a092-1603-476c-9f1b-2558afcd29c2) | * Closes N/A * [x] I have followed the instructions in the PR template --- crates/egui_demo_lib/src/demo/text_edit.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 _; From 4f99b4fd8d73a7439d25a38f74bbb25279d2667e Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson Date: Mon, 2 Mar 2026 08:52:16 +0100 Subject: [PATCH 11/31] Update selected dependencies (#7920) * [x] I have followed the instructions in the PR template A number of separate commits to possibly easily revert some of them. General idea: selectively update dependencies to remove transitive dependencies and multiple versions etc. As well as updating "major" (the one that `cargo update` doesn't update) version for some in Cargo.toml. Rendering pipelines in `vello_cpu` wasn't obvious. Now both are used. --- Cargo.lock | 390 ++++++++++++--------------------- Cargo.toml | 12 +- crates/egui_kittest/Cargo.toml | 2 +- deny.toml | 3 +- 4 files changed, 151 insertions(+), 256 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9173deaff..9140f2160 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -138,7 +138,7 @@ dependencies = [ "once_cell", "serde", "version_check", - "zerocopy 0.8.27", + "zerocopy", ] [[package]] @@ -312,28 +312,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 +349,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 +379,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" @@ -1164,9 +1120,9 @@ dependencies = [ [[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", @@ -1780,9 +1736,9 @@ 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", ] @@ -1845,32 +1801,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 +1828,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 +1839,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", ] @@ -2132,9 +2076,9 @@ 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 = [ "foldhash 0.2.0", ] @@ -2381,23 +2325,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]] @@ -2568,9 +2501,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 +2606,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" @@ -2806,7 +2739,7 @@ dependencies = [ "cfg_aliases", "codespan-reporting", "half", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "hexf-parse", "indexmap", "libm", @@ -2851,9 +2784,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", @@ -3310,13 +3243,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", ] @@ -3413,12 +3346,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 +3365,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 +3460,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 +3475,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 +3561,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 +3671,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 +3759,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 +3926,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 +4007,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 +4031,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 +4105,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", @@ -4582,51 +4489,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", ] @@ -4801,15 +4723,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" @@ -4858,13 +4773,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", @@ -4873,11 +4800,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", ] @@ -5071,12 +4999,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", ] @@ -5154,7 +5082,7 @@ dependencies = [ "cfg-if", "cfg_aliases", "document-features", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "js-sys", "log", "naga", @@ -5185,7 +5113,7 @@ dependencies = [ "bytemuck", "cfg_aliases", "document-features", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "indexmap", "log", "naga", @@ -5262,7 +5190,7 @@ dependencies = [ "gpu-alloc", "gpu-allocator", "gpu-descriptor", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "js-sys", "khronos-egl", "libc", @@ -5851,16 +5779,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" @@ -5931,13 +5849,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", @@ -5954,12 +5871,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", @@ -5991,9 +5907,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", @@ -6006,57 +5922,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]] @@ -6130,6 +6023,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" @@ -6147,15 +6046,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", @@ -6163,9 +6060,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", @@ -6176,14 +6073,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..aaf9b9398 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,7 +84,7 @@ 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 } @@ -119,24 +119,24 @@ 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 } 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/deny.toml b/deny.toml index e07d476fa..e740494fb 100644 --- a/deny.toml +++ b/deny.toml @@ -53,13 +53,12 @@ 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 = "toml_datetime" }, # required while eco-system updates to toml 1.0 { name = "windows-sys" }, # mostly hopeless to avoid - { name = "zerocopy" }, # Small enough ] skip-tree = [ { name = "hashbrown" }, # wgpu's naga depends on 0.16, accesskit depends on 0.15 From bd636471774fda847e2a6eaa92a72600428a16cc Mon Sep 17 00:00:00 2001 From: Carter Schmidt <35572862+Fyrecean@users.noreply.github.com> Date: Sun, 1 Mar 2026 23:54:47 -0800 Subject: [PATCH 12/31] Fix crash when dragging a DragValue through small floats. (#7939) Increased smart_aim `NUM_DECIMALS` from 15 to 16 to fix crash in `best_in_range_f64` The f64 value 0.09999999999999995, when multiplied by `scale_factor` and rounded, becomes 16 digits (999999999999999.5 -> 1000000000000000) and the leading 1 is clipped off by `to_decimal_string` resulting in all 0s and triggering the debug_assert! message "Bug in smart aim code" * Closes * [x] I have followed the instructions in the PR template --- crates/emath/src/smart_aim.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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"); From e505d98215f80764dec2b8017fad9535a24ff799 Mon Sep 17 00:00:00 2001 From: Jiayi Zhuang Date: Mon, 2 Mar 2026 15:59:29 +0800 Subject: [PATCH 13/31] Fix: update get_proc_address to use Arc for better ownership management (#7922) * [x] I have followed the instructions in the PR template `get_proc_address` was introduced in #4145, but its lifetime was designed to be tied to the lifetime `'s` of `CreationContext`. This means that using `get_proc_address` outside the lifetime of `CreationContext` is undefined behavior. This contradicts the original intent behind introducing `get_proc_address`, as this API is intended for integration with external libraries that cannot easily guarantee alignment with egui's lifetimes. This PR changes the type of `get_proc_address` from a reference to an `Arc`, decoupling its lifetime from `CreationContext` to achieve safer memory management. --- crates/eframe/src/epi.rs | 3 ++- crates/eframe/src/native/glow_integration.rs | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) 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/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index 1cd49449f..233ce5a49 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()), From 4e43e6575682cca2a5a891936051d00ecd4bf8c5 Mon Sep 17 00:00:00 2001 From: Jhynjhiruu Date: Mon, 2 Mar 2026 08:04:41 +0000 Subject: [PATCH 14/31] Fix emoji icon font (#7940) * Closes (again) * [x] I have followed the instructions in the PR template Short and simple PR, just moves the updated font to the right place. I note that the license for that font says copyright 2014, which might need to be updated to reflect when the font was modified. --- .../epaint_default_fonts/emoji-icon-font.ttf | Bin 324132 -> 0 bytes .../fonts/emoji-icon-font.ttf | Bin 317864 -> 324132 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 crates/epaint_default_fonts/emoji-icon-font.ttf 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 0f29dfed814a752e23d019a6c55a864175d5e1ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 324132 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Ê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

~lcKp~1uF-@Zu8_5 z($V_7Qz9PGl|oeVa2BlwiN+vNB)o7vvl zwHCI!JA2z8e;HtwOOvfVK7Yl|r+j2p8FG_>tBmib2WJ#zq>h028fLiLjniU9B3d2Y@zt z<@J+j?*suJ@{>eo;SM8!a=sNbo1JcH-qsutq7?^X4G~MFxy9hGv)6zRcs$hXV2<*b zo@XT$MeNzl)$1&c6kmaSP@a-VS)3YY@+S#NU!oxaj1>_xw7yV;WU0~&LoE;wEdT=% zQ4FAZGrYsvlr2xq;ddUFhIvflY*oq@+wF7@gT@T}#00o3SSYy+%a@>K?P7uBf34H4 z;&OhXoWwgK+iaoS?Xb|Hdqv&Ini;5PdXb}&Bdbu}|%~uFxTP(;P zBZEV`=AlN30&^3otoRF1)okTBs7ARf%tQCX+Yy+w0W(v0$?TzKx?Ct>hfNu!nVBj& zki}=ro__iE(o(O4VSjbv=#l=hD=)ib@#0H1r`xS!i6NaY7I6s)z7cH`#urV?iHW%b z*RHf$L9#JjC=hQ{iphMfUa3q~`nW$+CxN_~eC`myOjh~?r3{J- z3(eB(Y_C@>Vp9ltS*YNsE(iaS=j0&IyeyT&sX0`bmT$Yf8zjr6rKQPk9hGY^oLcTq zFcb^r5>o&NCbYLwtyY3$vsr-%Oofehy`=tR@4FVNPTL_WszJTh$dyZZ_0%9mnlHv0{Zo$3L3{V0pEDumJ9-tq8FJG%Muvy83dtu+kBc}?*L^k}} za3eL(xx#8BhZ+jkBC#4`sl`>3Sh4&{EQkzxL2{2TH2>L}ft8D-O9!|2iQU6A1C5R#DM zOx;RzOe&pLA2*F)oqx!qnUH3J>h{Lgwn@?vEJ~He)PcuQD*U(h`%R+?zm(a|6=KT( zNyjDPWWOI1UB@!kku23ziQN5`REf}H9J&c`5vPH~k%Av-=p@5VECd+(W9Nz3hwz{> zZBHdm4?1|?95e7g4 za&``KkY6*5K?sKl(&-X1xvaCs`ceI2s$Z6er=Bh9BkS>~)#j?t9l%Jd$}bn?`{=cA ziMhuHFzD6jwN|LfhI3N(HPfHQqTONBbO6S&20E*;B>Ft@um4o}M+3%^B-E~S$Bh9d zbvR+b&S2~yN(iy1kt{y^9Z9rM=D=JD6FY6*;*^zWOogqAVk(AAhxP+oi?mPZqxDUk zleSb#@INe6$cre%%25^49?-!uVlWDnSX5?D65i=uR#;=@SjS{3uNuX1i*I3vW}&s$ zC9nfXgcAk>nVl(vTPB$|*btT5-nXLJ`fX)IQo{vvO--tdIz@e`e0za*Nf@Q}K*LDN zq62YW5Kbu9l$C(WycFpq8M=ddgbi*@Tx_kkFL8t97=4Bq6L)7HyD>0;L?BYLBSCHB zsevMZ4B_!xk-IK9I<5Jrq)8phUKZr~sG#^B+zCVW#BQQuESoewK&pL9=pfAdZbMs* zx3z+3y(|Pl1%M1M5x*2n4(lS_>cexPRDurZSBcj=?_jwh$D(o-M3@%54l_%oOYM@+^ zH$0*c^$_AR4I_kR;6-GLq@WW2xh%GjMwG}&gO4EgY{(GarbYl`W2Y3A3*BHX6UsFH zDc689#K`;rqKnCv{B^?~uNY<{ft45G_>)$mjX?&g%Ah2X<@A+FWYlFgHq~w*GzA;s z%Am0B7U-0M+>9n^I`LDj*2q^ez)E={9Q+;YMuSLwDC1{M2`X0hXex_IAirfbG}^0v zr>QSgMj!5MECW1s>mX$~W;_QuRg}iNpZOJ#}G&q#Egftk&M$^U=ZfpG(?3DUNL(>*%wkem&_J3h?_B;C4*={EkQyJ z-KvQWvdopF;Xw~EUdR7NBk!*Qxi1n-2s2#N*g@>3Vak{?tO$PVQ9#oe!K7U9CZ$?K zSWDQx7J^t?BUL|7g&!(rL8PnY*2Cr}4-Nnc3)fqIvr@`+nWqkX+5-5=FqnQQ8WDBq``Au|mKAfKB$N09+y%8|l ze2;BuGqs;iXo-$pjL1 zMtf1H=1kq2Z{Kp~;V>APEehFa9uZWY;amBRsa#Qv*!CFyMg0O#e7QxhfZtE7La>RL zH2oWzERKNJ&72SzhMg;0K>XL4MPL>!9Nr?QedC=ux*r=SYKUb}A@RMy8`=~?)D3%Z zh%UfGCBlhk=xtMv7&d_$kR!sFGU*f%#-my&Tq;DKf_4E~9kCULk8uCi;0Gr*@*@&A zM$vdhsw+ZDN(x|=D1Q2qT0Pu)hnxO)jKpZLlc7wHbI=i`JtnSRdX|(di$`% z6dQ+U{N6Q+T+?HxgUQLh7lG5M`WVUnbfhlgOaUVV4JJ1p@#z*R<){>unE>@S>! zvd6M%D3jh{Z-Dgh4}W#pBCTShOM8?SYd);nh-pNu*;Wg!sm%iUidJ|w8SN9cX<6CO zC|-s=Rwh1xXQheSfvD5Y#H->1l-oB_BfgYE3$<4|sC@HI1f6Pv?zAFFqdB6S^%JT^ zB`H3KvhZJ)GmaeefH6{Hf$C~6C$mSbS&3PG}l9A$tv)XbkIQG6!bvc?B1BR;713fPs45l3y~=W? z+t@gxLshp*afzM07)o?K(D(jKnS5cN(&oI z48Wi*z$T$5e+?T{qQNYX$!8cLosV^l6mme9Ua`mg(p%9aV_-WKt*2a!05W*pnt!@@ zL0(U2o>A<=Ep)r2&Y&VIM|oU=n!a=pc!#<)e7FEe_~&1QIaLAhR-=X>YfyCJ4uc|m zsVxq_?$j_fhDK7BAx#58XrMyf@lAEsMsaBZQ;rVxk;?^6r0gWdno53EF9>--?=VS( zE{&Av6ICiWXpq(A&@&T+0x=$>!mZ1hk2!Y||Kfas>du=pNbjst4;`(F@m2C5L?TTV zH)go8@gYz#m&#zzZllzE1Oc+J3k(2kh75CEF@sX#1!JalkU@!Z6gC-hl4)+X=<|KK{DoO@nj8;8c$z$!lX88a*EMmxB#CD zp~sjV`lq#+By}KTn?EdW8n!|a-!zh(>^$1c>=;5%m_cs2Q^w!uWJNXMmPp%$-l20- z#583jsAR!y#o2qbm(wkC!K0-H>o4-tt8!<>VDKGWb5gw`jU~B3C%0-@8!l-5=oKZ041@nx%}W+EZ0 z=w8s8tCp>h#=+d;R8P-Qk;OQ4=?vEVVeI)j=uK#+EegQOrS2T$KbU zRv7o%ej|8UEv!8KBoh};O*p3Y=Z6EFizgBl=3SEbRj*0BB=JMUgnuV7mVYks?-Re3 zI2TL=R|L;bUJ>r#+143t7bzghutDyJBTFVsh`4W5_H9V}e3Tu6iy9c=Zp-AefY)vG zYHV9k6zHNM<@fpQY@V~ek}M!eVIEK?S*UG0JN{#jgyHWM&)~na!x#9?R2dZj7du=w z986%3+TqZ-S9FYXb}s;uVPpw6`4keh5-2&$#`pOfn=n^OVPfuxTt`-?V0zkWRU*U+v-C#ZV=)<$I$THa&H`OL)#>f zd}-hfOuhu=kqWL2nY~H55J$Tt*R8fD(279KqQM$k%;lQ70xF64TGBdXiyY57WmXwh zYCHN|6d0&>JNTl#MN~<>^U;?y8YS~vK5NF{}hwb=gReU%!O?vSE z#7X5iX*Eu1Kqb{`aw>(3);jPR8C9#Acl12&%H_ytODm|(g+%b_M$3HgF~(n39@J&^ zTB{kIh;+HEGawSHHCE6hNB$GUeZ-eGWC7lty0t()Kb@LmRsZGo#^mJg zcoexo1R`Wc1&*ssrkvtEBesm>;|nv?e?KI zdDS+^*JKkvTB}Xp{yGO5*g4Q)+JrOtUbVK^ETcO=UvIBZOdM%74zadQ5bduzU2d(^ zYEuO`d@c3q!fdm#)}<6Wpmm_d1+a?A0Tf}fbGXwv%plPQb>N!GsYM41cu^{vnYsRO z7P`2M8Wqsin%dbv=G+D@wI;SEdfUB;!wu1gxzzjgPOb78?Jw*;47M^_<{V+Bi@5Bg z`c$PT3+Xc9P0MhHMm89snY3XqE$f`^U5Yfx7KZD5Y3VY|=`$w9G&*w32+UJNJi}w+ zr?W6aNz#ByAJCV6KneKmW}LF=4`62Eln)__tjw3>fx>uF4GJ3stx7hY`M}@%HD-uWm2jciZ^p=>A-y(8&^sGq(nO6(E>4P zq%&A8JgV{HG*x)n`ua7Y`d){*bR3h3TL^Snr$E-DLkI*A=AzIP%tUO2 zm@`l-_;4K!^VeLe)@I7ZsbZ;CEEHzyg<5+D*eJ+s+p zHaH9^=trm*P$cdWgcFU{^DbO6IknLaP$8>nf>CA2fl?{r`IsSLUa~zpQ$&hF4ms_@ zGG(L?4;b4FQ@$W034~DTp;(9V;zSK9s@N;5Ql8R$CY6xpDj2pFnn4LgOPa`W6r)*u zR+`8%xhfTjL<%89%k7P+sY@1}jq9CSn7^d^GIe)>@I2r?Q^mnvnEdXk)AREuTF^!L zMEl(Dq969&#GQ#>OZ*?fWN=&Xn&6$mCxS1+otP{?(xS30BO(A`M`8oUh>-9-d5jlf z(-_}3qFvX|A1KOp)ZIv-2;T+}4*TTYzT@)kJ5{_XNu=(lIF^AeHR2YATW!35d;NU- z0YE#wU?4{O=Jxr7j`X=q@yF})z1eqhD-lz4C(hV=rIogTU$7W70vuurJZzz-D<2m^ zb=PHbS5yYOpA;-X4q-t~CTgA|0&IjXrvr9Y38{KiCu-FwM(i+yTs4A6IgqeZfrxZ; z8p~-fsgj+N#*^AV{viBYHqrGdV->>usiQ|9xcu@5jvhT#(CV2^POakwu2riaoz&pf z0$0ps8^yvzdGgfa!nLa_*DfxeoGPP~YX+7k)D4r9N9*O@D7U% zr>AvVtp%6=oxAsrlk7U{yzAausVnDPU8%dPdwQl*b(qjSL7F6utr=x2jx9^FWI2M3 zZPr0fcx78gmKS4eg0PKu7eofzBqOj*dSQ8OV0YQY;57?tYsy2}Yf#oxuh*5pIQ)0-pJPYOpq$ssxn9h%EiS&p3l zR$IyD3eNG@E$&@g+q?L>;||NUQm;{^xagB`>Z)YgX+$=XAD6l$+vBfWj#rX~R%SAt zEOcJU=ZmUu{5dID%nb8dh?23a5=k>U3d|%caYkxzo7X#5ug?j4Wm2hyN@XEs0)8TM z_4=`%RT60>JO`weL~CQ3h>A;yo!E#nB+ml z`Utgwr&7$mR>X{rBl9nF=Qt_&GO-6Dxo}W=Qmdt(G$zA^S(02U5l^w)U`x9;O~@QK z3zwU{N z1gVh zjgX+g*np?_YJwj!F{zUZBJ(_uZzq#kl%mx1^M+sT9V(R?X44Q)(#&diggulkrDYOA zm&<$d{4{9N$=31ySG%ElsdS(dE~e8ng~Dt)T?`M0+37?)NgTm+Ha9xT{YEMgZxc&v zaVL?_9HkoUuuLM}AQUkX&uACIX~YRmH;Pm^FX2^YM0#JTrQlYH453n#w;@z&l%jUJ zcsH}(B^X6*HIL7r(Shv{`yj4HSi769J)KW=atAU0)Vb4ft z;0A2OR00ksqC84Gg4j)hVta+cVp;Lvoz(Er#C)~7-4R8CWJq0A+C$Q7a-WK%CGtpv zs_K{24Kzrg5b1mtN?fBcbCcIPP^r!(Qk5wp7pA7FspF8V>D=V9une~tRAz9ezbs(U z&+uM?mZ%p4_CzDH1TBcPY6deQ96h;gK*xX|O2Z5Ht)nZ?1#%ak4aB{__Ffy94V(zv8+cvd2`tz(^fu2BH@N0* zyZ5!r7vUcLVtef0k=j<lj?S>_m*ZeRV5h8nn$E zA&1DWEr4uLoVK&bI415p%zWRR@>c9AbPn|}vB z0F9w%wDvN4aEE=<5%l=}J`>P$RR}?)%Cnb1laX8oR|TE^Mz1(<_DklBda1l(r4YPw zcwI`0OmYG%Gs(}fGYBVvA1&ts_azjieaHrk1dYIrwa}tHBsj39cb#Np{h8MNqlL?U zn|s_hLypM!AyZu_O(1kEn@abI%A?R;BvwtOI{ETyt-e{WZ`EpRrF@6X*)h_mb3?hG zN@wF!v3eSrTGo#=By9i=VcgqULdEu!oF6KJRcEt@Xr2qs;K(8v1#HJ#$S22n_MIRP zF^t0HywG0nT2vjNon984nVZWGZ_3Y6_aK$)3Kx0FW5PtSkQ8`vuaO5S#v>qcMih)k z;J0KMjR&ilsgi~c>V?3m=L(ADMR0A3FvDzx0dpgz573#QD8!eA)`DKXj(UVr(MarI z!(*=~qBL{dQa-=l%(lo_gQUwCy}Af#(0iZozT-CQqMPJ&NwqW0^?bez?hXeRjjnFG zT07BqqWu%q8f9}CdU5zWJJ#C#z>aD}Wg=(og2(b?_Y_IWC$BhR>Yf|AWUNaB1x};dWVOSTy&qcl+EuD%1SrXv~#;yVrM*K2zVGZf@jrT~u)KP$89U=Sux_ zDhsTW-M^hq)#D`2F@MDrIowg*Y1FP@w3KTnQ-u&LMy8v~Z#1X3>;0{9szHNf~Mx6ku55{o}Q2^T7w3)!Oksm7Cy062oVX@H}$pU3bCD z(8dwxt;_=49SZg+E1W36F`1vqDb_zx44)K(R##~h0B zX+Vs26BhJe1s9 zt8Nq}JV20O5ZqD^b<>6aL5@=LphEbRTOe9W#miFdK;+PXi1CN_KypGTrJh?E9T@ol z;^@Hg({agq@#*|`CEJ;7HzR2$9g(o5^afNgQj|zYg@r3|wNA)!q?G!DJVC?=IKm-> zIaA`HnUQwOD9I3z@KcOrr*eDB@D){3OYEW23AxooaglYCZR-3cc>#!;L_+LHup;EW z26=h)QhBFKZ7tMJ;5}?+Q<=0TmyA~M2$>9#GRnRsV^bm4+b)->qY`fyo(dUH17u!f zX;V+2K%^3M_?SadauFL}5iUgk6Rye62B!$mii*O43<^{VN`yK}e6;6=AJyZ=zX3ci z1t#;4G_I9XrE_s47!qLPBql-`<*CV{qS6x)Kl=u@WJO=iI0Fo^3X}OqZ$vo7a;#Y2Fx~PX1Jy zOThH(-c&P1vNI~k13)63R7@&Urg^ z;x)N(z3-7yVry@_nSuzyLzV~i3vY9j28hZyaa0p3u2xiGH1wkeL_8@%@+A5~ZHd>5 zSip1eqo^_A@4{147)Mdv!ByU>G;k5;94rjony09Hucu_xfLzf_A_Ajunro#kz!@qlzrK}U4nET=E z>D1zMX&yC*N3~DjjIy89vo6IAoUxXMj{ukwT0Zkfcv(`b&(z`h=hn*Q?H)`hB6-H# z0-#A@U=tuKwG_q~?t{Bh9GLnrhG9VGxQ47blk(NRv60QiQ{r?i%kC}Qpm;>gQi3im z87c@tqfAS90-5tL%4REtoEKRXVb%f5k5T?;X6(X9Pa1S?HY$W3{y*Vu%2!H;^fE2gxpy59o(D-nWpMVClx79#bm6Pgw02YH$gQl`PRzL&D_zS?4!QJzP7ri0s$RU zjp&FpTt-M^`-pg${SN1>xf?wPLf0l10^*-=@JkYWF-bs_H0qn5+}b>HWOM7un~nJA zMIa@fTR$-~d#ahL;Qzwnv7|B%Aty?whMNezBZTP^q_>i4o|>IGQQOg^ZC*BgF+2~^ zN@8P+8|I$OFw?oxzCxaSp{$$v`9vt6NKR+j&m=(?Yfii@4|Be48!w&i;<8Plfa z$>t0;uxc8+X$90vK2KW~zioMMd!r48!uKdY;>d+etoDZ8$lc?;CpM1V9rnYCHvAfx zJgYVh?y+t54c@p)=hj?eZ?xT*&duX?l#`vi>-}6PR49Z(xxS?SX0uj3(f>UU&a0Zq zK!dVK;yO-Go8xRU(adD3@MWGiahyu#qzlL;;s|IQ0w<4naIe zvI}rC0GQf1dZ|!|k(wb)S4*XX-Oiz6soJen_ZLh1E7k7(aoJgc)FA1QI3}Mgn}* z^}lVq#-L!kW*GMP4GD|^3~1an`^FsKs_sftQPbTOU4yIW+C|ZBx>gdP*p%Sk3w&eX zSG>pFPXqsV1zrP50%V|gr2jOy{1UQfE?a3*kY{eVgj84|4$!nD&L>fm)Y1(c zd;MT=Q=@UyVDS25lgFP^f@Mk!)|4z44On$hs}0DbT$(&DzCm0>yIw!NuyDFwZ;Qo{ zhwJCaXH-w)4;52pqk1N-BlpuYo%Y>ZTX(lRGrLD`#~dO}9lDxYLb@8IK`N#u&w}VA zIpn6)N2{gYrYIbZLD_TW(sdGB@tN#S^*Mg};(zxZ^gbSV#(4*`JF!9SHr@T+#I3>3 z<)z=;;X66*-aArhi`0&617q|t$u}5x2r$@$t<(kr+a>f#wHcK?jvy|Mp;a z{?v$bUO6kviF^$TD)C9Fh9Qw5H&-SPo1wj-G|o$cDmd#Y>PtdO5pc;0Yv>-_D-|Fm zp+Tc%!oqXSb*E5(EvDq$1gQeOuA}B0wNAWg(ObqQayhmw_I-=>EuwU^|H%mCYal1>B&2urbZlj?U4X15n*hIv}0HC8xSEiJ|z(|8;#E@gO z^>8PWkC`bi={SYy45q(>o%FOUhe{$CC#*^`I*zP}!zoG&7myiEpo?C_b1G%Wsnwj> zcoCu9am%SxbIP(Lx{l9kJ(Ha)L`VjX^4pG#VKH3I4vdQ!AIfgynud#nCJYHDmtbF` zAU*jo#R}4JeuTofUawl+D(cNKXd;gCX`k-)4izK>ke(Y1kWHA_nyl)QlB7f-E*|re zMmmC>anfZ(0@e}-7fM0aGjd5O#NAwNA(_IB5tpCjV0wC++;POm5QHTjD1>KYnsUL@ zs>bXE%b83!p1XHv`<(~OsH3oo@p9TKx={k?q$vm+CGA}rs|XyOTvhW^2^?2&6Gi+o z)rFWDnoGpo#EHFoU)V3U$W*5{m&lB&f|w+^E5gyGBBc6`7oxF}a(?Tr`5b-y#>U+< z;Gd%xpYi^{dj{_2B>0EK0tgBAdqQC7XW46@34{T5J0gbBQ0dm$?0~XBViGp824x?@ z7Un%wr`Ymrg{*s1y-F#UTdF*8N2hb=gLnSmwa0HN97-lTrw5(Rk;F{h^E#Ca?W2z- z%f`?ftt1~k>O8tVGqb%mJ-t?$!9}N(rckmQsNc1J|C@HE_w4!L>vj35VW6ERjcdVq zw9rc?({0|R!@KaGeV4r9%?9>+f|``GmIN_q#GaL|amO=txkK3=6@`rBSJu2dg^~s@ z{Aaz2`{het#SL8fD#I&Y#r0ohIC>S{B@#$@U-w=E)qUZFWXV+bypAWWp|3cc)LdHT zGg7vtouA`M-ueVya>eOvbc){B(SNnF+NL4*}G_7~j`U3kd7F*}W&-f@OM=G-9nBQEDg>b6`&7OC)u zotyA$1!{MDlfow*8vf9SrM5~(cAvQLGjDp6;Y%)F41CP{YxhfmrNEKEIcBIh*cg;M zTLb>>MJ#_|%DfzQc2Ia58|CVt8b~4iU&Jy?i_>_{LEX1pA!h?-%{uTfzFP5Ae{gr-)w(wyI*?dt)KbK zTc5e`J?2qbIoLjbe)sr$E)4Gn&6B}WjhsI}dCW_?&LbCp#eJ9eX?&-y8Nq7C7_gx9lO*C4dGzGYT(gMU5OhpsTVsWilTrCxw|8WgD zmm0}8-xkNX6Lj~3PD9>n3|jRb%}^A%K}JhXl6EoL$Yf_SqKP1sa5?v*7kCGH(dB>4Y0eh-P$G`bY>%+fDL8SDD-aNPQrMEAj zr-MogAsaF%jbre_j~vE=mdawhn=2j0?!^7!NIt1G4gA8zU-6ExrjY^1vZ`g{QG@@Q4LRv1A4HC%(#{S>100QC-4!z=*0eE^308 z3aK=tY$CZ{X_t?*t@)!Cyq8uDkIB1ejEboQtZ9#rH_}gW`y{Jpqjo6wVsu@g`tE9H zMG1st52AY1irgT!obSjY?uSEY+EXZRk!CAMoTo7HqxkPvtLu5KZakcY4n-1%_b^`W zu~2qUuCyKU`(`2vJ~RQqp(!`LztLQhQ8XMs{gSa`9DchsluO!0Fg!4eiZFp+;0PoO z2q`%%8xN8$J3P8~#1)g*O3{!KPMX}|8h#;k$a;4w?84kh9$cwYLBDVYvGA7?irlm;C;) z`%CY6c8}-%$5%y$zw0AI4ANrkP|Bx9aWRV@jm|~Q&>M;e_rQUByPdh=%4JRLK6crW!M*v5e@+gvw*=Aw$=X&k zW@TIC$k_=@!s}0j6hWMKTNFz%6xk(2bXZUb@kO-440SvXo@=;`P8T zcV`qch#WhGCY1g@MMT$DZ|2s;`4i3NJ|cmnAC=uS9^0B6_~}zGm)bUzO{3=}3qbWyr*mp<@%wJS{R0)J`H@?1eLqRyZA`HOiw}GM+x^RYEirOa2ezIMK1&0gERe zKVx5K!BBnp+7Hu{?ZfuH+KxG@&Bm+y$wJN~g6gw`dMwpsSH^D{aa$;q z5IF=|tt==~$^_>QwWTZjTCHP5;;{95%^k*2}Wk-51eu@h$rbzEBdz{!*MH?kPIW@O=G6jB7p(+#q~ z(7^4Y3}Kn1r!cf8O3@I0aV_zFo>N-LWJbkniI4}>VLesd3q6A@h>#!J0Uozb7K%B& zl-jCfGW{ZkPZE8g*iFX<_1ePbw@Tji^3wJO7ei#+#ThCfZUWrpcG# zP1Q@^v(`@AmEU$!HEVokuUr$mRuMawz9~gqq-BH4TbEog2;X3vvVVQrz>0lt$&dk1 zgZi92WuKV+m-I4cX4bRW0;Y#-ZZMt33Obi85KBCt@oH2`OO^KKvb9iA93CYFk~7s5 zTKs~35fNUD2Fuyp-fFd$POq%ECsE6xd9SHb119)%x<(D7xI_LQTq;sB6iy!JsPiD} z2HXXxfZ8HBT@W4?c9vy51UcY8fe)|I!BSBKzTK6EOx8kKK&SBSrR)hzxqIO&|Nno7 zFISo9atOOGMHqPb0#=fAWfoCDznoI2n#=7irKplu^D^_L;=V$1wpi$=T7z5`!xl2~ zU}j~N$*(%UXXkw?hNYzjwW2CUExinSj*y}N6CO%xQ1ukNMktt$HsEb38-w$V^pO~s z>~S{!7HMqp8NxRQJt|C!;z&fxc8va(CGhY}9v|b@Gkn9pfR}K-M(W=f>gSb0p@FEP zXsJ*dU2n*t>`M|+FOirjKODyTmNq(1hq@bN4&5m6K2{RmF@Q8dHQ*0dL$jiv&39Arb6?Ly= zrvTC9JRstY2{y;N@P0Y5$Wz7GY9@};vP%eM8CNz|Wpw<-e*y0RTNZFGei?Z*g*=M? zl#K=*lX1~WqrP0V`Yb~;typCVwykSzt&8@IuK+i!kgw?Jj8#A|ao+6usZueYV#OmK z`!8+Q6>JapoT*gryzaWYD%G^~m9Wkdth7f_&Y>R4m5Qe}Hg0WAcY;fu*7R)~pUuyf ziW}V`%GB98v_Rqd4ACv&U_PCi$rP`Fv0iGmt|?NvFrBCRO+GbKCr4gpZZ@7M6uTS6 z(ri9YWzJ4AHJ>lk>xKM$DoN3+eqQt9nt6E{^Kv)2mI5IwX&4E_GBH=1CF^wcPnspe z^faB0wb@>+LCdQAlNvPBPJ@#(9(Wt7AegH_D_-l1vQU^2WfmLmYh!S z-?wioorX>z)yVij9uOS(TveikM&O)t>K0`%3Sao;#Y5R#nyfgvu#4*{F@#wC*eAIA zBn!psw-22u6q3ffPw8<(ZW6ClH;iv>BAv?}TugT5**Guf<;9~X7Z=Y)ld_AYlC?~F zmYTcKXuXy|e#+Nk=vUlSEMARM#jM*_T*QlTrkM(BK?AU5Im6a0D=5i9Th+ zo;2Tn|MrYw!cm(D^356ltxVQC7;5&uulgMwY`B|pBQ|a{^3z}`WV3pD3a3zotEbrd zHg+W){j29X=}bwDm@OTf@87+;de?maSP4T`3|PWA0!0L7@fwdM1*))8K%ZC6=LZdJ z^nA*z&Bn>S(fYn@t`X-_X}wb2n+6J@HMpUN&!ABu`3fv2;;-P{$zfS(k^u@Yq)L*w zoZ{Y+!ohkO$<^FIho}n>cFFZAbquw;Z5~Z0wbt$2xNqN$-OgGHC7)g@D7BN~sj zE6QXW3zin({c&fQYE>D1Je*FgwwuSNr{|q0e#^DAGB*$(EsBa?G`_!4FGofTrLYm9 zi-*L3GdEyhydFq@iB5r!+#pFMrSNYBm8&S9)4#S-sn;u&Yv!fD#+_QYGZMHjaE7 z?RiV8tWm{6vMLeyVDD>}e8KWk*yh2pz^M%)`Ine|(=^>{+u?Srf9$@o;r)))rtNAj z?fv6#?!C;GY`UOw@pOZRl&PeLf*@P%5X0XMEb7su)=(p5vk%F_iv< zZ5c(?-^e9e#D&Cji`DA>cAL2J#aw(klW8S#7&9H?E05!^!nT2-90hVQaYS(rrZQp+ zl%64T7c5|DxmMk5)HjuC49*ds_{?IV*rL+iRID03#}-wh4Zs%!05f;t_r+72yp)_4 zRm(Lu$cLqrU7~vIXqayqMId?F;6@)y%Iy(`lCsaFD&SV-c#A8*f)8e*{qUE_y`r4P zkQ;06hf-7&hMqQJ6?;efa)h9r!mgZTdlPPxC?SxifZMA5@A%8iF(@1n)s4C^t+lAr@`##4DJw0`%n-*!Y2MRKCb$X!1p`*F2hu2Q)$1WQS_bsFWXyO z=uG0a$tPynjg#6Ywx8MhmZP(1hi@Szvon2+WcKrp^qUb2uKmQ4lQ&VmED`tdcv2*C3d7;Nq6=_npIT_Q zZrd1q^Y->b9eLXV8?0keVYO9xE zo0GGp@_K#dP_dB5pP6Svt;4lyyH%}hH}H_lE|lwoQh63PZCu&%wdH)i5!re{tJUqc zT6b(l8~OZlRRO~F*zg;S@lxO)Ja+8AGEAsBJ3%=vQ-%Quwlx7-ifpVc%oGWx!4pwp zc=+%Dx0V#p6phJ05o>dvEt$%~#FxV}=g4!LC|cw|k%F2y9ahp6ELLfCU%@9(`| z_V?Cffxl&E*?n~7Zn-MI9J-oTI0Ve2yX_nHbd{*+F-bLm>sybs+eYCvWng)fRy5+6 zb^&rf?emzCuv^IxQR}?05jGYG+q}IB;47rZ*lF!~3|%W-o8f4eJsxeMflEGS;ufbO zvc3=*!XHjs|AviX{Ft+GDUvvcGQ*jV<*aR1ijC2cbGw5|H+SOW2+Q7{dskNOnFn4K z7zI(9>K-T-%JByt%I9*W_-wEHid)VeKmHRB)bKxT1&770`xh2=a4r)p+Ky!S6(&T; zW_u7-bEMln)ys8IF2)Ocn#~)x0e4xpWF{kUkb#)L9IF*mU+2z|PR43yIB}nOx7}vUFczjCK5*dS>CJ@1GcxO>qY83~g_BhRWkQ7Tq&GW1 z^YWMf#(Mo+r_-wJPo?&}xO!-L`N>xgpY1fKZ(J3CGNqyb=s~4TBhz)603g^CjhiyL z(P`>XC&~O3?-UCADz=^&H*lM@K2Tyb)sJS=73Idc`sM!@kQ6SnK;?temO*Jg(wacF{f)>okZ?Qax;|L;#Bc;YvHY2c{D|7-1MfK@3X{TQK}hD-tk3IZui46(6dfhTAa4e4~u8 z3I}Gu$N*qP-0NYZ*sB_}4vgpi^{XcjTc*@SDb-KKM+ zAUQ=!NEnhtkXW4ko$i*o>O60k5y2}< zED5~T(KM>Th|{l>=Pfj74x*v?a;4AvUyx*Qu-iMZ+v}$_kWcUaKG5qP)SO-5y-h0^ z1Zaaqr5479$@K>D>wn#`eS&J}xR>jJhMSv&MZ`%1@=Ej*Rm1gAW zFR3RQdW{C~n%&>JN{8I)xTY1!Ab^CRLuu<0uj7}NTA;w6)LD8hI)RpHwGE^xeVwjw zBL!hd&@6d8X|^ABbZg{yefhpxpOnCKKADJW}C@QyIK39F%-^AF!wN+rt|KAa+!zx>_#Mm&~@ z-W%f9TIkNH25G{R4i5UwY%!TEde@r%E*nF^ba&YhF8uiLf9CS}oRf4O`VG_p!AkB+ zu?n#3IBbzh?2mF4bO*WL9)4(a-(!E9CzT>{eY{=#(+pQ*+5d9k-&8Briu2I$L;O>9 zU-p%3E|+D!7BBuM?_slE*P*-nfY!@Ui7PeL`AekA-9;EJ!2lO>QCXr=?D}1z^P7~5 zy{j)4@~ppDVIBC3_vK&7!Sb^{<%_@TeJgpc z6$AZ1;5+`2_32z~;n+|9Cdl_tH^2k@oMZ3bJbulk^92%{iC}^|2nVyt(>-D{=e_u{T1CEpS(K|y7>Enly|`U z5nl#=%J~OYZ&G zkTdbazfd;9ujS$|+3=Ckz2Mh>#>b{7hqZ(eme_2x?Y580reZu`^WMC=-kxtTk$E+r z8Ryu{f_-0c<@UwF?*_$l-l7*@ZDp=Xm`7g@f`a=tbjQBo<=@=gRn21>(E#Wg4%oD; z!I+is-`x(+p5eF<9!W|a6wGK<9mi*^rLXq~?v7qwj*-!HpjZ8-^{_ z5tQ|-yNBio1xqOMsQ3hFf#yq+-{BHaaT_f++%7*c2$kn?^u`kie!-MBq$Fizl=~&} zjb!E_HH!%g5StFK@E9^a%wf7pD!Rt}{QJJ)HFMWAabXwx91BC}WmDQ)D(@}9GZ@}N ze1?5is(I=M8fKK-ces>dlrky9T7n@l*Jd z(O!{Uzj_K|GQIXr&(56gXS8^ZA6CR@c ze(DCFX{M0LBrwf!P24Oi4wJ>)8gWe%whP%->@We<04UOj-hvu_#XXn~a)N^(CDGCP z!;_oEW&ZG%BL#%%3}-}Gvu?gMC=}ZXrO-@-tN{R@4btNaio)JI6$w~#%f$9|-id}6 z;3F8D^o|5lqtlT+G8D*P{6+82yq^zL5P;UG67ikj;7PnYiG(IoqWjp!Y;zwMO>Tc* zWF{QfNX-)5!7FyV3#s_&M2x>GZH!Qq;s+;s-?FIGqNpp_Vc$k??(vOzlL~*^rgd)~ zr3VpmG8os#Mk-n75AX`S-nWKSy{L7AwS%#BmC%TihJB1^X=f5u3OyPI(awx; zV0HIKCyMBVmI63;HkOy(c?%osu5y_8Ms}qq*Cvn> zBoX-^R#P8UE)(q(B!0@_BTdl_v8HyEU>R%uhj z0XaJ2yb>J4Z!ix3jgEdE{|ADFf@k+FojJ3#bT&v8n*4kY&r|ZBR+n=5dbo3_TAk0_ zXF zY`;G{`?BNwAp48r`4VCI4+8}On!{dEjq#eiIJe^J2dy!691xm8Nd_5deSs$Q1CGhb8-xH^+VF%=R{ ziUz(VaLjwXcTM0KWY>E)bqCUdgX6aODLb{g_6i!yIql$!5(i_XkY8kOKN) zmrCKj9irX!##2FQ)wI*q=}6By*|vGn-c^%d7bnoB9gU9?or^a-wmvwtcfS9!BW72) zd#|h4rW@7ji9KY?SG=DfUrq^TuLa%f?x{Db!P!HlVg>(|YvYUiD5G~I7(TXY}0Oa4>3IX zOlD8DvY5%_a+%Cx#i&$4lO=3!C|jj(O?cOAYxvi`VRiL589@b?x$U|B-D|6N%*`Fj z^Ha{Y8D;TgAPfg0421V8(BIU0W{UHFvyO4g5bA=-!hu?vMu=gb6LHYq=5yc;wZ(k^1Q1arjjl>3R7viP4tEH7!in_f};WuNq6bqxw+*} z6v+hM4xu3@_#JxGh4^?t5XR^BMlN3@>q*v~$6+FKAE~0;P_)06!izecTI-YT6FXS* z06ASVL@vi#<-(p??V5diuc_7c6l$HRSTwU(s~wnjU*A4hsm#ROS2#%KxQPRz@ATjt0$k{L_EeLH` zL~POJm816k&Fqtt%qM-=v99(fb!C%W;Z-jTuJ(lYOX3Wqx>ooN42g4ObMyT-sjPS^ za*sG5F${1=PUe1zcR?q6zt{yyRjzx#%>t+ZaCG%0Xla}mNh+Chj`P0oN$(8`8VtV+;bSC-;Vg-*tAt^5o#*>$!$+KfZ=jEor zcy)t+CpgsPp;bOuspxR3PvUXUHgS-P9h)omSop<-5AE;(i8|NS|HXyjbZ@C z=!-FfT)jNRqY@mctT$>A_6hNhbSQ772M{R65&ii`ua`iF_o}Z8x03pwT1lP$^(W_6 zE<3MdP|OzSp6;OhxoBfhC{&yq-?zE312%hyP%jt_naDd&z`w-F`?Fdl9x?3N`*L#y zieA1?X_+ZkX?O@Ni&t2#RtD9%qos09_aieZr>{xnNQbaS@Wl`tEm*BoJ~~$&RI1B` zU!`GdU{DD$W{O@c1>i=m$UT6u=#s8N8uU>rU^3Ilztea86rV&ET_uD^Vo#9ImzPYI z@#B#V0#2y`3x+hW4g)o(3oh1f25W$ zN5MyMq|@J}Gyfdt(3SpcaQ&z8=lUWT<~rwQgP|<`@xLufnM4WCdAzxD3EG&H`zEl> zE<<$L$&myFJ`mGm^k(+vNfBuBai4=5p_4H!y6ix2q13YGGY)mNerf)#DT1mu6)ex^ zFwxv$adDP;qsqGPoEkQw)}<&+RmqWN89U?U7)f*dT09w|J-Q8VZh1clTY?Z2ucT6V za(1=lmF75SLdOyzm>`jNR0^apPRNH?cz{HFj%u>1inm#EARUD%8HW4}7ITs!^R43gqQbJtPaDXKY^a>joKPFJwl*#Wb)d8@AND&Ju z=L0`M19B3zQkD#~!@otilpUKK=x9OU-f&Y?PHc;!D5b!E6xUK#Iobjhx=#b>Y&w3z zOA$c_po>W3Q?2nB4^y_E{xG8ICWYBK?uDOl@pg%n+|TCw#Dr(af*UuHd%TB7m!tzx zKIUka401D+n9UJ?pv>ejhIEWyt-~0>l`*_tDoHtL<0omeZL{4#mKujqv1zm0TDnSJ zEk4$aMd}qDX@j%Ph5W&IxMWI}7B)0{pEl5GX-43k=`a&jN;`$jAPa(&gqI3?3dQ;2 zG>a*2mVkE?PRQsr9N8}EXZ#=CW95La4L?s=$}uZ5q$vV&0293GNPc1m1?G?&EiXLv zMaq|Yp_JsMVBq$PKj%H=eKxQac$gfpkFl?f`Bn7Bc z41Bk?r&~ulWkL{}!N}6e>XY}nx4d<6aUOh$XHucH-Dqw#n0yk3;_$fl{GNp!a&D7v zB3nAp>z&(scKCCKVnqAx*6my2nd#}Js3SZ~!2w%aFW zPkrz6p8vK5)qBCA!b~nDoR>Q7TQ+8HSz5Yp?c8JQ>&FxO;)nJvz5ev_J$v__ZLghe zP46|Ks+hgAQ?qZn?z-=|roG*0td$#NZK{Nauh5DHz2#dP&01{zY`s41e#z@J8qZ$| zR_l#h_p?@!K=tAuc>l}$-asSJ4(tTp9C%mYKL`FBD`wR@<$iI})zz{`8{UHc zwl*SLaGJ1#qjC>cvjj5qbx;lLy#^&(In-~|cE}>iT;Owp{jv?TnPlipop3Op%}{Z~ z!EHwZs##=T?6m+>w1Bo+HcCIa<8z|uQExll2{&ef#n{#H}&d0JaC#QD~f8V|Le&6Ap z)99ZH$y>C6oeQMHpgvseu1~oy-5#vJ`-U6dy*}7xV?#c{Pw0xAg<5Szf|cj4R5n|! zXRrQ1sf}sS`^h{1^6sa$w@;qj-hS%tw6Ovpp;JCCb8tnum>7Kp zMUZOKO;s^EgYwub%ZD;l<_iWpB1yCP4|YdX2JmLYo{RBEqO_q)+ZmkAh3etXLV~$`UXZZ2imb6H%fhAmBJ5 zG*Y=Hxi*z1G!$2nKCjAB=VP1nr z*@fa#jP1$1ii~lvBVtg{Ei0*%dGKM=QjyezN%Nu#RL;dsOW;=&h!=mA98#Y$Hsx;! ze90lJ)K%jUwE)F>!w@# zIAx-no^f0)hW zr%DbEY>GYSeY}rxAh+*l%@OOyC~?`9^8LI~B8_%^Zhn3a?K;(8JNf%1MrXqt=NA@E zHYr*n44qnRHcu`roZlc00MU0^rG7HeR3!#1!vrGvWvRq5H)n7(EGTJgozUW$W^)E> z0tVs`p_18Fx;za&mMqOF{QDBd(@*A3oU;kqotob+6pDk@)%V}~hI<}gF@KNW(>hSA zE)`lUOfa97R$-}HJJ1rGe*{_X0c5#4T;Gj&CXsq{wkY=%K0d%27}~M`X#r>CM=V#Q zF_djxlJy7m6gaeFA<}Xs{!_(lC+iIGIYRm|8>i_|J+Y|&IlJLK;KbctyM6b;6xQo@ zrh9&6<+iH8BK*8mi_|Ll$4jJ+?vmJmot=!-9@yG{e`WYYtyDTh`ub9-<_v$%aqfvt zk!bgCm>p8lzjk!`a^2F>!&|hTnazDPA#*Fn z>T+uM?<~w^uU}jHrtRPFY?n$^veZ-b)v9%oi$J8enm$;Ra&q_^&fvm7IKTJ@3^p_M z>60JddC0kroWlXndEnxI@EYDP1b#m79|M0r=F{vpw%StTlx-)TZ@Y!roBG%l;M?BG zH)0}Q&Di_qx{I(<(gs>-Gja)W!xMCzX$+(35eH9jd$;|z{Toi44*hQFXB5FE_W=7Rx|9YfF}hxJ@~0*NaGC_?BV^Y7z2oe6Gy(ZruoZMn!|Lr?_7fd@_F_ zDg!$CDa8pYfJbtd#6crKBQd+VbY`*YST&fs2)ajr2qjuTX~mnOh&PtG51@T>smIya ztP7HYUhk&Gy?5>5wDuU_BznF%+Tt(P9$>nJwW(K<;Za+wcM8Y?lk`@WsSd zlfAr9EEZB!Z=T6J?i#_}lX>{{h6(cRp1%0Y?uXGYpA4K~`)3XJzUX9outx?Ye9C|z zVA+3z2rN{xPqY4_;)@8p3_5nXyaR zDK0ES40l%00R%%wzxc(nbC)biA!or9k4)}6n9C_~B`rx3Vg(TQ-NZrxaC_hIKgn1T z8GfI9WBJ23)_D_CH@v8QMz)PV_x{#_}<+FiWU=P^A5cLRbgGB?1DnU}x+HlTaqB%Ts$<+VR#fLXH z?w=d(o$DeqF4|wtZ}rWuVZL#FfBsy%GaK|Ojh}apJmcNBxa#zrB*-GDeftim#USeQgJ>zeR_ssJ5XaZBv4(?dqwu}-hJPA^18PS2G1*J znn_|SNT?4Ik(fk5ci49HTSl!GuP3Rz7Aq%-*;D(Cl=C&56_VX-`&hl!p|D>I2k2BX z4?JAcR^7im%r{CGH>{Kc`382RJ;|tSmGCpT)*R~3zo3UkeJUZ{Ji6#soG7%a=jGER zT2U@r)IX?q!p40aWs*@QlVca$svQAlk@83BsDZ1Cfy1Jn>un{ z@uo9Q^vs)%y>ek;X=i=oz30xocVm5LX<^}&$8OpiD8xd7pDFL2Q$~9E5_)B2*eMVkf@Tu5>hIhX2)TyQ0kKK6c4L2UU{nU?~JNH!k zyS{$I)Y)TCOuuSp@a?DGck0)6UNvyfK5^^?H+=oOBE!$W`q!NAeD(12zZSgx`VV}| zh3~ok_QyWxzWZaWE}$v<#mA()GvovOIqbm@12@D)gq<&eRki%e)EwM%=+L?Aj$M1` z(6z^|JG3!;wezjR*Ex^1j~#moOY`>j)L^jp*s&Ms-0(-7bEE&;x9xo313S0jpH9Z$ z6>1H9-g^cuzx;E`?se{M-l}(-_c8CcN$T1O?gXz1J{Wvk@FT&WFnFP7wSV0?7)SpY zSerQR&2`DR%i{U=K}%dl*rabBUp4ZzuW}j(Z4>h~qC`dR#}o*)G7tKwgZvKW1|Qc< z982M3pS8^D=-XF4_zzg5vmeES@xYjH_3(`b^k=t;eMtgQ?N8Vf+%UDPt<1gtnxI>1Jm35Wi4VP%K zC0fP>`y4N=sj%~82V=&d$>G4xK^ckHsCJabM@tv(E;|Nt>|03DXc{%sw=;`1$Gi`-tn(~PX^s2sq${aO-@MxRRW?Q|?cZTlO+sgOT6`E9w*Ujq1tT@^ zgoOSVm66)`%)-0MTNQ?2FjwxA+TT$j@9ElAhoS#vJb))8zMo~5|%WKQzN8Whi zL>Vv2umq8oMzyFBG8z=)_{5+#3!?cLepLdQ%z=1FBk3?07z+rCc_Y0PqcWy%UYiNQ z*n|ZfS4(*V@KWZa-poioSW&nQR-M;}8JBOhY=-iK=5~2R=!_AVHRaaffcd6d^Q{?@ zlpxb0-t5YfPh)DF79;||2_Z>Dg7%U)^w!{20+Pf6~X;ox6#*)@SnK!nR%0hsxV!- z`f94o8nV2k51^V|KG-m6X-N_yTdqa~6T^c*J@#FhN&aA6;|hA&tb}fI>52Ptaw#Vm zZ;{|m32mi6ka-o|vcO6+p%>qfF6qtk*bn1|EDz;%MqSIBO_)xz7>6wY<8_R9%Klc@ zEHDGUW{Tws#V`ujvvXN8$CG*_68(Om(^N7GhZn;@(<;+ZCh5MNu+Sp02fR8Z!sFyR z?KP|+Jjp#)^P@^ z=QU?6fR>hjjbJW69c76S{?O*o)XLvCYWLYO+5YVYAWB!GFfrEL&kFF-{y#2^p?iQt z@u!*=0Vi;+mzF!UZ~)7$6%$(1Ee75UBaPRw(!1+V*Nq#3eh5T_c=*Ojp*_H#f?5c{ zuyC$xwl%KcRVoh&L7zGo7&7hP`%FyPR8oR{&9Z_!($NPwG|(gi0(Wm{voJnYXT`o^ z_l;@X?~B{n!#2N_OX(p!&fhDHTQ&!b!%hj!F_HAf8R=;4X|skpXR!nLiOV24`Ozv! zgsxdMqd`H-#DvK^%=8J=+IgT8g|jeuOus-5v;(3Lww2GjURdBDk(M@T7a(B3X2Tgr zeJvkrVHZgEGafc=6djjwx6qQ9d`@~!Q)O0BBVz!=w{oU+O}Be(`^Dk?dlp`@X~E3y zXYNs&V>OO0orWPbT%iUHbw?HdV?w=Y(fJ395X|&xGGsN9^NGNT%$Oi^gEEzaIhTwZ z++fXDT516{<54?|p52fN55H5GgS+z?d0d31Xfu_TZ$~7r3_&lJsY^&nuw=A$ni9Ry z!6HoB%cxqOiKH`jRt83b2NqK~7lA+KEZ8|TQ!dn@HW&zwC5KXQn&BjBNVnX=ic2oV zNH+OK;S&;|5K|T@WwLXbT0S?2&#V&&UcBf%>%EZUFM~>auIqDVhP9D*Cnqfj z$oI$>y*%*KWLai3Z+Wf3+KSB@H7br}dDWKBl8&rVM!Z<|{hKWRIr>G`3SVJNDmLA* zXyxfkgr!MMjL+m*#?t%s44$w@W|$$Cu6?vQjW^x^Eo@th8TMw5pzFwFN=mFq5L0UE zi8KyAVtDWwbieV4a1}m@=r4fv1vLXSORF07_MCy0kCyc{8=~>eZDKB#sW1GG4RT zO;Q$_sw1g>rGim0?F6Z)+bI>-nvG+4y(#hnSrN z&YDy39Bw@f*=(BZec(J{2%*@}`LCPjQ2E3nPVKZ3UWVJp+O3_Yhl8FOC@7E}N)~ub zJoVJtD-Y~{)q3w(qq!!9p~L?;?0Ao&De>)+k=0AAj8^ROBQ!0m9w~cHe*`EJ{))i> z=FeVf_>7YEj6TC>O0tSB&CfrwQ=HG|XR`Nx`BCSEUp{L~AniOdKfmPO+1aT#Rtr3d zfhBHgi8@CMtBv|j$K1e#W?~{OJ z1tTHK|2Yz(EHLuj19+|9@K|I6g8jGJurkeflZ<60b8uC#wL8r|YXu5>X4NzQs`0VR zXU1o{n>2GEdoT>UbA35_qH_YkeeqCF+AiETSZCQnk+}+zsfx6r>=LkZwW}!w0=F8e znnHCzOrR%70LW3wpJX^yL&k_>Z>J=1ZM9hJ#?V1er=|IsqF}2)4>BTRg4u%FNhKn; z6Vpd2H<>2>du@g$P*qtsJ?PO?C2d`UkJyG|-C}X2>W_>>f8~UDj9{v=K>#tvXY-BX=C)dBJns_SPm+)<5I&+tDu znADUA2&HUxZMJu^)f!|Zg6TiDCm9>F;2Q28e!{j|?Gf`7^M?cuN||n+?D?NEJ@ILR$?q3DUvZCh8%anUY ztlcVGnDKKpIeovTi66UYpV0T*V$tIVEr^kYvvf_!C#F)_?~E*2Y#|vpzUwiMv^5w_U9r?NJIy zR!?X5Wqx2Ckyd*X@gwEVhUc4BvmHCL z$|T9BS9c0sl@;7@*4LDCuIT-}iS#JSH?yDj{$%)`>6L6Y?>v8J=Y!W%mo^5I`LEs? z!6!d6oxbqhrCYYQzw^9PoP*%?6Tj*G2J+RvWfIRfH%XJ6H@-Tn zCP!)W_n-ZmRLiL;=ke>_cHqGI{?qLP?bh=K!#`mm9IE&T@5ckti$9O=-!BB72>e3e z_X2+hYr4-l?Y!7|Gc|ssi8YwaVj&+F85sI!9AAMiEv~aL_D?$&!n1ewR7ervHQ&PD z7UW0YlQhb=H(N8ZV*AHPbrkj=p;f<3xF3>>ijm=>uZnQ9=!8N4I;o|!jERu*RW8l? zCeVpV^U?Lj>?A%PowQ>LjaxGra>gB@Qk>{kzLcW@NvY+TA?uuUl}5#;6d{@rH?V$p zuRBjBmL5zF0;3ijrWtm@$2ag)FuE}Nr~=GYxjs^zL3k66ALavxEKr&dsCW(STmKm` z-x9clwm_811{PLN^WEOv>+et@lX#GV0aRV9#$qW_5t=LkO4rw{)~@aMudP*^N&*;r zhe<*Py%N5J?=eUJo_F>_eVr4F4WbYBEmQa8wL~H))wP0W?JeK*W_In?lZBw5fLn}!T^=Xrwj@W}4&Z?(=HTDzl7>AyFmNMtlyB0NnzCJ|~S z>eyg`Y~`|hYO_Zx(AVDtTL}Gu*RfZ`D7}O*fsnesiSku5W%@bqpZ0G$ zX9neuI~1KC513me3pjxmwCqojW9t0a>gOZ!i zH=la+OU``dUEKd}-TS9YNM6R&vZ-`Lxb67cA|v^vci{ zzw9u1YIjrB8d=MyW9~0?k5+3fi!FpOf^Mr;J=)!Oyj(8Jm;CHm`I7VF_`auXoq(+u z&CKjUNtB9PeVNszIP<-k7abIu5ef&L{Mx>=)6K3JJM#yw?>48;?%R3!^2)h3jjnHR zuhV?{+{*IHcP_lsun&7?W?r-vDW%dgCADhFi_gv-eA(jSHt7@$h7;STISX9;GBV~< zfd@@KEGr+Gi4gG{&66*vjX6`yQc%Ou$C|?w*K5K>#^>#UKf5DNK*WB-DlqH>GU1Z- zY@gP6i4_z3pIthibAa(CTxKpWcD$LaB2K7kEM>q+l-$T9O*QxJJ=&_MWU( z>gJ+HmEIuABcc=Yaa=5`>vj2yn3AQ*a!h%ht4T>I$Y@6OB+pN)<-Fp-`g#?Q%{WE{ z;&xDgd5<1Du>V81-S(mV2abswUlOj_@Il)MW?&Xiikky32)xO;+j;Q_{>66i@s|r*8H|-C77^kA`S(}tyep%~gke?7ZEVct z4ZAGdU0lVjkYKn2j%U{0H>_Hdo3*ENF9$)et=qTkpoJmI*h3TyZ#H5KcdY8dA06X? z`cKsliC1Z(gE)u&n{Zrl1QWyOLiWP20F*4whg* z=WHNf+Ub&}dVSy?m&pxfGsJr;7%>4{Y+eYQ=e=7JmHs$k*$aWA^pWT*NpSLRr_fwV zroxE~K(w5xESE}isYE1|UTxj|nO5tqf9M?em3sY4FFUP7v9Mjl;N=bD=WFY+SHgl& z-P&+40KLH7_QxJTy9z5}Fo2QlwA}$8Bybx9XBidyfI$)XNe{k4V?H% zhK%6?MS?m3l*M4uH;OHz%CIV4os%G`au5Q4Q@22V`9!ekG`DMT8~oo8rZV@^{~c1q zPbnJAZz_}3xgBuN+^rU}WIYqG;RM~7s&n0Ryl1V~;cM3~? z@z%HfR<``>k3a6D_YON#SzM`lf zQf=`7(ZWc|XzyVJJ?BNT%0;pJm_Q4&+3l8B8-(wQm-92`0}2|vSi@kQzN8vN<1M|7 zc||RinPp%EK2tEF9{9jT^58vYrg%%~JtZn|8_+``@UeeCcdL@EO?bC5J|t-ON^_!1n}ATO?DSf7@39(R$c@MPK5?+RF+p!-g^z00;yA>y{qO7cSJ+0=9}L^ zZ}RNG+`ZIjTwklXPnZ@j>*ZgM9`u6uFPrCSA{ttrJigzY+U@K~(`rV!GUTo}70gT? zo3TAQEImYp6~r7HKNx8M*`OeZXhyPb>$5}`|g*Pd>5@t zxZMW6W4O=cSts+pN6OQV($03OPKWKtO-NM{kix9*M{ZRsok@gLy!s{Y%f^5ndG`IX zXX8o^isVlgQ*6v+^?4-$WT?YsmC;`$IO}@PzMnUH!?W)KdB4(g|M0cnaN#eUj~V4Q zQyB{n?=fa&*=54c4RVj*kmiOzwtL>|V7<~bS-%$S)fb+CY-6b09YF{Wf1FufC2}4M zUU-Rd`1n8Uop*p-SAFm2+&jJZnVl`OGqbz1yV|PNYSp!}m1Jai{P#wq z*>l^u=bn4c@BHfb_cG^pD|orAQkVba=6QV&X`Xzr^oTk84@|MWQ%b_i1^B<;>#+`C z^a**oJge3D>RRv*ek#7nT;ReS`>YsHpwUzRCsyh87H-ogm+}OHT`DA*^6?T^H?AlmE~K zSFjV_Oa>c+#;A;h%`#uDN`i!P0<}qnLIZ_)6J8EpfN*Vi5-g?c6m^r~9yEp3r^|JC zO;Lq#7E-CXm|8eh2D9cebVJQK`UcC|VJhX$ZX={JQC&GHC#d_%ZHnfs zg$4?<1anP4>1N7#d^Mk`A70ousIPv0S}iUIVuFBym7W%=s%jF|((#eXVBUCI-5VlK zn_64H&69;o)XG4*LSB}|V>seyld zSS@rTPuE?fo~4T<-f!XHm+S@m{pefn_xzm)y8_f)PYTy-HKWZYE@>}h4vmmmHtS|< zEw^lU8EC2nT(-_k)Ry=~!CbJ6_cJHWc9Ik=66P|qHC#6-x8}7_64%qawSO8_wwa}7 zLJn?F(d@ zgf#m&PKF{0Foofd1|)W?9Oo2XwuH9X4{Sk$o=J0qL`4GEG+v`vL$NLq#+AaO&Dt#J zYcVSUAYL%sI8&)hEFz1#Vp^(_^$AE$>r1FlWjLXz#ruVXKUiHak&*XyqR`|6_@%n5 zG?_~0a;fx0$$63UCt{d0fxSpAh%CZp#PND%kg4;}7g+_*AR^gtM&-Hu-uC%euDHaE zaLp#F6Yy@pS53N2B51Q>u?0Zs1QF}Fvz`l*$poj3nHOUS?8Ez26CHQ*$h^eCrjO{C zm*@x85B+-Pk%V08-UZx5GOt_Wz!kuUgvDB*NU58ZGU@UAy6rv0RF%Ob*hVxM3*9pI z!d)xTDfwzF2TexAa4gMJ-C(y2i$Ka(;tl^=2njel>D2ah_D9!kPp2ZP@$}+i;{p4> z+dRV(MswD5z0MzFU$=D69q!~+Bp(|bEaV4;5S%eKc86Z|t!vsTJR0|E#=ttxDX8zu z`VSLu@Mq}a?&g^)#K#=VtgWToIp((3XWF@uU_P@*8JJynoabt^o+L9hvlhCuJ!ijp z&6*sE-IB?8d@#3kss7{cvfdx7=5w>KI8+Z+3= z{a-rs%)1*;TLaDi_9LiKVu8b4I^%p-^)09dEUIOSSrx^~Cr7yGnxhexZ6bPojm#Wg#Vo{1^t>@H2+tG32wJagCZ9a@6?X8w;k8 zKofyL=!Ryvt=y>gs}MzFM9-n9DoQBjCq8r<|x-XHlUBx@y+w8sbqqn&+!`o@t&`F9L2|Q)|t6 z<&IFI&bvyxrrfGbP1Ywb{Lwpq;HX1g|)%vaoBn@G}NZsZ`e{5>_I< zc5v|IYISWqVZ{?`2XF2g2!*Y1cmTUo7kH2%Dd!6_Mb#T7U&%;b+%knP6Ml@u#CSwN zXa#8-&P83Ly;w9JLRFtAJ2_rnCq8N>&CMo#b+nex1ODHx%&STh*=$+zGqcmVN7gR6 z0XX#LI$gDrq$wi>Rv=Vat92vF>vi4wGOe7m;viaXHuv|b6xC+>BeEK}4UZ1w^sO;> zq7D-#(0!mc4qn;qPaV?L1B3b%IA7O&T|Bhki(P;l zcY}jvx`f7(IV*{YRZJ_=YQNoW`hIY0cWG4M0c5Z4n)pz$xUs5xx@ayh&*njBF&FA5 zkCe07sUH2>)UjwRI2qq6UB7v0)L$g>wqGVmAK>@c2R~~!-1mH|=iih^ zVL5&FcUt+4@3b>a*5wzLu?dKaHu=6GnI%B7xKf2L`qdtiNtWefHPxi zXZwt$H`$jrzAK`y!op3~& z*y$Ve5Y$6WKx$+zloF1&T?V1d?2T{MPip;IKhvOoR{f-don?JagVfISjP;tv$E;(W ze?{g5vqF+{AeewMwNXDQa)N{k5MvRJ6XM<*U#Hg1o*`gXVecOpSA7u`gpwg!;DbOk zT^Pfr2)qH`oDPLX3dS)&KyHEw0B>T*%TQJ1AMs`lL=0M`05Q{tetV;!n$rLDh*j5* zd=k$Yibg{`Get8`b#eJ#=10>%#E&;%7EM_hpFmF~S4qvJDt4ft_%~c)R+=70##N zSVOt;!5?T`3E#nA9$Z9tTcBqOo?*PjPsB47LOwfa8--yDl!qSBa(Ks%L(V7|;($fS z)hqT7dy`9DL_FfDk_g_a-QAz~No&IwctRb(fYv(n#I2IA-T#Dkjz?QIU$^Wx4vS~M zcr3+(O9$-!pHPcza#iCgYf3hmMN&WBWR&&6n}uK|nvR?qt+zTyxHX`aN@S~r1AjEtd4n5=>v)$5+(oz3Q!|M-wOkQX_i zdKE z!jOt5mX&OmU;gg)InEjCD(Ce_`hQVn*+>0_uEbfbtGtMG9|O7E0Ny|9A|MaA6Zh(L z{M)9RVfVzONjvnx#z?yz*4On#JLIg`(!c6PS+B2DxU2boufP8eR9bg8x7qvemCDO2 z6>GxW38(D@#3V1iUj!7y;{#bOX~F^kHYHg;E|aF}OsS?H#3Rjz;Dy9|f&BwpqtTnV zh=ek#B!e;1R4BQtopw?%E06g#k<(_C6Ddf4*@Q=Nm%g@GBS{ivrxxwZk)ea;Ag8Cf-k3Jq%cD1|vnBK}NRyXTuoB)VGz8K1>sVBy8F-eQ+ z6G~how|PH?HY)BproVqfMi6$%u`pT$xmto}gMrd$H0F)uHuv>S5sQe$+ZQfHK{AsV zRA8FWiPeJXQ|t8&woNQ#8Pr&X4E%3sY2NI)oDE6+y{4`u*l81(ZqN&+^%0p-^bQ-? zO@-N`&&-kLHTsC0);c1mwXSI$v3|vp7>$tpAoBCBOlBgN3q+$mYHy{xw4++JBhfx* za%N=Y(lu*TCc9c)ApQmeVk8sI77?17|TXI(l*s zK2-^;6aoD!DsSr-JyDX`eV>#^ih3p@5!U1eu8b3#@e+!se@l3_UkVhr$q1+!1XDJ zs~cb=NBf^p9?t!SJ@CZRw?4nQXSi#4XiL}T?%~q#;Et{xJrkwLp&g}T}>`vc_STVtw)0i|BX0;Q6xWE1;YGljolq3#m#7P7x~j`}q%j)pg6?-`juv00|a5 zzJUxr3A6QzO4(?iWQFGcUEQHls5{8NL;fCSZ!Q1j^I>(8y{PYh%5$o%9?a#AvZ*|fet@NS6jp)4C-+}^s9xWvK1mZ^C@nB? z2u~1)@DX|<1h;jE=dGU4c)sQNrOKe*+Q}H;4WXpW)kLfW*v}O_JH>+)(!z7y)FQ0d zk%c}lSL`;I?jzaj47jWT%P_)*DI5CDN-d^*YvCh1XVJ^Up+ zC%!1cpe}ty@3o#uCrGSS7(rY_gfgYH^Pf8F0^a&D@DPCK#of!-n@AM$iA1ko71~`u zNq+*~SL#PycEqyZ(%k8uqz}J^-b09NU5Wbwx-@o*Ns^*DEOsL3ujT1Kc)&h@kM1sX z&mI`nCWo%oI2+Y--gYe_uC`tDJgzFp6%L;JcFKOol5-wW1PnCP?EdqPrff?v3pO6X z6mEQSp^B@Z{zL-RqbV=g`xiJr>Yo>O)Q_{pZ!9km4p)rX^E#)}(!#tBOr8f!#p^w) z^3o#jLwVloImy1*p2eaeYhdTpM|oz5QPZxGs@b!RuSl-apk#CmTOP-gsm5@(kHxAP z>%2+p8;y71z)upQ6psAXd1T|QW*Sdrs?k`&!i|rxhl36LbH0Ut&N0Y5FY&zE^JZG` z5zpsYhyTs<-`JPZs-mU<{ydfT%nC`YJ?ywB zkkTx$p>>7STyB}YX|}NZuqtSW+Zhx68ps{ZX5qP<+^pH)Di>U{2vHy!@Ta+*`wg3V zMQNYBVslwl_G2N{Qz&d5tnaK=H}`aJsZ@8?2e%fA-72I}jMd{vv_ZmW1!}^t-bajg zJmk#9H`MFrOp=y@NSb%>+?|TIkWWJ8#!I~frE}`fQ4oU2#B|WuK#w=qUR|OreQcO5j`=7 zi2%B$KIqd@u8~&@CKS@(logCeyFWi|@Wjk1)4su}wT3Ko*5l@7aGfQnUQQWr#WLo6 zb1_vAyhv@FoOH3woAhAX7Z{wXno!%ilA@-AEf;?71l5q#Ih>fsg%%LTISJ5344)fi zZ=FW9@aYU`s@WeA@0myh3@b>o^UlT>QKqJe;6qIFPqGUD>_`N`*Q)m+aOX9^g0eG& zJ*yk63A_dR>KwGChB!)sqMxura_%zRLjEaz*cn$}6zD8I&L*$(LiI(jvq^9IeSmhN znbdAV2<_^HUgv(L1Sm?WYo(|_@`;E?4th_lG#J5bPE!Az)#kDI2kBVu6DD4CzoDk7yq%S+T z3c>T%JDC#XX79W{Xaj>@>%!0!Ny-|=adlo84R)T$YWup|EzGPp@QW71ZxLN{ z1TkKo^_CZ3f^xgu)A+LayZqQ6>Z`MMYLlt#flFR|3w?-k0&R7;r%WWbtV_xhKKOU| zjegkkgvVp@X)Z?)nOZG*8(=LocOgUX=)RIISGx>$Mtg`qH3QVu<>-}6$K8@ESL2wh78U=dAg+n)>1%B_{G7yAC(_dkAeQ3vV=^=iWOii_Prk*1Y zbbh>$AtDLf5uA0&ogfL3biyrdY83x1#xr&XhRaNlq>uNfGowY45T&XDC!Oq#$HjXXeU(lbE*dPLf#5s^3Qhi4acCWB<(hiOzL1Kz#om$uaq?ULq(*kk$)zg=#ecqEDrk{lKDe)xZwI) z|10(Rte;RnN50Xn-5S>A#xGAj^{sC`_0(hVrs}L;SGxCbJJkdilJru1n_u3p4;OBc z9rj=|eu$ji6WEJoB@qGFg(oqxZl@gU(s0RbCdGo`WsTd0%LC468KI_b*vLouNLb7Z zZF^|y5=JAa*|pDXeBCLhyl!K0{<_i8sq?+XH122QJx&*WznmH!xqhpOw9?Sd)T^WeA2;#*0Do-htL@%V!n@I%(e~+5hBL+HRmQj9ZQ+g4` zzr`evlg_js-4iBlZNx5HU=U>^&JB{qHe!{DhR2z1k0er+RIY`XeMM8c&&QU2bk5qj za{zfsRpY7}k1GySP?nWl(iEWu5)%5Q!FlK#%{ z3Q|~8<9AJ!Xu4LpK5b~d8J~ucC@bU3;EvXm*$e3xgEBmeFdF`;T6PKzkCdxfsaG!( zQV>o>oZqMU#(4$2fh*A>7~wOvXW46Vu3PpJ<*kK6>PJBV&eRSXkU{6?m`(L3?2n1B zT=bTBgp?#CCyB&wiOQas=vSPdbFzFiZ6t4>`?=Yj9LCtIHjL z!d_3t><2sbdFo>Ihw82B?JdfS!^d)Yw)17zmeJ9cvxz${nrF(ya@BV^?D1_STXUyN zjAwtVLv|;@UoMIZzOWe|%+aGhFS35U)hP z{>|dt+iiPDpp?ocut8+|H7Ra*G*N{Yjs*JkuXrMe9SziqMO}6tkZpJD{>CE*+8xwU?BBoNUex_MuYVg0 zx~EOAn>Vnu&|bf+1zk(S{L#H_d)gH*9Z&~!f47@N$+q?#_0GnEen(`5yT0dHc|Kn!x3XLzZ#;(%!_&-( zN1OG3oZZRpuy_cCyOZku?XvT!uZj6o_)@RcQM|Gp$s;8J-AILh;EbuS*^9c8r8l%I zv9HwSaiP6`NN(rgtE)<961wThC6wFy=b+p zr%=y1tyRN;rIT%Kkz4b`tzg#~xu8XHTd0t3@^Q562-KAz(UD!99%k}sIedY3*)176tzVRiky4Mx~ zYgU(2f+oucK|ggBp$|f1a80^OpM&?2xvPoqFo6nz&`_y+$IwZ4PEM{$hK8^!yJf~D z?gj2Tx!dzX&!2!k^Ksscs~*=Yn;ZG-a+OWW?5>uzWnMPVvV!^y$Gz+w8W*|Ra#IvH z5I2?PkU!#JPaXP(4a06P==C7k2M=8+z(e=#Zx@rAaxLmJ3>E93Ze~rkB zimD%rQ*Ua2su#mmM{)ne?!I!ZR_@z9(Jtv&FdZ4zE~!P`f|czuP1Mb5(^lP*gB`6> zraZ4HO}l-%q>_g;O1H1EYelPc`_#gUcIg%}9t3|b2JibKPt~*0Gf!;Bd7fL~f!$Zj zdYqI09TRIVW%L!PPPV3Vy`K|LTM6)hDUw&)d9mwxbwmO7TVtp6_EsL{Q|awJWo-1M zo}K~Q9_Z;gX|%j9n=2M`*>&aqxm>PL$mQnxe`)nL{zZMSudh*Q+x^<5EyR|Ibgy+< zD8Bfho_G;epE~CJWO#gda_m%YS1xs*FruCwIib5$c3xM^Qt`5zWs46w1L|q#M0K0< z3WNnQDYkDSX2$pe2-%WI2`L5Hg}`q~`eoLE@WFqw|HFO&IyjAA^Ha>WBXwNvNC$}X zq92>Ao+~dHu|Ai|RJcsiDH-yp^Ugx=mc`DZOVU*|DROwCHjT0&5lTv&>%=d}b+hAAh;+n%8i9yb zG~|3m-VYE_yfb?`RgQ@?5i6(E$6Jkr&^H_Yq`T8RgZnfuPWEH6!hF@ARQDqgML{7& z5OOZF$Vm+B*#$u{z~TYuHdQwW)nMMpYD{k5j|)q!eAPMb|3Ke;}5w3|M;uXq~Gm%BXZ6MJddGK|7*_| ziHZE0=l^*A#q%@IFFg%`;@5y`^{zAJMD@mw)1gZN+& z*Q`UE1+GfAwQdwo#k%-u;KgXp)Xo}*|1lScD+gB)hDpFU-=Qi-V%N#zkn42v86H?- zEak$PoF;wsLr!ehU=+dTWaB!r;^f)oAqXb$V2}!Q)sv({WI~D@grbpzQ2FIU&54w-u~9d z-Jb6kwQ}>@I=X!Cvs~S}LbXbO<|ZUg8PcFsA5D?$iPA20onBhag1=xHmfqbGF|!c2 z%0z1h!}p+!CLX5MU%EtK&3y!Q7GjO>#Bt{fpKto6sh%>C-M~>2(y1%a=K@U&eD}A9 zD*by`Pd}gBc7v7v=TEQR3u5BF`T2d$fo3Z0>X4*_3IU`$~4Xd!217 zSd_!DEqb5kJ3O8bdp*`;tc&M+Zo~iM5q$9AM736>lbtXG$r#fO7aB0G2u;@1@E0z^ z>1Mo0T<;N=o9$$3U6&|B&sK!%%$P-1-|pxS`AqW;ZMO9;9gUhBGU2I1Iqm!VeQYeL zS|(jfk=L*BOxHLtyqQ#LoCJATVs>DkqHCv+n}TeJ7for5Gt$Y#Xt4oCzeb&OT~P$~ z(&v96i=9TmWJH?7tTyh>_7s;TNbT>P&1CcWY-YB%%^<7q_f8~|sZ=sC(HkR;but-^ z_Qw89y8z(nnu=tTwC-IrG?}gG=IO@OI?Lvi;#e}7Q_gpF$_!+bp!Z$BWih^@jk?{8 z8Lwv%nR-Oa)U!H9%=4@=wSA;_vrHq`Io5QPwDoOgtStbmuO*t&b>auL&G|y9RLF0x zRhy-1x-44BXjD6#nNn)W38-HkS(;w@!|r@xbG5dqkng6niP|Wgf!7)?6z%Fzea6%| z!gLzk`53;cFY;8dA-#Z^GLxIDyADz?XnZx$l=kH|oS@IF8|GK9E-fgkf-9cY&8lgB z^#Y0071i3lfvVUArip#qJYnYOB*VclU7lwb;V^L{m5-oLqq3Hz})v%?r9g#HZ)E^ z3UiSHK~Bln5-8M{{w1B;pVd6PZBp-cG1Ss8{>Vo!^Ps!;xOo?I#9({0)?8TS*_fI& z-e8+m&CLy2$&IUAlXlF^x`LxKe-MkWTGRj(3k{DZ;%ZTO4tktt%3ZanS{+ zIAoh<(3SZX*OfVTMCr!TGO5v~@LT;+Nyy`Zmu;;SGPRBz4DfKEEw1tVFLAM$iy8F&s6?qlgXe?$w}I zKg%KT$Xnc^NFFUVlGLxya!7ZOs}@*JsZ&QjWEKurS!CMUWR)uV84i2M@?l6oN)r*KHpOoVt7tzu)bW22*}zGu#rUGp8q#$v}tM^TNp8Lek8Ic6Z3 z@uJ6sDO$7yIGIEDmsUKUe$Y!-JfD72XS}0D9Yw422|D(7c0|XsAE{5Q`wrj2#u1)K zd7ix|+D94G$VQG@v~MT#r(nW}fkXroKN%2FMa-!`)yhe&u>9%%{Xey@2dvOe#+N<^ zpvLheGo*DY_?H{rs7tJEj;oiaRpN}nmqm1Ea$*l_(AnsIp7HyozHQy!(*$x8{25iOdA;d=Pn-7TT40)I%TpIbUF=lj_h~A2h z=UWSU%Q0KJtjt;a+E+I^j+(o<=QtfFxvetQvHS@=Sk$pSVtJ9|3a$oQlDhJ!;;qM@ zmTqz@>lm{GU%tL(*GTfz)`a0|!fiV@1J<8@{PZBvY#GtI2czeV%?5&n{`k6st%602*n7KbzCg%+G zMPv8(7XraqsjPJtmBA~K|9~o~ti8cFfiF7vDe#OI@o6|6IzR_VA82OquJIej*yU@p9I z>OFVzE_MGa1W|*lmR;{Y%K^sWf~N+i3DFD7dY5p1qS!n6QuKhn4!*pw;zil}+nv0h zcX43ZyLn1?t$rEH-jOiP7AGhCaf0og-({l+= zpr?>A+z9OUthg` z`vv=6Kf7k5&pR|a^0MchFh*{onQXjXD9n_qYmy*YC~+gjRWX0%>nA5hEMl(kStA&% zFTLi9W468LhN<d^7gi@Kq)Q!l#>$}E=7aI|0|TcG44^{mN`^KM z*U#wxl4RAYrJ_lTl%7#;HJ!)ajthbp0<5&YVjfxqXOz&sY>uim@#){GHSPCMV9W{ZnmVpyt(5zIT^sqOksF_8`G+%tgsc68GNw zYiCbPo>g=H=DD4}>{c?yTReinKs&Nzd_-HPfgEp1xpb;43MvX>4^9RGE8tLh|^7Q%HJp;APz5Sb5Hgeg_x@u+j@bIzO z$=B}Q{qV^*smyJ+eSqwnU8Tw7P4|tA^lf_Srp?!{E{enTZ)2XU z_R06zjb|RR>OR75oXuUQ7Ek%uJ6{sW6lG|Q z$(1o_nK7q-T^qHGO>tSq;kDx+QmTMIn?IArtZu?|42@hn18Sex?pj}+$3>IQ8CkLT z8arZHWv5z5_+>Rkn2|Z$2Mn<(nT#9u&tI1rP9`%F_ka}sZ9(~o1f5UBYAJDECiqcp z2P%o4|27oN_{8FVh0YwgPc{jTKr1Rsglq$#0b!Nc1Adz@ykSb5)=e;iw#jfS$&tb( z5$(^wJ>8_nf;h_cz!h;bJe2|*U=O>&#t>fzz9b&!eBw7)`*K3a{dTlBogV43{^-68 z2~sP>T}Zgk0nxjr1I{PJk=nRXtL?-$)zdUzQo| zVIEVq^+*4u5<%q9a2|r&A!kUCggl;D2lbb24#<6It|CxFP?m6?&ta_uJ;c@-G(A3# z|KLB+osYodp5?hg@=4egV7xxhQ1Hh1F6DX%6F*Y z#~2;#4?a@N7B?N?|6iPQ&d-lZW|Y+LFT9~#=;)z#bE)g`53SFioZt(%s8&fU9gbJMNrU5(il zx6!xORqNXUBdl*X-u0?izS8;eD_^NnuhNyTxFPFJ{NRu6AKCYKX5e>-yOT*|77aaz zBq!x`hbEbvqU&9TMbGN#cx?NLC;jlDLL!?T3k1)Ym^e|1ge=iX*`V6Hue+ze5c<@$ z*FI+79#hrIdmpNA#uG?|o;Nvp66j=M;^g_riQN@B@w&(0V-I?+Bu7`?vz@UbkrcBv z`InpW*Zc#5RYU_>4h?-`-KYVI@rxU$BTZnm) zNxUmPoJ!@GT%=TgWfbHNZ{ydyWGXVlH2JWF4u@qP>3}_^l+O-F3A8*}r_KXLAQx3{ zaQH4`ACIIE0~_jr3gFYEAX_>I3|t$U&CSEzxjg@V$b zTD8K6FhMsi8)^u9r(o_9UM?U=PAg!fsHG=&F_54(SyH%GicMBI;I8`mb%+2;cZ-t;*>D40Ku+a_Je30`{_*Gj~lzBZU4vKn6>=hbMde0>>fK;+|W1dUN7 z(fHR7;y)A*I%odI9#xNVJeiPZr-=nnX!OfOEhy&#i^H`s_3DOJYfqY+e9APyP{JOx^rYTAAu-yiNc(^X({l%lB+q>VX z+qa_4@Cu@*S+xJx-VX{I)B@hj$3Hl=Y)a^96XBX>qxy&$Dt8M?!{ev9;Vm>Hob5yO{$*}U2LyR9lSrZT5Fi<|BJ<2|pK zx^)eF&zf7O?(7+N{_7M$r_gaIf};wo_bJY`>I&yg>T>6e>Q6Oqlh84tAJ7^2%n%e3 z&|wRckvn4|J39xotNG4#2AjDQL0@0585qgVedhW(^`-v_WTMgTcv~wG?~ala?tdgN zTo8o?0HQ;v9$*AB(dTlmdiC#G!x8RASFuKa?%?O_HN>tBfb6i3oGb6)4bRm~=ysr< zn=>TSu3$4^cFz^$Ez^~7=X!3t9SgUom)ET>3pQwILyrATVt+4kIt@23 zw|-xpvUzLG%3EuD&TXD`R^+Kj_hcs16FmOZNI#IcrHv{%R2olby7itMpa-|iGbzA> z41s?E6=oKNaV#W@MU^D)pYCc2~>7m@q^d;<+OY~tVw-3mScs8Kw;_}!& zqDyKX^wz{c*1qP{QzMmZZo0egsYq!elkN#QQ|cS+%1)R1p9*&a@YNf9v-m*5-Gfr8 z?;GU4-O1#j+!ufjaY@y;^${hOttI0508NFU>)rY)pfmk}6T&ohAp4RNQB6Xg6Tz!l8tKTUCjd`W|cdLG>@`}uRO=y;z6ODetklW@- z^8VYWnzz*4=PnAidkU3n8f^}X8=oN$dhnQAwQi_IU30y;pJ||r=_+B|8h=T*-uT&e zRzNw?6aiY#W=8KJzcP&_-Jqsa9ZfN7O&8{{A8gx)z!Bq52QZHUnQ{G)|vLGCET5chfR_f0TH<;2K&$-_hrFEEQDkl zr6XQ!1psXHrPE_w82b2z0RH34049VRzyTIC9nKw%i!+ihN+5Z{50(N5B_gr-(*22e zi7c5CjV{i)2p%F#AY>s=fKNn-xi}TG=mkqi$oSv}{eYbKSv6p$V4zqGG@qr{kd`pR zqA45?Sh_K2a{rdzzIEM3dhya7VP95YXXSWTEY_P01Ls7F4Z_fS)3Ay}{m&urq#fv1=2&D$I#NESbNCp=k8*teusIz=21Tf-vOY(wfi3G|iqU9w~ z1Oe{x5{VWIBDs+vnU+iKoA8vDJ`(a21hO}r3Z|(M6x^QaT(&|mJ5JNqlBC@Z@{cQk zkJ5hEyx({!LVtSwh=ZXFghST-c<`b`E&vl4iBt>tVhfPBH^ME^4TN)jxGA${O4@3{ z2%cD*tw~N0=kpTjpN;s7(_O{YU8RvkGAk>r76sv>SfyzbSD~H{+2J4xKVpKr2qZ?u z9uK2;fb)pcWu#RmxuEo#Zi>992)QN!TJic3Yw00>DGDH1jFiGUC8Efn92eWKC@$qb zGG4X0UC;#x1CIDb?+wp{!yQ6rxu@rhwZ<`)n#fERFLq58ixY+XXeL`tN+wF)3-8Wi zFiG~5WU8K*SKAW@VG_IVrx-)xNK1ee!zF?Lds-$8QoBGR2(E#adEw=Y*}gESD*%E< zN>Okc`4ZdA=p}|I7VQOrA)Bd!aUZ_L$n0A3hf$1&vNC={KGvm>--_fTd?im}$g|h} zZ+i%9|0nVFp2D~Ldi8Gg86+@0jg1@sjHaAMIU?$i4x(4+nC)||Lx!)>SG6d*kP_|2 zw^Sv&vhj)lU7NUDUnLhvXML%bFw zw73=tAf4%pGS^9qPRd^pm{`&KxAyj~P9t{^CWiEAdzpqPc^EuULs8|~0%ze1W&n_9 zY6)SKv$PH;91Nrhz$PGEh)ysSiS;Ct)r`2T*0bep==I25W2(9;3rU07~W^fNnBr%X(o zTUFqC_fj`RLPQjZwLcmZGEPRjQUa~2dJ=R1&?NcXAQvi?0y{Mlk8Mrw# z0rc7=&}gYR(Bg6L`kSUEH_OX1(ulO!Dr7Qi`dD;$!|`%F(Hkd^QWV-OR2KtQG2XK} zlOYX1ZU9J2mru0Uo2V<;Z)kHyloosmaG z3p)~JcgJptVT5~-HBIs?(N5{gsP9TM03p&TmQ4t{zC8JAJIbweTzm+88lyHj2hfn`b}H*nHeS0E|ex#_8{r?y*}*j<{0!@b0`&aKQ` z8L+nS+!4BuaRmYfOFX;_@10lzGH7|#trm+YR!wv=)|FeXc>LHMyLRn3_VFv)rGDlD z5mT`9A_53*GSlhAJ65mWf}a^G313$4)+c0TktB)lmes58IPtf=PJM#6W%MJTBWIG= zF99dszd?r!BcZ_|)RowSh^1iRw_dE4Ry0^Qed&hZPvegKP~tPwjokY0_GiFg7mOZF z)Z5EfQJ%Cvr6n$7Cfp#sz#yLQUf1_aKLJvPQ3vt(c%H&|<<^-9^ls;75Lsf1S!uX?nnN6N7De(SPR|9E0@RV01+&Yh2( z8wB2N3f~9-?t;SS+36SzTQt5ZpCg$;mP?7M3;1Kg?*)?npkP^tg4J}U5`xnVMpR_D zR2qm!p0{Po8%_m)p|{+3?xs!GjjdX1*|lo*g3X(*93GjnJeHb0_!aA8_WQ_ja2ACj(|6|JoF934E(Y}x1y$QqYlXywTR&%0x8 zo0lnX%=(z~UcgVFDoPa~mBpw8_@z?MTQzpf&*0d4Aa;Ht4e23tXEM`Jtg+zuGLYs7 zYuGUK3_Fk=498aiawwBZ=p##40`e49t3V}VdHqr2U%p8l5PhW*XzOP4A4IR`%Vc2q z(q{{GN23!fgyTTd!W~0nEv-;fnL#ppi2km+73*BFGLBtF7DYG$NO7h@7@`{ATdj;L zc))d@sQrX}Bjff{ssz%>lj?g=%^r9_JxW_llyPr^>*!!IpmbWz>JCUzx2=>gNd)Ce z@N}SI0Sg(ILc&ool1Gg`*3{8ifuU(;j+8A;V9kd4ZMBSdYAQ57cd(z(aWey#83b3} z!EfC8I&U+hpRcKI=03}AVn&A1B(xfeGI^*f{3K7KpS~AXS|s_Vw_wv#@X%J9!8q$W znz>tg<(X`;a^}hEx)WP1rBz$;TE|4tTv3+4E2`3ANHD7M41*oYM%Sgw)ffVlSe;Zu zRWfqR-!&}<5oX8S5wF@M0%j~A4z-AYK0CXX!N&+TyJcCP1M|&N(U5zhcrpuv0R@%R zqS*g3tmNRI2?md7O<9SaVf>3Z!Y~Z#1zF)q07AnBS{ooao6BLy@Rt=1CBw3&F$cB3 z2fLP?NE^t$eT=lldXea>5q`v2sx#M%WJT3veKA#sqTJUXo0GH69^w2fwN~0-p3-eY zh({2Bd_JMd4OCP*|Ra6)qo?BZZsp}X>9e)t51-;JgG9NrRec+=fgz?O1bqGu$ zH`FJ7kEcwPKvxz8aQU4s_87 z=YUS=q`Cb|Sdq~@gvj%BcNPkh<1RV=W|gW-SNp8GQsX)e6XylIoKK^4E-33kA4H3n zB5%dEk~6|t!yh85O7GYN5lLboU=?Y)Zj}%qc?RAruZIT|t{$chMlvYEw%6GVC8t6@ z)}ExCE!A@w(P_dRi%$sWa~0Q814@PvBSzQj-T?&+ya1;`cSFA1W;v>ve5=0fGCfvw zGikqY@fz?kf(=0LBz6_kjF8+JX@|VAL)+2#+hz~YPqobVDws>bCU5I z_KGHfA+)?JEXgZ$_^!ANCzpCIH&&QAyIL7mYPeE8d!{f(5EO*lwkGtAUL*WBlLVOo z3h%{c#C(YQ@7C>@03Zbq*S?f~W%_NWz{reY%fPurz9}ASOdfZP!V3;}uEfQ?`rwoH zP4=VYNc*(sdpgF)a2Mv+$kPy5GKE4_^`=XbptB<7wi)GszQhng;Ne=*s!O(Xj+Yh* zD|9RiW@&97T0wU8EhPNXoMA{ZH=fl*?=ZHH0$H_bY$r_=lAK?5s_VYt68QCyQ*65x zY*WHCxO?pxRP}*$G*ZoGh7!E)2up21l}3R@&u21gD~uql#@$qn{sx~WpAW@Bw-}Nw zRauK<1$g>sN%Z|0L90s)p%MbTl}c5i6hP<67*f-CBR#LT_jyxOmrn~3Ap>=wRB%?S zu3THK=OrVMg2vzZ>bz%$Z$Kc zc#d$-ks`Yp?!rc9BchxoNK}-8-md9Fek6@(j)@mHU2Xzlt6Vrfu}zT~3x+8#8Yq7# z*pr8S^2Wnz+r;>V74=^Fs|_v0hWMiR?RJh`v;7*n!HY;>L`^2XYy`Yh&nuS)2Fm5< zO(i7P4jJr75=1{n5R99fVuw5UE6?fFFs*I*a|-7l8F>W;HTv&$ng!`zrS8Lb$m$=aCSHQ& zyjcWg)*^2xU? zWJX=#1%}ukyFz8j{FrVZ&5owhc}WY)JMhebP5A|KXGk3CTmkcr5o<8zB!p+fL@{ZV zWut6d+5&YSR$Wv#R$NjUKuFHW0=eBK%l?awXUmc zkSU5!(b+S_=_|ED-&S|NKKX`g)ng~@sqHx)p$kWfE6-5JUM<3avYz^Q-?zVZkDd45 zA(~sied3>FU$R{W@{b1_#T==c)7f?1-K$WfKo>yCO(c3^+nfhW>V$KIFTgAkbe3Qk z#U;*@@dP|J`iYkki`h%;?x1JHGmbByvwwBM7Y2H3&LsmqLr@gFXpo+(^5v!<|m^~Jqw zE}Q9Po8rjb)BiR3-W}(^Xllo?Q#YP}!SYS#j;U9i|6%L#^Y5JOnN-`Ss5{ymI&M z^v$0?{Y|&*{z6Altv~%gz{`5DMe2dv1Ibduv)aFyy0^Ee`79D!SLsFgqa?de9m{gkQvl zw)Pp})Lr0~d71DI+$B}+-MsTEWrlm_`fIK(%*ky;4C4kZbpjGW!TL`zhIqpP=RPqo zh(YCDZ$tryK_M1&c8DfRBCsqX?XRiNQ>^p6;71lE)SC9X2gd z!`Z4BE5-Pdg*+G!7K)0@Q=kb%{np>>YjxS#Hn3CBj)nCvL1_fe`HT^OFED+l(^Ea& zQ|WXrolLIk+7Oc@?|vISBrAX@uJGpb#)IksmQSr@0qWW5(QJAhv$Q!-ivvSkq?i>2Q$)#bo7`y<}Ya` za)Zop5khSHv(PyLh0y4771sdT~my8u?W-k3Zct~ol-yH{H8^+Xv*`@9cX&oHt= z;Md>gd6j3s=UsR-NM^o&_WTbB;ly%)!o&}db7ok`lB5TXyfS%}t0~N3jQCv+<9e1W z07wUwo zudI7d`1MEZTe3qqX6Mt@WU`zK5mofO^C^(-i)K*|dyfX5A=jSaTyiVs?h^%$Y*{&l8W1^o%?<`WA8<4)RD65F=R*vxBjC z;(g>7#gm;ps)7VrO7L`{=uXVGJX;`;ODG06bHffQe6N0S@G%`D7bEMWi_7Q+>X6J$ zFTpGw0hVzyiuf&#zp=JON92fNn@@xhlKoA>nF^n}%>fDcRa)bt6jS-W<9Jbv}bCp~h;gKE<$nMxe{ zIF+b)CUeZ{%qp0o?gtB#f-n*5B9C5}N^}Ks{fA9aa!xD}|4FP3v9e~Z-Q~HG6~W^w zc-(N0RC}3(2d@;6{nWZmFD%yyB zYvX?FvUUy5rH8A+p3`?~q}nH1Kh`a1?9umFagn)y37!;BF-Q?t2L<@I~Opoks(FZ*Zqeds17x9N#GqxTDlB)bwcP)~+jVyNjz zgD2uztGrID{UBdRe&A-))wt?TRj2jeoInu{atV}{{G?o1DL~$!hGrk$wUt>auHhhP zcIh;yKK49tTr75iI)7U~5-G>P>Z_^Ed2y=38G@pS$tKeBnheVzOGQTjpdcx0bkx?))2OVdR%)&wcdK^CFD{ zvwG|4M;~>56j3L!pKZ3z69_jCKlnYK$+%oYj@P$&zN&(#`EF1Ts;8}#C9@I!8K%Mv zDl`Awv1+6cH`}YwdFGuNX)dghIopN3Fe{A$1%rxSe#nW1d3&mPbkp% zoVE-ijiZ}A(-;DguTZIJS6XcM@NGE*uOT_1tw;F% zoV~0qXNrmhcCw>iMB#Gg;oI{2JiGB1hwm1vvtR57SHAb9hYmj;>^)}x(&u;_;?+orW?gS8*cc&yVxSi;J^K*4P2sOQ_rv|=4&S)k!-wy`97lhZrRy<^<0L zNHq6(-sJf+&pSX8eIHn&p21qZ5-=jIi^bhcXy77!+9K=dpFzV3A=Eb2t*&HIV6C1t zu1&5y+!@9gI1-y>sN3NT$o0;21m9QQ+mKelc9<>43A;AM4O}Y>X@AY z1bMS=8$QjIzQjY&-H(TRO=`%QH{izTw-i*?>L|+f*fj0DknC)`;5u}28h1Q1k8Xx`K96*uBJG8c2qi zq*zE>_{{}XQQ^?lho`FFSITCxtg}9UE|C}(cQBzjLUf$}oI=}#m?8pzzq72qFHUXc z#2D;_KQWT4)@*Kk(qn7(^_@9BaaQHnO0Q+r`u<+M;_t5+5tr@c?Bgf)ojNjldL=(1 zSH{(cBjmRuq_!`Gn_3pwa%CT6EjA07*6Fw-YXrJx8 zYZXp!f~o9NULR$wg_z*HKH?XWU|ame{+yl#8$5eOj}7v%NOetDt>x-FBS;*HYJtG5 z1;WFFV{2mh&ZWO<1N+-L53yN@8?@ZmY^OV=j&9R!Z>xc{@hHFA zmN~saC)d)_*o^TOH1k@1QPvrav1ZC$LV&AMhv3D{>P=@b(@V5oELL3a(t?8VVA6r; zc@5RUDK+(3!;z7((TawngdB85qB3Jhb)`KMaJfwf)W`9}<(nByX9hD8z8LTprV7Q$ zVs2F~zbYp|>se6}rxVHH`~{2q_ATzi{XCwdR^(d@99=i!6P&|S0$V+vW7s>jTdj`z zY+QT6XD__qvlmE`tu&6h;){Tx1mb{$g*Lh{Q^kk^yDTmjxQRk##4$jQiqnYjk}xmi zNsd>t^S5u~D}bTNASyUS6D~(dCfDlA*%T*z?jTAwCA?u!ba3)vJ5q?y50|3<#Q`@x zoX<~ok+Y9)svk0uL|YWiNAOaU44PiZRDhenzV?G!t*`2A|91mT}87I+1$YhOy9 zDjmv`i#x5vil3H5SE*2b6fl986HgfwRXqe)QVaTVE#uyj?kxHcl@Qi%m~maDC4u?` zXAsYs3`M)5v0}_r4pWw_CL?5K%|${4?q)*5RfoK(ZX8jl2J8nw@(Qs?F2a|a^Um~} z3)4A#1SAXaBXD>abu3ya% z-8RRdYtGNB1?P6_Wu3ia9@H`?EVOaE79HMC$9Bh&c>u^djw~!R7U$KXxslnw*m1Vr zugw02G+@E<=zZ;TbV>SbC;GC-(U*0xDy^r-F2V`}y4jk!wY>TQ%oscq8nS>N-AH|C z7Hdq6zu8Rx!;eL?tS+F zqz9hH{d4c2soMRsV)F|rEMC5^%hd5L>#s2|OpiV4-kKx&I#OULG<%cMt-vL|+4 zv-9c48;|w(PfUo+A;0SJqk7hz*Icvnn~y)P?i-g2$Ia!BwV%lgrt22_>-I6&;zqEY zAA`-U_5M3v{Y=trvS|{nsqJA6TEJ=UV5+kkk=t zj%wvmN4I!bo};3V*1c_7$Q*KUZR=RpHey*L`!V!x4#5Cz1>>xc?^Wh+n>4Z-nzd0@ z#I0h>)I#sf;igsg5nyqwgoXdkX)>a#XL%5+^I()BJV5z|T%K$Qh4*Q`V^IhwrsK zPbZW4A5A^{_iCZg)6JzMd`}iW#+il2PIK|QC}-}K_4Cmm=_T_%_1MEppSA9lTR8Ws z#~+4PoPfwX}zvbaZM$z1 z&&qwd{Vd`@hP}M)Y_#+eUH;&s?Oh#35joEGl{Aaa`?Zei4bQ7oss05-tP@1reiDbWxtE3TJ z{p_X2gX#xP-hQ{f_`Neu{uuLAcnupMq=^X|JTf0Oz4{?_Ekv1AA?aJ0Xgknqkw(0OE6ZUBTkMjjJCkh*J zj=B#ejWo}1IbU$TfKwnZfnE1WP>t5@%(3^7a|N*T&~smP_}#zz?dUJBNKW>@+WpM_ z^`1|7zE2lw1){ss5CoGo3?*tjmz%@;*68#)xVs4c&@}_rV)(z7Hj%4c6`oNL8VrVB zbC(t8c3#v{8+I1l*aoe3bSYu85w1&!t+kkD2{j5)V^-_Dxm#mSy%c2}s;pNUozLIN zt3&nUwQtkW*Q)kpO_W(a`=t`~#KFIP^Kr-BWjz%`(=l8?@gus&0+4WoR_UN5A6mXx z7a`_}OfRWY07OBJk9Vm!nCkE#LVqMw0Tq*||0F<5LSqP67Y}bvk}vKFCYXqOa^H*f zy-^@#qPr#6Wu@N_SJ zvFu^#VyJO~^)BbsU;XN@3D^fPMqn23B@<5x39+(w=mBfTP)qtDxZUY;2#viAVwOj7 zJn+le;Mb4_0=U8q*(iv^6=lA_xuEvsPV&KcB2IWduxH#A-d4wC2UE%JC;_P~-DprP zIzv|$j7#*s&iN~CrX8{`&W&j(mR}GkmU&B3TdEPV1>yoxss($ILLW z7!smi<}}>(mu=4x6=?=yr|2YVDQjZK56Z{aYTLRi{r#278LL+9u5_1iIZml3F~k5* z5##;ukN~i#Z(LZ89TVG)E8aM5_B|P|r7_Oxk7Pg7g)i^;ehPNKzhvsG@af)Gs~y+x ztRcq?8S_}xLffUXafug;_;!r%5Qcri+{Rj~YZ>UQcpz^2rb+vXo@+gx8PsTago*dq z^8YK~c15eMMK&}0%+#5!bx4ti&YjMAI5TUmo)>`ch56>k`W9r6n1DEtZH#ZS!mX-Q z$8byqLMIr=#=8cvNYDF|iESG?cXyuNR||&#wE?1j)66N)&llqMyw`(z!2M^HbK{wG zkwvE`OK3@|FkCL5TJPRiESA@=8oPKbcEgsf7q|Dd&#RP5+p8P)q2sR(K$TJqe_qpy7l+{1*ctWbqR~715LkWA*Ln@)F{#uS81EPuDn9~9V(v@S!^eS=P zM|XflgvAADP>_D*MOgPa)w22%=gb?dQ?{S?Q0AeGdKVGrL5cTNzc}v~OpKTSQshF; zk2}^^CY)JwPjXj&(S^(UEf^BsliNjEd<;4YMBwXA)lzS9E(==u9e@0A=HZMYpiz)^ zhyYi=IR6(G_hM=PwJc(t^~pU>W~Z$2%{yzBUy9PdbJ$gUkB^k)FoVj)_v%* z+YYl(+_@@s7x#Eh0fkik*NXcuKSAcJ6SeP?Yz$Cx-IvDLB% zlOe8>WL0e%v%Dq z5`$**S+##m^Ky5mRt3RxnLaeie-oQ2C(u;QPML)3;)&5}sxGv9g{RX&Mx>16v;Q$CPiTaWRZqh56 zXsi@?ono?vS%I-c;#h#`juDZKb9dbyD@+`)6!|cpvGl*4*?mOyZc7si4l&7w_M zMVb6!nuG!n#-0ua!q!hx-66sTfcE zc!g~U`_p+=ybzgvB6%L2C}B>~=S%P>o^cB1z#GBP2Y*32GoZ^LQQ(4jleB~z>u?}> z3c5EKk{|?NEQAP#1o4PSKdf;9pYT!A5ZDs{>2hIlz$0i*nkKeeB8!N+<2CUp$kl-W z{0=-49e`{v5qWewcaUrFBbB>pk5RD4=^q(olkgm9#PLYpoZD|bc zYHwY7hV|_eTE{Nkdo!OU`MKHtlmE~hl`Saw*|0da(&^Gxp2iN9A`zlbS)daV15Y%i z#J)?q%LF!bJ#Khl*i6_ndK&0KVSVTj8F#!ld4~ekL9+?K&?e+K?!-8iEzux3 z45J}u!*fc&6$69!7FHki2E*Z=L@AI=tqMP`f$ZohZ~(jkY{fAJW+9PaqG9k`EUcM; zo;1`f?56JfP!(uG0%QPv@nTK~YZzyO^iakii>WwvV=rkr@Y9i5NANM-7z^9%>-wx z_$`LGAcIM)mt?$3G4@2p=R}F(rxqWP%k+~G420umiB~Fw5E}5yfR;yMp3#YndBLq> z9B3**7i&QP&6RNMK>;z~WjJT029n95-wOmF_*$B9AnJtDQGl8J5_`=g^4UB#P&uVk zvLA?4q{-&mZgPuU+rT~l~$cOJaTf)_kT;f5;!@FD&JMrJ>ApO)Aup=^i0o{ znPk$(_kBH`gk+{?rjsZn?d8t}d)ADuG4t zLJ$>~^~76QQCC@al{EW*^{Quvkobe$$zecn?)qAgAy;mF>nqIbSdT6NV$(%lK z;H;sSPP#`cc(?e3o!ilq#V=7uPbf*3S*?eY>vVkl(9lpWH#Br;Jf3dw2mUx~GWnOU z$Oq!wzAhu)*#?|5xzqL2|4WY>Hq}VeP@K|BRCx&rJuu5x|F^%L;$JRO&4rCW{lBcD}33(Y4Q z&P!v7nfa_YVz!Hd5-^{Jzgg&>hyye-DKpMs#PAzq#A8<+hC~!~#sWLQ>t|fmUxVYP zVmYUNZg|`|*9K#mJ=GPrPkE3{vI&O>!3$#$+Gl?wDLhCaLw@S8R6v8F_W{@t>w}eR9jkjs43hm6brwyw8YkFdUqu^g`0(s@ge-RcSOO20rQ}i2j<~h zsOs5x8RP47C@MiS9siKLW`hluUnfpgWN) zw1+ywVVqgEnwuBI+m@u$7;zYXqM0bH0K3UGb4&0wP@SV1+7?|PqbU`wujWJ_O>^a{u4p*2 zpsh;}FC>x%Z_xuzjD&ssH~Y2|uFb9+Kp*B>gYy*4cu980+=ZpMnfu9n4wIRTWmCmj z;c$kWoN{6skNFH2f9v{k*_t$p+)P;Lc@nX}h~T(gHkSKRvs~d3RcO1BmL_J29`II-F4F6+6J4x>bGY15A{YNZ+MP@(vpM*_Px&C zbHU(X3366XFNuT{tBBavrWBkQ%k^Wk)+4#lj78^YVd#^?gU+;0gDO&cESob2je+8oYAQQ<}VhKB6w5xsXgTehwb!cXc#P#P>ndwakDmIene z=wUllYK=#sQ#2bIKfMe@t(~%$TDuhG!#;5;GP!cs%FEkZZGn?a3H#5oRw(Q+nf@Zm~!d zhxHsa%D&28tusL9uk79h`2XVjTAiW29YJ^nK&3U@x zoWDM zKaGUKM(^c1I2s+{etZZ{>g&9Zdr`0YO9|K9u3xz>RY}zV?cZ8hHNp>OjMY!PD$nwu zC*1R@Y=)e1GD5zmXT*lNV!>q zuXk$EqN(1#`EMj&PUshSoSMB7BeU1DR~8*_61@RPm=W(%ZIhtw6f&9F>!q;&g_nGB z-mnv*cyb|UxO!*i1n{#UPq=#Wo_lm3dR*75=jHEla_xB$7l`l#D}0d36RBlQ_h&Sd zGv}I^6eb;>$;46B4m!Nj!z&TxntqV3p%7!`npPK@YjA0GeYH~sG$w`t(^Qz@(-VwU zjf~qK@OK@gP2&i7I>AZcg-SI5Qs_9uGy-eo@t;(klxs zF3<#Xo9F%o!e}4mq$|zq1>rSThSiNJ2r|8-s&J=MNh<}XSFOdic?c+a!2%xzi}uz- zDb+|0Y;altdKRx+LW@+7xeiSj76m#dKr#jhFti$^!h-KXcy0kmJjLt+GYL%~9@9dE z{hL5TF4NQ>0iOqY0G+{tv**u0XFh!L*F#pp(%o3!&>li8M!1jw9%5pK1JHMn6oW$C z3tL+TNCgIE2&o-On?jY>suW!4TG0Lg){r_Gg4ssBIQ~2FML%rVg_krSK8`rEZz2 zg%mq!!_@Ci!&Prr7>7He-H}uh`Wf8_Of+;PrZ4TEdWZ{e^m^d0PKIz$0Ot?i@!ZWLdJd#&g&t!>-%v z+Eg8gJhZOK*0!yyS8s*g9|E{wDX2?DaGnk_KD-OY$R>cg3G>hAnTEa@v-6PFaqkD} z;D)-95IF{Cfj~h|T|8Q?&tT<891T)nBDe^hEl)KxgyA_P;onGsTo52O)zSmHq+=T1-@3TDIRkI(SQoXjv9G0N5q#EzQ@|v;1OC;0&?xFIfu;9>M)C?C{Y49?AlwQ{gMJeA!3K9gYC956iT(S9K;cyrfJ@cUGDZugxL?*Cx z=VNEg8cEGsJi(jEy|gR97Jq9&jYUWa_Ge+7r!eHhSt6PwV5A$L~2= zPU6Fk;*7~*bxd2mJ>dE#=DSvZ44E~WI|_H^sb;l!!JSJBW{gtj(gYlro-;-#-f^$j zPV;C~s1?HO!KrmGr;JH`$`|S|7T$n&OOmVw_>y|Hjyxn|(4kIAWcDM}mPi{UZx(`j^25F#UrrIVJisIJ1INE3sq&$5`l}ZS^Pn2g>7{ zHcw4$+FTy*9~kH#FK^y7HMM!uczK}zy^F*zH(tDD&6@1Oqvz~eF*cT6*w$7O5AWQx z8L#nE9^bra=T3Y)KG5GkFb+UFcb_AExp-lA&Dt&1Z!c9mVhHsJp?x zU|`i*@whjgu8Tw}qt4=B&~sEFe0A$RpP#=VJQ|)i@9qx{EetfcN5zq2`e$@F-C2(J zUzE!&SE@8Hurb}beqdlR6bRt&{7Y{*?X>l7F=1P88VOk~&3zJW@#?tXf5Lg@CQ#iK zuG3v-!spUeuTuL3Kbm%im)e2{{w%uYnhP152=Xme#DB%gTHQ4)2IRB~2ce{CeV_Zt z=yj#o{ppFx)vG5bKD~Qa?P}M?TS}!eygqa#9@zSaYRi(Ybfu?y|NY+;)56`lB;8f% z-9TT!gGEh_5J_l*+7UX!2hO+?7TW0lu{T|;G5X(64FkgnhbKzvj z3TK8}d%0ZBtk~RY5eW7VsmVVT4_UQWgO~wNib49tp$!d>E6Lv$XxqT*E5i9T99C!` zhMmA^dm{vDyB@Pv=?>Sis!pmeFne3I%?9d}TE$Ft=JqM89ms~onG-7+&fDfP%x1Qr z(;N^s(hi&L3{@unFhDX2EDWGVCkB9arfCNkrUPUSKsy50z|92IH`susUEu1UNG%Vc z2>)Qsa0CV)bPqit&Pb44NQ(oReSr5OWeYK&2$zJgZ+=f^X{xEUy{Tz5(=*=M(gmR5 zdOB*Pg#vK05Je#;K?~ZA_<^|BT1ATS7ABARIKWPvZhcQvbE&;&S#uMPZl;!Hj{lJi z!c{6BFSNGi;4wTAjbvMSD<1}!;1I!QlRXQ`F=V~~DA=ZkRE9(P;W}|Qz|zJD8m|R7 zh+OVrxFLlYM=isa6^ucAvU(l8u1SY(M19lt90H86KLYCz+VjWWJQ^Arh{fC0y%gBT zhb{=$>)4*qJca$!TC9W=4NjdTBCP%16s#@1-~fn^#}mN8#lDkxjTc!1W9{Z*bDT;qP;;aF?;k06-e$;#0W#-4T1I?s;x6q==8RW5z)Fn z(a>Iq#Zrj7RcP;^D=La5(iVRMB6hVp+P52hMzw?ET1XjM2qh6q%Gf|8+DUyPq2UM(L}P90dGG^shYyleop%rQ z3XE+?+#;Qb3cxGoa1cHXpk5&Z9Z2sK-Vi1ZzNdna*b|KuAP7)VFmMd@Z4Ep?XE+}R<<_K7+p&Y@X~Q~D91hd1{dX`8#ow9+ zj7V+hWoE^OU@w!YkWeqDjKV;XhFYjAY(a-R!CEu z6ly-T`h7U-fSC$Hhd6{nY+H@&Fwmm)3bsX1fhf-gO$*dr8YHMeTPT!*s0^)4`OQ8U z<+*=;GFj=X?U&Pv1y}!MK8GD?+TMCItsh{^CW!Q)ysK$FB_cOUtD4un^rizDD07~s zAoT$vpP(N8y2oz9dA&^1-p^WxMJ z%iQ@qcb+To3z=>>b;!gr7$S`G%k?I30s*lJIDq#vpPmvL`_pL$ssY1rG8~FJT-XRJ zulYD!fZrtKV-@_6=q*GLn`2P>iq?LW+1E0OpX-S0J$Gw(!~=)Mjh4*%th1pZ zq?UQW%cnf0==M#C#y~PI@6xWB!!;+(VePK@W^DNf8U?f=@lZ=R)Y>Qzx4R{b2Mr)Y zq;O5#Qa!$Iohp&XYVz(3KhLf8bXDIPI{ve2G!ltc#aR!B;YceLd8!`afJ0c8ntV@1 zQW&ks!_~X^8`*L)2Zf3ykdjNDpr_A9;Y%;_p%<@H zmq1tYo|hg}=VLA>UBj;BuIn&rTKK_kiKg^=Lj)&Fid_(a@C3?GP<0Ftv8I?v#$bin zb>>J7XB{gtf63DxU(f1CHOp$$D?CL>$6AvxIRhw2m%P3shBLN)3BK(e@xjKRif>qd zu=+&pX=B5+`e|(A`h&N%TfD|wSmWcdWHJ^nblfU0K?D?S@JrUr+NIABbRym$v=sl_fvt-f;)zW)1F~rWn}v;<#+=az1}J)Yz*B1ekVvVP-%tPpkL&{(lVV_2 z4Rm>XAs%mbw`!JO^sUY3*Xox70eRxZ&Yud505n9TL-Ia@ZWr=g$2m6`rum2n($Fn) zL@SU?8V*q9>YYTInFsVfLngTm?%XG|Lpty0t8`u(Ll@adfoG%jUhSfH1!<>=UV{dc zbz6gX%~gRopCyeWqLzkCs%B4nG}<8`Q;_Nz!F#srAjL4JI4$2Y&^iqcN#THn@M9i#CnCg z9me)~Jn#s$5|PLWwjKdG?WJ8y)$3RFOG#P^)O{3<^T;ot?g+dEgm=5H2EOlB_qf)> zU)`-PIyuAc6WP#~)(FrUTEszUh`j)spYo276d!WZubE9g6;kM*E3#@NWq~#|<#191>kd!%>@nVr|oK4B^;CLiedyNRrn)%zF$#GZNrg3n#7g&;v^W2t;)< zfVG^E%0O@k5)fNJAG~e2LPyve7$HurZ~hxV9GKrh`E=Lm`ZGdY9R~; z<0Dmyt~5h|@%0G`j@2kys4~5%SjP`%C779Nc|~VH8NO>|Z zh&G4|{A6m-2UZEV18Ern&T;0k|D~_1$JD(dEuc~DrK7V%Av$X887~=l+DE}0L(I3q zwV)YOeQbqj4R}m?O^=bCQKnx@Hf%;|jBoI1NMz&0ahTukc#f~k7tFHqb|1Mmto`QN zu;SqykU8>;3NkB8W<0#-L#i4mY*0M^TG?rLtw{7Fp=7pw6_sq>lNHS>m5NTUW8XG8i+_3 z8B;TqCK+ONC0 z`Z<^t33%G=#uju4Hi&q!G}s-9cEOQou<^w|(3UuP1jR2Ir|PSHVkt=H5;VI3`&Ml! z#2HU4Xu_@T0V#L3T)dOma}-VDGpL*orll#Ty?sd=o}3Av9XQGX=^ZWdR>i$vQR zK?%W^Cn)$@BQzz_FZ=|MV3Gj?<`;?nAlZlvh|3_+TxejJxawx%c>sL?RYMCYMW{dU zzJWf1?S}`i&tL?ge!vrW0W;SUWkt<3c?TOb z*KS-dG1ngO>8s3j9iAUB*M7vGxEiM<_Tq%}Mc5T_P3@h&2+3MJyU=ymwFjQT58!s{ z!oz#E;^LKGzVZp3x;TKy*}DOAANIX+kPvb%*M_MB2X{~JOXr4h^U7Qm{q-Sye<9xP z(=fKW4k6uvXWMZ4I1L|1+ws*I_)n0BU9QUXzC-B?r}j-9*m`JcM|#`k>6P23&zPRx zhj&;f{^F0k0Yf7ZVL+iT-U$ijQBW3->%*cBG|T6@5mU*J(3=5)!&+DX!id2c6*1VB zB}5V=wn;Q2#&RpjdppQ&C)T1a>~3U4kLVSBI3qm(zqx~8H|L85SfCb)EJ$-+z!)F# zj2(Ee7!f702reX+AnwOf$g!hhOq9jAm=KeqB9@C4Vx@SUc)d7XoPj-))#6OC1}MBv ztQTiN0^cak7Uv*b+IeD=*euQ$7lA;&|fi~kXS5`PviisMjJzeI?D<%DQEC@~D3e6n8pWrGaJMj4bL8Ae=|sEol$f>v>X9F_}ZR_0`07NjL@>Bu6M z-I81+7t1B`G`UnRlcRD>mf=ZuLQcwxTrOA0mGX7+_40IihFm3A%QNK~xmK=|>*ZN; zgWM?3mgmTG<#}?G+$_(R7sxGgtK25H%N=q`?vxkGU2?a)NM0=W$h~r(oR<6L8{`3b zP#%(pR+vOedBl2(Mo${mdWAg9h z$K~J4yX4*S6Y?H;ulxu3kMfi9Q}UnWee!$r{p)}H|4kF)AHN$-{dp$-{p7Ycjfow z_vH`d59PD+NAkz=C-SHAXY%Lr7xI_#SMoXeAM)4odHEapTlqWrpYr$e5Ap^1U-FOg zzvchPKgmDK7v*tTl`nw^U~zz4gY5>arie!eTSUKVPyyAbf-0oKDx#urx)@gpaNsG` zq?)lLx2iVPt~ykwN~Nve6>IgtA#47aw@M1%7RB>M-|nG zDychMnp73FT&++m)$7#j)#>UCwF+L*&QxpETD4BCN7SGV zYNI+^oukfG=c!F?TgUbRn6tNrQ?>VP_^ z4#C3Z5_PG%OkEDYE?1~G!JErl)L+4`?Um{(^;UJYdYgK?y2g8W-|j4aOqz2~9zHM~ z*neQ^;PwN%_a6cw_iWuWwSO1A&*#cxCM^fI?>?}7&(z+-dk*d1zvpuOIA5AHX+@`n zoMX}vlXCV#-rSFv_ao-MROIhUwfhp^j~jTC=6=$^nVjJFlU#10qMMN~RC4^hQZT7y zQro1a+=?l$V#=!+_?0p5j*AcO+PY(^e&_Bzd!}}5o4!ovw8t2tU3TiY1NiYcSHPa& zg6#<|(4OD|9q!+}ljZkLHm~zJSqA51`Fkf@`<^T4WVu34mf<@&ZlIGhaB}AR95=wp zjWgUFcZ`#(<>L-?@+N=Yl%F@{=S}%}121pN&vOSkX26`hsZX8RBk&AKbfp&(zMX+o$SwPVEJXx#vrTPmQybgV#x}LF4HVnd#x}LF zO>Jyb8{5>zHnp)Uo`d@_s9cJZb8p{sc$;U})Yb!sygPPp-8;Q+hj~?M*n9Zk?(JLm z?3+F`6`XUuW7{6{i7BIK$}E~Pil)US)8djDsgmjOlIii1X=BN>v1Hn4P~Rz;wv`P0 zk|}S@z#TJi#|&J9n9i7iJ7(aGnLaC+I+mBSY zAE_QEqhh54a**nBkm`0I)#V}8_=i;Y@1&t@NKLspQ*O?b%j1gord%Fh+?#Tl z0^(k`6R9abXUb>Fi07t!ri{2Z?U4A9U<6g<}e6Qrjb-9(1if&hB zl4)V3!sCfl!$YdugH*#q%J3>Yez@2Dh?L<~c;%^7czlrR`XM#&czkeg;PLq2UXKq_ z-9JbTT<-Tuh5H?;ZZ}c`m-`*}rk>pIxHt9We#gCm%l(dfQ(x|P-0OZvs>dCv?sufz zKNarxN`?C!sVSfP9rvbu?swdq@(ZS2yrSW`X;;CNU(k4m=cZiCw9hi|Ez@sIPbwA5 z^qXbiTL!*m+GiQ~mVs{>_?BrO&tt$b?Xyh#cwSd3wkh8><=duwUTg8*l+SA|?oIi; z*5clj&ucC2P5F}s?xbCJI5&~YO(Fr(Bs!oV~#L3Y@RN`3jt`P|+DI{?g)tEY4u@mll6%F=(sI8FY(4 zAN8HhU)ubo&0pFje#b4c$N7#MW}EC>y2CYdxYiEmb2y*F`5ewya`8am~iKW@B8lF|OGdgC1khW$so+ltP(HD05xP471EI%M7#3Fv~nRWd_Cw zRv72(%*-9A_xwoPC_Lk8_nKxe}AyXOj$NlJiY+zDdqE$@wNZUxo8kbUm%C z?iVX-?sPsYtJ`j6bp|V|t7|c`TRB}%E64d5udSRe!OC&IJOj(?(yctd%k#T}uCB%C zZ54ENt%B|)tH56t%$Hm&wiwGTn?W;{TaNAm z%i-*d-j>5Ha`XUL4%f_KD2)0RqrSzcZ#fK;XMn{sz+%+581-RIuPebbz~UKT6*(W{ zyj9dFWsPu^cm`NJ11z2a7S8}{gnMa(yLE)2jBtmHaEFu_3Qr5G#4Rdui%LAUCCe6SexEyi+-vD{+xw#p2Q5!zyew#qzWjMr9~Tf~@bF(zA#$<{cxff3qb zgto>x`#85@oU>1GQzkTCTN7N*2`+ttyLEzlVuJHca6X<7)+D!G6Zne7IB!*WzzynG zxI-#jr3wS9aJN>tmns^xoz-=*vpTz-)pfz%7>%vX3xl22P;6cw?5u`j^E|S79@#vP z?5wV*&5MT3)5+%PWb<^ga~kXHoUWjq*NwLGTpmv@J8#}`u{_mmo@zEvH9N1{VDrqe zEzV$Z21~cr=9yzNCfg3ztjNtQaoy$Yvz6nVQ;6KW(O; zHsg}bxMY|07}<}{< z|2n+RIAgjzhv${UUE=Vv;czE7+YPKAV|sGOt5A%{Y0k9bPuk&G;f zsE|`qBTI4^5ppOMg%G7usl;@B7cYoA18v({;G7`?^nSZLKV; zwd`!I2r&^=qY_foGaT;I-F&3=axyN)qO{RXg+*a%ft2yv1$-yn*1LU{9AA-6#Ql&T zIAqkIu?voWJe#QMd7@fX?jJOMEXotm{v2rR{l$}qU+@2%MRbFojCBJN>=fhA%i@(6b~e7auxZGqXtbH zEAQ1PMSV8fCyyR9YS;(e7hfXdXiJn$A3J9JgijvraT6KG*23P7PUpD(!UaFgj+kS2 z8nt82){0GvzGFVK(`Z`no#-4-Pp1iF{r!<=P5)6NMJaXd`SL!ER993a|GSalwi8hE z@J{1P7IXbBBQ92AiYXom$xGztxI zNDoq1{#7@^X(QAP)DJYaMHoU|%;eohy|z!GDBEd*p>!!h%siy$1EL;xL{(81dCZ}^ zjhj3k^(Qwm*Y7r(#`QppT%d@^;&))~bg0h8e1kQw`-aa+Xh>3G0)+#sMGu+EU^ z1<>I@7oan6A7Ga;&=j!Sw*c%O)ByxgaQ0YG59lkPN$SskwOxh0-LruxQ=q$mM0LHD z@?P6GPQ-v?iIG8^qPn%-_piUDc@w)QGOGO$!tV(72f z(cxGc2Os{0w5`z&K2jH)D0@v++Wd}>zjypRYoFnZ+<_+Rfa-t&d%U2%P>pzw+Cf+! z#bc0x1|4FWO*u`9b7uNA5tnr9o!r z{YG4*779oWjlKgRr_rC&$oB^Z19oFup;GgyEznvmr+O+WdJ1JW-;=-;z-~7axD%)k zybjntdkKn$0bS7%bRv39PY>G|2)#lX2Hiro_wB|6C)qu+dlBpA?V+Dxr-2?o5c9`Y zX6H*h8P4>y(+Re4GvNS&7UGc1IAAo0iC#zWRWW-WFz#*U=qzzrNxkfhS^3|2&;s+& z0VC9@AiuESnU^|dW=L|0RbqLpUhL8HKfA;dy8 z^PrK=WUfDGw2zdduL&67IjHGRGbo+%&;&68D4-31Dr1gb^b+k-dyi-XTE!_hjG@TS zq7sUsc+@4+Dq5GEk-RpgNlIEuAmzPgO`4g_mZUaFZJ9bV^~KbWQ-4eSvqfo(*II0B zab$2>Oj=x8Qd)9aMp}oo{Io%7#c7eW`Du&NmZiO(wmxlJ+Rn7YY1h&vy=r=m^px~g z>HhS>^kL~{`YY*|T32hG+WOhnYkK$mF8JHizpeOf-__{#0nsYa`qBQ;Z=+F|g~?Ew zA@w6IQ=d#Vq56l^OHjQOs`sZ6G*?e+l-4XQJ1sY@Puh^QN7Lq{El69MwgQ^BLi5LI zXQ4R;nrlOIW_lNBo)66zTYFkJZ9TK~%HG|-qyNy%(S&Hz=owpc^lWrMwBJ`>d^O6?@Y>~8A=7565b%Gj@Rsdp%0FQFhLF&xjx}xDI9-Lc@k_ zO*9YakF*c;jE1cy`mF}vM*K+=2gEq+U3kR-@$H~*IUw!@y2Al+Z_rO15ceUz6zhQa z4&unAD#&0Blsm1=_;_@n}#O4&bal1_|4$JHR{^)K-p(!Q(*h0`5k7Jm>)6KBOmt4sk#{8FU2j z0LrI;+6qS@{TQgtKLKf|xbz4x1?eY1rvi^5{Uqo#yYfk7W`b%E&qlh`0r7L7^BoY+ z0eu-*g7(jY!tIwZjo5Cx48X#}FMzHE%8*_JY60&dZP#yeK)ebRLvsnMAxBmt@d1Dt z#&3Y`bwKQx)@Amlrjz(LEE3hvE#9KkV0OFTnaRqk6it0$0gC;m2-U(U@zy*hQf!1|Eyc@J3 z&A1C2=F=5O+k-4AV~qWJ^U5Qn}MEjK++uatUdl%)uaV77l7Z8wu1(dS@9Rr zH-OqMyN+}_;>+;TWkxy!6kY;IvOwVtfW!w14_vN+v>z0m07yE5q7#=9u+j+>0ecyX zn{>f=U54_@ZIQ_b)gZyDb-62W3(C8J_I5yW8|Z@$NP2>faX^CLyZn#?l3t(_0qomi z^G$a^f|d00GY&`)h?g;Zmm|y3=x!u1egFxg^71MNBm+R-0M?=W9?-WOklY7a25dz6 zK+ty`kPHQV53q-F7-%`L1L^xgF--xIk)W7@m$9zM1H^SNA4296WQsvCewV*MdUR=W z!nLL`2pW9gZ~F-JpC7j4)$DxKs9$mSr$9-wVqgKVfhZOBH;0hs>wycnEyn}PaAWRC)CvMxqkWs9MD0=D9!fIM&NqiB z&;}R*piOWkQBDRh5J3Hnvv4CvKJNfF!a_8<30B>_l&Cu@dKBYUdIC4)p1AnI3wI)a zcLLB2mmJXk=ZNls^+V1R4eN^QeF=_{bAVk$#gH`$yko#SHkRn2K134^6HS7FQzj5S zHjii;+Du=IOXg{uD6e86d;Dy;MiJm)vgJeIV=Vubly-HNN2-re2F9}$WBjo@b zDUmjp5iLY-UV^g4wSjX)FN1Fh6fK>KV`e$gYa57G%pzLVm1uoOqPH&)m7)EHc%pY^ z5LqbO6j?;{E)3WVg_{o%ZHMLWL-5W`IL9NsXDo1<=tGq41O2Fy=n&EfUHWt?(P!w% z@jgUfW)Pi%;IGPw&g2rE1@Cveh|WRLk8_BAM*YvHiGG1GSZ(OyQs6Ac{}LKkj3v6< z96%2)?|1%lZbO>5O)|sd?U~ic67qb?t-@Y;3+`95beTPIc~}(zBLIrPu!ys4vO7yKILuAO zA+8)}v<1Wy&~8En@uUd=>K;wP`3r3yKS5jq-Y4LYf1vE?O5$0KfVIT`oP%Q)_@9GO z&n*B@H)k1+Ra1%QcEh4P7Y*hf#@VVlFbmj1JTDhO*$XIp0Zj9g0a$Gs0P+jk5Wmg8fMcDw#7Oe%&6EB9W#q)qjCGpFJz**uY@jyQS z1}%*RIszjAC|tS)I8FQtguDX!svp=y{8|Pu1Bbq$zy_d#czI6%GL}QxiZ(zAaDn*s zu>g2i!tj-lw+cEUs~~XIGUC-xzB(6xvemnY*Yp7n5U;Im9}0=zf{?eNu&g8ThM717 zLb&C}K`$9+x(UEuoafMHYbDNdi-@W z<9~1j@y7#k4r_$76$BiE($k=4ngg)<>siESHxPd}kN8K>^8<)~fr8%*Y}P2d0)u}) zkE0Lxua%JSN}OH};dIgyr;>9d)hbA8H^*UODM{S}B=yh+PllxNRU96QBRDnqNmA$F z0B{x?KN?^~6<;AqAe$sOgCx|4Bzq_cR#WMKww-H}6m=!JDVL-x=&exLt%9V-GVId> zun)%qlSuj|U>^qm-KVj=E+DzL8_6KZ7?MOXd=a)sp_pM0kOecLb?~5>j=R zk!palW=l!6no6qOP*Ryd)-F<^LQ>fYzzI@0m83eHCUxU_QXMyt>SU10?Mf=oPbxoy zRKXTfVLSx84SYS(z87@#O`?dp8wK|!lNyM^5lN&*?j`jgl#gmoYTQavk8~t81@uuc zl*}VFqnOl_Xg3o^Jv)ii?17}71JB%^q)MTv^c<;qGf2(P1Ijq_*+S}bDEcCq)Uj+*$Cr}&@+hejpr_FGYm|Spp43^;bCB`lCQ?5eq<$Gd>er5> zE<{$6x`-ZKsU-C~@CSPM*D_Lg2CSkz$uJs`5d-S&Mn>#V-~t(OlgNlK1~!mUZ7dlz zP@gcBj6|U31Tt!&u671cNk*M5WYoifsa|t3l9rNDzl4kiBgnuEFd9Nuq)8t#nj(So zh>-%?yb*AKj22hPNIyzOTPV!}{OidGqAmpHPCzdB@=#v@L4{Bn-bKdE3&^--DjBy{ zlF_|38NKI_aR<nG#B1TqE|k}(MDzcIKg8AIX$6bzXJfO$BC-46vL zT9WZVPhc$>53VEwdx0?;>4yf8F%E6VZy*CR#+VA8$6-{-IWnftB;y~D_ay3`M%^<* z$(X%~jOS4{7c%Cd?F%quK0L92pk(nDGO*kluQnp%wZmjAA4|r{fn=l*{h#_yXg_7`IhPWpz8>DP_gEOS~ST2I|YQgsyX>;NQFk9$u;P z$`VzAZ$d6tl~k74XXt1-LZ}=?ag=~_=)bfnPWQ%fdg-}a+EyVCwTd27U#YUvCbv$P zE?oB7twU>gmPEI!zGd6Lp=damOcxlwlXzQ1Dm*|NhUrWnc5Kca`!Rkfbt z@rLtpJRBs>s9UdYO>bvTx1V>FjUF|TJp-fV>gBR?qd#S-!DU;x;D+Hc|* zyX3=(&v>|td0`_?_oE(-lNwSs`O!P1?32d^#oLbXKl4-qtFo;Nlez4~OZ`2bAO!9! z>-fq|hNnEbO?5Auxjf414>E*eA(I^ z>m()`gYUr*fi~OVf_L;HBtp6;+$d-apb*)!0c5DH&Z$ zi}6-xZzD7Dq^ezN99tqyrEJBqR4(0nyuWmjva~N#@eocq!eh#Mo>=HLj2Kl?_RBXn zuy+I`E>N3Gn|vGM#L~Xsra*D&M+f+>hbtw zPBdF*b0f2BHfLIsvhl1_C8ng8R?ki{+h>c%Tyqy!Gq>m92`HCVZA(f%zz+Ol zQX(UyQxXODw(G>@KrWYXE}4UG!CC1 ziYzflcjO)3CegieLcMDRJMj_Wx6SxmJRj|0_RPgo(YMV-I(Isk7uHGi;lGqR_%AUP z|KVLqASKb9lgBNr4SBpss^axdf_l<^HlAqqFT^Xs7Ylikl~BY#DeqYL=LNOflAF1^ zhd;J@_TZ>^)1zn9DYd}rdpoZ(c-F?g{A?WGWX-yd^Azu}J{ru;B^Hz6E5oZ+zy9Y? z7BSeW9L_#v`!e$ZJQemfj-FKCsMoB!ALP-TP@h{Q)@qTM;z?|gs6fqQ#oTh^DBj2R z+u8%8`A<-5?-=f0rFsocO%M6%d3~Yu)EaE1PT?9c2IlolD_FvZR4k%kn>wh*Sk-6n zm!6uBV&Z$LDk4^;)Jo@+I58`q=2q4X&me?(wAFJC?_?fi)t<}u#CaZ5XJm!-@JqZ# zc&pj$Wj>?&N4HxsOE{C&7B$!Eu$0fR`aEdJ;Wv;Qx0VNSrg?ZRR+sIj@dh5E zziVc{foJ9g=A<{cKfh@neghWtHixg{yAZ$S>-bhRM)fwUzsbWm&z$fk=f&AEH$<+f zUgq4lcroHF<84moj%L5N5lzF*WpCq|e3E(MZGMCwGW(Qq`_dtat;}go6eCNEZVyrduMRLSb2TSB->#!l0@WY7cE_K~H zP(`MDysuzR-lWRSz6tV$Ew+84tc~$BMd<-T*16E)RaKg2ZD=bm#jwZh>6fV}Kj4=`*5ZInsp2uHnX0rJ<;s0={HHbbX8Dyp zeW{8%V~xF4?y~pr{oQ1qE`*FHjI=~@x4_&dB%XxN^|p*tbyaD1@nIE$qBO8uosO@s#5K+`c9UqN}`q4;i-1x8e$Hb zCP|>y)M;{~pz1Gc%XBHj^u?f5s;$<;Ps#U+w_Eod(`b}n{|AvbYgW;KDQcdm&St88i>*AxI;cp@bIbi_Y)1!m#p8*i#_IRix8y4H+YWT|^E}lGM+Eaw2l%yVzA6## zW=zJ_YNNHHP~ByCHb>8@;g)f;iWvNrIitJU!zos`9_n{PRlyz_?yXvQJhh^~!pO1x z)Kl!i8)Ef`Re6_sP`vL@EwxsSHb)OoWjK167w%EFn4|7d(~z-{8B*OlnDW)js zvEtZT3NH};RC}CIz6EcJui(jD-lY20QxnuY;p{|o_aWYK4-0*rru?)ZVjHU>Q=QIPnnIO@y25wRiIy4bJY^V^MTr=-m^ZRuRc+p zJEL3GQfuu(g>blP^;@K{^z5*ztWZf9zH8B~hEhAtR;$&ETw(26tv(aPvbFFH)evX2 znv{W+;R9>SI@Lw&Ze3finv1j-t9hCFEjAYC5c?-!nABU;@50|%wYRHV4GcuO`pX=; zQ+1byY?bX)Z+fu94Eaz^MTnVaK2$9%?><$Nt)%_xKIQEZeOHxX;e7lfHQGM8=KkdX0`Jw*X(g#wYDamSAU7uzz^eoL;y_rRSjg@yl*jE7Fu6lP_;zj z*o|LXcW*sKAF(<|N^p$1?cFyZY zu6n|HJJs+S3d^UtCEd7)*ux3z?UqI%&UTg9rIuqezTVo{syqdV+fS_W_Qn%zUoOhc zaHbK0$4eh7O0vvMW1+!wtgN7c!(98)+>Q;btJ%gHdkUplD+-JvueTxA@bA_8*2Pr%)YEdsGlu|FnpxnxmT(vYA-#jpxmrBXC5-BOkEy03|ZG>?1Rk=71 z+BDg?s5oG*dCa)ky!@Ck(Hix*QKGDO-sI2m6A<@; zbY+?`hGVKSI@qE#B~;srOgAPg_L+Y?X?$iLe9Gu&HJ@qpPgG#Ao_Nu?)8mi-;@!01xZli8E0hP_w1IS2H|@cNV7QyM zUza`ZrmIi`+H9^nYSfF=grC@mbG+5cO&jnWo=`dE9{d!khntQ;`8YRSh5R&whT%s< zW9R|sA2Wgt!ed5HNWOpA=wagqO&B&bdGO@qn}>|)K4#45 zc4jX6i8qE-BVxZB$L$mc6ouy&&(LoBcR#em{^bugh`IRX&;REi{&3cR{NN|@fBD7F z|Ng-b-^M-pcJ77yLm$3_`{KgX50{&}xjzrU<>+3%j|bvfG?<6*P+W3`G6|ALj@z=hOHJ+~8*5y7nYL#WVS7 zeukgrS^Q6)&Cl^1TsG$7zBi9w;Q2VbEZ`S;A-}|naDRH4m+(@4g0v$6I(SZ{zJ;&O7*hTugWI2fUm2 z@Lv89m*M?*j1Th>%*`+OC?DhF{3V~jb?Ov<#iwyg{hGhwv-~Z8 z$KUfg{(*nwpZGlg%)juje1U)Ci+qVI@IwDegn#F&{0INZe{m&WMy`_)bA$@Vg?k9K2-O^tM$USne+$RHNkPMa~GE|1i zaJgSb$VhoW9+YAkC8Kd47%LCSI2kV!WMV`fmPcfgOqMC~s7#f|0le z`C7h_v$&UhC*R9C`9Xe^pX9v!EWgOFazTERi*iXSB63--$nSDh{*XWAFR7Gka$TZW z3|I;N*@B@wDh3yxSQUp8b5#|us;TO#2A(P=s+y{ns;%naAqIXqt?H`=s-bG68mlI% zsY=Gy)J&zS=BfoQ?&+$fx@aSP8>S<0s(eicwb6;jzMM|Ds);+EP; zbyi(euF6ySsz4R0A{EB1x2w8Y-J)()-BfqgL*0hU!tJV;>aF^yJ5*nFr|PHfQg^HV zYJj>&-HSu#Ks87WRzuWKHB1dx_p1?VqRsU8xucv+_3RO#*G|1 z0ZXxGQ1P&_BkcOD%=|o;=2sswa@>&OVWTD%PZ&A2c(PNRl~d%>aKuUYGJ`J7cB$s~ zWx4rmw?5m==Y({-oWJuqI$sdhw8+gDxpImM-Eyti7k2uQy+Vc1m=&hrj^aQuitLx;r;A6Z;HZ0O)IlN`=Ko|YKM55{O0=;8uxL7-48jsyy| z#z3Lg7}PG#3TEgA!Hg`2F_@v{1v7N}V8-9=wS~b9ZBZ~oD+p%lE(SAQIhk(zOx=TE zW`Qj?k`>IQ?1c9W`tonbXhYl`wo47hct`9=4j!?wybB^2R9Cx>&L%_ zPS0_Bnd9~{$L*y%0fISh?{ZuPIj+JyS8kpwH_w&pMrSaR=L*hq1?IVK%Xb;`U9aT3 z78ke%xic+T=;qxp4i>rcid;EGuKV2?8FXi4(4CQyVA!=R>{=Ff4RL2&(4BEX%rtvx z35MOCg|%TJcYcI2G+!t~D-LC7#UXcIgfcXLC_{TFl;LC-7Khw5D3s|obmv7V zQx8$doe!ZbZE(nq>yXdQ`&>Cbx4mC`Fywd3gL>#f?$CvtH4O0|a>pj*dLZPkMfvUw z&v$#4@AfR;?ODFtvwXK_`EJkhGqi{DGqi{D-45it6FA>pMDw!|GZDBnKU?=WKU=#d zKTo?ZKTii$ex6&O=hO#$en)V?7jSx5H_?<&?qjJy*gpxMs;gM`yl zP}hJ=*8sgpqTDq=uaU^R2I!b53hI~ubq&z2M&9kYUMrDzd#=|?CP%SX5hY)!!IIM><9M;r?2nDV{ zJx0hoV+87W2-Fp*Mqu(wd;|04b`qk-j%CekGv~aZ&MKzxPrCok$1)()Nwti zGxnfP@PTR%g|+L$VeNWQw`bb*$h)4>u1DVWl+QK5+1?@}QQ;ck(?byUxd!-L#eTPk zepi9t?V(;+!ePJLL%*xQ?<(-Sp7Xo%{jPk!E8mX}V*W$G--_HG`rRJt`5g`iTmu5G z0Rh(lz4fBrH9&8@$h!vUtrvOM0KN4h?;22)>CFEyqAg}(W?^P#5eOnD(;@kq6lhZD z5MM^NUFXZl)1=TLnHiesCYf0}=hMWmiLC)c71Si8Np?)cF3r;XKF#RUj6Tii(~Q2b z!{OI0{aTY>Gx&8&zi#Q*viAIQ;_x=(=uozqVU zlda9q*1pWv(sFc@99@{B&Cbzg>jCxUX<>O*(oDG=G8SFVGz=&=wYG zF$J2xK=T)9n~Jm*McQjcT1t`TE7E*Lny*On6=}Y3L^FmROZ^#+Yy25*&SCUtIKB60 zI2`^A$6mhFD-nIx75`nlDQW%W~BFvvggSuJdI^>|pcjAou$mgZ)0o zQGTCp>T{cFy*l#!I`aK~ZIfTu1)Sdd1KK7X>wX>UemmBoNQb&VphY9p?fK^qI!^Eh zHNOsWe^B=+=!}6ss7(uMF?s^{^#t(i3E&TEp?V(p^*r$F3EU*#Dg&aVJ1nhkRJu6xPzh+OuKpr?4YCkm2wLG930mhGR@1!?7Zu zH;F)oBPF2sia>@VC7@?hK+mQ?M#PooSQ^OG()6qf=vfuevnr73gkB)iu`rP3bUTox z73w(_$a3qnUOnRidd3Cxj09{Dy=Aja=+NsU<7U|j`~Rcze*lmYW90w< delta 12010 zcma)i33wF6*7iA7)ic?*$wmlCCX>vBeaXh2fKgEaQBV9i4}#!pH+^yO}( zD~W>Ij~p|!+Yqhy7x|>Vxr&U=qlQcxD>jDvNS(++g_O}lMh#!_bnHo_`@xCKv148s z4@Zeq)l9RTq#|D-_wyoQCe4omOqH|-SuQ}zj4=%ygrWtcBrq(n>)}Q=8km7xZ~X^?l$g@?tFK5_Yn6`_fzhX?pNGi_Z;^J?gj28?xpV4 z?k(;e?t|`Q?sM+n+`qdoxv#pfyKj1gC)gA2iT1>L5jPERvWTThlJ-&5cj?$JGq zJ(rt@HcxKuYW`;P<$Zhg?elZM&p8+0xcJ${udn*Ow+j92xA}wojr?8w1N~?HKlyL^ z{fN@+mH!#t1}nM`|0_Bj(f#WB-Bs(l{6C^Q<|Zq+2rIZmcdEO+6qMO;g%!-bF z4!RSa6`b@3_#^zu{%-#cj{QISm-`F-{ZD`AJzaR}U#HHU`u5brCu>hmIytVUzUD^F z^_t&nN^9P!nOf7cCbOn(O`Dq5)q3@t)o)Z!tR7lDqNilRST-- zSIw)MQ}v*)sdmT|RWqvoQ8lG%V%3DI=c-0j^{9%k3O}*+_^RUzkIy@vb39?? zODiWP>`B-iJ|X<^@SFf4J`+N)AilwW@ns&(&zaNJ*R9F-twYSw3o$PweAwQ=)>Jm9 zbINU)Q~hxhb8>H{|EQ_gWwx1%AK+@n~>w3&dl<2m&A;3xl*@KY?A4y{jPXu|T{U{57x-1sK!I-&i0n2cH3cK>k|r&lZTyhR$0c zt^ofA{0lnk!M|G|-UzO>K)eb3w*}(O#B~bo-ofx7|bq&p7o1@wme9rz&u$Fz$3`720vzj_#_zf2_QZN9taddJ`FAc zhTulTXCYv6)tQs%NAL@PIf?!NW8&4}=ELa1x>qcq|G-l$;0ynlLv_=ka|Mh^Rre<3 z8{oHqw;|sGms%i!WStLqpGYv+I~QMbk-%8gVe$YZH1J{zBy8YMERZmV2$KpR5ddCl zfkYq}6A&N~1YQlSfgB7jw?HBk{FMa~Vc-qGCL*s0hhUEXR#b=rn`4WaDWbvVc%hafiwo5OPyauV1al5ZiWfX%M2hMWpEyZba`7uf7!ENUVhd=9{(Cfwj!bNnwN zk%0teX59_Q=A^j^+=ATN>{WmvV**`~7D!}*(K7&vEHHZGN;u?faHItiUBC_i6IOHu zBZwP`S&LViE+Gz%m!`L4{eKw1YQZOLjDurH5Nz| zfy;q)$R7e;Z-K;c@J0Y*DV_pX0^1;u0OOJcNIVPv8o;_D%s>tSharz54!VNzyJC*t z7+-DUo82%cu?@kTz#XdLgDIQa!Akyr<>{mMa_jQpP4_VOEpOHQ6V@)sdGe$3Z(CfB z;%&aK^QQ5J^4|Gnq2Z25XQY#XNb`$)s<)G=+|@hQCgS4CJN0{?1;>>ieq@X&@7DiA zyB0XUJ9c#7MqzfzH`#C|f+-0}WLN3zEr$R6?;PqqxysF}Y? zo|bETe>^!&=^_;$T`QuAZed8NE;;*9_U!IvMu9wCI? z*JxdHU-_3;%9oW7(6r~!!29I^>+e|ol zG8;Vp&B9zx{i%x5<~pR%1pSuMt`& z^pe-;oWPxgol)YKTa8x}`97tE`M;6NjQuIRCJY^Dcw6%eDZ&w>FD~Wi0H@`24C� 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 From 9276778181619be4065715ab03d36ca0b318667d Mon Sep 17 00:00:00 2001 From: Matthew Runo <74583+inktomi@users.noreply.github.com> Date: Mon, 2 Mar 2026 04:00:18 -0800 Subject: [PATCH 15/31] Avoid repaints on device mouse motion outside window (#7866) ## Summary - Ignore raw device mouse motion unless the window is focused and the pointer is inside it - Also handles pointers starting down and then moving into or out of the window (drag & drop) - Prevents global mouse motion from triggering continuous repaint loops - Applies to both glow and wgpu backends ## Testing - I ran the check script, nothing seemed to fail --------- Co-authored-by: Emil Ernerfeldt --- crates/eframe/src/native/glow_integration.rs | 15 ++++++++++++--- crates/eframe/src/native/wgpu_integration.rs | 15 ++++++++++++--- crates/egui-winit/src/lib.rs | 18 +++++++++++++++++- crates/egui_kittest/README.md | 4 ++-- 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index 233ce5a49..48557675c 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -448,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())); } } diff --git a/crates/eframe/src/native/wgpu_integration.rs b/crates/eframe/src/native/wgpu_integration.rs index cb634200a..7cfdab148 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())); } } diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index 54059cbd6..c010febd5 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -640,11 +640,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_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. From 2be6e225bf9eae7e1e5f224f53aaf512a952eb66 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 2 Mar 2026 18:36:04 +0100 Subject: [PATCH 16/31] Add `ViewportInfo::occluded` and `visible` (#7948) * Part of https://github.com/emilk/egui/issues/5112 * Part of https://github.com/emilk/egui/issues/5113 * Part of https://github.com/emilk/egui/issues/5136 Once we support calling `App::logic` when an app is occluded or minimized, it is useful to know that it is, in fact, occluded or minimized. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Cargo.lock | 8 +++ crates/eframe/src/native/glow_integration.rs | 8 +++ crates/eframe/src/native/wgpu_integration.rs | 8 +++ crates/eframe/src/web/backend.rs | 13 +++- crates/egui/src/data/input.rs | 31 ++++++++++ tests/test_background_logic/Cargo.toml | 14 +++++ tests/test_background_logic/src/main.rs | 64 ++++++++++++++++++++ 7 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 tests/test_background_logic/Cargo.toml create mode 100644 tests/test_background_logic/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 9140f2160..eb9a385b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4303,6 +4303,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" diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index 48557675c..4cda3e762 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -822,6 +822,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 7cfdab148..d4dfeb45d 100644 --- a/crates/eframe/src/native/wgpu_integration.rs +++ b/crates/eframe/src/native/wgpu_integration.rs @@ -852,6 +852,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/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/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/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 + }) +} From 1b8a9fe95ea63278ce7edcf5371fc0efb81fe137 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 2 Mar 2026 19:30:24 +0100 Subject: [PATCH 17/31] Only run `App::ui` if the application is visible (#7950) * Closes https://github.com/emilk/egui/issues/5113 * Part of https://github.com/emilk/egui/issues/5112 * Part of https://github.com/emilk/egui/issues/5136 If the application is invisible (occluded or minimized), and the user calls `.request_repaint`, then we should call `App::logic`, but NOT `App::ui`. There are still some situations where `App::logic` is not called when it should be, but at least now we can skip running the UI code when the app is invisible. --- crates/eframe/src/native/epi_integration.rs | 25 +-- crates/eframe/src/native/glow_integration.rs | 151 ++++++++++--------- crates/eframe/src/native/wgpu_integration.rs | 97 ++++++------ crates/eframe/src/web/app_runner.rs | 20 ++- 4 files changed, 164 insertions(+), 129 deletions(-) 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 4cda3e762..724ddc6d5 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -545,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 { @@ -556,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); }; @@ -571,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 @@ -587,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(); @@ -622,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, + ); // ------------------------------------------------------------ @@ -667,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); diff --git a/crates/eframe/src/native/wgpu_integration.rs b/crates/eframe/src/native/wgpu_integration.rs index d4dfeb45d..9d6283808 100644 --- a/crates/eframe/src/native/wgpu_integration.rs +++ b/crates/eframe/src/native/wgpu_integration.rs @@ -573,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(); @@ -617,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))))?; @@ -637,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, + ); // ------------------------------------------------------------ @@ -685,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(); 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`]. From 124bde4883bb7e4fbe40e28c12ef93a79e402504 Mon Sep 17 00:00:00 2001 From: RndUsr123 <150948884+RndUsr123@users.noreply.github.com> Date: Tue, 3 Mar 2026 07:46:45 +0000 Subject: [PATCH 18/31] Fixes the overly aggressive overflow elision in `truncate()` and similar for os scaling other than 100% (#7867) * Closes #7818 * [x] I have followed the instructions in the PR template --------- Co-authored-by: Emil Ernerfeldt --- crates/epaint/src/text/text_layout.rs | 40 ++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/crates/epaint/src/text/text_layout.rs b/crates/epaint/src/text/text_layout.rs index 5b2400646..9aeeff137 100644 --- a/crates/epaint/src/text/text_layout.rs +++ b/crates/epaint/src/text/text_layout.rs @@ -495,7 +495,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: @@ -1166,6 +1168,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; From 20f3cb52cc53730fd15f7c154a4ca373bbf68055 Mon Sep 17 00:00:00 2001 From: Dion Bramley Date: Tue, 3 Mar 2026 09:13:56 +0100 Subject: [PATCH 19/31] Make `Galley::pos_from_layout_cursor` `pub` (#7864) * [x] I have followed the instructions in the PR template This PR just exposes the pos_from_layout_cursor function as public. Hi, I'm trying to make a git gui with a merge editor, and for this I need a much more efficient and flexible text editor than the one currently in egui. So I'm working on building a more suitable one, which I intend to contribute back once it's working, but for now I would like to not need to fork the entirety of egui. By exposing this one function I (and others) can much more easily reuse Galleys. I suggest also exposing end_pos, but I'm not currently using that. Let me know if I should update this PR to do so. Thanks for the otherwise awesome tool :) --- crates/epaint/src/text/text_layout_types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/epaint/src/text/text_layout_types.rs b/crates/epaint/src/text/text_layout_types.rs index 3e8a53d9e..b5bef62d5 100644 --- a/crates/epaint/src/text/text_layout_types.rs +++ b/crates/epaint/src/text/text_layout_types.rs @@ -934,7 +934,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(); }; From a354c02e7672ea3b548395fbe4263917a807d677 Mon Sep 17 00:00:00 2001 From: Lucas Meurer Date: Tue, 3 Mar 2026 11:35:29 +0100 Subject: [PATCH 20/31] Add `Atom` prefix/suffix support to `DragValue` (#7949) --- crates/egui/src/atomics/atoms.rs | 12 +++ crates/egui/src/widgets/button.rs | 7 ++ crates/egui/src/widgets/drag_value.rs | 83 ++++++++++++------- .../tests/snapshots/demos/Code Example.png | 4 +- .../tests/snapshots/demos/Frame.png | 4 +- .../snapshots/demos/Tessellation Test.png | 4 +- .../tessellation_test/Additive rectangle.png | 4 +- .../tessellation_test/Blurred stroke.png | 4 +- .../snapshots/tessellation_test/Blurred.png | 4 +- .../tessellation_test/Minimal rounding.png | 4 +- .../snapshots/tessellation_test/Normal.png | 4 +- .../Thick stroke, minimal rounding.png | 4 +- .../tessellation_test/Thin filled.png | 4 +- .../tessellation_test/Thin stroked.png | 4 +- .../snapshots/widget_gallery_dark_x1.png | 4 +- .../snapshots/widget_gallery_dark_x2.png | 4 +- .../snapshots/widget_gallery_light_x1.png | 4 +- .../snapshots/widget_gallery_light_x2.png | 4 +- .../tests/snapshots/layout/drag_value.png | 4 +- .../tests/snapshots/visuals/drag_value.png | 4 +- tests/egui_tests/tests/test_widgets.rs | 11 ++- 21 files changed, 115 insertions(+), 66 deletions(-) diff --git a/crates/egui/src/atomics/atoms.rs b/crates/egui/src/atomics/atoms.rs index 1db7c63c6..2cb668cff 100644 --- a/crates/egui/src/atomics/atoms.rs +++ b/crates/egui/src/atomics/atoms.rs @@ -26,6 +26,18 @@ impl<'a> Atoms<'a> { self.0.insert(0, atom.into()); } + /// Insert atoms at the beginning of the list (left side). + pub fn extend_left(&mut self, atoms: impl IntoAtoms<'a>) { + let mut left = atoms.into_atoms(); + left.0.append(&mut self.0); + *self = left; + } + + /// Insert atoms at the end of the list (right side). + pub fn extend_right(&mut self, atoms: impl IntoAtoms<'a>) { + self.0.append(&mut atoms.into_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/widgets/button.rs b/crates/egui/src/widgets/button.rs index 4b4c2fe32..7ad155f16 100644 --- a/crates/egui/src/widgets/button.rs +++ b/crates/egui/src/widgets/button.rs @@ -259,6 +259,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..d12020097 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); 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); 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,26 @@ 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() { + match &atom.kind { + AtomKind::Custom(id) if *id == atom_id => { + past_value = true; + } + AtomKind::Text(text) => { + 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 +563,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 +604,22 @@ 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 let AtomKind::Custom(id) = atom.kind + && id == 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 +634,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 +729,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_demo_lib/tests/snapshots/demos/Code Example.png b/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png index 5d4cc4d49..742c62272 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:9d6ba2c4825517b4cc030b7639771d06913da86c2d52fd40e6263692335afa04 +size 76079 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Frame.png b/crates/egui_demo_lib/tests/snapshots/demos/Frame.png index 6efc3f4a9..9ce775f7e 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:a169cda21797152fb8aa69928ad3f4cef1b45cc5f213e5bfa01b8fe7723a4852 +size 25391 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..9d9d26c7c 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:01b34a7371dd8b3539ed20594a42f3cd9792d391d2cb44740aa5ef301c9652f3 +size 70244 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..5c6b867d0 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:0574527ce659559f5b0709d84903afcec60b4ffa6b7979e8985027d326fd782a +size 45066 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..64c5f12e1 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:50eca0feefe5d43db74f5e3bc08abda13c5986710cc4aaa03e9382af56264fc2 +size 86826 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..e0dcebac3 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:91110d6a1da995e3e215cc92fdcceac84335e60b5b2fdbb2f16d5ecc6065fe55 +size 118912 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..1a69351b8 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:31464d796f1660bdd5c98cf73186d7b68918fd42f292bc03e274e43d995edc16 +size 51363 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..5c3621856 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:9dca12eb3b99976db20c77a6c540cda450e53f6ded89708d2e2320194723c0e2 +size 54569 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..c8c5c6899 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:05a2778e7da867ab46a6760ea3925a2398c6b9a21d2767aef11cf98ef5292c82 +size 55034 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..8bfc2f47a 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:686e37635da6ba218c9539f8b145239bbed2bab6696384ed1cb725db657ec642 +size 35961 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..274e577d5 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:151171625b9cb8eaac3fb83260c6cc76cbf66003d9a940be1d5021a3303956c9 +size 35923 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..408cba0b8 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:79d2935aa8f0f6941167b69840142599a2994a5eaa239757d91847d4d6533174 +size 64165 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..9aa9c130a 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:a5665e3a715ca7576df5b63af14b871f355d3e1db801c20089a60640373388ff +size 150095 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 5a2b44feb..ef68941bc 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:c0635f1564d6c9707efa68003fb8c9b6eb00408aa8f24c972e33c6c79fed5bdf -size 59354 +oid sha256:bd31bddf25cf93d3ae79ce9b314cf3a3ebbf8c3b6cae2027f3f3b1593ac293e5 +size 59346 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..67d8979ff 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:04dd62767ae9c18b5e89cea5bdd243b66c5986bfdb71fb9b01772ab9d150ab7e +size 145223 diff --git a/tests/egui_tests/tests/snapshots/layout/drag_value.png b/tests/egui_tests/tests/snapshots/layout/drag_value.png index 44bf0bfcb..86dfef652 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:96c78de8d82a5cb4e91912823b88bc0465bf67f09b500e5bde8f43b001f35a66 +size 264421 diff --git a/tests/egui_tests/tests/snapshots/visuals/drag_value.png b/tests/egui_tests/tests/snapshots/visuals/drag_value.png index 2b04df5a5..463c4cbef 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:0f6babaa4f9359517f58b1160a915069c56c338b7c0d8d4306cde67628442397 +size 8995 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, ); From b73367976017c7868946ae330c0e30cbb9d5a56c Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 3 Mar 2026 13:27:56 +0100 Subject: [PATCH 21/31] Fix text color when selecting newline character (#7951) * Closes https://github.com/emilk/egui/issues/7865 --- crates/egui/src/text_selection/visuals.rs | 4 +-- crates/egui_demo_lib/tests/misc.rs | 34 +++++++++++-------- .../tests/snapshots/text_selection.png | 3 -- .../tests/snapshots/text_selection_0.png | 3 ++ .../tests/snapshots/text_selection_1.png | 3 ++ 5 files changed, 27 insertions(+), 20 deletions(-) delete mode 100644 crates/egui_demo_lib/tests/snapshots/text_selection.png create mode 100644 crates/egui_demo_lib/tests/snapshots/text_selection_0.png create mode 100644 crates/egui_demo_lib/tests/snapshots/text_selection_1.png 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_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/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 From ee3e73bdf9b6d658ef7734a6d07e3bc8237bc885 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 3 Mar 2026 17:02:30 +0100 Subject: [PATCH 22/31] Fix: repaint on drag-and-drop files (#7953) When someone drag-and-drops files onto an egui/eframe app, it makes sense to wake it up --- crates/egui/src/input_state/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/egui/src/input_state/mod.rs b/crates/egui/src/input_state/mod.rs index 37faf64c2..7122af699 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); From 699fc7e887107225b940f10b2811861b052e27d9 Mon Sep 17 00:00:00 2001 From: valadaptive <79560998+valadaptive@users.noreply.github.com> Date: Tue, 3 Mar 2026 16:58:42 -0500 Subject: [PATCH 23/31] Add font variations API (#7859) * Closes N/A * [x] I have followed the instructions in the PR template This was mostly from last month, but I never got around to submitting it. This PR adds font variation coordinates to the `TextFormat` struct, and uses them when rendering text. The coordinates are stored in a `SmallVec`; I've chosen to store up to 2 inline, which makes it take up 24 bytes (the minimum possible for a `SmallVec`). The variation axis tags are stored as the `font_types::Tag` type, which I've chosen to re-export from `epaint::text`. The variation coordinates are resolved to a `skrifa::Location` during font rendering/scaling, and are cached in the same way as all the other scaled metrics. I've renamed the `ScaledMetrics` struct to `StyledMetrics`, since it now also contains the resolved variation coordinates. I haven't benchmarked the performance of text layout with variation coordinates, but the existing text layout performance is unchanged. I've replaced the API for manually overriding a font's weight (https://github.com/emilk/egui/pull/7790) with an API for manually overriding any variation coordinates via `FontTweak`. This should support the same use case as #7790 while being substantially more flexible. I have *not* yet added any higher-level API for mapping style attributes (weight, width, slant, etc) to variation coordinates or to different font faces within a single family. That's a pretty huge can of worms, and it'd involve rethinking the split between `FontId` and `TextFormat` (and whether `FontId` is so big that we should provide a way to reuse it). This API is intentionally pretty low-level for now. Likewise, I've intentionally not used variation coordinates when computing a font's row height. I can't think of any fonts that change their vertical metrics depending on variation axes, so this should be fine for now. --------- Co-authored-by: Emil Ernerfeldt --- .typos.toml | 1 + Cargo.lock | 6 ++ Cargo.toml | 1 + crates/egui/src/context.rs | 2 +- crates/egui/src/style.rs | 53 +++++++++- crates/egui/src/widget_text.rs | 23 +++- crates/epaint/Cargo.toml | 4 +- crates/epaint/src/text/font.rs | 92 +++++++--------- crates/epaint/src/text/fonts.rs | 57 +++------- crates/epaint/src/text/text_layout.rs | 19 ++-- crates/epaint/src/text/text_layout_types.rs | 110 +++++++++++++++++++- 11 files changed, 258 insertions(+), 110 deletions(-) diff --git a/.typos.toml b/.typos.toml index 16659f4c7..6c5f1564a 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" diff --git a/Cargo.lock b/Cargo.lock index eb9a385b0..62fd9b36c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1567,6 +1567,7 @@ dependencies = [ "ecolor", "emath", "epaint_default_fonts", + "font-types", "log", "mimalloc", "nohash-hasher", @@ -1577,6 +1578,7 @@ dependencies = [ "serde", "similar-asserts", "skrifa", + "smallvec", "vello_cpu", ] @@ -1741,6 +1743,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1e4d2d0cf79d38430cc9dc9aadec84774bff2e1ba30ae2bf6c16cfce9385a23" dependencies = [ "bytemuck", + "serde", ] [[package]] @@ -4136,6 +4139,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" diff --git a/Cargo.toml b/Cargo.toml index aaf9b9398..b10a4fd44 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,6 +90,7 @@ 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 } diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 2665d5edd..55e3d75e1 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -3262,7 +3262,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/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/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/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 9aeeff137..9b53e3301 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, }, }; @@ -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(); } @@ -468,7 +470,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() { @@ -519,7 +521,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 @@ -1212,7 +1215,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); @@ -1245,7 +1248,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 b5bef62d5..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); From 76332582198bed9410278183f8b8d7b7e8a173f6 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 5 Mar 2026 10:51:12 +0100 Subject: [PATCH 24/31] Enforce 'suffix' for consistency --- .typos.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.typos.toml b/.typos.toml index 6c5f1564a..97c54d657 100644 --- a/.typos.toml +++ b/.typos.toml @@ -21,6 +21,8 @@ teselation = "tessellation" tessalation = "tessellation" tesselation = "tessellation" +# For consistency +postfix = "suffix" # Use the more common spelling adaptor = "adapter" From 9bc062c8eee17f154743a35145983f0e6f02b1c3 Mon Sep 17 00:00:00 2001 From: Lander Brandt Date: Thu, 5 Mar 2026 03:47:57 -0800 Subject: [PATCH 25/31] Fix wgpu memory leak leading to panic when window is minimized (#7434) (#7928) --- crates/egui-wgpu/src/winit.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/crates/egui-wgpu/src/winit.rs b/crates/egui-wgpu/src/winit.rs index 167d10c79..869c13259 100644 --- a/crates/egui-wgpu/src/winit.rs +++ b/crates/egui-wgpu/src/winit.rs @@ -421,12 +421,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; }; @@ -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. From 8b90dc60c66bd59ba808b8c094453ab2088a94c0 Mon Sep 17 00:00:00 2001 From: Lucas Meurer Date: Tue, 10 Mar 2026 12:03:10 +0100 Subject: [PATCH 26/31] =?UTF-8?q?=E2=9A=A0=EF=B8=8F=20Atom=20improvements:?= =?UTF-8?q?=20`Atom::id`,=20`align`,=20`closure`,=20`max=5Fsize`=20(#7958)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migration guide: - `AtomKind::Custom` has been removed. You can now set an id to any kind via `Atom::custom` or `AtomExt::atom_id`. --- crates/egui/src/atomics/atom.rs | 59 +++++++++-- crates/egui/src/atomics/atom_ext.rs | 25 ++++- crates/egui/src/atomics/atom_kind.rs | 114 +++++++++++++++------ crates/egui/src/atomics/atom_layout.rs | 76 ++++++++++---- crates/egui/src/atomics/atoms.rs | 23 +++-- crates/egui/src/atomics/sized_atom.rs | 5 + crates/egui/src/atomics/sized_atom_kind.rs | 20 ++-- crates/egui/src/widgets/drag_value.rs | 27 ++--- 8 files changed, 256 insertions(+), 93 deletions(-) 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 2cb668cff..fb04ee2dd 100644 --- a/crates/egui/src/atomics/atoms.rs +++ b/crates/egui/src/atomics/atoms.rs @@ -21,21 +21,24 @@ 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()); } - /// Insert atoms at the beginning of the list (left side). - pub fn extend_left(&mut self, atoms: impl IntoAtoms<'a>) { - let mut left = atoms.into_atoms(); - left.0.append(&mut self.0); - *self = left; - } - - /// Insert atoms at the end of the list (right side). - pub fn extend_right(&mut self, atoms: impl IntoAtoms<'a>) { - self.0.append(&mut atoms.into_atoms().0); + /// 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. 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/widgets/drag_value.rs b/crates/egui/src/widgets/drag_value.rs index d12020097..1297b614b 100644 --- a/crates/egui/src/widgets/drag_value.rs +++ b/crates/egui/src/widgets/drag_value.rs @@ -166,14 +166,14 @@ impl<'a> DragValue<'a> { /// Show a prefix before the number, e.g. "x: " #[inline] pub fn prefix(mut self, prefix: impl IntoAtoms<'a>) -> Self { - self.atoms.extend_left(prefix); + 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 IntoAtoms<'a>) -> Self { - self.atoms.extend_right(suffix); + self.atoms.extend_right(suffix.into_atoms()); self } @@ -447,18 +447,15 @@ impl Widget for DragValue<'_> { let mut past_value = false; let atom_id = Id::new(Self::ATOM_ID); for atom in atoms.iter() { - match &atom.kind { - AtomKind::Custom(id) if *id == atom_id => { - past_value = true; + 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()); } - AtomKind::Text(text) => { - if past_value { - suffix_text.push_str(text.text()); - } else { - prefix_text.push_str(text.text()); - } - } - _ => {} } } @@ -605,9 +602,7 @@ impl Widget for DragValue<'_> { response } else { atoms.map_atoms(|atom| { - if let AtomKind::Custom(id) = atom.kind - && id == atom_id - { + if atom.id == Some(atom_id) { RichText::new(value_text.clone()) .text_style(text_style.clone()) .into() From 14afefa2521d1baaf4fd02105eec2d3727a7ac36 Mon Sep 17 00:00:00 2001 From: Lucas Meurer Date: Thu, 12 Mar 2026 13:49:16 +0100 Subject: [PATCH 27/31] Fix galley width calculation being off due to subpixel binning (#7972) - fix for https://github.com/rerun-io/reality/pull/1075 The galleys row size was calculated by looking at the last glyphs pos_x, which got changed to be rounded to integers when we added subpixel binning. This introduced a subtle bug which caused the width of galleys to be slightly off. This PR fixes this by looking at the actual cursor position instead, which is not rounded. Also added a test to ensure this is correct. Previously, for the second and last line, the `x` was too close to the `0`. image --------- Co-authored-by: lucasmerlin <8009393+lucasmerlin@users.noreply.github.com> --- .../egui_demo_app/tests/snapshots/clock.png | 4 ++-- .../tests/snapshots/custom3d.png | 4 ++-- .../tests/snapshots/easymarkeditor.png | 4 ++-- .../tests/snapshots/imageviewer.png | 4 ++-- .../tests/snapshots/demos/Clipboard Test.png | 2 +- .../tests/snapshots/demos/Code Editor.png | 4 ++-- .../tests/snapshots/demos/Code Example.png | 4 ++-- .../tests/snapshots/demos/Cursor Test.png | 2 +- .../tests/snapshots/demos/Dancing Strings.png | 4 ++-- .../tests/snapshots/demos/Font Book.png | 4 ++-- .../tests/snapshots/demos/Frame.png | 4 ++-- .../tests/snapshots/demos/Grid Test.png | 4 ++-- .../tests/snapshots/demos/Highlighting.png | 4 ++-- .../tests/snapshots/demos/ID Test.png | 4 ++-- .../snapshots/demos/Interactive Container.png | 4 ++-- .../tests/snapshots/demos/Layout Test.png | 4 ++-- .../snapshots/demos/Manual Layout Test.png | 4 ++-- .../tests/snapshots/demos/Misc Demos.png | 4 ++-- .../tests/snapshots/demos/Modals.png | 2 +- .../tests/snapshots/demos/Painting.png | 4 ++-- .../tests/snapshots/demos/Popups.png | 4 ++-- .../tests/snapshots/demos/Scene.png | 4 ++-- .../tests/snapshots/demos/Screenshot.png | 4 ++-- .../tests/snapshots/demos/Scrolling.png | 4 ++-- .../tests/snapshots/demos/Sliders.png | 4 ++-- .../tests/snapshots/demos/Table.png | 4 ++-- .../snapshots/demos/Tessellation Test.png | 4 ++-- .../tests/snapshots/demos/Text Layout.png | 4 ++-- .../tests/snapshots/demos/TextEdit.png | 4 ++-- .../tests/snapshots/demos/Undo Redo.png | 2 +- .../tests/snapshots/demos/Window Options.png | 4 ++-- .../snapshots/demos/Window Resize Test.png | 4 ++-- .../snapshots/image_kerning/image_dark_x1.png | 4 ++-- .../image_kerning/image_light_x1.png | 4 ++-- .../tests/snapshots/modals_1.png | 4 ++-- .../tests/snapshots/modals_2.png | 4 ++-- .../tests/snapshots/modals_3.png | 4 ++-- ...rop_should_prevent_focusing_lower_area.png | 4 ++-- .../snapshots/rendering_test/dpi_1.00.png | 4 ++-- .../snapshots/rendering_test/dpi_1.25.png | 4 ++-- .../snapshots/rendering_test/dpi_1.50.png | 4 ++-- .../snapshots/rendering_test/dpi_1.67.png | 4 ++-- .../snapshots/rendering_test/dpi_1.75.png | 4 ++-- .../snapshots/rendering_test/dpi_2.00.png | 4 ++-- .../tessellation_test/Additive rectangle.png | 4 ++-- .../tessellation_test/Blurred stroke.png | 4 ++-- .../snapshots/tessellation_test/Blurred.png | 4 ++-- .../tessellation_test/Minimal rounding.png | 4 ++-- .../snapshots/tessellation_test/Normal.png | 4 ++-- .../Thick stroke, minimal rounding.png | 4 ++-- .../tessellation_test/Thin filled.png | 4 ++-- .../tessellation_test/Thin stroked.png | 4 ++-- .../snapshots/widget_gallery_dark_x1.png | 4 ++-- .../snapshots/widget_gallery_dark_x2.png | 4 ++-- .../snapshots/widget_gallery_light_x1.png | 4 ++-- .../snapshots/widget_gallery_light_x2.png | 4 ++-- .../tests/snapshots/combobox_closed.png | 4 ++-- .../tests/snapshots/combobox_opened.png | 4 ++-- .../tests/snapshots/menu/closed_hovered.png | 4 ++-- .../tests/snapshots/menu/opened.png | 4 ++-- .../tests/snapshots/menu/submenu.png | 4 ++-- .../tests/snapshots/menu/subsubmenu.png | 4 ++-- .../override_text_color_interactive.png | 4 ++-- .../tests/snapshots/readme_example.png | 4 ++-- .../tests/snapshots/test_masking.png | 4 ++-- .../tests/snapshots/test_shrink.png | 4 ++-- .../tests/snapshots/test_tooltip_hidden.png | 4 ++-- .../tests/snapshots/test_tooltip_shown.png | 4 ++-- crates/epaint/src/text/text_layout.rs | 23 +++++++++++-------- .../tests/snapshots/atom_letter_spacing.png | 3 +++ tests/egui_tests/tests/snapshots/grow_all.png | 2 +- .../tests/snapshots/layout/atoms_image.png | 4 ++-- .../tests/snapshots/layout/atoms_minimal.png | 4 ++-- .../snapshots/layout/atoms_multi_grow.png | 4 ++-- .../tests/snapshots/layout/button.png | 4 ++-- .../tests/snapshots/layout/button_image.png | 4 ++-- .../layout/button_image_shortcut.png | 4 ++-- .../tests/snapshots/layout/checkbox.png | 4 ++-- .../snapshots/layout/checkbox_checked.png | 4 ++-- .../tests/snapshots/layout/drag_value.png | 4 ++-- .../tests/snapshots/layout/radio.png | 4 ++-- .../tests/snapshots/layout/radio_checked.png | 4 ++-- .../snapshots/layout/selectable_value.png | 4 ++-- .../layout/selectable_value_selected.png | 4 ++-- .../tests/snapshots/layout/slider.png | 4 ++-- .../tests/snapshots/layout/text_edit.png | 4 ++-- .../tests/snapshots/layout/text_edit_clip.png | 4 ++-- .../snapshots/layout/text_edit_no_clip.png | 4 ++-- .../layout/text_edit_placeholder_clip.png | 4 ++-- .../egui_tests/tests/snapshots/max_width.png | 2 +- .../tests/snapshots/max_width_and_grow.png | 2 +- .../tests/snapshots/shrink_first_text.png | 2 +- .../tests/snapshots/shrink_last_text.png | 4 ++-- .../sides/default_long_fit_contents.png | 4 ++-- .../sides/default_short_fit_contents.png | 4 ++-- .../sides/shrink_left_long_fit_contents.png | 4 ++-- .../sides/shrink_left_short_fit_contents.png | 4 ++-- .../sides/shrink_right_long_fit_contents.png | 4 ++-- .../sides/shrink_right_short_fit_contents.png | 4 ++-- .../sides/wrap_left_long_fit_contents.png | 4 ++-- .../sides/wrap_left_short_fit_contents.png | 4 ++-- .../sides/wrap_right_long_fit_contents.png | 4 ++-- .../sides/wrap_right_short_fit_contents.png | 4 ++-- .../tests/snapshots/size_max_size.png | 2 +- .../tests/snapshots/text_edit_rtl_0.png | 4 ++-- .../tests/snapshots/text_edit_rtl_1.png | 4 ++-- .../tests/snapshots/text_edit_rtl_2.png | 2 +- .../tests/snapshots/visuals/button.png | 4 ++-- .../tests/snapshots/visuals/button_image.png | 4 ++-- .../visuals/button_image_shortcut.png | 4 ++-- .../button_image_shortcut_selected.png | 4 ++-- .../tests/snapshots/visuals/drag_value.png | 4 ++-- .../tests/snapshots/visuals/radio.png | 4 ++-- .../tests/snapshots/visuals/radio_checked.png | 4 ++-- .../snapshots/visuals/selectable_value.png | 4 ++-- .../visuals/selectable_value_selected.png | 4 ++-- .../snapshots/visuals/text_edit_no_clip.png | 4 ++-- tests/egui_tests/tests/test_atoms.rs | 21 +++++++++++++++++ 118 files changed, 258 insertions(+), 229 deletions(-) create mode 100644 tests/egui_tests/tests/snapshots/atom_letter_spacing.png diff --git a/crates/egui_demo_app/tests/snapshots/clock.png b/crates/egui_demo_app/tests/snapshots/clock.png index 1f6b6438f..b8ba70789 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:5878bc5beaf4406c24f23d900aa9ac7c5507e44cb3ade83b743b8b62e7da1615 -size 335355 +oid sha256:e4cff85a005ad897624f6c7a2b2ad599325ec99e0b3c9c35963f16611f283997 +size 335371 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/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 742c62272..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:9d6ba2c4825517b4cc030b7639771d06913da86c2d52fd40e6263692335afa04 -size 76079 +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 9ce775f7e..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:a169cda21797152fb8aa69928ad3f4cef1b45cc5f213e5bfa01b8fe7723a4852 -size 25391 +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 95c172d26..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:56b44d26946770c0878e11e3197633697ad339a7e8fcffe7279a6b4c45cd3582 -size 65384 +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 9d9d26c7c..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:01b34a7371dd8b3539ed20594a42f3cd9792d391d2cb44740aa5ef301c9652f3 -size 70244 +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 5c6b867d0..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:0574527ce659559f5b0709d84903afcec60b4ffa6b7979e8985027d326fd782a -size 45066 +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 64c5f12e1..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:50eca0feefe5d43db74f5e3bc08abda13c5986710cc4aaa03e9382af56264fc2 -size 86826 +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 e0dcebac3..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:91110d6a1da995e3e215cc92fdcceac84335e60b5b2fdbb2f16d5ecc6065fe55 -size 118912 +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 1a69351b8..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:31464d796f1660bdd5c98cf73186d7b68918fd42f292bc03e274e43d995edc16 -size 51363 +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 5c3621856..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:9dca12eb3b99976db20c77a6c540cda450e53f6ded89708d2e2320194723c0e2 -size 54569 +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 c8c5c6899..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:05a2778e7da867ab46a6760ea3925a2398c6b9a21d2767aef11cf98ef5292c82 -size 55034 +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 8bfc2f47a..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:686e37635da6ba218c9539f8b145239bbed2bab6696384ed1cb725db657ec642 -size 35961 +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 274e577d5..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:151171625b9cb8eaac3fb83260c6cc76cbf66003d9a940be1d5021a3303956c9 -size 35923 +oid sha256:ac6f9adeef92be9f69cb288ccafda8d522b8c3cde64352cd5369ae63668240c0 +size 35973 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 408cba0b8..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:79d2935aa8f0f6941167b69840142599a2994a5eaa239757d91847d4d6533174 -size 64165 +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 9aa9c130a..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:a5665e3a715ca7576df5b63af14b871f355d3e1db801c20089a60640373388ff -size 150095 +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 ef68941bc..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:bd31bddf25cf93d3ae79ce9b314cf3a3ebbf8c3b6cae2027f3f3b1593ac293e5 -size 59346 +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 67d8979ff..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:04dd62767ae9c18b5e89cea5bdd243b66c5986bfdb71fb9b01772ab9d150ab7e -size 145223 +oid sha256:355d8f08d08011635bf812aea1edeabd69e1ac3c724b521ed243f2b52e9b444b +size 145257 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/epaint/src/text/text_layout.rs b/crates/epaint/src/text/text_layout.rs index 9b53e3301..0233c1c58 100644 --- a/crates/epaint/src/text/text_layout.rs +++ b/crates/epaint/src/text/text_layout.rs @@ -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); @@ -254,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 @@ -279,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(); @@ -305,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), @@ -314,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, }); 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 86dfef652..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:96c78de8d82a5cb4e91912823b88bc0465bf67f09b500e5bde8f43b001f35a66 -size 264421 +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..eca582ec0 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:a2a017c2b93d1920ae85792c13eafa2fd43f93b2e3bbaa5981ed3a43050c0995 +size 11808 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 463c4cbef..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:0f6babaa4f9359517f58b1160a915069c56c338b7c0d8d4306cde67628442397 -size 8995 +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..b35eb2d51 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:4502cc58a4085d1e0f9945d0bd1d25adeefe71094ce94a210c57f113727f3a5a +size 11806 diff --git a/tests/egui_tests/tests/snapshots/visuals/radio_checked.png b/tests/egui_tests/tests/snapshots/visuals/radio_checked.png index f91c4a549..7bdae8cf1 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:cdedec6788b1a5026603322db9dd9f5fa398813d8aa2c56bc60acad390110501 +size 12499 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"); +} From 5031c47cb27e2801d849eb04543eb61bcd79de55 Mon Sep 17 00:00:00 2001 From: shuppy Date: Mon, 16 Mar 2026 17:34:54 +0800 Subject: [PATCH 28/31] Update accesskit to 0.24.0 (and related deps) (#7850) this patch updates our deps to [accesskit 0.24.0](https://docs.rs/accesskit/0.24.0/accesskit/) (was [0.21.1](https://docs.rs/accesskit/0.21.1/accesskit/)), [accesskit_consumer 0.35.0](https://docs.rs/accesskit_consumer/0.35.0/accesskit_consumer/) (was [0.30.1](https://docs.rs/accesskit_consumer/0.30.1/accesskit_consumer/)), and [accesskit_winit 0.32.0](https://docs.rs/accesskit_winit/0.32.0/accesskit_winit/) (was [0.29.1](https://docs.rs/accesskit_winit/0.29.1/accesskit_winit/)), allowing egui to be used in apps that use accessibility subtrees (AccessKit/accesskit#655). for now, we handle the subtree-related breaking changes by assuming that egui will use [the root tree](https://docs.rs/accesskit/0.24.0/accesskit/struct.TreeId.html#associatedconstant.ROOT), which is good enough for [servoshell](https://github.com/servo/servo) and does not require any API changes. * [x] I have followed the instructions in the PR template image --------- Co-authored-by: Luke Warlow Co-authored-by: lucasmerlin Co-authored-by: Arnold Loubriat --- Cargo.lock | 225 ++++++++++++------ Cargo.toml | 8 +- crates/egui-winit/src/lib.rs | 2 +- crates/egui/src/context.rs | 1 + crates/egui/src/id.rs | 2 +- crates/egui/src/input_state/mod.rs | 6 +- crates/egui/src/memory/mod.rs | 6 +- .../egui/src/text_selection/accesskit_text.rs | 209 +++++++++++----- .../egui/src/text_selection/cursor_range.rs | 38 ++- .../src/accessibility_inspector.rs | 30 ++- crates/egui_kittest/src/node.rs | 28 ++- deny.toml | 1 + 12 files changed, 383 insertions(+), 173 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 62fd9b36c..1c22e9ddf 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 0.62.2", + "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", @@ -452,20 +453,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", @@ -477,23 +477,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", @@ -2484,12 +2472,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]] @@ -2689,8 +2675,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", ] @@ -3269,8 +3255,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]] @@ -3279,8 +3276,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]] @@ -3289,24 +3286,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" @@ -3317,6 +3337,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" @@ -4692,7 +4721,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", ] @@ -5289,24 +5318,23 @@ dependencies = [ [[package]] name = "windows" -version = "0.61.1" +version = "0.62.2" 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]] @@ -5328,21 +5356,35 @@ 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 0.60.2", + "windows-interface 0.59.3", "windows-link 0.1.3", "windows-result 0.3.2", "windows-strings 0.4.0", ] [[package]] -name = "windows-future" -version = "0.2.0" +name = "windows-core" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-core 0.61.0", - "windows-link 0.1.3", + "windows-implement 0.60.2", + "windows-interface 0.59.3", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading", ] [[package]] @@ -5358,9 +5400,9 @@ dependencies = [ [[package]] name = "windows-implement" -version = "0.60.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", @@ -5380,9 +5422,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", @@ -5403,12 +5445,12 @@ 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.61.0", - "windows-link 0.1.3", + "windows-core 0.62.2", + "windows-link 0.2.1", ] [[package]] @@ -5429,6 +5471,15 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-strings" version = "0.1.0" @@ -5448,6 +5499,15 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-sys" version = "0.45.0" @@ -5541,6 +5601,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" @@ -5897,9 +5966,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", @@ -5907,9 +5976,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", diff --git a/Cargo.toml b/Cargo.toml index b10a4fd44..f083dd586 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", @@ -148,6 +148,8 @@ wgpu = { version = "27.0.1", 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/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index c010febd5..243ed119a 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, diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 55e3d75e1..3b36150e4 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -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, }); } 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 7122af699..b32a75c57 100644 --- a/crates/egui/src/input_state/mod.rs +++ b/crates/egui/src/input_state/mod.rs @@ -871,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); @@ -888,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/memory/mod.rs b/crates/egui/src/memory/mod.rs index fbc8e6f68..51ab2cde4 100644 --- a/crates/egui/src/memory/mod.rs +++ b/crates/egui/src/memory/mod.rs @@ -564,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/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_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_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/deny.toml b/deny.toml index e740494fb..845aa847c 100644 --- a/deny.toml +++ b/deny.toml @@ -64,6 +64,7 @@ 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 ] From 41b8f5f4e773543451a99e0c82af2426ecc19c76 Mon Sep 17 00:00:00 2001 From: SuchAFuriousDeath <48620541+SuchAFuriousDeath@users.noreply.github.com> Date: Mon, 16 Mar 2026 11:56:07 +0100 Subject: [PATCH 29/31] Update wgpu to 28.0.0 (#7853) Co-authored-by: lucasmerlin --- Cargo.lock | 176 +++++------------- Cargo.toml | 2 +- crates/eframe/src/web/web_painter_wgpu.rs | 17 +- crates/egui-wgpu/src/capture.rs | 3 +- crates/egui-wgpu/src/lib.rs | 13 +- crates/egui-wgpu/src/renderer.rs | 4 +- crates/egui-wgpu/src/winit.rs | 12 +- .../egui_demo_app/src/apps/custom3d_wgpu.rs | 4 +- crates/egui_demo_app/src/backend_panel.rs | 17 ++ deny.toml | 2 +- 10 files changed, 102 insertions(+), 148 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1c22e9ddf..800618e4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,7 +95,7 @@ dependencies = [ "accesskit_consumer", "hashbrown 0.16.1", "static_assertions", - "windows 0.62.2", + "windows", "windows-core 0.62.2", ] @@ -151,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" @@ -1994,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]] @@ -2071,6 +2060,8 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.2.0", ] @@ -2151,7 +2142,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.0", + "windows-core 0.61.2", ] [[package]] @@ -2640,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", @@ -2717,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", @@ -5116,12 +5107,13 @@ 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", @@ -5145,9 +5137,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", @@ -5178,45 +5170,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", @@ -5230,7 +5222,6 @@ dependencies = [ "core-graphics-types 0.2.0", "glow", "glutin_wgl_sys", - "gpu-alloc", "gpu-allocator", "gpu-descriptor", "hashbrown 0.16.1", @@ -5257,21 +5248,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", ] @@ -5306,16 +5296,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.58.0" -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.62.2" @@ -5339,28 +5319,15 @@ dependencies = [ [[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.2", - "windows-interface 0.59.3", + "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]] @@ -5369,8 +5336,8 @@ version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-implement 0.60.2", - "windows-interface 0.59.3", + "windows-implement", + "windows-interface", "windows-link 0.2.1", "windows-result 0.4.1", "windows-strings 0.5.1", @@ -5387,17 +5354,6 @@ dependencies = [ "windows-threading", ] -[[package]] -name = "windows-implement" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-implement" version = "0.60.2" @@ -5409,17 +5365,6 @@ dependencies = [ "syn", ] -[[package]] -name = "windows-interface" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-interface" version = "0.59.3" @@ -5455,18 +5400,9 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.2.0" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-result" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ "windows-link 0.1.3", ] @@ -5482,19 +5418,9 @@ dependencies = [ [[package]] name = "windows-strings" -version = "0.1.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -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" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ "windows-link 0.1.3", ] diff --git a/Cargo.toml b/Cargo.toml index f083dd586..d12b040fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,7 +144,7 @@ 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 } 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 869c13259..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, @@ -582,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 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/deny.toml b/deny.toml index 845aa847c..01377b90b 100644 --- a/deny.toml +++ b/deny.toml @@ -58,13 +58,13 @@ skip = [ { name = "thiserror" }, # ecosystem is in the process of migrating from 1.x to 2.x { name = "thiserror-impl" }, # same as above { name = "toml_datetime" }, # required while eco-system updates to toml 1.0 - { name = "windows-sys" }, # mostly hopeless to avoid ] 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 ] From f32727ddca01a033a7af072ac1a5d504b9178c05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uma=C4=B5o?= <107099960+umajho@users.noreply.github.com> Date: Tue, 17 Mar 2026 03:09:07 +0800 Subject: [PATCH 30/31] Improve IME, and restrict mac-specific workaround (#7973) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Closes N/A * [x] I have followed the instructions in the PR template My PR that fixes the macOS backspacing issue (#7810) unfortunately breaks text selection on Wayland (Fedora KDE Plasma Desktop 43 [Wayland, with or without IBus]). I had actually tested on a Wayland setup but failed to notice that :( Windows and Linux+X11 (Debian 13 [Cinnamon 6.4.10 + X11 + fcitx5 5.1.2]) are not affected. This PR fixes the issue by restricting the macOS fix to macOS-only.

Here is the correct behavior on Wayland after this PR (and before #7810 is applied) ![2026-03-13 5 25 24 PM](https://github.com/user-attachments/assets/3b0831c1-1d96-4003-9109-4bfe68e06d40)
Here is the buggy behavior on Wayland before this PR ![2026-03-13 5 31 58 PM](https://github.com/user-attachments/assets/c6d69382-0104-4e38-ad47-2d431f83f1fa)
## Cause of the Wayland issue On Wayland, `winit` constantly emits `winit::event::Ime::Preedit("", None)` events. PR #7810 added these lines for handling `winit::event::Ime::Preedit(_, None)` in `egui-winit` without considering the `target_os`: https://github.com/emilk/egui/blob/14afefa2521d1baaf4fd02105eec2d3727a7ac36/crates/egui-winit/src/lib.rs#L619-L621 As a result, while text is being selected, `egui-winit` receives these `winit::event::Ime::Preedit("", None)` events from `winit` and forwards them to `egui` as `egui::ImeEvent::Preedit("")`. `egui` then clears the current text selection, because it currently does not distinguish between IME pre-edit text and selected text. --------- Co-authored-by: lucasmerlin --- crates/egui-winit/src/lib.rs | 47 +++++++++------ crates/egui/src/widgets/text_edit/builder.rs | 62 ++++++++++++-------- 2 files changed, 65 insertions(+), 44 deletions(-) diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index 243ed119a..234a9989b 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -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(); } } 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)) From dd15ce980d3ac90d7e2e462734c5a290319e3df2 Mon Sep 17 00:00:00 2001 From: adrien <221212@umons.ac.be> Date: Tue, 17 Mar 2026 17:10:41 +0100 Subject: [PATCH 31/31] update snapshot for linux --- crates/egui_demo_app/tests/snapshots/clock.png | 4 ++-- tests/egui_tests/tests/snapshots/visuals/button_image.png | 4 ++-- tests/egui_tests/tests/snapshots/visuals/radio.png | 4 ++-- tests/egui_tests/tests/snapshots/visuals/radio_checked.png | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/egui_demo_app/tests/snapshots/clock.png b/crates/egui_demo_app/tests/snapshots/clock.png index b8ba70789..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:e4cff85a005ad897624f6c7a2b2ad599325ec99e0b3c9c35963f16611f283997 -size 335371 +oid sha256:80c64fe75dfc412bb33aa977055afcb140e6bfff19128ecf5bd35318ab6773d0 +size 335380 diff --git a/tests/egui_tests/tests/snapshots/visuals/button_image.png b/tests/egui_tests/tests/snapshots/visuals/button_image.png index eca582ec0..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:a2a017c2b93d1920ae85792c13eafa2fd43f93b2e3bbaa5981ed3a43050c0995 -size 11808 +oid sha256:31111fd16534107d74c03b798f53a013741bc1c83cf4bb24e374e4c3cac13af3 +size 11783 diff --git a/tests/egui_tests/tests/snapshots/visuals/radio.png b/tests/egui_tests/tests/snapshots/visuals/radio.png index b35eb2d51..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:4502cc58a4085d1e0f9945d0bd1d25adeefe71094ce94a210c57f113727f3a5a -size 11806 +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 7bdae8cf1..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:cdedec6788b1a5026603322db9dd9f5fa398813d8aa2c56bc60acad390110501 -size 12499 +oid sha256:33fb05197d6ae6f56d79263db49fcbb9534ec5552e4712ee5dfd0c1f76dc745e +size 12483