mirror of
https://github.com/emilk/egui.git
synced 2026-09-02 14:50:03 -04:00
Merge branch 'master' into master
This commit is contained in:
51
CHANGELOG.md
51
CHANGELOG.md
@@ -7,6 +7,57 @@ This file is updated upon each release.
|
|||||||
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.24.0 - 2023-11-23 - Multi-viewport
|
||||||
|
|
||||||
|
### ✨ Highlights
|
||||||
|
You can now spawn multiple native windows on supported backends (e.g. `eframe`), using [the new `viewport` API](https://docs.rs/egui/latest/egui/viewport/index.html) ([#3172](https://github.com/emilk/egui/pull/3172)).
|
||||||
|
|
||||||
|
You can easily zoom any egui app using Cmd+Plus, Cmd+Minus or Cmd+0, just like in a browser ([#3608](https://github.com/emilk/egui/pull/3608)).
|
||||||
|
|
||||||
|
Scrollbars are now hidden by default until you hover the `ScrollArea` ([#3539](https://github.com/emilk/egui/pull/3539)).
|
||||||
|
|
||||||
|
### ⭐ Added
|
||||||
|
* Multiple viewports/windows [#3172](https://github.com/emilk/egui/pull/3172) (thanks [@konkitoman](https://github.com/konkitoman)!)
|
||||||
|
* Introduce global `zoom_factor` [#3608](https://github.com/emilk/egui/pull/3608)
|
||||||
|
* Floating scroll bars [#3539](https://github.com/emilk/egui/pull/3539)
|
||||||
|
* Add redo support to `Undoer` [#3478](https://github.com/emilk/egui/pull/3478) (thanks [@LoganDark](https://github.com/LoganDark)!)
|
||||||
|
* Add `egui::Vec2b` [#3543](https://github.com/emilk/egui/pull/3543)
|
||||||
|
* Add max `Window` size & other size helpers [#3537](https://github.com/emilk/egui/pull/3537) (thanks [@arduano](https://github.com/arduano)!)
|
||||||
|
* Allow changing shape of slider handle [#3429](https://github.com/emilk/egui/pull/3429) (thanks [@YgorSouza](https://github.com/YgorSouza)!)
|
||||||
|
* `RawInput::viewports` contains a list of all viewports. Access the current one with `ctx.input(|i| i.viewport())`
|
||||||
|
|
||||||
|
### 🔧 Changed
|
||||||
|
* Replace `Id::null()` with `Id::NULL` [#3544](https://github.com/emilk/egui/pull/3544)
|
||||||
|
* Update MSRV to Rust 1.72 [#3595](https://github.com/emilk/egui/pull/3595)
|
||||||
|
* Update puffin to 0.18 [#3600](https://github.com/emilk/egui/pull/3600)
|
||||||
|
|
||||||
|
### 🐛 Fixed
|
||||||
|
* Fix upside down slider in the vertical orientation [#3424](https://github.com/emilk/egui/pull/3424) (thanks [@YgorSouza](https://github.com/YgorSouza)!)
|
||||||
|
* Make slider step account for range start [#3488](https://github.com/emilk/egui/pull/3488) (thanks [@YgorSouza](https://github.com/YgorSouza)!)
|
||||||
|
* Fix rounding of `ImageButton` [#3531](https://github.com/emilk/egui/pull/3531) (thanks [@chriscate](https://github.com/chriscate)!)
|
||||||
|
* Fix naming: `constraint_to` -> `constrain_to` [#3438](https://github.com/emilk/egui/pull/3438) (thanks [@rinde](https://github.com/rinde)!)
|
||||||
|
* Fix Shift+Tab behavior when no widget is focused [#3498](https://github.com/emilk/egui/pull/3498) (thanks [@DataTriny](https://github.com/DataTriny)!)
|
||||||
|
* Fix scroll not sticking when scrollbar is hidden [#3434](https://github.com/emilk/egui/pull/3434) (thanks [@LoganDark](https://github.com/LoganDark)!)
|
||||||
|
* Add `#[inline]` to all builder-pattern functions [#3557](https://github.com/emilk/egui/pull/3557)
|
||||||
|
* Properly reverse bool animation if value changes before it's finished [#3577](https://github.com/emilk/egui/pull/3577) (thanks [@YgorSouza](https://github.com/YgorSouza)!)
|
||||||
|
|
||||||
|
|
||||||
|
### ⚠️ BREAKING
|
||||||
|
* `egui::gui_zoom::zoom_with_keyboard_shortcuts` is gone, replaced with `Options::zoom_with_keyboard`, which is `true` by default
|
||||||
|
* `Spacing::scroll_bar_X` has been moved to `Spacing::scroll_bar.X`
|
||||||
|
* `Context::set_pixels_per_point` now calls `Context::set_zoom_level`, and it may make sense for you to call that directly instead
|
||||||
|
* If you are using `eframe`, check out the breaking changes in [the `eframe` changelog](crates/eframe/CHANGELOG.md)
|
||||||
|
|
||||||
|
#### For integrations
|
||||||
|
There are several changes relevant to integrations.
|
||||||
|
|
||||||
|
* Added `crate::RawInput::viewports` with information about all active viewports
|
||||||
|
* The repaint callback set by `Context::set_request_repaint_callback` now points to which viewport should be repainted
|
||||||
|
* `Context::run` now returns a list of `ViewportOutput` in `FullOutput` which should result in their own independent windows
|
||||||
|
* There is a new `Context::set_immediate_viewport_renderer` for setting up the immediate viewport integration
|
||||||
|
* If you support viewports, you need to call `Context::set_embed_viewports(false)`, or all new viewports will be embedded (the default behavior)
|
||||||
|
|
||||||
|
|
||||||
## 0.23.0 - 2023-09-27 - New image API
|
## 0.23.0 - 2023-09-27 - New image API
|
||||||
This release contains a simple and powerful image API:
|
This release contains a simple and powerful image API:
|
||||||
|
|
||||||
|
|||||||
26
Cargo.lock
generated
26
Cargo.lock
generated
@@ -1266,7 +1266,7 @@ checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ecolor"
|
name = "ecolor"
|
||||||
version = "0.23.0"
|
version = "0.24.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cint",
|
"cint",
|
||||||
@@ -1277,7 +1277,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "eframe"
|
name = "eframe"
|
||||||
version = "0.23.0"
|
version = "0.24.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cocoa",
|
"cocoa",
|
||||||
@@ -1314,7 +1314,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui"
|
name = "egui"
|
||||||
version = "0.23.0"
|
version = "0.24.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"ahash",
|
"ahash",
|
||||||
@@ -1330,7 +1330,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui-wgpu"
|
name = "egui-wgpu"
|
||||||
version = "0.23.0"
|
version = "0.24.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"document-features",
|
"document-features",
|
||||||
@@ -1346,7 +1346,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui-winit"
|
name = "egui-winit"
|
||||||
version = "0.23.0"
|
version = "0.24.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit_winit",
|
"accesskit_winit",
|
||||||
"arboard",
|
"arboard",
|
||||||
@@ -1364,7 +1364,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_demo_app"
|
name = "egui_demo_app"
|
||||||
version = "0.23.0"
|
version = "0.24.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -1377,6 +1377,8 @@ dependencies = [
|
|||||||
"image",
|
"image",
|
||||||
"log",
|
"log",
|
||||||
"poll-promise",
|
"poll-promise",
|
||||||
|
"puffin",
|
||||||
|
"puffin_http",
|
||||||
"rfd",
|
"rfd",
|
||||||
"serde",
|
"serde",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
@@ -1386,7 +1388,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_demo_lib"
|
name = "egui_demo_lib"
|
||||||
version = "0.23.0"
|
version = "0.24.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"criterion",
|
"criterion",
|
||||||
@@ -1401,7 +1403,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_extras"
|
name = "egui_extras"
|
||||||
version = "0.23.0"
|
version = "0.24.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"document-features",
|
"document-features",
|
||||||
@@ -1421,7 +1423,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_glow"
|
name = "egui_glow"
|
||||||
version = "0.23.0"
|
version = "0.24.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"document-features",
|
"document-features",
|
||||||
@@ -1440,7 +1442,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_plot"
|
name = "egui_plot"
|
||||||
version = "0.23.0"
|
version = "0.24.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"document-features",
|
"document-features",
|
||||||
"egui",
|
"egui",
|
||||||
@@ -1469,7 +1471,7 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "emath"
|
name = "emath"
|
||||||
version = "0.23.0"
|
version = "0.24.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"document-features",
|
"document-features",
|
||||||
@@ -1545,7 +1547,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "epaint"
|
name = "epaint"
|
||||||
version = "0.23.0"
|
version = "0.24.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ab_glyph",
|
"ab_glyph",
|
||||||
"ahash",
|
"ahash",
|
||||||
|
|||||||
@@ -16,6 +16,13 @@ members = [
|
|||||||
"examples/*",
|
"examples/*",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[workspace.package]
|
||||||
|
edition = "2021"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
rust-version = "1.72"
|
||||||
|
version = "0.24.0"
|
||||||
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
# lto = true # VERY slightly smaller wasm
|
# lto = true # VERY slightly smaller wasm
|
||||||
# opt-level = 's' # 10-20% smaller wasm compared to `opt-level = 3`
|
# opt-level = 's' # 10-20% smaller wasm compared to `opt-level = 3`
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ disallowed-types = [
|
|||||||
# "std::sync::Once", # enabled for now as the `log_once` macro uses it internally
|
# "std::sync::Once", # enabled for now as the `log_once` macro uses it internally
|
||||||
|
|
||||||
"ring::digest::SHA1_FOR_LEGACY_USE_ONLY", # SHA1 is cryptographically broken
|
"ring::digest::SHA1_FOR_LEGACY_USE_ONLY", # SHA1 is cryptographically broken
|
||||||
|
|
||||||
|
"winit::dpi::LogicalSize", # We do our own pixels<->point conversion, taking `egui_ctx.zoom_factor` into account
|
||||||
|
"winit::dpi::LogicalPosition", # We do our own pixels<->point conversion, taking `egui_ctx.zoom_factor` into account
|
||||||
]
|
]
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ This file is updated upon each release.
|
|||||||
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.24.0 - 2023-11-23
|
||||||
|
* Update MSRV to Rust 1.72 [#3595](https://github.com/emilk/egui/pull/3595)
|
||||||
|
* Add `#[inline]` to all color-related function [38b4234](https://github.com/emilk/egui/commit/38b4234c3282a7c044c18b77234ee8c204efe171)
|
||||||
|
|
||||||
|
|
||||||
## 0.22.0 - 2023-05-23
|
## 0.22.0 - 2023-05-23
|
||||||
* Nothing new
|
* Nothing new
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ecolor"
|
name = "ecolor"
|
||||||
version = "0.23.0"
|
version.workspace = true
|
||||||
authors = [
|
authors = [
|
||||||
"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>",
|
"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>",
|
||||||
"Andreas Reich <reichandreas@gmx.de>",
|
"Andreas Reich <reichandreas@gmx.de>",
|
||||||
]
|
]
|
||||||
description = "Color structs and color conversion utilities"
|
description = "Color structs and color conversion utilities"
|
||||||
edition = "2021"
|
edition.workspace = true
|
||||||
rust-version = "1.72"
|
rust-version.workspace = true
|
||||||
homepage = "https://github.com/emilk/egui"
|
homepage = "https://github.com/emilk/egui"
|
||||||
license = "MIT OR Apache-2.0"
|
license.workspace = true
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/emilk/egui"
|
repository = "https://github.com/emilk/egui"
|
||||||
categories = ["mathematics", "encoding"]
|
categories = ["mathematics", "encoding"]
|
||||||
|
|||||||
@@ -6,6 +6,56 @@ NOTE: [`egui-winit`](../egui-winit/CHANGELOG.md), [`egui_glium`](../egui_glium/C
|
|||||||
This file is updated upon each release.
|
This file is updated upon each release.
|
||||||
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.24.0 - 2023-11-23
|
||||||
|
* Multiple viewports/windows [#3172](https://github.com/emilk/egui/pull/3172) (thanks [@konkitoman](https://github.com/konkitoman)!)
|
||||||
|
* Replace `eframe::Frame` commands and `WindowInfo` with egui [#3564](https://github.com/emilk/egui/pull/3564)
|
||||||
|
* Use `egui::ViewportBuilder` in `eframe::NativeOptions` [#3572](https://github.com/emilk/egui/pull/3572)
|
||||||
|
* Remove warm-starting [#3574](https://github.com/emilk/egui/pull/3574)
|
||||||
|
* Fix copy and cut on Safari [#3513](https://github.com/emilk/egui/pull/3513) (thanks [@lunixbochs](https://github.com/lunixbochs)!)
|
||||||
|
* Update puffin to 0.18 [#3600](https://github.com/emilk/egui/pull/3600)
|
||||||
|
* Update MSRV to Rust 1.72 [#3595](https://github.com/emilk/egui/pull/3595)
|
||||||
|
|
||||||
|
### Breaking changes:
|
||||||
|
Most settings in `NativeOptions` have been moved to `NativeOptions::viewport`, which uses the new `egui::ViewportBuilder`:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
let native_options = eframe::nativeOptions {
|
||||||
|
- initial_window_size: Some(egui::vec2(320.0, 240.0)),
|
||||||
|
- drag_and_drop_support: true,
|
||||||
|
+ viewport: egui::ViewportBuilder::default()
|
||||||
|
+ .with_inner_size([320.0, 240.0])
|
||||||
|
+ .with_drag_and_drop(true),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
`NativeOptions::fullsize_content` has been replaced with four settings: `ViewportBuilder::with_fullsize_content_view`, `with_title_shown`, `with_titlebar_shown`, `with_titlebar_buttons_shown`
|
||||||
|
|
||||||
|
`frame.info().window_info` is gone, replaced with `ctx.input(|i| i.viewport())`.
|
||||||
|
|
||||||
|
`frame.info().native_pixels_per_point` is replaced with `ctx.input(|i| i.raw.native_pixels_per_point)`.
|
||||||
|
|
||||||
|
Most commands in `eframe::Frame` has been replaced with `egui::ViewportCommand`, so So `frame.close()` becomes `ctx.send_viewport_cmd(ViewportCommand::Close)`, etc.
|
||||||
|
|
||||||
|
`App::on_close_event` has been replaced with `ctx.input(|i| i.viewport().close_requested())` and `ctx.send_viewport_cmd(ViewportCommand::CancelClose)`.
|
||||||
|
|
||||||
|
`eframe::IconData` is now `egui::IconData`.
|
||||||
|
|
||||||
|
`eframe::IconData::try_from_png_bytes` is now `eframe::icon_data::from_png_bytes`.
|
||||||
|
|
||||||
|
`App::post_rendering` is gone. Screenshots are taken with `ctx.send_viewport_cmd(ViewportCommand::Screenshots)` and are returned in `egui::Event` which you can check with:
|
||||||
|
``` rust
|
||||||
|
ui.input(|i| {
|
||||||
|
for event in &i.raw.events {
|
||||||
|
if let egui::Event::Screenshot { viewport_id, image } = event {
|
||||||
|
// handle it here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## 0.23.0 - 2023-09-27
|
## 0.23.0 - 2023-09-27
|
||||||
* Update MSRV to Rust 1.70.0 [#3310](https://github.com/emilk/egui/pull/3310)
|
* Update MSRV to Rust 1.70.0 [#3310](https://github.com/emilk/egui/pull/3310)
|
||||||
* Update to puffin 0.16 [#3144](https://github.com/emilk/egui/pull/3144)
|
* Update to puffin 0.16 [#3144](https://github.com/emilk/egui/pull/3144)
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "eframe"
|
name = "eframe"
|
||||||
version = "0.23.0"
|
version.workspace = true
|
||||||
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
||||||
description = "egui framework - write GUI apps that compiles to web and/or natively"
|
description = "egui framework - write GUI apps that compiles to web and/or natively"
|
||||||
edition = "2021"
|
edition.workspace = true
|
||||||
rust-version = "1.72"
|
rust-version.workspace = true
|
||||||
homepage = "https://github.com/emilk/egui/tree/master/crates/eframe"
|
homepage = "https://github.com/emilk/egui/tree/master/crates/eframe"
|
||||||
license = "MIT OR Apache-2.0"
|
license.workspace = true
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/emilk/egui/tree/master/crates/eframe"
|
repository = "https://github.com/emilk/egui/tree/master/crates/eframe"
|
||||||
categories = ["gui", "game-development"]
|
categories = ["gui", "game-development"]
|
||||||
@@ -98,7 +98,7 @@ x11 = ["egui-winit/x11"]
|
|||||||
__screenshot = []
|
__screenshot = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
egui = { version = "0.23.0", path = "../egui", default-features = false, features = [
|
egui = { version = "0.24.0", path = "../egui", default-features = false, features = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"log",
|
"log",
|
||||||
] }
|
] }
|
||||||
@@ -111,7 +111,7 @@ thiserror.workspace = true
|
|||||||
## Enable this when generating docs.
|
## Enable this when generating docs.
|
||||||
document-features = { version = "0.2", optional = true }
|
document-features = { version = "0.2", optional = true }
|
||||||
|
|
||||||
egui_glow = { version = "0.23.0", path = "../egui_glow", optional = true, default-features = false }
|
egui_glow = { version = "0.24.0", path = "../egui_glow", optional = true, default-features = false }
|
||||||
glow = { version = "0.12", optional = true }
|
glow = { version = "0.12", optional = true }
|
||||||
ron = { version = "0.8", optional = true, features = ["integer128"] }
|
ron = { version = "0.8", optional = true, features = ["integer128"] }
|
||||||
serde = { version = "1", optional = true, features = ["derive"] }
|
serde = { version = "1", optional = true, features = ["derive"] }
|
||||||
@@ -119,7 +119,7 @@ serde = { version = "1", optional = true, features = ["derive"] }
|
|||||||
# -------------------------------------------
|
# -------------------------------------------
|
||||||
# native:
|
# native:
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
egui-winit = { version = "0.23.0", path = "../egui-winit", default-features = false, features = [
|
egui-winit = { version = "0.24.0", path = "../egui-winit", default-features = false, features = [
|
||||||
"clipboard",
|
"clipboard",
|
||||||
"links",
|
"links",
|
||||||
] }
|
] }
|
||||||
@@ -131,7 +131,7 @@ winit = { version = "0.29", default-features = false, features = ["rwh_05"] }
|
|||||||
|
|
||||||
# optional native:
|
# optional native:
|
||||||
directories-next = { version = "2", optional = true }
|
directories-next = { version = "2", optional = true }
|
||||||
egui-wgpu = { version = "0.23.0", path = "../egui-wgpu", optional = true, features = [
|
egui-wgpu = { version = "0.24.0", path = "../egui-wgpu", optional = true, features = [
|
||||||
"winit",
|
"winit",
|
||||||
] } # if wgpu is used, use it with winit
|
] } # if wgpu is used, use it with winit
|
||||||
pollster = { version = "0.3", optional = true } # needed for wgpu
|
pollster = { version = "0.3", optional = true } # needed for wgpu
|
||||||
@@ -205,7 +205,7 @@ web-sys = { version = "0.3.58", features = [
|
|||||||
] }
|
] }
|
||||||
|
|
||||||
# optional web:
|
# optional web:
|
||||||
egui-wgpu = { version = "0.23.0", path = "../egui-wgpu", optional = true } # if wgpu is used, use it without (!) winit
|
egui-wgpu = { version = "0.24.0", path = "../egui-wgpu", optional = true } # if wgpu is used, use it without (!) winit
|
||||||
raw-window-handle = { workspace = true, optional = true }
|
raw-window-handle = { workspace = true, optional = true }
|
||||||
tts = { version = "0.25", optional = true, default-features = false }
|
tts = { version = "0.25", optional = true, default-features = false }
|
||||||
wgpu = { workspace = true, optional = true }
|
wgpu = { workspace = true, optional = true }
|
||||||
|
|||||||
@@ -149,25 +149,10 @@ pub trait App {
|
|||||||
/// On native the path is picked using [`crate::storage_dir`].
|
/// On native the path is picked using [`crate::storage_dir`].
|
||||||
fn save(&mut self, _storage: &mut dyn Storage) {}
|
fn save(&mut self, _storage: &mut dyn Storage) {}
|
||||||
|
|
||||||
/// Called when the user attempts to close the desktop window and/or quit the application.
|
|
||||||
///
|
|
||||||
/// By returning `false` the closing will be aborted. To continue the closing return `true`.
|
|
||||||
///
|
|
||||||
/// A scenario where this method will be run is after pressing the close button on a native
|
|
||||||
/// window, which allows you to ask the user whether they want to do something before exiting.
|
|
||||||
/// See the example at <https://github.com/emilk/egui/blob/master/examples/confirm_exit/> for practical usage.
|
|
||||||
///
|
|
||||||
/// It will _not_ be called on the web or when the window is forcefully closed.
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
#[doc(alias = "exit")]
|
|
||||||
#[doc(alias = "quit")]
|
|
||||||
fn on_close_event(&mut self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called once on shutdown, after [`Self::save`].
|
/// Called once on shutdown, after [`Self::save`].
|
||||||
///
|
///
|
||||||
/// If you need to abort an exit use [`Self::on_close_event`].
|
/// If you need to abort an exit check `ctx.input(|i| i.viewport().close_requested())`
|
||||||
|
/// and respond with [`egui::ViewportCommand::CancelClose`].
|
||||||
///
|
///
|
||||||
/// To get a [`glow`] context you need to compile with the `glow` feature flag,
|
/// To get a [`glow`] context you need to compile with the `glow` feature flag,
|
||||||
/// and run eframe with the glow backend.
|
/// and run eframe with the glow backend.
|
||||||
@@ -248,6 +233,9 @@ pub struct NativeOptions {
|
|||||||
/// Controls the native window of the root viewport.
|
/// Controls the native window of the root viewport.
|
||||||
///
|
///
|
||||||
/// This is where you set things like window title and size.
|
/// This is where you set things like window title and size.
|
||||||
|
///
|
||||||
|
/// If you don't set an icon, a default egui icon will be used.
|
||||||
|
/// To avoid this, set the icon to [`egui::IconData::default`].
|
||||||
pub viewport: egui::ViewportBuilder,
|
pub viewport: egui::ViewportBuilder,
|
||||||
|
|
||||||
/// Turn on vertical syncing, limiting the FPS to the display refresh rate.
|
/// Turn on vertical syncing, limiting the FPS to the display refresh rate.
|
||||||
@@ -379,13 +367,7 @@ impl Clone for NativeOptions {
|
|||||||
impl Default for NativeOptions {
|
impl Default for NativeOptions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
viewport: egui::ViewportBuilder {
|
viewport: Default::default(),
|
||||||
icon: Some(std::sync::Arc::new(
|
|
||||||
crate::icon_data::from_png_bytes(&include_bytes!("../data/icon.png")[..])
|
|
||||||
.unwrap(),
|
|
||||||
)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
|
|
||||||
vsync: true,
|
vsync: true,
|
||||||
multisampling: 0,
|
multisampling: 0,
|
||||||
|
|||||||
@@ -13,7 +13,13 @@ pub struct AppTitleIconSetter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AppTitleIconSetter {
|
impl AppTitleIconSetter {
|
||||||
pub fn new(title: String, icon_data: Option<Arc<IconData>>) -> Self {
|
pub fn new(title: String, mut icon_data: Option<Arc<IconData>>) -> Self {
|
||||||
|
if let Some(icon) = &icon_data {
|
||||||
|
if **icon == IconData::default() {
|
||||||
|
icon_data = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
title,
|
title,
|
||||||
icon_data,
|
icon_data,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use egui_winit::{EventResponse, WindowSettings};
|
|||||||
use crate::{epi, Theme};
|
use crate::{epi, Theme};
|
||||||
|
|
||||||
pub fn viewport_builder<E>(
|
pub fn viewport_builder<E>(
|
||||||
|
egui_zoom_factor: f32,
|
||||||
event_loop: &EventLoopWindowTarget<E>,
|
event_loop: &EventLoopWindowTarget<E>,
|
||||||
native_options: &mut epi::NativeOptions,
|
native_options: &mut epi::NativeOptions,
|
||||||
window_settings: Option<WindowSettings>,
|
window_settings: Option<WindowSettings>,
|
||||||
@@ -26,8 +27,9 @@ pub fn viewport_builder<E>(
|
|||||||
let inner_size_points = if let Some(mut window_settings) = window_settings {
|
let inner_size_points = if let Some(mut window_settings) = window_settings {
|
||||||
// Restore pos/size from previous session
|
// Restore pos/size from previous session
|
||||||
|
|
||||||
window_settings.clamp_size_to_sane_values(largest_monitor_point_size(event_loop));
|
window_settings
|
||||||
window_settings.clamp_position_to_monitors(event_loop);
|
.clamp_size_to_sane_values(largest_monitor_point_size(egui_zoom_factor, event_loop));
|
||||||
|
window_settings.clamp_position_to_monitors(egui_zoom_factor, event_loop);
|
||||||
|
|
||||||
viewport_builder = window_settings.initialize_viewport_builder(viewport_builder);
|
viewport_builder = window_settings.initialize_viewport_builder(viewport_builder);
|
||||||
window_settings.inner_size_points()
|
window_settings.inner_size_points()
|
||||||
@@ -37,8 +39,8 @@ pub fn viewport_builder<E>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(initial_window_size) = viewport_builder.inner_size {
|
if let Some(initial_window_size) = viewport_builder.inner_size {
|
||||||
let initial_window_size =
|
let initial_window_size = initial_window_size
|
||||||
initial_window_size.at_most(largest_monitor_point_size(event_loop));
|
.at_most(largest_monitor_point_size(egui_zoom_factor, event_loop));
|
||||||
viewport_builder = viewport_builder.with_inner_size(initial_window_size);
|
viewport_builder = viewport_builder.with_inner_size(initial_window_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,9 +51,11 @@ pub fn viewport_builder<E>(
|
|||||||
if native_options.centered {
|
if native_options.centered {
|
||||||
crate::profile_scope!("center");
|
crate::profile_scope!("center");
|
||||||
if let Some(monitor) = event_loop.available_monitors().next() {
|
if let Some(monitor) = event_loop.available_monitors().next() {
|
||||||
let monitor_size = monitor.size().to_logical::<f32>(monitor.scale_factor());
|
let monitor_size = monitor
|
||||||
|
.size()
|
||||||
|
.to_logical::<f32>(egui_zoom_factor as f64 * monitor.scale_factor());
|
||||||
let inner_size = inner_size_points.unwrap_or(egui::Vec2 { x: 800.0, y: 600.0 });
|
let inner_size = inner_size_points.unwrap_or(egui::Vec2 { x: 800.0, y: 600.0 });
|
||||||
if monitor_size.width > 0.0 && monitor_size.height > 0.0 {
|
if 0.0 < monitor_size.width && 0.0 < monitor_size.height {
|
||||||
let x = (monitor_size.width - inner_size.x) / 2.0;
|
let x = (monitor_size.width - inner_size.x) / 2.0;
|
||||||
let y = (monitor_size.height - inner_size.y) / 2.0;
|
let y = (monitor_size.height - inner_size.y) / 2.0;
|
||||||
viewport_builder = viewport_builder.with_position([x, y]);
|
viewport_builder = viewport_builder.with_position([x, y]);
|
||||||
@@ -76,7 +80,10 @@ pub fn apply_window_settings(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn largest_monitor_point_size<E>(event_loop: &EventLoopWindowTarget<E>) -> egui::Vec2 {
|
fn largest_monitor_point_size<E>(
|
||||||
|
egui_zoom_factor: f32,
|
||||||
|
event_loop: &EventLoopWindowTarget<E>,
|
||||||
|
) -> egui::Vec2 {
|
||||||
crate::profile_function!();
|
crate::profile_function!();
|
||||||
|
|
||||||
let mut max_size = egui::Vec2::ZERO;
|
let mut max_size = egui::Vec2::ZERO;
|
||||||
@@ -87,7 +94,9 @@ fn largest_monitor_point_size<E>(event_loop: &EventLoopWindowTarget<E>) -> egui:
|
|||||||
};
|
};
|
||||||
|
|
||||||
for monitor in available_monitors {
|
for monitor in available_monitors {
|
||||||
let size = monitor.size().to_logical::<f32>(monitor.scale_factor());
|
let size = monitor
|
||||||
|
.size()
|
||||||
|
.to_logical::<f32>(egui_zoom_factor as f64 * monitor.scale_factor());
|
||||||
let size = egui::vec2(size.width, size.height);
|
let size = egui::vec2(size.width, size.height);
|
||||||
max_size = max_size.max(size);
|
max_size = max_size.max(size);
|
||||||
}
|
}
|
||||||
@@ -137,21 +146,15 @@ pub struct EpiIntegration {
|
|||||||
impl EpiIntegration {
|
impl EpiIntegration {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
egui_ctx: egui::Context,
|
||||||
window: &winit::window::Window,
|
window: &winit::window::Window,
|
||||||
system_theme: Option<Theme>,
|
system_theme: Option<Theme>,
|
||||||
app_name: &str,
|
app_name: &str,
|
||||||
native_options: &crate::NativeOptions,
|
native_options: &crate::NativeOptions,
|
||||||
storage: Option<Box<dyn epi::Storage>>,
|
storage: Option<Box<dyn epi::Storage>>,
|
||||||
is_desktop: bool,
|
|
||||||
#[cfg(feature = "glow")] gl: Option<std::rc::Rc<glow::Context>>,
|
#[cfg(feature = "glow")] gl: Option<std::rc::Rc<glow::Context>>,
|
||||||
#[cfg(feature = "wgpu")] wgpu_render_state: Option<egui_wgpu::RenderState>,
|
#[cfg(feature = "wgpu")] wgpu_render_state: Option<egui_wgpu::RenderState>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let egui_ctx = egui::Context::default();
|
|
||||||
egui_ctx.set_embed_viewports(!is_desktop);
|
|
||||||
|
|
||||||
let memory = load_egui_memory(storage.as_deref()).unwrap_or_default();
|
|
||||||
egui_ctx.memory_mut(|mem| *mem = memory);
|
|
||||||
|
|
||||||
let frame = epi::Frame {
|
let frame = epi::Frame {
|
||||||
info: epi::IntegrationInfo {
|
info: epi::IntegrationInfo {
|
||||||
system_theme,
|
system_theme,
|
||||||
@@ -166,13 +169,19 @@ impl EpiIntegration {
|
|||||||
raw_window_handle: window.raw_window_handle(),
|
raw_window_handle: window.raw_window_handle(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let icon = native_options
|
||||||
|
.viewport
|
||||||
|
.icon
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| std::sync::Arc::new(load_default_egui_icon()));
|
||||||
|
|
||||||
let app_icon_setter = super::app_icon::AppTitleIconSetter::new(
|
let app_icon_setter = super::app_icon::AppTitleIconSetter::new(
|
||||||
native_options
|
native_options
|
||||||
.viewport
|
.viewport
|
||||||
.title
|
.title
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| app_name.to_owned()),
|
.unwrap_or_else(|| app_name.to_owned()),
|
||||||
native_options.viewport.icon.clone(),
|
Some(icon),
|
||||||
);
|
);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@@ -220,22 +229,14 @@ impl EpiIntegration {
|
|||||||
|
|
||||||
pub fn on_window_event(
|
pub fn on_window_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
app: &mut dyn epi::App,
|
|
||||||
event: &winit::event::WindowEvent,
|
event: &winit::event::WindowEvent,
|
||||||
egui_winit: &mut egui_winit::State,
|
egui_winit: &mut egui_winit::State,
|
||||||
viewport_id: ViewportId,
|
|
||||||
) -> EventResponse {
|
) -> EventResponse {
|
||||||
crate::profile_function!(egui_winit::short_window_event_description(event));
|
crate::profile_function!(egui_winit::short_window_event_description(event));
|
||||||
|
|
||||||
use winit::event::{ElementState, MouseButton, WindowEvent};
|
use winit::event::{ElementState, MouseButton, WindowEvent};
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
WindowEvent::CloseRequested => {
|
|
||||||
if viewport_id == ViewportId::ROOT {
|
|
||||||
self.close = app.on_close_event();
|
|
||||||
log::debug!("App::on_close_event returned {}", self.close);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WindowEvent::Destroyed => {
|
WindowEvent::Destroyed => {
|
||||||
log::debug!("Received WindowEvent::Destroyed");
|
log::debug!("Received WindowEvent::Destroyed");
|
||||||
self.close = true;
|
self.close = true;
|
||||||
@@ -245,9 +246,6 @@ impl EpiIntegration {
|
|||||||
state: ElementState::Pressed,
|
state: ElementState::Pressed,
|
||||||
..
|
..
|
||||||
} => self.can_drag_window = true,
|
} => self.can_drag_window = true,
|
||||||
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
|
|
||||||
egui_winit.egui_input_mut().native_pixels_per_point = Some(*scale_factor as _);
|
|
||||||
}
|
|
||||||
WindowEvent::ThemeChanged(winit_theme) if self.follow_system_theme => {
|
WindowEvent::ThemeChanged(winit_theme) if self.follow_system_theme => {
|
||||||
let theme = theme_from_winit_theme(*winit_theme);
|
let theme = theme_from_winit_theme(*winit_theme);
|
||||||
self.frame.info.system_theme = Some(theme);
|
self.frame.info.system_theme = Some(theme);
|
||||||
@@ -275,23 +273,32 @@ impl EpiIntegration {
|
|||||||
) -> egui::FullOutput {
|
) -> egui::FullOutput {
|
||||||
raw_input.time = Some(self.beginning.elapsed().as_secs_f64());
|
raw_input.time = Some(self.beginning.elapsed().as_secs_f64());
|
||||||
|
|
||||||
|
let close_requested = raw_input.viewport().close_requested();
|
||||||
|
|
||||||
let full_output = self.egui_ctx.run(raw_input, |egui_ctx| {
|
let full_output = self.egui_ctx.run(raw_input, |egui_ctx| {
|
||||||
if let Some(viewport_ui_cb) = viewport_ui_cb {
|
if let Some(viewport_ui_cb) = viewport_ui_cb {
|
||||||
// Child viewport
|
// Child viewport
|
||||||
crate::profile_scope!("viewport_callback");
|
crate::profile_scope!("viewport_callback");
|
||||||
viewport_ui_cb(egui_ctx);
|
viewport_ui_cb(egui_ctx);
|
||||||
} else {
|
} else {
|
||||||
// Root viewport
|
|
||||||
if egui_ctx.input(|i| i.viewport().close_requested()) {
|
|
||||||
self.close = app.on_close_event();
|
|
||||||
log::debug!("App::on_close_event returned {}", self.close);
|
|
||||||
}
|
|
||||||
|
|
||||||
crate::profile_scope!("App::update");
|
crate::profile_scope!("App::update");
|
||||||
app.update(egui_ctx, &mut self.frame);
|
app.update(egui_ctx, &mut self.frame);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let is_root_viewport = viewport_ui_cb.is_none();
|
||||||
|
if is_root_viewport && close_requested {
|
||||||
|
let canceled = full_output.viewport_output[&ViewportId::ROOT]
|
||||||
|
.commands
|
||||||
|
.contains(&egui::ViewportCommand::CancelClose);
|
||||||
|
if canceled {
|
||||||
|
log::debug!("Closing of root viewport canceled with ViewportCommand::CancelClose");
|
||||||
|
} else {
|
||||||
|
log::debug!("Closing root viewport (ViewportCommand::CancelClose was not sent)");
|
||||||
|
self.close = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.pending_full_output.append(full_output);
|
self.pending_full_output.append(full_output);
|
||||||
std::mem::take(&mut self.pending_full_output)
|
std::mem::take(&mut self.pending_full_output)
|
||||||
}
|
}
|
||||||
@@ -309,16 +316,6 @@ impl EpiIntegration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_platform_output(
|
|
||||||
&mut self,
|
|
||||||
window: &winit::window::Window,
|
|
||||||
viewport_id: ViewportId,
|
|
||||||
platform_output: egui::PlatformOutput,
|
|
||||||
egui_winit: &mut egui_winit::State,
|
|
||||||
) {
|
|
||||||
egui_winit.handle_platform_output(window, viewport_id, &self.egui_ctx, platform_output);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Persistence stuff:
|
// Persistence stuff:
|
||||||
|
|
||||||
@@ -346,7 +343,7 @@ impl EpiIntegration {
|
|||||||
epi::set_value(
|
epi::set_value(
|
||||||
storage,
|
storage,
|
||||||
STORAGE_WINDOW_KEY,
|
STORAGE_WINDOW_KEY,
|
||||||
&WindowSettings::from_display(window),
|
&WindowSettings::from_window(self.egui_ctx.zoom_factor(), window),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -366,6 +363,11 @@ impl EpiIntegration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_default_egui_icon() -> egui::IconData {
|
||||||
|
crate::profile_function!();
|
||||||
|
crate::icon_data::from_png_bytes(&include_bytes!("../../data/icon.png")[..]).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "persistence")]
|
#[cfg(feature = "persistence")]
|
||||||
const STORAGE_EGUI_MEMORY_KEY: &str = "egui";
|
const STORAGE_EGUI_MEMORY_KEY: &str = "egui";
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ use egui_winit::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
native::epi_integration::EpiIntegration, App, AppCreator, CreationContext, NativeOptions,
|
native::{epi_integration::EpiIntegration, winit_integration::create_egui_context},
|
||||||
Result, Storage,
|
App, AppCreator, CreationContext, NativeOptions, Result, Storage,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@@ -87,6 +87,8 @@ struct GlowWinitRunning {
|
|||||||
/// The setup is divided between the `new` fn and `on_resume` fn. we can just assume that `on_resume` is a continuation of
|
/// The setup is divided between the `new` fn and `on_resume` fn. we can just assume that `on_resume` is a continuation of
|
||||||
/// `new` fn on all platforms. only on android, do we get multiple resumed events because app can be suspended.
|
/// `new` fn on all platforms. only on android, do we get multiple resumed events because app can be suspended.
|
||||||
struct GlutinWindowContext {
|
struct GlutinWindowContext {
|
||||||
|
egui_ctx: egui::Context,
|
||||||
|
|
||||||
swap_interval: glutin::surface::SwapInterval,
|
swap_interval: glutin::surface::SwapInterval,
|
||||||
gl_config: glutin::config::Config,
|
gl_config: glutin::config::Config,
|
||||||
|
|
||||||
@@ -139,6 +141,7 @@ impl GlowWinitApp {
|
|||||||
|
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
fn create_glutin_windowed_context(
|
fn create_glutin_windowed_context(
|
||||||
|
egui_ctx: &egui::Context,
|
||||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||||
storage: Option<&dyn Storage>,
|
storage: Option<&dyn Storage>,
|
||||||
native_options: &mut NativeOptions,
|
native_options: &mut NativeOptions,
|
||||||
@@ -147,11 +150,16 @@ impl GlowWinitApp {
|
|||||||
|
|
||||||
let window_settings = epi_integration::load_window_settings(storage);
|
let window_settings = epi_integration::load_window_settings(storage);
|
||||||
|
|
||||||
let winit_window_builder =
|
let winit_window_builder = epi_integration::viewport_builder(
|
||||||
epi_integration::viewport_builder(event_loop, native_options, window_settings);
|
egui_ctx.zoom_factor(),
|
||||||
|
event_loop,
|
||||||
|
native_options,
|
||||||
|
window_settings,
|
||||||
|
);
|
||||||
|
|
||||||
let mut glutin_window_context =
|
let mut glutin_window_context = unsafe {
|
||||||
unsafe { GlutinWindowContext::new(winit_window_builder, native_options, event_loop)? };
|
GlutinWindowContext::new(egui_ctx, winit_window_builder, native_options, event_loop)?
|
||||||
|
};
|
||||||
|
|
||||||
// Creates the window - must come before we create our glow context
|
// Creates the window - must come before we create our glow context
|
||||||
glutin_window_context.on_resume(event_loop)?;
|
glutin_window_context.on_resume(event_loop)?;
|
||||||
@@ -191,7 +199,10 @@ impl GlowWinitApp {
|
|||||||
.unwrap_or(&self.app_name),
|
.unwrap_or(&self.app_name),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let egui_ctx = create_egui_context(storage.as_deref());
|
||||||
|
|
||||||
let (mut glutin, painter) = Self::create_glutin_windowed_context(
|
let (mut glutin, painter) = Self::create_glutin_windowed_context(
|
||||||
|
&egui_ctx,
|
||||||
event_loop,
|
event_loop,
|
||||||
storage.as_deref(),
|
storage.as_deref(),
|
||||||
&mut self.native_options,
|
&mut self.native_options,
|
||||||
@@ -210,12 +221,12 @@ impl GlowWinitApp {
|
|||||||
winit_integration::system_theme(&glutin.window(ViewportId::ROOT), &self.native_options);
|
winit_integration::system_theme(&glutin.window(ViewportId::ROOT), &self.native_options);
|
||||||
|
|
||||||
let integration = EpiIntegration::new(
|
let integration = EpiIntegration::new(
|
||||||
|
egui_ctx,
|
||||||
&glutin.window(ViewportId::ROOT),
|
&glutin.window(ViewportId::ROOT),
|
||||||
system_theme,
|
system_theme,
|
||||||
&self.app_name,
|
&self.app_name,
|
||||||
&self.native_options,
|
&self.native_options,
|
||||||
storage,
|
storage,
|
||||||
winit_integration::IS_DESKTOP,
|
|
||||||
Some(gl.clone()),
|
Some(gl.clone()),
|
||||||
#[cfg(feature = "wgpu")]
|
#[cfg(feature = "wgpu")]
|
||||||
None,
|
None,
|
||||||
@@ -498,7 +509,7 @@ impl GlowWinitRunning {
|
|||||||
let window = viewport.window.as_ref().unwrap();
|
let window = viewport.window.as_ref().unwrap();
|
||||||
|
|
||||||
let egui_winit = viewport.egui_winit.as_mut().unwrap();
|
let egui_winit = viewport.egui_winit.as_mut().unwrap();
|
||||||
let mut raw_input = egui_winit.take_egui_input(window, viewport.ids);
|
let mut raw_input = egui_winit.take_egui_input(window);
|
||||||
let viewport_ui_cb = viewport.viewport_ui_cb.clone();
|
let viewport_ui_cb = viewport.viewport_ui_cb.clone();
|
||||||
|
|
||||||
self.integration.pre_update();
|
self.integration.pre_update();
|
||||||
@@ -549,12 +560,13 @@ impl GlowWinitRunning {
|
|||||||
} = &mut *glutin;
|
} = &mut *glutin;
|
||||||
|
|
||||||
let viewport = viewports.get_mut(&viewport_id).unwrap();
|
let viewport = viewports.get_mut(&viewport_id).unwrap();
|
||||||
|
viewport.info.events.clear(); // they should have been processed
|
||||||
let window = viewport.window.as_ref().unwrap();
|
let window = viewport.window.as_ref().unwrap();
|
||||||
let gl_surface = viewport.gl_surface.as_ref().unwrap();
|
let gl_surface = viewport.gl_surface.as_ref().unwrap();
|
||||||
let egui_winit = viewport.egui_winit.as_mut().unwrap();
|
let egui_winit = viewport.egui_winit.as_mut().unwrap();
|
||||||
|
|
||||||
integration.post_update();
|
integration.post_update();
|
||||||
integration.handle_platform_output(window, viewport_id, platform_output, egui_winit);
|
egui_winit.handle_platform_output(window, &integration.egui_ctx, platform_output);
|
||||||
|
|
||||||
let clipped_primitives = integration.egui_ctx.tessellate(shapes, pixels_per_point);
|
let clipped_primitives = integration.egui_ctx.tessellate(shapes, pixels_per_point);
|
||||||
|
|
||||||
@@ -632,7 +644,7 @@ impl GlowWinitRunning {
|
|||||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
glutin.handle_viewport_output(viewport_output);
|
glutin.handle_viewport_output(&integration.egui_ctx, viewport_output);
|
||||||
|
|
||||||
if integration.should_close() {
|
if integration.should_close() {
|
||||||
EventResult::Exit
|
EventResult::Exit
|
||||||
@@ -728,12 +740,9 @@ impl GlowWinitRunning {
|
|||||||
};
|
};
|
||||||
if let Some(viewport_id) = viewport_id {
|
if let Some(viewport_id) = viewport_id {
|
||||||
if let Some(viewport) = glutin.viewports.get_mut(&viewport_id) {
|
if let Some(viewport) = glutin.viewports.get_mut(&viewport_id) {
|
||||||
event_response = self.integration.on_window_event(
|
event_response = self
|
||||||
self.app.as_mut(),
|
.integration
|
||||||
event,
|
.on_window_event(event, viewport.egui_winit.as_mut().unwrap());
|
||||||
viewport.egui_winit.as_mut().unwrap(),
|
|
||||||
viewport.ids.this,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,6 +761,7 @@ impl GlowWinitRunning {
|
|||||||
impl GlutinWindowContext {
|
impl GlutinWindowContext {
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
unsafe fn new(
|
unsafe fn new(
|
||||||
|
egui_ctx: &egui::Context,
|
||||||
viewport_builder: ViewportBuilder,
|
viewport_builder: ViewportBuilder,
|
||||||
native_options: &NativeOptions,
|
native_options: &NativeOptions,
|
||||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||||
@@ -803,7 +813,11 @@ impl GlutinWindowContext {
|
|||||||
let display_builder = glutin_winit::DisplayBuilder::new()
|
let display_builder = glutin_winit::DisplayBuilder::new()
|
||||||
// we might want to expose this option to users in the future. maybe using an env var or using native_options.
|
// we might want to expose this option to users in the future. maybe using an env var or using native_options.
|
||||||
.with_preference(glutin_winit::ApiPreference::FallbackEgl) // https://github.com/emilk/egui/issues/2520#issuecomment-1367841150
|
.with_preference(glutin_winit::ApiPreference::FallbackEgl) // https://github.com/emilk/egui/issues/2520#issuecomment-1367841150
|
||||||
.with_window_builder(Some(create_winit_window_builder(viewport_builder.clone())));
|
.with_window_builder(Some(create_winit_window_builder(
|
||||||
|
egui_ctx,
|
||||||
|
event_loop,
|
||||||
|
viewport_builder.clone(),
|
||||||
|
)));
|
||||||
|
|
||||||
let (window, gl_config) = {
|
let (window, gl_config) = {
|
||||||
crate::profile_scope!("DisplayBuilder::build");
|
crate::profile_scope!("DisplayBuilder::build");
|
||||||
@@ -899,6 +913,7 @@ impl GlutinWindowContext {
|
|||||||
// https://github.com/emilk/egui/pull/2541#issuecomment-1370767582
|
// https://github.com/emilk/egui/pull/2541#issuecomment-1370767582
|
||||||
|
|
||||||
let mut slf = GlutinWindowContext {
|
let mut slf = GlutinWindowContext {
|
||||||
|
egui_ctx: egui_ctx.clone(),
|
||||||
swap_interval,
|
swap_interval,
|
||||||
gl_config,
|
gl_config,
|
||||||
current_gl_context: None,
|
current_gl_context: None,
|
||||||
@@ -958,7 +973,7 @@ impl GlutinWindowContext {
|
|||||||
log::trace!("Window doesn't exist yet. Creating one now with finalize_window");
|
log::trace!("Window doesn't exist yet. Creating one now with finalize_window");
|
||||||
let window = glutin_winit::finalize_window(
|
let window = glutin_winit::finalize_window(
|
||||||
event_loop,
|
event_loop,
|
||||||
create_winit_window_builder(viewport.builder.clone()),
|
create_winit_window_builder(&self.egui_ctx, event_loop, viewport.builder.clone()),
|
||||||
&self.gl_config,
|
&self.gl_config,
|
||||||
)?;
|
)?;
|
||||||
apply_viewport_builder_to_new_window(&window, &viewport.builder);
|
apply_viewport_builder_to_new_window(&window, &viewport.builder);
|
||||||
@@ -1009,6 +1024,7 @@ impl GlutinWindowContext {
|
|||||||
|
|
||||||
viewport.egui_winit.get_or_insert_with(|| {
|
viewport.egui_winit.get_or_insert_with(|| {
|
||||||
egui_winit::State::new(
|
egui_winit::State::new(
|
||||||
|
viewport_id,
|
||||||
event_loop,
|
event_loop,
|
||||||
Some(window.scale_factor() as f32),
|
Some(window.scale_factor() as f32),
|
||||||
self.max_texture_side,
|
self.max_texture_side,
|
||||||
@@ -1085,7 +1101,11 @@ impl GlutinWindowContext {
|
|||||||
self.gl_config.display().get_proc_address(addr)
|
self.gl_config.display().get_proc_address(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_viewport_output(&mut self, viewport_output: ViewportIdMap<ViewportOutput>) {
|
fn handle_viewport_output(
|
||||||
|
&mut self,
|
||||||
|
egui_ctx: &egui::Context,
|
||||||
|
viewport_output: ViewportIdMap<ViewportOutput>,
|
||||||
|
) {
|
||||||
crate::profile_function!();
|
crate::profile_function!();
|
||||||
|
|
||||||
let active_viewports_ids: ViewportIdSet = viewport_output.keys().copied().collect();
|
let active_viewports_ids: ViewportIdSet = viewport_output.keys().copied().collect();
|
||||||
@@ -1105,6 +1125,7 @@ impl GlutinWindowContext {
|
|||||||
let ids = ViewportIdPair::from_self_and_parent(viewport_id, parent);
|
let ids = ViewportIdPair::from_self_and_parent(viewport_id, parent);
|
||||||
|
|
||||||
let viewport = initialize_or_update_viewport(
|
let viewport = initialize_or_update_viewport(
|
||||||
|
egui_ctx,
|
||||||
&mut self.viewports,
|
&mut self.viewports,
|
||||||
ids,
|
ids,
|
||||||
class,
|
class,
|
||||||
@@ -1116,6 +1137,7 @@ impl GlutinWindowContext {
|
|||||||
if let Some(window) = &viewport.window {
|
if let Some(window) = &viewport.window {
|
||||||
let is_viewport_focused = self.focused_viewport == Some(viewport_id);
|
let is_viewport_focused = self.focused_viewport == Some(viewport_id);
|
||||||
egui_winit::process_viewport_commands(
|
egui_winit::process_viewport_commands(
|
||||||
|
egui_ctx,
|
||||||
&mut viewport.info,
|
&mut viewport.info,
|
||||||
commands,
|
commands,
|
||||||
window,
|
window,
|
||||||
@@ -1148,14 +1170,15 @@ impl Viewport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_or_update_viewport(
|
fn initialize_or_update_viewport<'vp>(
|
||||||
viewports: &mut ViewportIdMap<Viewport>,
|
egu_ctx: &'_ egui::Context,
|
||||||
|
viewports: &'vp mut ViewportIdMap<Viewport>,
|
||||||
ids: ViewportIdPair,
|
ids: ViewportIdPair,
|
||||||
class: ViewportClass,
|
class: ViewportClass,
|
||||||
mut builder: ViewportBuilder,
|
mut builder: ViewportBuilder,
|
||||||
viewport_ui_cb: Option<Arc<dyn Fn(&egui::Context) + Send + Sync>>,
|
viewport_ui_cb: Option<Arc<dyn Fn(&egui::Context) + Send + Sync>>,
|
||||||
focused_viewport: Option<ViewportId>,
|
focused_viewport: Option<ViewportId>,
|
||||||
) -> &mut Viewport {
|
) -> &'vp mut Viewport {
|
||||||
crate::profile_function!();
|
crate::profile_function!();
|
||||||
|
|
||||||
if builder.icon.is_none() {
|
if builder.icon.is_none() {
|
||||||
@@ -1190,19 +1213,20 @@ fn initialize_or_update_viewport(
|
|||||||
viewport.class = class;
|
viewport.class = class;
|
||||||
viewport.viewport_ui_cb = viewport_ui_cb;
|
viewport.viewport_ui_cb = viewport_ui_cb;
|
||||||
|
|
||||||
let (delta_commands, recreate) = viewport.builder.patch(&builder);
|
let (delta_commands, recreate) = viewport.builder.patch(builder);
|
||||||
|
|
||||||
if recreate {
|
if recreate {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"Recreating window for viewport {:?} ({:?})",
|
"Recreating window for viewport {:?} ({:?})",
|
||||||
ids.this,
|
ids.this,
|
||||||
builder.title
|
viewport.builder.title
|
||||||
);
|
);
|
||||||
viewport.window = None;
|
viewport.window = None;
|
||||||
viewport.egui_winit = None;
|
viewport.egui_winit = None;
|
||||||
} else if let Some(window) = &viewport.window {
|
} else if let Some(window) = &viewport.window {
|
||||||
let is_viewport_focused = focused_viewport == Some(ids.this);
|
let is_viewport_focused = focused_viewport == Some(ids.this);
|
||||||
process_viewport_commands(
|
process_viewport_commands(
|
||||||
|
egu_ctx,
|
||||||
&mut viewport.info,
|
&mut viewport.info,
|
||||||
delta_commands,
|
delta_commands,
|
||||||
window,
|
window,
|
||||||
@@ -1238,6 +1262,7 @@ fn render_immediate_viewport(
|
|||||||
let mut glutin = glutin.borrow_mut();
|
let mut glutin = glutin.borrow_mut();
|
||||||
|
|
||||||
let viewport = initialize_or_update_viewport(
|
let viewport = initialize_or_update_viewport(
|
||||||
|
egui_ctx,
|
||||||
&mut glutin.viewports,
|
&mut glutin.viewports,
|
||||||
ids,
|
ids,
|
||||||
ViewportClass::Immediate,
|
ViewportClass::Immediate,
|
||||||
@@ -1267,7 +1292,7 @@ fn render_immediate_viewport(
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut raw_input = winit_state.take_egui_input(window, ids);
|
let mut raw_input = winit_state.take_egui_input(window);
|
||||||
raw_input.viewports = glutin
|
raw_input.viewports = glutin
|
||||||
.viewports
|
.viewports
|
||||||
.iter()
|
.iter()
|
||||||
@@ -1304,6 +1329,7 @@ fn render_immediate_viewport(
|
|||||||
let Some(viewport) = viewports.get_mut(&ids.this) else {
|
let Some(viewport) = viewports.get_mut(&ids.this) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
viewport.info.events.clear(); // they should have been processed
|
||||||
|
|
||||||
let Some(winit_state) = &mut viewport.egui_winit else {
|
let Some(winit_state) = &mut viewport.egui_winit else {
|
||||||
return;
|
return;
|
||||||
@@ -1351,9 +1377,9 @@ fn render_immediate_viewport(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
winit_state.handle_platform_output(window, ids.this, egui_ctx, platform_output);
|
winit_state.handle_platform_output(window, egui_ctx, platform_output);
|
||||||
|
|
||||||
glutin.handle_viewport_output(viewport_output);
|
glutin.handle_viewport_output(egui_ctx, viewport_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "__screenshot")]
|
#[cfg(feature = "__screenshot")]
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ struct WgpuWinitRunning {
|
|||||||
///
|
///
|
||||||
/// Wrapped in an `Rc<RefCell<…>>` so it can be re-entrantly shared via a weak-pointer.
|
/// Wrapped in an `Rc<RefCell<…>>` so it can be re-entrantly shared via a weak-pointer.
|
||||||
pub struct SharedState {
|
pub struct SharedState {
|
||||||
|
egui_ctx: egui::Context,
|
||||||
viewports: Viewports,
|
viewports: Viewports,
|
||||||
painter: egui_wgpu::winit::Painter,
|
painter: egui_wgpu::winit::Painter,
|
||||||
viewport_from_window: HashMap<WindowId, ViewportId>,
|
viewport_from_window: HashMap<WindowId, ViewportId>,
|
||||||
@@ -123,7 +124,12 @@ impl WgpuWinitApp {
|
|||||||
|
|
||||||
for viewport in viewports.values_mut() {
|
for viewport in viewports.values_mut() {
|
||||||
if viewport.window.is_none() {
|
if viewport.window.is_none() {
|
||||||
viewport.init_window(viewport_from_window, painter, event_loop);
|
viewport.init_window(
|
||||||
|
&running.integration.egui_ctx,
|
||||||
|
viewport_from_window,
|
||||||
|
painter,
|
||||||
|
event_loop,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,6 +146,7 @@ impl WgpuWinitApp {
|
|||||||
|
|
||||||
fn init_run_state(
|
fn init_run_state(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
egui_ctx: egui::Context,
|
||||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||||
storage: Option<Box<dyn Storage>>,
|
storage: Option<Box<dyn Storage>>,
|
||||||
window: Window,
|
window: Window,
|
||||||
@@ -163,12 +170,12 @@ impl WgpuWinitApp {
|
|||||||
|
|
||||||
let system_theme = winit_integration::system_theme(&window, &self.native_options);
|
let system_theme = winit_integration::system_theme(&window, &self.native_options);
|
||||||
let integration = EpiIntegration::new(
|
let integration = EpiIntegration::new(
|
||||||
|
egui_ctx.clone(),
|
||||||
&window,
|
&window,
|
||||||
system_theme,
|
system_theme,
|
||||||
&self.app_name,
|
&self.app_name,
|
||||||
&self.native_options,
|
&self.native_options,
|
||||||
storage,
|
storage,
|
||||||
winit_integration::IS_DESKTOP,
|
|
||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
None,
|
None,
|
||||||
wgpu_render_state.clone(),
|
wgpu_render_state.clone(),
|
||||||
@@ -177,9 +184,7 @@ impl WgpuWinitApp {
|
|||||||
{
|
{
|
||||||
let event_loop_proxy = self.repaint_proxy.clone();
|
let event_loop_proxy = self.repaint_proxy.clone();
|
||||||
|
|
||||||
integration
|
egui_ctx.set_request_repaint_callback(move |info| {
|
||||||
.egui_ctx
|
|
||||||
.set_request_repaint_callback(move |info| {
|
|
||||||
log::trace!("request_repaint_callback: {info:?}");
|
log::trace!("request_repaint_callback: {info:?}");
|
||||||
let when = Instant::now() + info.delay;
|
let when = Instant::now() + info.delay;
|
||||||
let frame_nr = info.current_frame_nr;
|
let frame_nr = info.current_frame_nr;
|
||||||
@@ -195,7 +200,9 @@ impl WgpuWinitApp {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused_mut)] // used for accesskit
|
||||||
let mut egui_winit = egui_winit::State::new(
|
let mut egui_winit = egui_winit::State::new(
|
||||||
|
ViewportId::ROOT,
|
||||||
event_loop,
|
event_loop,
|
||||||
Some(window.scale_factor() as f32),
|
Some(window.scale_factor() as f32),
|
||||||
painter.max_texture_side(),
|
painter.max_texture_side(),
|
||||||
@@ -207,12 +214,12 @@ impl WgpuWinitApp {
|
|||||||
integration.init_accesskit(&mut egui_winit, &window, event_loop_proxy);
|
integration.init_accesskit(&mut egui_winit, &window, event_loop_proxy);
|
||||||
}
|
}
|
||||||
let theme = system_theme.unwrap_or(self.native_options.default_theme);
|
let theme = system_theme.unwrap_or(self.native_options.default_theme);
|
||||||
integration.egui_ctx.set_visuals(theme.egui_visuals());
|
egui_ctx.set_visuals(theme.egui_visuals());
|
||||||
|
|
||||||
let app_creator = std::mem::take(&mut self.app_creator)
|
let app_creator = std::mem::take(&mut self.app_creator)
|
||||||
.expect("Single-use AppCreator has unexpectedly already been taken");
|
.expect("Single-use AppCreator has unexpectedly already been taken");
|
||||||
let cc = CreationContext {
|
let cc = CreationContext {
|
||||||
egui_ctx: integration.egui_ctx.clone(),
|
egui_ctx: egui_ctx.clone(),
|
||||||
integration_info: integration.frame.info().clone(),
|
integration_info: integration.frame.info().clone(),
|
||||||
storage: integration.frame.storage(),
|
storage: integration.frame.storage(),
|
||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
@@ -249,6 +256,7 @@ impl WgpuWinitApp {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let shared = Rc::new(RefCell::new(SharedState {
|
let shared = Rc::new(RefCell::new(SharedState {
|
||||||
|
egui_ctx,
|
||||||
viewport_from_window,
|
viewport_from_window,
|
||||||
viewports,
|
viewports,
|
||||||
painter,
|
painter,
|
||||||
@@ -262,20 +270,14 @@ impl WgpuWinitApp {
|
|||||||
|
|
||||||
let event_loop: *const EventLoopWindowTarget<UserEvent> = event_loop;
|
let event_loop: *const EventLoopWindowTarget<UserEvent> = event_loop;
|
||||||
|
|
||||||
egui::Context::set_immediate_viewport_renderer(move |egui_ctx, immediate_viewport| {
|
egui::Context::set_immediate_viewport_renderer(move |_egui_ctx, immediate_viewport| {
|
||||||
if let Some(shared) = shared.upgrade() {
|
if let Some(shared) = shared.upgrade() {
|
||||||
// SAFETY: the event loop lives longer than
|
// SAFETY: the event loop lives longer than
|
||||||
// the Rc:s we just upgraded above.
|
// the Rc:s we just upgraded above.
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
let event_loop = unsafe { event_loop.as_ref().unwrap() };
|
let event_loop = unsafe { event_loop.as_ref().unwrap() };
|
||||||
|
|
||||||
render_immediate_viewport(
|
render_immediate_viewport(event_loop, beginning, &shared, immediate_viewport);
|
||||||
event_loop,
|
|
||||||
egui_ctx,
|
|
||||||
beginning,
|
|
||||||
&shared,
|
|
||||||
immediate_viewport,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
log::warn!("render_sync_callback called after window closed");
|
log::warn!("render_sync_callback called after window closed");
|
||||||
}
|
}
|
||||||
@@ -373,9 +375,14 @@ impl WinitApp for WgpuWinitApp {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap_or(&self.app_name),
|
.unwrap_or(&self.app_name),
|
||||||
);
|
);
|
||||||
let (window, builder) =
|
let egui_ctx = winit_integration::create_egui_context(storage.as_deref());
|
||||||
create_window(event_loop, storage.as_deref(), &mut self.native_options)?;
|
let (window, builder) = create_window(
|
||||||
self.init_run_state(event_loop, storage, window, builder)?
|
&egui_ctx,
|
||||||
|
event_loop,
|
||||||
|
storage.as_deref(),
|
||||||
|
&mut self.native_options,
|
||||||
|
)?;
|
||||||
|
self.init_run_state(egui_ctx, event_loop, storage, window, builder)?
|
||||||
};
|
};
|
||||||
|
|
||||||
EventResult::RepaintNow(
|
EventResult::RepaintNow(
|
||||||
@@ -504,7 +511,6 @@ impl WgpuWinitRunning {
|
|||||||
viewport.update_viewport_info();
|
viewport.update_viewport_info();
|
||||||
|
|
||||||
let Viewport {
|
let Viewport {
|
||||||
ids,
|
|
||||||
viewport_ui_cb,
|
viewport_ui_cb,
|
||||||
window,
|
window,
|
||||||
egui_winit,
|
egui_winit,
|
||||||
@@ -525,10 +531,7 @@ impl WgpuWinitRunning {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut raw_input = egui_winit.as_mut().unwrap().take_egui_input(
|
let mut raw_input = egui_winit.as_mut().unwrap().take_egui_input(window);
|
||||||
window,
|
|
||||||
ViewportIdPair::from_self_and_parent(viewport_id, ids.parent),
|
|
||||||
);
|
|
||||||
|
|
||||||
integration.pre_update();
|
integration.pre_update();
|
||||||
|
|
||||||
@@ -552,6 +555,7 @@ impl WgpuWinitRunning {
|
|||||||
let mut shared = shared.borrow_mut();
|
let mut shared = shared.borrow_mut();
|
||||||
|
|
||||||
let SharedState {
|
let SharedState {
|
||||||
|
egui_ctx,
|
||||||
viewports,
|
viewports,
|
||||||
painter,
|
painter,
|
||||||
viewport_from_window,
|
viewport_from_window,
|
||||||
@@ -562,6 +566,8 @@ impl WgpuWinitRunning {
|
|||||||
return EventResult::Wait;
|
return EventResult::Wait;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
viewport.info.events.clear(); // they should have been processed
|
||||||
|
|
||||||
let Viewport {
|
let Viewport {
|
||||||
window: Some(window),
|
window: Some(window),
|
||||||
egui_winit: Some(egui_winit),
|
egui_winit: Some(egui_winit),
|
||||||
@@ -581,16 +587,16 @@ impl WgpuWinitRunning {
|
|||||||
viewport_output,
|
viewport_output,
|
||||||
} = full_output;
|
} = full_output;
|
||||||
|
|
||||||
integration.handle_platform_output(window, viewport_id, platform_output, egui_winit);
|
egui_winit.handle_platform_output(window, egui_ctx, platform_output);
|
||||||
|
|
||||||
{
|
{
|
||||||
let clipped_primitives = integration.egui_ctx.tessellate(shapes, pixels_per_point);
|
let clipped_primitives = egui_ctx.tessellate(shapes, pixels_per_point);
|
||||||
|
|
||||||
let screenshot_requested = std::mem::take(&mut viewport.screenshot_requested);
|
let screenshot_requested = std::mem::take(&mut viewport.screenshot_requested);
|
||||||
let screenshot = painter.paint_and_update_textures(
|
let screenshot = painter.paint_and_update_textures(
|
||||||
viewport_id,
|
viewport_id,
|
||||||
pixels_per_point,
|
pixels_per_point,
|
||||||
app.clear_color(&integration.egui_ctx.style().visuals),
|
app.clear_color(&egui_ctx.style().visuals),
|
||||||
&clipped_primitives,
|
&clipped_primitives,
|
||||||
&textures_delta,
|
&textures_delta,
|
||||||
screenshot_requested,
|
screenshot_requested,
|
||||||
@@ -610,7 +616,12 @@ impl WgpuWinitRunning {
|
|||||||
|
|
||||||
let active_viewports_ids: ViewportIdSet = viewport_output.keys().copied().collect();
|
let active_viewports_ids: ViewportIdSet = viewport_output.keys().copied().collect();
|
||||||
|
|
||||||
handle_viewport_output(viewport_output, viewports, *focused_viewport);
|
handle_viewport_output(
|
||||||
|
&integration.egui_ctx,
|
||||||
|
viewport_output,
|
||||||
|
viewports,
|
||||||
|
*focused_viewport,
|
||||||
|
);
|
||||||
|
|
||||||
// Prune dead viewports:
|
// Prune dead viewports:
|
||||||
viewports.retain(|id, _| active_viewports_ids.contains(id));
|
viewports.retain(|id, _| active_viewports_ids.contains(id));
|
||||||
@@ -649,8 +660,8 @@ impl WgpuWinitRunning {
|
|||||||
|
|
||||||
let Self {
|
let Self {
|
||||||
integration,
|
integration,
|
||||||
app,
|
|
||||||
shared,
|
shared,
|
||||||
|
..
|
||||||
} = self;
|
} = self;
|
||||||
let mut shared = shared.borrow_mut();
|
let mut shared = shared.borrow_mut();
|
||||||
|
|
||||||
@@ -725,9 +736,10 @@ impl WgpuWinitRunning {
|
|||||||
let event_response = viewport_id
|
let event_response = viewport_id
|
||||||
.and_then(|viewport_id| {
|
.and_then(|viewport_id| {
|
||||||
shared.viewports.get_mut(&viewport_id).and_then(|viewport| {
|
shared.viewports.get_mut(&viewport_id).and_then(|viewport| {
|
||||||
viewport.egui_winit.as_mut().map(|egui_winit| {
|
viewport
|
||||||
integration.on_window_event(app.as_mut(), event, egui_winit, viewport_id)
|
.egui_winit
|
||||||
})
|
.as_mut()
|
||||||
|
.map(|egui_winit| integration.on_window_event(event, egui_winit))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
@@ -749,6 +761,7 @@ impl WgpuWinitRunning {
|
|||||||
impl Viewport {
|
impl Viewport {
|
||||||
fn init_window(
|
fn init_window(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
egui_ctx: &egui::Context,
|
||||||
windows_id: &mut HashMap<WindowId, ViewportId>,
|
windows_id: &mut HashMap<WindowId, ViewportId>,
|
||||||
painter: &mut egui_wgpu::winit::Painter,
|
painter: &mut egui_wgpu::winit::Painter,
|
||||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||||
@@ -757,7 +770,9 @@ impl Viewport {
|
|||||||
|
|
||||||
let viewport_id = self.ids.this;
|
let viewport_id = self.ids.this;
|
||||||
|
|
||||||
match create_winit_window_builder(self.builder.clone()).build(event_loop) {
|
match create_winit_window_builder(egui_ctx, event_loop, self.builder.clone())
|
||||||
|
.build(event_loop)
|
||||||
|
{
|
||||||
Ok(window) => {
|
Ok(window) => {
|
||||||
apply_viewport_builder_to_new_window(&window, &self.builder);
|
apply_viewport_builder_to_new_window(&window, &self.builder);
|
||||||
|
|
||||||
@@ -769,6 +784,7 @@ impl Viewport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.egui_winit = Some(egui_winit::State::new(
|
self.egui_winit = Some(egui_winit::State::new(
|
||||||
|
viewport_id,
|
||||||
event_loop,
|
event_loop,
|
||||||
Some(window.scale_factor() as f32),
|
Some(window.scale_factor() as f32),
|
||||||
painter.max_texture_side(),
|
painter.max_texture_side(),
|
||||||
@@ -799,6 +815,7 @@ impl Viewport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn create_window(
|
fn create_window(
|
||||||
|
egui_ctx: &egui::Context,
|
||||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||||
storage: Option<&dyn Storage>,
|
storage: Option<&dyn Storage>,
|
||||||
native_options: &mut NativeOptions,
|
native_options: &mut NativeOptions,
|
||||||
@@ -806,11 +823,16 @@ fn create_window(
|
|||||||
crate::profile_function!();
|
crate::profile_function!();
|
||||||
|
|
||||||
let window_settings = epi_integration::load_window_settings(storage);
|
let window_settings = epi_integration::load_window_settings(storage);
|
||||||
let viewport_builder =
|
let viewport_builder = epi_integration::viewport_builder(
|
||||||
epi_integration::viewport_builder(event_loop, native_options, window_settings);
|
egui_ctx.zoom_factor(),
|
||||||
|
event_loop,
|
||||||
|
native_options,
|
||||||
|
window_settings,
|
||||||
|
);
|
||||||
let window = {
|
let window = {
|
||||||
crate::profile_scope!("WindowBuilder::build");
|
crate::profile_scope!("WindowBuilder::build");
|
||||||
create_winit_window_builder(viewport_builder.clone()).build(event_loop)?
|
create_winit_window_builder(egui_ctx, event_loop, viewport_builder.clone())
|
||||||
|
.build(event_loop)?
|
||||||
};
|
};
|
||||||
apply_viewport_builder_to_new_window(&window, &viewport_builder);
|
apply_viewport_builder_to_new_window(&window, &viewport_builder);
|
||||||
epi_integration::apply_window_settings(&window, window_settings);
|
epi_integration::apply_window_settings(&window, window_settings);
|
||||||
@@ -819,7 +841,6 @@ fn create_window(
|
|||||||
|
|
||||||
fn render_immediate_viewport(
|
fn render_immediate_viewport(
|
||||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||||
egui_ctx: &egui::Context,
|
|
||||||
beginning: Instant,
|
beginning: Instant,
|
||||||
shared: &RefCell<SharedState>,
|
shared: &RefCell<SharedState>,
|
||||||
immediate_viewport: ImmediateViewport<'_>,
|
immediate_viewport: ImmediateViewport<'_>,
|
||||||
@@ -834,6 +855,7 @@ fn render_immediate_viewport(
|
|||||||
|
|
||||||
let input = {
|
let input = {
|
||||||
let SharedState {
|
let SharedState {
|
||||||
|
egui_ctx,
|
||||||
viewports,
|
viewports,
|
||||||
painter,
|
painter,
|
||||||
viewport_from_window,
|
viewport_from_window,
|
||||||
@@ -841,6 +863,7 @@ fn render_immediate_viewport(
|
|||||||
} = &mut *shared.borrow_mut();
|
} = &mut *shared.borrow_mut();
|
||||||
|
|
||||||
let viewport = initialize_or_update_viewport(
|
let viewport = initialize_or_update_viewport(
|
||||||
|
egui_ctx,
|
||||||
viewports,
|
viewports,
|
||||||
ids,
|
ids,
|
||||||
ViewportClass::Immediate,
|
ViewportClass::Immediate,
|
||||||
@@ -849,7 +872,7 @@ fn render_immediate_viewport(
|
|||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
if viewport.window.is_none() {
|
if viewport.window.is_none() {
|
||||||
viewport.init_window(viewport_from_window, painter, event_loop);
|
viewport.init_window(egui_ctx, viewport_from_window, painter, event_loop);
|
||||||
}
|
}
|
||||||
viewport.update_viewport_info();
|
viewport.update_viewport_info();
|
||||||
|
|
||||||
@@ -857,7 +880,7 @@ fn render_immediate_viewport(
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut input = winit_state.take_egui_input(window, ids);
|
let mut input = winit_state.take_egui_input(window);
|
||||||
input.viewports = viewports
|
input.viewports = viewports
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(id, viewport)| (*id, viewport.info.clone()))
|
.map(|(id, viewport)| (*id, viewport.info.clone()))
|
||||||
@@ -866,6 +889,8 @@ fn render_immediate_viewport(
|
|||||||
input
|
input
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let egui_ctx = shared.borrow().egui_ctx.clone();
|
||||||
|
|
||||||
// ------------------------------------------
|
// ------------------------------------------
|
||||||
|
|
||||||
// Run the user code, which could re-entrantly call this function again (!).
|
// Run the user code, which could re-entrantly call this function again (!).
|
||||||
@@ -893,6 +918,7 @@ fn render_immediate_viewport(
|
|||||||
let Some(viewport) = viewports.get_mut(&ids.this) else {
|
let Some(viewport) = viewports.get_mut(&ids.this) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
viewport.info.events.clear(); // they should have been processed
|
||||||
let Some(winit_state) = &mut viewport.egui_winit else {
|
let Some(winit_state) = &mut viewport.egui_winit else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -917,13 +943,14 @@ fn render_immediate_viewport(
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
winit_state.handle_platform_output(window, ids.this, egui_ctx, platform_output);
|
winit_state.handle_platform_output(window, &egui_ctx, platform_output);
|
||||||
|
|
||||||
handle_viewport_output(viewport_output, viewports, *focused_viewport);
|
handle_viewport_output(&egui_ctx, viewport_output, viewports, *focused_viewport);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add new viewports, and update existing ones:
|
/// Add new viewports, and update existing ones:
|
||||||
fn handle_viewport_output(
|
fn handle_viewport_output(
|
||||||
|
egui_ctx: &egui::Context,
|
||||||
viewport_output: ViewportIdMap<ViewportOutput>,
|
viewport_output: ViewportIdMap<ViewportOutput>,
|
||||||
viewports: &mut ViewportIdMap<Viewport>,
|
viewports: &mut ViewportIdMap<Viewport>,
|
||||||
focused_viewport: Option<ViewportId>,
|
focused_viewport: Option<ViewportId>,
|
||||||
@@ -943,6 +970,7 @@ fn handle_viewport_output(
|
|||||||
let ids = ViewportIdPair::from_self_and_parent(viewport_id, parent);
|
let ids = ViewportIdPair::from_self_and_parent(viewport_id, parent);
|
||||||
|
|
||||||
let viewport = initialize_or_update_viewport(
|
let viewport = initialize_or_update_viewport(
|
||||||
|
egui_ctx,
|
||||||
viewports,
|
viewports,
|
||||||
ids,
|
ids,
|
||||||
class,
|
class,
|
||||||
@@ -954,6 +982,7 @@ fn handle_viewport_output(
|
|||||||
if let Some(window) = viewport.window.as_ref() {
|
if let Some(window) = viewport.window.as_ref() {
|
||||||
let is_viewport_focused = focused_viewport == Some(viewport_id);
|
let is_viewport_focused = focused_viewport == Some(viewport_id);
|
||||||
egui_winit::process_viewport_commands(
|
egui_winit::process_viewport_commands(
|
||||||
|
egui_ctx,
|
||||||
&mut viewport.info,
|
&mut viewport.info,
|
||||||
commands,
|
commands,
|
||||||
window,
|
window,
|
||||||
@@ -964,14 +993,15 @@ fn handle_viewport_output(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_or_update_viewport(
|
fn initialize_or_update_viewport<'vp>(
|
||||||
viewports: &mut Viewports,
|
egui_ctx: &egui::Context,
|
||||||
|
viewports: &'vp mut Viewports,
|
||||||
ids: ViewportIdPair,
|
ids: ViewportIdPair,
|
||||||
class: ViewportClass,
|
class: ViewportClass,
|
||||||
mut builder: ViewportBuilder,
|
mut builder: ViewportBuilder,
|
||||||
viewport_ui_cb: Option<Arc<dyn Fn(&egui::Context) + Send + Sync>>,
|
viewport_ui_cb: Option<Arc<dyn Fn(&egui::Context) + Send + Sync>>,
|
||||||
focused_viewport: Option<ViewportId>,
|
focused_viewport: Option<ViewportId>,
|
||||||
) -> &mut Viewport {
|
) -> &'vp mut Viewport {
|
||||||
if builder.icon.is_none() {
|
if builder.icon.is_none() {
|
||||||
// Inherit icon from parent
|
// Inherit icon from parent
|
||||||
builder.icon = viewports
|
builder.icon = viewports
|
||||||
@@ -1003,19 +1033,20 @@ fn initialize_or_update_viewport(
|
|||||||
viewport.ids.parent = ids.parent;
|
viewport.ids.parent = ids.parent;
|
||||||
viewport.viewport_ui_cb = viewport_ui_cb;
|
viewport.viewport_ui_cb = viewport_ui_cb;
|
||||||
|
|
||||||
let (delta_commands, recreate) = viewport.builder.patch(&builder);
|
let (delta_commands, recreate) = viewport.builder.patch(builder);
|
||||||
|
|
||||||
if recreate {
|
if recreate {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"Recreating window for viewport {:?} ({:?})",
|
"Recreating window for viewport {:?} ({:?})",
|
||||||
ids.this,
|
ids.this,
|
||||||
builder.title
|
viewport.builder.title
|
||||||
);
|
);
|
||||||
viewport.window = None;
|
viewport.window = None;
|
||||||
viewport.egui_winit = None;
|
viewport.egui_winit = None;
|
||||||
} else if let Some(window) = &viewport.window {
|
} else if let Some(window) = &viewport.window {
|
||||||
let is_viewport_focused = focused_viewport == Some(ids.this);
|
let is_viewport_focused = focused_viewport == Some(ids.this);
|
||||||
process_viewport_commands(
|
process_viewport_commands(
|
||||||
|
egui_ctx,
|
||||||
&mut viewport.info,
|
&mut viewport.info,
|
||||||
delta_commands,
|
delta_commands,
|
||||||
window,
|
window,
|
||||||
|
|||||||
@@ -11,13 +11,27 @@ use egui_winit::accesskit_winit;
|
|||||||
|
|
||||||
use super::epi_integration::EpiIntegration;
|
use super::epi_integration::EpiIntegration;
|
||||||
|
|
||||||
pub const IS_DESKTOP: bool = cfg!(any(
|
/// Create an egui context, restoring it from storage if possible.
|
||||||
|
pub fn create_egui_context(storage: Option<&dyn crate::Storage>) -> egui::Context {
|
||||||
|
crate::profile_function!();
|
||||||
|
|
||||||
|
pub const IS_DESKTOP: bool = cfg!(any(
|
||||||
target_os = "freebsd",
|
target_os = "freebsd",
|
||||||
target_os = "linux",
|
target_os = "linux",
|
||||||
target_os = "macos",
|
target_os = "macos",
|
||||||
target_os = "openbsd",
|
target_os = "openbsd",
|
||||||
target_os = "windows",
|
target_os = "windows",
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let egui_ctx = egui::Context::default();
|
||||||
|
|
||||||
|
egui_ctx.set_embed_viewports(!IS_DESKTOP);
|
||||||
|
|
||||||
|
let memory = crate::native::epi_integration::load_egui_memory(storage).unwrap_or_default();
|
||||||
|
egui_ctx.memory_mut(|mem| *mem = memory);
|
||||||
|
|
||||||
|
egui_ctx
|
||||||
|
}
|
||||||
|
|
||||||
/// The custom even `eframe` uses with the [`winit`] event loop.
|
/// The custom even `eframe` uses with the [`winit`] event loop.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use egui::TexturesDelta;
|
use egui::TexturesDelta;
|
||||||
use wasm_bindgen::JsValue;
|
|
||||||
|
|
||||||
use crate::{epi, App};
|
use crate::{epi, App};
|
||||||
|
|
||||||
@@ -17,7 +16,10 @@ pub struct AppRunner {
|
|||||||
screen_reader: super::screen_reader::ScreenReader,
|
screen_reader: super::screen_reader::ScreenReader,
|
||||||
pub(crate) text_cursor_pos: Option<egui::Pos2>,
|
pub(crate) text_cursor_pos: Option<egui::Pos2>,
|
||||||
pub(crate) mutable_text_under_cursor: bool,
|
pub(crate) mutable_text_under_cursor: bool,
|
||||||
|
|
||||||
|
// Output for the last run:
|
||||||
textures_delta: TexturesDelta,
|
textures_delta: TexturesDelta,
|
||||||
|
clipped_primitives: Option<Vec<egui::ClippedPrimitive>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for AppRunner {
|
impl Drop for AppRunner {
|
||||||
@@ -58,6 +60,12 @@ impl AppRunner {
|
|||||||
));
|
));
|
||||||
super::storage::load_memory(&egui_ctx);
|
super::storage::load_memory(&egui_ctx);
|
||||||
|
|
||||||
|
egui_ctx.options_mut(|o| {
|
||||||
|
// On web, the browser controls the zoom factor:
|
||||||
|
o.zoom_with_keyboard = false;
|
||||||
|
o.zoom_factor = 1.0;
|
||||||
|
});
|
||||||
|
|
||||||
let theme = system_theme.unwrap_or(web_options.default_theme);
|
let theme = system_theme.unwrap_or(web_options.default_theme);
|
||||||
egui_ctx.set_visuals(theme.egui_visuals());
|
egui_ctx.set_visuals(theme.egui_visuals());
|
||||||
|
|
||||||
@@ -109,10 +117,17 @@ impl AppRunner {
|
|||||||
text_cursor_pos: None,
|
text_cursor_pos: None,
|
||||||
mutable_text_under_cursor: false,
|
mutable_text_under_cursor: false,
|
||||||
textures_delta: Default::default(),
|
textures_delta: Default::default(),
|
||||||
|
clipped_primitives: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
runner.input.raw.max_texture_side = Some(runner.painter.max_texture_side());
|
runner.input.raw.max_texture_side = Some(runner.painter.max_texture_side());
|
||||||
runner.input.raw.native_pixels_per_point = Some(super::native_pixels_per_point());
|
runner
|
||||||
|
.input
|
||||||
|
.raw
|
||||||
|
.viewports
|
||||||
|
.entry(egui::ViewportId::ROOT)
|
||||||
|
.or_default()
|
||||||
|
.native_pixels_per_point = Some(super::native_pixels_per_point());
|
||||||
|
|
||||||
Ok(runner)
|
Ok(runner)
|
||||||
}
|
}
|
||||||
@@ -158,8 +173,26 @@ impl AppRunner {
|
|||||||
self.painter.destroy();
|
self.painter.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call [`Self::paint`] later to paint
|
/// Runs the user code and paints the UI.
|
||||||
pub fn logic(&mut self) -> Vec<egui::ClippedPrimitive> {
|
///
|
||||||
|
/// If there is already an outstanding frame of output,
|
||||||
|
/// that is painted instead.
|
||||||
|
pub fn run_and_paint(&mut self) {
|
||||||
|
if self.clipped_primitives.is_none() {
|
||||||
|
// Run user code, and paint the results:
|
||||||
|
self.logic();
|
||||||
|
self.paint();
|
||||||
|
} else {
|
||||||
|
// We have already run the logic, e.g. in an on-click event,
|
||||||
|
// so let's only present the results:
|
||||||
|
self.paint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs the logic, but doesn't paint the result.
|
||||||
|
///
|
||||||
|
/// The result can be painted later with a call to [`Self::run_and_paint`] or [`Self::paint`].
|
||||||
|
pub fn logic(&mut self) {
|
||||||
let frame_start = now_sec();
|
let frame_start = now_sec();
|
||||||
|
|
||||||
super::resize_canvas_to_screen_size(self.canvas_id(), self.web_options.max_size_points);
|
super::resize_canvas_to_screen_size(self.canvas_id(), self.web_options.max_size_points);
|
||||||
@@ -191,25 +224,26 @@ impl AppRunner {
|
|||||||
|
|
||||||
self.handle_platform_output(platform_output);
|
self.handle_platform_output(platform_output);
|
||||||
self.textures_delta.append(textures_delta);
|
self.textures_delta.append(textures_delta);
|
||||||
let clipped_primitives = self.egui_ctx.tessellate(shapes, pixels_per_point);
|
self.clipped_primitives = Some(self.egui_ctx.tessellate(shapes, pixels_per_point));
|
||||||
|
|
||||||
self.frame.info.cpu_usage = Some((now_sec() - frame_start) as f32);
|
self.frame.info.cpu_usage = Some((now_sec() - frame_start) as f32);
|
||||||
|
|
||||||
clipped_primitives
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Paint the results of the last call to [`Self::logic`].
|
/// Paint the results of the last call to [`Self::logic`].
|
||||||
pub fn paint(&mut self, clipped_primitives: &[egui::ClippedPrimitive]) -> Result<(), JsValue> {
|
pub fn paint(&mut self) {
|
||||||
let textures_delta = std::mem::take(&mut self.textures_delta);
|
let textures_delta = std::mem::take(&mut self.textures_delta);
|
||||||
|
let clipped_primitives = std::mem::take(&mut self.clipped_primitives);
|
||||||
|
|
||||||
self.painter.paint_and_update_textures(
|
if let Some(clipped_primitives) = clipped_primitives {
|
||||||
|
if let Err(err) = self.painter.paint_and_update_textures(
|
||||||
self.app.clear_color(&self.egui_ctx.style().visuals),
|
self.app.clear_color(&self.egui_ctx.style().visuals),
|
||||||
clipped_primitives,
|
&clipped_primitives,
|
||||||
self.egui_ctx.pixels_per_point(),
|
self.egui_ctx.pixels_per_point(),
|
||||||
&textures_delta,
|
&textures_delta,
|
||||||
)?;
|
) {
|
||||||
|
log::error!("Failed to paint: {}", super::string_from_js_value(&err));
|
||||||
Ok(())
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_platform_output(&mut self, platform_output: egui::PlatformOutput) {
|
fn handle_platform_output(&mut self, platform_output: egui::PlatformOutput) {
|
||||||
|
|||||||
@@ -23,12 +23,17 @@ pub(crate) struct WebInput {
|
|||||||
|
|
||||||
impl WebInput {
|
impl WebInput {
|
||||||
pub fn new_frame(&mut self, canvas_size: egui::Vec2) -> egui::RawInput {
|
pub fn new_frame(&mut self, canvas_size: egui::Vec2) -> egui::RawInput {
|
||||||
egui::RawInput {
|
let mut raw_input = egui::RawInput {
|
||||||
screen_rect: Some(egui::Rect::from_min_size(Default::default(), canvas_size)),
|
screen_rect: Some(egui::Rect::from_min_size(Default::default(), canvas_size)),
|
||||||
pixels_per_point: Some(super::native_pixels_per_point()), // We ALWAYS use the native pixels-per-point
|
|
||||||
time: Some(super::now_sec()),
|
time: Some(super::now_sec()),
|
||||||
..self.raw.take()
|
..self.raw.take()
|
||||||
}
|
};
|
||||||
|
raw_input
|
||||||
|
.viewports
|
||||||
|
.entry(egui::ViewportId::ROOT)
|
||||||
|
.or_default()
|
||||||
|
.native_pixels_per_point = Some(super::native_pixels_per_point());
|
||||||
|
raw_input
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_web_page_focus_change(&mut self, focused: bool) {
|
pub fn on_web_page_focus_change(&mut self, focused: bool) {
|
||||||
@@ -68,6 +73,10 @@ impl NeedRepaint {
|
|||||||
*repaint_time = repaint_time.min(super::now_sec() + num_seconds);
|
*repaint_time = repaint_time.min(super::now_sec() + num_seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn needs_repaint(&self) -> bool {
|
||||||
|
self.when_to_repaint() <= super::now_sec()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn repaint_asap(&self) {
|
pub fn repaint_asap(&self) {
|
||||||
*self.0.lock() = f64::NEG_INFINITY;
|
*self.0.lock() = f64::NEG_INFINITY;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,22 +8,19 @@ use super::*;
|
|||||||
fn paint_and_schedule(runner_ref: &WebRunner) -> Result<(), JsValue> {
|
fn paint_and_schedule(runner_ref: &WebRunner) -> Result<(), JsValue> {
|
||||||
// Only paint and schedule if there has been no panic
|
// Only paint and schedule if there has been no panic
|
||||||
if let Some(mut runner_lock) = runner_ref.try_lock() {
|
if let Some(mut runner_lock) = runner_ref.try_lock() {
|
||||||
paint_if_needed(&mut runner_lock)?;
|
paint_if_needed(&mut runner_lock);
|
||||||
drop(runner_lock);
|
drop(runner_lock);
|
||||||
request_animation_frame(runner_ref.clone())?;
|
request_animation_frame(runner_ref.clone())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint_if_needed(runner: &mut AppRunner) -> Result<(), JsValue> {
|
fn paint_if_needed(runner: &mut AppRunner) {
|
||||||
if runner.needs_repaint.when_to_repaint() <= now_sec() {
|
if runner.needs_repaint.needs_repaint() {
|
||||||
runner.needs_repaint.clear();
|
runner.needs_repaint.clear();
|
||||||
let clipped_primitives = runner.logic();
|
runner.run_and_paint();
|
||||||
runner.paint(&clipped_primitives)?;
|
|
||||||
runner.auto_save_if_needed();
|
|
||||||
}
|
}
|
||||||
Ok(())
|
runner.auto_save_if_needed();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn request_animation_frame(runner_ref: WebRunner) -> Result<(), JsValue> {
|
pub(crate) fn request_animation_frame(runner_ref: WebRunner) -> Result<(), JsValue> {
|
||||||
@@ -177,10 +174,14 @@ pub(crate) fn install_document_events(runner_ref: &WebRunner) -> Result<(), JsVa
|
|||||||
"cut",
|
"cut",
|
||||||
|event: web_sys::ClipboardEvent, runner| {
|
|event: web_sys::ClipboardEvent, runner| {
|
||||||
runner.input.raw.events.push(egui::Event::Cut);
|
runner.input.raw.events.push(egui::Event::Cut);
|
||||||
|
|
||||||
// In Safari we are only allowed to write to the clipboard during the
|
// In Safari we are only allowed to write to the clipboard during the
|
||||||
// event callback, which is why we run the app logic here and now:
|
// event callback, which is why we run the app logic here and now:
|
||||||
runner.logic(); // we ignore the returned triangles, but schedule a repaint right after
|
runner.logic();
|
||||||
|
|
||||||
|
// Make sure we paint the output of the above logic call asap:
|
||||||
runner.needs_repaint.repaint_asap();
|
runner.needs_repaint.repaint_asap();
|
||||||
|
|
||||||
event.stop_propagation();
|
event.stop_propagation();
|
||||||
event.prevent_default();
|
event.prevent_default();
|
||||||
},
|
},
|
||||||
@@ -192,10 +193,14 @@ pub(crate) fn install_document_events(runner_ref: &WebRunner) -> Result<(), JsVa
|
|||||||
"copy",
|
"copy",
|
||||||
|event: web_sys::ClipboardEvent, runner| {
|
|event: web_sys::ClipboardEvent, runner| {
|
||||||
runner.input.raw.events.push(egui::Event::Copy);
|
runner.input.raw.events.push(egui::Event::Copy);
|
||||||
|
|
||||||
// In Safari we are only allowed to write to the clipboard during the
|
// In Safari we are only allowed to write to the clipboard during the
|
||||||
// event callback, which is why we run the app logic here and now:
|
// event callback, which is why we run the app logic here and now:
|
||||||
runner.logic(); // we ignore the returned triangles, but schedule a repaint right after
|
runner.logic();
|
||||||
|
|
||||||
|
// Make sure we paint the output of the above logic call asap:
|
||||||
runner.needs_repaint.repaint_asap();
|
runner.needs_repaint.repaint_asap();
|
||||||
|
|
||||||
event.stop_propagation();
|
event.stop_propagation();
|
||||||
event.prevent_default();
|
event.prevent_default();
|
||||||
},
|
},
|
||||||
@@ -281,6 +286,12 @@ pub(crate) fn install_canvas_events(runner_ref: &WebRunner) -> Result<(), JsValu
|
|||||||
pressed: true,
|
pressed: true,
|
||||||
modifiers,
|
modifiers,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// In Safari we are only allowed to write to the clipboard during the
|
||||||
|
// event callback, which is why we run the app logic here and now:
|
||||||
|
runner.logic();
|
||||||
|
|
||||||
|
// Make sure we paint the output of the above logic call asap:
|
||||||
runner.needs_repaint.repaint_asap();
|
runner.needs_repaint.repaint_asap();
|
||||||
}
|
}
|
||||||
event.stop_propagation();
|
event.stop_propagation();
|
||||||
@@ -310,6 +321,12 @@ pub(crate) fn install_canvas_events(runner_ref: &WebRunner) -> Result<(), JsValu
|
|||||||
pressed: false,
|
pressed: false,
|
||||||
modifiers,
|
modifiers,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// In Safari we are only allowed to write to the clipboard during the
|
||||||
|
// event callback, which is why we run the app logic here and now:
|
||||||
|
runner.logic();
|
||||||
|
|
||||||
|
// Make sure we paint the output of the above logic call asap:
|
||||||
runner.needs_repaint.repaint_asap();
|
runner.needs_repaint.repaint_asap();
|
||||||
|
|
||||||
text_agent::update_text_agent(runner);
|
text_agent::update_text_agent(runner);
|
||||||
|
|||||||
@@ -49,6 +49,10 @@ use crate::Theme;
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
pub(crate) fn string_from_js_value(value: &JsValue) -> String {
|
||||||
|
value.as_string().unwrap_or_else(|| format!("{value:#?}"))
|
||||||
|
}
|
||||||
|
|
||||||
/// Current time in seconds (since undefined point in time).
|
/// Current time in seconds (since undefined point in time).
|
||||||
///
|
///
|
||||||
/// Monotonically increasing.
|
/// Monotonically increasing.
|
||||||
@@ -196,7 +200,7 @@ fn set_clipboard_text(s: &str) {
|
|||||||
let future = wasm_bindgen_futures::JsFuture::from(promise);
|
let future = wasm_bindgen_futures::JsFuture::from(promise);
|
||||||
let future = async move {
|
let future = async move {
|
||||||
if let Err(err) = future.await {
|
if let Err(err) = future.await {
|
||||||
log::error!("Copy/cut action failed: {err:?}");
|
log::error!("Copy/cut action failed: {}", string_from_js_value(&err));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
wasm_bindgen_futures::spawn_local(future);
|
wasm_bindgen_futures::spawn_local(future);
|
||||||
|
|||||||
@@ -95,7 +95,10 @@ impl WebRunner {
|
|||||||
log::debug!("Unsubscribing from {} events", events_to_unsubscribe.len());
|
log::debug!("Unsubscribing from {} events", events_to_unsubscribe.len());
|
||||||
for x in events_to_unsubscribe {
|
for x in events_to_unsubscribe {
|
||||||
if let Err(err) = x.unsubscribe() {
|
if let Err(err) = x.unsubscribe() {
|
||||||
log::warn!("Failed to unsubscribe from event: {err:?}");
|
log::warn!(
|
||||||
|
"Failed to unsubscribe from event: {}",
|
||||||
|
super::string_from_js_value(&err)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,13 @@ This file is updated upon each release.
|
|||||||
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 0.24.0 - 2023-11-23
|
||||||
|
* Updated to wgpu 0.18 [#3505](https://github.com/emilk/egui/pull/3505) (thanks [@Wumpf](https://github.com/Wumpf)!)
|
||||||
|
* Update MSRV to Rust 1.72 [#3595](https://github.com/emilk/egui/pull/3595)
|
||||||
|
* Properly clamp and round viewport values, preventing rare warnings [#3604](https://github.com/emilk/egui/pull/3604) (thanks [@Wumpf](https://github.com/Wumpf)!)
|
||||||
|
|
||||||
|
|
||||||
## 0.23.0 - 2023-09-27
|
## 0.23.0 - 2023-09-27
|
||||||
* Update to `wgpu` 0.17.0 [#3170](https://github.com/emilk/egui/pull/3170) (thanks [@Aaron1011](https://github.com/Aaron1011)!)
|
* Update to `wgpu` 0.17.0 [#3170](https://github.com/emilk/egui/pull/3170) (thanks [@Aaron1011](https://github.com/Aaron1011)!)
|
||||||
* Improved wgpu callbacks [#3253](https://github.com/emilk/egui/pull/3253) (thanks [@Wumpf](https://github.com/Wumpf)!)
|
* Improved wgpu callbacks [#3253](https://github.com/emilk/egui/pull/3253) (thanks [@Wumpf](https://github.com/Wumpf)!)
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "egui-wgpu"
|
name = "egui-wgpu"
|
||||||
version = "0.23.0"
|
version.workspace = true
|
||||||
description = "Bindings for using egui natively using the wgpu library"
|
description = "Bindings for using egui natively using the wgpu library"
|
||||||
authors = [
|
authors = [
|
||||||
"Nils Hasenbanck <nils@hasenbanck.de>",
|
"Nils Hasenbanck <nils@hasenbanck.de>",
|
||||||
"embotech <opensource@embotech.com>",
|
"embotech <opensource@embotech.com>",
|
||||||
"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>",
|
"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>",
|
||||||
]
|
]
|
||||||
edition = "2021"
|
edition.workspace = true
|
||||||
rust-version = "1.72"
|
rust-version.workspace = true
|
||||||
homepage = "https://github.com/emilk/egui/tree/master/crates/egui-wgpu"
|
homepage = "https://github.com/emilk/egui/tree/master/crates/egui-wgpu"
|
||||||
license = "MIT OR Apache-2.0"
|
license.workspace = true
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/emilk/egui/tree/master/crates/egui-wgpu"
|
repository = "https://github.com/emilk/egui/tree/master/crates/egui-wgpu"
|
||||||
categories = ["gui", "game-development"]
|
categories = ["gui", "game-development"]
|
||||||
@@ -36,8 +36,8 @@ winit = ["dep:winit"]
|
|||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
egui = { version = "0.23.0", path = "../egui", default-features = false }
|
egui = { version = "0.24.0", path = "../egui", default-features = false }
|
||||||
epaint = { version = "0.23.0", path = "../epaint", default-features = false, features = [
|
epaint = { version = "0.24.0", path = "../epaint", default-features = false, features = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
|
|||||||
@@ -475,10 +475,10 @@ impl Renderer {
|
|||||||
let viewport_px = info.viewport_in_pixels();
|
let viewport_px = info.viewport_in_pixels();
|
||||||
|
|
||||||
render_pass.set_viewport(
|
render_pass.set_viewport(
|
||||||
viewport_px.left_px,
|
viewport_px.left_px as f32,
|
||||||
viewport_px.top_px,
|
viewport_px.top_px as f32,
|
||||||
viewport_px.width_px,
|
viewport_px.width_px as f32,
|
||||||
viewport_px.height_px,
|
viewport_px.height_px as f32,
|
||||||
0.0,
|
0.0,
|
||||||
1.0,
|
1.0,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ This file is updated upon each release.
|
|||||||
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.24.0 - 2023-11-23
|
||||||
|
* Update MSRV to Rust 1.72 [#3595](https://github.com/emilk/egui/pull/3595)
|
||||||
|
* Some breaking changes required for multi-viewport support
|
||||||
|
|
||||||
|
|
||||||
## 0.23.0 - 2023-09-27
|
## 0.23.0 - 2023-09-27
|
||||||
* Only show on-screen-keyboard and IME when editing text [#3362](https://github.com/emilk/egui/pull/3362) (thanks [@Barugon](https://github.com/Barugon)!)
|
* Only show on-screen-keyboard and IME when editing text [#3362](https://github.com/emilk/egui/pull/3362) (thanks [@Barugon](https://github.com/Barugon)!)
|
||||||
* Replace `instant` with `web_time` [#3296](https://github.com/emilk/egui/pull/3296)
|
* Replace `instant` with `web_time` [#3296](https://github.com/emilk/egui/pull/3296)
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "egui-winit"
|
name = "egui-winit"
|
||||||
version = "0.23.0"
|
version.workspace = true
|
||||||
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
||||||
description = "Bindings for using egui with winit"
|
description = "Bindings for using egui with winit"
|
||||||
edition = "2021"
|
edition.workspace = true
|
||||||
rust-version = "1.72"
|
rust-version.workspace = true
|
||||||
homepage = "https://github.com/emilk/egui/tree/master/crates/egui-winit"
|
homepage = "https://github.com/emilk/egui/tree/master/crates/egui-winit"
|
||||||
license = "MIT OR Apache-2.0"
|
license.workspace = true
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/emilk/egui/tree/master/crates/egui-winit"
|
repository = "https://github.com/emilk/egui/tree/master/crates/egui-winit"
|
||||||
categories = ["gui", "game-development"]
|
categories = ["gui", "game-development"]
|
||||||
@@ -55,7 +55,7 @@ wayland = ["winit/wayland", "bytemuck"]
|
|||||||
x11 = ["winit/x11", "bytemuck"]
|
x11 = ["winit/x11", "bytemuck"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
egui = { version = "0.23.0", path = "../egui", default-features = false, features = [
|
egui = { version = "0.24.0", path = "../egui", default-features = false, features = [
|
||||||
"log",
|
"log",
|
||||||
] }
|
] }
|
||||||
log = { version = "0.4", features = ["std"] }
|
log = { version = "0.4", features = ["std"] }
|
||||||
|
|||||||
@@ -14,9 +14,7 @@ pub use accesskit_winit;
|
|||||||
pub use egui;
|
pub use egui;
|
||||||
#[cfg(feature = "accesskit")]
|
#[cfg(feature = "accesskit")]
|
||||||
use egui::accesskit;
|
use egui::accesskit;
|
||||||
use egui::{
|
use egui::{Pos2, Rect, Vec2, ViewportBuilder, ViewportCommand, ViewportId, ViewportInfo};
|
||||||
Pos2, Rect, Vec2, ViewportBuilder, ViewportCommand, ViewportId, ViewportIdPair, ViewportInfo,
|
|
||||||
};
|
|
||||||
pub use winit;
|
pub use winit;
|
||||||
|
|
||||||
pub mod clipboard;
|
pub mod clipboard;
|
||||||
@@ -26,9 +24,14 @@ pub use window_settings::WindowSettings;
|
|||||||
|
|
||||||
use raw_window_handle::HasRawDisplayHandle;
|
use raw_window_handle::HasRawDisplayHandle;
|
||||||
|
|
||||||
pub fn native_pixels_per_point(window: &Window) -> f32 {
|
#[allow(unused_imports)]
|
||||||
window.scale_factor() as f32
|
pub(crate) use profiling_scopes::*;
|
||||||
}
|
|
||||||
|
use winit::{
|
||||||
|
dpi::{PhysicalPosition, PhysicalSize},
|
||||||
|
event_loop::EventLoopWindowTarget,
|
||||||
|
window::{CursorGrabMode, Window, WindowButtons, WindowLevel},
|
||||||
|
};
|
||||||
|
|
||||||
pub fn screen_size_in_pixels(window: &Window) -> egui::Vec2 {
|
pub fn screen_size_in_pixels(window: &Window) -> egui::Vec2 {
|
||||||
let size = window.inner_size();
|
let size = window.inner_size();
|
||||||
@@ -59,6 +62,7 @@ pub struct EventResponse {
|
|||||||
///
|
///
|
||||||
/// Instantiate one of these per viewport/window.
|
/// Instantiate one of these per viewport/window.
|
||||||
pub struct State {
|
pub struct State {
|
||||||
|
viewport_id: ViewportId,
|
||||||
start_time: web_time::Instant,
|
start_time: web_time::Instant,
|
||||||
egui_input: egui::RawInput,
|
egui_input: egui::RawInput,
|
||||||
pointer_pos_in_points: Option<egui::Pos2>,
|
pointer_pos_in_points: Option<egui::Pos2>,
|
||||||
@@ -93,6 +97,7 @@ pub struct State {
|
|||||||
impl State {
|
impl State {
|
||||||
/// Construct a new instance
|
/// Construct a new instance
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
viewport_id: ViewportId,
|
||||||
display_target: &dyn HasRawDisplayHandle,
|
display_target: &dyn HasRawDisplayHandle,
|
||||||
native_pixels_per_point: Option<f32>,
|
native_pixels_per_point: Option<f32>,
|
||||||
max_texture_side: Option<usize>,
|
max_texture_side: Option<usize>,
|
||||||
@@ -105,12 +110,13 @@ impl State {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut slf = Self {
|
let mut slf = Self {
|
||||||
|
viewport_id,
|
||||||
start_time: web_time::Instant::now(),
|
start_time: web_time::Instant::now(),
|
||||||
egui_input,
|
egui_input,
|
||||||
pointer_pos_in_points: None,
|
pointer_pos_in_points: None,
|
||||||
any_pointer_button_down: false,
|
any_pointer_button_down: false,
|
||||||
current_cursor_icon: None,
|
current_cursor_icon: None,
|
||||||
current_pixels_per_point: 1.0,
|
current_pixels_per_point: native_pixels_per_point.unwrap_or(1.0),
|
||||||
|
|
||||||
clipboard: clipboard::Clipboard::new(display_target),
|
clipboard: clipboard::Clipboard::new(display_target),
|
||||||
|
|
||||||
@@ -124,9 +130,13 @@ impl State {
|
|||||||
|
|
||||||
allow_ime: false,
|
allow_ime: false,
|
||||||
};
|
};
|
||||||
if let Some(native_pixels_per_point) = native_pixels_per_point {
|
|
||||||
slf.set_pixels_per_point(native_pixels_per_point);
|
slf.egui_input
|
||||||
}
|
.viewports
|
||||||
|
.entry(ViewportId::ROOT)
|
||||||
|
.or_default()
|
||||||
|
.native_pixels_per_point = native_pixels_per_point;
|
||||||
|
|
||||||
if let Some(max_texture_side) = max_texture_side {
|
if let Some(max_texture_side) = max_texture_side {
|
||||||
slf.set_max_texture_side(max_texture_side);
|
slf.set_max_texture_side(max_texture_side);
|
||||||
}
|
}
|
||||||
@@ -154,19 +164,6 @@ impl State {
|
|||||||
self.egui_input.max_texture_side = Some(max_texture_side);
|
self.egui_input.max_texture_side = Some(max_texture_side);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call this when a new native Window is created for rendering to initialize the `pixels_per_point`
|
|
||||||
/// for that window.
|
|
||||||
///
|
|
||||||
/// In particular, on Android it is necessary to call this after each `Resumed` lifecycle
|
|
||||||
/// event, each time a new native window is created.
|
|
||||||
///
|
|
||||||
/// Once this has been initialized for a new window then this state will be maintained by handling
|
|
||||||
/// [`winit::event::WindowEvent::ScaleFactorChanged`] events.
|
|
||||||
pub fn set_pixels_per_point(&mut self, pixels_per_point: f32) {
|
|
||||||
self.egui_input.pixels_per_point = Some(pixels_per_point);
|
|
||||||
self.current_pixels_per_point = pixels_per_point;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The number of physical pixels per logical point,
|
/// The number of physical pixels per logical point,
|
||||||
/// as configured on the current egui context (see [`egui::Context::pixels_per_point`]).
|
/// as configured on the current egui context (see [`egui::Context::pixels_per_point`]).
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -192,7 +189,7 @@ impl State {
|
|||||||
///
|
///
|
||||||
/// Call before [`Self::update_viewport_info`]
|
/// Call before [`Self::update_viewport_info`]
|
||||||
pub fn update_viewport_info(&self, info: &mut ViewportInfo, window: &Window) {
|
pub fn update_viewport_info(&self, info: &mut ViewportInfo, window: &Window) {
|
||||||
update_viewport_info(info, window, self.pixels_per_point());
|
update_viewport_info(info, window, self.current_pixels_per_point);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepare for a new frame by extracting the accumulated input,
|
/// Prepare for a new frame by extracting the accumulated input,
|
||||||
@@ -202,26 +199,30 @@ impl State {
|
|||||||
/// You need to set [`egui::RawInput::viewports`] yourself though.
|
/// You need to set [`egui::RawInput::viewports`] yourself though.
|
||||||
/// Use [`Self::update_viewport_info`] to update the info for each
|
/// Use [`Self::update_viewport_info`] to update the info for each
|
||||||
/// viewport.
|
/// viewport.
|
||||||
pub fn take_egui_input(&mut self, window: &Window, ids: ViewportIdPair) -> egui::RawInput {
|
pub fn take_egui_input(&mut self, window: &Window) -> egui::RawInput {
|
||||||
crate::profile_function!();
|
crate::profile_function!();
|
||||||
|
|
||||||
let pixels_per_point = self.pixels_per_point();
|
|
||||||
|
|
||||||
self.egui_input.time = Some(self.start_time.elapsed().as_secs_f64());
|
self.egui_input.time = Some(self.start_time.elapsed().as_secs_f64());
|
||||||
|
|
||||||
// On Windows, a minimized window will have 0 width and height.
|
// On Windows, a minimized window will have 0 width and height.
|
||||||
// See: https://github.com/rust-windowing/winit/issues/208
|
// See: https://github.com/rust-windowing/winit/issues/208
|
||||||
// This solves an issue where egui window positions would be changed when minimizing on Windows.
|
// This solves an issue where egui window positions would be changed when minimizing on Windows.
|
||||||
let screen_size_in_pixels = screen_size_in_pixels(window);
|
let screen_size_in_pixels = screen_size_in_pixels(window);
|
||||||
let screen_size_in_points = screen_size_in_pixels / pixels_per_point;
|
let screen_size_in_points = screen_size_in_pixels / self.current_pixels_per_point;
|
||||||
|
|
||||||
self.egui_input.screen_rect = (screen_size_in_points.x > 0.0
|
self.egui_input.screen_rect = (screen_size_in_points.x > 0.0
|
||||||
&& screen_size_in_points.y > 0.0)
|
&& screen_size_in_points.y > 0.0)
|
||||||
.then(|| Rect::from_min_size(Pos2::ZERO, screen_size_in_points));
|
.then(|| Rect::from_min_size(Pos2::ZERO, screen_size_in_points));
|
||||||
|
|
||||||
// Tell egui which viewport is now active:
|
// Tell egui which viewport is now active:
|
||||||
self.egui_input.viewport_ids = ids;
|
self.egui_input.viewport_id = self.viewport_id;
|
||||||
self.egui_input.native_pixels_per_point = Some(native_pixels_per_point(window));
|
|
||||||
|
self.egui_input
|
||||||
|
.viewports
|
||||||
|
.entry(self.viewport_id)
|
||||||
|
.or_default()
|
||||||
|
.native_pixels_per_point = Some(window.scale_factor() as f32);
|
||||||
|
|
||||||
self.egui_input.take()
|
self.egui_input.take()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,9 +239,14 @@ impl State {
|
|||||||
use winit::event::WindowEvent;
|
use winit::event::WindowEvent;
|
||||||
match event {
|
match event {
|
||||||
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
|
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
|
||||||
let pixels_per_point = *scale_factor as f32;
|
let native_pixels_per_point = *scale_factor as f32;
|
||||||
self.egui_input.pixels_per_point = Some(pixels_per_point);
|
|
||||||
self.current_pixels_per_point = pixels_per_point;
|
self.egui_input
|
||||||
|
.viewports
|
||||||
|
.entry(self.viewport_id)
|
||||||
|
.or_default()
|
||||||
|
.native_pixels_per_point = Some(native_pixels_per_point);
|
||||||
|
self.current_pixels_per_point = egui_ctx.zoom_factor() * native_pixels_per_point;
|
||||||
EventResponse {
|
EventResponse {
|
||||||
repaint: true,
|
repaint: true,
|
||||||
consumed: false,
|
consumed: false,
|
||||||
@@ -695,7 +701,6 @@ impl State {
|
|||||||
pub fn handle_platform_output(
|
pub fn handle_platform_output(
|
||||||
&mut self,
|
&mut self,
|
||||||
window: &Window,
|
window: &Window,
|
||||||
viewport_id: ViewportId,
|
|
||||||
egui_ctx: &egui::Context,
|
egui_ctx: &egui::Context,
|
||||||
platform_output: egui::PlatformOutput,
|
platform_output: egui::PlatformOutput,
|
||||||
) {
|
) {
|
||||||
@@ -712,7 +717,7 @@ impl State {
|
|||||||
accesskit_update,
|
accesskit_update,
|
||||||
} = platform_output;
|
} = platform_output;
|
||||||
|
|
||||||
self.current_pixels_per_point = egui_ctx.input_for(viewport_id, |i| i.pixels_per_point); // someone can have changed it to scale the UI
|
self.current_pixels_per_point = egui_ctx.pixels_per_point(); // someone can have changed it to scale the UI
|
||||||
|
|
||||||
self.set_cursor_icon(window, cursor_icon);
|
self.set_cursor_icon(window, cursor_icon);
|
||||||
|
|
||||||
@@ -838,15 +843,21 @@ fn update_viewport_info(viewport_info: &mut ViewportInfo, window: &Window, pixel
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
viewport_info.title = Some(window.title());
|
|
||||||
viewport_info.pixels_per_point = pixels_per_point;
|
|
||||||
viewport_info.monitor_size = monitor_size;
|
|
||||||
viewport_info.inner_rect = inner_rect;
|
|
||||||
viewport_info.outer_rect = outer_rect;
|
|
||||||
viewport_info.fullscreen = Some(window.fullscreen().is_some());
|
|
||||||
viewport_info.focused = Some(window.has_focus());
|
viewport_info.focused = Some(window.has_focus());
|
||||||
viewport_info.minimized = window.is_minimized().or(viewport_info.minimized);
|
viewport_info.fullscreen = Some(window.fullscreen().is_some());
|
||||||
|
viewport_info.inner_rect = inner_rect;
|
||||||
|
viewport_info.monitor_size = monitor_size;
|
||||||
|
viewport_info.native_pixels_per_point = Some(window.scale_factor() as f32);
|
||||||
|
viewport_info.outer_rect = outer_rect;
|
||||||
|
viewport_info.title = Some(window.title());
|
||||||
|
|
||||||
|
if false {
|
||||||
|
// It's tempting to do this, but it leads to a deadlock on Mac when running
|
||||||
|
// `cargo run -p custom_window_frame`.
|
||||||
|
// See https://github.com/emilk/egui/issues/3494
|
||||||
viewport_info.maximized = Some(window.is_maximized());
|
viewport_info.maximized = Some(window.is_maximized());
|
||||||
|
viewport_info.minimized = window.is_minimized().or(viewport_info.minimized);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_url_in_browser(_url: &str) {
|
fn open_url_in_browser(_url: &str) {
|
||||||
@@ -1035,21 +1046,49 @@ fn translate_cursor(cursor_icon: egui::CursorIcon) -> Option<winit::window::Curs
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
pub fn process_viewport_commands(
|
pub fn process_viewport_commands(
|
||||||
|
egui_ctx: &egui::Context,
|
||||||
info: &mut ViewportInfo,
|
info: &mut ViewportInfo,
|
||||||
commands: impl IntoIterator<Item = ViewportCommand>,
|
commands: impl IntoIterator<Item = ViewportCommand>,
|
||||||
window: &Window,
|
window: &Window,
|
||||||
is_viewport_focused: bool,
|
is_viewport_focused: bool,
|
||||||
screenshot_requested: &mut bool,
|
screenshot_requested: &mut bool,
|
||||||
|
) {
|
||||||
|
for command in commands {
|
||||||
|
process_viewport_command(
|
||||||
|
egui_ctx,
|
||||||
|
window,
|
||||||
|
command,
|
||||||
|
info,
|
||||||
|
is_viewport_focused,
|
||||||
|
screenshot_requested,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_viewport_command(
|
||||||
|
egui_ctx: &egui::Context,
|
||||||
|
window: &Window,
|
||||||
|
command: ViewportCommand,
|
||||||
|
info: &mut ViewportInfo,
|
||||||
|
is_viewport_focused: bool,
|
||||||
|
screenshot_requested: &mut bool,
|
||||||
) {
|
) {
|
||||||
crate::profile_function!();
|
crate::profile_function!();
|
||||||
|
|
||||||
use winit::window::ResizeDirection;
|
use winit::window::ResizeDirection;
|
||||||
|
|
||||||
for command in commands {
|
log::debug!("Processing ViewportCommand::{command:?}");
|
||||||
|
|
||||||
|
let egui_zoom_factor = egui_ctx.zoom_factor();
|
||||||
|
let pixels_per_point = egui_zoom_factor * window.scale_factor() as f32;
|
||||||
|
|
||||||
match command {
|
match command {
|
||||||
ViewportCommand::Close => {
|
ViewportCommand::Close => {
|
||||||
info.events.push(egui::ViewportEvent::Close);
|
info.events.push(egui::ViewportEvent::Close);
|
||||||
}
|
}
|
||||||
|
ViewportCommand::CancelClose => {
|
||||||
|
// Need to be handled elsewhere
|
||||||
|
}
|
||||||
ViewportCommand::StartDrag => {
|
ViewportCommand::StartDrag => {
|
||||||
// If `is_viewport_focused` is not checked on x11 the input will be permanently taken until the app is killed!
|
// If `is_viewport_focused` is not checked on x11 the input will be permanently taken until the app is killed!
|
||||||
|
|
||||||
@@ -1063,9 +1102,9 @@ pub fn process_viewport_commands(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ViewportCommand::InnerSize(size) => {
|
ViewportCommand::InnerSize(size) => {
|
||||||
let width = size.x.max(1.0);
|
let width_px = pixels_per_point * size.x.max(1.0);
|
||||||
let height = size.y.max(1.0);
|
let height_px = pixels_per_point * size.y.max(1.0);
|
||||||
let _ = window.request_inner_size(LogicalSize::new(width, height));
|
window.set_inner_size(PhysicalSize::new(width_px, height_px));
|
||||||
}
|
}
|
||||||
ViewportCommand::BeginResize(direction) => {
|
ViewportCommand::BeginResize(direction) => {
|
||||||
if let Err(err) = window.drag_resize_window(match direction {
|
if let Err(err) = window.drag_resize_window(match direction {
|
||||||
@@ -1086,20 +1125,25 @@ pub fn process_viewport_commands(
|
|||||||
ViewportCommand::Transparent(v) => window.set_transparent(v),
|
ViewportCommand::Transparent(v) => window.set_transparent(v),
|
||||||
ViewportCommand::Visible(v) => window.set_visible(v),
|
ViewportCommand::Visible(v) => window.set_visible(v),
|
||||||
ViewportCommand::OuterPosition(pos) => {
|
ViewportCommand::OuterPosition(pos) => {
|
||||||
window.set_outer_position(LogicalPosition::new(pos.x, pos.y));
|
window.set_outer_position(PhysicalPosition::new(
|
||||||
|
pixels_per_point * pos.x,
|
||||||
|
pixels_per_point * pos.y,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
ViewportCommand::MinInnerSize(s) => {
|
ViewportCommand::MinInnerSize(s) => {
|
||||||
window.set_min_inner_size(
|
window.set_min_inner_size((s.is_finite() && s != Vec2::ZERO).then_some(
|
||||||
(s.is_finite() && s != Vec2::ZERO).then_some(LogicalSize::new(s.x, s.y)),
|
PhysicalSize::new(pixels_per_point * s.x, pixels_per_point * s.y),
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
ViewportCommand::MaxInnerSize(s) => {
|
ViewportCommand::MaxInnerSize(s) => {
|
||||||
window.set_max_inner_size(
|
window.set_max_inner_size((s.is_finite() && s != Vec2::INFINITY).then_some(
|
||||||
(s.is_finite() && s != Vec2::INFINITY).then_some(LogicalSize::new(s.x, s.y)),
|
PhysicalSize::new(pixels_per_point * s.x, pixels_per_point * s.y),
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
ViewportCommand::ResizeIncrements(s) => {
|
ViewportCommand::ResizeIncrements(s) => {
|
||||||
window.set_resize_increments(s.map(|s| LogicalSize::new(s.x, s.y)));
|
window.set_resize_increments(
|
||||||
|
s.map(|s| PhysicalSize::new(pixels_per_point * s.x, pixels_per_point * s.y)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ViewportCommand::Resizable(v) => window.set_resizable(v),
|
ViewportCommand::Resizable(v) => window.set_resizable(v),
|
||||||
ViewportCommand::EnableButtons {
|
ViewportCommand::EnableButtons {
|
||||||
@@ -1145,14 +1189,10 @@ pub fn process_viewport_commands(
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
ViewportCommand::IMEPosition(pos) => {
|
ViewportCommand::IMEPosition(pos) => {
|
||||||
window.set_ime_cursor_area(
|
window.set_ime_position(PhysicalPosition::new(
|
||||||
LogicalPosition::new(pos.x, pos.y),
|
pixels_per_point * pos.x,
|
||||||
winit::dpi::LogicalSize {
|
pixels_per_point * pos.y,
|
||||||
// TODO: What size to use? New size arg in winit 0.29
|
));
|
||||||
width: 10,
|
|
||||||
height: 10,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
ViewportCommand::IMEAllowed(v) => window.set_ime_allowed(v),
|
ViewportCommand::IMEAllowed(v) => window.set_ime_allowed(v),
|
||||||
ViewportCommand::IMEPurpose(p) => window.set_ime_purpose(match p {
|
ViewportCommand::IMEPurpose(p) => window.set_ime_purpose(match p {
|
||||||
@@ -1183,7 +1223,10 @@ pub fn process_viewport_commands(
|
|||||||
}),
|
}),
|
||||||
ViewportCommand::ContentProtected(v) => window.set_content_protected(v),
|
ViewportCommand::ContentProtected(v) => window.set_content_protected(v),
|
||||||
ViewportCommand::CursorPosition(pos) => {
|
ViewportCommand::CursorPosition(pos) => {
|
||||||
if let Err(err) = window.set_cursor_position(LogicalPosition::new(pos.x, pos.y)) {
|
if let Err(err) = window.set_cursor_position(PhysicalPosition::new(
|
||||||
|
pixels_per_point * pos.x,
|
||||||
|
pixels_per_point * pos.y,
|
||||||
|
)) {
|
||||||
log::warn!("{command:?}: {err}");
|
log::warn!("{command:?}: {err}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1206,14 +1249,30 @@ pub fn process_viewport_commands(
|
|||||||
*screenshot_requested = true;
|
*screenshot_requested = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_winit_window_builder(
|
pub fn create_winit_window_builder<T>(
|
||||||
|
egui_ctx: &egui::Context,
|
||||||
|
event_loop: &EventLoopWindowTarget<T>,
|
||||||
viewport_builder: ViewportBuilder,
|
viewport_builder: ViewportBuilder,
|
||||||
) -> winit::window::WindowBuilder {
|
) -> winit::window::WindowBuilder {
|
||||||
crate::profile_function!();
|
crate::profile_function!();
|
||||||
|
|
||||||
|
// We set sizes and positions in egui:s own ui points, which depends on the egui
|
||||||
|
// zoom_factor and the native pixels per point, so we need to know that here.
|
||||||
|
let native_pixels_per_point = event_loop
|
||||||
|
.primary_monitor()
|
||||||
|
.or_else(|| event_loop.available_monitors().next())
|
||||||
|
.map_or_else(
|
||||||
|
|| {
|
||||||
|
log::debug!("Failed to find a monitor - assuming native_pixels_per_point of 1.0");
|
||||||
|
1.0
|
||||||
|
},
|
||||||
|
|m| m.scale_factor() as f32,
|
||||||
|
);
|
||||||
|
let zoom_factor = egui_ctx.zoom_factor();
|
||||||
|
let pixels_per_point = zoom_factor * native_pixels_per_point;
|
||||||
|
|
||||||
let ViewportBuilder {
|
let ViewportBuilder {
|
||||||
title,
|
title,
|
||||||
position,
|
position,
|
||||||
@@ -1233,13 +1292,18 @@ pub fn create_winit_window_builder(
|
|||||||
maximize_button,
|
maximize_button,
|
||||||
window_level,
|
window_level,
|
||||||
|
|
||||||
// only handled on some platforms:
|
// macOS:
|
||||||
title_hidden: _title_hidden,
|
|
||||||
titlebar_transparent: _titlebar_transparent,
|
|
||||||
fullsize_content_view: _fullsize_content_view,
|
fullsize_content_view: _fullsize_content_view,
|
||||||
app_id: _app_id,
|
title_shown: _title_shown,
|
||||||
|
titlebar_buttons_shown: _titlebar_buttons_shown,
|
||||||
|
titlebar_shown: _titlebar_shown,
|
||||||
|
|
||||||
|
// Windows:
|
||||||
drag_and_drop: _drag_and_drop,
|
drag_and_drop: _drag_and_drop,
|
||||||
|
|
||||||
|
// wayland:
|
||||||
|
app_id: _app_id,
|
||||||
|
|
||||||
mouse_passthrough: _, // handled in `apply_viewport_builder_to_new_window`
|
mouse_passthrough: _, // handled in `apply_viewport_builder_to_new_window`
|
||||||
} = viewport_builder;
|
} = viewport_builder;
|
||||||
|
|
||||||
@@ -1250,7 +1314,7 @@ pub fn create_winit_window_builder(
|
|||||||
.with_resizable(resizable.unwrap_or(true))
|
.with_resizable(resizable.unwrap_or(true))
|
||||||
.with_visible(visible.unwrap_or(true))
|
.with_visible(visible.unwrap_or(true))
|
||||||
.with_maximized(maximized.unwrap_or(false))
|
.with_maximized(maximized.unwrap_or(false))
|
||||||
.with_window_level(match window_level {
|
.with_window_level(match window_level.unwrap_or_default() {
|
||||||
egui::viewport::WindowLevel::AlwaysOnBottom => WindowLevel::AlwaysOnBottom,
|
egui::viewport::WindowLevel::AlwaysOnBottom => WindowLevel::AlwaysOnBottom,
|
||||||
egui::viewport::WindowLevel::AlwaysOnTop => WindowLevel::AlwaysOnTop,
|
egui::viewport::WindowLevel::AlwaysOnTop => WindowLevel::AlwaysOnTop,
|
||||||
egui::viewport::WindowLevel::Normal => WindowLevel::Normal,
|
egui::viewport::WindowLevel::Normal => WindowLevel::Normal,
|
||||||
@@ -1274,27 +1338,31 @@ pub fn create_winit_window_builder(
|
|||||||
.with_active(active.unwrap_or(true));
|
.with_active(active.unwrap_or(true));
|
||||||
|
|
||||||
if let Some(inner_size) = inner_size {
|
if let Some(inner_size) = inner_size {
|
||||||
window_builder = window_builder
|
window_builder = window_builder.with_inner_size(PhysicalSize::new(
|
||||||
.with_inner_size(winit::dpi::LogicalSize::new(inner_size.x, inner_size.y));
|
pixels_per_point * inner_size.x,
|
||||||
|
pixels_per_point * inner_size.y,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(min_inner_size) = min_inner_size {
|
if let Some(min_inner_size) = min_inner_size {
|
||||||
window_builder = window_builder.with_min_inner_size(winit::dpi::LogicalSize::new(
|
window_builder = window_builder.with_min_inner_size(PhysicalSize::new(
|
||||||
min_inner_size.x,
|
pixels_per_point * min_inner_size.x,
|
||||||
min_inner_size.y,
|
pixels_per_point * min_inner_size.y,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(max_inner_size) = max_inner_size {
|
if let Some(max_inner_size) = max_inner_size {
|
||||||
window_builder = window_builder.with_max_inner_size(winit::dpi::LogicalSize::new(
|
window_builder = window_builder.with_max_inner_size(PhysicalSize::new(
|
||||||
max_inner_size.x,
|
pixels_per_point * max_inner_size.x,
|
||||||
max_inner_size.y,
|
pixels_per_point * max_inner_size.y,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(position) = position {
|
if let Some(position) = position {
|
||||||
window_builder =
|
window_builder = window_builder.with_position(PhysicalPosition::new(
|
||||||
window_builder.with_position(winit::dpi::LogicalPosition::new(position.x, position.y));
|
pixels_per_point * position.x,
|
||||||
|
pixels_per_point * position.y,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(icon) = icon {
|
if let Some(icon) = icon {
|
||||||
@@ -1320,8 +1388,9 @@ pub fn create_winit_window_builder(
|
|||||||
{
|
{
|
||||||
use winit::platform::macos::WindowBuilderExtMacOS as _;
|
use winit::platform::macos::WindowBuilderExtMacOS as _;
|
||||||
window_builder = window_builder
|
window_builder = window_builder
|
||||||
.with_title_hidden(_title_hidden.unwrap_or(false))
|
.with_title_hidden(!_title_shown.unwrap_or(true))
|
||||||
.with_titlebar_transparent(_titlebar_transparent.unwrap_or(false))
|
.with_titlebar_buttons_hidden(!_titlebar_buttons_shown.unwrap_or(true))
|
||||||
|
.with_titlebar_transparent(!_titlebar_shown.unwrap_or(true))
|
||||||
.with_fullsize_content_view(_fullsize_content_view.unwrap_or(false));
|
.with_fullsize_content_view(_fullsize_content_view.unwrap_or(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1432,10 +1501,3 @@ mod profiling_scopes {
|
|||||||
}
|
}
|
||||||
pub(crate) use profile_scope;
|
pub(crate) use profile_scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
pub(crate) use profiling_scopes::*;
|
|
||||||
use winit::{
|
|
||||||
dpi::{LogicalPosition, LogicalSize},
|
|
||||||
window::{CursorGrabMode, Window, WindowButtons, WindowLevel},
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ pub struct WindowSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WindowSettings {
|
impl WindowSettings {
|
||||||
pub fn from_display(window: &winit::window::Window) -> Self {
|
pub fn from_window(egui_zoom_factor: f32, window: &winit::window::Window) -> Self {
|
||||||
let inner_size_points = window.inner_size().to_logical::<f32>(window.scale_factor());
|
let inner_size_points = window
|
||||||
|
.inner_size()
|
||||||
|
.to_logical::<f32>(egui_zoom_factor as f64 * window.scale_factor());
|
||||||
|
|
||||||
let inner_position_pixels = window
|
let inner_position_pixels = window
|
||||||
.inner_position()
|
.inner_position()
|
||||||
@@ -100,6 +102,7 @@ impl WindowSettings {
|
|||||||
|
|
||||||
pub fn clamp_position_to_monitors<E>(
|
pub fn clamp_position_to_monitors<E>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
egui_zoom_factor: f32,
|
||||||
event_loop: &winit::event_loop::EventLoopWindowTarget<E>,
|
event_loop: &winit::event_loop::EventLoopWindowTarget<E>,
|
||||||
) {
|
) {
|
||||||
// If the app last ran on two monitors and only one is now connected, then
|
// If the app last ran on two monitors and only one is now connected, then
|
||||||
@@ -116,15 +119,16 @@ impl WindowSettings {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(pos_px) = &mut self.inner_position_pixels {
|
if let Some(pos_px) = &mut self.inner_position_pixels {
|
||||||
clamp_pos_to_monitors(event_loop, inner_size_points, pos_px);
|
clamp_pos_to_monitors(egui_zoom_factor, event_loop, inner_size_points, pos_px);
|
||||||
}
|
}
|
||||||
if let Some(pos_px) = &mut self.outer_position_pixels {
|
if let Some(pos_px) = &mut self.outer_position_pixels {
|
||||||
clamp_pos_to_monitors(event_loop, inner_size_points, pos_px);
|
clamp_pos_to_monitors(egui_zoom_factor, event_loop, inner_size_points, pos_px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clamp_pos_to_monitors<E>(
|
fn clamp_pos_to_monitors<E>(
|
||||||
|
egui_zoom_factor: f32,
|
||||||
event_loop: &winit::event_loop::EventLoopWindowTarget<E>,
|
event_loop: &winit::event_loop::EventLoopWindowTarget<E>,
|
||||||
window_size_pts: egui::Vec2,
|
window_size_pts: egui::Vec2,
|
||||||
position_px: &mut egui::Pos2,
|
position_px: &mut egui::Pos2,
|
||||||
@@ -142,7 +146,7 @@ fn clamp_pos_to_monitors<E>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
for monitor in monitors {
|
for monitor in monitors {
|
||||||
let window_size_px = window_size_pts * (monitor.scale_factor() as f32);
|
let window_size_px = window_size_pts * (egui_zoom_factor * monitor.scale_factor() as f32);
|
||||||
let monitor_x_range = (monitor.position().x - window_size_px.x as i32)
|
let monitor_x_range = (monitor.position().x - window_size_px.x as i32)
|
||||||
..(monitor.position().x + monitor.size().width as i32);
|
..(monitor.position().x + monitor.size().width as i32);
|
||||||
let monitor_y_range = (monitor.position().y - window_size_px.y as i32)
|
let monitor_y_range = (monitor.position().y - window_size_px.y as i32)
|
||||||
@@ -155,10 +159,14 @@ fn clamp_pos_to_monitors<E>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut window_size_px = window_size_pts * (active_monitor.scale_factor() as f32);
|
let mut window_size_px =
|
||||||
|
window_size_pts * (egui_zoom_factor * active_monitor.scale_factor() as f32);
|
||||||
// Add size of title bar. This is 32 px by default in Win 10/11.
|
// Add size of title bar. This is 32 px by default in Win 10/11.
|
||||||
if cfg!(target_os = "windows") {
|
if cfg!(target_os = "windows") {
|
||||||
window_size_px += egui::Vec2::new(0.0, 32.0 * active_monitor.scale_factor() as f32);
|
window_size_px += egui::Vec2::new(
|
||||||
|
0.0,
|
||||||
|
32.0 * egui_zoom_factor * active_monitor.scale_factor() as f32,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let monitor_position = egui::Pos2::new(
|
let monitor_position = egui::Pos2::new(
|
||||||
active_monitor.position().x as f32,
|
active_monitor.position().x as f32,
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "egui"
|
name = "egui"
|
||||||
version = "0.23.0"
|
version.workspace = true
|
||||||
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
||||||
description = "An easy-to-use immediate mode GUI that runs on both web and native"
|
description = "An easy-to-use immediate mode GUI that runs on both web and native"
|
||||||
edition = "2021"
|
edition.workspace = true
|
||||||
rust-version = "1.72"
|
rust-version.workspace = true
|
||||||
homepage = "https://github.com/emilk/egui"
|
homepage = "https://github.com/emilk/egui"
|
||||||
license = "MIT OR Apache-2.0"
|
license.workspace = true
|
||||||
readme = "../../README.md"
|
readme = "../../README.md"
|
||||||
repository = "https://github.com/emilk/egui"
|
repository = "https://github.com/emilk/egui"
|
||||||
categories = ["gui", "game-development"]
|
categories = ["gui", "game-development"]
|
||||||
@@ -76,7 +76,7 @@ unity = ["epaint/unity"]
|
|||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
epaint = { version = "0.23.0", path = "../epaint", default-features = false }
|
epaint = { version = "0.24.0", path = "../epaint", default-features = false }
|
||||||
|
|
||||||
ahash = { version = "0.8.6", default-features = false, features = [
|
ahash = { version = "0.8.6", default-features = false, features = [
|
||||||
"no-rng", # we don't need DOS-protection, so we let users opt-in to it instead
|
"no-rng", # we don't need DOS-protection, so we let users opt-in to it instead
|
||||||
|
|||||||
@@ -200,6 +200,9 @@ struct ContextImpl {
|
|||||||
animation_manager: AnimationManager,
|
animation_manager: AnimationManager,
|
||||||
tex_manager: WrappedTextureManager,
|
tex_manager: WrappedTextureManager,
|
||||||
|
|
||||||
|
/// Set during the frame, becomes active at the start of the next frame.
|
||||||
|
new_zoom_factor: Option<f32>,
|
||||||
|
|
||||||
os: OperatingSystem,
|
os: OperatingSystem,
|
||||||
|
|
||||||
/// How deeply nested are we?
|
/// How deeply nested are we?
|
||||||
@@ -227,8 +230,15 @@ struct ContextImpl {
|
|||||||
|
|
||||||
impl ContextImpl {
|
impl ContextImpl {
|
||||||
fn begin_frame_mut(&mut self, mut new_raw_input: RawInput) {
|
fn begin_frame_mut(&mut self, mut new_raw_input: RawInput) {
|
||||||
let ids = new_raw_input.viewport_ids;
|
let viewport_id = new_raw_input.viewport_id;
|
||||||
let viewport_id = ids.this;
|
let parent_id = new_raw_input
|
||||||
|
.viewports
|
||||||
|
.get(&viewport_id)
|
||||||
|
.and_then(|v| v.parent)
|
||||||
|
.unwrap_or_default();
|
||||||
|
let ids = ViewportIdPair::from_self_and_parent(viewport_id, parent_id);
|
||||||
|
|
||||||
|
let is_outermost_viewport = self.viewport_stack.is_empty(); // not necessarily root, just outermost immediate viewport
|
||||||
self.viewport_stack.push(ids);
|
self.viewport_stack.push(ids);
|
||||||
let viewport = self.viewports.entry(viewport_id).or_default();
|
let viewport = self.viewports.entry(viewport_id).or_default();
|
||||||
|
|
||||||
@@ -247,19 +257,26 @@ impl ContextImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(new_pixels_per_point) = self.memory.override_pixels_per_point {
|
if is_outermost_viewport {
|
||||||
if viewport.input.pixels_per_point != new_pixels_per_point {
|
if let Some(new_zoom_factor) = self.new_zoom_factor.take() {
|
||||||
new_raw_input.pixels_per_point = Some(new_pixels_per_point);
|
let ratio = self.memory.options.zoom_factor / new_zoom_factor;
|
||||||
|
self.memory.options.zoom_factor = new_zoom_factor;
|
||||||
|
|
||||||
let input = &viewport.input;
|
let input = &viewport.input;
|
||||||
// This is a bit hacky, but is required to avoid jitter:
|
// This is a bit hacky, but is required to avoid jitter:
|
||||||
let ratio = input.pixels_per_point / new_pixels_per_point;
|
|
||||||
let mut rect = input.screen_rect;
|
let mut rect = input.screen_rect;
|
||||||
rect.min = (ratio * rect.min.to_vec2()).to_pos2();
|
rect.min = (ratio * rect.min.to_vec2()).to_pos2();
|
||||||
rect.max = (ratio * rect.max.to_vec2()).to_pos2();
|
rect.max = (ratio * rect.max.to_vec2()).to_pos2();
|
||||||
new_raw_input.screen_rect = Some(rect);
|
new_raw_input.screen_rect = Some(rect);
|
||||||
|
// We should really scale everything else in the input too,
|
||||||
|
// but the `screen_rect` is the most important part.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let pixels_per_point = self.memory.options.zoom_factor
|
||||||
|
* new_raw_input
|
||||||
|
.viewport()
|
||||||
|
.native_pixels_per_point
|
||||||
|
.unwrap_or(1.0);
|
||||||
|
|
||||||
viewport.layer_rects_prev_frame = std::mem::take(&mut viewport.layer_rects_this_frame);
|
viewport.layer_rects_prev_frame = std::mem::take(&mut viewport.layer_rects_this_frame);
|
||||||
|
|
||||||
@@ -270,8 +287,11 @@ impl ContextImpl {
|
|||||||
self.memory
|
self.memory
|
||||||
.begin_frame(&viewport.input, &new_raw_input, &all_viewport_ids);
|
.begin_frame(&viewport.input, &new_raw_input, &all_viewport_ids);
|
||||||
|
|
||||||
viewport.input = std::mem::take(&mut viewport.input)
|
viewport.input = std::mem::take(&mut viewport.input).begin_frame(
|
||||||
.begin_frame(new_raw_input, viewport.repaint.requested_last_frame);
|
new_raw_input,
|
||||||
|
viewport.repaint.requested_last_frame,
|
||||||
|
pixels_per_point,
|
||||||
|
);
|
||||||
|
|
||||||
viewport.frame_state.begin_frame(&viewport.input);
|
viewport.frame_state.begin_frame(&viewport.input);
|
||||||
|
|
||||||
@@ -464,13 +484,11 @@ impl std::cmp::PartialEq for Context {
|
|||||||
|
|
||||||
impl Default for Context {
|
impl Default for Context {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let s = Self(Arc::new(RwLock::new(ContextImpl::default())));
|
let ctx = ContextImpl {
|
||||||
|
embed_viewports: true,
|
||||||
s.write(|ctx| {
|
..Default::default()
|
||||||
ctx.embed_viewports = true;
|
};
|
||||||
});
|
Self(Arc::new(RwLock::new(ctx)))
|
||||||
|
|
||||||
s
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1333,44 +1351,90 @@ impl Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The number of physical pixels for each logical point.
|
/// The number of physical pixels for each logical point.
|
||||||
|
///
|
||||||
|
/// This is calculated as [`Self::zoom_factor`] * [`Self::native_pixels_per_point`]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn pixels_per_point(&self) -> f32 {
|
pub fn pixels_per_point(&self) -> f32 {
|
||||||
self.input(|i| i.pixels_per_point())
|
self.input(|i| i.pixels_per_point)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the number of physical pixels for each logical point.
|
/// Set the number of physical pixels for each logical point.
|
||||||
/// Will become active at the start of the next frame.
|
/// Will become active at the start of the next frame.
|
||||||
///
|
///
|
||||||
/// Note that this may be overwritten by input from the integration via [`RawInput::pixels_per_point`].
|
/// This will actually translate to a call to [`Self::set_zoom_factor`].
|
||||||
/// For instance, when using `eframe` on web, the browsers native zoom level will always be used.
|
|
||||||
pub fn set_pixels_per_point(&self, pixels_per_point: f32) {
|
pub fn set_pixels_per_point(&self, pixels_per_point: f32) {
|
||||||
if pixels_per_point != self.pixels_per_point() {
|
if pixels_per_point != self.pixels_per_point() {
|
||||||
self.write(|ctx| {
|
self.set_zoom_factor(pixels_per_point / self.native_pixels_per_point().unwrap_or(1.0));
|
||||||
ctx.memory.override_pixels_per_point = Some(pixels_per_point);
|
|
||||||
for id in ctx.all_viewport_ids() {
|
|
||||||
ctx.request_repaint(id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The number of physical pixels for each logical point on this monitor.
|
||||||
|
///
|
||||||
|
/// This is given as input to egui via [`ViewportInfo::native_pixels_per_point`]
|
||||||
|
/// and cannot be changed.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn native_pixels_per_point(&self) -> Option<f32> {
|
||||||
|
self.input(|i| i.viewport().native_pixels_per_point)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Global zoom factor of the UI.
|
||||||
|
///
|
||||||
|
/// This is used to calculate the `pixels_per_point`
|
||||||
|
/// for the UI as `pixels_per_point = zoom_fator * native_pixels_per_point`.
|
||||||
|
///
|
||||||
|
/// The default is 1.0.
|
||||||
|
/// Make larger to make everything larger.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn zoom_factor(&self) -> f32 {
|
||||||
|
self.options(|o| o.zoom_factor)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets zoom factor of the UI.
|
||||||
|
/// Will become active at the start of the next frame.
|
||||||
|
///
|
||||||
|
/// Note that calling this will not update [`Self::zoom_factor`] until the end of the frame.
|
||||||
|
///
|
||||||
|
/// This is used to calculate the `pixels_per_point`
|
||||||
|
/// for the UI as `pixels_per_point = zoom_fator * native_pixels_per_point`.
|
||||||
|
///
|
||||||
|
/// The default is 1.0.
|
||||||
|
/// Make larger to make everything larger.
|
||||||
|
///
|
||||||
|
/// It is better to call this than modifying
|
||||||
|
/// [`Options::zoom_factor`].
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_zoom_factor(&self, zoom_factor: f32) {
|
||||||
|
self.write(|ctx| {
|
||||||
|
if ctx.memory.options.zoom_factor != zoom_factor {
|
||||||
|
ctx.new_zoom_factor = Some(zoom_factor);
|
||||||
|
for id in ctx.all_viewport_ids() {
|
||||||
|
ctx.request_repaint(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Useful for pixel-perfect rendering
|
/// Useful for pixel-perfect rendering
|
||||||
|
#[inline]
|
||||||
pub(crate) fn round_to_pixel(&self, point: f32) -> f32 {
|
pub(crate) fn round_to_pixel(&self, point: f32) -> f32 {
|
||||||
let pixels_per_point = self.pixels_per_point();
|
let pixels_per_point = self.pixels_per_point();
|
||||||
(point * pixels_per_point).round() / pixels_per_point
|
(point * pixels_per_point).round() / pixels_per_point
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Useful for pixel-perfect rendering
|
/// Useful for pixel-perfect rendering
|
||||||
|
#[inline]
|
||||||
pub(crate) fn round_pos_to_pixels(&self, pos: Pos2) -> Pos2 {
|
pub(crate) fn round_pos_to_pixels(&self, pos: Pos2) -> Pos2 {
|
||||||
pos2(self.round_to_pixel(pos.x), self.round_to_pixel(pos.y))
|
pos2(self.round_to_pixel(pos.x), self.round_to_pixel(pos.y))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Useful for pixel-perfect rendering
|
/// Useful for pixel-perfect rendering
|
||||||
|
#[inline]
|
||||||
pub(crate) fn round_vec_to_pixels(&self, vec: Vec2) -> Vec2 {
|
pub(crate) fn round_vec_to_pixels(&self, vec: Vec2) -> Vec2 {
|
||||||
vec2(self.round_to_pixel(vec.x), self.round_to_pixel(vec.y))
|
vec2(self.round_to_pixel(vec.x), self.round_to_pixel(vec.y))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Useful for pixel-perfect rendering
|
/// Useful for pixel-perfect rendering
|
||||||
|
#[inline]
|
||||||
pub(crate) fn round_rect_to_pixels(&self, rect: Rect) -> Rect {
|
pub(crate) fn round_rect_to_pixels(&self, rect: Rect) -> Rect {
|
||||||
Rect {
|
Rect {
|
||||||
min: self.round_pos_to_pixels(rect.min),
|
min: self.round_pos_to_pixels(rect.min),
|
||||||
@@ -1491,6 +1555,11 @@ impl Context {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn end_frame(&self) -> FullOutput {
|
pub fn end_frame(&self) -> FullOutput {
|
||||||
crate::profile_function!();
|
crate::profile_function!();
|
||||||
|
|
||||||
|
if self.options(|o| o.zoom_with_keyboard) {
|
||||||
|
crate::gui_zoom::zoom_with_keyboard(self);
|
||||||
|
}
|
||||||
|
|
||||||
self.write(|ctx| ctx.end_frame())
|
self.write(|ctx| ctx.end_frame())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use epaint::ColorImage;
|
use epaint::ColorImage;
|
||||||
|
|
||||||
use crate::{emath::*, ViewportIdMap, ViewportIdPair};
|
use crate::{emath::*, ViewportId, ViewportIdMap};
|
||||||
|
|
||||||
/// What the integrations provides to egui at the start of each frame.
|
/// What the integrations provides to egui at the start of each frame.
|
||||||
///
|
///
|
||||||
@@ -11,12 +11,15 @@ use crate::{emath::*, ViewportIdMap, ViewportIdPair};
|
|||||||
/// You can check if `egui` is using the inputs using
|
/// You can check if `egui` is using the inputs using
|
||||||
/// [`crate::Context::wants_pointer_input`] and [`crate::Context::wants_keyboard_input`].
|
/// [`crate::Context::wants_pointer_input`] and [`crate::Context::wants_keyboard_input`].
|
||||||
///
|
///
|
||||||
/// All coordinates are in points (logical pixels) with origin (0, 0) in the top left corner.
|
/// All coordinates are in points (logical pixels) with origin (0, 0) in the top left .corner.
|
||||||
|
///
|
||||||
|
/// Ii "points" can be calculated from native physical pixels
|
||||||
|
/// using `pixels_per_point` = [`crate::Context::zoom_factor`] * `native_pixels_per_point`;
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
pub struct RawInput {
|
pub struct RawInput {
|
||||||
/// The id of the active viewport, and out parent.
|
/// The id of the active viewport.
|
||||||
pub viewport_ids: ViewportIdPair,
|
pub viewport_id: ViewportId,
|
||||||
|
|
||||||
/// Information about all egui viewports.
|
/// Information about all egui viewports.
|
||||||
pub viewports: ViewportIdMap<ViewportInfo>,
|
pub viewports: ViewportIdMap<ViewportInfo>,
|
||||||
@@ -31,20 +34,6 @@ pub struct RawInput {
|
|||||||
/// `None` will be treated as "same as last frame", with the default being a very big area.
|
/// `None` will be treated as "same as last frame", with the default being a very big area.
|
||||||
pub screen_rect: Option<Rect>,
|
pub screen_rect: Option<Rect>,
|
||||||
|
|
||||||
/// Also known as device pixel ratio, > 1 for high resolution screens.
|
|
||||||
///
|
|
||||||
/// If text looks blurry you probably forgot to set this.
|
|
||||||
/// Set this the first frame, whenever it changes, or just on every frame.
|
|
||||||
pub pixels_per_point: Option<f32>,
|
|
||||||
|
|
||||||
/// The OS native pixels-per-point.
|
|
||||||
///
|
|
||||||
/// This should always be set, if known.
|
|
||||||
///
|
|
||||||
/// On web this takes browser scaling into account,
|
|
||||||
/// and orresponds to [`window.devicePixelRatio`](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) in JavaScript.
|
|
||||||
pub native_pixels_per_point: Option<f32>,
|
|
||||||
|
|
||||||
/// Maximum size of one side of the font texture.
|
/// Maximum size of one side of the font texture.
|
||||||
///
|
///
|
||||||
/// Ask your graphics drivers about this. This corresponds to `GL_MAX_TEXTURE_SIZE`.
|
/// Ask your graphics drivers about this. This corresponds to `GL_MAX_TEXTURE_SIZE`.
|
||||||
@@ -89,11 +78,9 @@ pub struct RawInput {
|
|||||||
impl Default for RawInput {
|
impl Default for RawInput {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
viewport_ids: Default::default(),
|
viewport_id: ViewportId::ROOT,
|
||||||
viewports: Default::default(),
|
viewports: std::iter::once((ViewportId::ROOT, Default::default())).collect(),
|
||||||
screen_rect: None,
|
screen_rect: None,
|
||||||
pixels_per_point: None,
|
|
||||||
native_pixels_per_point: None,
|
|
||||||
max_texture_side: None,
|
max_texture_side: None,
|
||||||
time: None,
|
time: None,
|
||||||
predicted_dt: 1.0 / 60.0,
|
predicted_dt: 1.0 / 60.0,
|
||||||
@@ -107,17 +94,21 @@ impl Default for RawInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RawInput {
|
impl RawInput {
|
||||||
|
/// Info about the active viewport
|
||||||
|
#[inline]
|
||||||
|
pub fn viewport(&self) -> &ViewportInfo {
|
||||||
|
self.viewports.get(&self.viewport_id).expect("Failed to find current viewport in egui RawInput. This is the fault of the egui backend")
|
||||||
|
}
|
||||||
|
|
||||||
/// Helper: move volatile (deltas and events), clone the rest.
|
/// Helper: move volatile (deltas and events), clone the rest.
|
||||||
///
|
///
|
||||||
/// * [`Self::hovered_files`] is cloned.
|
/// * [`Self::hovered_files`] is cloned.
|
||||||
/// * [`Self::dropped_files`] is moved.
|
/// * [`Self::dropped_files`] is moved.
|
||||||
pub fn take(&mut self) -> RawInput {
|
pub fn take(&mut self) -> RawInput {
|
||||||
RawInput {
|
RawInput {
|
||||||
viewport_ids: self.viewport_ids,
|
viewport_id: self.viewport_id,
|
||||||
viewports: self.viewports.clone(),
|
viewports: self.viewports.clone(),
|
||||||
screen_rect: self.screen_rect.take(),
|
screen_rect: self.screen_rect.take(),
|
||||||
pixels_per_point: self.pixels_per_point.take(), // take the diff
|
|
||||||
native_pixels_per_point: self.native_pixels_per_point, // copy
|
|
||||||
max_texture_side: self.max_texture_side.take(),
|
max_texture_side: self.max_texture_side.take(),
|
||||||
time: self.time.take(),
|
time: self.time.take(),
|
||||||
predicted_dt: self.predicted_dt,
|
predicted_dt: self.predicted_dt,
|
||||||
@@ -132,11 +123,9 @@ impl RawInput {
|
|||||||
/// Add on new input.
|
/// Add on new input.
|
||||||
pub fn append(&mut self, newer: Self) {
|
pub fn append(&mut self, newer: Self) {
|
||||||
let Self {
|
let Self {
|
||||||
viewport_ids,
|
viewport_id: viewport_ids,
|
||||||
viewports,
|
viewports,
|
||||||
screen_rect,
|
screen_rect,
|
||||||
pixels_per_point,
|
|
||||||
native_pixels_per_point,
|
|
||||||
max_texture_side,
|
max_texture_side,
|
||||||
time,
|
time,
|
||||||
predicted_dt,
|
predicted_dt,
|
||||||
@@ -147,11 +136,9 @@ impl RawInput {
|
|||||||
focused,
|
focused,
|
||||||
} = newer;
|
} = newer;
|
||||||
|
|
||||||
self.viewport_ids = viewport_ids;
|
self.viewport_id = viewport_ids;
|
||||||
self.viewports = viewports;
|
self.viewports = viewports;
|
||||||
self.screen_rect = screen_rect.or(self.screen_rect);
|
self.screen_rect = screen_rect.or(self.screen_rect);
|
||||||
self.pixels_per_point = pixels_per_point.or(self.pixels_per_point);
|
|
||||||
self.native_pixels_per_point = native_pixels_per_point.or(self.native_pixels_per_point);
|
|
||||||
self.max_texture_side = max_texture_side.or(self.max_texture_side);
|
self.max_texture_side = max_texture_side.or(self.max_texture_side);
|
||||||
self.time = time; // use latest time
|
self.time = time; // use latest time
|
||||||
self.predicted_dt = predicted_dt; // use latest dt
|
self.predicted_dt = predicted_dt; // use latest dt
|
||||||
@@ -169,16 +156,23 @@ impl RawInput {
|
|||||||
pub enum ViewportEvent {
|
pub enum ViewportEvent {
|
||||||
/// The user clicked the close-button on the window, or similar.
|
/// The user clicked the close-button on the window, or similar.
|
||||||
///
|
///
|
||||||
/// It is up to the user to react to this by _not_ showing the viewport in the next frame in the parent viewport.
|
/// If this is the root viewport, the application will exit
|
||||||
|
/// after this frame unless you send a
|
||||||
|
/// [`crate::ViewportCommand::CancelClose`] command.
|
||||||
|
///
|
||||||
|
/// If this is not the root viewport,
|
||||||
|
/// it is up to the user to hide this viewport the next frame.
|
||||||
///
|
///
|
||||||
/// This even will wake up both the child and parent viewport.
|
/// This even will wake up both the child and parent viewport.
|
||||||
Close,
|
Close,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information about the current viewport,
|
/// Information about the current viewport, given as input each frame.
|
||||||
/// given as input each frame.
|
|
||||||
///
|
///
|
||||||
/// `None` means "unknown".
|
/// `None` means "unknown".
|
||||||
|
///
|
||||||
|
/// All units are in ui "points", which can be calculated from native physical pixels
|
||||||
|
/// using `pixels_per_point` = [`crate::Context::zoom_factor`] * `[Self::native_pixels_per_point`];
|
||||||
#[derive(Clone, Debug, Default, PartialEq)]
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
pub struct ViewportInfo {
|
pub struct ViewportInfo {
|
||||||
@@ -190,8 +184,13 @@ pub struct ViewportInfo {
|
|||||||
|
|
||||||
pub events: Vec<ViewportEvent>,
|
pub events: Vec<ViewportEvent>,
|
||||||
|
|
||||||
/// Number of physical pixels per ui point.
|
/// The OS native pixels-per-point.
|
||||||
pub pixels_per_point: f32,
|
///
|
||||||
|
/// This should always be set, if known.
|
||||||
|
///
|
||||||
|
/// On web this takes browser scaling into account,
|
||||||
|
/// and orresponds to [`window.devicePixelRatio`](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) in JavaScript.
|
||||||
|
pub native_pixels_per_point: Option<f32>,
|
||||||
|
|
||||||
/// Current monitor size in egui points.
|
/// Current monitor size in egui points.
|
||||||
pub monitor_size: Option<Vec2>,
|
pub monitor_size: Option<Vec2>,
|
||||||
@@ -222,6 +221,14 @@ pub struct ViewportInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ViewportInfo {
|
impl ViewportInfo {
|
||||||
|
/// This viewport has been told to close.
|
||||||
|
///
|
||||||
|
/// If this is the root viewport, the application will exit
|
||||||
|
/// after this frame unless you send a
|
||||||
|
/// [`crate::ViewportCommand::CancelClose`] command.
|
||||||
|
///
|
||||||
|
/// If this is not the root viewport,
|
||||||
|
/// it is up to the user to hide this viewport the next frame.
|
||||||
pub fn close_requested(&self) -> bool {
|
pub fn close_requested(&self) -> bool {
|
||||||
self.events
|
self.events
|
||||||
.iter()
|
.iter()
|
||||||
@@ -233,7 +240,7 @@ impl ViewportInfo {
|
|||||||
parent,
|
parent,
|
||||||
title,
|
title,
|
||||||
events,
|
events,
|
||||||
pixels_per_point,
|
native_pixels_per_point,
|
||||||
monitor_size,
|
monitor_size,
|
||||||
inner_rect,
|
inner_rect,
|
||||||
outer_rect,
|
outer_rect,
|
||||||
@@ -256,8 +263,8 @@ impl ViewportInfo {
|
|||||||
ui.label(format!("{events:?}"));
|
ui.label(format!("{events:?}"));
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
|
|
||||||
ui.label("Pixels per point:");
|
ui.label("Native pixels-per-point:");
|
||||||
ui.label(pixels_per_point.to_string());
|
ui.label(opt_as_str(native_pixels_per_point));
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
|
|
||||||
ui.label("Monitor size:");
|
ui.label("Monitor size:");
|
||||||
@@ -1106,11 +1113,9 @@ fn format_kb_shortcut() {
|
|||||||
impl RawInput {
|
impl RawInput {
|
||||||
pub fn ui(&self, ui: &mut crate::Ui) {
|
pub fn ui(&self, ui: &mut crate::Ui) {
|
||||||
let Self {
|
let Self {
|
||||||
viewport_ids,
|
viewport_id,
|
||||||
viewports,
|
viewports,
|
||||||
screen_rect,
|
screen_rect,
|
||||||
pixels_per_point,
|
|
||||||
native_pixels_per_point,
|
|
||||||
max_texture_side,
|
max_texture_side,
|
||||||
time,
|
time,
|
||||||
predicted_dt,
|
predicted_dt,
|
||||||
@@ -1121,10 +1126,7 @@ impl RawInput {
|
|||||||
focused,
|
focused,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
ui.label(format!(
|
ui.label(format!("Active viwport: {viewport_id:?}"));
|
||||||
"Active viwport: {:?}, parent: {:?}",
|
|
||||||
viewport_ids.this, viewport_ids.parent,
|
|
||||||
));
|
|
||||||
for (id, viewport) in viewports {
|
for (id, viewport) in viewports {
|
||||||
ui.group(|ui| {
|
ui.group(|ui| {
|
||||||
ui.label(format!("Viewport {id:?}"));
|
ui.label(format!("Viewport {id:?}"));
|
||||||
@@ -1134,16 +1136,7 @@ impl RawInput {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
ui.label(format!("screen_rect: {screen_rect:?} points"));
|
ui.label(format!("screen_rect: {screen_rect:?} points"));
|
||||||
ui.label(format!("pixels_per_point: {pixels_per_point:?}"))
|
|
||||||
.on_hover_text(
|
|
||||||
"Also called HDPI factor.\nNumber of physical pixels per each logical pixel.",
|
|
||||||
);
|
|
||||||
ui.label(format!(
|
|
||||||
"native_pixels_per_point: {native_pixels_per_point:?}"
|
|
||||||
))
|
|
||||||
.on_hover_text(
|
|
||||||
"Also called HDPI factor.\nNumber of physical pixels per each logical pixel.",
|
|
||||||
);
|
|
||||||
ui.label(format!("max_texture_side: {max_texture_side:?}"));
|
ui.label(format!("max_texture_side: {max_texture_side:?}"));
|
||||||
if let Some(time) = time {
|
if let Some(time) = time {
|
||||||
ui.label(format!("time: {time:.3} s"));
|
ui.label(format!("time: {time:.3} s"));
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ pub struct FullOutput {
|
|||||||
pub pixels_per_point: f32,
|
pub pixels_per_point: f32,
|
||||||
|
|
||||||
/// All the active viewports, including the root.
|
/// All the active viewports, including the root.
|
||||||
|
///
|
||||||
|
/// It is up to the integration to spawn a native window for each viewport,
|
||||||
|
/// and to close any window that no longer has a viewport in this map.
|
||||||
pub viewport_output: ViewportIdMap<ViewportOutput>,
|
pub viewport_output: ViewportIdMap<ViewportOutput>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,20 +12,14 @@ pub mod kb_shortcuts {
|
|||||||
pub const ZOOM_RESET: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::Num0);
|
pub const ZOOM_RESET: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::Num0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Let the user scale the GUI (change `Context::pixels_per_point`) by pressing
|
/// Let the user scale the GUI (change [`Context::zoom_factor`]) by pressing
|
||||||
/// Cmd+Plus, Cmd+Minus or Cmd+0, just like in a browser.
|
/// Cmd+Plus, Cmd+Minus or Cmd+0, just like in a browser.
|
||||||
///
|
///
|
||||||
/// ```
|
/// By default, [`crate::Context`] calls this function at the end of each frame,
|
||||||
/// # let ctx = &egui::Context::default();
|
/// controllable by [`crate::Options::zoom_with_keyboard`].
|
||||||
/// // On web, the browser controls the gui zoom.
|
pub(crate) fn zoom_with_keyboard(ctx: &Context) {
|
||||||
/// #[cfg(not(target_arch = "wasm32"))]
|
|
||||||
/// egui::gui_zoom::zoom_with_keyboard_shortcuts(ctx);
|
|
||||||
/// ```
|
|
||||||
pub fn zoom_with_keyboard_shortcuts(ctx: &Context) {
|
|
||||||
if ctx.input_mut(|i| i.consume_shortcut(&kb_shortcuts::ZOOM_RESET)) {
|
if ctx.input_mut(|i| i.consume_shortcut(&kb_shortcuts::ZOOM_RESET)) {
|
||||||
if let Some(native_pixels_per_point) = ctx.input(|i| i.raw.native_pixels_per_point) {
|
ctx.set_zoom_factor(1.0);
|
||||||
ctx.set_pixels_per_point(native_pixels_per_point);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if ctx.input_mut(|i| i.consume_shortcut(&kb_shortcuts::ZOOM_IN)) {
|
if ctx.input_mut(|i| i.consume_shortcut(&kb_shortcuts::ZOOM_IN)) {
|
||||||
zoom_in(ctx);
|
zoom_in(ctx);
|
||||||
@@ -36,47 +30,34 @@ pub fn zoom_with_keyboard_shortcuts(ctx: &Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const MIN_PIXELS_PER_POINT: f32 = 0.2;
|
const MIN_ZOOM_FACTOR: f32 = 0.2;
|
||||||
const MAX_PIXELS_PER_POINT: f32 = 4.0;
|
const MAX_ZOOM_FACTOR: f32 = 5.0;
|
||||||
|
|
||||||
/// Make everything larger.
|
/// Make everything larger by increasing [`Context::zoom_factor`].
|
||||||
pub fn zoom_in(ctx: &Context) {
|
pub fn zoom_in(ctx: &Context) {
|
||||||
let mut pixels_per_point = ctx.pixels_per_point();
|
let mut zoom_factor = ctx.zoom_factor();
|
||||||
pixels_per_point += 0.1;
|
zoom_factor += 0.1;
|
||||||
pixels_per_point = pixels_per_point.clamp(MIN_PIXELS_PER_POINT, MAX_PIXELS_PER_POINT);
|
zoom_factor = zoom_factor.clamp(MIN_ZOOM_FACTOR, MAX_ZOOM_FACTOR);
|
||||||
pixels_per_point = (pixels_per_point * 10.).round() / 10.;
|
zoom_factor = (zoom_factor * 10.).round() / 10.;
|
||||||
ctx.set_pixels_per_point(pixels_per_point);
|
ctx.set_zoom_factor(zoom_factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Make everything smaller.
|
/// Make everything smaller by decreasing [`Context::zoom_factor`].
|
||||||
pub fn zoom_out(ctx: &Context) {
|
pub fn zoom_out(ctx: &Context) {
|
||||||
let mut pixels_per_point = ctx.pixels_per_point();
|
let mut zoom_factor = ctx.zoom_factor();
|
||||||
pixels_per_point -= 0.1;
|
zoom_factor -= 0.1;
|
||||||
pixels_per_point = pixels_per_point.clamp(MIN_PIXELS_PER_POINT, MAX_PIXELS_PER_POINT);
|
zoom_factor = zoom_factor.clamp(MIN_ZOOM_FACTOR, MAX_ZOOM_FACTOR);
|
||||||
pixels_per_point = (pixels_per_point * 10.).round() / 10.;
|
zoom_factor = (zoom_factor * 10.).round() / 10.;
|
||||||
ctx.set_pixels_per_point(pixels_per_point);
|
ctx.set_zoom_factor(zoom_factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show buttons for zooming the ui.
|
/// Show buttons for zooming the ui.
|
||||||
///
|
///
|
||||||
/// This is meant to be called from within a menu (See [`Ui::menu_button`]).
|
/// This is meant to be called from within a menu (See [`Ui::menu_button`]).
|
||||||
///
|
pub fn zoom_menu_buttons(ui: &mut Ui) {
|
||||||
/// When using [`eframe`](https://github.com/emilk/egui/tree/master/crates/eframe), you want to call this as:
|
|
||||||
/// ```ignore
|
|
||||||
/// // On web, the browser controls the gui zoom.
|
|
||||||
/// if !frame.is_web() {
|
|
||||||
/// ui.menu_button("View", |ui| {
|
|
||||||
/// egui::gui_zoom::zoom_menu_buttons(
|
|
||||||
/// ui,
|
|
||||||
/// frame.info().native_pixels_per_point,
|
|
||||||
/// );
|
|
||||||
/// });
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub fn zoom_menu_buttons(ui: &mut Ui, native_pixels_per_point: Option<f32>) {
|
|
||||||
if ui
|
if ui
|
||||||
.add_enabled(
|
.add_enabled(
|
||||||
ui.ctx().pixels_per_point() < MAX_PIXELS_PER_POINT,
|
ui.ctx().zoom_factor() < MAX_ZOOM_FACTOR,
|
||||||
Button::new("Zoom In").shortcut_text(ui.ctx().format_shortcut(&kb_shortcuts::ZOOM_IN)),
|
Button::new("Zoom In").shortcut_text(ui.ctx().format_shortcut(&kb_shortcuts::ZOOM_IN)),
|
||||||
)
|
)
|
||||||
.clicked()
|
.clicked()
|
||||||
@@ -87,7 +68,7 @@ pub fn zoom_menu_buttons(ui: &mut Ui, native_pixels_per_point: Option<f32>) {
|
|||||||
|
|
||||||
if ui
|
if ui
|
||||||
.add_enabled(
|
.add_enabled(
|
||||||
ui.ctx().pixels_per_point() > MIN_PIXELS_PER_POINT,
|
ui.ctx().zoom_factor() > MIN_ZOOM_FACTOR,
|
||||||
Button::new("Zoom Out")
|
Button::new("Zoom Out")
|
||||||
.shortcut_text(ui.ctx().format_shortcut(&kb_shortcuts::ZOOM_OUT)),
|
.shortcut_text(ui.ctx().format_shortcut(&kb_shortcuts::ZOOM_OUT)),
|
||||||
)
|
)
|
||||||
@@ -97,17 +78,15 @@ pub fn zoom_menu_buttons(ui: &mut Ui, native_pixels_per_point: Option<f32>) {
|
|||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(native_pixels_per_point) = native_pixels_per_point {
|
|
||||||
if ui
|
if ui
|
||||||
.add_enabled(
|
.add_enabled(
|
||||||
ui.ctx().pixels_per_point() != native_pixels_per_point,
|
ui.ctx().zoom_factor() != 1.0,
|
||||||
Button::new("Reset Zoom")
|
Button::new("Reset Zoom")
|
||||||
.shortcut_text(ui.ctx().format_shortcut(&kb_shortcuts::ZOOM_RESET)),
|
.shortcut_text(ui.ctx().format_shortcut(&kb_shortcuts::ZOOM_RESET)),
|
||||||
)
|
)
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
ui.ctx().set_pixels_per_point(native_pixels_per_point);
|
ui.ctx().set_zoom_factor(1.0);
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ impl InputState {
|
|||||||
mut self,
|
mut self,
|
||||||
mut new: RawInput,
|
mut new: RawInput,
|
||||||
requested_repaint_last_frame: bool,
|
requested_repaint_last_frame: bool,
|
||||||
|
pixels_per_point: f32,
|
||||||
) -> InputState {
|
) -> InputState {
|
||||||
crate::profile_function!();
|
crate::profile_function!();
|
||||||
|
|
||||||
@@ -217,7 +218,7 @@ impl InputState {
|
|||||||
scroll_delta,
|
scroll_delta,
|
||||||
zoom_factor_delta,
|
zoom_factor_delta,
|
||||||
screen_rect,
|
screen_rect,
|
||||||
pixels_per_point: new.pixels_per_point.unwrap_or(self.pixels_per_point),
|
pixels_per_point,
|
||||||
max_texture_side: new.max_texture_side.unwrap_or(self.max_texture_side),
|
max_texture_side: new.max_texture_side.unwrap_or(self.max_texture_side),
|
||||||
time,
|
time,
|
||||||
unstable_dt,
|
unstable_dt,
|
||||||
@@ -232,8 +233,9 @@ impl InputState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Info about the active viewport
|
/// Info about the active viewport
|
||||||
|
#[inline]
|
||||||
pub fn viewport(&self) -> &ViewportInfo {
|
pub fn viewport(&self) -> &ViewportInfo {
|
||||||
self.raw.viewports.get(&self.raw.viewport_ids.this).expect("Failed to find current viewport in egui RawInput. This is the fault of the egui backend")
|
self.raw.viewport()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|||||||
@@ -336,7 +336,8 @@
|
|||||||
|
|
||||||
#![allow(clippy::float_cmp)]
|
#![allow(clippy::float_cmp)]
|
||||||
#![allow(clippy::manual_range_contains)]
|
#![allow(clippy::manual_range_contains)]
|
||||||
#![deny(unsafe_code)]
|
#![cfg_attr(feature = "puffin", deny(unsafe_code))]
|
||||||
|
#![cfg_attr(not(feature = "puffin"), forbid(unsafe_code))]
|
||||||
|
|
||||||
mod animation_manager;
|
mod animation_manager;
|
||||||
pub mod containers;
|
pub mod containers;
|
||||||
|
|||||||
@@ -71,10 +71,6 @@ pub struct Memory {
|
|||||||
pub caches: crate::util::cache::CacheStorage,
|
pub caches: crate::util::cache::CacheStorage,
|
||||||
|
|
||||||
// ------------------------------------------
|
// ------------------------------------------
|
||||||
/// new scale that will be applied at the start of the next frame
|
|
||||||
#[cfg_attr(feature = "persistence", serde(skip))]
|
|
||||||
pub(crate) override_pixels_per_point: Option<f32>,
|
|
||||||
|
|
||||||
/// new fonts that will be applied at the start of the next frame
|
/// new fonts that will be applied at the start of the next frame
|
||||||
#[cfg_attr(feature = "persistence", serde(skip))]
|
#[cfg_attr(feature = "persistence", serde(skip))]
|
||||||
pub(crate) new_font_definitions: Option<epaint::text::FontDefinitions>,
|
pub(crate) new_font_definitions: Option<epaint::text::FontDefinitions>,
|
||||||
@@ -111,7 +107,6 @@ impl Default for Memory {
|
|||||||
options: Default::default(),
|
options: Default::default(),
|
||||||
data: Default::default(),
|
data: Default::default(),
|
||||||
caches: Default::default(),
|
caches: Default::default(),
|
||||||
override_pixels_per_point: Default::default(),
|
|
||||||
new_font_definitions: Default::default(),
|
new_font_definitions: Default::default(),
|
||||||
interactions: Default::default(),
|
interactions: Default::default(),
|
||||||
viewport_id: Default::default(),
|
viewport_id: Default::default(),
|
||||||
@@ -176,6 +171,25 @@ pub struct Options {
|
|||||||
#[cfg_attr(feature = "serde", serde(skip))]
|
#[cfg_attr(feature = "serde", serde(skip))]
|
||||||
pub(crate) style: std::sync::Arc<Style>,
|
pub(crate) style: std::sync::Arc<Style>,
|
||||||
|
|
||||||
|
/// Global zoom factor of the UI.
|
||||||
|
///
|
||||||
|
/// This is used to calculate the `pixels_per_point`
|
||||||
|
/// for the UI as `pixels_per_point = zoom_fator * native_pixels_per_point`.
|
||||||
|
///
|
||||||
|
/// The default is 1.0.
|
||||||
|
/// Make larger to make everything larger.
|
||||||
|
///
|
||||||
|
/// Please call [`crate::Context::set_zoom_factor`]
|
||||||
|
/// instead of modifying this directly!
|
||||||
|
pub zoom_factor: f32,
|
||||||
|
|
||||||
|
/// If `true`, egui will change the scale of the ui ([`crate::Context::zoom_factor`]) when the user
|
||||||
|
/// presses Cmd+Plus, Cmd+Minus or Cmd+0, just like in a browser.
|
||||||
|
///
|
||||||
|
/// This is `true` by default.
|
||||||
|
#[cfg_attr(feature = "serde", serde(skip))]
|
||||||
|
pub zoom_with_keyboard: bool,
|
||||||
|
|
||||||
/// Controls the tessellator.
|
/// Controls the tessellator.
|
||||||
pub tessellation_options: epaint::TessellationOptions,
|
pub tessellation_options: epaint::TessellationOptions,
|
||||||
|
|
||||||
@@ -208,6 +222,8 @@ impl Default for Options {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
style: Default::default(),
|
style: Default::default(),
|
||||||
|
zoom_factor: 1.0,
|
||||||
|
zoom_with_keyboard: true,
|
||||||
tessellation_options: Default::default(),
|
tessellation_options: Default::default(),
|
||||||
screen_reader: false,
|
screen_reader: false,
|
||||||
preload_font_glyphs: true,
|
preload_font_glyphs: true,
|
||||||
@@ -559,7 +575,7 @@ impl Memory {
|
|||||||
self.window_interactions
|
self.window_interactions
|
||||||
.retain(|id, _| viewports.contains(id));
|
.retain(|id, _| viewports.contains(id));
|
||||||
|
|
||||||
self.viewport_id = new_input.viewport_ids.this;
|
self.viewport_id = new_input.viewport_id;
|
||||||
self.interactions
|
self.interactions
|
||||||
.entry(self.viewport_id)
|
.entry(self.viewport_id)
|
||||||
.or_default()
|
.or_default()
|
||||||
|
|||||||
@@ -275,16 +275,19 @@ pub struct ViewportBuilder {
|
|||||||
pub icon: Option<Arc<IconData>>,
|
pub icon: Option<Arc<IconData>>,
|
||||||
pub active: Option<bool>,
|
pub active: Option<bool>,
|
||||||
pub visible: Option<bool>,
|
pub visible: Option<bool>,
|
||||||
pub title_hidden: Option<bool>,
|
|
||||||
pub titlebar_transparent: Option<bool>,
|
|
||||||
pub fullsize_content_view: Option<bool>,
|
|
||||||
pub drag_and_drop: Option<bool>,
|
pub drag_and_drop: Option<bool>,
|
||||||
|
|
||||||
|
// macOS:
|
||||||
|
pub fullsize_content_view: Option<bool>,
|
||||||
|
pub title_shown: Option<bool>,
|
||||||
|
pub titlebar_buttons_shown: Option<bool>,
|
||||||
|
pub titlebar_shown: Option<bool>,
|
||||||
|
|
||||||
pub close_button: Option<bool>,
|
pub close_button: Option<bool>,
|
||||||
pub minimize_button: Option<bool>,
|
pub minimize_button: Option<bool>,
|
||||||
pub maximize_button: Option<bool>,
|
pub maximize_button: Option<bool>,
|
||||||
|
|
||||||
pub window_level: WindowLevel,
|
pub window_level: Option<WindowLevel>,
|
||||||
|
|
||||||
pub mouse_passthrough: Option<bool>,
|
pub mouse_passthrough: Option<bool>,
|
||||||
}
|
}
|
||||||
@@ -400,35 +403,37 @@ impl ViewportBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hides the window title.
|
/// macOS: Makes the window content appear behind the titlebar.
|
||||||
///
|
///
|
||||||
/// Mac Os only.
|
/// You often want to combine this with [`Self::with_titlebar_shown`]
|
||||||
#[inline]
|
/// and [`Self::with_title_shown`].
|
||||||
pub fn with_title_hidden(mut self, title_hidden: bool) -> Self {
|
|
||||||
self.title_hidden = Some(title_hidden);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Makes the titlebar transparent and allows the content to appear behind it.
|
|
||||||
///
|
|
||||||
/// Mac Os only.
|
|
||||||
#[inline]
|
|
||||||
pub fn with_titlebar_transparent(mut self, value: bool) -> Self {
|
|
||||||
self.titlebar_transparent = Some(value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// On Mac: the window doesn't have a titlebar, but floating window buttons.
|
|
||||||
///
|
|
||||||
/// See [winit's documentation][with_fullsize_content_view] for information on Mac-specific options.
|
|
||||||
///
|
|
||||||
/// [with_fullsize_content_view]: https://docs.rs/winit/latest/x86_64-apple-darwin/winit/platform/macos/trait.WindowBuilderExtMacOS.html#tymethod.with_fullsize_content_view
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_fullsize_content_view(mut self, value: bool) -> Self {
|
pub fn with_fullsize_content_view(mut self, value: bool) -> Self {
|
||||||
self.fullsize_content_view = Some(value);
|
self.fullsize_content_view = Some(value);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// macOS: Set to `false` to hide the window title.
|
||||||
|
#[inline]
|
||||||
|
pub fn with_title_shown(mut self, title_shown: bool) -> Self {
|
||||||
|
self.title_shown = Some(title_shown);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// macOS: Set to `false` to hide the titlebar button (close, minimize, maximize)
|
||||||
|
#[inline]
|
||||||
|
pub fn with_titlebar_buttons_shown(mut self, titlebar_buttons_shown: bool) -> Self {
|
||||||
|
self.titlebar_buttons_shown = Some(titlebar_buttons_shown);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// macOS: Set to `false` to make the titlebar transparent, allowing the content to appear behind it.
|
||||||
|
#[inline]
|
||||||
|
pub fn with_titlebar_shown(mut self, shown: bool) -> Self {
|
||||||
|
self.titlebar_shown = Some(shown);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Requests the window to be of specific dimensions.
|
/// Requests the window to be of specific dimensions.
|
||||||
///
|
///
|
||||||
/// If this is not set, some platform-specific dimensions will be used.
|
/// If this is not set, some platform-specific dimensions will be used.
|
||||||
@@ -539,7 +544,7 @@ impl ViewportBuilder {
|
|||||||
/// Control if window i always-on-top, always-on-bottom, or neither.
|
/// Control if window i always-on-top, always-on-bottom, or neither.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_window_level(mut self, level: WindowLevel) -> Self {
|
pub fn with_window_level(mut self, level: WindowLevel) -> Self {
|
||||||
self.window_level = level;
|
self.window_level = Some(level);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -561,156 +566,199 @@ impl ViewportBuilder {
|
|||||||
|
|
||||||
/// Update this `ViewportBuilder` with a delta,
|
/// Update this `ViewportBuilder` with a delta,
|
||||||
/// returning a list of commands and a bool intdicating if the window needs to be recreated.
|
/// returning a list of commands and a bool intdicating if the window needs to be recreated.
|
||||||
pub fn patch(&mut self, new: &ViewportBuilder) -> (Vec<ViewportCommand>, bool) {
|
#[must_use]
|
||||||
|
pub fn patch(&mut self, new_vp_builder: ViewportBuilder) -> (Vec<ViewportCommand>, bool) {
|
||||||
|
let ViewportBuilder {
|
||||||
|
title: new_title,
|
||||||
|
app_id: new_app_id,
|
||||||
|
position: new_position,
|
||||||
|
inner_size: new_inner_size,
|
||||||
|
min_inner_size: new_min_inner_size,
|
||||||
|
max_inner_size: new_max_inner_size,
|
||||||
|
fullscreen: new_fullscreen,
|
||||||
|
maximized: new_maximized,
|
||||||
|
resizable: new_resizable,
|
||||||
|
transparent: new_transparent,
|
||||||
|
decorations: new_decorations,
|
||||||
|
icon: new_icon,
|
||||||
|
active: new_active,
|
||||||
|
visible: new_visible,
|
||||||
|
drag_and_drop: new_drag_and_drop,
|
||||||
|
fullsize_content_view: new_fullsize_content_view,
|
||||||
|
title_shown: new_title_shown,
|
||||||
|
titlebar_buttons_shown: new_titlebar_buttons_shown,
|
||||||
|
titlebar_shown: new_titlebar_shown,
|
||||||
|
close_button: new_close_button,
|
||||||
|
minimize_button: new_minimize_button,
|
||||||
|
maximize_button: new_maximize_button,
|
||||||
|
window_level: new_window_level,
|
||||||
|
mouse_passthrough: new_mouse_passthrough,
|
||||||
|
} = new_vp_builder;
|
||||||
|
|
||||||
let mut commands = Vec::new();
|
let mut commands = Vec::new();
|
||||||
|
|
||||||
if let Some(new_title) = &new.title {
|
if let Some(new_title) = new_title {
|
||||||
if Some(new_title) != self.title.as_ref() {
|
if Some(&new_title) != self.title.as_ref() {
|
||||||
self.title = Some(new_title.clone());
|
self.title = Some(new_title.clone());
|
||||||
commands.push(ViewportCommand::Title(new_title.clone()));
|
commands.push(ViewportCommand::Title(new_title));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(new_position) = new.position {
|
if let Some(new_position) = new_position {
|
||||||
if Some(new_position) != self.position {
|
if Some(new_position) != self.position {
|
||||||
self.position = Some(new_position);
|
self.position = Some(new_position);
|
||||||
commands.push(ViewportCommand::OuterPosition(new_position));
|
commands.push(ViewportCommand::OuterPosition(new_position));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(new_inner_size) = new.inner_size {
|
if let Some(new_inner_size) = new_inner_size {
|
||||||
if Some(new_inner_size) != self.inner_size {
|
if Some(new_inner_size) != self.inner_size {
|
||||||
self.inner_size = Some(new_inner_size);
|
self.inner_size = Some(new_inner_size);
|
||||||
commands.push(ViewportCommand::InnerSize(new_inner_size));
|
commands.push(ViewportCommand::InnerSize(new_inner_size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(new_min_inner_size) = new.min_inner_size {
|
if let Some(new_min_inner_size) = new_min_inner_size {
|
||||||
if Some(new_min_inner_size) != self.min_inner_size {
|
if Some(new_min_inner_size) != self.min_inner_size {
|
||||||
self.min_inner_size = Some(new_min_inner_size);
|
self.min_inner_size = Some(new_min_inner_size);
|
||||||
commands.push(ViewportCommand::MinInnerSize(new_min_inner_size));
|
commands.push(ViewportCommand::MinInnerSize(new_min_inner_size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(new_max_inner_size) = new.max_inner_size {
|
if let Some(new_max_inner_size) = new_max_inner_size {
|
||||||
if Some(new_max_inner_size) != self.max_inner_size {
|
if Some(new_max_inner_size) != self.max_inner_size {
|
||||||
self.max_inner_size = Some(new_max_inner_size);
|
self.max_inner_size = Some(new_max_inner_size);
|
||||||
commands.push(ViewportCommand::MaxInnerSize(new_max_inner_size));
|
commands.push(ViewportCommand::MaxInnerSize(new_max_inner_size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(new_fullscreen) = new.fullscreen {
|
if let Some(new_fullscreen) = new_fullscreen {
|
||||||
if Some(new_fullscreen) != self.fullscreen {
|
if Some(new_fullscreen) != self.fullscreen {
|
||||||
self.fullscreen = Some(new_fullscreen);
|
self.fullscreen = Some(new_fullscreen);
|
||||||
commands.push(ViewportCommand::Fullscreen(new_fullscreen));
|
commands.push(ViewportCommand::Fullscreen(new_fullscreen));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(new_maximized) = new.maximized {
|
if let Some(new_maximized) = new_maximized {
|
||||||
if Some(new_maximized) != self.maximized {
|
if Some(new_maximized) != self.maximized {
|
||||||
self.maximized = Some(new_maximized);
|
self.maximized = Some(new_maximized);
|
||||||
commands.push(ViewportCommand::Maximized(new_maximized));
|
commands.push(ViewportCommand::Maximized(new_maximized));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(new_resizable) = new.resizable {
|
if let Some(new_resizable) = new_resizable {
|
||||||
if Some(new_resizable) != self.resizable {
|
if Some(new_resizable) != self.resizable {
|
||||||
self.resizable = Some(new_resizable);
|
self.resizable = Some(new_resizable);
|
||||||
commands.push(ViewportCommand::Resizable(new_resizable));
|
commands.push(ViewportCommand::Resizable(new_resizable));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(new_transparent) = new.transparent {
|
if let Some(new_transparent) = new_transparent {
|
||||||
if Some(new_transparent) != self.transparent {
|
if Some(new_transparent) != self.transparent {
|
||||||
self.transparent = Some(new_transparent);
|
self.transparent = Some(new_transparent);
|
||||||
commands.push(ViewportCommand::Transparent(new_transparent));
|
commands.push(ViewportCommand::Transparent(new_transparent));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(new_decorations) = new.decorations {
|
if let Some(new_decorations) = new_decorations {
|
||||||
if Some(new_decorations) != self.decorations {
|
if Some(new_decorations) != self.decorations {
|
||||||
self.decorations = Some(new_decorations);
|
self.decorations = Some(new_decorations);
|
||||||
commands.push(ViewportCommand::Decorations(new_decorations));
|
commands.push(ViewportCommand::Decorations(new_decorations));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(new_icon) = &new.icon {
|
if let Some(new_icon) = new_icon {
|
||||||
let is_new = match &self.icon {
|
let is_new = match &self.icon {
|
||||||
Some(existing) => !Arc::ptr_eq(new_icon, existing),
|
Some(existing) => !Arc::ptr_eq(&new_icon, existing),
|
||||||
None => true,
|
None => true,
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_new {
|
if is_new {
|
||||||
commands.push(ViewportCommand::Icon(Some(new_icon.clone())));
|
commands.push(ViewportCommand::Icon(Some(new_icon.clone())));
|
||||||
self.icon = Some(new_icon.clone());
|
self.icon = Some(new_icon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(new_visible) = new.visible {
|
if let Some(new_visible) = new_visible {
|
||||||
if Some(new_visible) != self.active {
|
if Some(new_visible) != self.active {
|
||||||
self.visible = Some(new_visible);
|
self.visible = Some(new_visible);
|
||||||
commands.push(ViewportCommand::Visible(new_visible));
|
commands.push(ViewportCommand::Visible(new_visible));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(new_mouse_passthrough) = new.mouse_passthrough {
|
if let Some(new_mouse_passthrough) = new_mouse_passthrough {
|
||||||
if Some(new_mouse_passthrough) != self.mouse_passthrough {
|
if Some(new_mouse_passthrough) != self.mouse_passthrough {
|
||||||
self.mouse_passthrough = Some(new_mouse_passthrough);
|
self.mouse_passthrough = Some(new_mouse_passthrough);
|
||||||
commands.push(ViewportCommand::MousePassthrough(new_mouse_passthrough));
|
commands.push(ViewportCommand::MousePassthrough(new_mouse_passthrough));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement compare for windows buttons
|
if let Some(new_window_level) = new_window_level {
|
||||||
|
if Some(new_window_level) != self.window_level {
|
||||||
|
self.window_level = Some(new_window_level);
|
||||||
|
commands.push(ViewportCommand::WindowLevel(new_window_level));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------
|
||||||
|
// Things we don't have commands for require a full window recreation.
|
||||||
|
// The reason we don't have commands for them is that `winit` doesn't support
|
||||||
|
// changing them without recreating the window.
|
||||||
|
|
||||||
let mut recreate_window = false;
|
let mut recreate_window = false;
|
||||||
|
|
||||||
if let Some(new_active) = new.active {
|
if new_active.is_some() && self.active != new_active {
|
||||||
if Some(new_active) != self.active {
|
self.active = new_active;
|
||||||
self.active = Some(new_active);
|
|
||||||
recreate_window = true;
|
recreate_window = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(new_close_button) = new.close_button {
|
if new_app_id.is_some() && self.app_id != new_app_id {
|
||||||
if Some(new_close_button) != self.close_button {
|
self.app_id = new_app_id;
|
||||||
self.close_button = Some(new_close_button);
|
|
||||||
recreate_window = true;
|
recreate_window = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(new_minimize_button) = new.minimize_button {
|
if new_close_button.is_some() && self.close_button != new_close_button {
|
||||||
if Some(new_minimize_button) != self.minimize_button {
|
self.close_button = new_close_button;
|
||||||
self.minimize_button = Some(new_minimize_button);
|
|
||||||
recreate_window = true;
|
recreate_window = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(new_maximized_button) = new.maximize_button {
|
if new_minimize_button.is_some() && self.minimize_button != new_minimize_button {
|
||||||
if Some(new_maximized_button) != self.maximize_button {
|
self.minimize_button = new_minimize_button;
|
||||||
self.maximize_button = Some(new_maximized_button);
|
|
||||||
recreate_window = true;
|
recreate_window = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(new_title_hidden) = new.title_hidden {
|
if new_maximize_button.is_some() && self.maximize_button != new_maximize_button {
|
||||||
if Some(new_title_hidden) != self.title_hidden {
|
self.maximize_button = new_maximize_button;
|
||||||
self.title_hidden = Some(new_title_hidden);
|
|
||||||
recreate_window = true;
|
recreate_window = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(new_titlebar_transparent) = new.titlebar_transparent {
|
if new_title_shown.is_some() && self.title_shown != new_title_shown {
|
||||||
if Some(new_titlebar_transparent) != self.titlebar_transparent {
|
self.title_shown = new_title_shown;
|
||||||
self.titlebar_transparent = Some(new_titlebar_transparent);
|
|
||||||
recreate_window = true;
|
recreate_window = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(new_fullsize_content_view) = new.fullsize_content_view {
|
if new_titlebar_buttons_shown.is_some()
|
||||||
if Some(new_fullsize_content_view) != self.fullsize_content_view {
|
&& self.titlebar_buttons_shown != new_titlebar_buttons_shown
|
||||||
self.fullsize_content_view = Some(new_fullsize_content_view);
|
{
|
||||||
|
self.titlebar_buttons_shown = new_titlebar_buttons_shown;
|
||||||
recreate_window = true;
|
recreate_window = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if new_titlebar_shown.is_some() && self.titlebar_shown != new_titlebar_shown {
|
||||||
|
self.titlebar_shown = new_titlebar_shown;
|
||||||
|
recreate_window = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if new_fullsize_content_view.is_some()
|
||||||
|
&& self.fullsize_content_view != new_fullsize_content_view
|
||||||
|
{
|
||||||
|
self.fullsize_content_view = new_fullsize_content_view;
|
||||||
|
recreate_window = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if new_drag_and_drop.is_some() && self.drag_and_drop != new_drag_and_drop {
|
||||||
|
self.drag_and_drop = new_drag_and_drop;
|
||||||
|
recreate_window = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
(commands, recreate_window)
|
(commands, recreate_window)
|
||||||
@@ -783,6 +831,9 @@ pub enum ViewportCommand {
|
|||||||
/// For other viewports, the [`crate::ViewportInfo::close_requested`] flag will be set.
|
/// For other viewports, the [`crate::ViewportInfo::close_requested`] flag will be set.
|
||||||
Close,
|
Close,
|
||||||
|
|
||||||
|
/// Calcel the closing that was signaled by [`crate::ViewportInfo::close_requested`].
|
||||||
|
CancelClose,
|
||||||
|
|
||||||
/// Set the window title.
|
/// Set the window title.
|
||||||
Title(String),
|
Title(String),
|
||||||
|
|
||||||
@@ -913,6 +964,9 @@ impl ViewportCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Describes a viewport, i.e. a native window.
|
/// Describes a viewport, i.e. a native window.
|
||||||
|
///
|
||||||
|
/// This is returned by [`crate::Context::run`] on each frame, and should be applied
|
||||||
|
/// by the integration.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ViewportOutput {
|
pub struct ViewportOutput {
|
||||||
/// Id of our parent viewport.
|
/// Id of our parent viewport.
|
||||||
@@ -925,6 +979,10 @@ pub struct ViewportOutput {
|
|||||||
pub class: ViewportClass,
|
pub class: ViewportClass,
|
||||||
|
|
||||||
/// The window attrbiutes such as title, position, size, etc.
|
/// The window attrbiutes such as title, position, size, etc.
|
||||||
|
///
|
||||||
|
/// Use this when first constructing the native window.
|
||||||
|
/// Also check for changes in it using [`ViewportBuilder::patch`],
|
||||||
|
/// and apply them as needed.
|
||||||
pub builder: ViewportBuilder,
|
pub builder: ViewportBuilder,
|
||||||
|
|
||||||
/// The user-code that shows the GUI, used for deferred viewports.
|
/// The user-code that shows the GUI, used for deferred viewports.
|
||||||
@@ -958,7 +1016,7 @@ impl ViewportOutput {
|
|||||||
|
|
||||||
self.parent = parent;
|
self.parent = parent;
|
||||||
self.class = class;
|
self.class = class;
|
||||||
self.builder.patch(&builder);
|
let _ = self.builder.patch(builder); // we ignore the returned command, because `self.builder` will be the basis of a new patch
|
||||||
self.viewport_ui_cb = viewport_ui_cb;
|
self.viewport_ui_cb = viewport_ui_cb;
|
||||||
self.commands.append(&mut commands);
|
self.commands.append(&mut commands);
|
||||||
self.repaint_delay = self.repaint_delay.min(repaint_delay);
|
self.repaint_delay = self.repaint_delay.min(repaint_delay);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "egui_demo_app"
|
name = "egui_demo_app"
|
||||||
version = "0.23.0"
|
version.workspace = true
|
||||||
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
||||||
license = "MIT OR Apache-2.0"
|
license.workspace = true
|
||||||
edition = "2021"
|
edition.workspace = true
|
||||||
rust-version = "1.72"
|
rust-version.workspace = true
|
||||||
publish = false
|
publish = false
|
||||||
default-run = "egui_demo_app"
|
default-run = "egui_demo_app"
|
||||||
|
|
||||||
@@ -24,9 +24,10 @@ web_app = ["http", "persistence", "web_screen_reader"]
|
|||||||
http = ["ehttp", "image", "poll-promise", "egui_extras/image"]
|
http = ["ehttp", "image", "poll-promise", "egui_extras/image"]
|
||||||
image_viewer = ["image", "egui_extras/all_loaders", "rfd"]
|
image_viewer = ["image", "egui_extras/all_loaders", "rfd"]
|
||||||
persistence = ["eframe/persistence", "egui/persistence", "serde"]
|
persistence = ["eframe/persistence", "egui/persistence", "serde"]
|
||||||
web_screen_reader = ["eframe/web_screen_reader"] # experimental
|
puffin = ["eframe/puffin", "dep:puffin", "dep:puffin_http"]
|
||||||
serde = ["dep:serde", "egui_demo_lib/serde", "egui/serde"]
|
serde = ["dep:serde", "egui_demo_lib/serde", "egui/serde"]
|
||||||
syntect = ["egui_demo_lib/syntect"]
|
syntect = ["egui_demo_lib/syntect"]
|
||||||
|
web_screen_reader = ["eframe/web_screen_reader"] # experimental
|
||||||
|
|
||||||
glow = ["eframe/glow"]
|
glow = ["eframe/glow"]
|
||||||
wgpu = ["eframe/wgpu", "bytemuck"]
|
wgpu = ["eframe/wgpu", "bytemuck"]
|
||||||
@@ -36,23 +37,26 @@ chrono = { version = "0.4", default-features = false, features = [
|
|||||||
"js-sys",
|
"js-sys",
|
||||||
"wasmbind",
|
"wasmbind",
|
||||||
] }
|
] }
|
||||||
eframe = { version = "0.23.0", path = "../eframe", default-features = false }
|
eframe = { version = "0.24.0", path = "../eframe", default-features = false }
|
||||||
egui = { version = "0.23.0", path = "../egui", features = [
|
egui = { version = "0.24.0", path = "../egui", features = [
|
||||||
"callstack",
|
"callstack",
|
||||||
"extra_debug_asserts",
|
"extra_debug_asserts",
|
||||||
"log",
|
"log",
|
||||||
] }
|
] }
|
||||||
egui_demo_lib = { version = "0.23.0", path = "../egui_demo_lib", features = [
|
egui_demo_lib = { version = "0.24.0", path = "../egui_demo_lib", features = [
|
||||||
"chrono",
|
"chrono",
|
||||||
] }
|
] }
|
||||||
|
egui_extras = { version = "0.24.0", path = "../egui_extras", features = [
|
||||||
|
"image",
|
||||||
|
] }
|
||||||
log = { version = "0.4", features = ["std"] }
|
log = { version = "0.4", features = ["std"] }
|
||||||
|
|
||||||
# Optional dependencies:
|
# Optional dependencies:
|
||||||
|
|
||||||
bytemuck = { version = "1.7.1", optional = true }
|
bytemuck = { version = "1.7.1", optional = true }
|
||||||
egui_extras = { version = "0.23.0", path = "../egui_extras", features = [
|
puffin = { version = "0.18", optional = true }
|
||||||
"image",
|
puffin_http = { version = "0.15", optional = true }
|
||||||
] }
|
|
||||||
|
|
||||||
# feature "http":
|
# feature "http":
|
||||||
ehttp = { version = "0.3.1", optional = true }
|
ehttp = { version = "0.3.1", optional = true }
|
||||||
|
|||||||
@@ -51,10 +51,6 @@ pub struct BackendPanel {
|
|||||||
// go back to [`RunMode::Reactive`] mode each time we start
|
// go back to [`RunMode::Reactive`] mode each time we start
|
||||||
run_mode: RunMode,
|
run_mode: RunMode,
|
||||||
|
|
||||||
/// current slider value for current gui scale
|
|
||||||
#[cfg_attr(feature = "serde", serde(skip))]
|
|
||||||
pixels_per_point: Option<f32>,
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", serde(skip))]
|
#[cfg_attr(feature = "serde", serde(skip))]
|
||||||
frame_history: crate::frame_history::FrameHistory,
|
frame_history: crate::frame_history::FrameHistory,
|
||||||
|
|
||||||
@@ -82,7 +78,7 @@ impl BackendPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
|
pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
|
||||||
self.integration_ui(ui, frame);
|
integration_ui(ui, frame);
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
@@ -97,12 +93,13 @@ impl BackendPanel {
|
|||||||
ui.label("egui windows:");
|
ui.label("egui windows:");
|
||||||
self.egui_windows.checkboxes(ui);
|
self.egui_windows.checkboxes(ui);
|
||||||
|
|
||||||
ui.separator();
|
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
ui.separator();
|
||||||
if ui.ctx().style().debug.debug_on_hover_with_all_modifiers {
|
if ui.ctx().style().debug.debug_on_hover_with_all_modifiers {
|
||||||
ui.label("Press down all modifiers and hover a widget to see a callstack for it");
|
ui.label("Press down all modifiers and hover a widget to see a callstack for it");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
#[cfg(feature = "web_screen-reader")]
|
#[cfg(feature = "web_screen-reader")]
|
||||||
@@ -113,14 +110,6 @@ impl BackendPanel {
|
|||||||
ui.ctx().options_mut(|o| o.screen_reader = screen_reader);
|
ui.ctx().options_mut(|o| o.screen_reader = screen_reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
{
|
|
||||||
ui.separator();
|
|
||||||
if ui.button("Quit").clicked() {
|
|
||||||
ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg!(debug_assertions) && cfg!(target_arch = "wasm32") {
|
if cfg!(debug_assertions) && cfg!(target_arch = "wasm32") {
|
||||||
ui.separator();
|
ui.separator();
|
||||||
// For testing panic handling on web:
|
// For testing panic handling on web:
|
||||||
@@ -129,117 +118,12 @@ impl BackendPanel {
|
|||||||
panic!("intentional panic!");
|
panic!("intentional panic!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !cfg!(target_arch = "wasm32") {
|
||||||
|
ui.separator();
|
||||||
|
if ui.button("Quit").clicked() {
|
||||||
|
ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn integration_ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.spacing_mut().item_spacing.x = 0.0;
|
|
||||||
ui.label("egui running inside ");
|
|
||||||
ui.hyperlink_to(
|
|
||||||
"eframe",
|
|
||||||
"https://github.com/emilk/egui/tree/master/crates/eframe",
|
|
||||||
);
|
|
||||||
ui.label(".");
|
|
||||||
});
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
ui.collapsing("Web info (location)", |ui| {
|
|
||||||
ui.style_mut().wrap = Some(false);
|
|
||||||
ui.monospace(format!("{:#?}", frame.info().web_info.location));
|
|
||||||
});
|
|
||||||
|
|
||||||
// On web, the browser controls `pixels_per_point`.
|
|
||||||
let integration_controls_pixels_per_point = frame.is_web();
|
|
||||||
if !integration_controls_pixels_per_point {
|
|
||||||
self.pixels_per_point_ui(ui);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
{
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
{
|
|
||||||
let mut fullscreen = ui.input(|i| i.viewport().fullscreen.unwrap_or(false));
|
|
||||||
if ui
|
|
||||||
.checkbox(&mut fullscreen, "🗖 Fullscreen (F11)")
|
|
||||||
.on_hover_text("Fullscreen the window")
|
|
||||||
.changed()
|
|
||||||
{
|
|
||||||
ui.ctx()
|
|
||||||
.send_viewport_cmd(egui::ViewportCommand::Fullscreen(fullscreen));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ui
|
|
||||||
.button("📱 Phone Size")
|
|
||||||
.on_hover_text("Resize the window to be small like a phone.")
|
|
||||||
.clicked()
|
|
||||||
{
|
|
||||||
// let size = egui::vec2(375.0, 812.0); // iPhone 12 mini
|
|
||||||
let size = egui::vec2(375.0, 667.0); // iPhone SE 2nd gen
|
|
||||||
|
|
||||||
ui.ctx()
|
|
||||||
.send_viewport_cmd(egui::ViewportCommand::InnerSize(size));
|
|
||||||
ui.ctx()
|
|
||||||
.send_viewport_cmd(egui::ViewportCommand::Fullscreen(false));
|
|
||||||
ui.close_menu();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let fullscreen = ui.input(|i| i.viewport().fullscreen.unwrap_or(false));
|
|
||||||
if !fullscreen
|
|
||||||
&& ui
|
|
||||||
.button("Drag me to drag window")
|
|
||||||
.is_pointer_button_down_on()
|
|
||||||
{
|
|
||||||
ui.ctx().send_viewport_cmd(egui::ViewportCommand::StartDrag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pixels_per_point_ui(&mut self, ui: &mut egui::Ui) {
|
|
||||||
let pixels_per_point = self
|
|
||||||
.pixels_per_point
|
|
||||||
.get_or_insert_with(|| ui.ctx().pixels_per_point());
|
|
||||||
|
|
||||||
let mut reset = false;
|
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.spacing_mut().slider_width = 90.0;
|
|
||||||
|
|
||||||
let response = ui
|
|
||||||
.add(
|
|
||||||
egui::Slider::new(pixels_per_point, 0.5..=5.0)
|
|
||||||
.logarithmic(true)
|
|
||||||
.clamp_to_range(true)
|
|
||||||
.text("Scale"),
|
|
||||||
)
|
|
||||||
.on_hover_text("Physical pixels per point.");
|
|
||||||
|
|
||||||
if response.drag_released() {
|
|
||||||
// We wait until mouse release to activate:
|
|
||||||
ui.ctx().set_pixels_per_point(*pixels_per_point);
|
|
||||||
reset = true;
|
|
||||||
} else if !response.is_pointer_button_down_on() {
|
|
||||||
// When not dragging, show the current pixels_per_point so others can change it.
|
|
||||||
reset = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(native_pixels_per_point) = ui.input(|i| i.raw.native_pixels_per_point) {
|
|
||||||
let enabled = ui.ctx().pixels_per_point() != native_pixels_per_point;
|
|
||||||
if ui
|
|
||||||
.add_enabled(enabled, egui::Button::new("Reset"))
|
|
||||||
.on_hover_text(format!(
|
|
||||||
"Reset scale to native value ({native_pixels_per_point:.1})"
|
|
||||||
))
|
|
||||||
.clicked()
|
|
||||||
{
|
|
||||||
ui.ctx().set_pixels_per_point(native_pixels_per_point);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if reset {
|
|
||||||
self.pixels_per_point = None;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,6 +170,65 @@ impl BackendPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn integration_ui(ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.spacing_mut().item_spacing.x = 0.0;
|
||||||
|
ui.label("egui running inside ");
|
||||||
|
ui.hyperlink_to(
|
||||||
|
"eframe",
|
||||||
|
"https://github.com/emilk/egui/tree/master/crates/eframe",
|
||||||
|
);
|
||||||
|
ui.label(".");
|
||||||
|
});
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
ui.collapsing("Web info (location)", |ui| {
|
||||||
|
ui.style_mut().wrap = Some(false);
|
||||||
|
ui.monospace(format!("{:#?}", _frame.info().web_info.location));
|
||||||
|
});
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
{
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
{
|
||||||
|
let mut fullscreen = ui.input(|i| i.viewport().fullscreen.unwrap_or(false));
|
||||||
|
if ui
|
||||||
|
.checkbox(&mut fullscreen, "🗖 Fullscreen (F11)")
|
||||||
|
.on_hover_text("Fullscreen the window")
|
||||||
|
.changed()
|
||||||
|
{
|
||||||
|
ui.ctx()
|
||||||
|
.send_viewport_cmd(egui::ViewportCommand::Fullscreen(fullscreen));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui
|
||||||
|
.button("📱 Phone Size")
|
||||||
|
.on_hover_text("Resize the window to be small like a phone.")
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
// let size = egui::vec2(375.0, 812.0); // iPhone 12 mini
|
||||||
|
let size = egui::vec2(375.0, 667.0); // iPhone SE 2nd gen
|
||||||
|
|
||||||
|
ui.ctx()
|
||||||
|
.send_viewport_cmd(egui::ViewportCommand::InnerSize(size));
|
||||||
|
ui.ctx()
|
||||||
|
.send_viewport_cmd(egui::ViewportCommand::Fullscreen(false));
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let fullscreen = ui.input(|i| i.viewport().fullscreen.unwrap_or(false));
|
||||||
|
if !fullscreen
|
||||||
|
&& ui
|
||||||
|
.button("Drag me to drag window")
|
||||||
|
.is_pointer_button_down_on()
|
||||||
|
{
|
||||||
|
ui.ctx().send_viewport_cmd(egui::ViewportCommand::StartDrag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
|||||||
@@ -4,6 +4,22 @@
|
|||||||
|
|
||||||
// When compiling natively:
|
// When compiling natively:
|
||||||
fn main() -> Result<(), eframe::Error> {
|
fn main() -> Result<(), eframe::Error> {
|
||||||
|
for arg in std::env::args().skip(1) {
|
||||||
|
match arg.as_str() {
|
||||||
|
"--profile" => {
|
||||||
|
#[cfg(feature = "puffin")]
|
||||||
|
start_puffin_server();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "puffin"))]
|
||||||
|
panic!("Unknown argument: {arg} - you need to enable the 'puffin' feature to use this.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
panic!("Unknown argument: {arg}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Silence wgpu log spam (https://github.com/gfx-rs/wgpu/issues/3206)
|
// Silence wgpu log spam (https://github.com/gfx-rs/wgpu/issues/3206)
|
||||||
let mut rust_log = std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_owned());
|
let mut rust_log = std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_owned());
|
||||||
@@ -33,3 +49,28 @@ fn main() -> Result<(), eframe::Error> {
|
|||||||
Box::new(|cc| Box::new(egui_demo_app::WrapApp::new(cc))),
|
Box::new(|cc| Box::new(egui_demo_app::WrapApp::new(cc))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "puffin")]
|
||||||
|
fn start_puffin_server() {
|
||||||
|
puffin::set_scopes_on(true); // tell puffin to collect data
|
||||||
|
|
||||||
|
match puffin_http::Server::new("127.0.0.1:8585") {
|
||||||
|
Ok(puffin_server) => {
|
||||||
|
eprintln!("Run: cargo install puffin_viewer && puffin_viewer --url 127.0.0.1:8585");
|
||||||
|
|
||||||
|
std::process::Command::new("puffin_viewer")
|
||||||
|
.arg("--url")
|
||||||
|
.arg("127.0.0.1:8585")
|
||||||
|
.spawn()
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
// We can store the server if we want, but in this case we just want
|
||||||
|
// it to keep running. Dropping it closes the server, so let's not drop it!
|
||||||
|
#[allow(clippy::mem_forget)]
|
||||||
|
std::mem::forget(puffin_server);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Failed to start puffin server: {err}");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -283,11 +283,6 @@ impl eframe::App for WrapApp {
|
|||||||
|
|
||||||
self.ui_file_drag_and_drop(ctx);
|
self.ui_file_drag_and_drop(ctx);
|
||||||
|
|
||||||
// On web, the browser controls `pixels_per_point`.
|
|
||||||
if !frame.is_web() {
|
|
||||||
egui::gui_zoom::zoom_with_keyboard_shortcuts(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.run_cmd(ctx, cmd);
|
self.run_cmd(ctx, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "egui_demo_lib"
|
name = "egui_demo_lib"
|
||||||
version = "0.23.0"
|
version.workspace = true
|
||||||
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
||||||
description = "Example library for egui"
|
description = "Example library for egui"
|
||||||
edition = "2021"
|
edition.workspace = true
|
||||||
rust-version = "1.72"
|
rust-version.workspace = true
|
||||||
homepage = "https://github.com/emilk/egui/tree/master/crates/egui_demo_lib"
|
homepage = "https://github.com/emilk/egui/tree/master/crates/egui_demo_lib"
|
||||||
license = "MIT OR Apache-2.0"
|
license.workspace = true
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/emilk/egui/tree/master/crates/egui_demo_lib"
|
repository = "https://github.com/emilk/egui/tree/master/crates/egui_demo_lib"
|
||||||
categories = ["gui", "graphics"]
|
categories = ["gui", "graphics"]
|
||||||
@@ -38,9 +38,9 @@ syntect = ["egui_extras/syntect"]
|
|||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
egui = { version = "0.23.0", path = "../egui", default-features = false }
|
egui = { version = "0.24.0", path = "../egui", default-features = false }
|
||||||
egui_extras = { version = "0.23.0", path = "../egui_extras" }
|
egui_extras = { version = "0.24.0", path = "../egui_extras" }
|
||||||
egui_plot = { version = "0.23.0", path = "../egui_plot" }
|
egui_plot = { version = "0.24.0", path = "../egui_plot" }
|
||||||
log = { version = "0.4", features = ["std"] }
|
log = { version = "0.4", features = ["std"] }
|
||||||
unicode_names2 = { version = "0.6.0", default-features = false } # this old version has fewer dependencies
|
unicode_names2 = { version = "0.6.0", default-features = false } # this old version has fewer dependencies
|
||||||
|
|
||||||
|
|||||||
@@ -329,7 +329,12 @@ fn file_menu_button(ui: &mut Ui) {
|
|||||||
// On the web the browser controls the zoom
|
// On the web the browser controls the zoom
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
{
|
{
|
||||||
egui::gui_zoom::zoom_menu_buttons(ui, None);
|
egui::gui_zoom::zoom_menu_buttons(ui);
|
||||||
|
ui.weak(format!(
|
||||||
|
"Current zoom: {:.0}%",
|
||||||
|
100.0 * ui.ctx().zoom_factor()
|
||||||
|
))
|
||||||
|
.on_hover_text("The UI zoom level, on top of the operating system's default value");
|
||||||
ui.separator();
|
ui.separator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
|
|
||||||
#![allow(clippy::float_cmp)]
|
#![allow(clippy::float_cmp)]
|
||||||
#![allow(clippy::manual_range_contains)]
|
#![allow(clippy::manual_range_contains)]
|
||||||
#![deny(unsafe_code)]
|
#![cfg_attr(feature = "puffin", deny(unsafe_code))]
|
||||||
|
#![cfg_attr(not(feature = "puffin"), forbid(unsafe_code))]
|
||||||
|
|
||||||
mod color_test;
|
mod color_test;
|
||||||
mod demo;
|
mod demo;
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ This file is updated upon each release.
|
|||||||
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.24.0 - 2023-11-23
|
||||||
|
* Fix Table stripe pattern when combining `row()` and `rows()` [#3442](https://github.com/emilk/egui/pull/3442) (thanks [@YgorSouza](https://github.com/YgorSouza)!)
|
||||||
|
* Update MSRV to Rust 1.72 [#3595](https://github.com/emilk/egui/pull/3595)
|
||||||
|
|
||||||
|
|
||||||
## 0.23.0 - 2023-09-27
|
## 0.23.0 - 2023-09-27
|
||||||
* `egui_extras::install_image_loaders` [#3297](https://github.com/emilk/egui/pull/3297) [#3315](https://github.com/emilk/egui/pull/3315) [#3328](https://github.com/emilk/egui/pull/3328) (thanks [@jprochazk](https://github.com/jprochazk)!)
|
* `egui_extras::install_image_loaders` [#3297](https://github.com/emilk/egui/pull/3297) [#3315](https://github.com/emilk/egui/pull/3315) [#3328](https://github.com/emilk/egui/pull/3328) (thanks [@jprochazk](https://github.com/jprochazk)!)
|
||||||
* Add syntax highlighting feature to `egui_extras` [#3333](https://github.com/emilk/egui/pull/3333) [#3388](https://github.com/emilk/egui/pull/3388)
|
* Add syntax highlighting feature to `egui_extras` [#3333](https://github.com/emilk/egui/pull/3333) [#3388](https://github.com/emilk/egui/pull/3388)
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "egui_extras"
|
name = "egui_extras"
|
||||||
version = "0.23.0"
|
version.workspace = true
|
||||||
authors = [
|
authors = [
|
||||||
"Dominik Rössler <dominik@freshx.de>",
|
"Dominik Rössler <dominik@freshx.de>",
|
||||||
"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>",
|
"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>",
|
||||||
"René Rössler <rene@freshx.de>",
|
"René Rössler <rene@freshx.de>",
|
||||||
]
|
]
|
||||||
description = "Extra functionality and widgets for the egui GUI library"
|
description = "Extra functionality and widgets for the egui GUI library"
|
||||||
edition = "2021"
|
edition.workspace = true
|
||||||
rust-version = "1.72"
|
rust-version.workspace = true
|
||||||
homepage = "https://github.com/emilk/egui"
|
homepage = "https://github.com/emilk/egui"
|
||||||
license = "MIT OR Apache-2.0"
|
license.workspace = true
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/emilk/egui"
|
repository = "https://github.com/emilk/egui"
|
||||||
categories = ["gui", "game-development"]
|
categories = ["gui", "game-development"]
|
||||||
@@ -60,7 +60,7 @@ syntect = ["dep:syntect"]
|
|||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
egui = { version = "0.23.0", path = "../egui", default-features = false, features = [
|
egui = { version = "0.24.0", path = "../egui", default-features = false, features = [
|
||||||
"serde",
|
"serde",
|
||||||
] }
|
] }
|
||||||
enum-map = { version = "2", features = ["serde"] }
|
enum-map = { version = "2", features = ["serde"] }
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
|
|
||||||
#![allow(clippy::float_cmp)]
|
#![allow(clippy::float_cmp)]
|
||||||
#![allow(clippy::manual_range_contains)]
|
#![allow(clippy::manual_range_contains)]
|
||||||
#![deny(unsafe_code)]
|
#![cfg_attr(feature = "puffin", deny(unsafe_code))]
|
||||||
|
#![cfg_attr(not(feature = "puffin"), forbid(unsafe_code))]
|
||||||
|
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
mod datepicker;
|
mod datepicker;
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ This file is updated upon each release.
|
|||||||
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.24.0 - 2023-11-23
|
||||||
|
* Change `Arc<glow::Context>` to `Rc<glow::Context>` [#3598](https://github.com/emilk/egui/pull/3598)
|
||||||
|
* Update MSRV to Rust 1.72 [#3595](https://github.com/emilk/egui/pull/3595)
|
||||||
|
* Clamp viewport values [#3604](https://github.com/emilk/egui/pull/3604) (thanks [@Wumpf](https://github.com/Wumpf)!)
|
||||||
|
|
||||||
|
|
||||||
## 0.23.0 - 2023-09-27
|
## 0.23.0 - 2023-09-27
|
||||||
* Update `egui`
|
* Update `egui`
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "egui_glow"
|
name = "egui_glow"
|
||||||
version = "0.23.0"
|
version.workspace = true
|
||||||
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
||||||
description = "Bindings for using egui natively using the glow library"
|
description = "Bindings for using egui natively using the glow library"
|
||||||
edition = "2021"
|
edition.workspace = true
|
||||||
rust-version = "1.72"
|
rust-version.workspace = true
|
||||||
homepage = "https://github.com/emilk/egui/tree/master/crates/egui_glow"
|
homepage = "https://github.com/emilk/egui/tree/master/crates/egui_glow"
|
||||||
license = "MIT OR Apache-2.0"
|
license.workspace = true
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/emilk/egui/tree/master/crates/egui_glow"
|
repository = "https://github.com/emilk/egui/tree/master/crates/egui_glow"
|
||||||
categories = ["gui", "game-development"]
|
categories = ["gui", "game-development"]
|
||||||
@@ -44,7 +44,7 @@ winit = ["egui-winit"]
|
|||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
egui = { version = "0.23.0", path = "../egui", default-features = false, features = [
|
egui = { version = "0.24.0", path = "../egui", default-features = false, features = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ document-features = { version = "0.2", optional = true }
|
|||||||
|
|
||||||
# Native:
|
# Native:
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
egui-winit = { version = "0.23.0", path = "../egui-winit", optional = true, default-features = false }
|
egui-winit = { version = "0.24.0", path = "../egui-winit", optional = true, default-features = false }
|
||||||
puffin = { workspace = true, optional = true }
|
puffin = { workspace = true, optional = true }
|
||||||
|
|
||||||
# Web:
|
# Web:
|
||||||
|
|||||||
@@ -404,10 +404,10 @@ impl Painter {
|
|||||||
let viewport_px = info.viewport_in_pixels();
|
let viewport_px = info.viewport_in_pixels();
|
||||||
unsafe {
|
unsafe {
|
||||||
self.gl.viewport(
|
self.gl.viewport(
|
||||||
viewport_px.left_px.round() as _,
|
viewport_px.left_px,
|
||||||
viewport_px.from_bottom_px.round() as _,
|
viewport_px.from_bottom_px,
|
||||||
viewport_px.width_px.round() as _,
|
viewport_px.width_px,
|
||||||
viewport_px.height_px.round() as _,
|
viewport_px.height_px,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
pub use egui_winit;
|
pub use egui_winit;
|
||||||
pub use egui_winit::EventResponse;
|
pub use egui_winit::EventResponse;
|
||||||
|
|
||||||
use egui::{ViewportId, ViewportIdPair, ViewportOutput};
|
use egui::{ViewportId, ViewportOutput};
|
||||||
use egui_winit::winit;
|
use egui_winit::winit;
|
||||||
|
|
||||||
use crate::shader_version::ShaderVersion;
|
use crate::shader_version::ShaderVersion;
|
||||||
@@ -35,6 +35,7 @@ impl EguiGlow {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let egui_winit = egui_winit::State::new(
|
let egui_winit = egui_winit::State::new(
|
||||||
|
ViewportId::ROOT,
|
||||||
event_loop,
|
event_loop,
|
||||||
native_pixels_per_point,
|
native_pixels_per_point,
|
||||||
Some(painter.max_texture_side()),
|
Some(painter.max_texture_side()),
|
||||||
@@ -58,9 +59,7 @@ impl EguiGlow {
|
|||||||
|
|
||||||
/// Call [`Self::paint`] later to paint.
|
/// Call [`Self::paint`] later to paint.
|
||||||
pub fn run(&mut self, window: &winit::window::Window, run_ui: impl FnMut(&egui::Context)) {
|
pub fn run(&mut self, window: &winit::window::Window, run_ui: impl FnMut(&egui::Context)) {
|
||||||
let raw_input = self
|
let raw_input = self.egui_winit.take_egui_input(window);
|
||||||
.egui_winit
|
|
||||||
.take_egui_input(window, ViewportIdPair::ROOT);
|
|
||||||
|
|
||||||
let egui::FullOutput {
|
let egui::FullOutput {
|
||||||
platform_output,
|
platform_output,
|
||||||
@@ -76,6 +75,7 @@ impl EguiGlow {
|
|||||||
for (_, ViewportOutput { commands, .. }) in viewport_output {
|
for (_, ViewportOutput { commands, .. }) in viewport_output {
|
||||||
let mut screenshot_requested = false;
|
let mut screenshot_requested = false;
|
||||||
egui_winit::process_viewport_commands(
|
egui_winit::process_viewport_commands(
|
||||||
|
&self.egui_ctx,
|
||||||
&mut self.viewport_info,
|
&mut self.viewport_info,
|
||||||
commands,
|
commands,
|
||||||
window,
|
window,
|
||||||
@@ -87,12 +87,8 @@ impl EguiGlow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.egui_winit.handle_platform_output(
|
self.egui_winit
|
||||||
window,
|
.handle_platform_output(window, &self.egui_ctx, platform_output);
|
||||||
ViewportId::ROOT,
|
|
||||||
&self.egui_ctx,
|
|
||||||
platform_output,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.shapes = shapes;
|
self.shapes = shapes;
|
||||||
self.pixels_per_point = pixels_per_point;
|
self.pixels_per_point = pixels_per_point;
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ This file is updated upon each release.
|
|||||||
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.24.0 - 2023-11-23
|
||||||
|
* Add `emath::Vec2b`, replacing `egui_plot::AxisBools` [#3543](https://github.com/emilk/egui/pull/3543)
|
||||||
|
* Add `auto_bounds/set_auto_bounds` to `PlotUi` [#3587](https://github.com/emilk/egui/pull/3587) [#3586](https://github.com/emilk/egui/pull/3586) (thanks [@abey79](https://github.com/abey79)!)
|
||||||
|
* Update MSRV to Rust 1.72 [#3595](https://github.com/emilk/egui/pull/3595)
|
||||||
|
|
||||||
|
|
||||||
## 0.23.0 - 2023-09-27 - Initial release, after being forked out from `egui`
|
## 0.23.0 - 2023-09-27 - Initial release, after being forked out from `egui`
|
||||||
* Draw axis labels and ticks outside of plotting window [#2284](https://github.com/emilk/egui/pull/2284) (thanks [@JohannesProgrammiert](https://github.com/JohannesProgrammiert)!)
|
* Draw axis labels and ticks outside of plotting window [#2284](https://github.com/emilk/egui/pull/2284) (thanks [@JohannesProgrammiert](https://github.com/JohannesProgrammiert)!)
|
||||||
* Add `PlotUi::response()` to replace `plot_clicked()` etc [#3223](https://github.com/emilk/egui/pull/3223)
|
* Add `PlotUi::response()` to replace `plot_clicked()` etc [#3223](https://github.com/emilk/egui/pull/3223)
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "egui_plot"
|
name = "egui_plot"
|
||||||
version = "0.23.0"
|
version.workspace = true
|
||||||
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
||||||
description = "Immediate mode plotting for the egui GUI library"
|
description = "Immediate mode plotting for the egui GUI library"
|
||||||
edition = "2021"
|
edition.workspace = true
|
||||||
rust-version = "1.72"
|
rust-version.workspace = true
|
||||||
homepage = "https://github.com/emilk/egui"
|
homepage = "https://github.com/emilk/egui"
|
||||||
license = "MIT OR Apache-2.0"
|
license.workspace = true
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/emilk/egui"
|
repository = "https://github.com/emilk/egui"
|
||||||
categories = ["visualization", "gui"]
|
categories = ["visualization", "gui"]
|
||||||
@@ -28,7 +28,7 @@ serde = ["dep:serde", "egui/serde"]
|
|||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
egui = { version = "0.23.0", path = "../egui", default-features = false }
|
egui = { version = "0.24.0", path = "../egui", default-features = false }
|
||||||
|
|
||||||
|
|
||||||
#! ### Optional dependencies
|
#! ### Optional dependencies
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "emath"
|
name = "emath"
|
||||||
version = "0.23.0"
|
version.workspace = true
|
||||||
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
||||||
description = "Minimal 2D math library for GUI work"
|
description = "Minimal 2D math library for GUI work"
|
||||||
edition = "2021"
|
edition.workspace = true
|
||||||
rust-version = "1.72"
|
rust-version.workspace = true
|
||||||
homepage = "https://github.com/emilk/egui/tree/master/crates/emath"
|
homepage = "https://github.com/emilk/egui/tree/master/crates/emath"
|
||||||
license = "MIT OR Apache-2.0"
|
license.workspace = true
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/emilk/egui/tree/master/crates/emath"
|
repository = "https://github.com/emilk/egui/tree/master/crates/emath"
|
||||||
categories = ["mathematics", "gui"]
|
categories = ["mathematics", "gui"]
|
||||||
|
|||||||
@@ -20,7 +20,8 @@
|
|||||||
//!
|
//!
|
||||||
|
|
||||||
#![allow(clippy::float_cmp)]
|
#![allow(clippy::float_cmp)]
|
||||||
#![deny(unsafe_code)]
|
#![cfg_attr(feature = "puffin", deny(unsafe_code))]
|
||||||
|
#![cfg_attr(not(feature = "puffin"), forbid(unsafe_code))]
|
||||||
|
|
||||||
use std::ops::{Add, Div, Mul, RangeInclusive, Sub};
|
use std::ops::{Add, Div, Mul, RangeInclusive, Sub};
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ This file is updated upon each release.
|
|||||||
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
|
||||||
|
|
||||||
|
|
||||||
|
## 0.24.0 - 2023-11-23
|
||||||
|
* Use `impl Into<Stroke>` as argument in a few more places [#3420](https://github.com/emilk/egui/pull/3420) (thanks [@Phen-Ro](https://github.com/Phen-Ro)!)
|
||||||
|
* Update MSRV to Rust 1.72 [#3595](https://github.com/emilk/egui/pull/3595)
|
||||||
|
* Make `ViewportInPixels` use integers, and clamp to bounds [#3604](https://github.com/emilk/egui/pull/3604) (thanks [@Wumpf](https://github.com/Wumpf)!)
|
||||||
|
|
||||||
|
|
||||||
## 0.23.0 - 2023-09-27
|
## 0.23.0 - 2023-09-27
|
||||||
* Update MSRV to Rust 1.70.0 [#3310](https://github.com/emilk/egui/pull/3310)
|
* Update MSRV to Rust 1.70.0 [#3310](https://github.com/emilk/egui/pull/3310)
|
||||||
* Add option to truncate text at wrap width [#3244](https://github.com/emilk/egui/pull/3244) [#3366](https://github.com/emilk/egui/pull/3366)
|
* Add option to truncate text at wrap width [#3244](https://github.com/emilk/egui/pull/3244) [#3366](https://github.com/emilk/egui/pull/3366)
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "epaint"
|
name = "epaint"
|
||||||
version = "0.23.0"
|
version.workspace = true
|
||||||
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
|
||||||
description = "Minimal 2D graphics library for GUI work"
|
description = "Minimal 2D graphics library for GUI work"
|
||||||
edition = "2021"
|
edition.workspace = true
|
||||||
rust-version = "1.72"
|
rust-version.workspace = true
|
||||||
homepage = "https://github.com/emilk/egui/tree/master/crates/epaint"
|
homepage = "https://github.com/emilk/egui/tree/master/crates/epaint"
|
||||||
license = "(MIT OR Apache-2.0) AND OFL-1.1 AND LicenseRef-UFL-1.0" # OFL and UFL used by default_fonts. See https://github.com/emilk/egui/issues/2321
|
license = "(MIT OR Apache-2.0) AND OFL-1.1 AND LicenseRef-UFL-1.0" # OFL and UFL used by default_fonts. See https://github.com/emilk/egui/issues/2321
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -70,8 +70,8 @@ serde = ["dep:serde", "ahash/serde", "emath/serde", "ecolor/serde"]
|
|||||||
unity = []
|
unity = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
emath = { version = "0.23.0", path = "../emath" }
|
emath = { version = "0.24.0", path = "../emath" }
|
||||||
ecolor = { version = "0.23.0", path = "../ecolor" }
|
ecolor = { version = "0.24.0", path = "../ecolor" }
|
||||||
|
|
||||||
ab_glyph = "0.2.11"
|
ab_glyph = "0.2.11"
|
||||||
ahash = { version = "0.8.1", default-features = false, features = [
|
ahash = { version = "0.8.1", default-features = false, features = [
|
||||||
|
|||||||
@@ -22,7 +22,8 @@
|
|||||||
|
|
||||||
#![allow(clippy::float_cmp)]
|
#![allow(clippy::float_cmp)]
|
||||||
#![allow(clippy::manual_range_contains)]
|
#![allow(clippy::manual_range_contains)]
|
||||||
#![deny(unsafe_code)]
|
#![cfg_attr(feature = "puffin", deny(unsafe_code))]
|
||||||
|
#![cfg_attr(not(feature = "puffin"), forbid(unsafe_code))]
|
||||||
|
|
||||||
mod bezier;
|
mod bezier;
|
||||||
pub mod image;
|
pub mod image;
|
||||||
|
|||||||
@@ -803,44 +803,83 @@ pub struct PaintCallbackInfo {
|
|||||||
pub screen_size_px: [u32; 2],
|
pub screen_size_px: [u32; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Size of the viewport in whole, physical pixels.
|
||||||
pub struct ViewportInPixels {
|
pub struct ViewportInPixels {
|
||||||
/// Physical pixel offset for left side of the viewport.
|
/// Physical pixel offset for left side of the viewport.
|
||||||
pub left_px: f32,
|
pub left_px: i32,
|
||||||
|
|
||||||
/// Physical pixel offset for top side of the viewport.
|
/// Physical pixel offset for top side of the viewport.
|
||||||
pub top_px: f32,
|
pub top_px: i32,
|
||||||
|
|
||||||
/// Physical pixel offset for bottom side of the viewport.
|
/// Physical pixel offset for bottom side of the viewport.
|
||||||
///
|
///
|
||||||
/// This is what `glViewport`, `glScissor` etc expects for the y axis.
|
/// This is what `glViewport`, `glScissor` etc expects for the y axis.
|
||||||
pub from_bottom_px: f32,
|
pub from_bottom_px: i32,
|
||||||
|
|
||||||
/// Viewport width in physical pixels.
|
/// Viewport width in physical pixels.
|
||||||
pub width_px: f32,
|
pub width_px: i32,
|
||||||
|
|
||||||
/// Viewport height in physical pixels.
|
/// Viewport height in physical pixels.
|
||||||
pub height_px: f32,
|
pub height_px: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ViewportInPixels {
|
||||||
|
fn from_points(rect: &Rect, pixels_per_point: f32, screen_size_px: [u32; 2]) -> Self {
|
||||||
|
// Fractional pixel values for viewports are generally valid, but may cause sampling issues
|
||||||
|
// and rounding errors might cause us to get out of bounds.
|
||||||
|
|
||||||
|
// Round:
|
||||||
|
let left_px = (pixels_per_point * rect.min.x).round() as i32; // inclusive
|
||||||
|
let top_px = (pixels_per_point * rect.min.y).round() as i32; // inclusive
|
||||||
|
let right_px = (pixels_per_point * rect.max.x).round() as i32; // exclusive
|
||||||
|
let bottom_px = (pixels_per_point * rect.max.y).round() as i32; // exclusive
|
||||||
|
|
||||||
|
// Clamp to screen:
|
||||||
|
let screen_width = screen_size_px[0] as i32;
|
||||||
|
let screen_height = screen_size_px[1] as i32;
|
||||||
|
let left_px = left_px.clamp(0, screen_width);
|
||||||
|
let right_px = right_px.clamp(left_px, screen_width);
|
||||||
|
let top_px = top_px.clamp(0, screen_height);
|
||||||
|
let bottom_px = bottom_px.clamp(top_px, screen_height);
|
||||||
|
|
||||||
|
let width_px = right_px - left_px;
|
||||||
|
let height_px = bottom_px - top_px;
|
||||||
|
|
||||||
|
Self {
|
||||||
|
left_px,
|
||||||
|
top_px,
|
||||||
|
from_bottom_px: screen_height - height_px - top_px,
|
||||||
|
width_px,
|
||||||
|
height_px,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_viewport_rounding() {
|
||||||
|
for i in 0..=10_000 {
|
||||||
|
// Two adjacent viewports should never overlap:
|
||||||
|
let x = i as f32 / 97.0;
|
||||||
|
let left = Rect::from_min_max(pos2(0.0, 0.0), pos2(100.0, 100.0)).with_max_x(x);
|
||||||
|
let right = Rect::from_min_max(pos2(0.0, 0.0), pos2(100.0, 100.0)).with_min_x(x);
|
||||||
|
|
||||||
|
for pixels_per_point in [0.618, 1.0, std::f32::consts::PI] {
|
||||||
|
let left = ViewportInPixels::from_points(&left, pixels_per_point, [100, 100]);
|
||||||
|
let right = ViewportInPixels::from_points(&right, pixels_per_point, [100, 100]);
|
||||||
|
assert_eq!(left.left_px + left.width_px, right.left_px);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaintCallbackInfo {
|
impl PaintCallbackInfo {
|
||||||
fn pixels_from_points(&self, rect: &Rect) -> ViewportInPixels {
|
|
||||||
ViewportInPixels {
|
|
||||||
left_px: rect.min.x * self.pixels_per_point,
|
|
||||||
top_px: rect.min.y * self.pixels_per_point,
|
|
||||||
from_bottom_px: self.screen_size_px[1] as f32 - rect.max.y * self.pixels_per_point,
|
|
||||||
width_px: rect.width() * self.pixels_per_point,
|
|
||||||
height_px: rect.height() * self.pixels_per_point,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The viewport rectangle. This is what you would use in e.g. `glViewport`.
|
/// The viewport rectangle. This is what you would use in e.g. `glViewport`.
|
||||||
pub fn viewport_in_pixels(&self) -> ViewportInPixels {
|
pub fn viewport_in_pixels(&self) -> ViewportInPixels {
|
||||||
self.pixels_from_points(&self.viewport)
|
ViewportInPixels::from_points(&self.viewport, self.pixels_per_point, self.screen_size_px)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The "scissor" or "clip" rectangle. This is what you would use in e.g. `glScissor`.
|
/// The "scissor" or "clip" rectangle. This is what you would use in e.g. `glScissor`.
|
||||||
pub fn clip_rect_in_pixels(&self) -> ViewportInPixels {
|
pub fn clip_rect_in_pixels(&self) -> ViewportInPixels {
|
||||||
self.pixels_from_points(&self.clip_rect)
|
ViewportInPixels::from_points(&self.clip_rect, self.pixels_per_point, self.screen_size_px)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,10 +36,13 @@ deny = [
|
|||||||
skip = [
|
skip = [
|
||||||
{ name = "arrayvec" }, # old version via tiny-skiaz
|
{ name = "arrayvec" }, # old version via tiny-skiaz
|
||||||
{ name = "base64" }, # small crate, old version from usvg
|
{ name = "base64" }, # small crate, old version from usvg
|
||||||
|
{ name = "glow" }, # TODO(@wumpf): Old version use for glow backend right now, newer for wgpu. Updating this trickles out to updating winit.
|
||||||
|
{ name = "glutin_wgl_sys" }, # TODO(@wumpf): Old version use for glow backend right now, newer for wgpu. Updating this trickles out to updating winit.
|
||||||
{ name = "libloading" }, # wgpu-hal itself depends on 0.8 while some of its dependencies, like ash and d3d12, depend on 0.7
|
{ name = "libloading" }, # wgpu-hal itself depends on 0.8 while some of its dependencies, like ash and d3d12, depend on 0.7
|
||||||
{ name = "memoffset" }, # tiny dependency
|
{ name = "memoffset" }, # tiny dependency
|
||||||
{ name = "nix" }, # old version via winit
|
{ name = "nix" }, # old version via winit
|
||||||
{ name = "redox_syscall" }, # old version via winit
|
{ name = "redox_syscall" }, # old version via winit
|
||||||
|
{ name = "spin" }, # old version via ring through rusttls and other libraries, newer for wgpu.
|
||||||
{ name = "time" }, # old version pulled in by unmaintianed crate 'chrono'
|
{ name = "time" }, # old version pulled in by unmaintianed crate 'chrono'
|
||||||
{ name = "tiny-skia" }, # winit uses a different version from egui_extras (TODO(emilk): update egui_extras!)
|
{ name = "tiny-skia" }, # winit uses a different version from egui_extras (TODO(emilk): update egui_extras!)
|
||||||
{ name = "ttf-parser" }, # different versions pulled in by ab_glyph and usvg
|
{ name = "ttf-parser" }, # different versions pulled in by ab_glyph and usvg
|
||||||
@@ -47,9 +50,6 @@ skip = [
|
|||||||
{ name = "windows_x86_64_msvc" }, # old version via glutin
|
{ name = "windows_x86_64_msvc" }, # old version via glutin
|
||||||
{ name = "windows-sys" }, # old version via glutin
|
{ name = "windows-sys" }, # old version via glutin
|
||||||
{ name = "windows" }, # old version via accesskit
|
{ name = "windows" }, # old version via accesskit
|
||||||
{ name = "spin" }, # old version via ring through rusttls and other libraries, newer for wgpu.
|
|
||||||
{ name = "glow" }, # TODO(@wumpf): Old version use for glow backend right now, newer for wgpu. Updating this trickles out to updating winit.
|
|
||||||
{ name = "glutin_wgl_sys" }, # TODO(@wumpf): As above
|
|
||||||
]
|
]
|
||||||
skip-tree = [
|
skip-tree = [
|
||||||
{ name = "criterion" }, # dev-dependency
|
{ name = "criterion" }, # dev-dependency
|
||||||
|
|||||||
@@ -17,33 +17,38 @@ fn main() -> Result<(), eframe::Error> {
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct MyApp {
|
struct MyApp {
|
||||||
allowed_to_close: bool,
|
|
||||||
show_confirmation_dialog: bool,
|
show_confirmation_dialog: bool,
|
||||||
|
allowed_to_close: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl eframe::App for MyApp {
|
impl eframe::App for MyApp {
|
||||||
fn on_close_event(&mut self) -> bool {
|
|
||||||
self.show_confirmation_dialog = true;
|
|
||||||
self.allowed_to_close
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
ui.heading("Try to close the window");
|
ui.heading("Try to close the window");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if ctx.input(|i| i.viewport().close_requested()) {
|
||||||
|
if self.allowed_to_close {
|
||||||
|
// do nothing - we will close
|
||||||
|
} else {
|
||||||
|
ctx.send_viewport_cmd(egui::ViewportCommand::CancelClose);
|
||||||
|
self.show_confirmation_dialog = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.show_confirmation_dialog {
|
if self.show_confirmation_dialog {
|
||||||
// Show confirmation dialog:
|
|
||||||
egui::Window::new("Do you want to quit?")
|
egui::Window::new("Do you want to quit?")
|
||||||
.collapsible(false)
|
.collapsible(false)
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
if ui.button("Cancel").clicked() {
|
if ui.button("No").clicked() {
|
||||||
self.show_confirmation_dialog = false;
|
self.show_confirmation_dialog = false;
|
||||||
|
self.allowed_to_close = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ui.button("Yes!").clicked() {
|
if ui.button("Yes").clicked() {
|
||||||
|
self.show_confirmation_dialog = false;
|
||||||
self.allowed_to_close = true;
|
self.allowed_to_close = true;
|
||||||
ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close);
|
ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ fn main() -> Result<(), eframe::Error> {
|
|||||||
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
||||||
let options = eframe::NativeOptions {
|
let options = eframe::NativeOptions {
|
||||||
viewport: egui::ViewportBuilder::default()
|
viewport: egui::ViewportBuilder::default()
|
||||||
|
.with_decorations(false) // Hide the OS-specific "chrome" around the window
|
||||||
.with_inner_size([400.0, 100.0])
|
.with_inner_size([400.0, 100.0])
|
||||||
.with_min_inner_size([400.0, 100.0])
|
.with_min_inner_size([400.0, 100.0])
|
||||||
.with_decorations(false) // Hide the OS-specific "chrome" around the window
|
|
||||||
.with_transparent(true), // To have rounded corners we need transparency
|
.with_transparent(true), // To have rounded corners we need transparency
|
||||||
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ fn main() -> Result<(), eframe::Error> {
|
|||||||
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
||||||
let options = eframe::NativeOptions {
|
let options = eframe::NativeOptions {
|
||||||
viewport: egui::ViewportBuilder::default()
|
viewport: egui::ViewportBuilder::default()
|
||||||
.with_inner_size([320.0, 240.0])
|
.with_inner_size([640.0, 240.0]) // wide enough for the drag-drop overlay text
|
||||||
.with_drag_and_drop(true),
|
.with_drag_and_drop(true),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,11 +26,11 @@ impl eframe::App for MyApp {
|
|||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
egui::ScrollArea::both().show(ui, |ui| {
|
egui::ScrollArea::both().show(ui, |ui| {
|
||||||
ui.image(egui::include_image!("ferris.svg"));
|
|
||||||
|
|
||||||
ui.add(
|
ui.add(
|
||||||
egui::Image::new("https://picsum.photos/seed/1.759706314/1024").rounding(10.0),
|
egui::Image::new("https://picsum.photos/seed/1.759706314/1024").rounding(10.0),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ui.image(egui::include_image!("ferris.svg"));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ rust-version = "1.72"
|
|||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
|
|
||||||
|
[features]
|
||||||
|
wgpu = ["eframe/wgpu"]
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
eframe = { path = "../../crates/eframe", features = [
|
eframe = { path = "../../crates/eframe", features = [
|
||||||
"puffin",
|
"puffin",
|
||||||
|
|||||||
@@ -13,8 +13,17 @@ fn main() -> Result<(), eframe::Error> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
struct MyApp {
|
||||||
struct MyApp {}
|
keep_repainting: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MyApp {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
keep_repainting: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl eframe::App for MyApp {
|
impl eframe::App for MyApp {
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
@@ -34,7 +43,15 @@ impl eframe::App for MyApp {
|
|||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|
||||||
ui.label("Note that this app runs in 'reactive' mode, so you must interact with the app for new profile events to be sent. Waving the mouse over this window is enough.");
|
ui.horizontal(|ui| {
|
||||||
|
ui.checkbox(&mut self.keep_repainting, "Keep repainting");
|
||||||
|
if self.keep_repainting {
|
||||||
|
ui.spinner();
|
||||||
|
ui.ctx().request_repaint();
|
||||||
|
} else {
|
||||||
|
ui.label("Repainting on events (e.g. mouse movement)");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if ui
|
if ui
|
||||||
.button(
|
.button(
|
||||||
@@ -42,9 +59,15 @@ impl eframe::App for MyApp {
|
|||||||
)
|
)
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
puffin::profile_scope!("sleep");
|
puffin::profile_scope!("long_sleep");
|
||||||
std::thread::sleep(std::time::Duration::from_millis(50));
|
std::thread::sleep(std::time::Duration::from_millis(50));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Sleep a bit to emulate some work:
|
||||||
|
puffin::profile_scope!("small_sleep");
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,7 +75,7 @@ impl eframe::App for MyApp {
|
|||||||
fn start_puffin_server() {
|
fn start_puffin_server() {
|
||||||
puffin::set_scopes_on(true); // tell puffin to collect data
|
puffin::set_scopes_on(true); // tell puffin to collect data
|
||||||
|
|
||||||
match puffin_http::Server::new("0.0.0.0:8585") {
|
match puffin_http::Server::new("127.0.0.1:8585") {
|
||||||
Ok(puffin_server) => {
|
Ok(puffin_server) => {
|
||||||
eprintln!("Run: cargo install puffin_viewer && puffin_viewer --url 127.0.0.1:8585");
|
eprintln!("Run: cargo install puffin_viewer && puffin_viewer --url 127.0.0.1:8585");
|
||||||
|
|
||||||
|
|||||||
11
examples/run_all.sh
Executable file
11
examples/run_all.sh
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -eu
|
||||||
|
script_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )
|
||||||
|
cd "$script_path/"
|
||||||
|
set -x
|
||||||
|
|
||||||
|
for example_name in *; do
|
||||||
|
if [ -d "$example_name" ]; then
|
||||||
|
cargo run --quiet -p $example_name
|
||||||
|
fi
|
||||||
|
done
|
||||||
@@ -6,7 +6,10 @@ fn main() -> Result<(), eframe::Error> {
|
|||||||
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
|
||||||
|
|
||||||
if cfg!(target_os = "macos") {
|
if cfg!(target_os = "macos") {
|
||||||
eprintln!("WARNING: this example does not work on Mac! See https://github.com/emilk/egui/issues/1918");
|
eprintln!(
|
||||||
|
"This example does not work on Mac! See https://github.com/emilk/egui/issues/1918"
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let options = eframe::NativeOptions {
|
let options = eframe::NativeOptions {
|
||||||
@@ -54,6 +57,11 @@ impl eframe::App for MyApp {
|
|||||||
"This is the last window. Program will end when closed"
|
"This is the last window. Program will end when closed"
|
||||||
};
|
};
|
||||||
ui.label(label_text);
|
ui.label(label_text);
|
||||||
|
|
||||||
|
if ctx.os() == egui::os::OperatingSystem::Mac {
|
||||||
|
ui.label("This example doesn't work on Mac!");
|
||||||
|
}
|
||||||
|
|
||||||
if ui.button("Close").clicked() {
|
if ui.button("Close").clicked() {
|
||||||
eprintln!("Pressed Close button");
|
eprintln!("Pressed Close button");
|
||||||
ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close);
|
ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close);
|
||||||
|
|||||||
@@ -237,30 +237,6 @@ fn generic_ui(ui: &mut egui::Ui, children: &[Arc<RwLock<ViewportState>>]) {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let tmp_pixels_per_point = ctx.pixels_per_point();
|
|
||||||
let mut pixels_per_point = ui.data_mut(|data| {
|
|
||||||
*data.get_temp_mut_or(container_id.with("pixels_per_point"), tmp_pixels_per_point)
|
|
||||||
});
|
|
||||||
let res = ui.add(
|
|
||||||
egui::DragValue::new(&mut pixels_per_point)
|
|
||||||
.prefix("Pixels per Point: ")
|
|
||||||
.speed(0.1)
|
|
||||||
.clamp_range(0.5..=4.0),
|
|
||||||
);
|
|
||||||
if res.drag_released() {
|
|
||||||
ctx.set_pixels_per_point(pixels_per_point);
|
|
||||||
}
|
|
||||||
if res.dragged() {
|
|
||||||
ui.data_mut(|data| {
|
|
||||||
data.insert_temp(container_id.with("pixels_per_point"), pixels_per_point);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
ui.data_mut(|data| {
|
|
||||||
data.insert_temp(container_id.with("pixels_per_point"), tmp_pixels_per_point);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
egui::gui_zoom::zoom_with_keyboard_shortcuts(&ctx);
|
|
||||||
|
|
||||||
if ctx.viewport_id() != ctx.parent_viewport_id() {
|
if ctx.viewport_id() != ctx.parent_viewport_id() {
|
||||||
let parent = ctx.parent_viewport_id();
|
let parent = ctx.parent_viewport_id();
|
||||||
if ui.button("Set parent pos 0,0").clicked() {
|
if ui.button("Set parent pos 0,0").clicked() {
|
||||||
|
|||||||
@@ -20,41 +20,41 @@ export RUSTDOCFLAGS="-D warnings" # https://github.com/emilk/egui/pull/1454
|
|||||||
typos
|
typos
|
||||||
./scripts/lint.py
|
./scripts/lint.py
|
||||||
cargo fmt --all -- --check
|
cargo fmt --all -- --check
|
||||||
cargo doc --lib --no-deps --all-features
|
cargo doc --quiet --lib --no-deps --all-features
|
||||||
cargo doc --document-private-items --no-deps --all-features
|
cargo doc --quiet --document-private-items --no-deps --all-features
|
||||||
|
|
||||||
cargo cranky --all-targets --all-features -- -D warnings
|
cargo cranky --quiet --all-targets --all-features -- -D warnings
|
||||||
./scripts/clippy_wasm.sh
|
./scripts/clippy_wasm.sh
|
||||||
|
|
||||||
cargo check --all-targets
|
cargo check --quiet --all-targets
|
||||||
cargo check --all-targets --all-features
|
cargo check --quiet --all-targets --all-features
|
||||||
cargo check -p egui_demo_app --lib --target wasm32-unknown-unknown
|
cargo check --quiet -p egui_demo_app --lib --target wasm32-unknown-unknown
|
||||||
cargo check -p egui_demo_app --lib --target wasm32-unknown-unknown --all-features
|
cargo check --quiet -p egui_demo_app --lib --target wasm32-unknown-unknown --all-features
|
||||||
cargo test --all-targets --all-features
|
cargo test --quiet --all-targets --all-features
|
||||||
cargo test --doc # slow - checks all doc-tests
|
cargo test --quiet --doc # slow - checks all doc-tests
|
||||||
|
|
||||||
(cd crates/eframe && cargo check --no-default-features --features "glow")
|
(cd crates/eframe && cargo check --quiet --no-default-features --features "glow")
|
||||||
(cd crates/eframe && cargo check --no-default-features --features "wgpu")
|
(cd crates/eframe && cargo check --quiet --no-default-features --features "wgpu")
|
||||||
(cd crates/egui && cargo check --no-default-features --features "serde")
|
(cd crates/egui && cargo check --quiet --no-default-features --features "serde")
|
||||||
(cd crates/egui_demo_app && cargo check --no-default-features --features "glow")
|
(cd crates/egui_demo_app && cargo check --quiet --no-default-features --features "glow")
|
||||||
(cd crates/egui_demo_app && cargo check --no-default-features --features "wgpu")
|
(cd crates/egui_demo_app && cargo check --quiet --no-default-features --features "wgpu")
|
||||||
(cd crates/egui_demo_lib && cargo check --no-default-features)
|
(cd crates/egui_demo_lib && cargo check --quiet --no-default-features)
|
||||||
(cd crates/egui_extras && cargo check --no-default-features)
|
(cd crates/egui_extras && cargo check --quiet --no-default-features)
|
||||||
(cd crates/egui_glow && cargo check --no-default-features)
|
(cd crates/egui_glow && cargo check --quiet --no-default-features)
|
||||||
(cd crates/egui-winit && cargo check --no-default-features --features "wayland")
|
(cd crates/egui-winit && cargo check --quiet --no-default-features --features "wayland")
|
||||||
(cd crates/egui-winit && cargo check --no-default-features --features "x11")
|
(cd crates/egui-winit && cargo check --quiet --no-default-features --features "x11")
|
||||||
(cd crates/emath && cargo check --no-default-features)
|
(cd crates/emath && cargo check --quiet --no-default-features)
|
||||||
(cd crates/epaint && cargo check --no-default-features --release)
|
(cd crates/epaint && cargo check --quiet --no-default-features --release)
|
||||||
(cd crates/epaint && cargo check --no-default-features)
|
(cd crates/epaint && cargo check --quiet --no-default-features)
|
||||||
|
|
||||||
(cd crates/eframe && cargo check --all-features)
|
(cd crates/eframe && cargo check --quiet --all-features)
|
||||||
(cd crates/egui && cargo check --all-features)
|
(cd crates/egui && cargo check --quiet --all-features)
|
||||||
(cd crates/egui_demo_app && cargo check --all-features)
|
(cd crates/egui_demo_app && cargo check --quiet --all-features)
|
||||||
(cd crates/egui_extras && cargo check --all-features)
|
(cd crates/egui_extras && cargo check --quiet --all-features)
|
||||||
(cd crates/egui_glow && cargo check --all-features)
|
(cd crates/egui_glow && cargo check --quiet --all-features)
|
||||||
(cd crates/egui-winit && cargo check --all-features)
|
(cd crates/egui-winit && cargo check --quiet --all-features)
|
||||||
(cd crates/emath && cargo check --all-features)
|
(cd crates/emath && cargo check --quiet --all-features)
|
||||||
(cd crates/epaint && cargo check --all-features)
|
(cd crates/epaint && cargo check --quiet --all-features)
|
||||||
|
|
||||||
./scripts/wasm_bindgen_check.sh
|
./scripts/wasm_bindgen_check.sh
|
||||||
|
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ set -x
|
|||||||
# Use scripts/clippy_wasm/clippy.toml
|
# Use scripts/clippy_wasm/clippy.toml
|
||||||
export CLIPPY_CONF_DIR="scripts/clippy_wasm"
|
export CLIPPY_CONF_DIR="scripts/clippy_wasm"
|
||||||
|
|
||||||
cargo cranky --all-features --target wasm32-unknown-unknown --target-dir target_wasm -p egui_demo_app --lib -- --deny warnings
|
cargo cranky --quiet --all-features --target wasm32-unknown-unknown --target-dir target_wasm -p egui_demo_app --lib -- --deny warnings
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ def get_github_token() -> str:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
print("ERROR: expected a GitHub token in the environment variable GH_ACCESS_TOKEN or in ~/.githubtoken")
|
print(
|
||||||
|
"ERROR: expected a GitHub token in the environment variable GH_ACCESS_TOKEN or in ~/.githubtoken"
|
||||||
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
@@ -99,7 +101,7 @@ def get_commit_info(commit: Any) -> CommitInfo:
|
|||||||
|
|
||||||
def remove_prefix(text, prefix):
|
def remove_prefix(text, prefix):
|
||||||
if text.startswith(prefix):
|
if text.startswith(prefix):
|
||||||
return text[len(prefix):]
|
return text[len(prefix) :]
|
||||||
return text # or whatever
|
return text # or whatever
|
||||||
|
|
||||||
|
|
||||||
@@ -110,6 +112,7 @@ def print_section(crate: str, items: List[str]) -> None:
|
|||||||
line = remove_prefix(line, f"[{crate}] ")
|
line = remove_prefix(line, f"[{crate}] ")
|
||||||
line = remove_prefix(line, f"{crate}: ")
|
line = remove_prefix(line, f"{crate}: ")
|
||||||
line = remove_prefix(line, f"`{crate}`: ")
|
line = remove_prefix(line, f"`{crate}`: ")
|
||||||
|
line = line[0].upper() + line[1:] # Upper-case first letter
|
||||||
print(f"* {line}")
|
print(f"* {line}")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
@@ -160,9 +163,9 @@ def main() -> None:
|
|||||||
title = pr_info.pr_title if pr_info else title
|
title = pr_info.pr_title if pr_info else title
|
||||||
labels = pr_info.labels if pr_info else []
|
labels = pr_info.labels if pr_info else []
|
||||||
|
|
||||||
if 'exclude from changelog' in labels:
|
if "exclude from changelog" in labels:
|
||||||
continue
|
continue
|
||||||
if 'typo' in labels:
|
if "typo" in labels:
|
||||||
# We get so many typo PRs. Let's not flood the changelog with them.
|
# We get so many typo PRs. Let's not flood the changelog with them.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user