mirror of
https://github.com/emilk/egui.git
synced 2026-09-01 14:20:04 -04:00
Merge main (v0.34.0) into feat/harfrust-text-shaping
This commit is contained in:
122
CHANGELOG.md
122
CHANGELOG.md
@@ -14,6 +14,128 @@ This file is updated upon each release.
|
|||||||
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
|
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
|
## 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<Target = Context>` 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
|
## 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)
|
* 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)
|
* Change text color of selected text [#7691](https://github.com/emilk/egui/pull/7691) by [@emilk](https://github.com/emilk)
|
||||||
|
|||||||
32
Cargo.lock
32
Cargo.lock
@@ -1193,7 +1193,7 @@ checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ecolor"
|
name = "ecolor"
|
||||||
version = "0.33.3"
|
version = "0.34.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cint",
|
"cint",
|
||||||
@@ -1205,7 +1205,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "eframe"
|
name = "eframe"
|
||||||
version = "0.33.3"
|
version = "0.34.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
@@ -1244,7 +1244,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui"
|
name = "egui"
|
||||||
version = "0.33.3"
|
version = "0.34.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"ahash",
|
"ahash",
|
||||||
@@ -1264,7 +1264,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui-wgpu"
|
name = "egui-wgpu"
|
||||||
version = "0.33.3"
|
version = "0.34.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
@@ -1282,7 +1282,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui-winit"
|
name = "egui-winit"
|
||||||
version = "0.33.3"
|
version = "0.34.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit_winit",
|
"accesskit_winit",
|
||||||
"arboard",
|
"arboard",
|
||||||
@@ -1305,7 +1305,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_demo_app"
|
name = "egui_demo_app"
|
||||||
version = "0.33.3"
|
version = "0.34.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"accesskit_consumer",
|
"accesskit_consumer",
|
||||||
@@ -1335,7 +1335,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_demo_lib"
|
name = "egui_demo_lib"
|
||||||
version = "0.33.3"
|
version = "0.34.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"criterion",
|
"criterion",
|
||||||
"document-features",
|
"document-features",
|
||||||
@@ -1352,7 +1352,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_extras"
|
name = "egui_extras"
|
||||||
version = "0.33.3"
|
version = "0.34.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"document-features",
|
"document-features",
|
||||||
@@ -1371,7 +1371,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_glow"
|
name = "egui_glow"
|
||||||
version = "0.33.3"
|
version = "0.34.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"document-features",
|
"document-features",
|
||||||
@@ -1390,7 +1390,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_kittest"
|
name = "egui_kittest"
|
||||||
version = "0.33.3"
|
version = "0.34.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dify",
|
"dify",
|
||||||
"document-features",
|
"document-features",
|
||||||
@@ -1410,7 +1410,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_tests"
|
name = "egui_tests"
|
||||||
version = "0.33.3"
|
version = "0.34.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"egui",
|
"egui",
|
||||||
"egui_extras",
|
"egui_extras",
|
||||||
@@ -1440,7 +1440,7 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "emath"
|
name = "emath"
|
||||||
version = "0.33.3"
|
version = "0.34.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"document-features",
|
"document-features",
|
||||||
@@ -1538,7 +1538,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "epaint"
|
name = "epaint"
|
||||||
version = "0.33.3"
|
version = "0.34.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
@@ -1567,7 +1567,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "epaint_default_fonts"
|
name = "epaint_default_fonts"
|
||||||
version = "0.33.3"
|
version = "0.34.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
@@ -3448,7 +3448,7 @@ checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "popups"
|
name = "popups"
|
||||||
version = "0.33.3"
|
version = "0.34.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"eframe",
|
"eframe",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
@@ -5843,7 +5843,7 @@ checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xtask"
|
name = "xtask"
|
||||||
version = "0.33.3"
|
version = "0.34.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yaml-rust"
|
name = "yaml-rust"
|
||||||
|
|||||||
26
Cargo.toml
26
Cargo.toml
@@ -24,7 +24,7 @@ members = [
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
rust-version = "1.92"
|
rust-version = "1.92"
|
||||||
version = "0.33.3"
|
version = "0.34.0"
|
||||||
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
@@ -55,18 +55,18 @@ opt-level = 2
|
|||||||
|
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
emath = { version = "0.33.3", path = "crates/emath", default-features = false }
|
emath = { version = "0.34.0", path = "crates/emath", default-features = false }
|
||||||
ecolor = { version = "0.33.3", path = "crates/ecolor", default-features = false }
|
ecolor = { version = "0.34.0", path = "crates/ecolor", default-features = false }
|
||||||
epaint = { version = "0.33.3", path = "crates/epaint", default-features = false }
|
epaint = { version = "0.34.0", path = "crates/epaint", default-features = false }
|
||||||
epaint_default_fonts = { version = "0.33.3", path = "crates/epaint_default_fonts" }
|
epaint_default_fonts = { version = "0.34.0", path = "crates/epaint_default_fonts" }
|
||||||
egui = { version = "0.33.3", path = "crates/egui", default-features = false }
|
egui = { version = "0.34.0", path = "crates/egui", default-features = false }
|
||||||
egui-winit = { version = "0.33.3", path = "crates/egui-winit", default-features = false }
|
egui-winit = { version = "0.34.0", path = "crates/egui-winit", default-features = false }
|
||||||
egui_extras = { version = "0.33.3", path = "crates/egui_extras", default-features = false }
|
egui_extras = { version = "0.34.0", path = "crates/egui_extras", default-features = false }
|
||||||
egui-wgpu = { version = "0.33.3", path = "crates/egui-wgpu", default-features = false }
|
egui-wgpu = { version = "0.34.0", path = "crates/egui-wgpu", default-features = false }
|
||||||
egui_demo_lib = { version = "0.33.3", path = "crates/egui_demo_lib", default-features = false }
|
egui_demo_lib = { version = "0.34.0", path = "crates/egui_demo_lib", default-features = false }
|
||||||
egui_glow = { version = "0.33.3", path = "crates/egui_glow", default-features = false }
|
egui_glow = { version = "0.34.0", path = "crates/egui_glow", default-features = false }
|
||||||
egui_kittest = { version = "0.33.3", path = "crates/egui_kittest", default-features = false }
|
egui_kittest = { version = "0.34.0", path = "crates/egui_kittest", default-features = false }
|
||||||
eframe = { version = "0.33.3", path = "crates/eframe", default-features = false }
|
eframe = { version = "0.34.0", path = "crates/eframe", default-features = false }
|
||||||
|
|
||||||
accesskit = "0.24.0"
|
accesskit = "0.24.0"
|
||||||
accesskit_consumer = "0.35.0"
|
accesskit_consumer = "0.35.0"
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ This file is updated upon each release.
|
|||||||
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
|
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.34.0 - 2026-03-26
|
||||||
|
Nothing new
|
||||||
|
|
||||||
|
|
||||||
## 0.33.3 - 2025-12-11
|
## 0.33.3 - 2025-12-11
|
||||||
Nothing new
|
Nothing new
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,30 @@ This file is updated upon each release.
|
|||||||
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
|
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
|
## 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
|
## 0.33.3 - 2025-12-11
|
||||||
Nothing new
|
Nothing new
|
||||||
|
|
||||||
|
|||||||
@@ -170,12 +170,16 @@ objc2-app-kit = { workspace = true, default-features = false, features = [
|
|||||||
"std",
|
"std",
|
||||||
"NSApplication",
|
"NSApplication",
|
||||||
"NSBitmapImageRep",
|
"NSBitmapImageRep",
|
||||||
|
"NSButton",
|
||||||
|
"NSControl",
|
||||||
"NSGraphics",
|
"NSGraphics",
|
||||||
"NSImage",
|
"NSImage",
|
||||||
"NSImageRep",
|
"NSImageRep",
|
||||||
"NSMenu",
|
"NSMenu",
|
||||||
"NSMenuItem",
|
"NSMenuItem",
|
||||||
"NSResponder",
|
"NSResponder",
|
||||||
|
"NSView",
|
||||||
|
"NSWindow",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
# windows:
|
# windows:
|
||||||
|
|||||||
@@ -190,6 +190,9 @@ pub use web::{WebLogger, WebRunner};
|
|||||||
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
mod native;
|
mod native;
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
pub use native::macos::WindowChromeMetrics;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
pub use native::run::EframeWinitApplication;
|
pub use native::run::EframeWinitApplication;
|
||||||
|
|||||||
76
crates/eframe/src/native/macos.rs
Normal file
76
crates/eframe/src/native/macos.rs
Normal file
@@ -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<Self> {
|
||||||
|
window_chrome_metrics(window_handle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn window_chrome_metrics(window_handle: &RawWindowHandle) -> Option<WindowChromeMetrics> {
|
||||||
|
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<Vec2> {
|
||||||
|
// 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::<NSView>();
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,9 @@ mod epi_integration;
|
|||||||
mod event_loop_context;
|
mod event_loop_context;
|
||||||
pub mod run;
|
pub mod run;
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
pub(crate) mod macos;
|
||||||
|
|
||||||
/// File storage which can be used by native backends.
|
/// File storage which can be used by native backends.
|
||||||
#[cfg(feature = "persistence")]
|
#[cfg(feature = "persistence")]
|
||||||
pub mod file_storage;
|
pub mod file_storage;
|
||||||
|
|||||||
@@ -6,6 +6,20 @@ This file is updated upon each release.
|
|||||||
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
|
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
|
## 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
|
## 0.33.3 - 2025-12-11
|
||||||
Nothing new
|
Nothing new
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,13 @@ This file is updated upon each release.
|
|||||||
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
|
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
|
## 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
|
## 0.33.3 - 2025-12-11
|
||||||
Nothing new
|
Nothing new
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
use std::ops::{Add, AddAssign, BitOr, BitOrAssign};
|
use std::ops::{Add, AddAssign, BitOr, BitOrAssign};
|
||||||
|
|
||||||
use emath::GuiRounding as _;
|
use emath::GuiRounding as _;
|
||||||
use epaint::Margin;
|
use epaint::{Color32, Direction, Margin, Shape};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Context, CursorIcon, Id, NumExt as _, Pos2, Rangef, Rect, Response, Sense, Ui, UiBuilder,
|
Context, CursorIcon, Id, NumExt as _, Pos2, Rangef, Rect, Response, Sense, Ui, UiBuilder,
|
||||||
@@ -1019,13 +1019,17 @@ impl ScrollArea {
|
|||||||
.inner;
|
.inner;
|
||||||
|
|
||||||
let (content_size, state) = prepared.end(ui);
|
let (content_size, state) = prepared.end(ui);
|
||||||
ScrollAreaOutput {
|
let output = ScrollAreaOutput {
|
||||||
inner,
|
inner,
|
||||||
id,
|
id,
|
||||||
state,
|
state,
|
||||||
content_size,
|
content_size,
|
||||||
inner_rect,
|
inner_rect,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
paint_fade_areas(ui, &output);
|
||||||
|
|
||||||
|
output
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1504,3 +1508,88 @@ impl Prepared {
|
|||||||
(content_size, state)
|
(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<R>(ui: &Ui, scroll_output: &ScrollAreaOutput<R>) {
|
||||||
|
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],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1545,6 +1545,11 @@ impl Context {
|
|||||||
crate::debug_text::print(self, text);
|
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?
|
/// What operating system are we running on?
|
||||||
///
|
///
|
||||||
/// When compiling natively, this is
|
/// When compiling natively, this is
|
||||||
@@ -1933,7 +1938,7 @@ impl Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Callbacks
|
/// Plugins
|
||||||
impl Context {
|
impl Context {
|
||||||
/// Call the given callback at the start of each pass of each viewport.
|
/// Call the given callback at the start of each pass of each viewport.
|
||||||
///
|
///
|
||||||
@@ -2945,6 +2950,15 @@ impl Context {
|
|||||||
self.egui_wants_keyboard_input()
|
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.
|
/// 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,
|
/// If you call this after the widget has been fully rendered,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use emath::GuiRounding as _;
|
use emath::GuiRounding as _;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Align,
|
Align, Direction,
|
||||||
emath::{Align2, NumExt as _, Pos2, Rect, Vec2, pos2, vec2},
|
emath::{Align2, NumExt as _, Pos2, Rect, Vec2, pos2, vec2},
|
||||||
};
|
};
|
||||||
const INFINITY: f32 = f32::INFINITY;
|
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".
|
/// The layout of a [`Ui`][`crate::Ui`], e.g. "vertical & centered".
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
|||||||
@@ -449,7 +449,7 @@ pub use emath::{
|
|||||||
remap_clamp, vec2,
|
remap_clamp, vec2,
|
||||||
};
|
};
|
||||||
pub use epaint::{
|
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,
|
PaintCallbackInfo, Shadow, Shape, Stroke, StrokeKind, TextureHandle, TextureId, mutex,
|
||||||
text::{FontData, FontDefinitions, FontFamily, FontId, FontTweak},
|
text::{FontData, FontDefinitions, FontFamily, FontId, FontTweak},
|
||||||
textures::{TextureFilter, TextureOptions, TextureWrapMode, TexturesDelta},
|
textures::{TextureFilter, TextureOptions, TextureWrapMode, TexturesDelta},
|
||||||
@@ -478,7 +478,7 @@ pub use self::{
|
|||||||
drag_and_drop::DragAndDrop,
|
drag_and_drop::DragAndDrop,
|
||||||
epaint::text::TextWrapMode,
|
epaint::text::TextWrapMode,
|
||||||
grid::Grid,
|
grid::Grid,
|
||||||
id::{Id, IdMap},
|
id::{Id, IdMap, IdSet},
|
||||||
input_state::{InputOptions, InputState, MultiTouchInfo, PointerState, SurrenderFocusOn},
|
input_state::{InputOptions, InputState, MultiTouchInfo, PointerState, SurrenderFocusOn},
|
||||||
layers::{LayerId, Order},
|
layers::{LayerId, Order},
|
||||||
layout::*,
|
layout::*,
|
||||||
|
|||||||
@@ -586,6 +586,8 @@ pub struct ScrollStyle {
|
|||||||
/// This is only for floating scroll bars.
|
/// This is only for floating scroll bars.
|
||||||
/// Solid scroll bars are always opaque.
|
/// Solid scroll bars are always opaque.
|
||||||
pub interact_handle_opacity: f32,
|
pub interact_handle_opacity: f32,
|
||||||
|
|
||||||
|
pub fade: ScrollFadeStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ScrollStyle {
|
impl Default for ScrollStyle {
|
||||||
@@ -616,6 +618,8 @@ impl ScrollStyle {
|
|||||||
dormant_handle_opacity: 0.0,
|
dormant_handle_opacity: 0.0,
|
||||||
active_handle_opacity: 0.6,
|
active_handle_opacity: 0.6,
|
||||||
interact_handle_opacity: 1.0,
|
interact_handle_opacity: 1.0,
|
||||||
|
|
||||||
|
fade: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -699,6 +703,8 @@ impl ScrollStyle {
|
|||||||
dormant_handle_opacity,
|
dormant_handle_opacity,
|
||||||
active_handle_opacity,
|
active_handle_opacity,
|
||||||
interact_handle_opacity,
|
interact_handle_opacity,
|
||||||
|
|
||||||
|
fade,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
@@ -772,6 +778,52 @@ impl ScrollStyle {
|
|||||||
ui.label("Inner margin");
|
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");
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -487,6 +487,12 @@ impl Ui {
|
|||||||
&mut self.style_mut().visuals
|
&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`].
|
/// Get a reference to this [`Ui`]'s [`UiStack`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn stack(&self) -> &Arc<UiStack> {
|
pub fn stack(&self) -> &Arc<UiStack> {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{any::Any, iter::FusedIterator};
|
use std::{any::Any, iter::FusedIterator};
|
||||||
|
|
||||||
|
use epaint::Color32;
|
||||||
|
|
||||||
use crate::{Direction, Frame, Id, Rect};
|
use crate::{Direction, Frame, Id, Rect};
|
||||||
|
|
||||||
/// What kind is this [`crate::Ui`]?
|
/// What kind is this [`crate::Ui`]?
|
||||||
@@ -253,6 +255,25 @@ impl UiStack {
|
|||||||
pub fn has_visible_frame(&self) -> bool {
|
pub fn has_visible_frame(&self) -> bool {
|
||||||
!self.info.frame.stroke.is_empty()
|
!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
|
// these methods act on the entire stack
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
// This will also allow users to pick their own serialization format per type.
|
// This will also allow users to pick their own serialization format per type.
|
||||||
|
|
||||||
use std::{any::Any, sync::Arc};
|
use std::{any::Any, sync::Arc};
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Like [`std::any::TypeId`], but can be serialized and deserialized.
|
/// 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]
|
#[inline]
|
||||||
pub(crate) fn get_temp<T: 'static>(&self) -> Option<&T> {
|
pub(crate) fn get_temp<T: 'static>(&self) -> Option<&T> {
|
||||||
match self {
|
match self {
|
||||||
@@ -316,6 +327,41 @@ fn from_ron_str<T: serde::de::DeserializeOwned>(ron: &str) -> Option<T> {
|
|||||||
|
|
||||||
use crate::Id;
|
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::<i32>(Id::NULL),
|
||||||
|
/// RawKey::new::<String>(Id::NULL),
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn new<T: 'static>(id: Id) -> Self {
|
||||||
|
let type_id = TypeId::of::<T>();
|
||||||
|
Self(type_id.value() ^ id.value())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(emilk): make IdTypeMap generic over the key (`Id`), and make a library of IdTypeMap.
|
// 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.
|
/// Stores values identified by an [`Id`] AND the [`std::any::TypeId`] of the value.
|
||||||
///
|
///
|
||||||
@@ -358,7 +404,7 @@ use crate::Id;
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
// We use `id XOR typeid` as a key, so we don't need to hash again!
|
// We use `id XOR typeid` as a key, so we don't need to hash again!
|
||||||
pub struct IdTypeMap {
|
pub struct IdTypeMap {
|
||||||
map: nohash_hasher::IntMap<u64, Element>,
|
map: nohash_hasher::IntMap<RawKey, Element>,
|
||||||
|
|
||||||
max_bytes_per_type: usize,
|
max_bytes_per_type: usize,
|
||||||
}
|
}
|
||||||
@@ -375,16 +421,23 @@ impl Default for IdTypeMap {
|
|||||||
impl IdTypeMap {
|
impl IdTypeMap {
|
||||||
/// Insert a value that will not be persisted.
|
/// Insert a value that will not be persisted.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert_temp<T: 'static + Any + Clone + Send + Sync>(&mut self, id: Id, value: T) {
|
pub fn insert_temp<T: 'static + Any + Clone + Send + Sync>(
|
||||||
let hash = hash(TypeId::of::<T>(), id);
|
&mut self,
|
||||||
self.map.insert(hash, Element::new_temp(value));
|
id: Id,
|
||||||
|
value: T,
|
||||||
|
) -> RawKey {
|
||||||
|
let key = RawKey::new::<T>(id);
|
||||||
|
self.map.insert(key, Element::new_temp(value));
|
||||||
|
key
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a value that will be persisted next time you start the app.
|
/// Insert a value that will be persisted next time you start the app.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert_persisted<T: SerializableAny>(&mut self, id: Id, value: T) {
|
pub fn insert_persisted<T: SerializableAny>(&mut self, id: Id, value: T) {
|
||||||
let hash = hash(TypeId::of::<T>(), id);
|
let key = RawKey::new::<T>(id);
|
||||||
self.map.insert(hash, Element::new_persisted(value));
|
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.
|
/// 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!
|
/// The call clones the value (if found), so make sure it is cheap to clone!
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_temp<T: 'static + Clone>(&self, id: Id) -> Option<T> {
|
pub fn get_temp<T: 'static + Clone>(&self, id: Id) -> Option<T> {
|
||||||
let hash = hash(TypeId::of::<T>(), id);
|
let key = RawKey::new::<T>(id);
|
||||||
self.map.get(&hash).and_then(|x| x.get_temp()).cloned()
|
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.
|
/// 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!
|
/// The call clones the value (if found), so make sure it is cheap to clone!
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_persisted<T: SerializableAny>(&mut self, id: Id) -> Option<T> {
|
pub fn get_persisted<T: SerializableAny>(&mut self, id: Id) -> Option<T> {
|
||||||
let hash = hash(TypeId::of::<T>(), id);
|
let key = RawKey::new::<T>(id);
|
||||||
self.map
|
self.map
|
||||||
.get_mut(&hash)
|
.get_mut(&key)
|
||||||
.and_then(|x| x.get_mut_persisted())
|
.and_then(|x| x.get_mut_persisted())
|
||||||
.cloned()
|
.cloned()
|
||||||
}
|
}
|
||||||
@@ -443,9 +516,9 @@ impl IdTypeMap {
|
|||||||
id: Id,
|
id: Id,
|
||||||
insert_with: impl FnOnce() -> T,
|
insert_with: impl FnOnce() -> T,
|
||||||
) -> &mut T {
|
) -> &mut T {
|
||||||
let hash = hash(TypeId::of::<T>(), id);
|
let key = RawKey::new::<T>(id);
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
match self.map.entry(hash) {
|
match self.map.entry(key) {
|
||||||
Entry::Vacant(vacant) => {
|
Entry::Vacant(vacant) => {
|
||||||
// this unwrap will never panic, because we insert correct type right now
|
// this unwrap will never panic, because we insert correct type right now
|
||||||
#[expect(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
@@ -465,9 +538,9 @@ impl IdTypeMap {
|
|||||||
id: Id,
|
id: Id,
|
||||||
insert_with: impl FnOnce() -> T,
|
insert_with: impl FnOnce() -> T,
|
||||||
) -> &mut T {
|
) -> &mut T {
|
||||||
let hash = hash(TypeId::of::<T>(), id);
|
let key = RawKey::new::<T>(id);
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
match self.map.entry(hash) {
|
match self.map.entry(key) {
|
||||||
Entry::Vacant(vacant) => {
|
Entry::Vacant(vacant) => {
|
||||||
// this unwrap will never panic, because we insert correct type right now
|
// this unwrap will never panic, because we insert correct type right now
|
||||||
#[expect(clippy::unwrap_used)]
|
#[expect(clippy::unwrap_used)]
|
||||||
@@ -486,7 +559,7 @@ impl IdTypeMap {
|
|||||||
#[cfg(feature = "persistence")]
|
#[cfg(feature = "persistence")]
|
||||||
#[allow(clippy::allow_attributes, unused)]
|
#[allow(clippy::allow_attributes, unused)]
|
||||||
fn get_generation<T: SerializableAny>(&self, id: Id) -> Option<usize> {
|
fn get_generation<T: SerializableAny>(&self, id: Id) -> Option<usize> {
|
||||||
let element = self.map.get(&hash(TypeId::of::<T>(), id))?;
|
let element = self.map.get(&RawKey::new::<T>(id))?;
|
||||||
match element {
|
match element {
|
||||||
Element::Value { .. } => Some(0),
|
Element::Value { .. } => Some(0),
|
||||||
Element::Serialized(SerializedElement { generation, .. }) => Some(*generation),
|
Element::Serialized(SerializedElement { generation, .. }) => Some(*generation),
|
||||||
@@ -496,18 +569,33 @@ impl IdTypeMap {
|
|||||||
/// Remove the state of this type and id.
|
/// Remove the state of this type and id.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn remove<T: 'static>(&mut self, id: Id) {
|
pub fn remove<T: 'static>(&mut self, id: Id) {
|
||||||
let hash = hash(TypeId::of::<T>(), id);
|
let key = RawKey::new::<T>(id);
|
||||||
self.map.remove(&hash);
|
self.map.remove(&key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove and fetch the state of this type and id.
|
/// Remove and fetch the state of this type and id.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn remove_temp<T: 'static + Default>(&mut self, id: Id) -> Option<T> {
|
pub fn remove_temp<T: 'static + Default>(&mut self, id: Id) -> Option<T> {
|
||||||
let hash = hash(TypeId::of::<T>(), id);
|
let key = RawKey::new::<T>(id);
|
||||||
let mut element = self.map.remove(&hash)?;
|
let mut element = self.map.remove(&key)?;
|
||||||
Some(std::mem::take(element.get_mut_temp()?))
|
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<Box<dyn Any + Send + Sync>> {
|
||||||
|
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.
|
/// Note all state of the given type.
|
||||||
pub fn remove_by_type<T: 'static>(&mut self) {
|
pub fn remove_by_type<T: 'static>(&mut self) {
|
||||||
let key = TypeId::of::<T>();
|
let key = TypeId::of::<T>();
|
||||||
@@ -532,6 +620,18 @@ impl IdTypeMap {
|
|||||||
self.map.len()
|
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<Item = RawKey> {
|
||||||
|
self.map
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, v)| v.is_temp())
|
||||||
|
.map(|(k, _)| *k)
|
||||||
|
}
|
||||||
|
|
||||||
/// Count how many values are stored but not yet deserialized.
|
/// Count how many values are stored but not yet deserialized.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn count_serialized(&self) -> usize {
|
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.
|
/// How [`IdTypeMap`] is persisted.
|
||||||
@@ -613,13 +708,13 @@ impl PersistedMap {
|
|||||||
|
|
||||||
{
|
{
|
||||||
profiling::scope!("gather");
|
profiling::scope!("gather");
|
||||||
for (hash, element) in &map.map {
|
for (key, element) in &map.map {
|
||||||
if let Some(element) = element.to_serialize() {
|
if let Some(element) = element.to_serialize() {
|
||||||
let stats = types_map.entry(element.type_id).or_default();
|
let stats = types_map.entry(element.type_id).or_default();
|
||||||
stats.num_bytes += element.ron.len();
|
stats.num_bytes += element.ron.len();
|
||||||
let generation_stats = stats.generations.entry(element.generation).or_default();
|
let generation_stats = stats.generations.entry(element.generation).or_default();
|
||||||
generation_stats.num_bytes += element.ron.len();
|
generation_stats.num_bytes += element.ron.len();
|
||||||
generation_stats.elements.push((*hash, element));
|
generation_stats.elements.push((key.0, element));
|
||||||
} else {
|
} else {
|
||||||
// temporary value that shouldn't be serialized
|
// temporary value that shouldn't be serialized
|
||||||
}
|
}
|
||||||
@@ -659,7 +754,7 @@ impl PersistedMap {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(
|
.map(
|
||||||
|(
|
|(
|
||||||
hash,
|
raw,
|
||||||
SerializedElement {
|
SerializedElement {
|
||||||
type_id,
|
type_id,
|
||||||
ron,
|
ron,
|
||||||
@@ -667,7 +762,7 @@ impl PersistedMap {
|
|||||||
},
|
},
|
||||||
)| {
|
)| {
|
||||||
(
|
(
|
||||||
hash,
|
RawKey(raw),
|
||||||
Element::Serialized(SerializedElement {
|
Element::Serialized(SerializedElement {
|
||||||
type_id,
|
type_id,
|
||||||
ron,
|
ron,
|
||||||
|
|||||||
@@ -371,6 +371,12 @@ impl<'a> Button<'a> {
|
|||||||
AtomLayoutResponse::empty(prepared.response)
|
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(|| {
|
response.response.widget_info(|| {
|
||||||
if let Some(text) = &text {
|
if let Some(text) = &text {
|
||||||
WidgetInfo::labeled(WidgetType::Button, ui.is_enabled(), text)
|
WidgetInfo::labeled(WidgetType::Button, ui.is_enabled(), text)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ impl crate::Demo for StripDemo {
|
|||||||
impl crate::View for StripDemo {
|
impl crate::View for StripDemo {
|
||||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||||
let dark_mode = ui.visuals().dark_mode;
|
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 {
|
let faded_color = |color: Color32| -> Color32 {
|
||||||
use egui::Rgba;
|
use egui::Rgba;
|
||||||
let t = if dark_mode { 0.95 } else { 0.8 };
|
let t = if dark_mode { 0.95 } else { 0.8 };
|
||||||
|
|||||||
@@ -5,6 +5,14 @@ This file is updated upon each release.
|
|||||||
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
|
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
|
## 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
|
## 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)
|
* Bump `ehttp` to 0.6.0 [#7757](https://github.com/emilk/egui/pull/7757) by [@jprochazk](https://github.com/jprochazk)
|
||||||
|
|
||||||
|
|||||||
@@ -656,14 +656,13 @@ impl TableState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn store(self, ui: &egui::Ui, state_id: egui::Id) {
|
fn store(self, ui: &egui::Ui, state_id: egui::Id) {
|
||||||
#![expect(clippy::needless_return)]
|
|
||||||
#[cfg(feature = "serde")]
|
#[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"))]
|
#[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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ Changes since the last release can be found at <https://github.com/emilk/egui/co
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 0.34.0 - 2026-03-26
|
||||||
|
Nothing new
|
||||||
|
|
||||||
|
|
||||||
## 0.33.3 - 2025-12-11
|
## 0.33.3 - 2025-12-11
|
||||||
Nothing new
|
Nothing new
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,13 @@ This file is updated upon each release.
|
|||||||
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
|
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
|
## 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
|
## 0.33.3 - 2025-12-11
|
||||||
* Enforce consistent snapshot updates [#7744](https://github.com/emilk/egui/pull/7744) by [@lucasmerlin](https://github.com/lucasmerlin)
|
* 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)
|
* `kittest`: add drag-and-drop helpers [#7690](https://github.com/emilk/egui/pull/7690) by [@emilk](https://github.com/emilk)
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ This file is updated upon each release.
|
|||||||
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
|
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.34.0 - 2026-03-26
|
||||||
|
Nothing new
|
||||||
|
|
||||||
|
|
||||||
## 0.33.3 - 2025-12-11
|
## 0.33.3 - 2025-12-11
|
||||||
Nothing new
|
Nothing new
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,25 @@ This file is updated upon each release.
|
|||||||
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
|
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
|
## 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
|
## 0.33.3 - 2025-12-11
|
||||||
Nothing new
|
Nothing new
|
||||||
|
|
||||||
|
|||||||
27
crates/epaint/src/direction.rs
Normal file
27
crates/epaint/src/direction.rs
Normal file
@@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,6 +27,7 @@ mod brush;
|
|||||||
pub mod color;
|
pub mod color;
|
||||||
mod corner_radius;
|
mod corner_radius;
|
||||||
mod corner_radius_f32;
|
mod corner_radius_f32;
|
||||||
|
mod direction;
|
||||||
pub mod image;
|
pub mod image;
|
||||||
mod margin;
|
mod margin;
|
||||||
mod margin_f32;
|
mod margin_f32;
|
||||||
@@ -50,6 +51,7 @@ pub use self::{
|
|||||||
color::ColorMode,
|
color::ColorMode,
|
||||||
corner_radius::CornerRadius,
|
corner_radius::CornerRadius,
|
||||||
corner_radius_f32::CornerRadiusF32,
|
corner_radius_f32::CornerRadiusF32,
|
||||||
|
direction::Direction,
|
||||||
image::{AlphaFromCoverage, ColorImage, ImageData, ImageDelta},
|
image::{AlphaFromCoverage, ColorImage, ImageData, ImageDelta},
|
||||||
margin::Margin,
|
margin::Margin,
|
||||||
margin_f32::*,
|
margin_f32::*,
|
||||||
|
|||||||
@@ -23,6 +23,18 @@ pub struct Vertex {
|
|||||||
pub color: Color32, // 32 bit
|
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)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
#[cfg(all(feature = "unity", not(feature = "_override_unity")))]
|
#[cfg(all(feature = "unity", not(feature = "_override_unity")))]
|
||||||
@@ -159,11 +171,7 @@ impl Mesh {
|
|||||||
self.texture_id == TextureId::default(),
|
self.texture_id == TextureId::default(),
|
||||||
"Mesh has an assigned texture"
|
"Mesh has an assigned texture"
|
||||||
);
|
);
|
||||||
self.vertices.push(Vertex {
|
self.vertices.push(Vertex::untextured(pos, color));
|
||||||
pos,
|
|
||||||
uv: WHITE_UV,
|
|
||||||
color,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a triangle.
|
/// Add a triangle.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use std::sync::Arc;
|
|||||||
use emath::{Align2, Pos2, Rangef, Rect, TSTransform, Vec2, pos2};
|
use emath::{Align2, Pos2, Rangef, Rect, TSTransform, Vec2, pos2};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Color32, CornerRadius, Mesh, Stroke, StrokeKind, TextureId,
|
Color32, CornerRadius, Direction, Mesh, Stroke, StrokeKind, TextureId, Vertex,
|
||||||
stroke::PathStroke,
|
stroke::PathStroke,
|
||||||
text::{FontId, FontsView, Galley},
|
text::{FontId, FontsView, Galley},
|
||||||
};
|
};
|
||||||
@@ -297,6 +297,32 @@ impl Shape {
|
|||||||
Self::Rect(RectShape::stroke(rect, corner_radius, stroke, stroke_kind))
|
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)]
|
#[expect(clippy::needless_pass_by_value)]
|
||||||
pub fn text(
|
pub fn text(
|
||||||
fonts: &mut FontsView<'_>,
|
fonts: &mut FontsView<'_>,
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ use emath::{GuiRounding as _, NumExt as _, Pos2, Rect, Rot2, Vec2, pos2, remap,
|
|||||||
use crate::{
|
use crate::{
|
||||||
CircleShape, ClippedPrimitive, ClippedShape, Color32, CornerRadiusF32, CubicBezierShape,
|
CircleShape, ClippedPrimitive, ClippedShape, Color32, CornerRadiusF32, CubicBezierShape,
|
||||||
EllipseShape, Mesh, PathShape, Primitive, QuadraticBezierShape, RectShape, Shape, Stroke,
|
EllipseShape, Mesh, PathShape, Primitive, QuadraticBezierShape, RectShape, Shape, Stroke,
|
||||||
StrokeKind, TextShape, TextureId, Vertex, WHITE_UV, color::ColorMode, emath,
|
StrokeKind, TextShape, TextureId, Vertex, color::ColorMode, emath, stroke::PathStroke,
|
||||||
stroke::PathStroke, texture_atlas::PreparedDisc,
|
texture_atlas::PreparedDisc,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@@ -809,11 +809,8 @@ fn fill_closed_path(feathering: f32, path: &mut [PathPoint], fill_color: Color32
|
|||||||
} else {
|
} else {
|
||||||
out.reserve_triangles(n as usize);
|
out.reserve_triangles(n as usize);
|
||||||
let idx = out.vertices.len() as u32;
|
let idx = out.vertices.len() as u32;
|
||||||
out.vertices.extend(path.iter().map(|p| Vertex {
|
out.vertices
|
||||||
pos: p.pos,
|
.extend(path.iter().map(|p| Vertex::untextured(p.pos, fill_color)));
|
||||||
uv: WHITE_UV,
|
|
||||||
color: fill_color,
|
|
||||||
}));
|
|
||||||
for i in 2..n {
|
for i in 2..n {
|
||||||
out.add_triangle(idx, idx + i - 1, idx + i);
|
out.add_triangle(idx, idx + i - 1, idx + i);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ This file is updated upon each release.
|
|||||||
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
|
Changes since the last release can be found at <https://github.com/emilk/egui/compare/latest...HEAD> or by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
|
## 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
|
## 0.33.3 - 2025-12-11
|
||||||
Nothing new
|
Nothing new
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:81620caad6d420f3bd0f224e5b07a02960a42436208a98d3aa012e5db61a743a
|
oid sha256:6f2a57ad8dbdd121cb181e74d76db68d800aba8fdc980d8de4e962e1e85fe8f6
|
||||||
size 1510
|
size 1803
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:f915eafb6490ff456c5b0a7c74c38ef143262bdf74a0c6561b9cf6ee66a679ea
|
oid sha256:43dc457cac18107772dbb7e5773961cf502dd685ce1cb4e94338e6b7daedaa77
|
||||||
size 1501
|
size 1861
|
||||||
|
|||||||
Reference in New Issue
Block a user