diff --git a/CHANGELOG.md b/CHANGELOG.md index 12010e287..e531dddc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,132 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.34.1 - 2026-03-27 +Nothing new + + +## 0.34.0 - 2026-03-26 + +### Highlights from this release +- Sharper text unlocked by switching font rendering crate to [`skrifa`](https://crates.io/crates/skrifa) +- Fade out edges of `ScrollArea`s +- Use `Ui` as the main entrypoint + +### Skrifa and font hinting +The font rendering backend was switched from `ab_glyph` to `skrifa` + `vello_cpu`. This enabled us support +font hinting and variations. It also paves the way for more font improvements in the future, like support for color +emojis and adding helpers for variations like `RichText::bold`. + +Font hinting makes text more clear (look at the =): + +https://github.com/user-attachments/assets/ea9151ec-869f-4c05-ab59-836114683417 + +We now support setting variable font parameters: + +https://github.com/user-attachments/assets/0febde1c-ebf6-4d85-8f96-86ec0f934ecf + +(Unfortunately there is currently a bug with variations, meaning changing them live like this won't work in practise. +There is a [draft PR](https://github.com/emilk/egui/pull/8029) to fix it, but it didn't make the release) + +* Replace ab_glyph with Skrifa + vello_cpu; enable font hinting [#7694](https://github.com/emilk/egui/pull/7694) by [@valadaptive](https://github.com/valadaptive) +* Add font variations API [#7859](https://github.com/emilk/egui/pull/7859) by [@valadaptive](https://github.com/valadaptive) + +### More `Ui`, less `Context` +egui has long had a confusing overlap in responsibilities between `Context` and `Ui`. +In particular, you could add panels to either one (or both!). +In this release, we switch from having `Context` be the main entrypoint, and instead provide whole-app `Ui`. +In egui we've replaced `Context::run` with `Context::run_ui`, and changed viewports to be given a `&mut Ui` instead of `Context`. +In `eframe` we've deprecated `App::update` replaced it with `App::ui` (which provides a `&mut Ui` instead of a `&Context`). + +In addition to this, `Ui` now derefs to `Context`, so all code like `ui.ctx().input(…)` can now be written `ui.input(…)`. +This means you are much less likely to have to use naked `Context`s. +`Context` can still be useful though, since they implement `Clone` and can be sent to other threads so you can call `.request_repaint` on them. + +* Add `Context::run_ui` [#7736](https://github.com/emilk/egui/pull/7736) by [@emilk](https://github.com/emilk) +* Add `Deref` for `Ui` [#7770](https://github.com/emilk/egui/pull/7770) by [@emilk](https://github.com/emilk) +* Replace `App::update` with `fn logic` and `fn ui` [#7775](https://github.com/emilk/egui/pull/7775) by [@emilk](https://github.com/emilk) +* Rename `Context::style` to `global_style`; avoid confusion w/ `Ui::style` [#7772](https://github.com/emilk/egui/pull/7772) by [@emilk](https://github.com/emilk) +* Rename functions in `Context` to avoid confusion [#7773](https://github.com/emilk/egui/pull/7773) by [@emilk](https://github.com/emilk) +* Viewports: give the caller a `Ui` instead of `Context` [#7779](https://github.com/emilk/egui/pull/7779) by [@emilk](https://github.com/emilk) + +### Changed panel API +As part of the above work, we have unified the panel API. +`SidePanel` and `TopBottomPanel` are deprecated, replaced by a single `Panel`. +Furthermore, it is now deprecated to use panels directly on `Context`. Use the `show_inside` functions instead, acting on `Ui`s. + +This unification and simplification will make it easier to maintain and improve panels going forward. + +* Add `Panel` to replace `SidePanel` and `TopBottomPanel` [#5659](https://github.com/emilk/egui/pull/5659) by [@sharky98](https://github.com/sharky98) +* Deprecate using `Panel` directly on a `Context` [#7781](https://github.com/emilk/egui/pull/7781) by [@emilk](https://github.com/emilk) +* Deprecate `CentralPanel::show` [#7783](https://github.com/emilk/egui/pull/7783) by [@emilk](https://github.com/emilk) +* Deprecate `Context::used_size` and `Context::available_rect` [#7788](https://github.com/emilk/egui/pull/7788) by [@emilk](https://github.com/emilk) + +### ⭐ Added +* Add `is_scrolling`/`is_smooth_scrolling` util, checking for active scroll action [#7669](https://github.com/emilk/egui/pull/7669) by [@IsseW](https://github.com/IsseW) +* Allow multiple atoms in `Button::shortcut_text` and `right_text` [#7696](https://github.com/emilk/egui/pull/7696) by [@emilk](https://github.com/emilk) +* Add `ScrollArea::content_margin` [#7722](https://github.com/emilk/egui/pull/7722) by [@emilk](https://github.com/emilk) +* Per-widget style [#7667](https://github.com/emilk/egui/pull/7667) by [@AdrienZianne](https://github.com/AdrienZianne) +* Plugin: export `TypedPluginGuard` and `TypedPluginHandle` [#7780](https://github.com/emilk/egui/pull/7780) by [@apekros](https://github.com/apekros) +* Add `ViewportInfo::occluded` and `visible` [#7948](https://github.com/emilk/egui/pull/7948) by [@emilk](https://github.com/emilk) +* Add `Atom` prefix/suffix support to `DragValue` [#7949](https://github.com/emilk/egui/pull/7949) by [@lucasmerlin](https://github.com/lucasmerlin) +* ⚠️ Atom improvements: `Atom::id`, `align`, `closure`, `max_size` [#7958](https://github.com/emilk/egui/pull/7958) by [@lucasmerlin](https://github.com/lucasmerlin) +* Add `DebugOptions::warn_if_rect_changes_id` [#7984](https://github.com/emilk/egui/pull/7984) by [@emilk](https://github.com/emilk) +* `TextEdit` `Atom` prefix/suffix [#7587](https://github.com/emilk/egui/pull/7587) by [@lucasmerlin](https://github.com/lucasmerlin) +* Add `Button::left_text` [#7955](https://github.com/emilk/egui/pull/7955) by [@rustbasic](https://github.com/rustbasic) +* Add `Response::parent_id` [#8010](https://github.com/emilk/egui/pull/8010) by [@lucasmerlin](https://github.com/lucasmerlin) +* Add `Context::text_edit_focused` [#8014](https://github.com/emilk/egui/pull/8014) by [@emilk](https://github.com/emilk) +* Add `Context::time` [#8017](https://github.com/emilk/egui/pull/8017) by [@emilk](https://github.com/emilk) +* Add `Ui::is_tooltip` [#8016](https://github.com/emilk/egui/pull/8016) by [@emilk](https://github.com/emilk) +* Add `UiStack::bg_color` [#8020](https://github.com/emilk/egui/pull/8020) by [@emilk](https://github.com/emilk) +* Make `egui::IdSet` public [#8019](https://github.com/emilk/egui/pull/8019) by [@lucasmerlin](https://github.com/lucasmerlin) +* Add raw key methods to TypeIdMap [#8007](https://github.com/emilk/egui/pull/8007) by [@AlexanderSchuetz97](https://github.com/AlexanderSchuetz97) + +### 🔧 Changed +* Remove `accesskit` feature and always depend on `accesskit` [#7701](https://github.com/emilk/egui/pull/7701) by [@emilk](https://github.com/emilk) +* Update MSRV from 1.88 to 1.92 [#7793](https://github.com/emilk/egui/pull/7793) by [@JasperBRiedel](https://github.com/JasperBRiedel) +* Improve modifier handling when scrolling [#7678](https://github.com/emilk/egui/pull/7678) by [@emilk](https://github.com/emilk) +* Apply preferred font weight when loading variable fonts [#7790](https://github.com/emilk/egui/pull/7790) by [@pmnxis](https://github.com/pmnxis) +* Make scroll bars and resize splitters visible to accesskit [#7804](https://github.com/emilk/egui/pull/7804) by [@emilk](https://github.com/emilk) +* Allow moving existing widgets to the top of interaction stack [#7805](https://github.com/emilk/egui/pull/7805) by [@emilk](https://github.com/emilk) +* Slightly change interact behavior around thin splitters [#7806](https://github.com/emilk/egui/pull/7806) by [@emilk](https://github.com/emilk) +* Move window resize interaction to be over contents [#7807](https://github.com/emilk/egui/pull/7807) by [@emilk](https://github.com/emilk) +* Don't expand widgets on hover [#7808](https://github.com/emilk/egui/pull/7808) by [@emilk](https://github.com/emilk) +* Make `FrameCache::get` return a reference instead of cloning the cached value [#7834](https://github.com/emilk/egui/pull/7834) by [@KonaeAkira](https://github.com/KonaeAkira) +* Update selected dependencies [#7920](https://github.com/emilk/egui/pull/7920) by [@oscargus](https://github.com/oscargus) +* Make `Galley::pos_from_layout_cursor` `pub` [#7864](https://github.com/emilk/egui/pull/7864) by [@dionb](https://github.com/dionb) +* Update accesskit to 0.24.0 (and related deps) [#7850](https://github.com/emilk/egui/pull/7850) by [@delan](https://github.com/delan) +* Quit on Ctrl-Q [#7985](https://github.com/emilk/egui/pull/7985) by [@emilk](https://github.com/emilk) +* Fade out the edges of `ScrollAreas` [#8018](https://github.com/emilk/egui/pull/8018) by [@emilk](https://github.com/emilk) + +### 🔥 Removed +* Remove `CacheTrait::as_any_mut` [#7833](https://github.com/emilk/egui/pull/7833) by [@emilk](https://github.com/emilk) + +### 🐛 Fixed +* Fix: ensure `CentralPanel::show_inside` allocates space in parent [#7778](https://github.com/emilk/egui/pull/7778) by [@emilk](https://github.com/emilk) +* Heed constrain rect when auto-positioning windows [#7786](https://github.com/emilk/egui/pull/7786) by [@emilk](https://github.com/emilk) +* Fix jitter when hovering edge of scroll area close to resize splitter [#7803](https://github.com/emilk/egui/pull/7803) by [@emilk](https://github.com/emilk) +* Don't focus Areas, Windows and ScrollAreas [#7827](https://github.com/emilk/egui/pull/7827) by [@lucasmerlin](https://github.com/lucasmerlin) +* Fix backspacing leaving last character in IME prediction not removed on macOS native and Safari [#7810](https://github.com/emilk/egui/pull/7810) by [@umajho](https://github.com/umajho) +* Implemented distance threshold for double/triple clicks [#7817](https://github.com/emilk/egui/pull/7817) by [@bl4ze4447](https://github.com/bl4ze4447) +* Fix `CentralPanel::show_inside_dyn` to round `panel_rect` [#7868](https://github.com/emilk/egui/pull/7868) by [@ripopov](https://github.com/ripopov) +* Stop ctrl+arrow etc from moving focus [#7897](https://github.com/emilk/egui/pull/7897) by [@emilk](https://github.com/emilk) +* Fix scroll area not consuming scroll events [#7904](https://github.com/emilk/egui/pull/7904) by [@lucasmerlin](https://github.com/lucasmerlin) +* Pass in an explicit id in `UiBuilder`, to avoid wrapping passed in ids with Id::new() [#7925](https://github.com/emilk/egui/pull/7925) by [@lucasmerlin](https://github.com/lucasmerlin) +* Fix crash when dragging a DragValue through small floats [#7939](https://github.com/emilk/egui/pull/7939) by [@Fyrecean](https://github.com/Fyrecean) +* Fix emoji icon font [#7940](https://github.com/emilk/egui/pull/7940) by [@Jhynjhiruu](https://github.com/Jhynjhiruu) +* Fixes the overly aggressive overflow elision in `truncate()` and similar for os scaling other than 100% [#7867](https://github.com/emilk/egui/pull/7867) by [@RndUsr123](https://github.com/RndUsr123) +* Fix text color when selecting newline character [#7951](https://github.com/emilk/egui/pull/7951) by [@emilk](https://github.com/emilk) +* Fix: repaint on drag-and-drop files [#7953](https://github.com/emilk/egui/pull/7953) by [@emilk](https://github.com/emilk) +* Fix instable IDs following animated panels [#7994](https://github.com/emilk/egui/pull/7994) by [@emilk](https://github.com/emilk) +* Enables every combination of `TextEdit` and `LayoutJob` alignments [#7831](https://github.com/emilk/egui/pull/7831) by [@RndUsr123](https://github.com/RndUsr123) +* Fix `horizontal_wrapping` row height after using `text_edit_multiline` [#8000](https://github.com/emilk/egui/pull/8000) by [@optozorax](https://github.com/optozorax) +* Fix menu keyboard toggle for open submenus [#7957](https://github.com/emilk/egui/pull/7957) by [@fjkorf](https://github.com/fjkorf) +* Fix: `Visuals::interact_cursor` support in `Button` [#7986](https://github.com/emilk/egui/pull/7986) by [@mango766](https://github.com/mango766) + +### 🚀 Performance +* Shrink the byte-size of `Response` slightly [#8011](https://github.com/emilk/egui/pull/8011) by [@emilk](https://github.com/emilk) + + ## 0.33.3 - 2025-12-11 * Treat `.` as a word-splitter in text navigation [#7741](https://github.com/emilk/egui/pull/7741) by [@emilk](https://github.com/emilk) * Change text color of selected text [#7691](https://github.com/emilk/egui/pull/7691) by [@emilk](https://github.com/emilk) diff --git a/Cargo.lock b/Cargo.lock index 37180245f..baed51ef1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1193,7 +1193,7 @@ checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" [[package]] name = "ecolor" -version = "0.33.3" +version = "0.34.1" dependencies = [ "bytemuck", "cint", @@ -1205,7 +1205,7 @@ dependencies = [ [[package]] name = "eframe" -version = "0.33.3" +version = "0.34.1" dependencies = [ "ahash", "bytemuck", @@ -1244,7 +1244,7 @@ dependencies = [ [[package]] name = "egui" -version = "0.33.3" +version = "0.34.1" dependencies = [ "accesskit", "ahash", @@ -1264,7 +1264,7 @@ dependencies = [ [[package]] name = "egui-wgpu" -version = "0.33.3" +version = "0.34.1" dependencies = [ "ahash", "bytemuck", @@ -1282,7 +1282,7 @@ dependencies = [ [[package]] name = "egui-winit" -version = "0.33.3" +version = "0.34.1" dependencies = [ "accesskit_winit", "arboard", @@ -1305,7 +1305,7 @@ dependencies = [ [[package]] name = "egui_demo_app" -version = "0.33.3" +version = "0.34.1" dependencies = [ "accesskit", "accesskit_consumer", @@ -1330,12 +1330,11 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "wgpu", ] [[package]] name = "egui_demo_lib" -version = "0.33.3" +version = "0.34.1" dependencies = [ "criterion", "document-features", @@ -1352,7 +1351,7 @@ dependencies = [ [[package]] name = "egui_extras" -version = "0.33.3" +version = "0.34.1" dependencies = [ "ahash", "document-features", @@ -1371,7 +1370,7 @@ dependencies = [ [[package]] name = "egui_glow" -version = "0.33.3" +version = "0.34.1" dependencies = [ "bytemuck", "document-features", @@ -1390,7 +1389,7 @@ dependencies = [ [[package]] name = "egui_kittest" -version = "0.33.3" +version = "0.34.1" dependencies = [ "dify", "document-features", @@ -1410,7 +1409,7 @@ dependencies = [ [[package]] name = "egui_tests" -version = "0.33.3" +version = "0.34.1" dependencies = [ "egui", "egui_extras", @@ -1440,7 +1439,7 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "emath" -version = "0.33.3" +version = "0.34.1" dependencies = [ "bytemuck", "document-features", @@ -1538,7 +1537,7 @@ dependencies = [ [[package]] name = "epaint" -version = "0.33.3" +version = "0.34.1" dependencies = [ "ahash", "bytemuck", @@ -1566,7 +1565,7 @@ dependencies = [ [[package]] name = "epaint_default_fonts" -version = "0.33.3" +version = "0.34.1" [[package]] name = "equivalent" @@ -3434,7 +3433,7 @@ checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" [[package]] name = "popups" -version = "0.33.3" +version = "0.34.1" dependencies = [ "eframe", "env_logger", @@ -5822,7 +5821,7 @@ checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" [[package]] name = "xtask" -version = "0.33.3" +version = "0.34.1" [[package]] name = "yaml-rust" diff --git a/Cargo.toml b/Cargo.toml index cf631eb25..6c6cb0c03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ members = [ edition = "2024" license = "MIT OR Apache-2.0" rust-version = "1.92" -version = "0.33.3" +version = "0.34.1" [profile.release] @@ -55,18 +55,18 @@ opt-level = 2 [workspace.dependencies] -emath = { version = "0.33.3", path = "crates/emath", default-features = false } -ecolor = { version = "0.33.3", path = "crates/ecolor", default-features = false } -epaint = { version = "0.33.3", path = "crates/epaint", default-features = false } -epaint_default_fonts = { version = "0.33.3", path = "crates/epaint_default_fonts" } -egui = { version = "0.33.3", path = "crates/egui", default-features = false } -egui-winit = { version = "0.33.3", path = "crates/egui-winit", default-features = false } -egui_extras = { version = "0.33.3", path = "crates/egui_extras", default-features = false } -egui-wgpu = { version = "0.33.3", path = "crates/egui-wgpu", default-features = false } -egui_demo_lib = { version = "0.33.3", path = "crates/egui_demo_lib", default-features = false } -egui_glow = { version = "0.33.3", path = "crates/egui_glow", default-features = false } -egui_kittest = { version = "0.33.3", path = "crates/egui_kittest", default-features = false } -eframe = { version = "0.33.3", path = "crates/eframe", default-features = false } +emath = { version = "0.34.1", path = "crates/emath", default-features = false } +ecolor = { version = "0.34.1", path = "crates/ecolor", default-features = false } +epaint = { version = "0.34.1", path = "crates/epaint", default-features = false } +epaint_default_fonts = { version = "0.34.1", path = "crates/epaint_default_fonts" } +egui = { version = "0.34.1", path = "crates/egui", default-features = false } +egui-winit = { version = "0.34.1", path = "crates/egui-winit", default-features = false } +egui_extras = { version = "0.34.1", path = "crates/egui_extras", default-features = false } +egui-wgpu = { version = "0.34.1", path = "crates/egui-wgpu", default-features = false } +egui_demo_lib = { version = "0.34.1", path = "crates/egui_demo_lib", default-features = false } +egui_glow = { version = "0.34.1", path = "crates/egui_glow", default-features = false } +egui_kittest = { version = "0.34.1", path = "crates/egui_kittest", default-features = false } +eframe = { version = "0.34.1", path = "crates/eframe", default-features = false } accesskit = "0.24.0" accesskit_consumer = "0.35.0" diff --git a/crates/ecolor/CHANGELOG.md b/crates/ecolor/CHANGELOG.md index a4fe1f3de..fe0333748 100644 --- a/crates/ecolor/CHANGELOG.md +++ b/crates/ecolor/CHANGELOG.md @@ -6,6 +6,14 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.34.1 - 2026-03-27 +Nothing new + + +## 0.34.0 - 2026-03-26 +Nothing new + + ## 0.33.3 - 2025-12-11 Nothing new diff --git a/crates/eframe/CHANGELOG.md b/crates/eframe/CHANGELOG.md index 74a705251..d9ba794b1 100644 --- a/crates/eframe/CHANGELOG.md +++ b/crates/eframe/CHANGELOG.md @@ -7,6 +7,35 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.34.1 - 2026-03-27 +* `wgpu` backend: Enable WebGL fallback [#8038](https://github.com/emilk/egui/pull/8038) by [@emilk](https://github.com/emilk) +* Only apply cursor style to the `` [#8036](https://github.com/emilk/egui/pull/8036) by [@mkeeter](https://github.com/mkeeter) + + +## 0.34.0 - 2026-03-26 +### ⭐ Added +* Add feature `wgpu_no_default_features` [#7700](https://github.com/emilk/egui/pull/7700) by [@emilk](https://github.com/emilk) +* Add `ViewportInfo::occluded` and `visible` [#7948](https://github.com/emilk/egui/pull/7948) by [@emilk](https://github.com/emilk) +* Add `eframe::WindowChromeMetrics` (macOS only) [#8015](https://github.com/emilk/egui/pull/8015) by [@emilk](https://github.com/emilk) + +### 🔧 Changed +* Replace `App::update` with `fn logic` and `fn ui` [#7775](https://github.com/emilk/egui/pull/7775) by [@emilk](https://github.com/emilk) +* Make `wgpu` the default renderer for `eframe` and egui.rs [#7615](https://github.com/emilk/egui/pull/7615) by [@emilk](https://github.com/emilk) +* Roll out new egui icon and logo [#7995](https://github.com/emilk/egui/pull/7995) by [@emilk](https://github.com/emilk) +* Update wasm-bindgen to 0.2.108, and ehttp to 0.7.1 [#7996](https://github.com/emilk/egui/pull/7996) by [@emilk](https://github.com/emilk) +* Update to `wgpu` 29 [#7990](https://github.com/emilk/egui/pull/7990) by [@cwfitzgerald](https://github.com/cwfitzgerald) +* Allow fallback from smithay to arboard when getting clipboard [#7976](https://github.com/emilk/egui/pull/7976) by [@wizzeh](https://github.com/wizzeh) + +### 🐛 Fixed +* Fix: update get_proc_address to use Arc for better ownership management [#7922](https://github.com/emilk/egui/pull/7922) by [@Wybxc](https://github.com/Wybxc) +* Much improved IME [#7967](https://github.com/emilk/egui/pull/7967) by [@umajho](https://github.com/umajho) +* Improve behavior of invisible windows [#7905](https://github.com/emilk/egui/pull/7905) by [@gcailly](https://github.com/gcailly) + +### 🚀 Performance +* Avoid repaints on device mouse motion outside window [#7866](https://github.com/emilk/egui/pull/7866) by [@inktomi](https://github.com/inktomi) +* Only run `App::ui` if the application is visible [#7950](https://github.com/emilk/egui/pull/7950) by [@emilk](https://github.com/emilk) + + ## 0.33.3 - 2025-12-11 Nothing new diff --git a/crates/eframe/Cargo.toml b/crates/eframe/Cargo.toml index 6219b90fd..0530b64a8 100644 --- a/crates/eframe/Cargo.toml +++ b/crates/eframe/Cargo.toml @@ -89,7 +89,7 @@ web_screen_reader = ["web-sys/SpeechSynthesis", "web-sys/SpeechSynthesisUtteranc ## See for more details. ## ## By default, eframe will prefer WebGPU over WebGL, but -## you can configure this at run-time with [`NativeOptions::wgpu_options`]. +## you can configure this at run-time with `WebOptions::wgpu_options`. wgpu = ["wgpu_no_default_features", "egui-wgpu/default"] ## This is exactly like the `wgpu` feature, but does NOT enable the default features of `wgpu` and `egui-wgpu`. @@ -155,7 +155,6 @@ glutin-winit = { workspace = true, optional = true, default-features = false, fe "wgl", ] } home = { workspace = true, optional = true } -wgpu = { workspace = true, optional = true } # mac: [target.'cfg(any(target_os = "macos"))'.dependencies] @@ -170,12 +169,16 @@ objc2-app-kit = { workspace = true, default-features = false, features = [ "std", "NSApplication", "NSBitmapImageRep", + "NSButton", + "NSControl", "NSGraphics", "NSImage", "NSImageRep", "NSMenu", "NSMenuItem", "NSResponder", + "NSView", + "NSWindow", ] } # windows: diff --git a/crates/eframe/src/lib.rs b/crates/eframe/src/lib.rs index 151fb79ce..c644289e0 100644 --- a/crates/eframe/src/lib.rs +++ b/crates/eframe/src/lib.rs @@ -159,7 +159,7 @@ pub use {egui, egui::emath, egui::epaint}; pub use {egui_glow, glow}; #[cfg(feature = "wgpu_no_default_features")] -pub use {egui_wgpu, wgpu}; +pub use {egui_wgpu, egui_wgpu::wgpu}; mod epi; @@ -190,6 +190,9 @@ pub use web::{WebLogger, WebRunner}; #[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))] mod native; +#[cfg(target_os = "macos")] +pub use native::macos::WindowChromeMetrics; + #[cfg(not(target_arch = "wasm32"))] #[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))] pub use native::run::EframeWinitApplication; diff --git a/crates/eframe/src/native/macos.rs b/crates/eframe/src/native/macos.rs new file mode 100644 index 000000000..b1f2552e5 --- /dev/null +++ b/crates/eframe/src/native/macos.rs @@ -0,0 +1,76 @@ +use egui::Vec2; +use objc2_app_kit::{NSView, NSWindow, NSWindowButton}; +use raw_window_handle::{AppKitWindowHandle, RawWindowHandle}; + +/// Size of the "traffic lights" (red/yellow/green close/minimize/maximize buttons) +/// on the native macOS window. +/// +/// This is very useful together with [`egui::ViewportBuilder::with_fullsize_content_view`]. +#[derive(Debug)] +pub struct WindowChromeMetrics { + /// Size of the "traffic lights" (red/yellow/green close/minimize/maximize buttons), + /// including margins. + /// + /// The unit here is in "native scale", which means it needs to be divided by [`egui::Context::zoom_factor`] + /// to get the size in egui points. + pub traffic_lights_size: Vec2, +} + +impl WindowChromeMetrics { + /// Get the window chrome metrics for a given window handle. + pub fn from_window_handle(window_handle: &RawWindowHandle) -> Option { + window_chrome_metrics(window_handle) + } +} + +fn window_chrome_metrics(window_handle: &RawWindowHandle) -> Option { + let RawWindowHandle::AppKit(appkit_handle) = window_handle else { + return None; + }; + + let ns_view = ns_view_from_handle(appkit_handle)?; + let ns_window = ns_view.window()?; + + Some(WindowChromeMetrics { + traffic_lights_size: traffic_lights_metrics(&ns_window)?, + }) +} + +fn traffic_lights_metrics(ns_window: &NSWindow) -> Option { + // Button order is CloseButton, MiniaturizeButton, ZoomButton: + let close_button = ns_window + .standardWindowButton(NSWindowButton::CloseButton)? + .frame(); + let zoom_button = ns_window + .standardWindowButton(NSWindowButton::ZoomButton)? + .frame(); + + let left_margin = close_button.origin.x; + let right_margin = left_margin; // for symmetry + + let total_width = zoom_button.origin.x + zoom_button.size.width + right_margin; + + let top_margin = close_button.origin.y; + let bottom_margin = top_margin; // Usually symmetric + let total_height = top_margin + close_button.size.height + bottom_margin; + + Some(Vec2::new(total_width as f32, total_height as f32)) +} + +fn ns_view_from_handle(handle: &AppKitWindowHandle) -> Option<&NSView> { + let ns_view_ptr = handle.ns_view.as_ptr().cast::(); + + // Validate the pointer is non-null + if ns_view_ptr.is_null() { + None + } else { + // SAFETY: + // - We've verified the pointer is non-null + // - The pointer comes from the windowing system, so it should be valid + // - NSView pointers from AppKit are expected to remain valid for the window lifetime + #[expect(unsafe_code)] + unsafe { + ns_view_ptr.as_ref() + } + } +} diff --git a/crates/eframe/src/native/mod.rs b/crates/eframe/src/native/mod.rs index eb9413717..771964ae7 100644 --- a/crates/eframe/src/native/mod.rs +++ b/crates/eframe/src/native/mod.rs @@ -3,6 +3,9 @@ mod epi_integration; mod event_loop_context; pub mod run; +#[cfg(target_os = "macos")] +pub(crate) mod macos; + /// File storage which can be used by native backends. #[cfg(feature = "persistence")] pub mod file_storage; diff --git a/crates/eframe/src/web/app_runner.rs b/crates/eframe/src/web/app_runner.rs index b90b8a5e1..7161a665e 100644 --- a/crates/eframe/src/web/app_runner.rs +++ b/crates/eframe/src/web/app_runner.rs @@ -393,7 +393,7 @@ impl AppRunner { } } - super::set_cursor_icon(cursor_icon); + super::set_cursor_icon(self.canvas(), cursor_icon); if self.has_focus() { // The eframe app has focus. diff --git a/crates/eframe/src/web/mod.rs b/crates/eframe/src/web/mod.rs index 87771f722..dc743ec49 100644 --- a/crates/eframe/src/web/mod.rs +++ b/crates/eframe/src/web/mod.rs @@ -178,10 +178,8 @@ fn canvas_size_in_points(canvas: &web_sys::HtmlCanvasElement, ctx: &egui::Contex // ---------------------------------------------------------------------------- /// Set the cursor icon. -fn set_cursor_icon(cursor: egui::CursorIcon) -> Option<()> { - let document = web_sys::window()?.document()?; - document - .body()? +fn set_cursor_icon(canvas: &web_sys::HtmlCanvasElement, cursor: egui::CursorIcon) -> Option<()> { + canvas .style() .set_property("cursor", cursor_web_name(cursor)) .ok() diff --git a/crates/egui-wgpu/CHANGELOG.md b/crates/egui-wgpu/CHANGELOG.md index cef640184..4cc344bf2 100644 --- a/crates/egui-wgpu/CHANGELOG.md +++ b/crates/egui-wgpu/CHANGELOG.md @@ -6,6 +6,24 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.34.1 - 2026-03-27 +* `wgpu` backend: Enable WebGL fallback [#8038](https://github.com/emilk/egui/pull/8038) by [@emilk](https://github.com/emilk) + + +## 0.34.0 - 2026-03-26 +### ⭐ Added +* Add error message when calling `.render()` without `.update_buffers()` [#8005](https://github.com/emilk/egui/pull/8005) by [@emilk](https://github.com/emilk) + +### 🔧 Changed +* Put the `capture` module behind a feature flag, make the `egui` dependency optional [#7698](https://github.com/emilk/egui/pull/7698) by [@StT191](https://github.com/StT191) +* Attach stencil buffer [#7702](https://github.com/emilk/egui/pull/7702) by [@jgraef](https://github.com/jgraef) +* Update wgpu to 28.0.0 [#7853](https://github.com/emilk/egui/pull/7853) by [@SuchAFuriousDeath](https://github.com/SuchAFuriousDeath) +* Update to wgpu 29 [#7990](https://github.com/emilk/egui/pull/7990) by [@cwfitzgerald](https://github.com/cwfitzgerald) + +### 🐛 Fixed +* Fix wgpu memory leak leading to panic when window is minimized (#7434) [#7928](https://github.com/emilk/egui/pull/7928) by [@landaire](https://github.com/landaire) + + ## 0.33.3 - 2025-12-11 Nothing new diff --git a/crates/egui-wgpu/Cargo.toml b/crates/egui-wgpu/Cargo.toml index 86cd3192b..0b081e134 100644 --- a/crates/egui-wgpu/Cargo.toml +++ b/crates/egui-wgpu/Cargo.toml @@ -25,7 +25,12 @@ all-features = true rustdoc-args = ["--generate-link-to-definition"] [features] -default = ["fragile-send-sync-non-atomic-wasm", "macos-window-resize-jitter-fix", "wgpu/default"] +default = [ + "fragile-send-sync-non-atomic-wasm", + "macos-window-resize-jitter-fix", + "wgpu/default", + "wgpu/webgl", # A very important fallback for web support +] ## Enables the `capture` module for capturing screenshots. capture = ["dep:egui"] diff --git a/crates/egui-winit/CHANGELOG.md b/crates/egui-winit/CHANGELOG.md index f88a8c84a..68fe5b2cd 100644 --- a/crates/egui-winit/CHANGELOG.md +++ b/crates/egui-winit/CHANGELOG.md @@ -5,6 +5,17 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.34.1 - 2026-03-27 +Nothing new + + +## 0.34.0 - 2026-03-26 +* Add `is_scrolling`/`is_smooth_scrolling` util, checking for active scroll action [#7669](https://github.com/emilk/egui/pull/7669) by [@IsseW](https://github.com/IsseW) +* Fix backspacing leaving last character in IME prediction not removed on macOS native and Safari [#7810](https://github.com/emilk/egui/pull/7810) by [@umajho](https://github.com/umajho) +* Much improved IME [#7967](https://github.com/emilk/egui/pull/7967) by [@umajho](https://github.com/umajho) +* Allow fallback from smithay to arboard when getting clipboard [#7976](https://github.com/emilk/egui/pull/7976) by [@wizzeh](https://github.com/wizzeh) + + ## 0.33.3 - 2025-12-11 Nothing new diff --git a/crates/egui/src/containers/scroll_area.rs b/crates/egui/src/containers/scroll_area.rs index 2616fb414..b99bcf5da 100644 --- a/crates/egui/src/containers/scroll_area.rs +++ b/crates/egui/src/containers/scroll_area.rs @@ -5,7 +5,7 @@ use std::ops::{Add, AddAssign, BitOr, BitOrAssign}; use emath::GuiRounding as _; -use epaint::Margin; +use epaint::{Color32, Direction, Margin, Shape}; use crate::{ Context, CursorIcon, Id, NumExt as _, Pos2, Rangef, Rect, Response, Sense, Ui, UiBuilder, @@ -1019,13 +1019,17 @@ impl ScrollArea { .inner; let (content_size, state) = prepared.end(ui); - ScrollAreaOutput { + let output = ScrollAreaOutput { inner, id, state, content_size, inner_rect, - } + }; + + paint_fade_areas(ui, &output); + + output } } @@ -1504,3 +1508,88 @@ impl Prepared { (content_size, state) } } + +/// Paint fade-out gradients at the top and/or bottom of a scroll area to +/// indicate that more content is available beyond the visible region. +fn paint_fade_areas(ui: &Ui, scroll_output: &ScrollAreaOutput) { + let crate::style::ScrollFadeStyle { + strength, + size: fade_size, + } = ui.spacing().scroll.fade; + + if strength <= 0.0 { + return; + } + + let bg = ui.stack().bg_color(); + + let offset = scroll_output.state.offset; + let overflow = scroll_output.content_size - scroll_output.inner_rect.size(); + + let paint_rect = scroll_output + .inner_rect + .intersect(ui.min_rect()) + .expand(ui.visuals().clip_rect_margin); + + // Top fade: animate opacity based on how far we've scrolled down. + if 0.0 < offset.y { + let t = (offset.y / fade_size).clamp(0.0, 1.0) * strength; + let bg_faded = bg.gamma_multiply(t); + let rect = Rect::from_min_max( + paint_rect.left_top(), + pos2(paint_rect.right(), paint_rect.top() + fade_size), + ); + ui.painter().add(Shape::gradient_rect( + rect, + Direction::TopDown, + [bg_faded, Color32::TRANSPARENT], + )); + } + + // Bottom fade: animate opacity based on distance from the bottom. + let distance_from_bottom = overflow.y - offset.y; + if 0.0 < distance_from_bottom { + let t = (distance_from_bottom / fade_size).clamp(0.0, 1.0) * strength; + let bg_faded = bg.gamma_multiply(t); + let rect = Rect::from_min_max( + pos2(paint_rect.left(), paint_rect.bottom() - fade_size), + paint_rect.right_bottom(), + ); + ui.painter().add(Shape::gradient_rect( + rect, + Direction::BottomUp, + [bg_faded, Color32::TRANSPARENT], + )); + } + + // Left fade: animate opacity based on how far we've scrolled right. + if 0.0 < offset.x { + let t = (offset.x / fade_size).clamp(0.0, 1.0) * strength; + let bg_faded = bg.gamma_multiply(t); + let rect = Rect::from_min_max( + paint_rect.left_top(), + pos2(paint_rect.left() + fade_size, paint_rect.bottom()), + ); + ui.painter().add(Shape::gradient_rect( + rect, + Direction::LeftToRight, + [bg_faded, Color32::TRANSPARENT], + )); + } + + // Right fade: animate opacity based on distance from the right edge. + let distance_from_right = overflow.x - offset.x; + if 0.0 < distance_from_right { + let t = (distance_from_right / fade_size).clamp(0.0, 1.0) * strength; + let bg_faded = bg.gamma_multiply(t); + let rect = Rect::from_min_max( + pos2(paint_rect.right() - fade_size, paint_rect.top()), + paint_rect.right_bottom(), + ); + ui.painter().add(Shape::gradient_rect( + rect, + Direction::RightToLeft, + [bg_faded, Color32::TRANSPARENT], + )); + } +} diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 51a663ce5..a8751bffc 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -1545,6 +1545,11 @@ impl Context { crate::debug_text::print(self, text); } + /// Current time in seconds, relative to some unknown epoch. + pub fn time(&self) -> f64 { + self.input(|i| i.time) + } + /// What operating system are we running on? /// /// When compiling natively, this is @@ -1933,7 +1938,7 @@ impl Context { } } -/// Callbacks +/// Plugins impl Context { /// Call the given callback at the start of each pass of each viewport. /// @@ -2945,6 +2950,15 @@ impl Context { self.egui_wants_keyboard_input() } + /// Is the currently focused widget a text edit? + pub fn text_edit_focused(&self) -> bool { + if let Some(id) = self.memory(|mem| mem.focused()) { + crate::text_edit::TextEditState::load(self, id).is_some() + } else { + false + } + } + /// Highlight this widget, to make it look like it is hovered, even if it isn't. /// /// If you call this after the widget has been fully rendered, diff --git a/crates/egui/src/layout.rs b/crates/egui/src/layout.rs index c35fd254b..c97652e8d 100644 --- a/crates/egui/src/layout.rs +++ b/crates/egui/src/layout.rs @@ -1,7 +1,7 @@ use emath::GuiRounding as _; use crate::{ - Align, + Align, Direction, emath::{Align2, NumExt as _, Pos2, Rect, Vec2, pos2, vec2}, }; const INFINITY: f32 = f32::INFINITY; @@ -87,36 +87,6 @@ impl Region { // ---------------------------------------------------------------------------- -/// Layout direction, one of [`LeftToRight`](Direction::LeftToRight), [`RightToLeft`](Direction::RightToLeft), [`TopDown`](Direction::TopDown), [`BottomUp`](Direction::BottomUp). -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub enum Direction { - LeftToRight, - RightToLeft, - TopDown, - BottomUp, -} - -impl Direction { - #[inline(always)] - pub fn is_horizontal(self) -> bool { - match self { - Self::LeftToRight | Self::RightToLeft => true, - Self::TopDown | Self::BottomUp => false, - } - } - - #[inline(always)] - pub fn is_vertical(self) -> bool { - match self { - Self::LeftToRight | Self::RightToLeft => false, - Self::TopDown | Self::BottomUp => true, - } - } -} - -// ---------------------------------------------------------------------------- - /// The layout of a [`Ui`][`crate::Ui`], e.g. "vertical & centered". /// /// ``` diff --git a/crates/egui/src/lib.rs b/crates/egui/src/lib.rs index d86851a1d..cd098eaea 100644 --- a/crates/egui/src/lib.rs +++ b/crates/egui/src/lib.rs @@ -449,7 +449,7 @@ pub use emath::{ remap_clamp, vec2, }; pub use epaint::{ - ClippedPrimitive, ColorImage, CornerRadius, ImageData, Margin, Mesh, PaintCallback, + ClippedPrimitive, ColorImage, CornerRadius, Direction, ImageData, Margin, Mesh, PaintCallback, PaintCallbackInfo, Shadow, Shape, Stroke, StrokeKind, TextureHandle, TextureId, mutex, text::{FontData, FontDefinitions, FontFamily, FontId, FontTweak}, textures::{TextureFilter, TextureOptions, TextureWrapMode, TexturesDelta}, @@ -478,7 +478,7 @@ pub use self::{ drag_and_drop::DragAndDrop, epaint::text::TextWrapMode, grid::Grid, - id::{Id, IdMap}, + id::{Id, IdMap, IdSet}, input_state::{InputOptions, InputState, MultiTouchInfo, PointerState, SurrenderFocusOn}, layers::{LayerId, Order}, layout::*, diff --git a/crates/egui/src/style.rs b/crates/egui/src/style.rs index 8c63b7e75..0fd0dcc07 100644 --- a/crates/egui/src/style.rs +++ b/crates/egui/src/style.rs @@ -586,6 +586,8 @@ pub struct ScrollStyle { /// This is only for floating scroll bars. /// Solid scroll bars are always opaque. pub interact_handle_opacity: f32, + + pub fade: ScrollFadeStyle, } impl Default for ScrollStyle { @@ -616,6 +618,8 @@ impl ScrollStyle { dormant_handle_opacity: 0.0, active_handle_opacity: 0.6, interact_handle_opacity: 1.0, + + fade: Default::default(), } } @@ -699,6 +703,8 @@ impl ScrollStyle { dormant_handle_opacity, active_handle_opacity, interact_handle_opacity, + + fade, } = self; ui.horizontal(|ui| { @@ -772,6 +778,52 @@ impl ScrollStyle { ui.label("Inner margin"); }); } + + ui.separator(); + fade.ui(ui); + } +} + +/// Controls if and how to fade out the sides of a [`crate::ScrollArea`] +/// to indicate there is more there if you scroll. +#[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", serde(default))] +pub struct ScrollFadeStyle { + /// Opacity of the fade effect at the outer edge, in 0.0-1.0. + /// + /// Set to 0.0 to disable the fade effect. + pub strength: f32, + + /// Size of the fade-area (height for vertical scrolling, + /// width for horizontal scrolling). + pub size: f32, +} + +impl Default for ScrollFadeStyle { + fn default() -> Self { + Self { + strength: 0.5, + size: 20.0, + } + } +} + +impl ScrollFadeStyle { + pub fn ui(&mut self, ui: &mut Ui) { + let Self { strength, size } = self; + + ui.horizontal(|ui| { + ui.add(DragValue::new(strength).speed(0.01).range(0.0..=1.0)); + ui.label("Fade strength"); + }); + + if 0.0 < *strength { + ui.horizontal(|ui| { + ui.add(DragValue::new(size).range(0.0..=64.0)); + ui.label("Fade size"); + }); + } } } diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index d2888b73e..fc8ad2fce 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -487,6 +487,12 @@ impl Ui { &mut self.style_mut().visuals } + /// Is this [`Ui`] in a tooltip? + #[inline] + pub fn is_tooltip(&self) -> bool { + self.layer_id().order == Order::Tooltip + } + /// Get a reference to this [`Ui`]'s [`UiStack`]. #[inline] pub fn stack(&self) -> &Arc { diff --git a/crates/egui/src/ui_stack.rs b/crates/egui/src/ui_stack.rs index 07026c45b..d72b3ea0b 100644 --- a/crates/egui/src/ui_stack.rs +++ b/crates/egui/src/ui_stack.rs @@ -1,6 +1,8 @@ use std::sync::Arc; use std::{any::Any, iter::FusedIterator}; +use epaint::Color32; + use crate::{Direction, Frame, Id, Rect}; /// What kind is this [`crate::Ui`]? @@ -253,6 +255,25 @@ impl UiStack { pub fn has_visible_frame(&self) -> bool { !self.info.frame.stroke.is_empty() } + + /// The background color of this [`crate::Ui`]. + /// + /// This blend together all [`Frame::fill`] colors + /// up to the root. + #[inline] + pub fn bg_color(&self) -> Color32 { + let mut total = Color32::TRANSPARENT; + for node in self.iter() { + let fill = node.frame().fill; + if fill != Color32::TRANSPARENT { + total = fill.blend(total); + if total.is_opaque() { + break; + } + } + } + total + } } // these methods act on the entire stack diff --git a/crates/egui/src/util/id_type_map.rs b/crates/egui/src/util/id_type_map.rs index b1312a1da..76f0da5d7 100644 --- a/crates/egui/src/util/id_type_map.rs +++ b/crates/egui/src/util/id_type_map.rs @@ -4,7 +4,6 @@ // This will also allow users to pick their own serialization format per type. use std::{any::Any, sync::Arc}; - // ----------------------------------------------------------------------------------------------- /// Like [`std::any::TypeId`], but can be serialized and deserialized. @@ -182,6 +181,18 @@ impl Element { } } + pub fn is_temp(&self) -> bool { + match self { + #[cfg(feature = "persistence")] + Self::Value { serialize_fn, .. } => serialize_fn.is_none(), + + #[cfg(not(feature = "persistence"))] + Self::Value { .. } => true, + + Self::Serialized(_) => false, + } + } + #[inline] pub(crate) fn get_temp(&self) -> Option<&T> { match self { @@ -316,6 +327,41 @@ fn from_ron_str(ron: &str) -> Option { use crate::Id; +/// The key used in [`IdTypeMap`], which is a combination of an [`Id`] and a [`TypeId`]. +/// +/// This key can be used to remove or access values in the [`IdTypeMap`] without +/// knowledge of the `TypeId` `T` that is required for other accessors. +/// +/// [`RawKey`]s make no guarantees about layout or their ability to be persisted. +/// They only produce deterministic results if they are used with the map +/// they were initially obtained from. Using them on other instances of [`IdTypeMap`] +/// may produce unexpected behavior. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +#[repr(transparent)] +pub struct RawKey(u64); + +impl nohash_hasher::IsEnabled for RawKey {} + +impl RawKey { + /// Create a new key for the given type. + /// + /// Note that two keys with the same id but different types + /// will be different keys. + /// + /// ``` + /// use egui::{Id, util::id_type_map::RawKey}; + /// assert_ne!( + /// RawKey::new::(Id::NULL), + /// RawKey::new::(Id::NULL), + /// ); + /// ``` + #[inline(always)] + pub fn new(id: Id) -> Self { + let type_id = TypeId::of::(); + Self(type_id.value() ^ id.value()) + } +} + // TODO(emilk): make IdTypeMap generic over the key (`Id`), and make a library of IdTypeMap. /// Stores values identified by an [`Id`] AND the [`std::any::TypeId`] of the value. /// @@ -358,7 +404,7 @@ use crate::Id; #[derive(Clone, Debug)] // We use `id XOR typeid` as a key, so we don't need to hash again! pub struct IdTypeMap { - map: nohash_hasher::IntMap, + map: nohash_hasher::IntMap, max_bytes_per_type: usize, } @@ -375,16 +421,23 @@ impl Default for IdTypeMap { impl IdTypeMap { /// Insert a value that will not be persisted. #[inline] - pub fn insert_temp(&mut self, id: Id, value: T) { - let hash = hash(TypeId::of::(), id); - self.map.insert(hash, Element::new_temp(value)); + pub fn insert_temp( + &mut self, + id: Id, + value: T, + ) -> RawKey { + let key = RawKey::new::(id); + self.map.insert(key, Element::new_temp(value)); + key } /// Insert a value that will be persisted next time you start the app. #[inline] pub fn insert_persisted(&mut self, id: Id, value: T) { - let hash = hash(TypeId::of::(), id); - self.map.insert(hash, Element::new_persisted(value)); + let key = RawKey::new::(id); + self.map.insert(key, Element::new_persisted(value)); + // We don't yet return the key here, because currently all our `raw` + // methods are only for temporary values. } /// Read a value without trying to deserialize a persisted value. @@ -392,8 +445,28 @@ impl IdTypeMap { /// The call clones the value (if found), so make sure it is cheap to clone! #[inline] pub fn get_temp(&self, id: Id) -> Option { - let hash = hash(TypeId::of::(), id); - self.map.get(&hash).and_then(|x| x.get_temp()).cloned() + let key = RawKey::new::(id); + self.map.get(&key).and_then(|x| x.get_temp()).cloned() + } + + /// Gets a reference to a value for a given raw key. + /// + /// Serialized values are ignored. + pub fn get_temp_raw(&self, raw: RawKey) -> Option<&(dyn Any + Send + Sync)> { + match self.map.get(&raw)? { + Element::Value { value, .. } => Some(value.as_ref()), + Element::Serialized(_) => None, + } + } + + /// Gets a mutable reference to a value for a given raw key. + /// + /// Serialized values are ignored. + pub fn get_temp_raw_mut(&mut self, raw: RawKey) -> Option<&mut (dyn Any + Send + Sync)> { + match self.map.get_mut(&raw)? { + Element::Value { value, .. } => Some(value.as_mut()), + Element::Serialized(_) => None, + } } /// Read a value, optionally deserializing it if available. @@ -404,9 +477,9 @@ impl IdTypeMap { /// The call clones the value (if found), so make sure it is cheap to clone! #[inline] pub fn get_persisted(&mut self, id: Id) -> Option { - let hash = hash(TypeId::of::(), id); + let key = RawKey::new::(id); self.map - .get_mut(&hash) + .get_mut(&key) .and_then(|x| x.get_mut_persisted()) .cloned() } @@ -443,9 +516,9 @@ impl IdTypeMap { id: Id, insert_with: impl FnOnce() -> T, ) -> &mut T { - let hash = hash(TypeId::of::(), id); + let key = RawKey::new::(id); use std::collections::hash_map::Entry; - match self.map.entry(hash) { + match self.map.entry(key) { Entry::Vacant(vacant) => { // this unwrap will never panic, because we insert correct type right now #[expect(clippy::unwrap_used)] @@ -465,9 +538,9 @@ impl IdTypeMap { id: Id, insert_with: impl FnOnce() -> T, ) -> &mut T { - let hash = hash(TypeId::of::(), id); + let key = RawKey::new::(id); use std::collections::hash_map::Entry; - match self.map.entry(hash) { + match self.map.entry(key) { Entry::Vacant(vacant) => { // this unwrap will never panic, because we insert correct type right now #[expect(clippy::unwrap_used)] @@ -486,7 +559,7 @@ impl IdTypeMap { #[cfg(feature = "persistence")] #[allow(clippy::allow_attributes, unused)] fn get_generation(&self, id: Id) -> Option { - let element = self.map.get(&hash(TypeId::of::(), id))?; + let element = self.map.get(&RawKey::new::(id))?; match element { Element::Value { .. } => Some(0), Element::Serialized(SerializedElement { generation, .. }) => Some(*generation), @@ -496,18 +569,33 @@ impl IdTypeMap { /// Remove the state of this type and id. #[inline] pub fn remove(&mut self, id: Id) { - let hash = hash(TypeId::of::(), id); - self.map.remove(&hash); + let key = RawKey::new::(id); + self.map.remove(&key); } /// Remove and fetch the state of this type and id. #[inline] pub fn remove_temp(&mut self, id: Id) -> Option { - let hash = hash(TypeId::of::(), id); - let mut element = self.map.remove(&hash)?; + let key = RawKey::new::(id); + let mut element = self.map.remove(&key)?; Some(std::mem::take(element.get_mut_temp()?)) } + /// Remove a temporary value given a raw key. + /// + /// Serialized values are ignored. + pub fn remove_temp_raw(&mut self, raw: RawKey) -> Option> { + use std::collections::hash_map::Entry; + if let Entry::Occupied(e) = self.map.entry(raw) + && e.get().is_temp() + && let Element::Value { value, .. } = e.remove() + { + Some(value) + } else { + None + } + } + /// Note all state of the given type. pub fn remove_by_type(&mut self) { let key = TypeId::of::(); @@ -532,6 +620,18 @@ impl IdTypeMap { self.map.len() } + /// Returns all [`RawKey`]s to values in this map. + /// + /// The returned keys can only be used with this map. + /// + /// Serializable values will be ignored. + pub fn temp_keys(&self) -> impl Iterator { + self.map + .iter() + .filter(|(_, v)| v.is_temp()) + .map(|(k, _)| *k) + } + /// Count how many values are stored but not yet deserialized. #[inline] pub fn count_serialized(&self) -> usize { @@ -576,11 +676,6 @@ impl IdTypeMap { } } -#[inline(always)] -fn hash(type_id: TypeId, id: Id) -> u64 { - type_id.value() ^ id.value() -} - // ---------------------------------------------------------------------------- /// How [`IdTypeMap`] is persisted. @@ -613,13 +708,13 @@ impl PersistedMap { { profiling::scope!("gather"); - for (hash, element) in &map.map { + for (key, element) in &map.map { if let Some(element) = element.to_serialize() { let stats = types_map.entry(element.type_id).or_default(); stats.num_bytes += element.ron.len(); let generation_stats = stats.generations.entry(element.generation).or_default(); generation_stats.num_bytes += element.ron.len(); - generation_stats.elements.push((*hash, element)); + generation_stats.elements.push((key.0, element)); } else { // temporary value that shouldn't be serialized } @@ -659,7 +754,7 @@ impl PersistedMap { .into_iter() .map( |( - hash, + raw, SerializedElement { type_id, ron, @@ -667,7 +762,7 @@ impl PersistedMap { }, )| { ( - hash, + RawKey(raw), Element::Serialized(SerializedElement { type_id, ron, diff --git a/crates/egui/src/widgets/button.rs b/crates/egui/src/widgets/button.rs index 7d9dddf0d..8ef047002 100644 --- a/crates/egui/src/widgets/button.rs +++ b/crates/egui/src/widgets/button.rs @@ -371,6 +371,12 @@ impl<'a> Button<'a> { AtomLayoutResponse::empty(prepared.response) }; + if let Some(cursor) = ui.visuals().interact_cursor + && response.response.hovered() + { + ui.ctx().set_cursor_icon(cursor); + } + response.response.widget_info(|| { if let Some(text) = &text { WidgetInfo::labeled(WidgetType::Button, ui.is_enabled(), text) diff --git a/crates/egui_demo_app/Cargo.toml b/crates/egui_demo_app/Cargo.toml index 49609746f..5bb093ced 100644 --- a/crates/egui_demo_app/Cargo.toml +++ b/crates/egui_demo_app/Cargo.toml @@ -37,7 +37,7 @@ serde = ["dep:serde", "egui_demo_lib/serde", "egui/serde"] syntect = ["egui_demo_lib/syntect"] glow = ["eframe/glow"] -wgpu = ["eframe/wgpu", "bytemuck", "dep:wgpu"] +wgpu = ["eframe/wgpu", "bytemuck"] wayland = ["eframe/wayland"] x11 = ["eframe/x11"] @@ -60,9 +60,6 @@ accesskit_consumer = { workspace = true, optional = true } bytemuck = { workspace = true, optional = true } puffin = { workspace = true, optional = true } puffin_http = { workspace = true, optional = true } -# Enable both WebGL & WebGPU when targeting the web (these features have no effect when not targeting wasm32) -# Also enable the default features so we have a supported backend for every platform. -wgpu = { workspace = true, features = ["default", "webgpu", "webgl"], optional = true } # feature "http": diff --git a/crates/egui_demo_app/src/backend_panel.rs b/crates/egui_demo_app/src/backend_panel.rs index dfc4d116b..cd17afd4a 100644 --- a/crates/egui_demo_app/src/backend_panel.rs +++ b/crates/egui_demo_app/src/backend_panel.rs @@ -211,7 +211,7 @@ fn integration_ui(ui: &mut egui::Ui, _frame: &mut eframe::Frame) { let wgpu_adapter_details_ui = |ui: &mut egui::Ui, adapter: &eframe::wgpu::Adapter| { let info = &adapter.get_info(); - let wgpu::AdapterInfo { + let eframe::wgpu::AdapterInfo { name, vendor, device, diff --git a/crates/egui_demo_app/tests/snapshots/easymarkeditor.png b/crates/egui_demo_app/tests/snapshots/easymarkeditor.png index a0c1a52de..5118065c4 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:b9ad01a55950f96a3ae9e48a2c026143d11ffee62bff4f83b4529cd884ce11f0 -size 169683 +oid sha256:84f0e72ce337d56f3767ebed1ab6a47f3d27c9fbcce4d8a19aeab358e12920f5 +size 169664 diff --git a/crates/egui_demo_lib/src/demo/strip_demo.rs b/crates/egui_demo_lib/src/demo/strip_demo.rs index 94df7bb57..69bcce4f6 100644 --- a/crates/egui_demo_lib/src/demo/strip_demo.rs +++ b/crates/egui_demo_lib/src/demo/strip_demo.rs @@ -27,7 +27,7 @@ impl crate::Demo for StripDemo { impl crate::View for StripDemo { fn ui(&mut self, ui: &mut egui::Ui) { let dark_mode = ui.visuals().dark_mode; - let faded_color = ui.visuals().window_fill(); + let faded_color = ui.stack().bg_color(); let faded_color = |color: Color32| -> Color32 { use egui::Rgba; let t = if dark_mode { 0.95 } else { 0.8 }; 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 f73093e3e..ac85845f9 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:8c630df841e98043132b920338793745549116890c1bc52d8d10b0def896a1e6 -size 114409 +oid sha256:deff441cd1d9142352f8759dff4b759f4572f0ddf93752349314da77abe4b254 +size 115028 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Panels.png b/crates/egui_demo_lib/tests/snapshots/demos/Panels.png index 992fb471e..70e7ce90e 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Panels.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Panels.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:627114dcbda4f3d2255d34926ed0b77c679d248ed1d822d723479b1e6652c67a -size 249258 +oid sha256:a46457b23b7b32694564d03b42bccac2f017a756225bc54b508bb6fe2ad8ee7b +size 249548 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png b/crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png index 24e42cb23..0924d3aa9 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:b5b965a7c690fd8e8646812513e2417170b687fd37e29d220c29127ba0cc200c -size 172609 +oid sha256:927a497e8b6f9ce3b71dcb67086f477e19d327c163b2b8ad868af10009c2faf2 +size 172981 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Table.png b/crates/egui_demo_lib/tests/snapshots/demos/Table.png index 3f72922d2..727c01434 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:931f38ade8373ff79801c05c5d4397f2c5fcfa27022f2e1abe9eb29d561a3aef -size 76022 +oid sha256:db4c0cf1c4cdae3d416afce5c58efd1cc382be86431e547fa66bcc95a0a17ddb +size 76364 diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png b/crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png index 23adea69c..e7ed07b04 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:89986132c5d2a3ccccf0a16cb2b0be07b7c5512838cc10ec9067022e7a238515 -size 63918 +oid sha256:57018beba5e4fb4f1e6de9c58bf898560b3a7669159d5bad91a4e2382ef57ce0 +size 64004 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 96ca9949e..c769f61bd 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:9c81d5a5915dac60297293b90cd3fc0d188a9335e99b318996e3e2934de7bee1 -size 483497 +oid sha256:999f9cc006302b8951d97b510a02f1209969c376ecc7909ed5d7b46da27c0637 +size 483753 diff --git a/crates/egui_extras/CHANGELOG.md b/crates/egui_extras/CHANGELOG.md index 15eaf8704..9d2bfd2d7 100644 --- a/crates/egui_extras/CHANGELOG.md +++ b/crates/egui_extras/CHANGELOG.md @@ -5,6 +5,18 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.34.1 - 2026-03-27 +Nothing new + + +## 0.34.0 - 2026-03-26 +* Fix media type with optional parameters [#7739](https://github.com/emilk/egui/pull/7739) by [@leungkkf](https://github.com/leungkkf) +* Fix: CodeTheme functions - ui and add builder [#7684](https://github.com/emilk/egui/pull/7684) by [@Its-Just-Nans](https://github.com/Its-Just-Nans) +* Update selected dependencies [#7920](https://github.com/emilk/egui/pull/7920) by [@oscargus](https://github.com/oscargus) +* Add `DatePickerButton::reverse_years/year_scroll_to` [#7978](https://github.com/emilk/egui/pull/7978) by [@Deuracell](https://github.com/Deuracell) +* Replace `chrono` with `jiff` [#8008](https://github.com/emilk/egui/pull/8008) by [@emilk](https://github.com/emilk) + + ## 0.33.3 - 2025-12-11 * Bump `ehttp` to 0.6.0 [#7757](https://github.com/emilk/egui/pull/7757) by [@jprochazk](https://github.com/jprochazk) diff --git a/crates/egui_extras/src/table.rs b/crates/egui_extras/src/table.rs index 78c6e7435..25257421a 100644 --- a/crates/egui_extras/src/table.rs +++ b/crates/egui_extras/src/table.rs @@ -656,14 +656,13 @@ impl TableState { } fn store(self, ui: &egui::Ui, state_id: egui::Id) { - #![expect(clippy::needless_return)] #[cfg(feature = "serde")] { - return ui.data_mut(|d| d.insert_persisted(state_id, self)); + ui.data_mut(|d| d.insert_persisted(state_id, self)); } #[cfg(not(feature = "serde"))] { - return ui.data_mut(|d| d.insert_temp(state_id, self)); + ui.data_mut(|d| d.insert_temp(state_id, self)); } } diff --git a/crates/egui_glow/CHANGELOG.md b/crates/egui_glow/CHANGELOG.md index 2fccf1166..a33d24bec 100644 --- a/crates/egui_glow/CHANGELOG.md +++ b/crates/egui_glow/CHANGELOG.md @@ -6,6 +6,14 @@ Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.34.1 - 2026-03-27 +Nothing new + + +## 0.34.0 - 2026-03-26 +* Turn `HarnessBuilder::with_options` into a proper builder method [#7697](https://github.com/emilk/egui/pull/7697) by [@emilk](https://github.com/emilk) +* Paint mouse cursor in kittest snapshot images [#7721](https://github.com/emilk/egui/pull/7721) by [@emilk](https://github.com/emilk) +* Add `kittest.toml` config file [#7643](https://github.com/emilk/egui/pull/7643) by [@lucasmerlin](https://github.com/lucasmerlin) +* Close debug_open_snapshot temp file before viewing it [#7841](https://github.com/emilk/egui/pull/7841) by [@yuriks](https://github.com/yuriks) + + ## 0.33.3 - 2025-12-11 * Enforce consistent snapshot updates [#7744](https://github.com/emilk/egui/pull/7744) by [@lucasmerlin](https://github.com/lucasmerlin) * `kittest`: add drag-and-drop helpers [#7690](https://github.com/emilk/egui/pull/7690) by [@emilk](https://github.com/emilk) diff --git a/crates/egui_kittest/tests/snapshots/test_scroll_initial.png b/crates/egui_kittest/tests/snapshots/test_scroll_initial.png index e61dc99a0..64ed153a6 100644 --- a/crates/egui_kittest/tests/snapshots/test_scroll_initial.png +++ b/crates/egui_kittest/tests/snapshots/test_scroll_initial.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eb8737af84c3d3b0c054b7e2a8bcb04685243d84cb13b72a1372dc40dbfd14fb -size 7267 +oid sha256:6154c8bb550575bcb9fa0bba06da4d47079a00dffc5754b62ef2a6e7529e2090 +size 7489 diff --git a/crates/egui_kittest/tests/snapshots/test_scroll_scrolled.png b/crates/egui_kittest/tests/snapshots/test_scroll_scrolled.png index 0cc3d6d15..a87f09545 100644 --- a/crates/egui_kittest/tests/snapshots/test_scroll_scrolled.png +++ b/crates/egui_kittest/tests/snapshots/test_scroll_scrolled.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f1651bb1b9bbaa3c65ecd07c39c57527f4beb4c607581a5b2596a49dcf4c5db3 -size 7996 +oid sha256:df2578c198b29950254ec62c6cc615a4b1c003e7ae3ea027da22fc868b392c74 +size 8342 diff --git a/crates/emath/CHANGELOG.md b/crates/emath/CHANGELOG.md index f559265c4..8c55b647f 100644 --- a/crates/emath/CHANGELOG.md +++ b/crates/emath/CHANGELOG.md @@ -6,6 +6,14 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.34.1 - 2026-03-27 +Nothing new + + +## 0.34.0 - 2026-03-26 +Nothing new + + ## 0.33.3 - 2025-12-11 Nothing new diff --git a/crates/epaint/CHANGELOG.md b/crates/epaint/CHANGELOG.md index dd6b4e2a5..0d577b120 100644 --- a/crates/epaint/CHANGELOG.md +++ b/crates/epaint/CHANGELOG.md @@ -5,6 +5,29 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.34.1 - 2026-03-27 +Nothing new + + +## 0.34.0 - 2026-03-26 +### ⭐ Added +* Replace ab_glyph with Skrifa + vello_cpu; enable font hinting [#7694](https://github.com/emilk/egui/pull/7694) by [@valadaptive](https://github.com/valadaptive) +* Add font variations API [#7859](https://github.com/emilk/egui/pull/7859) by [@valadaptive](https://github.com/valadaptive) +* Allow rotation of rectangles and ellipses [#7682](https://github.com/emilk/egui/pull/7682) by [@RyanBluth](https://github.com/RyanBluth) + +### 🔧 Changed +* Apply preferred font weight when loading variable fonts [#7790](https://github.com/emilk/egui/pull/7790) by [@pmnxis](https://github.com/pmnxis) +* Make `Galley::pos_from_layout_cursor` `pub` [#7864](https://github.com/emilk/egui/pull/7864) by [@dionb](https://github.com/dionb) + +### 🐛 Fixed +* Fixes the overly aggressive overflow elision in `truncate()` and similar for os scaling other than 100% [#7867](https://github.com/emilk/egui/pull/7867) by [@RndUsr123](https://github.com/RndUsr123) +* Fix text color when selecting newline character [#7951](https://github.com/emilk/egui/pull/7951) by [@emilk](https://github.com/emilk) +* Fix galley width calculation being off due to subpixel binning [#7972](https://github.com/emilk/egui/pull/7972) by [@lucasmerlin](https://github.com/lucasmerlin) + +### 🚀 Performance +* Avoid cloning `Row`s during `Galley::concat` [#7649](https://github.com/emilk/egui/pull/7649) by [@afishhh](https://github.com/afishhh) + + ## 0.33.3 - 2025-12-11 Nothing new diff --git a/crates/epaint/src/direction.rs b/crates/epaint/src/direction.rs new file mode 100644 index 000000000..b2f317c37 --- /dev/null +++ b/crates/epaint/src/direction.rs @@ -0,0 +1,27 @@ +/// A cardinal direction, one of [`LeftToRight`](Direction::LeftToRight), [`RightToLeft`](Direction::RightToLeft), [`TopDown`](Direction::TopDown), [`BottomUp`](Direction::BottomUp). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub enum Direction { + LeftToRight, + RightToLeft, + TopDown, + BottomUp, +} + +impl Direction { + #[inline(always)] + pub fn is_horizontal(self) -> bool { + match self { + Self::LeftToRight | Self::RightToLeft => true, + Self::TopDown | Self::BottomUp => false, + } + } + + #[inline(always)] + pub fn is_vertical(self) -> bool { + match self { + Self::LeftToRight | Self::RightToLeft => false, + Self::TopDown | Self::BottomUp => true, + } + } +} diff --git a/crates/epaint/src/lib.rs b/crates/epaint/src/lib.rs index 0042e0964..6f574e6b4 100644 --- a/crates/epaint/src/lib.rs +++ b/crates/epaint/src/lib.rs @@ -27,6 +27,7 @@ mod brush; pub mod color; mod corner_radius; mod corner_radius_f32; +mod direction; pub mod image; mod margin; mod margin_f32; @@ -50,6 +51,7 @@ pub use self::{ color::ColorMode, corner_radius::CornerRadius, corner_radius_f32::CornerRadiusF32, + direction::Direction, image::{AlphaFromCoverage, ColorImage, ImageData, ImageDelta}, margin::Margin, margin_f32::*, diff --git a/crates/epaint/src/mesh.rs b/crates/epaint/src/mesh.rs index 5343fdace..d48c98bc2 100644 --- a/crates/epaint/src/mesh.rs +++ b/crates/epaint/src/mesh.rs @@ -23,6 +23,18 @@ pub struct Vertex { pub color: Color32, // 32 bit } +impl Vertex { + /// An untextured vertex + #[inline] + pub fn untextured(pos: Pos2, color: Color32) -> Self { + Self { + pos, + uv: WHITE_UV, + color, + } + } +} + #[repr(C)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[cfg(all(feature = "unity", not(feature = "_override_unity")))] @@ -159,11 +171,7 @@ impl Mesh { self.texture_id == TextureId::default(), "Mesh has an assigned texture" ); - self.vertices.push(Vertex { - pos, - uv: WHITE_UV, - color, - }); + self.vertices.push(Vertex::untextured(pos, color)); } /// Add a triangle. diff --git a/crates/epaint/src/shapes/shape.rs b/crates/epaint/src/shapes/shape.rs index fa8a3e75c..f5ca6a325 100644 --- a/crates/epaint/src/shapes/shape.rs +++ b/crates/epaint/src/shapes/shape.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use emath::{Align2, Pos2, Rangef, Rect, TSTransform, Vec2, pos2}; use crate::{ - Color32, CornerRadius, Mesh, Stroke, StrokeKind, TextureId, + Color32, CornerRadius, Direction, Mesh, Stroke, StrokeKind, TextureId, Vertex, stroke::PathStroke, text::{FontId, FontsView, Galley}, }; @@ -297,6 +297,32 @@ impl Shape { Self::Rect(RectShape::stroke(rect, corner_radius, stroke, stroke_kind)) } + /// Paints a gradient rectangle that transitions from `color_from` to `color_to` + /// along the given `direction`. + /// + /// For example, [`Direction::TopDown`] paints `color_from` at the top edge fading + /// to `color_to` at the bottom edge. + #[inline] + pub fn gradient_rect(rect: Rect, direction: Direction, [from, to]: [Color32; 2]) -> Self { + let (left_top, right_top, left_bottom, right_bottom) = match direction { + Direction::TopDown => (from, from, to, to), + Direction::BottomUp => (to, to, from, from), + Direction::LeftToRight => (from, to, from, to), + Direction::RightToLeft => (to, from, to, from), + }; + + Self::from(Mesh { + indices: vec![0, 1, 2, 2, 1, 3], + vertices: vec![ + Vertex::untextured(rect.left_top(), left_top), + Vertex::untextured(rect.right_top(), right_top), + Vertex::untextured(rect.left_bottom(), left_bottom), + Vertex::untextured(rect.right_bottom(), right_bottom), + ], + texture_id: Default::default(), + }) + } + #[expect(clippy::needless_pass_by_value)] pub fn text( fonts: &mut FontsView<'_>, diff --git a/crates/epaint/src/tessellator.rs b/crates/epaint/src/tessellator.rs index 9256ae16e..a732402d7 100644 --- a/crates/epaint/src/tessellator.rs +++ b/crates/epaint/src/tessellator.rs @@ -10,8 +10,8 @@ use emath::{GuiRounding as _, NumExt as _, Pos2, Rect, Rot2, Vec2, pos2, remap, use crate::{ CircleShape, ClippedPrimitive, ClippedShape, Color32, CornerRadiusF32, CubicBezierShape, EllipseShape, Mesh, PathShape, Primitive, QuadraticBezierShape, RectShape, Shape, Stroke, - StrokeKind, TextShape, TextureId, Vertex, WHITE_UV, color::ColorMode, emath, - stroke::PathStroke, texture_atlas::PreparedDisc, + StrokeKind, TextShape, TextureId, Vertex, color::ColorMode, emath, stroke::PathStroke, + texture_atlas::PreparedDisc, }; // ---------------------------------------------------------------------------- @@ -809,11 +809,8 @@ fn fill_closed_path(feathering: f32, path: &mut [PathPoint], fill_color: Color32 } else { out.reserve_triangles(n as usize); let idx = out.vertices.len() as u32; - out.vertices.extend(path.iter().map(|p| Vertex { - pos: p.pos, - uv: WHITE_UV, - color: fill_color, - })); + out.vertices + .extend(path.iter().map(|p| Vertex::untextured(p.pos, fill_color))); for i in 2..n { out.add_triangle(idx, idx + i - 1, idx + i); } diff --git a/crates/epaint_default_fonts/CHANGELOG.md b/crates/epaint_default_fonts/CHANGELOG.md index 58fd310f4..daf46d4de 100644 --- a/crates/epaint_default_fonts/CHANGELOG.md +++ b/crates/epaint_default_fonts/CHANGELOG.md @@ -5,6 +5,14 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.34.1 - 2026-03-27 +Nothing new + + +## 0.34.0 - 2026-03-26 +* Fix emoji icon font [#7940](https://github.com/emilk/egui/pull/7940) by [@Jhynjhiruu](https://github.com/Jhynjhiruu) + + ## 0.33.3 - 2025-12-11 Nothing new diff --git a/tests/egui_tests/tests/snapshots/text_edit_scroll_0_focus.png b/tests/egui_tests/tests/snapshots/text_edit_scroll_0_focus.png index 1d0a5ed46..019154ba6 100644 --- a/tests/egui_tests/tests/snapshots/text_edit_scroll_0_focus.png +++ b/tests/egui_tests/tests/snapshots/text_edit_scroll_0_focus.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:81620caad6d420f3bd0f224e5b07a02960a42436208a98d3aa012e5db61a743a -size 1510 +oid sha256:6f2a57ad8dbdd121cb181e74d76db68d800aba8fdc980d8de4e962e1e85fe8f6 +size 1803 diff --git a/tests/egui_tests/tests/snapshots/text_edit_scroll_1_5.png b/tests/egui_tests/tests/snapshots/text_edit_scroll_1_5.png index bdb8d1b1b..6c1ee1d81 100644 --- a/tests/egui_tests/tests/snapshots/text_edit_scroll_1_5.png +++ b/tests/egui_tests/tests/snapshots/text_edit_scroll_1_5.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f915eafb6490ff456c5b0a7c74c38ef143262bdf74a0c6561b9cf6ee66a679ea -size 1501 +oid sha256:43dc457cac18107772dbb7e5773961cf502dd685ce1cb4e94338e6b7daedaa77 +size 1861