mirror of
https://github.com/emilk/egui.git
synced 2026-09-02 14:50:03 -04:00
Merge remote-tracking branch 'upstream/main'
This commit is contained in:
6
.github/workflows/rust.yml
vendored
6
.github/workflows/rust.yml
vendored
@@ -46,7 +46,9 @@ jobs:
|
|||||||
|
|
||||||
- run: cargo clippy --locked --no-default-features --lib --all-targets
|
- run: cargo clippy --locked --no-default-features --lib --all-targets
|
||||||
|
|
||||||
- run: cargo clippy --locked --no-default-features --features x11 --lib -p eframe
|
- run: cargo clippy --locked --no-default-features --lib -p eframe --features x11
|
||||||
|
|
||||||
|
- run: cargo clippy --locked --no-default-features --lib -p eframe --features x11,wgpu_no_default_features
|
||||||
|
|
||||||
- run: cargo clippy --locked --no-default-features --lib -p egui_extras
|
- run: cargo clippy --locked --no-default-features --lib -p egui_extras
|
||||||
|
|
||||||
@@ -87,7 +89,7 @@ jobs:
|
|||||||
run: cargo clippy -p egui_demo_app --lib --target wasm32-unknown-unknown --all-features
|
run: cargo clippy -p egui_demo_app --lib --target wasm32-unknown-unknown --all-features
|
||||||
|
|
||||||
- name: clippy wasm32 eframe
|
- name: clippy wasm32 eframe
|
||||||
run: cargo clippy -p eframe --lib --no-default-features --features glow,persistence --target wasm32-unknown-unknown
|
run: cargo clippy -p eframe --lib --no-default-features --features wgpu,persistence --target wasm32-unknown-unknown
|
||||||
|
|
||||||
- name: wasm-bindgen
|
- name: wasm-bindgen
|
||||||
uses: jetli/wasm-bindgen-action@v0.1.0
|
uses: jetli/wasm-bindgen-action@v0.1.0
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Also see [`CONTRIBUTING.md`](CONTRIBUTING.md) for what to do before opening a PR
|
|||||||
|
|
||||||
|
|
||||||
## Crate overview
|
## Crate overview
|
||||||
The crates in this repository are: `egui, emath, epaint, epaint_default_fonts, egui_extras, egui-winit, egui_glow, egui_demo_lib, egui_demo_app`.
|
The crates in this repository are: `egui, emath, epaint, epaint_default_fonts, egui_extras, egui-winit, egui_glow, egui-wgpu, egui_demo_lib, egui_demo_app`.
|
||||||
|
|
||||||
### `egui`: The main GUI library.
|
### `egui`: The main GUI library.
|
||||||
Example code: `if ui.button("Click me").clicked() { … }`
|
Example code: `if ui.button("Click me").clicked() { … }`
|
||||||
@@ -37,6 +37,9 @@ The library translates winit events to egui, handled copy/paste, updates the cur
|
|||||||
### `egui_glow`
|
### `egui_glow`
|
||||||
Puts an egui app inside a native window on your laptop. Paints the triangles that egui outputs using [glow](https://github.com/grovesNL/glow).
|
Puts an egui app inside a native window on your laptop. Paints the triangles that egui outputs using [glow](https://github.com/grovesNL/glow).
|
||||||
|
|
||||||
|
### `egui-wgpu`
|
||||||
|
Paints the triangles that egui outputs using [wgpu](https://github.com/grovesNL/wgpu).
|
||||||
|
|
||||||
### `eframe`
|
### `eframe`
|
||||||
`eframe` is the official `egui` framework, built so you can compile the same app for either web or native.
|
`eframe` is the official `egui` framework, built so you can compile the same app for either web or native.
|
||||||
|
|
||||||
|
|||||||
16
CHANGELOG.md
16
CHANGELOG.md
@@ -14,6 +14,22 @@ 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.33.2 - 2025-11-13
|
||||||
|
### ⭐ Added
|
||||||
|
* Add `Plugin::on_widget_under_pointer` to support widget inspector [#7652](https://github.com/emilk/egui/pull/7652) by [@juancampa](https://github.com/juancampa)
|
||||||
|
* Add `Response::total_drag_delta` and `PointerState::total_drag_delta` [#7708](https://github.com/emilk/egui/pull/7708) by [@emilk](https://github.com/emilk)
|
||||||
|
|
||||||
|
### 🔧 Changed
|
||||||
|
* Improve accessibility and testability of `ComboBox` [#7658](https://github.com/emilk/egui/pull/7658) by [@lucasmerlin](https://github.com/lucasmerlin)
|
||||||
|
|
||||||
|
### 🐛 Fixed
|
||||||
|
* Fix `profiling::scope` compile error when profiling using `tracing` backend [#7646](https://github.com/emilk/egui/pull/7646) by [@PPakalns](https://github.com/PPakalns)
|
||||||
|
* Fix edge cases in "smart aiming" in sliders [#7680](https://github.com/emilk/egui/pull/7680) by [@emilk](https://github.com/emilk)
|
||||||
|
* Hide scroll bars when dragging other things [#7689](https://github.com/emilk/egui/pull/7689) by [@emilk](https://github.com/emilk)
|
||||||
|
* Prevent widgets sometimes appearing to move relative to each other [#7710](https://github.com/emilk/egui/pull/7710) by [@emilk](https://github.com/emilk)
|
||||||
|
* Fix `ui.response().interact(Sense::click())` being flakey [#7713](https://github.com/emilk/egui/pull/7713) by [@lucasmerlin](https://github.com/lucasmerlin)
|
||||||
|
|
||||||
|
|
||||||
## 0.33.0 - 2025-10-09 - `egui::Plugin`, better kerning, kitdiff viewer
|
## 0.33.0 - 2025-10-09 - `egui::Plugin`, better kerning, kitdiff viewer
|
||||||
Highlights from this release:
|
Highlights from this release:
|
||||||
- `egui::Plugin` a improved way to create and access egui plugins
|
- `egui::Plugin` a improved way to create and access egui plugins
|
||||||
|
|||||||
32
Cargo.lock
32
Cargo.lock
@@ -1248,7 +1248,7 @@ checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ecolor"
|
name = "ecolor"
|
||||||
version = "0.33.0"
|
version = "0.33.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cint",
|
"cint",
|
||||||
@@ -1260,7 +1260,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "eframe"
|
name = "eframe"
|
||||||
version = "0.33.0"
|
version = "0.33.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
@@ -1299,7 +1299,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui"
|
name = "egui"
|
||||||
version = "0.33.0"
|
version = "0.33.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"ahash",
|
"ahash",
|
||||||
@@ -1319,7 +1319,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui-wgpu"
|
name = "egui-wgpu"
|
||||||
version = "0.33.0"
|
version = "0.33.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
@@ -1337,7 +1337,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui-winit"
|
name = "egui-winit"
|
||||||
version = "0.33.0"
|
version = "0.33.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit_winit",
|
"accesskit_winit",
|
||||||
"arboard",
|
"arboard",
|
||||||
@@ -1360,7 +1360,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_demo_app"
|
name = "egui_demo_app"
|
||||||
version = "0.33.0"
|
version = "0.33.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"accesskit_consumer",
|
"accesskit_consumer",
|
||||||
@@ -1390,7 +1390,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_demo_lib"
|
name = "egui_demo_lib"
|
||||||
version = "0.33.0"
|
version = "0.33.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"criterion",
|
"criterion",
|
||||||
@@ -1407,7 +1407,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_extras"
|
name = "egui_extras"
|
||||||
version = "0.33.0"
|
version = "0.33.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -1426,7 +1426,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_glow"
|
name = "egui_glow"
|
||||||
version = "0.33.0"
|
version = "0.33.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"document-features",
|
"document-features",
|
||||||
@@ -1445,7 +1445,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_kittest"
|
name = "egui_kittest"
|
||||||
version = "0.33.1"
|
version = "0.33.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dify",
|
"dify",
|
||||||
"document-features",
|
"document-features",
|
||||||
@@ -1463,7 +1463,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_tests"
|
name = "egui_tests"
|
||||||
version = "0.33.0"
|
version = "0.33.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"egui",
|
"egui",
|
||||||
"egui_extras",
|
"egui_extras",
|
||||||
@@ -1493,7 +1493,7 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "emath"
|
name = "emath"
|
||||||
version = "0.33.0"
|
version = "0.33.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"document-features",
|
"document-features",
|
||||||
@@ -1591,7 +1591,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "epaint"
|
name = "epaint"
|
||||||
version = "0.33.0"
|
version = "0.33.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ab_glyph",
|
"ab_glyph",
|
||||||
"ahash",
|
"ahash",
|
||||||
@@ -1613,7 +1613,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "epaint_default_fonts"
|
name = "epaint_default_fonts"
|
||||||
version = "0.33.0"
|
version = "0.33.2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
@@ -3425,7 +3425,7 @@ checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "popups"
|
name = "popups"
|
||||||
version = "0.33.0"
|
version = "0.33.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"eframe",
|
"eframe",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
@@ -5748,7 +5748,7 @@ checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xtask"
|
name = "xtask"
|
||||||
version = "0.33.0"
|
version = "0.33.2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yaml-rust"
|
name = "yaml-rust"
|
||||||
|
|||||||
28
Cargo.toml
28
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.88"
|
rust-version = "1.88"
|
||||||
version = "0.33.0"
|
version = "0.33.2"
|
||||||
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
@@ -55,18 +55,18 @@ opt-level = 2
|
|||||||
|
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
emath = { version = "0.33.0", path = "crates/emath", default-features = false }
|
emath = { version = "0.33.2", path = "crates/emath", default-features = false }
|
||||||
ecolor = { version = "0.33.0", path = "crates/ecolor", default-features = false }
|
ecolor = { version = "0.33.2", path = "crates/ecolor", default-features = false }
|
||||||
epaint = { version = "0.33.0", path = "crates/epaint", default-features = false }
|
epaint = { version = "0.33.2", path = "crates/epaint", default-features = false }
|
||||||
epaint_default_fonts = { version = "0.33.0", path = "crates/epaint_default_fonts" }
|
epaint_default_fonts = { version = "0.33.2", path = "crates/epaint_default_fonts" }
|
||||||
egui = { version = "0.33.0", path = "crates/egui", default-features = false }
|
egui = { version = "0.33.2", path = "crates/egui", default-features = false }
|
||||||
egui-winit = { version = "0.33.0", path = "crates/egui-winit", default-features = false }
|
egui-winit = { version = "0.33.2", path = "crates/egui-winit", default-features = false }
|
||||||
egui_extras = { version = "0.33.0", path = "crates/egui_extras", default-features = false }
|
egui_extras = { version = "0.33.2", path = "crates/egui_extras", default-features = false }
|
||||||
egui-wgpu = { version = "0.33.0", path = "crates/egui-wgpu", default-features = false }
|
egui-wgpu = { version = "0.33.2", path = "crates/egui-wgpu", default-features = false }
|
||||||
egui_demo_lib = { version = "0.33.0", path = "crates/egui_demo_lib", default-features = false }
|
egui_demo_lib = { version = "0.33.2", path = "crates/egui_demo_lib", default-features = false }
|
||||||
egui_glow = { version = "0.33.0", path = "crates/egui_glow", default-features = false }
|
egui_glow = { version = "0.33.2", path = "crates/egui_glow", default-features = false }
|
||||||
egui_kittest = { version = "0.33.1", path = "crates/egui_kittest", default-features = false }
|
egui_kittest = { version = "0.33.2", path = "crates/egui_kittest", default-features = false }
|
||||||
eframe = { version = "0.33.0", path = "crates/eframe", default-features = false }
|
eframe = { version = "0.33.2", path = "crates/eframe", default-features = false }
|
||||||
|
|
||||||
accesskit = "0.21.1"
|
accesskit = "0.21.1"
|
||||||
accesskit_consumer = "0.30.1"
|
accesskit_consumer = "0.30.1"
|
||||||
@@ -232,6 +232,7 @@ iter_on_single_items = "warn"
|
|||||||
iter_over_hash_type = "warn"
|
iter_over_hash_type = "warn"
|
||||||
iter_without_into_iter = "warn"
|
iter_without_into_iter = "warn"
|
||||||
large_digit_groups = "warn"
|
large_digit_groups = "warn"
|
||||||
|
large_futures = "warn"
|
||||||
large_include_file = "warn"
|
large_include_file = "warn"
|
||||||
large_stack_arrays = "warn"
|
large_stack_arrays = "warn"
|
||||||
large_stack_frames = "warn"
|
large_stack_frames = "warn"
|
||||||
@@ -329,6 +330,7 @@ unnecessary_semicolon = "warn"
|
|||||||
unnecessary_struct_initialization = "warn"
|
unnecessary_struct_initialization = "warn"
|
||||||
unnecessary_wraps = "warn"
|
unnecessary_wraps = "warn"
|
||||||
unnested_or_patterns = "warn"
|
unnested_or_patterns = "warn"
|
||||||
|
unused_async = "warn"
|
||||||
unused_peekable = "warn"
|
unused_peekable = "warn"
|
||||||
unused_rounding = "warn"
|
unused_rounding = "warn"
|
||||||
unused_self = "warn"
|
unused_self = "warn"
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ If you have questions, use [GitHub Discussions](https://github.com/emilk/egui/di
|
|||||||
|
|
||||||
To test the demo app locally, run `cargo run --release -p egui_demo_app`.
|
To test the demo app locally, run `cargo run --release -p egui_demo_app`.
|
||||||
|
|
||||||
The native backend is [`egui_glow`](https://github.com/emilk/egui/tree/main/crates/egui_glow) (using [`glow`](https://crates.io/crates/glow)) and should work out-of-the-box on Mac and Windows, but on Linux you need to first run:
|
The native backend is [`egui-wgpu`](https://github.com/emilk/egui/tree/main/crates/egui-wgpu) (using [`wgpu`](https://crates.io/crates/wgpu)) and should work out-of-the-box on Mac and Windows, but on Linux you need to first run:
|
||||||
|
|
||||||
`sudo apt-get install -y libclang-dev libgtk-3-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev`
|
`sudo apt-get install -y libclang-dev libgtk-3-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev`
|
||||||
|
|
||||||
|
|||||||
@@ -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.33.2 - 2025-11-13
|
||||||
|
Nothing new
|
||||||
|
|
||||||
|
|
||||||
## 0.33.0 - 2025-10-09
|
## 0.33.0 - 2025-10-09
|
||||||
* Align `Color32` to 4 bytes [#7318](https://github.com/emilk/egui/pull/7318) by [@anti-social](https://github.com/anti-social)
|
* Align `Color32` to 4 bytes [#7318](https://github.com/emilk/egui/pull/7318) by [@anti-social](https://github.com/anti-social)
|
||||||
* Make the `hex_color` macro `const` [#7444](https://github.com/emilk/egui/pull/7444) by [@YgorSouza](https://github.com/YgorSouza)
|
* Make the `hex_color` macro `const` [#7444](https://github.com/emilk/egui/pull/7444) by [@YgorSouza](https://github.com/YgorSouza)
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ 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.33.2 - 2025-11-13
|
||||||
|
* Fix jittering during window resize on MacOS for WGPU/Metal [#7641](https://github.com/emilk/egui/pull/7641) by [@aspcartman](https://github.com/aspcartman)
|
||||||
|
* Make sure `native_pixels_per_point` is set during app creation [#7683](https://github.com/emilk/egui/pull/7683) by [@emilk](https://github.com/emilk)
|
||||||
|
|
||||||
|
|
||||||
## 0.33.0 - 2025-10-09
|
## 0.33.0 - 2025-10-09
|
||||||
### ⭐ Added
|
### ⭐ Added
|
||||||
* Add an option to limit the repaint rate in the web runner [#7482](https://github.com/emilk/egui/pull/7482) by [@s-nie](https://github.com/s-nie)
|
* Add an option to limit the repaint rate in the web runner [#7482](https://github.com/emilk/egui/pull/7482) by [@s-nie](https://github.com/s-nie)
|
||||||
|
|||||||
@@ -28,19 +28,15 @@ workspace = true
|
|||||||
default = [
|
default = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"default_fonts",
|
"default_fonts",
|
||||||
"glow",
|
|
||||||
"wayland", # Required for Linux support (including CI!)
|
"wayland", # Required for Linux support (including CI!)
|
||||||
"web_screen_reader",
|
"web_screen_reader",
|
||||||
|
"wgpu",
|
||||||
"winit/default",
|
"winit/default",
|
||||||
"x11",
|
"x11",
|
||||||
"egui-wgpu?/fragile-send-sync-non-atomic-wasm",
|
|
||||||
# Let's enable some backends so that users can use `eframe` out-of-the-box
|
|
||||||
# without having to explicitly opt-in to backends
|
|
||||||
"egui-wgpu?/default",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
## Enable platform accessibility API implementations through [AccessKit](https://accesskit.dev/).
|
## Enable platform accessibility API implementations through [AccessKit](https://accesskit.dev/).
|
||||||
accesskit = ["egui/accesskit", "egui-winit/accesskit"]
|
accesskit = ["egui-winit/accesskit"]
|
||||||
|
|
||||||
# Allow crates to choose an android-activity backend via Winit
|
# Allow crates to choose an android-activity backend via Winit
|
||||||
# - It's important that most applications should not have to depend on android-activity directly, and can
|
# - It's important that most applications should not have to depend on android-activity directly, and can
|
||||||
@@ -56,7 +52,11 @@ android-native-activity = ["egui-winit/android-native-activity"]
|
|||||||
## If you plan on specifying your own fonts you may disable this feature.
|
## If you plan on specifying your own fonts you may disable this feature.
|
||||||
default_fonts = ["egui/default_fonts"]
|
default_fonts = ["egui/default_fonts"]
|
||||||
|
|
||||||
## Use [`glow`](https://github.com/grovesNL/glow) for painting, via [`egui_glow`](https://github.com/emilk/egui/tree/main/crates/egui_glow).
|
## Enable [`glow`](https://github.com/grovesNL/glow) for painting, via [`egui_glow`](https://github.com/emilk/egui/tree/main/crates/egui_glow).
|
||||||
|
##
|
||||||
|
## There is generally no need to enable both the `wgpu` and `glow` features,
|
||||||
|
## but if you do you can pick the renderer to use with [`NativeOptions::renderer`]
|
||||||
|
## and `WebOptions::renderer`.
|
||||||
glow = ["dep:egui_glow", "dep:glow", "dep:glutin-winit", "dep:glutin"]
|
glow = ["dep:egui_glow", "dep:glow", "dep:glutin-winit", "dep:glutin"]
|
||||||
|
|
||||||
## Enable saving app state to disk.
|
## Enable saving app state to disk.
|
||||||
@@ -78,20 +78,28 @@ wayland = [
|
|||||||
## For other platforms, use the `accesskit` feature instead.
|
## For other platforms, use the `accesskit` feature instead.
|
||||||
web_screen_reader = ["web-sys/SpeechSynthesis", "web-sys/SpeechSynthesisUtterance"]
|
web_screen_reader = ["web-sys/SpeechSynthesis", "web-sys/SpeechSynthesisUtterance"]
|
||||||
|
|
||||||
## Use [`wgpu`](https://docs.rs/wgpu) for painting (via [`egui-wgpu`](https://github.com/emilk/egui/tree/main/crates/egui-wgpu)).
|
## Enable [`wgpu`](https://docs.rs/wgpu) for painting (via [`egui-wgpu`](https://github.com/emilk/egui/tree/main/crates/egui-wgpu)).
|
||||||
##
|
##
|
||||||
## This overrides the `glow` feature.
|
## There is generally no need to enable both the `wgpu` and `glow` features,
|
||||||
|
## but if you do you can pick the renderer to use with [`NativeOptions::renderer`]
|
||||||
|
## and `WebOptions::renderer`.
|
||||||
##
|
##
|
||||||
## By default, only WebGPU is enabled on web.
|
## Switching from `wgpu (the default)` to `glow` can significantly reduce your binary size
|
||||||
## If you want to enable WebGL, you need to turn on the `webgl` feature of crate `wgpu`:
|
## (including the .wasm of a web app).
|
||||||
##
|
## See <https://github.com/emilk/egui/issues/5889> for more details.
|
||||||
## ```toml
|
|
||||||
## wgpu = { version = "*", features = ["webgpu", "webgl"] }
|
|
||||||
## ```
|
|
||||||
##
|
##
|
||||||
## By default, eframe will prefer WebGPU over WebGL, but
|
## 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 [`NativeOptions::wgpu_options`].
|
||||||
wgpu = ["dep:wgpu", "dep:egui-wgpu", "dep:pollster"]
|
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`.
|
||||||
|
##
|
||||||
|
## This means that no `wgpu` backends are enabled. You will need to enable them yourself, e.g. like this:
|
||||||
|
##
|
||||||
|
## ```toml
|
||||||
|
## wgpu = { version = "*", features = ["dx12", "metal", "webgl"] }
|
||||||
|
## ```
|
||||||
|
wgpu_no_default_features = ["dep:wgpu", "dep:egui-wgpu", "dep:pollster"]
|
||||||
|
|
||||||
## Enables compiling for x11.
|
## Enables compiling for x11.
|
||||||
x11 = [
|
x11 = [
|
||||||
@@ -137,6 +145,7 @@ winit = { workspace = true, default-features = false, features = ["rwh_06"] }
|
|||||||
# optional native:
|
# optional native:
|
||||||
egui-wgpu = { workspace = true, optional = true, features = [
|
egui-wgpu = { workspace = true, optional = true, features = [
|
||||||
"winit",
|
"winit",
|
||||||
|
"capture",
|
||||||
] } # if wgpu is used, use it with winit
|
] } # if wgpu is used, use it with winit
|
||||||
pollster = { workspace = true, optional = true } # needed for wgpu
|
pollster = { workspace = true, optional = true } # needed for wgpu
|
||||||
|
|
||||||
@@ -242,7 +251,7 @@ web-sys = { workspace = true, features = [
|
|||||||
] }
|
] }
|
||||||
|
|
||||||
# optional web:
|
# optional web:
|
||||||
egui-wgpu = { workspace = true, optional = true } # if wgpu is used, use it without (!) winit
|
egui-wgpu = { workspace = true, optional = true, features = ["capture"] } # if wgpu is used, use it without (!) winit
|
||||||
wgpu = { workspace = true, optional = true }
|
wgpu = { workspace = true, optional = true }
|
||||||
|
|
||||||
# Native dev dependencies for testing
|
# Native dev dependencies for testing
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ For how to use `egui`, see [the egui docs](https://docs.rs/egui).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
`eframe` uses [`egui_glow`](https://github.com/emilk/egui/tree/main/crates/egui_glow) for rendering, and on native it uses [`egui-winit`](https://github.com/emilk/egui/tree/main/crates/egui-winit).
|
`eframe` defaults to using [wgpu](https://crates.io/crates/wgpu) for rendering (with an option to change to [glow](https://crates.io/crates/glow)), and on native it uses [`egui-winit`](https://github.com/emilk/egui/tree/main/crates/egui-winit).
|
||||||
|
|
||||||
To use on Linux, first run:
|
To use on Linux, first run:
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ sudo apt-get install libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev lib
|
|||||||
|
|
||||||
You need to either use `edition = "2024"`, or set `resolver = "2"` in the `[workspace]` section of your to-level `Cargo.toml`. See [this link](https://doc.rust-lang.org/edition-guide/rust-2021/default-cargo-resolver.html) for more info.
|
You need to either use `edition = "2024"`, or set `resolver = "2"` in the `[workspace]` section of your to-level `Cargo.toml`. See [this link](https://doc.rust-lang.org/edition-guide/rust-2021/default-cargo-resolver.html) for more info.
|
||||||
|
|
||||||
You can opt-in to the using [`egui-wgpu`](https://github.com/emilk/egui/tree/main/crates/egui-wgpu) for rendering by enabling the `wgpu` feature and setting `NativeOptions::renderer` to `Renderer::Wgpu`.
|
You can opt-in to the using [`egui_glow`](https://github.com/emilk/egui/tree/main/crates/egui_glow) for rendering by enabling the `glow` feature and setting `NativeOptions::renderer` to `Renderer::Glow`.
|
||||||
|
|
||||||
## Alternatives
|
## Alternatives
|
||||||
`eframe` is not the only way to write an app using `egui`! You can also try [`egui-miniquad`](https://github.com/not-fl3/egui-miniquad), [`bevy_egui`](https://github.com/mvlabat/bevy_egui), [`egui_sdl2_gl`](https://github.com/ArjunNair/egui_sdl2_gl), and others.
|
`eframe` is not the only way to write an app using `egui`! You can also try [`egui-miniquad`](https://github.com/not-fl3/egui-miniquad), [`bevy_egui`](https://github.com/mvlabat/bevy_egui), [`egui_sdl2_gl`](https://github.com/ArjunNair/egui_sdl2_gl), and others.
|
||||||
@@ -35,7 +35,7 @@ You can also use `egui_glow` and [`winit`](https://github.com/rust-windowing/win
|
|||||||
|
|
||||||
|
|
||||||
## Limitations when running egui on the web
|
## Limitations when running egui on the web
|
||||||
`eframe` uses WebGL (via [`glow`](https://crates.io/crates/glow)) and Wasm, and almost nothing else from the web tech stack. This has some benefits, but also produces some challenges and serious downsides.
|
`eframe` and egui compiles to Wasm using either WebGPU (when available) or WebGL2 for rendering, and almost nothing else from the web tech stack. This has some benefits, but also produces some challenges and serious downsides.
|
||||||
|
|
||||||
* Rendering: Getting pixel-perfect rendering right on the web is very difficult.
|
* Rendering: Getting pixel-perfect rendering right on the web is very difficult.
|
||||||
* Search: you cannot search an egui web page like you would a normal web page.
|
* Search: you cannot search an egui web page like you would a normal web page.
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
pub use crate::native::winit_integration::UserEvent;
|
pub use crate::native::winit_integration::UserEvent;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
@@ -22,7 +22,7 @@ use raw_window_handle::{
|
|||||||
use static_assertions::assert_not_impl_any;
|
use static_assertions::assert_not_impl_any;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
pub use winit::{event_loop::EventLoopBuilder, window::WindowAttributes};
|
pub use winit::{event_loop::EventLoopBuilder, window::WindowAttributes};
|
||||||
|
|
||||||
/// Hook into the building of an event loop before it is run
|
/// Hook into the building of an event loop before it is run
|
||||||
@@ -30,7 +30,7 @@ pub use winit::{event_loop::EventLoopBuilder, window::WindowAttributes};
|
|||||||
/// You can configure any platform specific details required on top of the default configuration
|
/// You can configure any platform specific details required on top of the default configuration
|
||||||
/// done by `EFrame`.
|
/// done by `EFrame`.
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
pub type EventLoopBuilderHook = Box<dyn FnOnce(&mut EventLoopBuilder<UserEvent>)>;
|
pub type EventLoopBuilderHook = Box<dyn FnOnce(&mut EventLoopBuilder<UserEvent>)>;
|
||||||
|
|
||||||
/// Hook into the building of a the native window.
|
/// Hook into the building of a the native window.
|
||||||
@@ -38,7 +38,7 @@ pub type EventLoopBuilderHook = Box<dyn FnOnce(&mut EventLoopBuilder<UserEvent>)
|
|||||||
/// You can configure any platform specific details required on top of the default configuration
|
/// You can configure any platform specific details required on top of the default configuration
|
||||||
/// done by `eframe`.
|
/// done by `eframe`.
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
pub type WindowBuilderHook = Box<dyn FnOnce(egui::ViewportBuilder) -> egui::ViewportBuilder>;
|
pub type WindowBuilderHook = Box<dyn FnOnce(egui::ViewportBuilder) -> egui::ViewportBuilder>;
|
||||||
|
|
||||||
type DynError = Box<dyn std::error::Error + Send + Sync>;
|
type DynError = Box<dyn std::error::Error + Send + Sync>;
|
||||||
@@ -79,7 +79,7 @@ pub struct CreationContext<'s> {
|
|||||||
/// Only available when compiling with the `wgpu` feature and using [`Renderer::Wgpu`].
|
/// Only available when compiling with the `wgpu` feature and using [`Renderer::Wgpu`].
|
||||||
///
|
///
|
||||||
/// Can be used to manage GPU resources for custom rendering with WGPU using [`egui::PaintCallback`]s.
|
/// Can be used to manage GPU resources for custom rendering with WGPU using [`egui::PaintCallback`]s.
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
pub wgpu_render_state: Option<egui_wgpu::RenderState>,
|
pub wgpu_render_state: Option<egui_wgpu::RenderState>,
|
||||||
|
|
||||||
/// Raw platform window handle
|
/// Raw platform window handle
|
||||||
@@ -121,7 +121,7 @@ impl CreationContext<'_> {
|
|||||||
gl: None,
|
gl: None,
|
||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
get_proc_address: None,
|
get_proc_address: None,
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
wgpu_render_state: None,
|
wgpu_render_state: None,
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
raw_window_handle: Err(HandleError::NotSupported),
|
raw_window_handle: Err(HandleError::NotSupported),
|
||||||
@@ -137,7 +137,7 @@ impl CreationContext<'_> {
|
|||||||
pub trait App {
|
pub trait App {
|
||||||
/// Called each time the UI needs repainting, which may be many times per second.
|
/// Called each time the UI needs repainting, which may be many times per second.
|
||||||
///
|
///
|
||||||
/// Put your widgets into a [`egui::SidePanel`], [`egui::TopBottomPanel`], [`egui::CentralPanel`], [`egui::Window`] or [`egui::Area`].
|
/// Put your widgets into a [`egui::Panel`], [`egui::CentralPanel`], [`egui::Window`] or [`egui::Area`].
|
||||||
///
|
///
|
||||||
/// The [`egui::Context`] can be cloned and saved if you like.
|
/// The [`egui::Context`] can be cloned and saved if you like.
|
||||||
///
|
///
|
||||||
@@ -317,7 +317,7 @@ pub struct NativeOptions {
|
|||||||
pub hardware_acceleration: HardwareAcceleration,
|
pub hardware_acceleration: HardwareAcceleration,
|
||||||
|
|
||||||
/// What rendering backend to use.
|
/// What rendering backend to use.
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
pub renderer: Renderer,
|
pub renderer: Renderer,
|
||||||
|
|
||||||
/// This controls what happens when you close the main eframe window.
|
/// This controls what happens when you close the main eframe window.
|
||||||
@@ -340,7 +340,7 @@ pub struct NativeOptions {
|
|||||||
/// event loop before it is run.
|
/// event loop before it is run.
|
||||||
///
|
///
|
||||||
/// Note: A [`NativeOptions`] clone will not include any `event_loop_builder` hook.
|
/// Note: A [`NativeOptions`] clone will not include any `event_loop_builder` hook.
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
pub event_loop_builder: Option<EventLoopBuilderHook>,
|
pub event_loop_builder: Option<EventLoopBuilderHook>,
|
||||||
|
|
||||||
/// Hook into the building of a window.
|
/// Hook into the building of a window.
|
||||||
@@ -349,7 +349,7 @@ pub struct NativeOptions {
|
|||||||
/// window appearance.
|
/// window appearance.
|
||||||
///
|
///
|
||||||
/// Note: A [`NativeOptions`] clone will not include any `window_builder` hook.
|
/// Note: A [`NativeOptions`] clone will not include any `window_builder` hook.
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
pub window_builder: Option<WindowBuilderHook>,
|
pub window_builder: Option<WindowBuilderHook>,
|
||||||
|
|
||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
@@ -367,7 +367,7 @@ pub struct NativeOptions {
|
|||||||
pub centered: bool,
|
pub centered: bool,
|
||||||
|
|
||||||
/// Configures wgpu instance/device/adapter/surface creation and renderloop.
|
/// Configures wgpu instance/device/adapter/surface creation and renderloop.
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
pub wgpu_options: egui_wgpu::WgpuConfiguration,
|
pub wgpu_options: egui_wgpu::WgpuConfiguration,
|
||||||
|
|
||||||
/// Controls whether or not the native window position and size will be
|
/// Controls whether or not the native window position and size will be
|
||||||
@@ -404,13 +404,13 @@ impl Clone for NativeOptions {
|
|||||||
Self {
|
Self {
|
||||||
viewport: self.viewport.clone(),
|
viewport: self.viewport.clone(),
|
||||||
|
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
event_loop_builder: None, // Skip any builder callbacks if cloning
|
event_loop_builder: None, // Skip any builder callbacks if cloning
|
||||||
|
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
window_builder: None, // Skip any builder callbacks if cloning
|
window_builder: None, // Skip any builder callbacks if cloning
|
||||||
|
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
wgpu_options: self.wgpu_options.clone(),
|
wgpu_options: self.wgpu_options.clone(),
|
||||||
|
|
||||||
persistence_path: self.persistence_path.clone(),
|
persistence_path: self.persistence_path.clone(),
|
||||||
@@ -435,15 +435,15 @@ impl Default for NativeOptions {
|
|||||||
stencil_buffer: 0,
|
stencil_buffer: 0,
|
||||||
hardware_acceleration: HardwareAcceleration::Preferred,
|
hardware_acceleration: HardwareAcceleration::Preferred,
|
||||||
|
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
renderer: Renderer::default(),
|
renderer: Renderer::default(),
|
||||||
|
|
||||||
run_and_return: true,
|
run_and_return: true,
|
||||||
|
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
event_loop_builder: None,
|
event_loop_builder: None,
|
||||||
|
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
window_builder: None,
|
window_builder: None,
|
||||||
|
|
||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
@@ -451,7 +451,7 @@ impl Default for NativeOptions {
|
|||||||
|
|
||||||
centered: false,
|
centered: false,
|
||||||
|
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
wgpu_options: egui_wgpu::WgpuConfiguration::default(),
|
wgpu_options: egui_wgpu::WgpuConfiguration::default(),
|
||||||
|
|
||||||
persist_window: true,
|
persist_window: true,
|
||||||
@@ -471,6 +471,10 @@ impl Default for NativeOptions {
|
|||||||
/// Options when using `eframe` in a web page.
|
/// Options when using `eframe` in a web page.
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
pub struct WebOptions {
|
pub struct WebOptions {
|
||||||
|
/// What rendering backend to use.
|
||||||
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
|
pub renderer: Renderer,
|
||||||
|
|
||||||
/// Sets the number of bits in the depth buffer.
|
/// Sets the number of bits in the depth buffer.
|
||||||
///
|
///
|
||||||
/// `egui` doesn't need the depth buffer, so the default value is 0.
|
/// `egui` doesn't need the depth buffer, so the default value is 0.
|
||||||
@@ -484,7 +488,7 @@ pub struct WebOptions {
|
|||||||
pub webgl_context_option: WebGlContextOption,
|
pub webgl_context_option: WebGlContextOption,
|
||||||
|
|
||||||
/// Configures wgpu instance/device/adapter/surface creation and renderloop.
|
/// Configures wgpu instance/device/adapter/surface creation and renderloop.
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
pub wgpu_options: egui_wgpu::WgpuConfiguration,
|
pub wgpu_options: egui_wgpu::WgpuConfiguration,
|
||||||
|
|
||||||
/// Controls whether to apply dithering to minimize banding artifacts.
|
/// Controls whether to apply dithering to minimize banding artifacts.
|
||||||
@@ -519,12 +523,15 @@ pub struct WebOptions {
|
|||||||
impl Default for WebOptions {
|
impl Default for WebOptions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
|
renderer: Renderer::default(),
|
||||||
|
|
||||||
depth_buffer: 0,
|
depth_buffer: 0,
|
||||||
|
|
||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
webgl_context_option: WebGlContextOption::BestFirst,
|
webgl_context_option: WebGlContextOption::BestFirst,
|
||||||
|
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
wgpu_options: egui_wgpu::WgpuConfiguration::default(),
|
wgpu_options: egui_wgpu::WgpuConfiguration::default(),
|
||||||
|
|
||||||
dithering: true,
|
dithering: true,
|
||||||
@@ -561,7 +568,7 @@ pub enum WebGlContextOption {
|
|||||||
/// What rendering backend to use.
|
/// What rendering backend to use.
|
||||||
///
|
///
|
||||||
/// You need to enable the "glow" and "wgpu" features to have a choice.
|
/// You need to enable the "glow" and "wgpu" features to have a choice.
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
|
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
|
||||||
@@ -571,49 +578,49 @@ pub enum Renderer {
|
|||||||
Glow,
|
Glow,
|
||||||
|
|
||||||
/// Use [`egui_wgpu`] renderer for [`wgpu`](https://github.com/gfx-rs/wgpu).
|
/// Use [`egui_wgpu`] renderer for [`wgpu`](https://github.com/gfx-rs/wgpu).
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
Wgpu,
|
Wgpu,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
impl Default for Renderer {
|
impl Default for Renderer {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
#[cfg(not(feature = "glow"))]
|
#[cfg(not(feature = "glow"))]
|
||||||
#[cfg(not(feature = "wgpu"))]
|
#[cfg(not(feature = "wgpu_no_default_features"))]
|
||||||
compile_error!(
|
compile_error!(
|
||||||
"eframe: you must enable at least one of the rendering backend features: 'glow' or 'wgpu'"
|
"eframe: you must enable at least one of the rendering backend features: 'glow' or 'wgpu'"
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
#[cfg(not(feature = "wgpu"))]
|
#[cfg(not(feature = "wgpu_no_default_features"))]
|
||||||
return Self::Glow;
|
return Self::Glow;
|
||||||
|
|
||||||
#[cfg(not(feature = "glow"))]
|
#[cfg(not(feature = "glow"))]
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
return Self::Wgpu;
|
return Self::Wgpu;
|
||||||
|
|
||||||
// By default, only the `glow` feature is enabled, so if the user added `wgpu` to the feature list
|
// It's weird that the user has enabled both glow and wgpu,
|
||||||
// they probably wanted to use wgpu:
|
// but let's pick the better of the two (wgpu):
|
||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
return Self::Wgpu;
|
return Self::Wgpu;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
impl std::fmt::Display for Renderer {
|
impl std::fmt::Display for Renderer {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
Self::Glow => "glow".fmt(f),
|
Self::Glow => "glow".fmt(f),
|
||||||
|
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
Self::Wgpu => "wgpu".fmt(f),
|
Self::Wgpu => "wgpu".fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
impl std::str::FromStr for Renderer {
|
impl std::str::FromStr for Renderer {
|
||||||
type Err = String;
|
type Err = String;
|
||||||
|
|
||||||
@@ -622,7 +629,7 @@ impl std::str::FromStr for Renderer {
|
|||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
"glow" => Ok(Self::Glow),
|
"glow" => Ok(Self::Glow),
|
||||||
|
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
"wgpu" => Ok(Self::Wgpu),
|
"wgpu" => Ok(Self::Wgpu),
|
||||||
|
|
||||||
_ => Err(format!(
|
_ => Err(format!(
|
||||||
@@ -655,7 +662,7 @@ pub struct Frame {
|
|||||||
Option<Box<dyn FnMut(glow::Texture) -> egui::TextureId>>,
|
Option<Box<dyn FnMut(glow::Texture) -> egui::TextureId>>,
|
||||||
|
|
||||||
/// Can be used to manage GPU resources for custom rendering with WGPU using [`egui::PaintCallback`]s.
|
/// Can be used to manage GPU resources for custom rendering with WGPU using [`egui::PaintCallback`]s.
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub wgpu_render_state: Option<egui_wgpu::RenderState>,
|
pub wgpu_render_state: Option<egui_wgpu::RenderState>,
|
||||||
|
|
||||||
@@ -705,7 +712,7 @@ impl Frame {
|
|||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
raw_window_handle: Err(HandleError::NotSupported),
|
raw_window_handle: Err(HandleError::NotSupported),
|
||||||
storage: None,
|
storage: None,
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
wgpu_render_state: None,
|
wgpu_render_state: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -764,7 +771,7 @@ impl Frame {
|
|||||||
/// Only available when compiling with the `wgpu` feature and using [`Renderer::Wgpu`].
|
/// Only available when compiling with the `wgpu` feature and using [`Renderer::Wgpu`].
|
||||||
///
|
///
|
||||||
/// Can be used to manage GPU resources for custom rendering with WGPU using [`egui::PaintCallback`]s.
|
/// Can be used to manage GPU resources for custom rendering with WGPU using [`egui::PaintCallback`]s.
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
pub fn wgpu_render_state(&self) -> Option<&egui_wgpu::RenderState> {
|
pub fn wgpu_render_state(&self) -> Option<&egui_wgpu::RenderState> {
|
||||||
self.wgpu_render_state.as_ref()
|
self.wgpu_render_state.as_ref()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ pub use {egui, egui::emath, egui::epaint};
|
|||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
pub use {egui_glow, glow};
|
pub use {egui_glow, glow};
|
||||||
|
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
pub use {egui_wgpu, wgpu};
|
pub use {egui_wgpu, wgpu};
|
||||||
|
|
||||||
mod epi;
|
mod epi;
|
||||||
@@ -188,19 +188,19 @@ pub use web::{WebLogger, WebRunner};
|
|||||||
// When compiling natively
|
// When compiling natively
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
mod native;
|
mod native;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
pub use native::run::EframeWinitApplication;
|
pub use native::run::EframeWinitApplication;
|
||||||
|
|
||||||
#[cfg(not(any(target_arch = "wasm32", target_os = "ios")))]
|
#[cfg(not(any(target_arch = "wasm32", target_os = "ios")))]
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
pub use native::run::EframePumpStatus;
|
pub use native::run::EframePumpStatus;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
#[cfg(feature = "persistence")]
|
#[cfg(feature = "persistence")]
|
||||||
pub use native::file_storage::storage_dir;
|
pub use native::file_storage::storage_dir;
|
||||||
|
|
||||||
@@ -252,7 +252,7 @@ pub mod icon_data;
|
|||||||
/// # Errors
|
/// # Errors
|
||||||
/// This function can fail if we fail to set up a graphics context.
|
/// This function can fail if we fail to set up a graphics context.
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
#[allow(clippy::needless_pass_by_value, clippy::allow_attributes)]
|
#[allow(clippy::needless_pass_by_value, clippy::allow_attributes)]
|
||||||
pub fn run_native(
|
pub fn run_native(
|
||||||
app_name: &str,
|
app_name: &str,
|
||||||
@@ -268,7 +268,7 @@ pub fn run_native(
|
|||||||
native::run::run_glow(app_name, native_options, app_creator)
|
native::run::run_glow(app_name, native_options, app_creator)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
Renderer::Wgpu => {
|
Renderer::Wgpu => {
|
||||||
log::debug!("Using the wgpu renderer");
|
log::debug!("Using the wgpu renderer");
|
||||||
native::run::run_wgpu(app_name, native_options, app_creator)
|
native::run::run_wgpu(app_name, native_options, app_creator)
|
||||||
@@ -322,7 +322,7 @@ pub fn run_native(
|
|||||||
///
|
///
|
||||||
/// See the `external_eventloop` example for a more complete example.
|
/// See the `external_eventloop` example for a more complete example.
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
pub fn create_native<'a>(
|
pub fn create_native<'a>(
|
||||||
app_name: &str,
|
app_name: &str,
|
||||||
mut native_options: NativeOptions,
|
mut native_options: NativeOptions,
|
||||||
@@ -343,7 +343,7 @@ pub fn create_native<'a>(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
Renderer::Wgpu => {
|
Renderer::Wgpu => {
|
||||||
log::debug!("Using the wgpu renderer");
|
log::debug!("Using the wgpu renderer");
|
||||||
EframeWinitApplication::new(native::run::create_wgpu(
|
EframeWinitApplication::new(native::run::create_wgpu(
|
||||||
@@ -357,7 +357,7 @@ pub fn create_native<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
fn init_native(app_name: &str, native_options: &mut NativeOptions) -> Renderer {
|
fn init_native(app_name: &str, native_options: &mut NativeOptions) -> Renderer {
|
||||||
#[cfg(not(feature = "__screenshot"))]
|
#[cfg(not(feature = "__screenshot"))]
|
||||||
assert!(
|
assert!(
|
||||||
@@ -371,7 +371,7 @@ fn init_native(app_name: &str, native_options: &mut NativeOptions) -> Renderer {
|
|||||||
|
|
||||||
let renderer = native_options.renderer;
|
let renderer = native_options.renderer;
|
||||||
|
|
||||||
#[cfg(all(feature = "glow", feature = "wgpu"))]
|
#[cfg(all(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
{
|
{
|
||||||
match native_options.renderer {
|
match native_options.renderer {
|
||||||
Renderer::Glow => "glow",
|
Renderer::Glow => "glow",
|
||||||
@@ -420,7 +420,7 @@ fn init_native(app_name: &str, native_options: &mut NativeOptions) -> Renderer {
|
|||||||
/// # Errors
|
/// # Errors
|
||||||
/// This function can fail if we fail to set up a graphics context.
|
/// This function can fail if we fail to set up a graphics context.
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[cfg(any(feature = "glow", feature = "wgpu"))]
|
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
|
||||||
pub fn run_simple_native(
|
pub fn run_simple_native(
|
||||||
app_name: &str,
|
app_name: &str,
|
||||||
native_options: NativeOptions,
|
native_options: NativeOptions,
|
||||||
@@ -472,7 +472,7 @@ pub enum Error {
|
|||||||
OpenGL(egui_glow::PainterError),
|
OpenGL(egui_glow::PainterError),
|
||||||
|
|
||||||
/// An error from [`wgpu`].
|
/// An error from [`wgpu`].
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
Wgpu(egui_wgpu::WgpuError),
|
Wgpu(egui_wgpu::WgpuError),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,7 +510,7 @@ impl From<egui_glow::PainterError> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
impl From<egui_wgpu::WgpuError> for Error {
|
impl From<egui_wgpu::WgpuError> for Error {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(err: egui_wgpu::WgpuError) -> Self {
|
fn from(err: egui_wgpu::WgpuError) -> Self {
|
||||||
@@ -551,7 +551,7 @@ impl std::fmt::Display for Error {
|
|||||||
write!(f, "egui_glow: {err}")
|
write!(f, "egui_glow: {err}")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
Self::Wgpu(err) => {
|
Self::Wgpu(err) => {
|
||||||
write!(f, "WGPU error: {err}")
|
write!(f, "WGPU error: {err}")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,7 +179,9 @@ impl EpiIntegration {
|
|||||||
#[cfg(feature = "glow")] glow_register_native_texture: Option<
|
#[cfg(feature = "glow")] glow_register_native_texture: Option<
|
||||||
Box<dyn FnMut(glow::Texture) -> egui::TextureId>,
|
Box<dyn FnMut(glow::Texture) -> egui::TextureId>,
|
||||||
>,
|
>,
|
||||||
#[cfg(feature = "wgpu")] wgpu_render_state: Option<egui_wgpu::RenderState>,
|
#[cfg(feature = "wgpu_no_default_features")] wgpu_render_state: Option<
|
||||||
|
egui_wgpu::RenderState,
|
||||||
|
>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let frame = epi::Frame {
|
let frame = epi::Frame {
|
||||||
info: epi::IntegrationInfo { cpu_usage: None },
|
info: epi::IntegrationInfo { cpu_usage: None },
|
||||||
@@ -188,7 +190,7 @@ impl EpiIntegration {
|
|||||||
gl,
|
gl,
|
||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
glow_register_native_texture,
|
glow_register_native_texture,
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
wgpu_render_state,
|
wgpu_render_state,
|
||||||
raw_display_handle: window.display_handle().map(|h| h.as_raw()),
|
raw_display_handle: window.display_handle().map(|h| h.as_raw()),
|
||||||
raw_window_handle: window.window_handle().map(|h| h.as_raw()),
|
raw_window_handle: window.window_handle().map(|h| h.as_raw()),
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ impl<'app> GlowWinitApp<'app> {
|
|||||||
let painter = painter.clone();
|
let painter = painter.clone();
|
||||||
move |native| painter.borrow_mut().register_native_texture(native)
|
move |native| painter.borrow_mut().register_native_texture(native)
|
||||||
})),
|
})),
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -301,7 +301,7 @@ impl<'app> GlowWinitApp<'app> {
|
|||||||
storage: integration.frame.storage(),
|
storage: integration.frame.storage(),
|
||||||
gl: Some(gl),
|
gl: Some(gl),
|
||||||
get_proc_address: Some(&get_proc_address),
|
get_proc_address: Some(&get_proc_address),
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
wgpu_render_state: None,
|
wgpu_render_state: None,
|
||||||
raw_display_handle: window.display_handle().map(|h| h.as_raw()),
|
raw_display_handle: window.display_handle().map(|h| h.as_raw()),
|
||||||
raw_window_handle: window.window_handle().map(|h| h.as_raw()),
|
raw_window_handle: window.window_handle().map(|h| h.as_raw()),
|
||||||
|
|||||||
@@ -12,5 +12,5 @@ pub(crate) mod winit_integration;
|
|||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
mod glow_integration;
|
mod glow_integration;
|
||||||
|
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
mod wgpu_integration;
|
mod wgpu_integration;
|
||||||
|
|||||||
@@ -381,7 +381,7 @@ pub fn create_glow<'a>(
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
pub fn run_wgpu(
|
pub fn run_wgpu(
|
||||||
app_name: &str,
|
app_name: &str,
|
||||||
mut native_options: epi::NativeOptions,
|
mut native_options: epi::NativeOptions,
|
||||||
@@ -404,7 +404,7 @@ pub fn run_wgpu(
|
|||||||
run_and_exit(event_loop, wgpu_eframe)
|
run_and_exit(event_loop, wgpu_eframe)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
pub fn create_wgpu<'a>(
|
pub fn create_wgpu<'a>(
|
||||||
app_name: &str,
|
app_name: &str,
|
||||||
native_options: epi::NativeOptions,
|
native_options: epi::NativeOptions,
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
use egui::{TexturesDelta, UserData, ViewportCommand};
|
use egui::{TexturesDelta, UserData, ViewportCommand};
|
||||||
|
|
||||||
use crate::{App, epi};
|
use crate::{App, epi, web::web_painter::WebPainter};
|
||||||
|
|
||||||
use super::{NeedRepaint, now_sec, text_agent::TextAgent, web_painter::WebPainter as _};
|
use super::{NeedRepaint, now_sec, text_agent::TextAgent};
|
||||||
|
|
||||||
pub struct AppRunner {
|
pub struct AppRunner {
|
||||||
#[allow(dead_code, clippy::allow_attributes)]
|
#[allow(dead_code, clippy::allow_attributes)]
|
||||||
pub(crate) web_options: crate::WebOptions,
|
pub(crate) web_options: crate::WebOptions,
|
||||||
pub(crate) frame: epi::Frame,
|
pub(crate) frame: epi::Frame,
|
||||||
egui_ctx: egui::Context,
|
egui_ctx: egui::Context,
|
||||||
painter: super::ActiveWebPainter,
|
painter: Box<dyn WebPainter>,
|
||||||
pub(crate) input: super::WebInput,
|
pub(crate) input: super::WebInput,
|
||||||
app: Box<dyn epi::App>,
|
app: Box<dyn epi::App>,
|
||||||
pub(crate) needs_repaint: std::sync::Arc<NeedRepaint>,
|
pub(crate) needs_repaint: std::sync::Arc<NeedRepaint>,
|
||||||
@@ -34,6 +34,10 @@ impl Drop for AppRunner {
|
|||||||
impl AppRunner {
|
impl AppRunner {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// Failure to initialize WebGL renderer, or failure to create app.
|
/// Failure to initialize WebGL renderer, or failure to create app.
|
||||||
|
#[cfg_attr(
|
||||||
|
not(feature = "wgpu_no_default_features"),
|
||||||
|
expect(clippy::unused_async)
|
||||||
|
)]
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
canvas: web_sys::HtmlCanvasElement,
|
canvas: web_sys::HtmlCanvasElement,
|
||||||
web_options: crate::WebOptions,
|
web_options: crate::WebOptions,
|
||||||
@@ -41,7 +45,41 @@ impl AppRunner {
|
|||||||
text_agent: TextAgent,
|
text_agent: TextAgent,
|
||||||
) -> Result<Self, String> {
|
) -> Result<Self, String> {
|
||||||
let egui_ctx = egui::Context::default();
|
let egui_ctx = egui::Context::default();
|
||||||
let painter = super::ActiveWebPainter::new(egui_ctx.clone(), canvas, &web_options).await?;
|
|
||||||
|
#[allow(clippy::allow_attributes, unused_assignments)]
|
||||||
|
#[cfg(feature = "glow")]
|
||||||
|
let mut gl = None;
|
||||||
|
|
||||||
|
#[allow(clippy::allow_attributes, unused_assignments)]
|
||||||
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
|
let mut wgpu_render_state = None;
|
||||||
|
|
||||||
|
let painter = match web_options.renderer {
|
||||||
|
#[cfg(feature = "glow")]
|
||||||
|
epi::Renderer::Glow => {
|
||||||
|
log::debug!("Using the glow renderer");
|
||||||
|
let painter = super::web_painter_glow::WebPainterGlow::new(
|
||||||
|
egui_ctx.clone(),
|
||||||
|
canvas,
|
||||||
|
&web_options,
|
||||||
|
)?;
|
||||||
|
gl = Some(painter.gl().clone());
|
||||||
|
Box::new(painter) as Box<dyn WebPainter>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
|
epi::Renderer::Wgpu => {
|
||||||
|
log::debug!("Using the wgpu renderer");
|
||||||
|
let painter = super::web_painter_wgpu::WebPainterWgpu::new(
|
||||||
|
egui_ctx.clone(),
|
||||||
|
canvas,
|
||||||
|
&web_options,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
wgpu_render_state = painter.render_state();
|
||||||
|
Box::new(painter) as Box<dyn WebPainter>
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let info = epi::IntegrationInfo {
|
let info = epi::IntegrationInfo {
|
||||||
web_info: epi::WebInfo {
|
web_info: epi::WebInfo {
|
||||||
@@ -79,15 +117,13 @@ impl AppRunner {
|
|||||||
storage: Some(&storage),
|
storage: Some(&storage),
|
||||||
|
|
||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
gl: Some(painter.gl().clone()),
|
gl: gl.clone(),
|
||||||
|
|
||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
get_proc_address: None,
|
get_proc_address: None,
|
||||||
|
|
||||||
#[cfg(all(feature = "wgpu", not(feature = "glow")))]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
wgpu_render_state: painter.render_state(),
|
wgpu_render_state: wgpu_render_state.clone(),
|
||||||
#[cfg(all(feature = "wgpu", feature = "glow"))]
|
|
||||||
wgpu_render_state: None,
|
|
||||||
};
|
};
|
||||||
let app = app_creator(&cc).map_err(|err| err.to_string())?;
|
let app = app_creator(&cc).map_err(|err| err.to_string())?;
|
||||||
|
|
||||||
@@ -96,12 +132,10 @@ impl AppRunner {
|
|||||||
storage: Some(Box::new(storage)),
|
storage: Some(Box::new(storage)),
|
||||||
|
|
||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
gl: Some(painter.gl().clone()),
|
gl,
|
||||||
|
|
||||||
#[cfg(all(feature = "wgpu", not(feature = "glow")))]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
wgpu_render_state: painter.render_state(),
|
wgpu_render_state,
|
||||||
#[cfg(all(feature = "wgpu", feature = "glow"))]
|
|
||||||
wgpu_render_state: None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let needs_repaint: std::sync::Arc<NeedRepaint> =
|
let needs_repaint: std::sync::Arc<NeedRepaint> =
|
||||||
@@ -324,8 +358,7 @@ impl AppRunner {
|
|||||||
events: _, // already handled
|
events: _, // already handled
|
||||||
mutable_text_under_cursor: _, // TODO(#4569): https://github.com/emilk/egui/issues/4569
|
mutable_text_under_cursor: _, // TODO(#4569): https://github.com/emilk/egui/issues/4569
|
||||||
ime,
|
ime,
|
||||||
#[cfg(feature = "accesskit")]
|
accesskit_update: _, // not currently implemented
|
||||||
accesskit_update: _, // not currently implemented
|
|
||||||
num_completed_passes: _, // handled by `Context::run`
|
num_completed_passes: _, // handled by `Context::run`
|
||||||
request_discard_reasons: _, // handled by `Context::run`
|
request_discard_reasons: _, // handled by `Context::run`
|
||||||
} = platform_output;
|
} = platform_output;
|
||||||
|
|||||||
@@ -23,20 +23,16 @@ pub use panic_handler::{PanicHandler, PanicSummary};
|
|||||||
pub use web_logger::WebLogger;
|
pub use web_logger::WebLogger;
|
||||||
pub use web_runner::WebRunner;
|
pub use web_runner::WebRunner;
|
||||||
|
|
||||||
#[cfg(not(any(feature = "glow", feature = "wgpu")))]
|
#[cfg(not(any(feature = "glow", feature = "wgpu_no_default_features")))]
|
||||||
compile_error!("You must enable either the 'glow' or 'wgpu' feature");
|
compile_error!("You must enable either the 'glow' or 'wgpu' feature");
|
||||||
|
|
||||||
mod web_painter;
|
mod web_painter;
|
||||||
|
|
||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
mod web_painter_glow;
|
mod web_painter_glow;
|
||||||
#[cfg(feature = "glow")]
|
|
||||||
pub(crate) type ActiveWebPainter = web_painter_glow::WebPainterGlow;
|
|
||||||
|
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu_no_default_features")]
|
||||||
mod web_painter_wgpu;
|
mod web_painter_wgpu;
|
||||||
#[cfg(all(feature = "wgpu", not(feature = "glow")))]
|
|
||||||
pub(crate) type ActiveWebPainter = web_painter_wgpu::WebPainterWgpu;
|
|
||||||
|
|
||||||
pub use backend::*;
|
pub use backend::*;
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ impl WebPainterGlow {
|
|||||||
self.painter.gl()
|
self.painter.gl()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn new(
|
pub fn new(
|
||||||
_ctx: egui::Context,
|
_ctx: egui::Context,
|
||||||
canvas: HtmlCanvasElement,
|
canvas: HtmlCanvasElement,
|
||||||
options: &WebOptions,
|
options: &WebOptions,
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::web_painter::WebPainter;
|
|
||||||
use crate::WebOptions;
|
|
||||||
use egui::{Event, UserData, ViewportId};
|
use egui::{Event, UserData, ViewportId};
|
||||||
use egui_wgpu::capture::{CaptureReceiver, CaptureSender, CaptureState, capture_channel};
|
use egui_wgpu::{
|
||||||
use egui_wgpu::{RenderState, SurfaceErrorAction};
|
RenderState, SurfaceErrorAction,
|
||||||
|
capture::{CaptureReceiver, CaptureSender, CaptureState, capture_channel},
|
||||||
|
};
|
||||||
use wasm_bindgen::JsValue;
|
use wasm_bindgen::JsValue;
|
||||||
use web_sys::HtmlCanvasElement;
|
use web_sys::HtmlCanvasElement;
|
||||||
|
|
||||||
|
use super::web_painter::WebPainter;
|
||||||
|
|
||||||
pub(crate) struct WebPainterWgpu {
|
pub(crate) struct WebPainterWgpu {
|
||||||
canvas: HtmlCanvasElement,
|
canvas: HtmlCanvasElement,
|
||||||
surface: wgpu::Surface<'static>,
|
surface: wgpu::Surface<'static>,
|
||||||
@@ -23,7 +25,6 @@ pub(crate) struct WebPainterWgpu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WebPainterWgpu {
|
impl WebPainterWgpu {
|
||||||
#[expect(unused)] // only used if `wgpu` is the only active feature.
|
|
||||||
pub fn render_state(&self) -> Option<RenderState> {
|
pub fn render_state(&self) -> Option<RenderState> {
|
||||||
self.render_state.clone()
|
self.render_state.clone()
|
||||||
}
|
}
|
||||||
@@ -55,11 +56,10 @@ impl WebPainterWgpu {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(unused)] // only used if `wgpu` is the only active feature.
|
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
ctx: egui::Context,
|
ctx: egui::Context,
|
||||||
canvas: web_sys::HtmlCanvasElement,
|
canvas: web_sys::HtmlCanvasElement,
|
||||||
options: &WebOptions,
|
options: &crate::WebOptions,
|
||||||
) -> Result<Self, String> {
|
) -> Result<Self, String> {
|
||||||
log::debug!("Creating wgpu painter");
|
log::debug!("Creating wgpu painter");
|
||||||
|
|
||||||
|
|||||||
@@ -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.33.2 - 2025-11-13
|
||||||
|
* Fix jittering during window resize on MacOS for WGPU/Metal [#7641](https://github.com/emilk/egui/pull/7641) by [@aspcartman](https://github.com/aspcartman)
|
||||||
|
|
||||||
|
|
||||||
## 0.33.0 - 2025-10-09
|
## 0.33.0 - 2025-10-09
|
||||||
### 🔧 Changed
|
### 🔧 Changed
|
||||||
* Update wgpu to 26 and wasm-bindgen to 0.2.100 [#7540](https://github.com/emilk/egui/pull/7540) by [@Kumpelinus](https://github.com/Kumpelinus)
|
* Update wgpu to 26 and wasm-bindgen to 0.2.100 [#7540](https://github.com/emilk/egui/pull/7540) by [@Kumpelinus](https://github.com/Kumpelinus)
|
||||||
|
|||||||
@@ -27,8 +27,11 @@ rustdoc-args = ["--generate-link-to-definition"]
|
|||||||
[features]
|
[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"]
|
||||||
|
|
||||||
|
## Enables the `capture` module for capturing screenshots.
|
||||||
|
capture = ["dep:egui"]
|
||||||
|
|
||||||
## Enable [`winit`](https://docs.rs/winit) integration. On Linux, requires either `wayland` or `x11`
|
## Enable [`winit`](https://docs.rs/winit) integration. On Linux, requires either `wayland` or `x11`
|
||||||
winit = ["dep:winit", "winit/rwh_06"]
|
winit = ["dep:winit", "winit/rwh_06", "dep:egui", "capture"]
|
||||||
|
|
||||||
## Enables Wayland support for winit.
|
## Enables Wayland support for winit.
|
||||||
wayland = ["winit?/wayland"]
|
wayland = ["winit?/wayland"]
|
||||||
@@ -47,7 +50,6 @@ fragile-send-sync-non-atomic-wasm = ["wgpu/fragile-send-sync-non-atomic-wasm"]
|
|||||||
macos-window-resize-jitter-fix = ["wgpu/metal"]
|
macos-window-resize-jitter-fix = ["wgpu/metal"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
egui = { workspace = true, default-features = false }
|
|
||||||
epaint = { workspace = true, default-features = false, features = ["bytemuck"] }
|
epaint = { workspace = true, default-features = false, features = ["bytemuck"] }
|
||||||
|
|
||||||
ahash.workspace = true
|
ahash.workspace = true
|
||||||
@@ -62,4 +64,5 @@ wgpu = { workspace = true, features = ["wgsl"] }
|
|||||||
|
|
||||||
# Optional dependencies:
|
# Optional dependencies:
|
||||||
|
|
||||||
|
egui = { workspace = true, optional = true, default-features = false }
|
||||||
winit = { workspace = true, optional = true, default-features = false }
|
winit = { workspace = true, optional = true, default-features = false }
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ pub use renderer::*;
|
|||||||
pub use setup::{NativeAdapterSelectorMethod, WgpuSetup, WgpuSetupCreateNew, WgpuSetupExisting};
|
pub use setup::{NativeAdapterSelectorMethod, WgpuSetup, WgpuSetupCreateNew, WgpuSetupExisting};
|
||||||
|
|
||||||
/// Helpers for capturing screenshots of the UI.
|
/// Helpers for capturing screenshots of the UI.
|
||||||
|
#[cfg(feature = "capture")]
|
||||||
pub mod capture;
|
pub mod capture;
|
||||||
|
|
||||||
/// Module for painting [`egui`](https://github.com/emilk/egui) with [`wgpu`] on [`winit`].
|
/// Module for painting [`egui`](https://github.com/emilk/egui) with [`wgpu`] on [`winit`].
|
||||||
|
|||||||
@@ -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.33.2 - 2025-11-13
|
||||||
|
* Don't enable `arboard` on iOS [#7663](https://github.com/emilk/egui/pull/7663) by [@irh](https://github.com/irh)
|
||||||
|
|
||||||
|
|
||||||
## 0.33.0 - 2025-10-09
|
## 0.33.0 - 2025-10-09
|
||||||
### ⭐ Added
|
### ⭐ Added
|
||||||
* Add rotation gesture support for trackpad sources [#7453](https://github.com/emilk/egui/pull/7453) by [@thatcomputerguy0101](https://github.com/thatcomputerguy0101)
|
* Add rotation gesture support for trackpad sources [#7453](https://github.com/emilk/egui/pull/7453) by [@thatcomputerguy0101](https://github.com/thatcomputerguy0101)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ rustdoc-args = ["--generate-link-to-definition"]
|
|||||||
default = ["clipboard", "links", "wayland", "winit/default", "x11"]
|
default = ["clipboard", "links", "wayland", "winit/default", "x11"]
|
||||||
|
|
||||||
## Enable platform accessibility API implementations through [AccessKit](https://accesskit.dev/).
|
## Enable platform accessibility API implementations through [AccessKit](https://accesskit.dev/).
|
||||||
accesskit = ["dep:accesskit_winit", "egui/accesskit"]
|
accesskit = ["dep:accesskit_winit"]
|
||||||
|
|
||||||
# Allow crates to choose an android-activity backend via Winit
|
# Allow crates to choose an android-activity backend via Winit
|
||||||
# - It's important that most applications should not have to depend on android-activity directly, and can
|
# - It's important that most applications should not have to depend on android-activity directly, and can
|
||||||
|
|||||||
@@ -888,7 +888,6 @@ impl State {
|
|||||||
events: _, // handled elsewhere
|
events: _, // handled elsewhere
|
||||||
mutable_text_under_cursor: _, // only used in eframe web
|
mutable_text_under_cursor: _, // only used in eframe web
|
||||||
ime,
|
ime,
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
accesskit_update,
|
accesskit_update,
|
||||||
num_completed_passes: _, // `egui::Context::run` handles this
|
num_completed_passes: _, // `egui::Context::run` handles this
|
||||||
request_discard_reasons: _, // `egui::Context::run` handles this
|
request_discard_reasons: _, // `egui::Context::run` handles this
|
||||||
@@ -947,6 +946,9 @@ impl State {
|
|||||||
profiling::scope!("accesskit");
|
profiling::scope!("accesskit");
|
||||||
accesskit.update_if_active(|| update);
|
accesskit.update_if_active(|| update);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "accesskit"))]
|
||||||
|
let _ = accesskit_update;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_cursor_icon(&mut self, window: &Window, cursor_icon: egui::CursorIcon) {
|
fn set_cursor_icon(&mut self, window: &Window, cursor_icon: egui::CursorIcon) {
|
||||||
|
|||||||
@@ -26,10 +26,6 @@ rustdoc-args = ["--generate-link-to-definition"]
|
|||||||
[features]
|
[features]
|
||||||
default = ["default_fonts"]
|
default = ["default_fonts"]
|
||||||
|
|
||||||
## Exposes detailed accessibility implementation required by platform
|
|
||||||
## accessibility APIs. Also requires support in the egui integration.
|
|
||||||
accesskit = ["dep:accesskit"]
|
|
||||||
|
|
||||||
## [`bytemuck`](https://docs.rs/bytemuck) enables you to cast [`epaint::Vertex`], [`emath::Vec2`] etc to `&[u8]`.
|
## [`bytemuck`](https://docs.rs/bytemuck) enables you to cast [`epaint::Vertex`], [`emath::Vec2`] etc to `&[u8]`.
|
||||||
bytemuck = ["epaint/bytemuck"]
|
bytemuck = ["epaint/bytemuck"]
|
||||||
|
|
||||||
@@ -61,7 +57,7 @@ persistence = ["serde", "epaint/serde", "ron"]
|
|||||||
rayon = ["epaint/rayon"]
|
rayon = ["epaint/rayon"]
|
||||||
|
|
||||||
## Allow serialization using [`serde`](https://docs.rs/serde).
|
## Allow serialization using [`serde`](https://docs.rs/serde).
|
||||||
serde = ["dep:serde", "epaint/serde", "accesskit?/serde"]
|
serde = ["dep:serde", "epaint/serde", "accesskit/serde"]
|
||||||
|
|
||||||
## Change Vertex layout to be compatible with unity
|
## Change Vertex layout to be compatible with unity
|
||||||
unity = ["epaint/unity"]
|
unity = ["epaint/unity"]
|
||||||
@@ -75,6 +71,7 @@ _override_unity = ["epaint/_override_unity"]
|
|||||||
emath = { workspace = true, default-features = false }
|
emath = { workspace = true, default-features = false }
|
||||||
epaint = { workspace = true, default-features = false }
|
epaint = { workspace = true, default-features = false }
|
||||||
|
|
||||||
|
accesskit.workspace = true
|
||||||
ahash.workspace = true
|
ahash.workspace = true
|
||||||
bitflags.workspace = true
|
bitflags.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
@@ -84,7 +81,6 @@ smallvec.workspace = true
|
|||||||
unicode-segmentation.workspace = true
|
unicode-segmentation.workspace = true
|
||||||
|
|
||||||
#! ### Optional dependencies
|
#! ### Optional dependencies
|
||||||
accesskit = { workspace = true, optional = true }
|
|
||||||
|
|
||||||
backtrace = { workspace = true, optional = true }
|
backtrace = { workspace = true, optional = true }
|
||||||
|
|
||||||
|
|||||||
@@ -525,11 +525,21 @@ impl Area {
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Used to prevent drift
|
||||||
|
let pivot_at_start_of_drag_id = id.with("pivot_at_drag_start");
|
||||||
|
|
||||||
if movable
|
if movable
|
||||||
&& move_response.dragged()
|
&& move_response.dragged()
|
||||||
&& let Some(pivot_pos) = &mut state.pivot_pos
|
&& let Some(pivot_pos) = &mut state.pivot_pos
|
||||||
{
|
{
|
||||||
*pivot_pos += move_response.drag_delta();
|
let pivot_at_start_of_drag = ctx.data_mut(|data| {
|
||||||
|
*data.get_temp_mut_or::<Pos2>(pivot_at_start_of_drag_id, *pivot_pos)
|
||||||
|
});
|
||||||
|
|
||||||
|
*pivot_pos =
|
||||||
|
pivot_at_start_of_drag + move_response.total_drag_delta().unwrap_or_default();
|
||||||
|
} else {
|
||||||
|
ctx.data_mut(|data| data.remove::<Pos2>(pivot_at_start_of_drag_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (move_response.dragged() || move_response.clicked())
|
if (move_response.dragged() || move_response.clicked())
|
||||||
@@ -543,13 +553,14 @@ impl Area {
|
|||||||
move_response
|
move_response
|
||||||
};
|
};
|
||||||
|
|
||||||
if constrain {
|
state.set_left_top_pos(round_area_position(
|
||||||
state.set_left_top_pos(
|
ctx,
|
||||||
Context::constrain_window_rect_to_area(state.rect(), constrain_rect).min,
|
if constrain {
|
||||||
);
|
Context::constrain_window_rect_to_area(state.rect(), constrain_rect).min
|
||||||
}
|
} else {
|
||||||
|
state.left_top_pos()
|
||||||
state.set_left_top_pos(state.left_top_pos());
|
},
|
||||||
|
));
|
||||||
|
|
||||||
// Update response with possibly moved/constrained rect:
|
// Update response with possibly moved/constrained rect:
|
||||||
move_response.rect = state.rect();
|
move_response.rect = state.rect();
|
||||||
@@ -570,6 +581,16 @@ impl Area {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn round_area_position(ctx: &Context, pos: Pos2) -> Pos2 {
|
||||||
|
// We round a lot of rendering to pixels, so we round the whole
|
||||||
|
// area positions to pixels too, so avoid widgets appearing to float
|
||||||
|
// around independently of each other when the area is dragged.
|
||||||
|
// But just in case pixels_per_point is irrational,
|
||||||
|
// we then also round to ui coordinates:
|
||||||
|
|
||||||
|
pos.round_to_pixels(ctx.pixels_per_point()).round_ui()
|
||||||
|
}
|
||||||
|
|
||||||
impl Prepared {
|
impl Prepared {
|
||||||
pub(crate) fn state(&self) -> &AreaState {
|
pub(crate) fn state(&self) -> &AreaState {
|
||||||
&self.state
|
&self.state
|
||||||
|
|||||||
@@ -161,14 +161,13 @@ impl MenuState {
|
|||||||
if state.last_visible_pass + 1 < pass_nr {
|
if state.last_visible_pass + 1 < pass_nr {
|
||||||
state.open_item = None;
|
state.open_item = None;
|
||||||
}
|
}
|
||||||
if let Some(item) = state.open_item {
|
if let Some(item) = state.open_item
|
||||||
if data
|
&& data
|
||||||
.get_temp(item.with(Self::ID))
|
.get_temp(item.with(Self::ID))
|
||||||
.is_none_or(|item: Self| item.last_visible_pass + 1 < pass_nr)
|
.is_none_or(|item: Self| item.last_visible_pass + 1 < pass_nr)
|
||||||
{
|
{
|
||||||
// If the open item wasn't shown for at least a frame, reset the open item
|
// If the open item wasn't shown for at least a frame, reset the open item
|
||||||
state.open_item = None;
|
state.open_item = None;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let r = f(&mut state);
|
let r = f(&mut state);
|
||||||
data.insert_temp(state_id, state);
|
data.insert_temp(state_id, state);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Containers are pieces of the UI which wraps other pieces of UI. Examples: [`Window`], [`ScrollArea`], [`Resize`], [`SidePanel`], etc.
|
//! Containers are pieces of the UI which wraps other pieces of UI. Examples: [`Window`], [`ScrollArea`], [`Resize`], [`Panel`], etc.
|
||||||
//!
|
//!
|
||||||
//! For instance, a [`Frame`] adds a frame and background to some contained UI.
|
//! For instance, a [`Frame`] adds a frame and background to some contained UI.
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ pub use {
|
|||||||
frame::Frame,
|
frame::Frame,
|
||||||
modal::{Modal, ModalResponse},
|
modal::{Modal, ModalResponse},
|
||||||
old_popup::*,
|
old_popup::*,
|
||||||
panel::{CentralPanel, SidePanel, TopBottomPanel},
|
panel::*,
|
||||||
popup::*,
|
popup::*,
|
||||||
resize::Resize,
|
resize::Resize,
|
||||||
scene::{DragPanButtons, Scene},
|
scene::{DragPanButtons, Scene},
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use crate::{
|
|||||||
///
|
///
|
||||||
/// You can show multiple modals on top of each other. The topmost modal will always be
|
/// You can show multiple modals on top of each other. The topmost modal will always be
|
||||||
/// the most recently shown one.
|
/// the most recently shown one.
|
||||||
/// If multiple modals are newly shown in the same frame, the order of the modals not undefined
|
/// If multiple modals are newly shown in the same frame, the order of the modals is undefined
|
||||||
/// (either first or second could be top).
|
/// (either first or second could be top).
|
||||||
pub struct Modal {
|
pub struct Modal {
|
||||||
pub area: Area,
|
pub area: Area,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,12 @@
|
|||||||
|
//! See [`ScrollArea`] for docs.
|
||||||
|
|
||||||
#![allow(clippy::needless_range_loop)]
|
#![allow(clippy::needless_range_loop)]
|
||||||
|
|
||||||
use std::ops::{Add, AddAssign, BitOr, BitOrAssign};
|
use std::ops::{Add, AddAssign, BitOr, BitOrAssign};
|
||||||
|
|
||||||
|
use emath::GuiRounding as _;
|
||||||
|
use epaint::Margin;
|
||||||
|
|
||||||
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,
|
||||||
UiKind, UiStackInfo, Vec2, Vec2b, emath, epaint, lerp, pass_state, pos2, remap, remap_clamp,
|
UiKind, UiStackInfo, Vec2, Vec2b, emath, epaint, lerp, pass_state, pos2, remap, remap_clamp,
|
||||||
@@ -256,7 +261,7 @@ impl AddAssign for ScrollSource {
|
|||||||
/// ### Coordinate system
|
/// ### Coordinate system
|
||||||
/// * content: size of contents (generally large; that's why we want scroll bars)
|
/// * content: size of contents (generally large; that's why we want scroll bars)
|
||||||
/// * outer: size of scroll area including scroll bar(s)
|
/// * outer: size of scroll area including scroll bar(s)
|
||||||
/// * inner: excluding scroll bar(s). The area we clip the contents to.
|
/// * inner: excluding scroll bar(s). The area we clip the contents to. Includes `content_margin`.
|
||||||
///
|
///
|
||||||
/// If the floating scroll bars settings is turned on then `inner == outer`.
|
/// If the floating scroll bars settings is turned on then `inner == outer`.
|
||||||
///
|
///
|
||||||
@@ -292,6 +297,8 @@ pub struct ScrollArea {
|
|||||||
scroll_source: ScrollSource,
|
scroll_source: ScrollSource,
|
||||||
wheel_scroll_multiplier: Vec2,
|
wheel_scroll_multiplier: Vec2,
|
||||||
|
|
||||||
|
content_margin: Option<Margin>,
|
||||||
|
|
||||||
/// If true for vertical or horizontal the scroll wheel will stick to the
|
/// If true for vertical or horizontal the scroll wheel will stick to the
|
||||||
/// end position until user manually changes position. It will become true
|
/// end position until user manually changes position. It will become true
|
||||||
/// again once scroll handle makes contact with end.
|
/// again once scroll handle makes contact with end.
|
||||||
@@ -344,6 +351,7 @@ impl ScrollArea {
|
|||||||
on_drag_cursor: None,
|
on_drag_cursor: None,
|
||||||
scroll_source: ScrollSource::default(),
|
scroll_source: ScrollSource::default(),
|
||||||
wheel_scroll_multiplier: Vec2::splat(1.0),
|
wheel_scroll_multiplier: Vec2::splat(1.0),
|
||||||
|
content_margin: None,
|
||||||
stick_to_end: Vec2b::FALSE,
|
stick_to_end: Vec2b::FALSE,
|
||||||
animated: true,
|
animated: true,
|
||||||
}
|
}
|
||||||
@@ -591,6 +599,18 @@ impl ScrollArea {
|
|||||||
self.direction_enabled[0] || self.direction_enabled[1]
|
self.direction_enabled[0] || self.direction_enabled[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extra margin added around the contents.
|
||||||
|
///
|
||||||
|
/// The scroll bars will be either on top of this margin, or outside of it,
|
||||||
|
/// depending on the value of [`crate::style::ScrollStyle::floating`].
|
||||||
|
///
|
||||||
|
/// Default: [`crate::style::ScrollStyle::content_margin`].
|
||||||
|
#[inline]
|
||||||
|
pub fn content_margin(mut self, margin: impl Into<Margin>) -> Self {
|
||||||
|
self.content_margin = Some(margin.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// The scroll handle will stick to the rightmost position even while the content size
|
/// The scroll handle will stick to the rightmost position even while the content size
|
||||||
/// changes dynamically. This can be useful to simulate text scrollers coming in from right
|
/// changes dynamically. This can be useful to simulate text scrollers coming in from right
|
||||||
/// hand side. The scroll handle remains stuck until user manually changes position. Once "unstuck"
|
/// hand side. The scroll handle remains stuck until user manually changes position. Once "unstuck"
|
||||||
@@ -642,7 +662,7 @@ struct Prepared {
|
|||||||
scroll_bar_visibility: ScrollBarVisibility,
|
scroll_bar_visibility: ScrollBarVisibility,
|
||||||
scroll_bar_rect: Option<Rect>,
|
scroll_bar_rect: Option<Rect>,
|
||||||
|
|
||||||
/// Where on the screen the content is (excludes scroll bars).
|
/// Where on the screen the content is (excludes scroll bars; includes `content_margin`).
|
||||||
inner_rect: Rect,
|
inner_rect: Rect,
|
||||||
|
|
||||||
content_ui: Ui,
|
content_ui: Ui,
|
||||||
@@ -681,6 +701,7 @@ impl ScrollArea {
|
|||||||
on_drag_cursor,
|
on_drag_cursor,
|
||||||
scroll_source,
|
scroll_source,
|
||||||
wheel_scroll_multiplier,
|
wheel_scroll_multiplier,
|
||||||
|
content_margin: _, // Used elsewhere
|
||||||
stick_to_end,
|
stick_to_end,
|
||||||
animated,
|
animated,
|
||||||
} = self;
|
} = self;
|
||||||
@@ -748,6 +769,12 @@ impl ScrollArea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let content_max_rect = Rect::from_min_size(inner_rect.min - state.offset, content_max_size);
|
let content_max_rect = Rect::from_min_size(inner_rect.min - state.offset, content_max_size);
|
||||||
|
|
||||||
|
// Round to pixels to avoid widgets appearing to "float" when scrolling fractional amounts:
|
||||||
|
let content_max_rect = content_max_rect
|
||||||
|
.round_to_pixels(ui.pixels_per_point())
|
||||||
|
.round_ui();
|
||||||
|
|
||||||
let mut content_ui = ui.new_child(
|
let mut content_ui = ui.new_child(
|
||||||
UiBuilder::new()
|
UiBuilder::new()
|
||||||
.ui_stack_info(UiStackInfo::new(UiKind::ScrollArea))
|
.ui_stack_info(UiStackInfo::new(UiKind::ScrollArea))
|
||||||
@@ -975,10 +1002,21 @@ impl ScrollArea {
|
|||||||
ui: &mut Ui,
|
ui: &mut Ui,
|
||||||
add_contents: Box<dyn FnOnce(&mut Ui, Rect) -> R + 'c>,
|
add_contents: Box<dyn FnOnce(&mut Ui, Rect) -> R + 'c>,
|
||||||
) -> ScrollAreaOutput<R> {
|
) -> ScrollAreaOutput<R> {
|
||||||
|
let margin = self
|
||||||
|
.content_margin
|
||||||
|
.unwrap_or_else(|| ui.spacing().scroll.content_margin);
|
||||||
|
|
||||||
let mut prepared = self.begin(ui);
|
let mut prepared = self.begin(ui);
|
||||||
let id = prepared.id;
|
let id = prepared.id;
|
||||||
let inner_rect = prepared.inner_rect;
|
let inner_rect = prepared.inner_rect;
|
||||||
let inner = add_contents(&mut prepared.content_ui, prepared.viewport);
|
|
||||||
|
let inner = crate::Frame::NONE
|
||||||
|
.inner_margin(margin)
|
||||||
|
.show(&mut prepared.content_ui, |ui| {
|
||||||
|
add_contents(ui, prepared.viewport)
|
||||||
|
})
|
||||||
|
.inner;
|
||||||
|
|
||||||
let (content_size, state) = prepared.end(ui);
|
let (content_size, state) = prepared.end(ui);
|
||||||
ScrollAreaOutput {
|
ScrollAreaOutput {
|
||||||
inner,
|
inner,
|
||||||
|
|||||||
@@ -825,7 +825,7 @@ fn resize_response(
|
|||||||
area: &mut area::Prepared,
|
area: &mut area::Prepared,
|
||||||
resize_id: Id,
|
resize_id: Id,
|
||||||
) {
|
) {
|
||||||
let Some(mut new_rect) = move_and_resize_window(ctx, &resize_interaction) else {
|
let Some(mut new_rect) = move_and_resize_window(ctx, resize_id, &resize_interaction) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -847,27 +847,38 @@ fn resize_response(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Acts on outer rect (outside the stroke)
|
/// Acts on outer rect (outside the stroke)
|
||||||
fn move_and_resize_window(ctx: &Context, interaction: &ResizeInteraction) -> Option<Rect> {
|
fn move_and_resize_window(ctx: &Context, id: Id, interaction: &ResizeInteraction) -> Option<Rect> {
|
||||||
|
// Used to prevent drift
|
||||||
|
let rect_at_start_of_drag_id = id.with("window_rect_at_drag_start");
|
||||||
|
|
||||||
if !interaction.any_dragged() {
|
if !interaction.any_dragged() {
|
||||||
|
ctx.data_mut(|data| {
|
||||||
|
data.remove::<Rect>(rect_at_start_of_drag_id);
|
||||||
|
});
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let pointer_pos = ctx.input(|i| i.pointer.interact_pos())?;
|
let total_drag_delta = ctx.input(|i| i.pointer.total_drag_delta())?;
|
||||||
let mut rect = interaction.outer_rect; // prevent drift
|
|
||||||
|
let rect_at_start_of_drag = ctx.data_mut(|data| {
|
||||||
|
*data.get_temp_mut_or::<Rect>(rect_at_start_of_drag_id, interaction.outer_rect)
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut rect = rect_at_start_of_drag; // prevent drift
|
||||||
|
|
||||||
// Put the rect in the center of the stroke:
|
// Put the rect in the center of the stroke:
|
||||||
rect = rect.shrink(interaction.window_frame.stroke.width / 2.0);
|
rect = rect.shrink(interaction.window_frame.stroke.width / 2.0);
|
||||||
|
|
||||||
if interaction.left.drag {
|
if interaction.left.drag {
|
||||||
rect.min.x = pointer_pos.x;
|
rect.min.x += total_drag_delta.x;
|
||||||
} else if interaction.right.drag {
|
} else if interaction.right.drag {
|
||||||
rect.max.x = pointer_pos.x;
|
rect.max.x += total_drag_delta.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
if interaction.top.drag {
|
if interaction.top.drag {
|
||||||
rect.min.y = pointer_pos.y;
|
rect.min.y += total_drag_delta.y;
|
||||||
} else if interaction.bottom.drag {
|
} else if interaction.bottom.drag {
|
||||||
rect.max.y = pointer_pos.y;
|
rect.max.y += total_drag_delta.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return to having the rect outside the stroke:
|
// Return to having the rect outside the stroke:
|
||||||
@@ -899,7 +910,6 @@ fn resize_interaction(
|
|||||||
let rect = outer_rect.shrink(window_frame.stroke.width / 2.0);
|
let rect = outer_rect.shrink(window_frame.stroke.width / 2.0);
|
||||||
|
|
||||||
let side_response = |rect, id| {
|
let side_response = |rect, id| {
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
ctx.register_accesskit_parent(id, _accessibility_parent);
|
ctx.register_accesskit_parent(id, _accessibility_parent);
|
||||||
let response = ctx.create_widget(
|
let response = ctx.create_widget(
|
||||||
WidgetRect {
|
WidgetRect {
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ use crate::{
|
|||||||
viewport::ViewportClass,
|
viewport::ViewportClass,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
use crate::IdMap;
|
use crate::IdMap;
|
||||||
|
|
||||||
/// Information given to the backend about when it is time to repaint the ui.
|
/// Information given to the backend about when it is time to repaint the ui.
|
||||||
@@ -404,7 +403,6 @@ struct ContextImpl {
|
|||||||
|
|
||||||
embed_viewports: bool,
|
embed_viewports: bool,
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
is_accesskit_enabled: bool,
|
is_accesskit_enabled: bool,
|
||||||
|
|
||||||
loaders: Arc<Loaders>,
|
loaders: Arc<Loaders>,
|
||||||
@@ -507,7 +505,6 @@ impl ContextImpl {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
if self.is_accesskit_enabled {
|
if self.is_accesskit_enabled {
|
||||||
profiling::scope!("accesskit");
|
profiling::scope!("accesskit");
|
||||||
use crate::pass_state::AccessKitPassState;
|
use crate::pass_state::AccessKitPassState;
|
||||||
@@ -589,10 +586,10 @@ impl ContextImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
fn accesskit_node_builder(&mut self, id: Id) -> &mut accesskit::Node {
|
fn accesskit_node_builder(&mut self, id: Id) -> &mut accesskit::Node {
|
||||||
let state = self.viewport().this_pass.accesskit_state.as_mut().unwrap();
|
let state = self.viewport().this_pass.accesskit_state.as_mut().unwrap();
|
||||||
let builders = &mut state.nodes;
|
let builders = &mut state.nodes;
|
||||||
|
|
||||||
if let std::collections::hash_map::Entry::Vacant(entry) = builders.entry(id) {
|
if let std::collections::hash_map::Entry::Vacant(entry) = builders.entry(id) {
|
||||||
entry.insert(Default::default());
|
entry.insert(Default::default());
|
||||||
|
|
||||||
@@ -619,6 +616,7 @@ impl ContextImpl {
|
|||||||
let parent_builder = builders.get_mut(&parent_id).unwrap();
|
let parent_builder = builders.get_mut(&parent_id).unwrap();
|
||||||
parent_builder.push_child(id.accesskit_id());
|
parent_builder.push_child(id.accesskit_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
builders.get_mut(&id).unwrap()
|
builders.get_mut(&id).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -766,7 +764,7 @@ impl Context {
|
|||||||
/// and only on the rare occasion that [`Context::request_discard`] is called.
|
/// and only on the rare occasion that [`Context::request_discard`] is called.
|
||||||
/// Usually, it `run_ui` will only be called once.
|
/// Usually, it `run_ui` will only be called once.
|
||||||
///
|
///
|
||||||
/// Put your widgets into a [`crate::SidePanel`], [`crate::TopBottomPanel`], [`crate::CentralPanel`], [`crate::Window`] or [`crate::Area`].
|
/// Put your widgets into a [`crate::Panel`], [`crate::CentralPanel`], [`crate::Window`] or [`crate::Area`].
|
||||||
///
|
///
|
||||||
/// Instead of calling `run`, you can alternatively use [`Self::begin_pass`] and [`Context::end_pass`].
|
/// Instead of calling `run`, you can alternatively use [`Self::begin_pass`] and [`Context::end_pass`].
|
||||||
///
|
///
|
||||||
@@ -1204,7 +1202,6 @@ impl Context {
|
|||||||
plugins.on_widget_under_pointer(self, &w);
|
plugins.on_widget_under_pointer(self, &w);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
if allow_focus && w.sense.is_focusable() {
|
if allow_focus && w.sense.is_focusable() {
|
||||||
// Make sure anything that can receive focus has an AccessKit node.
|
// Make sure anything that can receive focus has an AccessKit node.
|
||||||
// TODO(mwcampbell): For nodes that are filled from widget info,
|
// TODO(mwcampbell): For nodes that are filled from widget info,
|
||||||
@@ -1212,7 +1209,6 @@ impl Context {
|
|||||||
self.accesskit_node_builder(w.id, |builder| res.fill_accesskit_node_common(builder));
|
self.accesskit_node_builder(w.id, |builder| res.fill_accesskit_node_common(builder));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
self.write(|ctx| {
|
self.write(|ctx| {
|
||||||
use crate::{Align, pass_state::ScrollTarget, style::ScrollAnimation};
|
use crate::{Align, pass_state::ScrollTarget, style::ScrollAnimation};
|
||||||
let viewport = ctx.viewport_for(ctx.viewport_id());
|
let viewport = ctx.viewport_for(ctx.viewport_id());
|
||||||
@@ -1220,12 +1216,14 @@ impl Context {
|
|||||||
viewport
|
viewport
|
||||||
.input
|
.input
|
||||||
.consume_accesskit_action_requests(res.id, |request| {
|
.consume_accesskit_action_requests(res.id, |request| {
|
||||||
|
use accesskit::Action;
|
||||||
|
|
||||||
// TODO(lucasmerlin): Correctly handle the scroll unit:
|
// TODO(lucasmerlin): Correctly handle the scroll unit:
|
||||||
// https://github.com/AccessKit/accesskit/blob/e639c0e0d8ccbfd9dff302d972fa06f9766d608e/common/src/lib.rs#L2621
|
// https://github.com/AccessKit/accesskit/blob/e639c0e0d8ccbfd9dff302d972fa06f9766d608e/common/src/lib.rs#L2621
|
||||||
const DISTANCE: f32 = 100.0;
|
const DISTANCE: f32 = 100.0;
|
||||||
|
|
||||||
match &request.action {
|
match &request.action {
|
||||||
accesskit::Action::ScrollIntoView => {
|
Action::ScrollIntoView => {
|
||||||
viewport.this_pass.scroll_target = [
|
viewport.this_pass.scroll_target = [
|
||||||
Some(ScrollTarget::new(
|
Some(ScrollTarget::new(
|
||||||
res.rect.x_range(),
|
res.rect.x_range(),
|
||||||
@@ -1239,16 +1237,16 @@ impl Context {
|
|||||||
)),
|
)),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
accesskit::Action::ScrollDown => {
|
Action::ScrollDown => {
|
||||||
viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::UP;
|
viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::UP;
|
||||||
}
|
}
|
||||||
accesskit::Action::ScrollUp => {
|
Action::ScrollUp => {
|
||||||
viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::DOWN;
|
viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::DOWN;
|
||||||
}
|
}
|
||||||
accesskit::Action::ScrollLeft => {
|
Action::ScrollLeft => {
|
||||||
viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::LEFT;
|
viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::LEFT;
|
||||||
}
|
}
|
||||||
accesskit::Action::ScrollRight => {
|
Action::ScrollRight => {
|
||||||
viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::RIGHT;
|
viewport.this_pass.scroll_delta.0 += DISTANCE * Vec2::RIGHT;
|
||||||
}
|
}
|
||||||
_ => return false,
|
_ => return false,
|
||||||
@@ -1341,7 +1339,6 @@ impl Context {
|
|||||||
res.flags.set(Flags::FAKE_PRIMARY_CLICKED, true);
|
res.flags.set(Flags::FAKE_PRIMARY_CLICKED, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
if enabled
|
if enabled
|
||||||
&& sense.senses_click()
|
&& sense.senses_click()
|
||||||
&& input.has_accesskit_action_request(id, accesskit::Action::Click)
|
&& input.has_accesskit_action_request(id, accesskit::Action::Click)
|
||||||
@@ -2498,7 +2495,6 @@ impl ContextImpl {
|
|||||||
|
|
||||||
let mut platform_output: PlatformOutput = std::mem::take(&mut viewport.output);
|
let mut platform_output: PlatformOutput = std::mem::take(&mut viewport.output);
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
{
|
{
|
||||||
profiling::scope!("accesskit");
|
profiling::scope!("accesskit");
|
||||||
let state = viewport.this_pass.accesskit_state.take();
|
let state = viewport.this_pass.accesskit_state.take();
|
||||||
@@ -3497,9 +3493,8 @@ impl Context {
|
|||||||
///
|
///
|
||||||
/// The `Context` lock is held while the given closure is called!
|
/// The `Context` lock is held while the given closure is called!
|
||||||
///
|
///
|
||||||
/// Returns `None` if acesskit is off.
|
/// Returns `None` if accesskit is off.
|
||||||
// TODO(emilk): consider making both read-only and read-write versions
|
// TODO(emilk): consider making both read-only and read-write versions
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
pub fn accesskit_node_builder<R>(
|
pub fn accesskit_node_builder<R>(
|
||||||
&self,
|
&self,
|
||||||
id: Id,
|
id: Id,
|
||||||
@@ -3515,7 +3510,6 @@ impl Context {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
pub(crate) fn register_accesskit_parent(&self, id: Id, parent_id: Id) {
|
pub(crate) fn register_accesskit_parent(&self, id: Id, parent_id: Id) {
|
||||||
self.write(|ctx| {
|
self.write(|ctx| {
|
||||||
if let Some(state) = ctx.viewport().this_pass.accesskit_state.as_mut() {
|
if let Some(state) = ctx.viewport().this_pass.accesskit_state.as_mut() {
|
||||||
@@ -3525,13 +3519,11 @@ impl Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Enable generation of AccessKit tree updates in all future frames.
|
/// Enable generation of AccessKit tree updates in all future frames.
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
pub fn enable_accesskit(&self) {
|
pub fn enable_accesskit(&self) {
|
||||||
self.write(|ctx| ctx.is_accesskit_enabled = true);
|
self.write(|ctx| ctx.is_accesskit_enabled = true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disable generation of AccessKit tree updates in all future frames.
|
/// Disable generation of AccessKit tree updates in all future frames.
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
pub fn disable_accesskit(&self) {
|
pub fn disable_accesskit(&self) {
|
||||||
self.write(|ctx| ctx.is_accesskit_enabled = false);
|
self.write(|ctx| ctx.is_accesskit_enabled = false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -548,7 +548,6 @@ pub enum Event {
|
|||||||
WindowFocused(bool),
|
WindowFocused(bool),
|
||||||
|
|
||||||
/// An assistive technology (e.g. screen reader) requested an action.
|
/// An assistive technology (e.g. screen reader) requested an action.
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
AccessKitActionRequest(accesskit::ActionRequest),
|
AccessKitActionRequest(accesskit::ActionRequest),
|
||||||
|
|
||||||
/// The reply of a screenshot requested with [`crate::ViewportCommand::Screenshot`].
|
/// The reply of a screenshot requested with [`crate::ViewportCommand::Screenshot`].
|
||||||
|
|||||||
@@ -128,7 +128,6 @@ pub struct PlatformOutput {
|
|||||||
/// The difference in the widget tree since last frame.
|
/// The difference in the widget tree since last frame.
|
||||||
///
|
///
|
||||||
/// NOTE: this needs to be per-viewport.
|
/// NOTE: this needs to be per-viewport.
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
pub accesskit_update: Option<accesskit::TreeUpdate>,
|
pub accesskit_update: Option<accesskit::TreeUpdate>,
|
||||||
|
|
||||||
/// How many ui passes is this the sum of?
|
/// How many ui passes is this the sum of?
|
||||||
@@ -175,7 +174,6 @@ impl PlatformOutput {
|
|||||||
mut events,
|
mut events,
|
||||||
mutable_text_under_cursor,
|
mutable_text_under_cursor,
|
||||||
ime,
|
ime,
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
accesskit_update,
|
accesskit_update,
|
||||||
num_completed_passes,
|
num_completed_passes,
|
||||||
mut request_discard_reasons,
|
mut request_discard_reasons,
|
||||||
@@ -190,12 +188,8 @@ impl PlatformOutput {
|
|||||||
self.request_discard_reasons
|
self.request_discard_reasons
|
||||||
.append(&mut request_discard_reasons);
|
.append(&mut request_discard_reasons);
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
// egui produces a complete AccessKit tree for each frame, so overwrite rather than append:
|
||||||
{
|
self.accesskit_update = accesskit_update;
|
||||||
// egui produces a complete AccessKit tree for each frame,
|
|
||||||
// so overwrite rather than appending.
|
|
||||||
self.accesskit_update = accesskit_update;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take everything ephemeral (everything except `cursor_icon` currently)
|
/// Take everything ephemeral (everything except `cursor_icon` currently)
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ impl Id {
|
|||||||
self.0.get()
|
self.0.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
pub(crate) fn accesskit_id(&self) -> accesskit::NodeId {
|
pub(crate) fn accesskit_id(&self) -> accesskit::NodeId {
|
||||||
self.value().into()
|
self.value().into()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -855,7 +855,6 @@ impl InputState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
pub fn accesskit_action_requests(
|
pub fn accesskit_action_requests(
|
||||||
&self,
|
&self,
|
||||||
id: crate::Id,
|
id: crate::Id,
|
||||||
@@ -873,7 +872,6 @@ impl InputState {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
pub fn consume_accesskit_action_requests(
|
pub fn consume_accesskit_action_requests(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: crate::Id,
|
id: crate::Id,
|
||||||
@@ -890,12 +888,10 @@ impl InputState {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
pub fn has_accesskit_action_request(&self, id: crate::Id, action: accesskit::Action) -> bool {
|
pub fn has_accesskit_action_request(&self, id: crate::Id, action: accesskit::Action) -> bool {
|
||||||
self.accesskit_action_requests(id, action).next().is_some()
|
self.accesskit_action_requests(id, action).next().is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
pub fn num_accesskit_action_requests(&self, id: crate::Id, action: accesskit::Action) -> usize {
|
pub fn num_accesskit_action_requests(&self, id: crate::Id, action: accesskit::Action) -> usize {
|
||||||
self.accesskit_action_requests(id, action).count()
|
self.accesskit_action_requests(id, action).count()
|
||||||
}
|
}
|
||||||
@@ -1267,6 +1263,11 @@ impl PointerState {
|
|||||||
self.press_origin
|
self.press_origin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// How far has the pointer moved since the start of the drag (if any)?
|
||||||
|
pub fn total_drag_delta(&self) -> Option<Vec2> {
|
||||||
|
Some(self.latest_pos? - self.press_origin?)
|
||||||
|
}
|
||||||
|
|
||||||
/// When did the current click/drag originate?
|
/// When did the current click/drag originate?
|
||||||
/// `None` if no mouse button is down.
|
/// `None` if no mouse button is down.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
//! which uses [`eframe`](https://docs.rs/eframe).
|
//! which uses [`eframe`](https://docs.rs/eframe).
|
||||||
//!
|
//!
|
||||||
//! To create a GUI using egui you first need a [`Context`] (by convention referred to by `ctx`).
|
//! To create a GUI using egui you first need a [`Context`] (by convention referred to by `ctx`).
|
||||||
//! Then you add a [`Window`] or a [`SidePanel`] to get a [`Ui`], which is what you'll be using to add all the buttons and labels that you need.
|
//! Then you add a [`Window`] or a [`Panel`] to get a [`Ui`], which is what you'll be using to add all the buttons and labels that you need.
|
||||||
//!
|
//!
|
||||||
//!
|
//!
|
||||||
//! ## Feature flags
|
//! ## Feature flags
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
//!
|
//!
|
||||||
//! ### Getting a [`Ui`]
|
//! ### Getting a [`Ui`]
|
||||||
//!
|
//!
|
||||||
//! Use one of [`SidePanel`], [`TopBottomPanel`], [`CentralPanel`], [`Window`] or [`Area`] to
|
//! Use one of [`Panel`], [`CentralPanel`], [`Window`] or [`Area`] to
|
||||||
//! get access to an [`Ui`] where you can put widgets. For example:
|
//! get access to an [`Ui`] where you can put widgets. For example:
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
@@ -322,7 +322,7 @@
|
|||||||
//! when you release the panel/window shrinks again.
|
//! when you release the panel/window shrinks again.
|
||||||
//! This is an artifact of immediate mode, and here are some alternatives on how to avoid it:
|
//! This is an artifact of immediate mode, and here are some alternatives on how to avoid it:
|
||||||
//!
|
//!
|
||||||
//! 1. Turn off resizing with [`Window::resizable`], [`SidePanel::resizable`], [`TopBottomPanel::resizable`].
|
//! 1. Turn off resizing with [`Window::resizable`], [`Panel::resizable`].
|
||||||
//! 2. Wrap your panel contents in a [`ScrollArea`], or use [`Window::vscroll`] and [`Window::hscroll`].
|
//! 2. Wrap your panel contents in a [`ScrollArea`], or use [`Window::vscroll`] and [`Window::hscroll`].
|
||||||
//! 3. Use a justified layout:
|
//! 3. Use a justified layout:
|
||||||
//!
|
//!
|
||||||
@@ -448,7 +448,6 @@ pub mod widgets;
|
|||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
mod callstack;
|
mod callstack;
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
pub use accesskit;
|
pub use accesskit;
|
||||||
|
|
||||||
#[deprecated = "Use the ahash crate directly."]
|
#[deprecated = "Use the ahash crate directly."]
|
||||||
@@ -708,7 +707,6 @@ pub fn __run_test_ui(add_contents: impl Fn(&mut Ui)) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
pub fn accesskit_root_id() -> Id {
|
pub fn accesskit_root_id() -> Id {
|
||||||
Id::new("accesskit_root")
|
Id::new("accesskit_root")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -470,7 +470,6 @@ pub(crate) struct Focus {
|
|||||||
/// The ID of a widget to give the focus to in the next frame.
|
/// The ID of a widget to give the focus to in the next frame.
|
||||||
id_next_frame: Option<Id>,
|
id_next_frame: Option<Id>,
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
id_requested_by_accesskit: Option<accesskit::NodeId>,
|
id_requested_by_accesskit: Option<accesskit::NodeId>,
|
||||||
|
|
||||||
/// If set, the next widget that is interested in focus will automatically get it.
|
/// If set, the next widget that is interested in focus will automatically get it.
|
||||||
@@ -529,10 +528,7 @@ impl Focus {
|
|||||||
}
|
}
|
||||||
let event_filter = self.focused_widget.map(|w| w.filter).unwrap_or_default();
|
let event_filter = self.focused_widget.map(|w| w.filter).unwrap_or_default();
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
self.id_requested_by_accesskit = None;
|
||||||
{
|
|
||||||
self.id_requested_by_accesskit = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.focus_direction = FocusDirection::None;
|
self.focus_direction = FocusDirection::None;
|
||||||
|
|
||||||
@@ -567,16 +563,13 @@ impl Focus {
|
|||||||
self.focus_direction = cardinality;
|
self.focus_direction = cardinality;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
if let crate::Event::AccessKitActionRequest(accesskit::ActionRequest {
|
||||||
|
action: accesskit::Action::Focus,
|
||||||
|
target,
|
||||||
|
data: None,
|
||||||
|
}) = event
|
||||||
{
|
{
|
||||||
if let crate::Event::AccessKitActionRequest(accesskit::ActionRequest {
|
self.id_requested_by_accesskit = Some(*target);
|
||||||
action: accesskit::Action::Focus,
|
|
||||||
target,
|
|
||||||
data: None,
|
|
||||||
}) = event
|
|
||||||
{
|
|
||||||
self.id_requested_by_accesskit = Some(*target);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -606,14 +599,11 @@ impl Focus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn interested_in_focus(&mut self, id: Id) {
|
fn interested_in_focus(&mut self, id: Id) {
|
||||||
#[cfg(feature = "accesskit")]
|
if self.id_requested_by_accesskit == Some(id.accesskit_id()) {
|
||||||
{
|
self.focused_widget = Some(FocusWidget::new(id));
|
||||||
if self.id_requested_by_accesskit == Some(id.accesskit_id()) {
|
self.id_requested_by_accesskit = None;
|
||||||
self.focused_widget = Some(FocusWidget::new(id));
|
self.give_to_next = false;
|
||||||
self.id_requested_by_accesskit = None;
|
self.reset_focus();
|
||||||
self.give_to_next = false;
|
|
||||||
self.reset_focus();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The rect is updated at the end of the frame.
|
// The rect is updated at the end of the frame.
|
||||||
@@ -1282,8 +1272,7 @@ impl Areas {
|
|||||||
pub fn top_layer_id(&self, order: Order) -> Option<LayerId> {
|
pub fn top_layer_id(&self, order: Order) -> Option<LayerId> {
|
||||||
self.order
|
self.order
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|layer| layer.order == order && !self.is_sublayer(layer))
|
.rfind(|layer| layer.order == order && !self.is_sublayer(layer))
|
||||||
.next_back()
|
|
||||||
.copied()
|
.copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ fn set_menu_style(style: &mut Style) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The menu bar goes well in a [`crate::TopBottomPanel::top`],
|
/// The menu bar goes well in a [`crate::Panel::top`],
|
||||||
/// but can also be placed in a [`crate::Window`].
|
/// but can also be placed in a [`crate::Window`].
|
||||||
/// In the latter case you may want to wrap it in [`Frame`].
|
/// In the latter case you may want to wrap it in [`Frame`].
|
||||||
#[deprecated = "Use `egui::MenuBar::new().ui(` instead"]
|
#[deprecated = "Use `egui::MenuBar::new().ui(` instead"]
|
||||||
@@ -634,7 +634,7 @@ impl SubMenu {
|
|||||||
/// Usually you don't need to use it directly.
|
/// Usually you don't need to use it directly.
|
||||||
pub struct MenuState {
|
pub struct MenuState {
|
||||||
/// The opened sub-menu and its [`Id`]
|
/// The opened sub-menu and its [`Id`]
|
||||||
sub_menu: Option<(Id, Arc<RwLock<MenuState>>)>,
|
sub_menu: Option<(Id, Arc<RwLock<Self>>)>,
|
||||||
|
|
||||||
/// Bounding box of this menu (without the sub-menu),
|
/// Bounding box of this menu (without the sub-menu),
|
||||||
/// including the frame and everything.
|
/// including the frame and everything.
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ impl ScrollTarget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AccessKitPassState {
|
pub struct AccessKitPassState {
|
||||||
pub nodes: IdMap<accesskit::Node>,
|
pub nodes: IdMap<accesskit::Node>,
|
||||||
@@ -225,7 +224,6 @@ pub struct PassState {
|
|||||||
/// as when swiping down on a touch-screen or track-pad with natural scrolling.
|
/// as when swiping down on a touch-screen or track-pad with natural scrolling.
|
||||||
pub scroll_delta: (Vec2, style::ScrollAnimation),
|
pub scroll_delta: (Vec2, style::ScrollAnimation),
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
pub accesskit_state: Option<AccessKitPassState>,
|
pub accesskit_state: Option<AccessKitPassState>,
|
||||||
|
|
||||||
/// Highlight these widgets the next pass.
|
/// Highlight these widgets the next pass.
|
||||||
@@ -247,7 +245,6 @@ impl Default for PassState {
|
|||||||
used_by_panels: Rect::NAN,
|
used_by_panels: Rect::NAN,
|
||||||
scroll_target: [None, None],
|
scroll_target: [None, None],
|
||||||
scroll_delta: (Vec2::default(), style::ScrollAnimation::none()),
|
scroll_delta: (Vec2::default(), style::ScrollAnimation::none()),
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
accesskit_state: None,
|
accesskit_state: None,
|
||||||
highlight_next_pass: Default::default(),
|
highlight_next_pass: Default::default(),
|
||||||
|
|
||||||
@@ -270,7 +267,6 @@ impl PassState {
|
|||||||
used_by_panels,
|
used_by_panels,
|
||||||
scroll_target,
|
scroll_target,
|
||||||
scroll_delta,
|
scroll_delta,
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
accesskit_state,
|
accesskit_state,
|
||||||
highlight_next_pass,
|
highlight_next_pass,
|
||||||
|
|
||||||
@@ -293,10 +289,7 @@ impl PassState {
|
|||||||
*debug_rect = None;
|
*debug_rect = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
*accesskit_state = None;
|
||||||
{
|
|
||||||
*accesskit_state = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
highlight_next_pass.clear();
|
highlight_next_pass.clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -396,7 +396,7 @@ impl Response {
|
|||||||
self.drag_stopped() && self.ctx.input(|i| i.pointer.button_released(button))
|
self.drag_stopped() && self.ctx.input(|i| i.pointer.button_released(button))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If dragged, how many points were we dragged and in what direction?
|
/// If dragged, how many points were we dragged in since last frame?
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn drag_delta(&self) -> Vec2 {
|
pub fn drag_delta(&self) -> Vec2 {
|
||||||
if self.dragged() {
|
if self.dragged() {
|
||||||
@@ -410,7 +410,22 @@ impl Response {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If dragged, how far did the mouse move?
|
/// If dragged, how many points have we been dragged since the start of the drag?
|
||||||
|
#[inline]
|
||||||
|
pub fn total_drag_delta(&self) -> Option<Vec2> {
|
||||||
|
if self.dragged() {
|
||||||
|
let mut delta = self.ctx.input(|i| i.pointer.total_drag_delta())?;
|
||||||
|
if let Some(from_global) = self.ctx.layer_transform_from_global(self.layer_id) {
|
||||||
|
delta *= from_global.scaling;
|
||||||
|
}
|
||||||
|
Some(delta)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If dragged, how far did the mouse move since last frame?
|
||||||
|
///
|
||||||
/// This will use raw mouse movement if provided by the integration, otherwise will fall back to [`Response::drag_delta`]
|
/// This will use raw mouse movement if provided by the integration, otherwise will fall back to [`Response::drag_delta`]
|
||||||
/// Raw mouse movement is unaccelerated and unclamped by screen boundaries, and does not relate to any position on the screen.
|
/// Raw mouse movement is unaccelerated and unclamped by screen boundaries, and does not relate to any position on the screen.
|
||||||
/// This may be useful in certain situations such as draggable values and 3D cameras, where screen position does not matter.
|
/// This may be useful in certain situations such as draggable values and 3D cameras, where screen position does not matter.
|
||||||
@@ -709,10 +724,9 @@ impl Response {
|
|||||||
/// ```
|
/// ```
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn interact(&self, sense: Sense) -> Self {
|
pub fn interact(&self, sense: Sense) -> Self {
|
||||||
if (self.sense | sense) == self.sense {
|
// We could check here if the new Sense equals the old one to avoid the extra create_widget
|
||||||
// Early-out: we already sense everything we need to sense.
|
// call. But that would break calling `interact` on a response from `Context::read_response`
|
||||||
return self.clone();
|
// or `Ui::response`. (See https://github.com/emilk/egui/pull/7713 for more details.)
|
||||||
}
|
|
||||||
|
|
||||||
self.ctx.create_widget(
|
self.ctx.create_widget(
|
||||||
WidgetRect {
|
WidgetRect {
|
||||||
@@ -793,7 +807,6 @@ impl Response {
|
|||||||
if let Some(event) = event {
|
if let Some(event) = event {
|
||||||
self.output_event(event);
|
self.output_event(event);
|
||||||
} else {
|
} else {
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
self.ctx.accesskit_node_builder(self.id, |builder| {
|
self.ctx.accesskit_node_builder(self.id, |builder| {
|
||||||
self.fill_accesskit_node_from_widget_info(builder, make_info());
|
self.fill_accesskit_node_from_widget_info(builder, make_info());
|
||||||
});
|
});
|
||||||
@@ -803,7 +816,6 @@ impl Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn output_event(&self, event: crate::output::OutputEvent) {
|
pub fn output_event(&self, event: crate::output::OutputEvent) {
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
self.ctx.accesskit_node_builder(self.id, |builder| {
|
self.ctx.accesskit_node_builder(self.id, |builder| {
|
||||||
self.fill_accesskit_node_from_widget_info(builder, event.widget_info().clone());
|
self.fill_accesskit_node_from_widget_info(builder, event.widget_info().clone());
|
||||||
});
|
});
|
||||||
@@ -814,7 +826,6 @@ impl Response {
|
|||||||
self.ctx.output_mut(|o| o.events.push(event));
|
self.ctx.output_mut(|o| o.events.push(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
pub(crate) fn fill_accesskit_node_common(&self, builder: &mut accesskit::Node) {
|
pub(crate) fn fill_accesskit_node_common(&self, builder: &mut accesskit::Node) {
|
||||||
if !self.enabled() {
|
if !self.enabled() {
|
||||||
builder.set_disabled();
|
builder.set_disabled();
|
||||||
@@ -833,7 +844,6 @@ impl Response {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
fn fill_accesskit_node_from_widget_info(
|
fn fill_accesskit_node_from_widget_info(
|
||||||
&self,
|
&self,
|
||||||
builder: &mut accesskit::Node,
|
builder: &mut accesskit::Node,
|
||||||
@@ -908,14 +918,9 @@ impl Response {
|
|||||||
/// # });
|
/// # });
|
||||||
/// ```
|
/// ```
|
||||||
pub fn labelled_by(self, id: Id) -> Self {
|
pub fn labelled_by(self, id: Id) -> Self {
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
self.ctx.accesskit_node_builder(self.id, |builder| {
|
self.ctx.accesskit_node_builder(self.id, |builder| {
|
||||||
builder.push_labelled_by(id.accesskit_id());
|
builder.push_labelled_by(id.accesskit_id());
|
||||||
});
|
});
|
||||||
#[cfg(not(feature = "accesskit"))]
|
|
||||||
{
|
|
||||||
let _ = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -508,6 +508,12 @@ pub struct ScrollStyle {
|
|||||||
/// it more promiment.
|
/// it more promiment.
|
||||||
pub floating: bool,
|
pub floating: bool,
|
||||||
|
|
||||||
|
/// Extra margin added around the contents of a [`crate::ScrollArea`].
|
||||||
|
///
|
||||||
|
/// The scroll bars will be either on top of this margin, or outside of it,
|
||||||
|
/// depending on the value of [`Self::floating`].
|
||||||
|
pub content_margin: Margin,
|
||||||
|
|
||||||
/// The width of the scroll bars at it largest.
|
/// The width of the scroll bars at it largest.
|
||||||
pub bar_width: f32,
|
pub bar_width: f32,
|
||||||
|
|
||||||
@@ -591,6 +597,7 @@ impl ScrollStyle {
|
|||||||
pub fn solid() -> Self {
|
pub fn solid() -> Self {
|
||||||
Self {
|
Self {
|
||||||
floating: false,
|
floating: false,
|
||||||
|
content_margin: Margin::ZERO,
|
||||||
bar_width: 6.0,
|
bar_width: 6.0,
|
||||||
handle_min_length: 12.0,
|
handle_min_length: 12.0,
|
||||||
bar_inner_margin: 4.0,
|
bar_inner_margin: 4.0,
|
||||||
@@ -672,6 +679,9 @@ impl ScrollStyle {
|
|||||||
pub fn details_ui(&mut self, ui: &mut Ui) {
|
pub fn details_ui(&mut self, ui: &mut Ui) {
|
||||||
let Self {
|
let Self {
|
||||||
floating,
|
floating,
|
||||||
|
|
||||||
|
content_margin,
|
||||||
|
|
||||||
bar_width,
|
bar_width,
|
||||||
handle_min_length,
|
handle_min_length,
|
||||||
bar_inner_margin,
|
bar_inner_margin,
|
||||||
@@ -695,6 +705,11 @@ impl ScrollStyle {
|
|||||||
ui.selectable_value(floating, true, "Floating");
|
ui.selectable_value(floating, true, "Floating");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Content margin:");
|
||||||
|
content_margin.ui(ui);
|
||||||
|
});
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add(DragValue::new(bar_width).range(0.0..=32.0));
|
ui.add(DragValue::new(bar_width).range(0.0..=32.0));
|
||||||
ui.label("Full bar width");
|
ui.label("Full bar width");
|
||||||
@@ -1824,6 +1839,10 @@ impl Spacing {
|
|||||||
ui.add(window_margin);
|
ui.add(window_margin);
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
|
|
||||||
|
ui.label("ScrollArea margin");
|
||||||
|
scroll.content_margin.ui(ui);
|
||||||
|
ui.end_row();
|
||||||
|
|
||||||
ui.label("Menu margin");
|
ui.label("Menu margin");
|
||||||
ui.add(menu_margin);
|
ui.add(menu_margin);
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
|
|||||||
@@ -42,8 +42,9 @@ pub fn update_accesskit_for_text_widget(
|
|||||||
|
|
||||||
for (row_index, row) in galley.rows.iter().enumerate() {
|
for (row_index, row) in galley.rows.iter().enumerate() {
|
||||||
let row_id = parent_id.with(row_index);
|
let row_id = parent_id.with(row_index);
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
ctx.register_accesskit_parent(row_id, parent_id);
|
ctx.register_accesskit_parent(row_id, parent_id);
|
||||||
|
|
||||||
ctx.accesskit_node_builder(row_id, |builder| {
|
ctx.accesskit_node_builder(row_id, |builder| {
|
||||||
builder.set_role(accesskit::Role::TextRun);
|
builder.set_role(accesskit::Role::TextRun);
|
||||||
let rect = global_from_galley * row.rect_without_leading_space();
|
let rect = global_from_galley * row.rect_without_leading_space();
|
||||||
|
|||||||
@@ -190,7 +190,6 @@ impl CCursorRange {
|
|||||||
..
|
..
|
||||||
} => self.on_key_press(os, galley, modifiers, *key),
|
} => self.on_key_press(os, galley, modifiers, *key),
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
Event::AccessKitActionRequest(accesskit::ActionRequest {
|
Event::AccessKitActionRequest(accesskit::ActionRequest {
|
||||||
action: accesskit::Action::SetTextSelection,
|
action: accesskit::Action::SetTextSelection,
|
||||||
target,
|
target,
|
||||||
@@ -220,7 +219,6 @@ impl CCursorRange {
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
fn ccursor_from_accesskit_text_position(
|
fn ccursor_from_accesskit_text_position(
|
||||||
id: Id,
|
id: Id,
|
||||||
galley: &Galley,
|
galley: &Galley,
|
||||||
|
|||||||
@@ -624,7 +624,6 @@ impl LabelSelectionState {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
super::accesskit_text::update_accesskit_for_text_widget(
|
super::accesskit_text::update_accesskit_for_text_widget(
|
||||||
ui.ctx(),
|
ui.ctx(),
|
||||||
response.id,
|
response.id,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
//! Helpers regarding text selection for labels and text edit.
|
//! Helpers regarding text selection for labels and text edit.
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
pub mod accesskit_text;
|
pub mod accesskit_text;
|
||||||
|
|
||||||
mod cursor_range;
|
mod cursor_range;
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ impl Ui {
|
|||||||
/// Create a new top-level [`Ui`].
|
/// Create a new top-level [`Ui`].
|
||||||
///
|
///
|
||||||
/// Normally you would not use this directly, but instead use
|
/// Normally you would not use this directly, but instead use
|
||||||
/// [`crate::SidePanel`], [`crate::TopBottomPanel`], [`crate::CentralPanel`], [`crate::Window`] or [`crate::Area`].
|
/// [`crate::Panel`], [`crate::CentralPanel`], [`crate::Window`] or [`crate::Area`].
|
||||||
pub fn new(ctx: Context, id: Id, ui_builder: UiBuilder) -> Self {
|
pub fn new(ctx: Context, id: Id, ui_builder: UiBuilder) -> Self {
|
||||||
let UiBuilder {
|
let UiBuilder {
|
||||||
id_salt,
|
id_salt,
|
||||||
@@ -133,7 +133,6 @@ impl Ui {
|
|||||||
sizing_pass,
|
sizing_pass,
|
||||||
style,
|
style,
|
||||||
sense,
|
sense,
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
accessibility_parent,
|
accessibility_parent,
|
||||||
} = ui_builder;
|
} = ui_builder;
|
||||||
|
|
||||||
@@ -175,7 +174,6 @@ impl Ui {
|
|||||||
min_rect_already_remembered: false,
|
min_rect_already_remembered: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
if let Some(accessibility_parent) = accessibility_parent {
|
if let Some(accessibility_parent) = accessibility_parent {
|
||||||
ui.ctx()
|
ui.ctx()
|
||||||
.register_accesskit_parent(ui.unique_id, accessibility_parent);
|
.register_accesskit_parent(ui.unique_id, accessibility_parent);
|
||||||
@@ -202,7 +200,6 @@ impl Ui {
|
|||||||
ui.set_invisible();
|
ui.set_invisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
ui.ctx().accesskit_node_builder(ui.unique_id, |node| {
|
ui.ctx().accesskit_node_builder(ui.unique_id, |node| {
|
||||||
node.set_role(accesskit::Role::GenericContainer);
|
node.set_role(accesskit::Role::GenericContainer);
|
||||||
});
|
});
|
||||||
@@ -273,7 +270,6 @@ impl Ui {
|
|||||||
sizing_pass,
|
sizing_pass,
|
||||||
style,
|
style,
|
||||||
sense,
|
sense,
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
accessibility_parent,
|
accessibility_parent,
|
||||||
} = ui_builder;
|
} = ui_builder;
|
||||||
|
|
||||||
@@ -343,7 +339,6 @@ impl Ui {
|
|||||||
child_ui.disable();
|
child_ui.disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
child_ui.ctx().register_accesskit_parent(
|
child_ui.ctx().register_accesskit_parent(
|
||||||
child_ui.unique_id,
|
child_ui.unique_id,
|
||||||
accessibility_parent.unwrap_or(self.unique_id),
|
accessibility_parent.unwrap_or(self.unique_id),
|
||||||
@@ -363,7 +358,6 @@ impl Ui {
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
child_ui
|
child_ui
|
||||||
.ctx()
|
.ctx()
|
||||||
.accesskit_node_builder(child_ui.unique_id, |node| {
|
.accesskit_node_builder(child_ui.unique_id, |node| {
|
||||||
@@ -1129,7 +1123,6 @@ impl Ui {
|
|||||||
impl Ui {
|
impl Ui {
|
||||||
/// Check for clicks, drags and/or hover on a specific region of this [`Ui`].
|
/// Check for clicks, drags and/or hover on a specific region of this [`Ui`].
|
||||||
pub fn interact(&self, rect: Rect, id: Id, sense: Sense) -> Response {
|
pub fn interact(&self, rect: Rect, id: Id, sense: Sense) -> Response {
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
self.ctx().register_accesskit_parent(id, self.unique_id);
|
self.ctx().register_accesskit_parent(id, self.unique_id);
|
||||||
|
|
||||||
self.ctx().create_widget(
|
self.ctx().create_widget(
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ pub struct UiBuilder {
|
|||||||
pub sizing_pass: bool,
|
pub sizing_pass: bool,
|
||||||
pub style: Option<Arc<Style>>,
|
pub style: Option<Arc<Style>>,
|
||||||
pub sense: Option<Sense>,
|
pub sense: Option<Sense>,
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
pub accessibility_parent: Option<Id>,
|
pub accessibility_parent: Option<Id>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,15 +186,9 @@ impl UiBuilder {
|
|||||||
///
|
///
|
||||||
/// This will override the automatic parent assignment for accessibility purposes.
|
/// This will override the automatic parent assignment for accessibility purposes.
|
||||||
/// If not set, the parent [`Ui`]'s ID will be used as the accessibility parent.
|
/// If not set, the parent [`Ui`]'s ID will be used as the accessibility parent.
|
||||||
///
|
|
||||||
/// This does nothing if the `accesskit` feature is not enabled.
|
|
||||||
#[cfg_attr(not(feature = "accesskit"), expect(unused_mut, unused_variables))]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn accessibility_parent(mut self, parent_id: Id) -> Self {
|
pub fn accessibility_parent(mut self, parent_id: Id) -> Self {
|
||||||
#[cfg(feature = "accesskit")]
|
self.accessibility_parent = Some(parent_id);
|
||||||
{
|
|
||||||
self.accessibility_parent = Some(parent_id);
|
|
||||||
}
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,16 +12,16 @@ pub enum UiKind {
|
|||||||
/// A [`crate::CentralPanel`].
|
/// A [`crate::CentralPanel`].
|
||||||
CentralPanel,
|
CentralPanel,
|
||||||
|
|
||||||
/// A left [`crate::SidePanel`].
|
/// A left [`crate::Panel`].
|
||||||
LeftPanel,
|
LeftPanel,
|
||||||
|
|
||||||
/// A right [`crate::SidePanel`].
|
/// A right [`crate::Panel`].
|
||||||
RightPanel,
|
RightPanel,
|
||||||
|
|
||||||
/// A top [`crate::TopBottomPanel`].
|
/// A top [`crate::Panel`].
|
||||||
TopPanel,
|
TopPanel,
|
||||||
|
|
||||||
/// A bottom [`crate::TopBottomPanel`].
|
/// A bottom [`crate::Panel`].
|
||||||
BottomPanel,
|
BottomPanel,
|
||||||
|
|
||||||
/// A modal [`crate::Modal`].
|
/// A modal [`crate::Modal`].
|
||||||
@@ -209,7 +209,7 @@ pub struct UiStack {
|
|||||||
pub layout_direction: Direction,
|
pub layout_direction: Direction,
|
||||||
pub min_rect: Rect,
|
pub min_rect: Rect,
|
||||||
pub max_rect: Rect,
|
pub max_rect: Rect,
|
||||||
pub parent: Option<Arc<UiStack>>,
|
pub parent: Option<Arc<Self>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// these methods act on this specific node
|
// these methods act on this specific node
|
||||||
|
|||||||
@@ -489,27 +489,21 @@ impl Widget for DragValue<'_> {
|
|||||||
- input.count_and_consume_key(Modifiers::NONE, Key::ArrowDown) as f64;
|
- input.count_and_consume_key(Modifiers::NONE, Key::ArrowDown) as f64;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
use accesskit::Action;
|
||||||
{
|
change += input.num_accesskit_action_requests(id, Action::Increment) as f64
|
||||||
use accesskit::Action;
|
- input.num_accesskit_action_requests(id, Action::Decrement) as f64;
|
||||||
change += input.num_accesskit_action_requests(id, Action::Increment) as f64
|
|
||||||
- input.num_accesskit_action_requests(id, Action::Decrement) as f64;
|
|
||||||
}
|
|
||||||
|
|
||||||
change
|
change
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
ui.input(|input| {
|
||||||
{
|
|
||||||
use accesskit::{Action, ActionData};
|
use accesskit::{Action, ActionData};
|
||||||
ui.input(|input| {
|
for request in input.accesskit_action_requests(id, Action::SetValue) {
|
||||||
for request in input.accesskit_action_requests(id, Action::SetValue) {
|
if let Some(ActionData::NumericValue(new_value)) = request.data {
|
||||||
if let Some(ActionData::NumericValue(new_value)) = request.data {
|
value = new_value;
|
||||||
value = new_value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
if clamp_existing_to_range {
|
if clamp_existing_to_range {
|
||||||
value = clamp_value_to_range(value, range.clone());
|
value = clamp_value_to_range(value, range.clone());
|
||||||
@@ -669,7 +663,6 @@ impl Widget for DragValue<'_> {
|
|||||||
|
|
||||||
response.widget_info(|| WidgetInfo::drag_value(ui.is_enabled(), value));
|
response.widget_info(|| WidgetInfo::drag_value(ui.is_enabled(), value));
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
ui.ctx().accesskit_node_builder(response.id, |builder| {
|
ui.ctx().accesskit_node_builder(response.id, |builder| {
|
||||||
use accesskit::Action;
|
use accesskit::Action;
|
||||||
// If either end of the range is unbounded, it's better
|
// If either end of the range is unbounded, it's better
|
||||||
|
|||||||
@@ -716,14 +716,11 @@ impl Slider<'_> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
ui.input(|input| {
|
||||||
{
|
|
||||||
use accesskit::Action;
|
use accesskit::Action;
|
||||||
ui.input(|input| {
|
decrement += input.num_accesskit_action_requests(response.id, Action::Decrement);
|
||||||
decrement += input.num_accesskit_action_requests(response.id, Action::Decrement);
|
increment += input.num_accesskit_action_requests(response.id, Action::Increment);
|
||||||
increment += input.num_accesskit_action_requests(response.id, Action::Increment);
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let kb_step = increment as f32 - decrement as f32;
|
let kb_step = increment as f32 - decrement as f32;
|
||||||
|
|
||||||
@@ -759,17 +756,14 @@ impl Slider<'_> {
|
|||||||
self.set_value(new_value);
|
self.set_value(new_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
ui.input(|input| {
|
||||||
{
|
|
||||||
use accesskit::{Action, ActionData};
|
use accesskit::{Action, ActionData};
|
||||||
ui.input(|input| {
|
for request in input.accesskit_action_requests(response.id, Action::SetValue) {
|
||||||
for request in input.accesskit_action_requests(response.id, Action::SetValue) {
|
if let Some(ActionData::NumericValue(new_value)) = request.data {
|
||||||
if let Some(ActionData::NumericValue(new_value)) = request.data {
|
self.set_value(new_value);
|
||||||
self.set_value(new_value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
// Paint it:
|
// Paint it:
|
||||||
if ui.is_rect_visible(response.rect) {
|
if ui.is_rect_visible(response.rect) {
|
||||||
@@ -978,7 +972,6 @@ impl Slider<'_> {
|
|||||||
}
|
}
|
||||||
response.widget_info(|| WidgetInfo::slider(ui.is_enabled(), value, self.text.text()));
|
response.widget_info(|| WidgetInfo::slider(ui.is_enabled(), value, self.text.text()));
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
ui.ctx().accesskit_node_builder(response.id, |builder| {
|
ui.ctx().accesskit_node_builder(response.id, |builder| {
|
||||||
use accesskit::Action;
|
use accesskit::Action;
|
||||||
builder.set_min_numeric_value(*self.range.start());
|
builder.set_min_numeric_value(*self.range.start());
|
||||||
|
|||||||
@@ -844,7 +844,6 @@ impl TextEdit<'_> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "accesskit")]
|
|
||||||
{
|
{
|
||||||
let role = if password {
|
let role = if password {
|
||||||
accesskit::Role::PasswordInput
|
accesskit::Role::PasswordInput
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ crate-type = ["cdylib", "rlib"]
|
|||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["glow", "persistence"]
|
default = ["wgpu", "persistence"]
|
||||||
|
|
||||||
# image_viewer adds about 0.9 MB of WASM
|
# image_viewer adds about 0.9 MB of WASM
|
||||||
web_app = ["http", "persistence"]
|
web_app = ["http", "persistence"]
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
|
use std::mem;
|
||||||
|
|
||||||
use accesskit::{Action, ActionRequest, NodeId};
|
use accesskit::{Action, ActionRequest, NodeId};
|
||||||
use accesskit_consumer::{FilterResult, Node, Tree, TreeChangeHandler};
|
use accesskit_consumer::{FilterResult, Node, Tree, TreeChangeHandler};
|
||||||
|
|
||||||
use eframe::epaint::text::TextWrapMode;
|
use eframe::epaint::text::TextWrapMode;
|
||||||
use egui::collapsing_header::CollapsingState;
|
|
||||||
use egui::{
|
use egui::{
|
||||||
Button, Color32, Context, Event, Frame, FullOutput, Id, Key, KeyboardShortcut, Label,
|
Button, Color32, Context, Event, Frame, FullOutput, Id, Key, KeyboardShortcut, Label,
|
||||||
Modifiers, RawInput, RichText, ScrollArea, SidePanel, TopBottomPanel, Ui,
|
Modifiers, Panel, RawInput, RichText, ScrollArea, Ui, collapsing_header::CollapsingState,
|
||||||
};
|
};
|
||||||
use std::mem;
|
|
||||||
|
|
||||||
/// This [`egui::Plugin`] adds an inspector Panel.
|
/// This [`egui::Plugin`] adds an inspector Panel.
|
||||||
///
|
///
|
||||||
@@ -86,10 +87,10 @@ impl egui::Plugin for AccessibilityInspectorPlugin {
|
|||||||
|
|
||||||
ctx.enable_accesskit();
|
ctx.enable_accesskit();
|
||||||
|
|
||||||
SidePanel::right(Self::id()).show(ctx, |ui| {
|
Panel::right(Self::id()).show(ctx, |ui| {
|
||||||
ui.heading("🔎 AccessKit Inspector");
|
ui.heading("🔎 AccessKit Inspector");
|
||||||
if let Some(selected_node) = self.selected_node {
|
if let Some(selected_node) = self.selected_node {
|
||||||
TopBottomPanel::bottom(Self::id().with("details_panel"))
|
Panel::bottom(Self::id().with("details_panel"))
|
||||||
.frame(Frame::new())
|
.frame(Frame::new())
|
||||||
.show_separator_line(false)
|
.show_separator_line(false)
|
||||||
.show_inside(ui, |ui| {
|
.show_inside(ui, |ui| {
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ impl Default for HttpApp {
|
|||||||
|
|
||||||
impl eframe::App for HttpApp {
|
impl eframe::App for HttpApp {
|
||||||
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
||||||
egui::TopBottomPanel::bottom("http_bottom").show(ctx, |ui| {
|
egui::Panel::bottom("http_bottom").show(ctx, |ui| {
|
||||||
let layout = egui::Layout::top_down(egui::Align::Center).with_main_justify(true);
|
let layout = egui::Layout::top_down(egui::Align::Center).with_main_justify(true);
|
||||||
ui.allocate_ui_with_layout(ui.available_size(), layout, |ui| {
|
ui.allocate_ui_with_layout(ui.available_size(), layout, |ui| {
|
||||||
ui.add(egui_demo_lib::egui_github_link_file!())
|
ui.add(egui_demo_lib::egui_github_link_file!())
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ use egui::ImageFit;
|
|||||||
use egui::Slider;
|
use egui::Slider;
|
||||||
use egui::Vec2;
|
use egui::Vec2;
|
||||||
use egui::emath::Rot2;
|
use egui::emath::Rot2;
|
||||||
use egui::panel::Side;
|
|
||||||
use egui::panel::TopBottomSide;
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
pub struct ImageViewer {
|
pub struct ImageViewer {
|
||||||
@@ -52,7 +50,7 @@ impl Default for ImageViewer {
|
|||||||
|
|
||||||
impl eframe::App for ImageViewer {
|
impl eframe::App for ImageViewer {
|
||||||
fn update(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
|
||||||
egui::TopBottomPanel::new(TopBottomSide::Top, "url bar").show(ctx, |ui| {
|
egui::Panel::top("url bar").show(ctx, |ui| {
|
||||||
ui.horizontal_centered(|ui| {
|
ui.horizontal_centered(|ui| {
|
||||||
let label = ui.label("URI:");
|
let label = ui.label("URI:");
|
||||||
ui.text_edit_singleline(&mut self.uri_edit_text)
|
ui.text_edit_singleline(&mut self.uri_edit_text)
|
||||||
@@ -73,7 +71,7 @@ impl eframe::App for ImageViewer {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
egui::SidePanel::new(Side::Left, "controls").show(ctx, |ui| {
|
egui::Panel::left("controls").show(ctx, |ui| {
|
||||||
// uv
|
// uv
|
||||||
ui.label("UV");
|
ui.label("UV");
|
||||||
ui.add(Slider::new(&mut self.image_options.uv.min.x, 0.0..=1.0).text("min x"));
|
ui.add(Slider::new(&mut self.image_options.uv.min.x, 0.0..=1.0).text("min x"));
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ impl eframe::App for WrapApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut cmd = Command::Nothing;
|
let mut cmd = Command::Nothing;
|
||||||
egui::TopBottomPanel::top("wrap_app_top_bar")
|
egui::Panel::top("wrap_app_top_bar")
|
||||||
.frame(egui::Frame::new().inner_margin(4))
|
.frame(egui::Frame::new().inner_margin(4))
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
ui.horizontal_wrapped(|ui| {
|
ui.horizontal_wrapped(|ui| {
|
||||||
@@ -341,7 +341,7 @@ impl WrapApp {
|
|||||||
|
|
||||||
let mut cmd = Command::Nothing;
|
let mut cmd = Command::Nothing;
|
||||||
|
|
||||||
egui::SidePanel::left("backend_panel")
|
egui::Panel::left("backend_panel")
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.show_animated(ctx, is_open, |ui| {
|
.show_animated(ctx, is_open, |ui| {
|
||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:44a68dc4d3aeebeb2d296c5c8e03aac330e1e4552364084347b710326c88f70c
|
oid sha256:784cbcdfd8deaf61e7b663f9416d67724e6a6a189a20ba3351908aa5c5f2deff
|
||||||
size 335794
|
size 336159
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:e9a760fe4a695e6321f00e40bfa76fd0195bee7157a1217572765e3f146ea2cc
|
oid sha256:4cdde1dda0e64f584c769c72f5910a7035e6a4a86a074b590e88365f12570109
|
||||||
size 93640
|
size 94062
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:a1670bbfc1f0a71e20cbbeb73625c148b680963bc503d9b48e9cc43e704d7c54
|
oid sha256:824d941ea538fd44fc374f5df1893eee2309004c0ee5e69a97f1c84a74b2b423
|
||||||
size 181671
|
size 182128
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:dc9c22567b76193a7f6753c4217adb3c92afa921c488ba1cf2e14b403814e7ac
|
oid sha256:44ea7ac8c8e22eb51fbcb63f00c8510de0e6ae126d19ab44c5d708d979b5362b
|
||||||
size 99841
|
size 100345
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ impl DemoWindows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn mobile_top_bar(&mut self, ctx: &Context) {
|
fn mobile_top_bar(&mut self, ctx: &Context) {
|
||||||
egui::TopBottomPanel::top("menu_bar").show(ctx, |ui| {
|
egui::Panel::top("menu_bar").show(ctx, |ui| {
|
||||||
menu::MenuBar::new()
|
menu::MenuBar::new()
|
||||||
.config(menu::MenuConfig::new().style(StyleModifier::default()))
|
.config(menu::MenuConfig::new().style(StyleModifier::default()))
|
||||||
.ui(ui, |ui| {
|
.ui(ui, |ui| {
|
||||||
@@ -262,10 +262,10 @@ impl DemoWindows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn desktop_ui(&mut self, ctx: &Context) {
|
fn desktop_ui(&mut self, ctx: &Context) {
|
||||||
egui::SidePanel::right("egui_demo_panel")
|
egui::Panel::right("egui_demo_panel")
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.default_width(160.0)
|
.default_size(160.0)
|
||||||
.min_width(160.0)
|
.min_size(160.0)
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
@@ -289,7 +289,7 @@ impl DemoWindows {
|
|||||||
self.demo_list_ui(ui);
|
self.demo_list_ui(ui);
|
||||||
});
|
});
|
||||||
|
|
||||||
egui::TopBottomPanel::top("menu_bar").show(ctx, |ui| {
|
egui::Panel::top("menu_bar").show(ctx, |ui| {
|
||||||
menu::MenuBar::new().ui(ui, |ui| {
|
menu::MenuBar::new().ui(ui, |ui| {
|
||||||
file_menu_button(ui);
|
file_menu_button(ui);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -451,7 +451,7 @@ enum Action {
|
|||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
struct Tree(Vec<Tree>);
|
struct Tree(Vec<Self>);
|
||||||
|
|
||||||
impl Tree {
|
impl Tree {
|
||||||
pub fn demo() -> Self {
|
pub fn demo() -> Self {
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ impl crate::View for Panels {
|
|||||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||||
// Note that the order we add the panels is very important!
|
// Note that the order we add the panels is very important!
|
||||||
|
|
||||||
egui::TopBottomPanel::top("top_panel")
|
egui::Panel::top("top_panel")
|
||||||
.resizable(true)
|
.resizable(true)
|
||||||
.min_height(32.0)
|
.min_size(32.0)
|
||||||
.show_inside(ui, |ui| {
|
.show_inside(ui, |ui| {
|
||||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
@@ -34,10 +34,10 @@ impl crate::View for Panels {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
egui::SidePanel::left("left_panel")
|
egui::Panel::left("left_panel")
|
||||||
.resizable(true)
|
.resizable(true)
|
||||||
.default_width(150.0)
|
.default_size(150.0)
|
||||||
.width_range(80.0..=200.0)
|
.size_range(80.0..=200.0)
|
||||||
.show_inside(ui, |ui| {
|
.show_inside(ui, |ui| {
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
ui.heading("Left Panel");
|
ui.heading("Left Panel");
|
||||||
@@ -47,10 +47,10 @@ impl crate::View for Panels {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
egui::SidePanel::right("right_panel")
|
egui::Panel::right("right_panel")
|
||||||
.resizable(true)
|
.resizable(true)
|
||||||
.default_width(150.0)
|
.default_size(150.0)
|
||||||
.width_range(80.0..=200.0)
|
.size_range(80.0..=200.0)
|
||||||
.show_inside(ui, |ui| {
|
.show_inside(ui, |ui| {
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
ui.heading("Right Panel");
|
ui.heading("Right Panel");
|
||||||
@@ -60,9 +60,9 @@ impl crate::View for Panels {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
egui::TopBottomPanel::bottom("bottom_panel")
|
egui::Panel::bottom("bottom_panel")
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.min_height(0.0)
|
.min_size(0.0)
|
||||||
.show_inside(ui, |ui| {
|
.show_inside(ui, |ui| {
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
ui.heading("Bottom Panel");
|
ui.heading("Bottom Panel");
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ impl crate::View for Tooltips {
|
|||||||
ui.add(crate::egui_github_link_file_line!());
|
ui.add(crate::egui_github_link_file_line!());
|
||||||
});
|
});
|
||||||
|
|
||||||
egui::SidePanel::right("scroll_test").show_inside(ui, |ui| {
|
egui::Panel::right("scroll_test").show_inside(ui, |ui| {
|
||||||
ui.label(
|
ui.label(
|
||||||
"The scroll area below has many labels with interactive tooltips. \
|
"The scroll area below has many labels with interactive tooltips. \
|
||||||
The purpose is to test that the tooltips close when you scroll.",
|
The purpose is to test that the tooltips close when you scroll.",
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ impl Default for EasyMarkEditor {
|
|||||||
|
|
||||||
impl EasyMarkEditor {
|
impl EasyMarkEditor {
|
||||||
pub fn panels(&mut self, ctx: &egui::Context) {
|
pub fn panels(&mut self, ctx: &egui::Context) {
|
||||||
egui::TopBottomPanel::bottom("easy_mark_bottom").show(ctx, |ui| {
|
egui::Panel::bottom("easy_mark_bottom").show(ctx, |ui| {
|
||||||
let layout = egui::Layout::top_down(egui::Align::Center).with_main_justify(true);
|
let layout = egui::Layout::top_down(egui::Align::Center).with_main_justify(true);
|
||||||
ui.allocate_ui_with_layout(ui.available_size(), layout, |ui| {
|
ui.allocate_ui_with_layout(ui.available_size(), layout, |ui| {
|
||||||
ui.add(crate::egui_github_link_file!())
|
ui.add(crate::egui_github_link_file!())
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:aff927596be5db77349ec0bbdcc852a0b1467e94c2a553a740a383ae318bad18
|
oid sha256:bb3f7b5f790830b46d1410c2bbb5e19c6beb403f8fe979eb8d250fba4f89be3e
|
||||||
size 51670
|
size 51670
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:9d6f055247034fa13ab55c9ec1fca275e6c23999c9a7e01c87af1fcc930faac6
|
oid sha256:170cee9d72a4ab59aa2faf1b77aff4a9eee64f3380aa3f1b256340d88b1dabc2
|
||||||
size 66777
|
size 66525
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:c91f592571ba654d0a96791662ae7530a1db4c1630b57c795d1c006ea6e46f19
|
oid sha256:f7a7d0e2618b852b5966073438c95cb62901d5410c1473639920b0b0bf2ec59b
|
||||||
size 256975
|
size 256913
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:4fbcca2b13c94769a62b44853b19f7e841bbb60c9197b3d0bf6e83ef9f8f76d1
|
oid sha256:72f4c6fe4f5ec243506152027e1150f3069caf98511ceef92b8fea4f6a1563d5
|
||||||
size 77815
|
size 77614
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:a31f0c12bb70449136443f9086103bd5b46356eedc2bb93ae1b6b10684ab69ca
|
oid sha256:611a2d6c793a85eebe807b2ddd4446cc0bc21e4284343dd756e64f0232fb6815
|
||||||
size 36285
|
size 35991
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:53c1509f7be264ed2947cd4ec0f10b555e9f710e949ed6fd8a73ca8ade53abd4
|
oid sha256:e7bc441559ff2d8723cf344113ce5ff8158e41179e4c93abcacbe7b1b13b3723
|
||||||
size 48570
|
size 48998
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:20eecafb998f69c2384afabc27eec1f97f413d603ece944adae9a99139be0b58
|
oid sha256:3e092be54efaeb700a63d9b679894647159f39a0d3062692ac7056e98242cbee
|
||||||
size 44689
|
size 45364
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:9a7f8282946761e6ab40193267e47a9421f5642bae67458a9aadb71ac1231c8f
|
oid sha256:88930779ac199e42fcc9ee25f29bd120478c129807713218370b617905340087
|
||||||
size 44581
|
size 45366
|
||||||
|
|||||||
@@ -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.33.2 - 2025-11-13
|
||||||
|
Nothing new
|
||||||
|
|
||||||
|
|
||||||
## 0.33.0 - 2025-10-09
|
## 0.33.0 - 2025-10-09
|
||||||
* Fix: use unique id for resize columns in `Table` [#7414](https://github.com/emilk/egui/pull/7414) by [@zezic](https://github.com/zezic)
|
* Fix: use unique id for resize columns in `Table` [#7414](https://github.com/emilk/egui/pull/7414) by [@zezic](https://github.com/zezic)
|
||||||
* Feat: Add serde serialization to SyntectSettings [#7506](https://github.com/emilk/egui/pull/7506) by [@bircni](https://github.com/bircni)
|
* Feat: Add serde serialization to SyntectSettings [#7506](https://github.com/emilk/egui/pull/7506) by [@bircni](https://github.com/bircni)
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ Changes since the last release can be found at <https://github.com/emilk/egui/co
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 0.33.2 - 2025-11-13
|
||||||
|
Nothing new
|
||||||
|
|
||||||
|
|
||||||
## 0.33.0 - 2025-10-09
|
## 0.33.0 - 2025-10-09
|
||||||
* Update MSRV from 1.86 to 1.88 [#7579](https://github.com/emilk/egui/pull/7579) by [@Wumpf](https://github.com/Wumpf)
|
* Update MSRV from 1.86 to 1.88 [#7579](https://github.com/emilk/egui/pull/7579) by [@Wumpf](https://github.com/Wumpf)
|
||||||
|
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ impl winit::application::ApplicationHandler<UserEvent> for GlowApp {
|
|||||||
self.egui_glow.as_mut().unwrap().run(
|
self.egui_glow.as_mut().unwrap().run(
|
||||||
self.gl_window.as_mut().unwrap().window(),
|
self.gl_window.as_mut().unwrap().window(),
|
||||||
|egui_ctx| {
|
|egui_ctx| {
|
||||||
egui::SidePanel::left("my_side_panel").show(egui_ctx, |ui| {
|
egui::Panel::left("my_side_panel").show(egui_ctx, |ui| {
|
||||||
ui.heading("Hello World!");
|
ui.heading("Hello World!");
|
||||||
if ui.button("Quit").clicked() {
|
if ui.button("Quit").clicked() {
|
||||||
quit = true;
|
quit = true;
|
||||||
|
|||||||
@@ -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.33.2 - 2025-11-13
|
||||||
|
Nothing new
|
||||||
|
|
||||||
|
|
||||||
## 0.33.1 - 2025-10-15
|
## 0.33.1 - 2025-10-15
|
||||||
* Add `egui_kittest::HarnessBuilder::with_options` [#7638](https://github.com/emilk/egui/pull/7638) by [@emilk](https://github.com/emilk)
|
* Add `egui_kittest::HarnessBuilder::with_options` [#7638](https://github.com/emilk/egui/pull/7638) by [@emilk](https://github.com/emilk)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "egui_kittest"
|
name = "egui_kittest"
|
||||||
version = "0.33.1"
|
version.workspace = true
|
||||||
authors = ["Lucas Meurer <lucasmeurer96@gmail.com>", "Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
authors = ["Lucas Meurer <lucasmeurer96@gmail.com>", "Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
||||||
description = "Testing library for egui based on kittest and AccessKit"
|
description = "Testing library for egui based on kittest and AccessKit"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
@@ -35,7 +35,7 @@ x11 = ["eframe?/x11"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
kittest.workspace = true
|
kittest.workspace = true
|
||||||
egui = { workspace = true, features = ["accesskit"] }
|
egui.workspace = true
|
||||||
eframe = { workspace = true, optional = true }
|
eframe = { workspace = true, optional = true }
|
||||||
|
|
||||||
# wgpu dependencies
|
# wgpu dependencies
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use egui::{Pos2, Rect, Vec2};
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
/// Builder for [`Harness`].
|
/// Builder for [`Harness`].
|
||||||
|
#[must_use]
|
||||||
pub struct HarnessBuilder<State = ()> {
|
pub struct HarnessBuilder<State = ()> {
|
||||||
pub(crate) screen_rect: Rect,
|
pub(crate) screen_rect: Rect,
|
||||||
pub(crate) pixels_per_point: f32,
|
pub(crate) pixels_per_point: f32,
|
||||||
@@ -64,8 +65,10 @@ impl<State> HarnessBuilder<State> {
|
|||||||
|
|
||||||
/// Set the default options used for snapshot tests on this harness.
|
/// Set the default options used for snapshot tests on this harness.
|
||||||
#[cfg(feature = "snapshot")]
|
#[cfg(feature = "snapshot")]
|
||||||
pub fn with_options(&mut self, options: crate::SnapshotOptions) {
|
#[inline]
|
||||||
|
pub fn with_options(mut self, options: crate::SnapshotOptions) -> Self {
|
||||||
self.default_snapshot_options = options;
|
self.default_snapshot_options = options;
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Override the [`egui::os::OperatingSystem`] reported to egui.
|
/// Override the [`egui::os::OperatingSystem`] reported to egui.
|
||||||
|
|||||||
@@ -657,7 +657,28 @@ impl<'a, State> Harness<'a, State> {
|
|||||||
/// Returns an error if the rendering fails.
|
/// Returns an error if the rendering fails.
|
||||||
#[cfg(any(feature = "wgpu", feature = "snapshot"))]
|
#[cfg(any(feature = "wgpu", feature = "snapshot"))]
|
||||||
pub fn render(&mut self) -> Result<image::RgbaImage, String> {
|
pub fn render(&mut self) -> Result<image::RgbaImage, String> {
|
||||||
self.renderer.render(&self.ctx, &self.output)
|
let mut output = self.output.clone();
|
||||||
|
|
||||||
|
if let Some(mouse_pos) = self.ctx.input(|i| i.pointer.hover_pos()) {
|
||||||
|
// Paint a mouse cursor:
|
||||||
|
let triangle = vec![
|
||||||
|
mouse_pos,
|
||||||
|
mouse_pos + egui::vec2(16.0, 8.0),
|
||||||
|
mouse_pos + egui::vec2(8.0, 16.0),
|
||||||
|
];
|
||||||
|
|
||||||
|
output.shapes.push(ClippedShape {
|
||||||
|
clip_rect: self.ctx.content_rect(),
|
||||||
|
shape: egui::epaint::PathShape::convex_polygon(
|
||||||
|
triangle,
|
||||||
|
Color32::WHITE,
|
||||||
|
egui::Stroke::new(1.0, Color32::BLACK),
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
self.renderer.render(&self.ctx, &output)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the root viewport output
|
/// Get the root viewport output
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:8f70ef032c241cd63675a246de07886c5c822e6fe21525b3a6d3fee106a589c9
|
oid sha256:42911cbb500fa49170aac0da8e4167641c5d7c9724a6accd4d400258fc74e2d7
|
||||||
size 7501
|
size 8061
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:dd6e159a462dde10240c4ca51da5ca5badfb7fc170bad97a59106babb72f8ae3
|
oid sha256:94ba2e648c981bf4afbd9b9d01eef0708f7067be6e4cefbdfacc13aa219c6289
|
||||||
size 10795
|
size 11253
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:8f2a5873350f85457d599c1fd165ac756ed69758e7647e160c64f44d2f35c804
|
oid sha256:436999f511dce318f29172f0b7e2007e1f0fedae58f5e0e85e19f1d8e0bee361
|
||||||
size 21812
|
size 22273
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:facc05c499745594ac286f15645e40447633a176058337cad9edcb850ad578c7
|
oid sha256:28435faf5c8c6d880cd50d52050c9f4cd6b992d0c621f01ca28fb5502eed16a1
|
||||||
size 29379
|
size 29863
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:9f23ff8c6782befdbe7bd5f076dcdda15c38555f8e505282369bf52e43938c1b
|
oid sha256:f9a364b4b8c4ad3e78a80b0c6825d9de28c0e0d2e18dcfcd0ff18652ca86c859
|
||||||
size 34194
|
size 34750
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:bb8d702361987803995c0f557ce94552a87b97dcd25bed5ee39af4c0e6090700
|
oid sha256:87c76a9d07174e4e24ad3d08585c1df7bf3628bdc8f183d11beb6f9e14c4b2ec
|
||||||
size 1904
|
size 2325
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:c267530452adb4f1ed1440df476d576ef4c2d96e6c58068bb57fed4615f5e113
|
oid sha256:e269ede9c0784d00c153d51a13566d9c8f0d61ce11565997691fa63be06ec889
|
||||||
size 4453
|
size 5075
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ All notable changes to the `emath` crate will be noted in this file.
|
|||||||
This file is updated upon each release.
|
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.33.2 - 2025-11-13
|
||||||
|
* Fix edge cases in "smart aiming" in sliders [#7680](https://github.com/emilk/egui/pull/7680) by [@emilk](https://github.com/emilk)
|
||||||
|
|
||||||
|
|
||||||
## 0.33.0 - 2025-10-09
|
## 0.33.0 - 2025-10-09
|
||||||
* Add `emath::fast_midpoint` [#7435](https://github.com/emilk/egui/pull/7435) by [@emilk](https://github.com/emilk)
|
* Add `emath::fast_midpoint` [#7435](https://github.com/emilk/egui/pull/7435) by [@emilk](https://github.com/emilk)
|
||||||
* Generate changelogs for emath [#7513](https://github.com/emilk/egui/pull/7513) by [@lucasmerlin](https://github.com/lucasmerlin)
|
* Generate changelogs for emath [#7513](https://github.com/emilk/egui/pull/7513) by [@lucasmerlin](https://github.com/lucasmerlin)
|
||||||
|
|||||||
@@ -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.33.2 - 2025-11-13
|
||||||
|
Nothing new
|
||||||
|
|
||||||
|
|
||||||
## 0.33.0 - 2025-10-09
|
## 0.33.0 - 2025-10-09
|
||||||
* Remove the `deadlock_detection` feature [#7497](https://github.com/emilk/egui/pull/7497) by [@lucasmerlin](https://github.com/lucasmerlin)
|
* Remove the `deadlock_detection` feature [#7497](https://github.com/emilk/egui/pull/7497) by [@lucasmerlin](https://github.com/lucasmerlin)
|
||||||
* More even text kerning [#7431](https://github.com/emilk/egui/pull/7431) by [@valadaptive](https://github.com/valadaptive)
|
* More even text kerning [#7431](https://github.com/emilk/egui/pull/7431) by [@valadaptive](https://github.com/valadaptive)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ pub enum Shape {
|
|||||||
|
|
||||||
/// Recursively nest more shapes - sometimes a convenience to be able to do.
|
/// Recursively nest more shapes - sometimes a convenience to be able to do.
|
||||||
/// For performance reasons it is better to avoid it.
|
/// For performance reasons it is better to avoid it.
|
||||||
Vec(Vec<Shape>),
|
Vec(Vec<Self>),
|
||||||
|
|
||||||
/// Circle with optional outline and fill.
|
/// Circle with optional outline and fill.
|
||||||
Circle(CircleShape),
|
Circle(CircleShape),
|
||||||
|
|||||||
@@ -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.33.2 - 2025-11-13
|
||||||
|
Nothing new
|
||||||
|
|
||||||
|
|
||||||
## 0.33.0 - 2025-10-09
|
## 0.33.0 - 2025-10-09
|
||||||
Nothing new
|
Nothing new
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ impl eframe::App for MyApp {
|
|||||||
// TODO(lucasmerlin): This is a pretty big hack, should be fixed once safe_area implemented
|
// TODO(lucasmerlin): This is a pretty big hack, should be fixed once safe_area implemented
|
||||||
// for android:
|
// for android:
|
||||||
// https://github.com/rust-windowing/winit/issues/3910
|
// https://github.com/rust-windowing/winit/issues/3910
|
||||||
egui::TopBottomPanel::top("status_bar_space").show(ctx, |ui| {
|
egui::Panel::top("status_bar_space").show(ctx, |ui| {
|
||||||
ui.set_height(32.0);
|
ui.set_height(32.0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -42,4 +42,5 @@ accept = [
|
|||||||
# Exclude URLs and mail addresses from checking (supports regex).
|
# Exclude URLs and mail addresses from checking (supports regex).
|
||||||
exclude = [
|
exclude = [
|
||||||
"https://creativecommons.org/.*", # They don't like bots
|
"https://creativecommons.org/.*", # They don't like bots
|
||||||
|
"https://www.unicode.org/.*",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ OPEN=false
|
|||||||
OPTIMIZE=false
|
OPTIMIZE=false
|
||||||
BUILD=debug
|
BUILD=debug
|
||||||
BUILD_FLAGS=""
|
BUILD_FLAGS=""
|
||||||
WGPU=false
|
GLOW=false
|
||||||
WASM_OPT_FLAGS="-O2 --fast-math"
|
WASM_OPT_FLAGS="-O2 --fast-math"
|
||||||
|
|
||||||
while test $# -gt 0; do
|
while test $# -gt 0; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
-h|--help)
|
-h|--help)
|
||||||
echo "build_demo_web.sh [--release] [--wgpu] [--open]"
|
echo "build_demo_web.sh [--release] [--glow] [--open]"
|
||||||
echo ""
|
echo ""
|
||||||
echo " -g: Keep debug symbols even with --release."
|
echo " -g: Keep debug symbols even with --release."
|
||||||
echo " These are useful profiling and size trimming."
|
echo " These are useful profiling and size trimming."
|
||||||
@@ -29,9 +29,7 @@ while test $# -gt 0; do
|
|||||||
echo " --release: Build with --release, and then run wasm-opt."
|
echo " --release: Build with --release, and then run wasm-opt."
|
||||||
echo " NOTE: --release also removes debug symbols, unless you also use -g."
|
echo " NOTE: --release also removes debug symbols, unless you also use -g."
|
||||||
echo ""
|
echo ""
|
||||||
echo " --wgpu: Build a binary using wgpu instead of glow/webgl."
|
echo " --glow: Build a binary using glow instead of wgpu."
|
||||||
echo " The resulting binary will automatically use WebGPU if available and"
|
|
||||||
echo " fall back to a WebGL emulation layer otherwise."
|
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
|
|
||||||
@@ -52,9 +50,9 @@ while test $# -gt 0; do
|
|||||||
BUILD_FLAGS="--release"
|
BUILD_FLAGS="--release"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
--wgpu)
|
--glow)
|
||||||
shift
|
shift
|
||||||
WGPU=true
|
GLOW=true
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
@@ -66,10 +64,10 @@ done
|
|||||||
|
|
||||||
OUT_FILE_NAME="egui_demo_app"
|
OUT_FILE_NAME="egui_demo_app"
|
||||||
|
|
||||||
if [[ "${WGPU}" == true ]]; then
|
if [[ "${GLOW}" == true ]]; then
|
||||||
FEATURES="${FEATURES},wgpu"
|
|
||||||
else
|
|
||||||
FEATURES="${FEATURES},glow"
|
FEATURES="${FEATURES},glow"
|
||||||
|
else
|
||||||
|
FEATURES="${FEATURES},wgpu"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
FINAL_WASM_PATH=web_demo/${OUT_FILE_NAME}_bg.wasm
|
FINAL_WASM_PATH=web_demo/${OUT_FILE_NAME}_bg.wasm
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
CRATE_NAME="egui_demo_app"
|
CRATE_NAME="egui_demo_app"
|
||||||
FEATURES="glow,http,persistence"
|
FEATURES="wgpu,http,persistence"
|
||||||
|
|
||||||
echo "Building rust…"
|
echo "Building rust…"
|
||||||
BUILD=debug # debug builds are faster
|
BUILD=debug # debug builds are faster
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use egui::accesskit::Role;
|
use egui::accesskit::Role;
|
||||||
use egui::{Align, Color32, Image, Label, Layout, RichText, TextWrapMode, include_image};
|
use egui::{Align, Color32, Image, Label, Layout, RichText, Sense, TextWrapMode, include_image};
|
||||||
use egui_kittest::Harness;
|
use egui_kittest::Harness;
|
||||||
use egui_kittest::kittest::Queryable as _;
|
use egui_kittest::kittest::Queryable as _;
|
||||||
|
|
||||||
@@ -75,3 +75,46 @@ fn combobox_should_have_value() {
|
|||||||
Some("Option 1")
|
Some("Option 1")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This test ensures that `ui.response().interact(...)` works correctly.
|
||||||
|
///
|
||||||
|
/// This was broken, because there was an optimization in [`egui::Response::interact`]
|
||||||
|
/// which caused the [`Sense`] of the original response to flip-flop between `click` and `hover`
|
||||||
|
/// between frames.
|
||||||
|
///
|
||||||
|
/// See <https://github.com/emilk/egui/pull/7713> for more details.
|
||||||
|
#[test]
|
||||||
|
fn interact_on_ui_response_should_be_stable() {
|
||||||
|
let mut first_frame = true;
|
||||||
|
let mut click_count = 0;
|
||||||
|
let mut harness = Harness::new_ui(|ui| {
|
||||||
|
let ui_response = ui.response();
|
||||||
|
if !first_frame {
|
||||||
|
assert!(
|
||||||
|
ui_response.sense.contains(Sense::click()),
|
||||||
|
"ui.response() didn't have click sense even though we called interact(Sense::click()) last frame"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a label so we have something to click with kittest
|
||||||
|
ui.add(
|
||||||
|
Label::new("senseless label")
|
||||||
|
.sense(Sense::hover())
|
||||||
|
.selectable(false),
|
||||||
|
);
|
||||||
|
|
||||||
|
let click_response = ui_response.interact(Sense::click());
|
||||||
|
if click_response.clicked() {
|
||||||
|
click_count += 1;
|
||||||
|
}
|
||||||
|
first_frame = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
for i in 0..=10 {
|
||||||
|
harness.run_steps(i);
|
||||||
|
harness.get_by_label("senseless label").click();
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(harness);
|
||||||
|
assert_eq!(click_count, 10, "We missed some clicks!");
|
||||||
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user