1
0
mirror of https://github.com/emilk/egui.git synced 2026-06-27 07:03:14 -04:00

Merge branch 'master' of https://github.com/emilk/egui into multiples_viewports

This commit is contained in:
Konkitoman
2023-09-30 09:39:45 +03:00
86 changed files with 2288 additions and 1413 deletions

View File

@@ -29,4 +29,4 @@ jobs:
with:
mode: minimum
count: 1
labels: "CI, dependencies, docs and examples, ecolor, eframe, egui_extras, egui_glow, egui_plot, egui-wgpu, egui-winit, egui, epaint, typo"
labels: "CI, dependencies, docs and examples, ecolor, eframe, egui_extras, egui_glow, egui_plot, egui-wgpu, egui-winit, egui, epaint, exclude from changelog, typo"

View File

@@ -81,6 +81,9 @@ jobs:
- name: Cranky
run: cargo cranky --all-targets --all-features -- -D warnings
- name: Cranky release
run: cargo cranky --all-targets --all-features --release -- -D warnings
# ---------------------------------------------------------------------------
check_wasm:

View File

@@ -7,6 +7,91 @@ This file is updated upon each release.
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
## 0.23.0 - 2023-09-27 - New image API
This release contains a simple and powerful image API:
```rs
// Load from web:
ui.image("https://www.example.com/some_image.png");
// Include image in the binary using `include_bytes`:
ui.image(egui::include_image!("../assets/ferris.svg"));
// With options:
ui.add(
egui::Image::new("file://path/to/image.jpg")
.max_width(200.0)
.rounding(10.0),
);
```
The API is based on a plugin-system, where you can tell `egui` how to load the images, and from where.
`egui_extras` comes with loaders for you, so all you need to do is add the following to your `Cargo.toml`:
```toml
egui_extras = { version = "0.23", features = ["all_loaders"] }
image = { version = "0.24", features = ["jpeg", "png"] } # Add the types you want support for
```
And this to your code:
```rs
egui_extras::install_image_loaders(egui_ctx);
```
### ⚠️ BREAKING
* Update MSRV to Rust 1.70.0 [#3310](https://github.com/emilk/egui/pull/3310)
* Break out plotting to own crate `egui_plot` [#3282](https://github.com/emilk/egui/pull/3282)
### ⭐ Added
* A new image API [#3297](https://github.com/emilk/egui/pull/3297) [#3315](https://github.com/emilk/egui/pull/3315) [#3328](https://github.com/emilk/egui/pull/3328) [#3338](https://github.com/emilk/egui/pull/3338) [#3342](https://github.com/emilk/egui/pull/3342) [#3343](https://github.com/emilk/egui/pull/3343) [#3402](https://github.com/emilk/egui/pull/3402) (thanks [@jprochazk](https://github.com/jprochazk)!)
* Add option to truncate text at some width [#3244](https://github.com/emilk/egui/pull/3244)
* Add control of line height and letter spacing [#3302](https://github.com/emilk/egui/pull/3302)
* Support images with rounded corners [#3257](https://github.com/emilk/egui/pull/3257)
* Change focused widget with arrow keys [#3272](https://github.com/emilk/egui/pull/3272) (thanks [@TimonPost](https://github.com/TimonPost)!)
* Add opt-in `puffin` feature to egui [#3298](https://github.com/emilk/egui/pull/3298)
* Add debug-option to show a callstack to the widget under the mouse [#3391](https://github.com/emilk/egui/pull/3391)
* Add `Context::open_url` and `Context::copy_text` [#3380](https://github.com/emilk/egui/pull/3380)
* Add `Area::constrain_to` and `Window::constrain_to` [#3396](https://github.com/emilk/egui/pull/3396)
* Add `Memory::area_rect` [#3161](https://github.com/emilk/egui/pull/3161) (thanks [@tosti007](https://github.com/tosti007)!)
* Add `Margin::expand_rect` and `shrink_rect` [#3214](https://github.com/emilk/egui/pull/3214)
* Provide `into_inner()` for `egui::mutex::{Mutex, RwLock}` [#3110](https://github.com/emilk/egui/pull/3110) (thanks [@KmolYuan](https://github.com/KmolYuan)!)
* Support multi-threaded Wasm [#3236](https://github.com/emilk/egui/pull/3236)
* Change touch force to be `Option<f32>` instead of `f32` [#3240](https://github.com/emilk/egui/pull/3240) (thanks [@lucasmerlin](https://github.com/lucasmerlin)!)
* Add option to always open hyperlink in a new browser tab [#3242](https://github.com/emilk/egui/pull/3242) (thanks [@FreddyFunk](https://github.com/FreddyFunk)!)
* Add `Window::drag_to_scroll` [#3118](https://github.com/emilk/egui/pull/3118) (thanks [@KYovchevski](https://github.com/KYovchevski)!)
* Add `CollapsingState::remove` to clear stored state [#3252](https://github.com/emilk/egui/pull/3252) (thanks [@dmackdev](https://github.com/dmackdev)!)
* Add tooltip_delay option [#3245](https://github.com/emilk/egui/pull/3245) (thanks [@YgorSouza](https://github.com/YgorSouza)!)
* Added `Context::is_context_menu_open()` [#3267](https://github.com/emilk/egui/pull/3267) (thanks [@dmlary](https://github.com/dmlary)!)
* Add `mime` field to `DroppedFile` [#3273](https://github.com/emilk/egui/pull/3273) (thanks [@abey79](https://github.com/abey79)!)
* Allow setting the progress bar height [#3183](https://github.com/emilk/egui/pull/3183) (thanks [@s-nie](https://github.com/s-nie)!)
* Add `scroll_area::State::velocity` [#3300](https://github.com/emilk/egui/pull/3300) (thanks [@Barugon](https://github.com/Barugon)!)
* Add `Visuals::interact_cursor` [#3312](https://github.com/emilk/egui/pull/3312) (thanks [@zkldi](https://github.com/zkldi)!)
* Add method to `RichText` making it easier to construct layout jobs [#3319](https://github.com/emilk/egui/pull/3319) (thanks [@OmegaJak](https://github.com/OmegaJak)!)
* Add `Context::style_mut` [#3359](https://github.com/emilk/egui/pull/3359)
* `std::borrow::Cow<'_, str>` now implements `TextBuffer` [#3164](https://github.com/emilk/egui/pull/3164) (thanks [@burtonageo](https://github.com/burtonageo)!)
### 🔧 Changed
* Separate text cursor from selection visuals [#3181](https://github.com/emilk/egui/pull/3181) (thanks [@lampsitter](https://github.com/lampsitter)!)
* `DragValue`: update value on each key press by default [#2880](https://github.com/emilk/egui/pull/2880) (thanks [@Barugon](https://github.com/Barugon)!)
* Replace uses of `RangeInclusive<f32>` with `emath::Rangef` [#3221](https://github.com/emilk/egui/pull/3221)
* Implement `Send + Sync` for `ColorPickerFn` and `Ui` (#3148) [#3233](https://github.com/emilk/egui/pull/3233) (thanks [@idanarye](https://github.com/idanarye)!)
* Use the minus character instead of "dash" [#3271](https://github.com/emilk/egui/pull/3271)
* Changing `menu_image_button` to use `ImageButton` builder [#3288](https://github.com/emilk/egui/pull/3288) (thanks [@v-kat](https://github.com/v-kat)!)
* Prune old egui memory data when reaching some limit [#3299](https://github.com/emilk/egui/pull/3299)
### 🐛 Fixed
* Fix TextEdit's character limit [#3173](https://github.com/emilk/egui/pull/3173) (thanks [@Serverator](https://github.com/Serverator)!)
* Set the correct unicode character for "ctrl" shortcuts [#3186](https://github.com/emilk/egui/pull/3186) (thanks [@abey79](https://github.com/abey79)!)
* Fix crash in `DragValue` when only setting `min_decimals` [#3231](https://github.com/emilk/egui/pull/3231)
* Fix clipping issued with `ScrollArea` [#2860](https://github.com/emilk/egui/pull/2860) (thanks [@Barugon](https://github.com/Barugon)!)
* Fix moving slider with arrow keys [#3354](https://github.com/emilk/egui/pull/3354)
* Fix problems with tabs in text [#3355](https://github.com/emilk/egui/pull/3355)
* Fix interaction with moved color-picker [#3395](https://github.com/emilk/egui/pull/3395)
## 0.22.0 - 2023-05-23 - A plethora of small improvements
### ⭐ Added
* Scroll bar visibility options [#2729](https://github.com/emilk/egui/pull/2729) (thanks [@IVAN-MK7](https://github.com/IVAN-MK7)!)

1084
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -95,7 +95,6 @@ warn = [
"clippy::rest_pat_in_fully_bound_structs",
"clippy::same_functions_in_if_condition",
"clippy::semicolon_if_nothing_returned",
"clippy::significant_drop_tightening",
"clippy::single_match_else",
"clippy::str_to_string",
"clippy::string_add_assign",
@@ -138,6 +137,8 @@ warn = [
allow = [
"clippy::manual_range_contains", # this one is just worse imho
"clippy::significant_drop_tightening", # False positives
# TODO(emilk): enable more of these lints:
"clippy::let_underscore_untyped",
"clippy::missing_assert_message",

106
README.md
View File

@@ -9,30 +9,24 @@
[![Apache](https://img.shields.io/badge/license-Apache-blue.svg)](https://github.com/emilk/egui/blob/master/LICENSE-APACHE)
[![Discord](https://img.shields.io/discord/900275882684477440?label=egui%20discord)](https://discord.gg/JFcEma9bJq)
<div align="center">
<a href="https://www.rerun.io/"><img src="media/rerun_io_logo.png" width="250"></a>
egui development is sponsored by [Rerun](https://www.rerun.io/), a startup building<br>
an SDK for visualizing streams of multimodal data.
</div>
---
👉 [Click to run the web demo](https://www.egui.rs/#demo) 👈
egui (pronounced "e-gooey") is a simple, fast, and highly portable immediate mode GUI library for Rust. egui runs on the web, natively, and [in your favorite game engine](#integrations) (or will soon).
egui (pronounced "e-gooey") is a simple, fast, and highly portable immediate mode GUI library for Rust. egui runs on the web, natively, and [in your favorite game engine](#integrations).
egui aims to be the easiest-to-use Rust GUI library, and the simplest way to make a web app in Rust.
egui can be used anywhere you can draw textured triangles, which means you can easily integrate it into your game engine of choice.
Sections:
* [Example](#example)
* [Quick start](#quick-start)
* [Demo](#demo)
* [Goals](#goals)
* [Who is egui for?](#who-is-egui-for)
* [State / features](#state)
* [Integrations](#integrations)
* [Why immediate mode](#why-immediate-mode)
* [FAQ](#faq)
* [Other](#other)
* [Credits](#credits)
([egui 的中文翻译文档 / chinese translation](https://github.com/Re-Ch-Love/egui-doc-cn/blob/main/README_zh-hans.md))
## Example
``` rust
@@ -46,10 +40,29 @@ if ui.button("Click each year").clicked() {
age += 1;
}
ui.label(format!("Hello '{name}', age {age}"));
ui.image(egui::include_image!("ferris.png"));
```
<img src="media/demo.gif">
## Sections:
* [Example](#example)
* [Quick start](#quick-start)
* [Demo](#demo)
* [Goals](#goals)
* [State / features](#state)
* [Dependencies](#dependencies)
* [Who is egui for?](#who-is-egui-for)
* [Integrations](#integrations)
* [Why immediate mode](#why-immediate-mode)
* [FAQ](#faq)
* [Other](#other)
* [Credits](#credits)
([egui 的中文翻译文档 / chinese translation](https://github.com/Re-Ch-Love/egui-doc-cn/blob/main/README_zh-hans.md))
## Quick start
There are simple examples in [the `examples/` folder](https://github.com/emilk/egui/blob/master/examples/). If you want to write a web app, then go to <https://github.com/emilk/eframe_template/> and follow the instructions. The official docs are at <https://docs.rs/egui>. For inspiration and more examples, check out the [the egui web demo](https://www.egui.rs/#demo) and follow the links in it to its source code.
@@ -99,6 +112,32 @@ egui is *not* a framework. egui is a library you call into, not an environment y
* Native looking interface
* Advanced and flexible layouts (that's fundamentally incompatible with immediate mode)
## State
egui is in active development. It works well for what it does, but it lacks many features and the interfaces are still in flux. New releases will have breaking changes.
Still, egui can be used to create professional looking applications, like [the Rerun Viewer](https://app.rerun.io/).
### Features
* Widgets: label, text button, hyperlink, checkbox, radio button, slider, draggable value, text editing, combo box, color picker, spinner
* Images
* Layouts: horizontal, vertical, columns, automatic wrapping
* Text editing: multiline, copy/paste, undo, emoji supports
* Windows: move, resize, name, minimize and close. Automatically sized and positioned.
* Regions: resizing, vertical scrolling, collapsing headers (sections)
* Rendering: Anti-aliased rendering of lines, circles, text and convex polygons.
* Tooltips on hover
* Accessibility via [AccessKit](https://accesskit.dev/)
* More
<img src="media/widget_gallery_0.23.gif" width="50%">
Light Theme:
<img src="media/widget_gallery_0.23_light.png" width="50%">
## Dependencies
`egui` has a minimal set of default dependencies:
@@ -142,27 +181,6 @@ So in summary:
* egui: pure Rust, new, exciting, work in progress
* Dear ImGui: feature rich, well tested, cumbersome Rust integration
## State
egui is in active development. It works well for what it does, but it lacks many features and the interfaces are still in flux. New releases will have breaking changes.
### Features
* Widgets: label, text button, hyperlink, checkbox, radio button, slider, draggable value, text editing, combo box, color picker
* Layouts: horizontal, vertical, columns, automatic wrapping
* Text editing: multiline, copy/paste, undo, emoji supports
* Windows: move, resize, name, minimize and close. Automatically sized and positioned.
* Regions: resizing, vertical scrolling, collapsing headers (sections)
* Rendering: Anti-aliased rendering of lines, circles, text and convex polygons.
* Tooltips on hover
* More
<img src="media/widget_gallery.gif" width="50%">
Light Theme:
<img src="media/light_theme.png" width="50%">
## Integrations
egui is built to be easy to integrate into any existing game engine or platform you are working on.
@@ -255,7 +273,7 @@ For "atomic" widgets (e.g. a button) `egui` knows the size before showing it, so
#### CPU usage
Since an immediate mode GUI does a full layout each frame, the layout code needs to be quick. If you have a very complex GUI this can tax the CPU. In particular, having a very large UI in a scroll area (with very long scrollback) can be slow, as the content needs to be laid out each frame.
If you design the GUI with this in mind and refrain from huge scroll areas (or only lay out the part that is in view) then the performance hit is generally pretty small. For most cases you can expect `egui` to take up 1-2 ms per frame, but `egui` still has a lot of room for optimization (it's not something I've focused on yet). You can also set up `egui` to only repaint when there is interaction (e.g. mouse movement).
If you design the GUI with this in mind and refrain from huge scroll areas (or only lay out the part that is in view) then the performance hit is generally pretty small. For most cases you can expect `egui` to take up 1-2 ms per frame, but `egui` still has a lot of room for optimization (it's not something I've focused on yet). `egui` only repaints when there is interaction (e.g. mouse movement) or an animation, so if your app is idle, no CPU is wasted.
If your GUI is highly interactive, then immediate mode may actually be more performant compared to retained mode. Go to any web page and resize the browser window, and you'll notice that the browser is very slow to do the layout and eats a lot of CPU doing it. Resize a window in `egui` by contrast, and you'll get smooth 60 FPS at no extra CPU cost.
@@ -278,6 +296,8 @@ Yes! But you need to install your own font (`.ttf` or `.otf`) using `Context::se
### Can I customize the look of egui?
Yes! You can customize the colors, spacing, fonts and sizes of everything using `Context::set_style`.
This is not yet as powerful as say CSS, [but this is going to improve soon](https://github.com/emilk/egui/issues/3284).
Here is an example (from https://github.com/AlexxxRu/TinyPomodoro):
<img src="media/pompodoro-skin.png" width="50%">
@@ -286,7 +306,7 @@ Here is an example (from https://github.com/AlexxxRu/TinyPomodoro):
If you call `.await` in your GUI code, the UI will freeze, which is very bad UX. Instead, keep the GUI thread non-blocking and communicate with any concurrent tasks (`async` tasks or other threads) with something like:
* Channels (e.g. [`std::sync::mpsc::channel`](https://doc.rust-lang.org/std/sync/mpsc/fn.channel.html)). Make sure to use [`try_recv`](https://doc.rust-lang.org/std/sync/mpsc/struct.Receiver.html#method.try_recv) so you don't block the gui thread!
* `Arc<Mutex<Value>>` (background thread sets a value; GUI thread reads it)
* [`poll_promise::Promise`](https://docs.rs/poll-promise) (example: [`examples/download_image/`](https://github.com/emilk/egui/blob/master/examples/download_image/))
* [`poll_promise::Promise`](https://docs.rs/poll-promise)
* [`eventuals::Eventual`](https://docs.rs/eventuals/latest/eventuals/struct.Eventual.html)
* [`tokio::sync::watch::channel`](https://docs.rs/tokio/latest/tokio/sync/watch/fn.channel.html)
@@ -385,8 +405,8 @@ Default fonts:
---
<div align="center">
<img src="media/rerun_io_logo.png" width="50%">
<a href="https://www.rerun.io/"><img src="media/rerun_io_logo.png" width="440"></a>
egui development is sponsored by [Rerun](https://www.rerun.io/), a startup doing<br>
visualizations for computer vision and robotics.
egui development is sponsored by [Rerun](https://www.rerun.io/), a startup building<br>
an SDK for visualizing streams of multimodal data.
</div>

View File

@@ -1,6 +1,6 @@
[package]
name = "ecolor"
version = "0.22.0"
version = "0.23.0"
authors = [
"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>",
"Andreas Reich <reichandreas@gmx.de>",

View File

@@ -6,6 +6,39 @@ NOTE: [`egui-winit`](../egui-winit/CHANGELOG.md), [`egui_glium`](../egui_glium/C
This file is updated upon each release.
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
## 0.23.0 - 2023-09-27
* 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 `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)!)
* Improve documentation of `eframe`, especially for wasm32 [#3295](https://github.com/emilk/egui/pull/3295)
* `eframe::Frame::info` returns a reference [#3301](https://github.com/emilk/egui/pull/3301) (thanks [@Barugon](https://github.com/Barugon)!)
* Move `App::persist_window` to `NativeOptions` and `App::max_size_points` to `WebOptions` [#3397](https://github.com/emilk/egui/pull/3397)
#### Desktop/Native:
* Only show on-screen-keyboard and IME when editing text [#3362](https://github.com/emilk/egui/pull/3362) (thanks [@Barugon](https://github.com/Barugon)!)
* Add `eframe::storage_dir` [#3286](https://github.com/emilk/egui/pull/3286)
* Add `NativeOptions::window_builder` for more customization [#3390](https://github.com/emilk/egui/pull/3390) (thanks [@twop](https://github.com/twop)!)
* Better restore Window position on Mac when on secondary monitor [#3239](https://github.com/emilk/egui/pull/3239)
* Fix iOS support in `eframe` [#3241](https://github.com/emilk/egui/pull/3241) (thanks [@lucasmerlin](https://github.com/lucasmerlin)!)
* Speed up `eframe` state storage [#3353](https://github.com/emilk/egui/pull/3353) (thanks [@sebbert](https://github.com/sebbert)!)
* Allow users to opt-out of default `winit` features [#3228](https://github.com/emilk/egui/pull/3228)
* Expose Raw Window and Display Handles [#3073](https://github.com/emilk/egui/pull/3073) (thanks [@bash](https://github.com/bash)!)
* Use window title as fallback when app_id is not set [#3107](https://github.com/emilk/egui/pull/3107) (thanks [@jacekpoz](https://github.com/jacekpoz)!)
* Sleep a bit only when minimized [#3139](https://github.com/emilk/egui/pull/3139) (thanks [@icedrocket](https://github.com/icedrocket)!)
* Prevent text from being cleared when selected due to winit IME [#3376](https://github.com/emilk/egui/pull/3376) (thanks [@YgorSouza](https://github.com/YgorSouza)!)
* Fix android app quit on resume with glow backend [#3080](https://github.com/emilk/egui/pull/3080) (thanks [@tkkcc](https://github.com/tkkcc)!)
* Fix panic with persistence without window [#3167](https://github.com/emilk/egui/pull/3167) (thanks [@sagebind](https://github.com/sagebind)!)
* Only call `run_return` twice on Windows [#3053](https://github.com/emilk/egui/pull/3053) (thanks [@pan93412](https://github.com/pan93412)!)
* Gracefully catch error saving state to disk [#3230](https://github.com/emilk/egui/pull/3230)
* Recognize numpad enter/plus/minus [#3285](https://github.com/emilk/egui/pull/3285)
* Add more puffin profile scopes to `eframe` [#3330](https://github.com/emilk/egui/pull/3330) [#3332](https://github.com/emilk/egui/pull/3332)
#### Web:
* Update to wasm-bindgen 0.2.87 [#3237](https://github.com/emilk/egui/pull/3237)
* Remove `Function()` invocation from eframe text_agent to bypass "unsafe-eval" restrictions in Chrome browser extensions. [#3349](https://github.com/emilk/egui/pull/3349) (thanks [@aspect](https://github.com/aspect)!)
* Fix docs about web [#3026](https://github.com/emilk/egui/pull/3026) (thanks [@kerryeon](https://github.com/kerryeon)!)
## 0.22.0 - 2023-05-23
* Fix: `request_repaint_after` works even when called from background thread [#2939](https://github.com/emilk/egui/pull/2939)

View File

@@ -1,6 +1,6 @@
[package]
name = "eframe"
version = "0.22.0"
version = "0.23.0"
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
description = "egui framework - write GUI apps that compiles to web and/or natively"
edition = "2021"
@@ -39,6 +39,16 @@ default = [
## Enable platform accessibility API implementations through [AccessKit](https://accesskit.dev/).
accesskit = ["egui/accesskit", "egui-winit/accesskit"]
# Allow crates to choose an android-activity backend via Winit
# - It's important that most applications should not have to depend on android-activity directly, and can
# rely on Winit to pull in a suitable version (unlike most Rust crates, any version conflicts won't link)
# - It's also important that we don't impose an android-activity backend by taking this choice away from applications.
## Enable the `game-activity` backend via `egui-winit` on Android
android-game-activity = ["egui-winit/android-game-activity"]
## Enable the `native-activity` backend via `egui-winit` on Android
android-native-activity = ["egui-winit/android-native-activity"]
## If set, egui will use `include_bytes!` to bundle some fonts.
## If you plan on specifying your own fonts you may disable this feature.
default_fonts = ["egui/default_fonts"]
@@ -46,12 +56,6 @@ default_fonts = ["egui/default_fonts"]
## Use [`glow`](https://github.com/grovesNL/glow) for painting, via [`egui_glow`](https://github.com/emilk/egui/tree/master/crates/egui_glow).
glow = ["dep:glow", "dep:egui_glow", "dep:glutin", "dep:glutin-winit"]
## Enables wayland support and fixes clipboard issue.
wayland = ["egui-winit/wayland"]
## Enables compiling for x11.
x11 = ["egui-winit/x11"]
## Enable saving app state to disk.
persistence = [
"directories-next",
@@ -63,35 +67,32 @@ persistence = [
## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate.
##
## Only enabled on native, because of the low resolution (1ms) of clocks in browsers.
## `eframe` will call `puffin::GlobalProfiler::lock().new_frame()` for you
##
## Only enabled on native, because of the low resolution (1ms) of clocks in browsers.
puffin = ["dep:puffin", "egui/puffin", "egui_glow?/puffin", "egui-wgpu?/puffin"]
## Enables wayland support and fixes clipboard issue.
wayland = ["egui-winit/wayland"]
## Enable screen reader support (requires `ctx.options_mut(|o| o.screen_reader = true);`) on web.
##
## For other platforms, use the "accesskit" feature instead.
## For other platforms, use the `accesskit` feature instead.
web_screen_reader = ["tts"]
## If set, eframe will look for the env-var `EFRAME_SCREENSHOT_TO` and write a screenshot to that location, and then quit.
## This is used to generate images for the examples.
__screenshot = []
## Use [`wgpu`](https://docs.rs/wgpu) for painting (via [`egui-wgpu`](https://github.com/emilk/egui/tree/master/crates/egui-wgpu)).
## This overrides the `glow` feature.
wgpu = ["dep:wgpu", "dep:egui-wgpu", "dep:pollster", "dep:raw-window-handle"]
# Allow crates to choose an android-activity backend via Winit
# - It's important that most applications should not have to depend on android-activity directly, and can
# rely on Winit to pull in a suitable version (unlike most Rust crates, any version conflicts won't link)
# - It's also important that we don't impose an android-activity backend by taking this choice away from applications.
## Enables compiling for x11.
x11 = ["egui-winit/x11"]
## Enable the `native-activity` backend via `egui-winit` on Android
android-native-activity = ["egui-winit/android-native-activity"]
## Enable the `game-activity` backend via `egui-winit` on Android
android-game-activity = ["egui-winit/android-game-activity"]
## If set, eframe will look for the env-var `EFRAME_SCREENSHOT_TO` and write a screenshot to that location, and then quit.
## This is used to generate images for examples.
__screenshot = []
[dependencies]
egui = { version = "0.22.0", path = "../egui", default-features = false, features = [
egui = { version = "0.23.0", path = "../egui", default-features = false, features = [
"bytemuck",
"log",
] }
@@ -104,7 +105,7 @@ thiserror.workspace = true
## Enable this when generating docs.
document-features = { version = "0.2", optional = true }
egui_glow = { version = "0.22.0", path = "../egui_glow", optional = true, default-features = false }
egui_glow = { version = "0.23.0", path = "../egui_glow", optional = true, default-features = false }
glow = { version = "0.12", optional = true }
ron = { version = "0.8", optional = true, features = ["integer128"] }
serde = { version = "1", optional = true, features = ["derive"] }
@@ -112,7 +113,7 @@ serde = { version = "1", optional = true, features = ["derive"] }
# -------------------------------------------
# native:
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
egui-winit = { version = "0.22.0", path = "../egui-winit", default-features = false, features = [
egui-winit = { version = "0.23.0", path = "../egui-winit", default-features = false, features = [
"clipboard",
"links",
] }
@@ -124,7 +125,7 @@ winit = { version = "0.28.1", default-features = false }
# optional native:
directories-next = { version = "2", optional = true }
egui-wgpu = { version = "0.22.0", path = "../egui-wgpu", optional = true, features = [
egui-wgpu = { version = "0.23.0", path = "../egui-wgpu", optional = true, features = [
"winit",
] } # if wgpu is used, use it with winit
pollster = { version = "0.3", optional = true } # needed for wgpu
@@ -138,7 +139,7 @@ wgpu = { workspace = true, optional = true }
# mac:
[target.'cfg(any(target_os = "macos"))'.dependencies]
cocoa = "0.24.1"
cocoa = "0.24.1" # Stuck on old version until we update to winit 0.29
objc = "0.2.7"
# windows:
@@ -198,7 +199,7 @@ web-sys = { version = "0.3.58", features = [
] }
# optional web:
egui-wgpu = { version = "0.22.0", path = "../egui-wgpu", optional = true } # if wgpu is used, use it without (!) winit
egui-wgpu = { version = "0.23.0", path = "../egui-wgpu", optional = true } # if wgpu is used, use it without (!) winit
raw-window-handle = { version = "0.5.2", optional = true }
tts = { version = "0.25", optional = true, default-features = false }
wgpu = { workspace = true, optional = true }

View File

@@ -28,6 +28,7 @@ You need to either use `edition = "2021"`, or set `resolver = "2"` in the `[work
You can opt-in to the using [`egui_wgpu`](https://github.com/emilk/egui/tree/master/crates/egui_wgpu) for rendering by enabling the `wgpu` feature and setting `NativeOptions::renderer` to `Renderer::Wgpu`.
To get copy-paste working on web, you need to compile with `export RUSTFLAGS=--cfg=web_sys_unstable_apis`.
## Alternatives
`eframe` is not the only way to write an app using `egui`! You can also try [`egui-miniquad`](https://github.com/not-fl3/egui-miniquad), [`bevy_egui`](https://github.com/mvlabat/bevy_egui), [`egui_sdl2_gl`](https://github.com/ArjunNair/egui_sdl2_gl), and others.

View File

@@ -14,6 +14,15 @@ pub struct IconData {
pub height: u32,
}
impl std::fmt::Debug for IconData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("IconData")
.field("width", &self.width)
.field("height", &self.height)
.finish_non_exhaustive()
}
}
impl IconData {
/// Convert into [`image::RgbaImage`]
///

View File

@@ -9,6 +9,7 @@
#[cfg(not(target_arch = "wasm32"))]
mod icon_data;
use egui::ViewportBuilder;
#[cfg(not(target_arch = "wasm32"))]
pub use icon_data::IconData;
@@ -28,7 +29,7 @@ use static_assertions::assert_not_impl_any;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(feature = "glow", feature = "wgpu"))]
pub use winit::event_loop::EventLoopBuilder;
pub use winit::{event_loop::EventLoopBuilder, window::WindowBuilder};
/// Hook into the building of an event loop before it is run
///
@@ -38,6 +39,14 @@ pub use winit::event_loop::EventLoopBuilder;
#[cfg(any(feature = "glow", feature = "wgpu"))]
pub type EventLoopBuilderHook = Box<dyn FnOnce(&mut EventLoopBuilder<UserEvent>)>;
/// Hook into the building of a the native window.
///
/// You can configure any platform specific details required on top of the default configuration
/// done by `eframe`.
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(feature = "glow", feature = "wgpu"))]
pub type WindowBuilderHook = Box<dyn FnOnce(ViewportBuilder) -> ViewportBuilder>;
/// This is how your app is created.
///
/// You can use the [`CreationContext`] to setup egui, restore state, setup OpenGL things, etc.
@@ -182,13 +191,6 @@ pub trait App {
std::time::Duration::from_secs(30)
}
/// The size limit of the web app canvas.
///
/// By default the max size is [`egui::Vec2::INFINITY`], i.e. unlimited.
fn max_size_points(&self) -> egui::Vec2 {
egui::Vec2::INFINITY
}
/// Background color values for the app, e.g. what is sent to `gl.clearColor`.
///
/// This is the background of your windows if you don't set a central panel.
@@ -208,12 +210,6 @@ pub trait App {
// _visuals.window_fill() would also be a natural choice
}
/// Controls whether or not the native window position and size will be
/// persisted (only if the "persistence" feature is enabled).
fn persist_native_window(&self) -> bool {
true
}
/// Controls whether or not the egui memory (window positions etc) will be
/// persisted (only if the "persistence" feature is enabled).
fn persist_egui_memory(&self) -> bool {
@@ -313,6 +309,7 @@ pub struct NativeOptions {
pub resizable: bool,
/// On desktop: make the window transparent.
///
/// You control the transparency with [`App::clear_color()`].
/// You should avoid having a [`egui::CentralPanel`], or make sure its frame is also transparent.
pub transparent: bool,
@@ -397,6 +394,15 @@ pub struct NativeOptions {
#[cfg(any(feature = "glow", feature = "wgpu"))]
pub event_loop_builder: Option<EventLoopBuilderHook>,
/// Hook into the building of a window.
///
/// Specify a callback here in case you need to make platform specific changes to the
/// window appearance.
///
/// Note: A [`NativeOptions`] clone will not include any `window_builder` hook.
#[cfg(any(feature = "glow", feature = "wgpu"))]
pub window_builder: Option<WindowBuilderHook>,
#[cfg(feature = "glow")]
/// Needed for cross compiling for VirtualBox VMSVGA driver with OpenGL ES 2.0 and OpenGL 2.1 which doesn't support SRGB texture.
/// See <https://github.com/emilk/egui/pull/1993>.
@@ -455,6 +461,10 @@ pub struct NativeOptions {
/// }
/// ```
pub app_id: Option<String>,
/// Controls whether or not the native window position and size will be
/// persisted (only if the "persistence" feature is enabled).
pub persist_window: bool,
}
#[cfg(not(target_arch = "wasm32"))]
@@ -466,6 +476,9 @@ impl Clone for NativeOptions {
#[cfg(any(feature = "glow", feature = "wgpu"))]
event_loop_builder: None, // Skip any builder callbacks if cloning
#[cfg(any(feature = "glow", feature = "wgpu"))]
window_builder: None, // Skip any builder callbacks if cloning
#[cfg(feature = "wgpu")]
wgpu_options: self.wgpu_options.clone(),
@@ -520,6 +533,9 @@ impl Default for NativeOptions {
#[cfg(any(feature = "glow", feature = "wgpu"))]
event_loop_builder: None,
#[cfg(any(feature = "glow", feature = "wgpu"))]
window_builder: None,
#[cfg(feature = "glow")]
shader_version: None,
@@ -529,6 +545,8 @@ impl Default for NativeOptions {
wgpu_options: egui_wgpu::WgpuConfiguration::default(),
app_id: None,
persist_window: true,
}
}
}
@@ -566,6 +584,11 @@ pub struct WebOptions {
/// Configures wgpu instance/device/adapter/surface creation and renderloop.
#[cfg(feature = "wgpu")]
pub wgpu_options: egui_wgpu::WgpuConfiguration,
/// The size limit of the web app canvas.
///
/// By default the max size is [`egui::Vec2::INFINITY`], i.e. unlimited.
pub max_size_points: egui::Vec2,
}
#[cfg(target_arch = "wasm32")]
@@ -581,6 +604,8 @@ impl Default for WebOptions {
#[cfg(feature = "wgpu")]
wgpu_options: egui_wgpu::WgpuConfiguration::default(),
max_size_points: egui::Vec2::INFINITY,
}
}
}
@@ -1164,7 +1189,8 @@ pub fn get_value<T: serde::de::DeserializeOwned>(storage: &dyn Storage, key: &st
.and_then(|value| match ron::from_str(&value) {
Ok(value) => Some(value),
Err(err) => {
log::warn!("Failed to decode RON: {err}");
// This happens on when we break the format, e.g. when updating egui.
log::debug!("Failed to decode RON: {err}");
None
}
})

View File

@@ -280,7 +280,7 @@ pub fn run_simple_native(
update_fun: U,
}
impl<U: FnMut(&egui::Context, &mut Frame)> App for SimpleApp<U> {
impl<U: FnMut(&egui::Context, &mut Frame) + 'static> App for SimpleApp<U> {
fn update(&mut self, ctx: &egui::Context, frame: &mut Frame) {
(self.update_fun)(ctx, frame);
}

View File

@@ -70,7 +70,7 @@ pub fn read_window_info(
pub fn window_builder<E>(
event_loop: &EventLoopWindowTarget<E>,
title: &str,
native_options: &epi::NativeOptions,
native_options: &mut epi::NativeOptions,
window_settings: Option<WindowSettings>,
) -> ViewportBuilder {
let epi::NativeOptions {
@@ -173,7 +173,10 @@ pub fn window_builder<E>(
}
}
window_builder
match std::mem::take(&mut native_options.window_builder) {
Some(hook) => hook(window_builder),
None => window_builder,
}
}
pub fn apply_native_options_to_window(
@@ -326,6 +329,8 @@ pub struct EpiIntegration {
can_drag_window: bool,
window_state: WindowState,
follow_system_theme: bool,
#[cfg(feature = "persistence")]
persist_window: bool,
app_icon_setter: super::app_icon::AppTitleIconSetter,
}
@@ -388,6 +393,8 @@ impl EpiIntegration {
can_drag_window: false,
window_state,
follow_system_theme: native_options.follow_system_theme,
#[cfg(feature = "persistence")]
persist_window: native_options.persist_window,
app_icon_setter,
beginning: Instant::now(),
}
@@ -578,7 +585,7 @@ impl EpiIntegration {
crate::profile_function!();
if let Some(window) = _window {
if _app.persist_native_window() {
if self.persist_window {
crate::profile_scope!("native_window");
epi::set_value(
storage,

View File

@@ -130,6 +130,7 @@ fn save_to_disk(file_path: &PathBuf, kv: &HashMap<String, String>) {
let mut writer = std::io::BufWriter::new(file);
let config = Default::default();
crate::profile_scope!("ron::serialize");
if let Err(err) = ron::ser::to_writer_pretty(&mut writer, &kv, config)
.and_then(|_| writer.flush().map_err(|err| err.into()))
{

View File

@@ -895,7 +895,7 @@ mod glow_integration {
event_loop: &EventLoopWindowTarget<UserEvent>,
storage: Option<&dyn epi::Storage>,
title: &str,
native_options: &NativeOptions,
native_options: &mut NativeOptions,
) -> Result<(GlutinWindowContext, glow::Context)> {
crate::profile_function!();
@@ -945,7 +945,7 @@ mod glow_integration {
event_loop,
storage.as_deref(),
&self.app_name,
&self.native_options,
&mut self.native_options,
)?;
let gl = Arc::new(gl);
@@ -1944,7 +1944,7 @@ mod wgpu_integration {
event_loop: &EventLoopWindowTarget<UserEvent>,
storage: Option<&dyn epi::Storage>,
title: &str,
native_options: &NativeOptions,
native_options: &mut NativeOptions,
) -> std::result::Result<(winit::window::Window, ViewportBuilder), winit::error::OsError>
{
crate::profile_function!();
@@ -2556,7 +2556,7 @@ mod wgpu_integration {
event_loop,
running.integration.read().frame.storage(),
&self.app_name,
&self.native_options,
&mut self.native_options,
)?;
self.set_window(ViewportId::MAIN)?;
}
@@ -2571,7 +2571,7 @@ mod wgpu_integration {
event_loop,
storage.as_deref(),
&self.app_name,
&self.native_options,
&mut self.native_options,
)?;
self.init_run_state(event_loop, storage, window, builder)?;
}

View File

@@ -7,6 +7,7 @@ use crate::{epi, App};
use super::{now_sec, web_painter::WebPainter, NeedRepaint};
pub struct AppRunner {
web_options: crate::WebOptions,
pub(crate) frame: epi::Frame,
egui_ctx: egui::Context,
painter: super::ActiveWebPainter,
@@ -99,6 +100,7 @@ impl AppRunner {
}
let mut runner = Self {
web_options,
frame,
egui_ctx,
painter,
@@ -175,7 +177,7 @@ impl AppRunner {
pub fn logic(&mut self) -> (std::time::Duration, Vec<egui::ClippedPrimitive>) {
let frame_start = now_sec();
super::resize_canvas_to_screen_size(self.canvas_id(), self.app.max_size_points());
super::resize_canvas_to_screen_size(self.canvas_id(), self.web_options.max_size_points);
let canvas_size = super::canvas_size_in_points(self.canvas_id());
let raw_input = self.input.new_frame(canvas_size);

View File

@@ -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.
## 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)!)
* Improved wgpu callbacks [#3253](https://github.com/emilk/egui/pull/3253) (thanks [@Wumpf](https://github.com/Wumpf)!)
* Fix depth texture init with multisampling [#3207](https://github.com/emilk/egui/pull/3207) (thanks [@mauliu](https://github.com/mauliu)!)
* Fix panic on wgpu GL backend due to new screenshot capability [#3078](https://github.com/emilk/egui/pull/3078) (thanks [@amfaber](https://github.com/amfaber)!)
## 0.22.0 - 2023-05-23
* Update to wgpu 0.16 [#2884](https://github.com/emilk/egui/pull/2884) (thanks [@niklaskorz](https://github.com/niklaskorz)!)
* Device configuration is now dependent on adapter [#2951](https://github.com/emilk/egui/pull/2951) (thanks [@Wumpf](https://github.com/Wumpf)!)

View File

@@ -1,6 +1,6 @@
[package]
name = "egui-wgpu"
version = "0.22.0"
version = "0.23.0"
description = "Bindings for using egui natively using the wgpu library"
authors = [
"Nils Hasenbanck <nils@hasenbanck.de>",
@@ -36,8 +36,8 @@ winit = ["dep:winit"]
[dependencies]
egui = { version = "0.22.0", path = "../egui", default-features = false }
epaint = { version = "0.22.0", path = "../epaint", default-features = false, features = [
egui = { version = "0.23.0", path = "../egui", default-features = false }
epaint = { version = "0.23.0", path = "../epaint", default-features = false, features = [
"bytemuck",
] }

View File

@@ -123,6 +123,16 @@ pub struct WgpuConfiguration {
pub on_surface_error: Arc<dyn Fn(wgpu::SurfaceError) -> SurfaceErrorAction>,
}
impl std::fmt::Debug for WgpuConfiguration {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("WgpuConfiguration")
.field("supported_backends", &self.supported_backends)
.field("present_mode", &self.present_mode)
.field("power_preference", &self.power_preference)
.finish_non_exhaustive()
}
}
impl Default for WgpuConfiguration {
fn default() -> Self {
Self {

View File

@@ -5,6 +5,13 @@ This file is updated upon each release.
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
## 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)!)
* Replace `instant` with `web_time` [#3296](https://github.com/emilk/egui/pull/3296)
* Allow users to opt-out of default `winit` features [#3228](https://github.com/emilk/egui/pull/3228)
* Recognize numpad enter/plus/minus [#3285](https://github.com/emilk/egui/pull/3285)
## 0.22.0 - 2023-05-23
* Only use `wasm-bindgen` feature for `instant` when building for wasm32 [#2808](https://github.com/emilk/egui/pull/2808) (thanks [@gferon](https://github.com/gferon)!)
* Fix unsafe API of `Clipboard::new` [#2765](https://github.com/emilk/egui/pull/2765) (thanks [@dhardy](https://github.com/dhardy)!)

View File

@@ -1,6 +1,6 @@
[package]
name = "egui-winit"
version = "0.22.0"
version = "0.23.0"
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
description = "Bindings for using egui with winit"
edition = "2021"
@@ -23,6 +23,15 @@ default = ["clipboard", "links", "wayland", "winit/default", "x11"]
## Enable platform accessibility API implementations through [AccessKit](https://accesskit.dev/).
accesskit = ["accesskit_winit", "egui/accesskit"]
# Allow crates to choose an android-activity backend via Winit
# - It's important that most applications should not have to depend on android-activity directly, and can
# rely on Winit to pull in a suitable version (unlike most Rust crates, any version conflicts won't link)
# - It's also important that we don't impose an android-activity backend by taking this choice away from applications.
## Enable the `game-activity` backend via Winit on Android
android-game-activity = ["winit/android-game-activity"]
## Enable the `native-activity` backend via Winit on Android
android-native-activity = ["winit/android-native-activity"]
## [`bytemuck`](https://docs.rs/bytemuck) enables you to cast [`egui::epaint::Vertex`], [`egui::Vec2`] etc to `&[u8]`.
bytemuck = ["egui/bytemuck"]
@@ -45,18 +54,8 @@ wayland = ["winit/wayland"]
## Enables compiling for x11.
x11 = ["winit/x11"]
# Allow crates to choose an android-activity backend via Winit
# - It's important that most applications should not have to depend on android-activity directly, and can
# rely on Winit to pull in a suitable version (unlike most Rust crates, any version conflicts won't link)
# - It's also important that we don't impose an android-activity backend by taking this choice away from applications.
## Enable the `native-activity` backend via Winit on Android
android-native-activity = ["winit/android-native-activity"]
## Enable the `game-activity` backend via Winit on Android
android-game-activity = ["winit/android-game-activity"]
[dependencies]
egui = { version = "0.22.0", path = "../egui", default-features = false, features = [
egui = { version = "0.23.0", path = "../egui", default-features = false, features = [
"log",
] }
log = { version = "0.4", features = ["std"] }

View File

@@ -1,6 +1,6 @@
[package]
name = "egui"
version = "0.22.0"
version = "0.23.0"
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
description = "An easy-to-use immediate mode GUI that runs on both web and native"
edition = "2021"
@@ -22,9 +22,18 @@ all-features = true
[features]
default = ["default_fonts"]
## Exposes detailed accessibility implementation required by platform
## accessibility APIs. Also requires support in the egui integration.
accesskit = ["dep:accesskit"]
## [`bytemuck`](https://docs.rs/bytemuck) enables you to cast [`epaint::Vertex`], [`emath::Vec2`] etc to `&[u8]`.
bytemuck = ["epaint/bytemuck"]
## Show a debug-ui on hover including the stacktrace to the hovered item.
## This is very useful in finding the code that creates a part of the UI.
## Does not work on web.
callstack = ["dep:backtrace"]
## [`cint`](https://docs.rs/cint) enables interoperability with other color libraries.
cint = ["epaint/cint"]
@@ -67,7 +76,7 @@ unity = ["epaint/unity"]
[dependencies]
epaint = { version = "0.22.0", path = "../epaint", default-features = false }
epaint = { version = "0.23.0", path = "../epaint", default-features = false }
ahash = { version = "0.8.1", default-features = false, features = [
"no-rng", # we don't need DOS-protection, so we let users opt-in to it instead
@@ -76,10 +85,10 @@ ahash = { version = "0.8.1", default-features = false, features = [
nohash-hasher = "0.2"
#! ### Optional dependencies
## Exposes detailed accessibility implementation required by platform
## accessibility APIs. Also requires support in the egui integration.
accesskit = { version = "0.11", optional = true }
backtrace = { version = "0.3", optional = true }
## Enable this when generating docs.
document-features = { version = "0.2", optional = true }

View File

@@ -0,0 +1,186 @@
#[derive(Clone)]
struct Frame {
/// `_main` is usually as the deepest depth.
depth: usize,
name: String,
file_and_line: String,
}
/// Capture a callstack, skipping the frames that are not interesting.
///
/// In particular: slips everything before `egui::Context::run`,
/// and skipping all frames in the `egui::` namespace.
pub fn capture() -> String {
let mut frames = vec![];
let mut depth = 0;
backtrace::trace(|frame| {
// Resolve this instruction pointer to a symbol name
backtrace::resolve_frame(frame, |symbol| {
let mut file_and_line = symbol.filename().map(shorten_source_file_path);
if let Some(file_and_line) = &mut file_and_line {
if let Some(line_nr) = symbol.lineno() {
file_and_line.push_str(&format!(":{line_nr}"));
}
}
let file_and_line = file_and_line.unwrap_or_default();
let name = symbol
.name()
.map(|name| name.to_string())
.unwrap_or_default();
frames.push(Frame {
depth,
name,
file_and_line,
});
});
depth += 1; // note: we can resolve multiple symbols on the same frame.
true // keep going to the next frame
});
if frames.is_empty() {
return Default::default();
}
// Inclusive:
let mut min_depth = 0;
let mut max_depth = frames.len() - 1;
for frame in &frames {
if frame.name.starts_with("egui::callstack::capture") {
min_depth = frame.depth + 1;
}
if frame.name.starts_with("egui::context::Context::run") {
max_depth = frame.depth;
}
}
// Remove frames that are uninteresting:
frames.retain(|frame| {
// Keep some special frames to give the user a sense of chronology:
if frame.name == "main"
|| frame.name == "_main"
|| frame.name.starts_with("egui::context::Context::run")
|| frame.name.starts_with("eframe::run_native")
{
return true;
}
if frame.depth < min_depth || max_depth < frame.depth {
return false;
}
// Remove stuff that isn't user calls:
let skip_prefixes = [
// "backtrace::", // not needed, since we cut at at egui::callstack::capture
"egui::",
"<egui::",
"<F as egui::widgets::Widget>",
"egui_plot::",
"egui_extras::",
"core::ptr::drop_in_place<egui::ui::Ui>::",
"eframe::",
"core::ops::function::FnOnce::call_once",
"<alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once",
];
for prefix in skip_prefixes {
if frame.name.starts_with(prefix) {
return false;
}
}
true
});
frames.reverse(); // main on top, i.e. chronological order. Same as Python.
let mut deepest_depth = 0;
let mut widest_file_line = 0;
for frame in &frames {
deepest_depth = frame.depth.max(deepest_depth);
widest_file_line = frame.file_and_line.len().max(widest_file_line);
}
let widest_depth = deepest_depth.to_string().len();
let mut formatted = String::new();
if !frames.is_empty() {
let mut last_depth = frames[0].depth;
for frame in &frames {
let Frame {
depth,
name,
file_and_line,
} = frame;
if frame.depth + 1 < last_depth || last_depth + 1 < frame.depth {
// Show that some frames were elided
formatted.push_str(&format!("{:widest_depth$}\n", ""));
}
formatted.push_str(&format!(
"{depth:widest_depth$}: {file_and_line:widest_file_line$} {name}\n"
));
last_depth = frame.depth;
}
}
formatted
}
/// Shorten a path to a Rust source file from a callstack.
///
/// Example input:
/// * `/Users/emilk/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.24.1/src/runtime/runtime.rs`
/// * `crates/rerun/src/main.rs`
/// * `/rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/ops/function.rs`
fn shorten_source_file_path(path: &std::path::Path) -> String {
// Look for `src` and strip everything up to it.
let components: Vec<_> = path.iter().map(|path| path.to_string_lossy()).collect();
let mut src_idx = None;
for (i, c) in components.iter().enumerate() {
if c == "src" {
src_idx = Some(i);
}
}
// Look for the last `src`:
if let Some(src_idx) = src_idx {
// Before `src` comes the name of the crate - let's include that:
let first_index = src_idx.saturating_sub(1);
let mut output = components[first_index].to_string();
for component in &components[first_index + 1..] {
output.push('/');
output.push_str(component);
}
output
} else {
// No `src` directory found - weird!
path.display().to_string()
}
}
#[test]
fn test_shorten_path() {
for (before, after) in [
("/Users/emilk/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.24.1/src/runtime/runtime.rs", "tokio-1.24.1/src/runtime/runtime.rs"),
("crates/rerun/src/main.rs", "rerun/src/main.rs"),
("/rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/ops/function.rs", "core/src/ops/function.rs"),
("/weird/path/file.rs", "/weird/path/file.rs"),
]
{
use std::str::FromStr as _;
let before = std::path::PathBuf::from_str(before).unwrap();
assert_eq!(shorten_source_file_path(&before), after);
}
}

View File

@@ -65,12 +65,12 @@ pub struct Area {
interactable: bool,
enabled: bool,
constrain: bool,
constrain_rect: Option<Rect>,
order: Order,
default_pos: Option<Pos2>,
pivot: Align2,
anchor: Option<(Align2, Vec2)>,
new_pos: Option<Pos2>,
drag_bounds: Option<Rect>,
}
impl Area {
@@ -80,13 +80,13 @@ impl Area {
movable: true,
interactable: true,
constrain: false,
constrain_rect: None,
enabled: true,
order: Order::Middle,
default_pos: None,
new_pos: None,
pivot: Align2::LEFT_TOP,
anchor: None,
drag_bounds: None,
}
}
@@ -155,6 +155,21 @@ impl Area {
self
}
/// Constraint the movement of the window to the given rectangle.
///
/// For instance: `.constrain_to(ctx.screen_rect())`.
pub fn constrain_to(mut self, constrain_rect: Rect) -> Self {
self.constrain = true;
self.constrain_rect = Some(constrain_rect);
self
}
#[deprecated = "Use `constrain_to` instead"]
pub fn drag_bounds(mut self, constrain_rect: Rect) -> Self {
self.constrain_rect = Some(constrain_rect);
self
}
/// Where the "root" of the area is.
///
/// For instance, if you set this to [`Align2::RIGHT_TOP`]
@@ -189,12 +204,6 @@ impl Area {
self.movable(false)
}
/// Constrain the area up to which the window can be dragged.
pub fn drag_bounds(mut self, bounds: Rect) -> Self {
self.drag_bounds = Some(bounds);
self
}
pub(crate) fn get_pivot(&self) -> Align2 {
if let Some((pivot, _)) = self.anchor {
pivot
@@ -209,7 +218,8 @@ pub(crate) struct Prepared {
state: State,
move_response: Response,
enabled: bool,
drag_bounds: Option<Rect>,
constrain: bool,
constrain_rect: Option<Rect>,
/// We always make windows invisible the first frame to hide "first-frame-jitters".
///
@@ -243,8 +253,8 @@ impl Area {
new_pos,
pivot,
anchor,
drag_bounds,
constrain,
constrain_rect,
} = self;
let layer_id = LayerId::new(order, id);
@@ -271,7 +281,7 @@ impl Area {
}
// interact right away to prevent frame-delay
let move_response = {
let mut move_response = {
let interact_id = layer_id.id.with("move");
let sense = if movable {
Sense::click_and_drag()
@@ -291,16 +301,8 @@ impl Area {
enabled,
);
// Important check - don't try to move e.g. a combobox popup!
if movable {
if move_response.dragged() {
state.pivot_pos += ctx.input(|i| i.pointer.delta());
}
state.set_left_top_pos(
ctx.constrain_window_rect_to_area(state.rect(), drag_bounds)
.min,
);
if movable && move_response.dragged() {
state.pivot_pos += ctx.input(|i| i.pointer.delta());
}
if (move_response.dragged() || move_response.clicked())
@@ -314,21 +316,25 @@ impl Area {
move_response
};
state.set_left_top_pos(ctx.round_pos_to_pixels(state.left_top_pos()));
if constrain {
state.set_left_top_pos(
ctx.constrain_window_rect_to_area(state.rect(), drag_bounds)
.left_top(),
ctx.constrain_window_rect_to_area(state.rect(), constrain_rect)
.min,
);
}
state.set_left_top_pos(ctx.round_pos_to_pixels(state.left_top_pos()));
// Update responsbe with posisbly moved/constrained rect:
move_response = move_response.with_new_rect(state.rect());
Prepared {
layer_id,
state,
move_response,
enabled,
drag_bounds,
constrain,
constrain_rect,
temporarily_invisible: is_new,
}
}
@@ -371,15 +377,19 @@ impl Prepared {
&mut self.state
}
pub(crate) fn drag_bounds(&self) -> Option<Rect> {
self.drag_bounds
pub(crate) fn constrain(&self) -> bool {
self.constrain
}
pub(crate) fn constrain_rect(&self) -> Option<Rect> {
self.constrain_rect
}
pub(crate) fn content_ui(&self, ctx: &Context) -> Ui {
let screen_rect = ctx.screen_rect();
let bounds = if let Some(bounds) = self.drag_bounds {
bounds.intersect(screen_rect) // protect against infinite bounds
let constrain_rect = if let Some(constrain_rect) = self.constrain_rect {
constrain_rect.intersect(screen_rect) // protect against infinite bounds
} else {
let central_area = ctx.available_rect();
@@ -393,7 +403,7 @@ impl Prepared {
let max_rect = Rect::from_min_max(
self.state.left_top_pos(),
bounds
constrain_rect
.max
.at_least(self.state.left_top_pos() + Vec2::splat(32.0)),
);
@@ -401,9 +411,9 @@ impl Prepared {
let shadow_radius = ctx.style().visuals.window_shadow.extrusion; // hacky
let clip_rect_margin = ctx.style().visuals.clip_rect_margin.max(shadow_radius);
let clip_rect = Rect::from_min_max(self.state.left_top_pos(), bounds.max)
let clip_rect = Rect::from_min_max(self.state.left_top_pos(), constrain_rect.max)
.expand(clip_rect_margin)
.intersect(bounds);
.intersect(constrain_rect);
let mut ui = Ui::new(
ctx.clone(),
@@ -424,7 +434,8 @@ impl Prepared {
mut state,
move_response,
enabled: _,
drag_bounds: _,
constrain: _,
constrain_rect: _,
temporarily_invisible: _,
} = self;

View File

@@ -260,9 +260,8 @@ fn show_tooltip_area_dyn<'c, R>(
Area::new(area_id)
.order(Order::Tooltip)
.fixed_pos(window_pos)
.constrain(true)
.constrain_to(ctx.screen_rect())
.interactable(false)
.drag_bounds(ctx.screen_rect())
.show(ctx, |ui| {
Frame::popup(&ctx.style())
.show(ui, |ui| {

View File

@@ -314,6 +314,7 @@ impl Resize {
state.store(ui.ctx(), id);
#[cfg(debug_assertions)]
if ui.ctx().style().debug.show_resize {
ui.ctx().debug_painter().debug_rect(
Rect::from_min_size(content_ui.min_rect().left_top(), state.desired_size),

View File

@@ -43,7 +43,7 @@ impl<'open> Window<'open> {
/// If you need a changing title, you must call `window.id(…)` with a fixed id.
pub fn new(title: impl Into<WidgetText>) -> Self {
let title = title.into().fallback_text_style(TextStyle::Heading);
let area = Area::new(Id::new(title.text()));
let area = Area::new(Id::new(title.text())).constrain(true);
Self {
title,
open: None,
@@ -146,11 +146,31 @@ impl<'open> Window<'open> {
}
/// Constrains this window to the screen bounds.
///
/// To change the area to constrain to, use [`Self::constraint_to`].
///
/// Default: `true`.
pub fn constrain(mut self, constrain: bool) -> Self {
self.area = self.area.constrain(constrain);
self
}
/// Constraint the movement of the window to the given rectangle.
///
/// For instance: `.constrain_to(ctx.screen_rect())`.
pub fn constraint_to(mut self, constrain_rect: Rect) -> Self {
self.area = self.area.constrain_to(constrain_rect);
self
}
#[deprecated = "Use `constrain_to` instead"]
pub fn drag_bounds(mut self, constrain_rect: Rect) -> Self {
#![allow(deprecated)]
self.area = self.area.drag_bounds(constrain_rect);
self
}
/// Where the "root" of the window is.
///
/// For instance, if you set this to [`Align2::RIGHT_TOP`]
@@ -276,12 +296,6 @@ impl<'open> Window<'open> {
self.scroll = self.scroll.drag_to_scroll(drag_to_scroll);
self
}
/// Constrain the area up to which the window can be dragged.
pub fn drag_bounds(mut self, bounds: Rect) -> Self {
self.area = self.area.drag_bounds(bounds);
self
}
}
impl<'open> Window<'open> {
@@ -452,13 +466,6 @@ impl<'open> Window<'open> {
content_inner
};
{
let pos = ctx
.constrain_window_rect_to_area(area.state().rect(), area.drag_bounds())
.left_top();
area.state_mut().set_left_top_pos(pos);
}
let full_response = area.end(ctx, area_content_ui);
let inner_response = InnerResponse {
@@ -562,9 +569,11 @@ fn interact(
resize_id: Id,
) -> Option<WindowInteraction> {
let new_rect = move_and_resize_window(ctx, &window_interaction)?;
let new_rect = ctx.round_rect_to_pixels(new_rect);
let mut new_rect = ctx.round_rect_to_pixels(new_rect);
let new_rect = ctx.constrain_window_rect_to_area(new_rect, area.drag_bounds());
if area.constrain() {
new_rect = ctx.constrain_window_rect_to_area(new_rect, area.constrain_rect());
}
// TODO(emilk): add this to a Window state instead as a command "move here next frame"
area.state_mut().set_left_top_pos(new_rect.left_top());

View File

@@ -830,6 +830,7 @@ impl Context {
// This solves the problem of overlapping widgets.
// Whichever widget is added LAST (=on top) gets the input:
if interact_rect.is_positive() && sense.interactive() {
#[cfg(debug_assertions)]
if self.style().debug.show_interactive_widgets {
Self::layer_painter(self, LayerId::debug()).rect(
interact_rect,
@@ -838,6 +839,8 @@ impl Context {
Stroke::new(1.0, Color32::YELLOW.additive().linear_multiply(0.05)),
);
}
#[cfg(debug_assertions)]
let mut show_blocking_widget = None;
self.write(|ctx| {
@@ -858,6 +861,7 @@ impl Context {
// Another interactive widget is covering us at the pointer position,
// so we aren't hovered.
#[cfg(debug_assertions)]
if ctx.memory.options.style.debug.show_blocking_widget {
// Store the rects to use them outside the write() call to
// avoid deadlock
@@ -873,6 +877,7 @@ impl Context {
}
});
#[cfg(debug_assertions)]
if let Some((interact_rect, prev_rect)) = show_blocking_widget {
Self::layer_painter(self, LayerId::debug()).debug_rect(
interact_rect,
@@ -1892,15 +1897,15 @@ impl Context {
// ---------------------------------------------------------------------
/// Whether or not to debug widget layout on hover.
#[cfg(debug_assertions)]
pub fn debug_on_hover(&self) -> bool {
self.options(|opt| opt.style.debug.debug_on_hover)
}
/// Turn on/off whether or not to debug widget layout on hover.
#[cfg(debug_assertions)]
pub fn set_debug_on_hover(&self, debug_on_hover: bool) {
let mut style = self.options(|opt| (*opt.style).clone());
style.debug.debug_on_hover = debug_on_hover;
self.set_style(style);
self.style_mut(|style| style.debug.debug_on_hover = debug_on_hover);
}
}
@@ -1991,7 +1996,6 @@ impl Context {
/// Show the state of egui, including its input and output.
pub fn inspection_ui(&self, ui: &mut Ui) {
use crate::containers::*;
crate::trace!(ui);
ui.label(format!("Is using pointer: {}", self.is_using_pointer()))
.on_hover_text(

View File

@@ -479,6 +479,11 @@ impl Modifiers {
!self.is_none()
}
#[inline]
pub fn all(&self) -> bool {
self.alt && self.ctrl && self.shift && self.command
}
/// Is shift the only pressed button?
#[inline]
pub fn shift_only(&self) -> bool {

View File

@@ -54,6 +54,9 @@ pub(crate) struct FrameState {
/// Highlight these widgets the next frame. Write to this.
pub(crate) highlight_next_frame: IdSet,
#[cfg(debug_assertions)]
pub(crate) has_debug_viewed_this_frame: bool,
}
impl Default for FrameState {
@@ -70,6 +73,9 @@ impl Default for FrameState {
accesskit_state: None,
highlight_this_frame: Default::default(),
highlight_next_frame: Default::default(),
#[cfg(debug_assertions)]
has_debug_viewed_this_frame: false,
}
}
}
@@ -89,6 +95,9 @@ impl FrameState {
accesskit_state,
highlight_this_frame,
highlight_next_frame,
#[cfg(debug_assertions)]
has_debug_viewed_this_frame,
} = self;
used_ids.clear();
@@ -99,6 +108,11 @@ impl FrameState {
*scroll_delta = input.scroll_delta;
*scroll_target = [None, None];
#[cfg(debug_assertions)]
{
*has_debug_viewed_this_frame = false;
}
#[cfg(feature = "accesskit")]
{
*accesskit_state = None;

View File

@@ -187,24 +187,27 @@ impl GridLayout {
}
pub(crate) fn advance(&mut self, cursor: &mut Rect, _frame_rect: Rect, widget_rect: Rect) {
let debug_expand_width = self.style.debug.show_expand_width;
let debug_expand_height = self.style.debug.show_expand_height;
if debug_expand_width || debug_expand_height {
let rect = widget_rect;
let too_wide = rect.width() > self.prev_col_width(self.col);
let too_high = rect.height() > self.prev_row_height(self.row);
#[cfg(debug_assertions)]
{
let debug_expand_width = self.style.debug.show_expand_width;
let debug_expand_height = self.style.debug.show_expand_height;
if debug_expand_width || debug_expand_height {
let rect = widget_rect;
let too_wide = rect.width() > self.prev_col_width(self.col);
let too_high = rect.height() > self.prev_row_height(self.row);
if (debug_expand_width && too_wide) || (debug_expand_height && too_high) {
let painter = self.ctx.debug_painter();
painter.rect_stroke(rect, 0.0, (1.0, Color32::LIGHT_BLUE));
if (debug_expand_width && too_wide) || (debug_expand_height && too_high) {
let painter = self.ctx.debug_painter();
painter.rect_stroke(rect, 0.0, (1.0, Color32::LIGHT_BLUE));
let stroke = Stroke::new(2.5, Color32::from_rgb(200, 0, 0));
let paint_line_seg = |a, b| painter.line_segment([a, b], stroke);
let stroke = Stroke::new(2.5, Color32::from_rgb(200, 0, 0));
let paint_line_seg = |a, b| painter.line_segment([a, b], stroke);
if debug_expand_width && too_wide {
paint_line_seg(rect.left_top(), rect.left_bottom());
paint_line_seg(rect.left_center(), rect.right_center());
paint_line_seg(rect.right_top(), rect.right_bottom());
if debug_expand_width && too_wide {
paint_line_seg(rect.left_top(), rect.left_bottom());
paint_line_seg(rect.left_center(), rect.right_center());
paint_line_seg(rect.right_top(), rect.right_bottom());
}
}
}
}

View File

@@ -801,6 +801,7 @@ impl Layout {
/// ## Debug stuff
impl Layout {
/// Shows where the next widget is going to be placed
#[cfg(debug_assertions)]
pub(crate) fn paint_text_at_cursor(
&self,
painter: &crate::Painter,

View File

@@ -12,6 +12,10 @@
//! Then you add a [`Window`] or a [`SidePanel`] to get a [`Ui`], which is what you'll be using to add all the buttons and labels that you need.
//!
//!
//! ## Feature flags
#![cfg_attr(feature = "document-features", doc = document_features::document_features!())]
//!
//!
//! # Using egui
//!
//! To see what is possible to build with egui you can check out the online demo at <https://www.egui.rs/#demo>.
@@ -163,7 +167,14 @@
//!
//! # Understanding immediate mode
//!
//! `egui` is an immediate mode GUI library. It is useful to fully grok what "immediate mode" implies.
//! `egui` is an immediate mode GUI library.
//!
//! Immediate mode has its roots in gaming, where everything on the screen is painted at the
//! display refresh rate, i.e. at 60+ frames per second.
//! In immediate mode GUIs, the entire interface is laid out and painted at the same high rate.
//! This makes immediate mode GUIs especially well suited for highly interactive applications.
//!
//! It is useful to fully grok what "immediate mode" implies.
//!
//! Here is an example to illustrate it:
//!
@@ -198,7 +209,7 @@
//! # });
//! ```
//!
//! Here egui will read `value` to display the slider, then look if the mouse is dragging the slider and if so change the `value`.
//! Here egui will read `value` (an `f32`) to display the slider, then look if the mouse is dragging the slider and if so change the `value`.
//! Note that `egui` does not store the slider value for you - it only displays the current value, and changes it
//! by how much the slider has been dragged in the previous few milliseconds.
//! This means it is responsibility of the egui user to store the state (`value`) so that it persists between frames.
@@ -318,10 +329,6 @@
//! }); // the temporary settings are reverted here
//! # });
//! ```
//!
//! ## Feature flags
#![cfg_attr(feature = "document-features", doc = document_features::document_features!())]
//!
#![allow(clippy::float_cmp)]
#![allow(clippy::manual_range_contains)]
@@ -354,6 +361,10 @@ pub mod viewport;
pub mod widget_text;
pub mod widgets;
#[cfg(feature = "callstack")]
#[cfg(debug_assertions)]
mod callstack;
#[cfg(feature = "accesskit")]
pub use accesskit;
@@ -439,7 +450,8 @@ pub fn warn_if_debug_build(ui: &mut crate::Ui) {
/// ui.image(egui::include_image!("../assets/ferris.png"));
/// ui.add(
/// egui::Image::new(egui::include_image!("../assets/ferris.png"))
/// .rounding(egui::Rounding::same(6.0))
/// .max_width(200.0)
/// .rounding(10.0),
/// );
///
/// let image_source: egui::ImageSource = egui::include_image!("../assets/ferris.png");
@@ -449,10 +461,10 @@ pub fn warn_if_debug_build(ui: &mut crate::Ui) {
#[macro_export]
macro_rules! include_image {
($path: literal) => {
$crate::ImageSource::Bytes(
::std::borrow::Cow::Borrowed(concat!("bytes://", $path)), // uri
$crate::load::Bytes::Static(include_bytes!($path)),
)
$crate::ImageSource::Bytes {
uri: ::std::borrow::Cow::Borrowed(concat!("bytes://", $path)),
bytes: $crate::load::Bytes::Static(include_bytes!($path)),
}
};
}
@@ -488,32 +500,6 @@ macro_rules! github_link_file {
// ----------------------------------------------------------------------------
/// Show debug info on hover when [`Context::set_debug_on_hover`] has been turned on.
///
/// ```
/// # egui::__run_test_ui(|ui| {
/// // Turn on tracing of widgets
/// ui.ctx().set_debug_on_hover(true);
///
/// /// Show [`std::file`], [`std::line`] and argument on hover
/// egui::trace!(ui, "MyWindow");
///
/// /// Show [`std::file`] and [`std::line`] on hover
/// egui::trace!(ui);
/// # });
/// ```
#[macro_export]
macro_rules! trace {
($ui: expr) => {{
$ui.trace_location(format!("{}:{}", file!(), line!()))
}};
($ui: expr, $label: expr) => {{
$ui.trace_location(format!("{} - {}:{}", $label, file!(), line!()))
}};
}
// ----------------------------------------------------------------------------
/// An assert that is only active when `egui` is compiled with the `extra_asserts` feature
/// or with the `extra_debug_asserts` feature in debug builds.
#[macro_export]

View File

@@ -4,7 +4,7 @@
//! will get you up and running quickly with its reasonable default implementations of the traits described below.
//!
//! 1. Add [`egui_extras`](https://crates.io/crates/egui_extras/) as a dependency with the `all_loaders` feature.
//! 2. Add a call to [`egui_extras::install_image_loaders`](https://docs.rs/egui_extras/latest/egui_extras/loaders/fn.install.html)
//! 2. Add a call to [`egui_extras::install_image_loaders`](https://docs.rs/egui_extras/latest/egui_extras/fn.install_image_loaders.html)
//! in your app's setup code.
//! 3. Use [`Ui::image`][`crate::ui::Ui::image`] with some [`ImageSource`][`crate::ImageSource`].
//!
@@ -55,17 +55,20 @@
mod bytes_loader;
mod texture_loader;
use crate::Context;
use std::borrow::Cow;
use std::fmt::Debug;
use std::ops::Deref;
use std::{error::Error as StdError, fmt::Display, sync::Arc};
use ahash::HashMap;
use epaint::mutex::Mutex;
use epaint::util::FloatOrd;
use epaint::util::OrderedFloat;
use epaint::TextureHandle;
use epaint::{textures::TextureOptions, ColorImage, TextureId, Vec2};
use std::borrow::Cow;
use std::fmt::Debug;
use std::ops::Deref;
use std::{error::Error as StdError, fmt::Display, sync::Arc};
use crate::Context;
pub use self::bytes_loader::DefaultBytesLoader;
pub use self::texture_loader::DefaultTextureLoader;
@@ -76,15 +79,15 @@ pub enum LoadError {
/// Programmer error: There are no image loaders installed.
NoImageLoaders,
/// A specific loader does not support this schema, protocol or image format.
/// A specific loader does not support this scheme, protocol or image format.
NotSupported,
/// Programmer error: Failed to find the bytes for this image because
/// there was no [`BytesLoader`] supporting the schema.
/// there was no [`BytesLoader`] supporting the scheme.
NoMatchingBytesLoader,
/// Programmer error: Failed to parse the bytes as an image because
/// there was no [`ImageLoader`] supporting the schema.
/// there was no [`ImageLoader`] supporting the scheme.
NoMatchingImageLoader,
/// Programmer error: no matching [`TextureLoader`].
@@ -108,7 +111,7 @@ impl Display for LoadError {
Self::NoMatchingTextureLoader => f.write_str("No matching TextureLoader. Did you remove the default one?"),
Self::NotSupported => f.write_str("Iagge schema or URI not supported by this loader"),
Self::NotSupported => f.write_str("Image scheme or URI not supported by this loader"),
Self::Loading(message) => f.write_str(message),
}

View File

@@ -146,10 +146,9 @@ pub(crate) fn menu_ui<'c, R>(
let area = Area::new(menu_id)
.order(Order::Foreground)
.constrain(true)
.fixed_pos(pos)
.interactable(true)
.drag_bounds(ctx.screen_rect());
.constrain_to(ctx.screen_rect())
.interactable(true);
area.show(ctx, |ui| {
set_menu_style(ui.style_mut());
@@ -348,8 +347,7 @@ impl MenuRoot {
if let Some(root) = root.inner.as_mut() {
if root.id == id {
// pressed somewhere while this menu is open
let menu_state = root.menu_state.read();
let in_menu = menu_state.area_contains(pos);
let in_menu = root.menu_state.read().area_contains(pos);
if !in_menu {
return MenuResponse::Close;
}
@@ -374,8 +372,7 @@ impl MenuRoot {
let mut destroy = false;
let mut in_old_menu = false;
if let Some(root) = root {
let menu_state = root.menu_state.read();
in_old_menu = menu_state.area_contains(pos);
in_old_menu = root.menu_state.read().area_contains(pos);
destroy = root.id == response.id;
}
if !in_old_menu {

View File

@@ -263,6 +263,7 @@ impl Placer {
}
impl Placer {
#[cfg(debug_assertions)]
pub(crate) fn debug_paint_cursor(&self, painter: &crate::Painter, text: impl ToString) {
let stroke = Stroke::new(1.0, Color32::DEBUG_COLOR);

View File

@@ -201,6 +201,9 @@ pub struct Style {
pub animation_time: f32,
/// Options to help debug why egui behaves strangely.
///
/// Only available in debug builds.
#[cfg(debug_assertions)]
pub debug: DebugOptions,
/// Show tooltips explaining [`DragValue`]:s etc when hovered.
@@ -690,12 +693,36 @@ impl WidgetVisuals {
}
/// Options for help debug egui by adding extra visualization
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg(debug_assertions)]
pub struct DebugOptions {
/// However over widgets to see their rectangles
/// Always show callstack to ui on hover.
///
/// Useful for figuring out where in the code some UI is being created.
///
/// Only works in debug builds.
/// Requires the `callstack` feature.
/// Does not work on web.
#[cfg(debug_assertions)]
pub debug_on_hover: bool,
/// Show callstack for the current widget on hover if all modifier keys are pressed down.
///
/// Useful for figuring out where in the code some UI is being created.
///
/// Only works in debug builds.
/// Requires the `callstack` feature.
/// Does not work on web.
///
/// Default is `true` in debug builds, on native, if the `callstack` feature is enabled.
#[cfg(debug_assertions)]
pub debug_on_hover_with_all_modifiers: bool,
/// If we show the hover ui, include where the next widget is placed.
#[cfg(debug_assertions)]
pub hover_shows_next: bool,
/// Show which widgets make their parent wider
pub show_expand_width: bool,
@@ -711,6 +738,23 @@ pub struct DebugOptions {
pub show_blocking_widget: bool,
}
#[cfg(debug_assertions)]
impl Default for DebugOptions {
fn default() -> Self {
Self {
debug_on_hover: false,
debug_on_hover_with_all_modifiers: cfg!(feature = "callstack")
&& !cfg!(target_arch = "wasm32"),
hover_shows_next: false,
show_expand_width: false,
show_expand_height: false,
show_resize: false,
show_interactive_widgets: false,
show_blocking_widget: false,
}
}
}
// ----------------------------------------------------------------------------
/// The default text styles of the default egui theme.
@@ -739,6 +783,7 @@ impl Default for Style {
interaction: Interaction::default(),
visuals: Visuals::default(),
animation_time: 1.0 / 12.0,
#[cfg(debug_assertions)]
debug: Default::default(),
explanation_tooltips: false,
}
@@ -993,6 +1038,7 @@ impl Style {
interaction,
visuals,
animation_time,
#[cfg(debug_assertions)]
debug,
explanation_tooltips,
} = self;
@@ -1055,6 +1101,8 @@ impl Style {
ui.collapsing("📏 Spacing", |ui| spacing.ui(ui));
ui.collapsing("☝ Interaction", |ui| interaction.ui(ui));
ui.collapsing("🎨 Visuals", |ui| visuals.ui(ui));
#[cfg(debug_assertions)]
ui.collapsing("🐛 Debug", |ui| debug.ui(ui));
ui.checkbox(explanation_tooltips, "Explanation tooltips")
@@ -1477,10 +1525,13 @@ impl Visuals {
}
}
#[cfg(debug_assertions)]
impl DebugOptions {
pub fn ui(&mut self, ui: &mut crate::Ui) {
let Self {
debug_on_hover,
debug_on_hover_with_all_modifiers,
hover_shows_next,
show_expand_width,
show_expand_height,
show_resize,
@@ -1488,7 +1539,16 @@ impl DebugOptions {
show_blocking_widget,
} = self;
ui.checkbox(debug_on_hover, "Show debug info on hover");
{
ui.checkbox(debug_on_hover, "Show widget info on hover.");
ui.checkbox(
debug_on_hover_with_all_modifiers,
"Show widget info on hover if holding all modifier keys",
);
ui.checkbox(hover_shows_next, "Show next widget placement on hover");
}
ui.checkbox(
show_expand_width,
"Show which widgets make their parent wider",

View File

@@ -738,43 +738,41 @@ impl Ui {
/// # });
/// ```
pub fn allocate_space(&mut self, desired_size: Vec2) -> (Id, Rect) {
// For debug rendering
#[cfg(debug_assertions)]
let original_available = self.available_size_before_wrap();
let too_wide = desired_size.x > original_available.x;
let too_high = desired_size.y > original_available.y;
let rect = self.allocate_space_impl(desired_size);
if self.style().debug.debug_on_hover && self.rect_contains_pointer(rect) {
let painter = self.ctx().debug_painter();
painter.rect_stroke(rect, 4.0, (1.0, Color32::LIGHT_BLUE));
self.placer.debug_paint_cursor(&painter, "next");
}
#[cfg(debug_assertions)]
{
let too_wide = desired_size.x > original_available.x;
let too_high = desired_size.y > original_available.y;
let debug_expand_width = self.style().debug.show_expand_width;
let debug_expand_height = self.style().debug.show_expand_height;
let debug_expand_width = self.style().debug.show_expand_width;
let debug_expand_height = self.style().debug.show_expand_height;
if (debug_expand_width && too_wide) || (debug_expand_height && too_high) {
self.painter
.rect_stroke(rect, 0.0, (1.0, Color32::LIGHT_BLUE));
if (debug_expand_width && too_wide) || (debug_expand_height && too_high) {
self.painter
.rect_stroke(rect, 0.0, (1.0, Color32::LIGHT_BLUE));
let stroke = Stroke::new(2.5, Color32::from_rgb(200, 0, 0));
let paint_line_seg = |a, b| self.painter().line_segment([a, b], stroke);
let stroke = Stroke::new(2.5, Color32::from_rgb(200, 0, 0));
let paint_line_seg = |a, b| self.painter().line_segment([a, b], stroke);
if debug_expand_width && too_wide {
paint_line_seg(rect.left_top(), rect.left_bottom());
paint_line_seg(rect.left_center(), rect.right_center());
paint_line_seg(
pos2(rect.left() + original_available.x, rect.top()),
pos2(rect.left() + original_available.x, rect.bottom()),
);
paint_line_seg(rect.right_top(), rect.right_bottom());
}
if debug_expand_width && too_wide {
paint_line_seg(rect.left_top(), rect.left_bottom());
paint_line_seg(rect.left_center(), rect.right_center());
paint_line_seg(
pos2(rect.left() + original_available.x, rect.top()),
pos2(rect.left() + original_available.x, rect.bottom()),
);
paint_line_seg(rect.right_top(), rect.right_bottom());
}
if debug_expand_height && too_high {
paint_line_seg(rect.left_top(), rect.right_top());
paint_line_seg(rect.center_top(), rect.center_bottom());
paint_line_seg(rect.left_bottom(), rect.right_bottom());
if debug_expand_height && too_high {
paint_line_seg(rect.left_top(), rect.right_top());
paint_line_seg(rect.center_top(), rect.center_bottom());
paint_line_seg(rect.left_bottom(), rect.right_bottom());
}
}
}
@@ -795,6 +793,8 @@ impl Ui {
self.placer
.advance_after_rects(frame_rect, widget_rect, item_spacing);
register_rect(self, widget_rect);
widget_rect
}
@@ -803,6 +803,7 @@ impl Ui {
/// Ignore the layout of the [`Ui`]: just put my widget here!
/// The layout cursor will advance to past this `rect`.
pub fn allocate_rect(&mut self, rect: Rect, sense: Sense) -> Response {
register_rect(self, rect);
let id = self.advance_cursor_after_rect(rect);
self.interact(rect, id, sense)
}
@@ -813,12 +814,6 @@ impl Ui {
let item_spacing = self.spacing().item_spacing;
self.placer.advance_after_rects(rect, rect, item_spacing);
if self.style().debug.debug_on_hover && self.rect_contains_pointer(rect) {
let painter = self.ctx().debug_painter();
painter.rect_stroke(rect, 4.0, (1.0, Color32::LIGHT_BLUE));
self.placer.debug_paint_cursor(&painter, "next");
}
let id = Id::new(self.next_auto_id_source);
self.next_auto_id_source = self.next_auto_id_source.wrapping_add(1);
id
@@ -896,13 +891,6 @@ impl Ui {
self.placer
.advance_after_rects(final_child_rect, final_child_rect, item_spacing);
if self.style().debug.debug_on_hover && self.rect_contains_pointer(final_child_rect) {
let painter = self.ctx().debug_painter();
painter.rect_stroke(frame_rect, 4.0, (1.0, Color32::LIGHT_BLUE));
painter.rect_stroke(final_child_rect, 4.0, (1.0, Color32::LIGHT_BLUE));
self.placer.debug_paint_cursor(&painter, "next");
}
let response = self.interact(final_child_rect, child_ui.id, Sense::hover());
InnerResponse::new(ret, response)
}
@@ -1561,7 +1549,7 @@ impl Ui {
/// Show an image available at the given `uri`.
///
/// ⚠ This will do nothing unless you install some image loaders first!
/// The easiest way to do this is via [`egui_extras::install_image_loaders`](https://docs.rs/egui_extras/latest/egui_extras/loaders/fn.install.html).
/// The easiest way to do this is via [`egui_extras::install_image_loaders`](https://docs.rs/egui_extras/latest/egui_extras/fn.install_image_loaders.html).
///
/// The loaders handle caching image data, sampled textures, etc. across frames, so calling this is immediate-mode safe.
///
@@ -1572,7 +1560,8 @@ impl Ui {
/// ui.image(egui::include_image!("../assets/ferris.png"));
/// ui.add(
/// egui::Image::new(egui::include_image!("../assets/ferris.png"))
/// .rounding(egui::Rounding::same(6.0))
/// .max_width(200.0)
/// .rounding(10.0),
/// );
/// # });
/// ```
@@ -1793,10 +1782,7 @@ impl Ui {
let mut child_rect = self.placer.available_rect_before_wrap();
child_rect.min.x += indent;
let mut child_ui = Self {
id: self.id.with(id_source),
..self.child_ui(child_rect, *self.layout())
};
let mut child_ui = self.child_ui_with_id_source(child_rect, *self.layout(), id_source);
let ret = add_contents(&mut child_ui);
let left_vline = self.visuals().indent_has_left_vline;
@@ -2024,12 +2010,6 @@ impl Ui {
let item_spacing = self.spacing().item_spacing;
self.placer.advance_after_rects(rect, rect, item_spacing);
if self.style().debug.debug_on_hover && self.rect_contains_pointer(rect) {
let painter = self.ctx().debug_painter();
painter.rect_stroke(rect, 4.0, (1.0, Color32::LIGHT_BLUE));
self.placer.debug_paint_cursor(&painter, "next");
}
InnerResponse::new(inner, self.interact(rect, child_ui.id, Sense::hover()))
}
@@ -2215,21 +2195,121 @@ impl Ui {
/// # Debug stuff
impl Ui {
/// Shows where the next widget is going to be placed
#[cfg(debug_assertions)]
pub fn debug_paint_cursor(&self) {
self.placer.debug_paint_cursor(&self.painter, "next");
}
}
/// Shows the given text where the next widget is to be placed
/// if when [`Context::set_debug_on_hover`] has been turned on and the mouse is hovering the Ui.
pub fn trace_location(&self, text: impl ToString) {
let rect = self.max_rect();
if self.style().debug.debug_on_hover && self.rect_contains_pointer(rect) {
self.placer
.debug_paint_cursor(&self.ctx().debug_painter(), text);
#[cfg(debug_assertions)]
impl Drop for Ui {
fn drop(&mut self) {
register_rect(self, self.min_rect());
}
}
/// Show this rectangle to the user if certain debug options are set.
#[cfg(debug_assertions)]
fn register_rect(ui: &Ui, rect: Rect) {
let debug = ui.style().debug;
let show_callstacks = debug.debug_on_hover
|| debug.debug_on_hover_with_all_modifiers && ui.input(|i| i.modifiers.all());
if !show_callstacks {
return;
}
if ui.ctx().frame_state(|o| o.has_debug_viewed_this_frame) {
return;
}
if !ui.rect_contains_pointer(rect) {
return;
}
// We only show one debug rectangle, or things get confusing:
ui.ctx()
.frame_state_mut(|o| o.has_debug_viewed_this_frame = true);
// ----------------------------------------------
let is_clicking = ui.input(|i| i.pointer.could_any_button_be_click());
// Use the debug-painter to avoid clip rect,
// otherwise the content of the widget may cover what we paint here!
let painter = ui.ctx().debug_painter();
// Paint rectangle around widget:
{
let rect_fg_color = if is_clicking {
Color32::WHITE
} else {
Color32::LIGHT_BLUE
};
let rect_bg_color = Color32::BLUE.gamma_multiply(0.5);
painter.rect(rect, 0.0, rect_bg_color, (1.0, rect_fg_color));
}
// ----------------------------------------------
if debug.hover_shows_next {
ui.placer.debug_paint_cursor(&painter, "next");
}
// ----------------------------------------------
#[cfg(feature = "callstack")]
let callstack = crate::callstack::capture();
#[cfg(not(feature = "callstack"))]
let callstack = String::default();
if !callstack.is_empty() {
let font_id = FontId::monospace(12.0);
let text = format!("{callstack}\n\n(click to copy)");
let galley = painter.layout_no_wrap(text, font_id, Color32::WHITE);
// Position the text either under or above:
let screen_rect = ui.ctx().screen_rect();
let y = if galley.size().y <= rect.top() {
// Above
rect.top() - galley.size().y
} else {
// Below
rect.bottom()
};
let y = y
.at_most(screen_rect.bottom() - galley.size().y)
.at_least(0.0);
let x = rect
.left()
.at_most(screen_rect.right() - galley.size().x)
.at_least(0.0);
let text_pos = pos2(x, y);
let text_bg_color = Color32::from_black_alpha(180);
let text_rect_stroke_color = if is_clicking {
Color32::WHITE
} else {
text_bg_color
};
let text_rect = Rect::from_min_size(text_pos, galley.size());
painter.rect(text_rect, 0.0, text_bg_color, (1.0, text_rect_stroke_color));
painter.galley(text_pos, galley);
if ui.input(|i| i.pointer.any_click()) {
ui.ctx().copy_text(callstack);
}
}
}
#[cfg(not(debug_assertions))]
fn register_rect(_ui: &Ui, _rect: Rect) {}
#[test]
fn ui_impl_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}

View File

@@ -588,10 +588,9 @@ impl PersistedMap {
crate::profile_scope!("gather");
for (hash, element) in &map.map {
if let Some(element) = element.to_serialize() {
let mut stats = types_map.entry(element.type_id).or_default();
let stats = types_map.entry(element.type_id).or_default();
stats.num_bytes += element.ron.len();
let mut generation_stats =
stats.generations.entry(element.generation).or_default();
let generation_stats = stats.generations.entry(element.generation).or_default();
generation_stats.num_bytes += element.ron.len();
generation_stats.elements.push((*hash, element));
} else {

View File

@@ -100,7 +100,10 @@ impl<'a> Image<'a> {
///
/// See [`ImageSource::Bytes`].
pub fn from_bytes(uri: impl Into<Cow<'static, str>>, bytes: impl Into<Bytes>) -> Self {
Self::new(ImageSource::Bytes(uri.into(), bytes.into()))
Self::new(ImageSource::Bytes {
uri: uri.into(),
bytes: bytes.into(),
})
}
/// Texture options used when creating the texture.
@@ -275,7 +278,7 @@ impl<'a> Image<'a> {
pub fn size(&self) -> Option<Vec2> {
match &self.source {
ImageSource::Texture(texture) => Some(texture.size),
ImageSource::Uri(_) | ImageSource::Bytes(_, _) => None,
ImageSource::Uri(_) | ImageSource::Bytes { .. } => None,
}
}
@@ -478,9 +481,10 @@ impl Default for ImageSize {
/// This is used by [`Image::new`] and [`Ui::image`].
#[derive(Clone)]
pub enum ImageSource<'a> {
/// Load the image from a URI.
/// Load the image from a URI, e.g. `https://example.com/image.png`.
///
/// This could be a `file://` path, `https://` url, `bytes://` identifier, or some other scheme.
///
/// This could be a `file://` url, `http(s)?://` url, or a `bare` identifier.
/// How the URI will be turned into a texture for rendering purposes is
/// up to the registered loaders to handle.
///
@@ -495,8 +499,6 @@ pub enum ImageSource<'a> {
/// Load the image from some raw bytes.
///
/// For better error messages, use the `bytes://` prefix for the URI.
///
/// The [`Bytes`] may be:
/// - `'static`, obtained from `include_bytes!` or similar
/// - Anything that can be converted to `Arc<[u8]>`
@@ -506,13 +508,22 @@ pub enum ImageSource<'a> {
/// See also [`include_image`] for an easy way to load and display static images.
///
/// See [`crate::load`] for more information.
Bytes(Cow<'static, str>, Bytes),
Bytes {
/// The unique identifier for this image, e.g. `bytes://my_logo.png`.
///
/// You should use a proper extension (`.jpg`, `.png`, `.svg`, etc) for the image to load properly.
///
/// Use the `bytes://` scheme for the URI for better error messages.
uri: Cow<'static, str>,
bytes: Bytes,
},
}
impl<'a> std::fmt::Debug for ImageSource<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ImageSource::Bytes(uri, _) | ImageSource::Uri(uri) => uri.as_ref().fmt(f),
ImageSource::Bytes { uri, .. } | ImageSource::Uri(uri) => uri.as_ref().fmt(f),
ImageSource::Texture(st) => st.id.fmt(f),
}
}
@@ -524,7 +535,7 @@ impl<'a> ImageSource<'a> {
pub fn texture_size(&self) -> Option<Vec2> {
match self {
ImageSource::Texture(texture) => Some(texture.size),
ImageSource::Uri(_) | ImageSource::Bytes(_, _) => None,
ImageSource::Uri(_) | ImageSource::Bytes { .. } => None,
}
}
@@ -539,7 +550,7 @@ impl<'a> ImageSource<'a> {
match self {
Self::Texture(texture) => Ok(TexturePoll::Ready { texture }),
Self::Uri(uri) => ctx.try_load_texture(uri.as_ref(), texture_options, size_hint),
Self::Bytes(uri, bytes) => {
Self::Bytes { uri, bytes } => {
ctx.include_bytes(uri.clone(), bytes);
ctx.try_load_texture(uri.as_ref(), texture_options, size_hint)
}
@@ -551,7 +562,7 @@ impl<'a> ImageSource<'a> {
/// This will return `None` for [`Self::Texture`].
pub fn uri(&self) -> Option<&str> {
match self {
ImageSource::Bytes(uri, _) | ImageSource::Uri(uri) => Some(uri),
ImageSource::Bytes { uri, .. } | ImageSource::Uri(uri) => Some(uri),
ImageSource::Texture(_) => None,
}
}
@@ -644,21 +655,30 @@ impl<'a> From<Cow<'a, str>> for ImageSource<'a> {
impl<T: Into<Bytes>> From<(&'static str, T)> for ImageSource<'static> {
#[inline]
fn from((uri, bytes): (&'static str, T)) -> Self {
Self::Bytes(uri.into(), bytes.into())
Self::Bytes {
uri: uri.into(),
bytes: bytes.into(),
}
}
}
impl<T: Into<Bytes>> From<(Cow<'static, str>, T)> for ImageSource<'static> {
#[inline]
fn from((uri, bytes): (Cow<'static, str>, T)) -> Self {
Self::Bytes(uri, bytes.into())
Self::Bytes {
uri,
bytes: bytes.into(),
}
}
}
impl<T: Into<Bytes>> From<(String, T)> for ImageSource<'static> {
#[inline]
fn from((uri, bytes): (String, T)) -> Self {
Self::Bytes(uri.into(), bytes.into())
Self::Bytes {
uri: uri.into(),
bytes: bytes.into(),
}
}
}

View File

@@ -1,6 +1,6 @@
[package]
name = "egui_demo_app"
version = "0.22.0"
version = "0.23.0"
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
license = "MIT OR Apache-2.0"
edition = "2021"
@@ -18,6 +18,9 @@ crate-type = ["cdylib", "rlib"]
[features]
default = ["glow", "persistence"]
# image_viewer adds about 0.9 MB of WASM
web_app = ["http", "persistence", "web_screen_reader"]
http = ["ehttp", "image", "poll-promise", "egui_extras/image"]
image_viewer = ["image", "egui_extras/all_loaders", "rfd"]
persistence = ["eframe/persistence", "egui/persistence", "serde"]
@@ -33,12 +36,13 @@ chrono = { version = "0.4", default-features = false, features = [
"js-sys",
"wasmbind",
] }
eframe = { version = "0.22.0", path = "../eframe", default-features = false }
egui = { version = "0.22.0", path = "../egui", features = [
eframe = { version = "0.23.0", path = "../eframe", default-features = false }
egui = { version = "0.23.0", path = "../egui", features = [
"callstack",
"extra_debug_asserts",
"log",
] }
egui_demo_lib = { version = "0.22.0", path = "../egui_demo_lib", features = [
egui_demo_lib = { version = "0.23.0", path = "../egui_demo_lib", features = [
"chrono",
] }
log = { version = "0.4", features = ["std"] }
@@ -46,18 +50,17 @@ log = { version = "0.4", features = ["std"] }
# Optional dependencies:
bytemuck = { version = "1.7.1", optional = true }
egui_extras = { version = "0.22.0", path = "../egui_extras", features = [
egui_extras = { version = "0.23.0", path = "../egui_extras", features = [
"image",
] }
rfd = { version = "0.11", optional = true }
# feature "http":
ehttp = { version = "0.3.0", optional = true }
ehttp = { version = "0.3.1", optional = true }
image = { version = "0.24", optional = true, default-features = false, features = [
"jpeg",
"png",
] }
poll-promise = { version = "0.2", optional = true, default-features = false }
poll-promise = { version = "0.3", optional = true, default-features = false }
# feature "persistence":
serde = { version = "1", optional = true, features = ["derive"] }
@@ -66,6 +69,7 @@ serde = { version = "1", optional = true, features = ["derive"] }
# native:
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
env_logger = "0.10"
rfd = { version = "0.11", optional = true }
# web:
[target.'cfg(target_arch = "wasm32")'.dependencies]

View File

@@ -82,8 +82,6 @@ impl BackendPanel {
}
pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
egui::trace!(ui);
self.integration_ui(ui, frame);
ui.separator();
@@ -101,11 +99,9 @@ impl BackendPanel {
ui.separator();
{
let mut debug_on_hover = ui.ctx().debug_on_hover();
ui.checkbox(&mut debug_on_hover, "🐛 Debug on hover")
.on_hover_text("Show structure of the ui when you hover with the mouse");
ui.ctx().set_debug_on_hover(debug_on_hover);
#[cfg(debug_assertions)]
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");
}
#[cfg(target_arch = "wasm32")]

View File

@@ -164,21 +164,22 @@ pub struct WrapApp {
}
impl WrapApp {
pub fn new(_cc: &eframe::CreationContext<'_>) -> Self {
egui_extras::install_image_loaders(&_cc.egui_ctx);
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
// This gives us image support:
egui_extras::install_image_loaders(&cc.egui_ctx);
#[allow(unused_mut)]
let mut slf = Self {
state: State::default(),
#[cfg(any(feature = "glow", feature = "wgpu"))]
custom3d: crate::apps::Custom3d::new(_cc),
custom3d: crate::apps::Custom3d::new(cc),
dropped_files: Default::default(),
};
#[cfg(feature = "persistence")]
if let Some(storage) = _cc.storage {
if let Some(storage) = cc.storage {
if let Some(state) = eframe::get_value(storage, eframe::APP_KEY) {
slf.state = state;
}
@@ -263,7 +264,6 @@ impl eframe::App for WrapApp {
let mut cmd = Command::Nothing;
egui::TopBottomPanel::top("wrap_app_top_bar").show(ctx, |ui| {
egui::trace!(ui);
ui.horizontal_wrapped(|ui| {
ui.visuals_mut().button_frame = false;
self.bar_contents(ui, frame, &mut cmd);

View File

@@ -1,6 +1,6 @@
[package]
name = "egui_demo_lib"
version = "0.22.0"
version = "0.23.0"
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
description = "Example library for egui"
edition = "2021"
@@ -11,7 +11,13 @@ readme = "README.md"
repository = "https://github.com/emilk/egui/tree/master/crates/egui_demo_lib"
categories = ["gui", "graphics"]
keywords = ["glium", "egui", "gui", "gamedev"]
include = ["../LICENSE-APACHE", "../LICENSE-MIT", "**/*.rs", "Cargo.toml"]
include = [
"../LICENSE-APACHE",
"../LICENSE-MIT",
"**/*.rs",
"Cargo.toml",
"data/icon.png",
]
[package.metadata.docs.rs]
all-features = true
@@ -32,9 +38,9 @@ syntect = ["egui_extras/syntect"]
[dependencies]
egui = { version = "0.22.0", path = "../egui", default-features = false }
egui_extras = { version = "0.22.0", path = "../egui_extras" }
egui_plot = { version = "0.22.0", path = "../egui_plot" }
egui = { version = "0.23.0", path = "../egui", default-features = false }
egui_extras = { version = "0.23.0", path = "../egui_extras" }
egui_plot = { version = "0.23.0", path = "../egui_plot" }
log = { version = "0.4", features = ["std"] }
unicode_names2 = { version = "0.6.0", default-features = false }

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -11,6 +11,7 @@ impl super::Demo for About {
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
egui::Window::new(self.name())
.default_width(320.0)
.default_height(480.0)
.open(open)
.show(ctx, |ui| {
use super::View as _;
@@ -41,6 +42,15 @@ impl super::View for About {
ui.add_space(12.0); // ui.separator();
ui.heading("Links");
links(ui);
ui.add_space(12.0);
ui.horizontal_wrapped(|ui| {
ui.spacing_mut().item_spacing.x = 0.0;
ui.label("egui development is sponsored by ");
ui.hyperlink_to("Rerun.io", "https://www.rerun.io/");
ui.label(", a startup building an SDK for visualizing streams of multimodal data");
});
}
}
@@ -65,15 +75,10 @@ fn about_immediate_mode(ui: &mut egui::Ui) {
);
ui.add_space(8.0);
ui.label("Note how there are no callbacks or messages, and no button state to store.");
ui.label("Immediate mode has its roots in gaming, where everything on the screen is painted at the display refresh rate, i.e. at 60+ frames per second. \
In immediate mode GUIs, the entire interface is laid out and painted at the same high rate. \
This makes immediate mode GUIs especially well suited for highly interactive applications.");
ui.horizontal_wrapped(|ui| {
ui.spacing_mut().item_spacing.x = 0.0;
ui.label("More about immediate mode ");
ui.label("There are no callbacks or messages, and no button state to store. ");
ui.label("Read more about immediate mode ");
ui.hyperlink_to("here", "https://github.com/emilk/egui#why-immediate-mode");
ui.label(".");
});

View File

@@ -1,6 +1,7 @@
use egui::{Context, Modifiers, ScrollArea, Ui};
use std::collections::BTreeSet;
use egui::{Context, Modifiers, NumExt as _, ScrollArea, Ui};
use super::About;
use super::Demo;
use super::View;
@@ -180,7 +181,7 @@ impl DemoWindows {
fn mobile_ui(&mut self, ctx: &Context) {
if self.about_is_open {
let screen_size = ctx.input(|i| i.screen_rect.size());
let default_width = (screen_size.x - 20.0).min(400.0);
let default_width = (screen_size.x - 32.0).at_most(400.0);
let mut close = false;
egui::Window::new(self.about.name())
@@ -243,7 +244,6 @@ impl DemoWindows {
.resizable(false)
.default_width(150.0)
.show(ctx, |ui| {
egui::trace!(ui);
ui.vertical_centered(|ui| {
ui.heading("✒ egui demos");
});

View File

@@ -197,7 +197,7 @@ impl WidgetGallery {
ui.end_row();
ui.add(doc_link_label("Image", "Image"));
let egui_icon = egui::include_image!("../../assets/icon.png");
let egui_icon = egui::include_image!("../../data/icon.png");
ui.add(egui::Image::new(egui_icon.clone()));
ui.end_row();

View File

@@ -6,6 +6,7 @@ pub struct WindowOptions {
closable: bool,
collapsible: bool,
resizable: bool,
constrain: bool,
scroll2: [bool; 2],
disabled_time: f64,
@@ -22,6 +23,7 @@ impl Default for WindowOptions {
closable: true,
collapsible: true,
resizable: true,
constrain: true,
scroll2: [true; 2],
disabled_time: f64::NEG_INFINITY,
anchored: false,
@@ -43,6 +45,7 @@ impl super::Demo for WindowOptions {
closable,
collapsible,
resizable,
constrain,
scroll2,
disabled_time,
anchored,
@@ -59,6 +62,7 @@ impl super::Demo for WindowOptions {
let mut window = egui::Window::new(title)
.id(egui::Id::new("demo_window_options")) // required since we change the title
.resizable(resizable)
.constrain(constrain)
.collapsible(collapsible)
.title_bar(title_bar)
.scroll2(scroll2)
@@ -81,6 +85,7 @@ impl super::View for WindowOptions {
closable,
collapsible,
resizable,
constrain,
scroll2,
disabled_time: _,
anchored,
@@ -99,6 +104,8 @@ impl super::View for WindowOptions {
ui.checkbox(closable, "closable");
ui.checkbox(collapsible, "collapsible");
ui.checkbox(resizable, "resizable");
ui.checkbox(constrain, "constrain")
.on_hover_text("Constrain window to the screen");
ui.checkbox(&mut scroll2[0], "hscroll");
ui.checkbox(&mut scroll2[1], "vscroll");
});

View File

@@ -5,6 +5,14 @@ This file is updated upon each release.
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
## 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)!)
* Add syntax highlighting feature to `egui_extras` [#3333](https://github.com/emilk/egui/pull/3333) [#3388](https://github.com/emilk/egui/pull/3388)
* Add `TableBuilder::drag_to_scroll` [#3100](https://github.com/emilk/egui/pull/3100) (thanks [@KYovchevski](https://github.com/KYovchevski)!)
* Add opt-in `puffin` feature to `egui-extras` [#3307](https://github.com/emilk/egui/pull/3307)
* Always depend on `log` crate [#3336](https://github.com/emilk/egui/pull/3336)
* Fix not taking clipping into account when calculating column remainder [#3357](https://github.com/emilk/egui/pull/3357) (thanks [@daxpedda](https://github.com/daxpedda)!)
## 0.22.0 - 2023-05-23
- Add option to hide datepicker button calendar icon [#2910](https://github.com/emilk/egui/pull/2910) (thanks [@Barugon](https://github.com/Barugon)!)

View File

@@ -1,6 +1,6 @@
[package]
name = "egui_extras"
version = "0.22.0"
version = "0.23.0"
authors = [
"Dominik Rössler <dominik@freshx.de>",
"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>",
@@ -42,7 +42,8 @@ http = ["dep:ehttp"]
##
## You also need to ALSO opt-in to the image formats you want to support, like so:
## ```toml
## image = { version = "0.24", features = ["jpeg", "png"] }
## image = { version = "0.24", features = ["jpeg", "png"] } # Add the types you want support for
## ```
image = ["dep:image"]
@@ -59,7 +60,7 @@ syntect = ["dep:syntect"]
[dependencies]
egui = { version = "0.22.0", path = "../egui", default-features = false, features = [
egui = { version = "0.23.0", path = "../egui", default-features = false, features = [
"serde",
] }
enum-map = { version = "2", features = ["serde"] }
@@ -96,4 +97,4 @@ tiny-skia = { version = "0.8", optional = true, default-features = false } # mus
usvg = { version = "0.28", optional = true, default-features = false }
# http feature
ehttp = { version = "0.3.0", optional = true, default-features = false }
ehttp = { version = "0.3.1", optional = true, default-features = false }

View File

@@ -13,7 +13,7 @@ One thing `egui_extras` is commonly used for is to install image loaders for `eg
```toml
egui_extras = { version = "*", features = ["all_loaders"] }
image = { version = "0.24", features = ["jpeg", "png"] }
image = { version = "0.24", features = ["jpeg", "png"] } # Add the types you want support for
```
```rs

View File

@@ -117,6 +117,7 @@ impl<'a> Widget for DatePickerButton<'a> {
} = Area::new(ui.make_persistent_id(self.id_source))
.order(Order::Foreground)
.fixed_pos(pos)
.constrain_to(ui.ctx().screen_rect())
.show(ui.ctx(), |ui| {
let frame = Frame::popup(ui.style());
frame

View File

@@ -20,7 +20,7 @@
///
/// ```toml,ignore
/// egui_extras = { version = "*", features = ["all_loaders"] }
/// image = { version = "0.24", features = ["jpeg", "png"] }
/// image = { version = "0.24", features = ["jpeg", "png"] } # Add the types you want support for
/// ```
///
/// ⚠ You have to configure both the supported loaders in `egui_extras` _and_ the supported image formats

View File

@@ -1,6 +1,6 @@
[package]
name = "egui_glium"
version = "0.22.0"
version = "0.23.0"
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
description = "Bindings for using egui natively using the glium library"
edition = "2021"
@@ -36,10 +36,10 @@ links = ["egui-winit/links"]
[dependencies]
egui = { version = "0.22.0", path = "../egui", default-features = false, features = [
egui = { version = "0.23.0", path = "../egui", default-features = false, features = [
"bytemuck",
] }
egui-winit = { version = "0.22.0", path = "../egui-winit", default-features = false }
egui-winit = { version = "0.23.0", path = "../egui-winit", default-features = false }
ahash = { version = "0.8.1", default-features = false, features = [
"no-rng", # we don't need DOS-protection, so we let users opt-in to it instead
@@ -54,5 +54,5 @@ document-features = { version = "0.2", optional = true }
[dev-dependencies]
egui_demo_lib = { version = "0.22.0", path = "../egui_demo_lib", default-features = false }
egui_demo_lib = { version = "0.23.0", path = "../egui_demo_lib", default-features = false }
image = { version = "0.24", default-features = false, features = ["png"] }

View File

@@ -5,7 +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.
## 0.23.0 - 2023-09-27
* Update `egui`
## 0.22.0 - 2023-05-23
* Update `egui`
## 0.21.0 - 2023-02-08

View File

@@ -1,6 +1,6 @@
[package]
name = "egui_glow"
version = "0.22.0"
version = "0.23.0"
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
description = "Bindings for using egui natively using the glow library"
edition = "2021"
@@ -44,7 +44,7 @@ winit = ["egui-winit"]
[dependencies]
egui = { version = "0.22.0", path = "../egui", default-features = false, features = [
egui = { version = "0.23.0", path = "../egui", default-features = false, features = [
"bytemuck",
] }
@@ -59,7 +59,7 @@ document-features = { version = "0.2", optional = true }
# Native:
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
egui-winit = { version = "0.22.0", path = "../egui-winit", optional = true, default-features = false }
egui-winit = { version = "0.23.0", path = "../egui-winit", optional = true, default-features = false }
puffin = { version = "0.16", optional = true }
# Web:

View File

@@ -1,6 +1,6 @@
//! [`egui`] bindings for [`glow`](https://github.com/grovesNL/glow).
//!
//! The main types you want to look are are [`Painter`].
//! The main type you want to look at is [`Painter`].
//!
//! If you are writing an app, you may want to look at [`eframe`](https://docs.rs/eframe) instead.
//!

View File

@@ -3,3 +3,14 @@ All notable changes to the `egui_plot` integration will be noted in this file.
This file is updated upon each release.
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
## 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)!)
* Add `PlotUi::response()` to replace `plot_clicked()` etc [#3223](https://github.com/emilk/egui/pull/3223)
* Add rotation feature to plot images [#3121](https://github.com/emilk/egui/pull/3121) (thanks [@ThundR67](https://github.com/ThundR67)!)
* Plot items: Image rotation and size in plot coordinates, polygon fill color [#3182](https://github.com/emilk/egui/pull/3182) (thanks [@s-nie](https://github.com/s-nie)!)
* Add method to specify `tip_size` of plot arrows [#3138](https://github.com/emilk/egui/pull/3138) (thanks [@nagua](https://github.com/nagua)!)
* Better handle additive colors in plots [#3387](https://github.com/emilk/egui/pull/3387)
* Fix auto_bounds when only one axis has restricted navigation [#3171](https://github.com/emilk/egui/pull/3171) (thanks [@KoffeinFlummi](https://github.com/KoffeinFlummi)!)
* Fix plot formatter not taking closures [#3260](https://github.com/emilk/egui/pull/3260) (thanks [@Wumpf](https://github.com/Wumpf)!)

View File

@@ -1,11 +1,7 @@
[package]
name = "egui_plot"
version = "0.22.0"
authors = [
"Dominik Rössler <dominik@freshx.de>",
"Emil Ernerfeldt <emil.ernerfeldt@gmail.com>",
"René Rössler <rene@freshx.de>",
]
version = "0.23.0"
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
description = "Immediate mode plotting for the egui GUI library"
edition = "2021"
rust-version = "1.70"
@@ -32,7 +28,7 @@ serde = ["dep:serde", "egui/serde"]
[dependencies]
egui = { version = "0.22.0", path = "../egui", default-features = false }
egui = { version = "0.23.0", path = "../egui", default-features = false }
#! ### Optional dependencies

View File

@@ -47,6 +47,7 @@ impl PlotPoint {
// ----------------------------------------------------------------------------
/// Solid, dotted, dashed, etc.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum LineStyle {
Solid,
@@ -319,6 +320,7 @@ impl PlotPoints {
// ----------------------------------------------------------------------------
/// Circle, Diamond, Square, Cross, …
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum MarkerShape {
Circle,

View File

@@ -1,4 +1,4 @@
//! Simple plotting library.
//! Simple plotting library for [`egui`](https://github.com/emilk/egui).
//!
//! ## Feature flags
#![cfg_attr(feature = "document-features", doc = document_features::document_features!())]
@@ -77,6 +77,7 @@ impl Default for CoordinatesFormatter {
const MIN_LINE_SPACING_IN_POINTS: f64 = 6.0; // TODO(emilk): large enough for a wide label
/// Two bools, one for each axis (X and Y).
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct AxisBools {

View File

@@ -1,6 +1,6 @@
[package]
name = "emath"
version = "0.22.0"
version = "0.23.0"
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
description = "Minimal 2D math library for GUI work"
edition = "2021"

View File

@@ -5,6 +5,17 @@ This file is updated upon each release.
Changes since the last release can be found by running the `scripts/generate_changelog.py` script.
## 0.23.0 - 2023-09-27
* 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 control of line height and letter spacing [#3302](https://github.com/emilk/egui/pull/3302)
* Support images with rounded corners [#3257](https://github.com/emilk/egui/pull/3257)
* Add `ColorImage::from_gray` [#3166](https://github.com/emilk/egui/pull/3166) (thanks [@thomaseliot](https://github.com/thomaseliot)!)
* Provide `into_inner()` for `egui::mutex::{Mutex, RwLock}` [#3110](https://github.com/emilk/egui/pull/3110) (thanks [@KmolYuan](https://github.com/KmolYuan)!)
* Fix problems with tabs in text [#3355](https://github.com/emilk/egui/pull/3355)
* Refactor: change `ClippedShape` from struct-enum to a normal struct [#3225](https://github.com/emilk/egui/pull/3225)
* Document when `Galley`s get invalidated [#3024](https://github.com/emilk/egui/pull/3024) (thanks [@e00E](https://github.com/e00E)!)
## 0.22.0 - 2023-05-23
* Fix compiling `epaint` without `bytemuck` dependency [#2913](https://github.com/emilk/egui/pull/2913) (thanks [@lunixbochs](https://github.com/lunixbochs)!)

View File

@@ -1,6 +1,6 @@
[package]
name = "epaint"
version = "0.22.0"
version = "0.23.0"
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
description = "Minimal 2D graphics library for GUI work"
edition = "2021"
@@ -70,8 +70,8 @@ serde = ["dep:serde", "ahash/serde", "emath/serde", "ecolor/serde"]
unity = []
[dependencies]
emath = { version = "0.22.0", path = "../emath" }
ecolor = { version = "0.22.0", path = "../ecolor" }
emath = { version = "0.23.0", path = "../emath" }
ecolor = { version = "0.23.0", path = "../ecolor" }
ab_glyph = "0.2.11"
ahash = { version = "0.8.1", default-features = false, features = [

View File

@@ -498,6 +498,8 @@ pub struct RectShape {
///
/// To display a texture, set [`Self::fill_texture_id`],
/// and set this to `Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0))`.
///
/// Use [`Rect::ZERO`] to turn off texturing.
pub uv: Rect,
}

View File

@@ -72,7 +72,7 @@ pub struct FontImpl {
height_in_points: f32,
// move each character by this much (hack)
y_offset: f32,
y_offset_in_points: f32,
ascent: f32,
pixels_per_point: f32,
@@ -111,22 +111,23 @@ impl FontImpl {
scale_in_points * tweak.y_offset_factor
} + tweak.y_offset;
// center scaled glyphs properly
let y_offset_points = y_offset_points + (tweak.scale - 1.0) * 0.5 * (ascent + descent);
// Center scaled glyphs properly:
let height = ascent + descent;
let y_offset_points = y_offset_points - (1.0 - tweak.scale) * 0.5 * height;
// Round to an even number of physical pixels to get even kerning.
// See https://github.com/emilk/egui/issues/382
let scale_in_pixels = scale_in_pixels.round() as u32;
// Round to closest pixel:
let y_offset = (y_offset_points * pixels_per_point).round() / pixels_per_point;
let y_offset_in_points = (y_offset_points * pixels_per_point).round() / pixels_per_point;
Self {
name,
ab_glyph_font,
scale_in_pixels,
height_in_points: ascent - descent + line_gap,
y_offset,
y_offset_in_points,
ascent: ascent + baseline_offset,
pixels_per_point,
glyph_info_cache: Default::default(),
@@ -283,7 +284,8 @@ impl FontImpl {
});
let offset_in_pixels = vec2(bb.min.x, bb.min.y);
let offset = offset_in_pixels / self.pixels_per_point + self.y_offset * Vec2::Y;
let offset =
offset_in_pixels / self.pixels_per_point + self.y_offset_in_points * Vec2::Y;
UvRect {
offset,
size: vec2(glyph_width as f32, glyph_height as f32) / self.pixels_per_point,

View File

@@ -36,6 +36,7 @@ deny = [
skip = [
{ name = "arrayvec" }, # old version via tiny-skiaz
{ 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 = "nix" }, # old version via winit
{ name = "redox_syscall" }, # old version via winit
{ name = "time" }, # old version pulled in by unmaintianed crate 'chrono'

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -12,4 +12,8 @@ publish = false
eframe = { path = "../../crates/eframe", features = [
"__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO
] }
# For image support:
egui_extras = { path = "../../crates/egui_extras", features = ["image"] }
env_logger = "0.10"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -11,7 +11,12 @@ fn main() -> Result<(), eframe::Error> {
eframe::run_native(
"My egui App",
options,
Box::new(|_cc| Box::<MyApp>::default()),
Box::new(|cc| {
// This gives us image support:
egui_extras::install_image_loaders(&cc.egui_ctx);
Box::<MyApp>::default()
}),
)
}
@@ -43,6 +48,10 @@ impl eframe::App for MyApp {
self.age += 1;
}
ui.label(format!("Hello '{}', age {}", self.name, self.age));
ui.image(egui::include_image!(
"../../../crates/egui/assets/ferris.png"
));
});
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -12,7 +12,7 @@ fn main() -> Result<(), eframe::Error> {
"Image Viewer",
options,
Box::new(|cc| {
// The following call is needed to load images when using `ui.image` and `egui::Image`:
// This gives us image support:
egui_extras::install_image_loaders(&cc.egui_ctx);
Box::<MyApp>::default()
}),
@@ -29,8 +29,7 @@ impl eframe::App for MyApp {
ui.image(egui::include_image!("ferris.svg"));
ui.add(
egui::Image::new("https://picsum.photos/seed/1.759706314/1024")
.rounding(egui::Rounding::same(10.0)),
egui::Image::new("https://picsum.photos/seed/1.759706314/1024").rounding(10.0),
);
});
});

View File

@@ -3,3 +3,5 @@ This example shows that you can save a plot in egui as a png.
```sh
cargo run -p save_plot
```
![](screenshot.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@@ -8,7 +8,7 @@ fn main() -> Result<(), eframe::Error> {
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
let options = eframe::NativeOptions {
initial_window_size: Some(egui::vec2(350.0, 400.0)),
initial_window_size: Some(egui::vec2(350.0, 200.0)),
..Default::default()
};
eframe::run_native(
@@ -27,45 +27,19 @@ impl eframe::App for MyApp {
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
let mut plot_rect = None;
egui::CentralPanel::default().show(ctx, |ui| {
// these are just some dummy variables for the example,
// such that the plot is not at position (0,0)
let height = 200.0;
let border_x = 11.0;
let border_y = 18.0;
let width = 300.0;
ui.heading("My egui Application");
// add some whitespace in y direction
ui.add_space(border_y);
if ui.button("Save Plot").clicked() {
frame.request_screenshot();
}
// add some whitespace in y direction
ui.add_space(border_y);
let my_plot = Plot::new("My Plot").legend(Legend::default());
ui.horizontal(|ui| {
// add some whitespace in x direction
ui.add_space(border_x);
let my_plot = Plot::new("My Plot")
.height(height)
.width(width)
.legend(Legend::default());
// let's create a dummy line in the plot
let graph: Vec<[f64; 2]> = vec![[0.0, 1.0], [2.0, 3.0], [3.0, 2.0]];
let inner = my_plot.show(ui, |plot_ui| {
plot_ui.line(Line::new(PlotPoints::from(graph)).name("curve"));
});
// Remember the position of the plot
plot_rect = Some(inner.response.rect);
// let's create a dummy line in the plot
let graph: Vec<[f64; 2]> = vec![[0.0, 1.0], [2.0, 3.0], [3.0, 2.0]];
let inner = my_plot.show(ui, |plot_ui| {
plot_ui.line(Line::new(PlotPoints::from(graph)).name("curve"));
});
// add some whitespace in y direction
ui.add_space(border_y);
// Remember the position of the plot
plot_rect = Some(inner.response.rect);
});
if let (Some(screenshot), Some(plot_location)) = (self.screenshot.take(), plot_rect) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

View File

@@ -12,8 +12,7 @@ export RUSTFLAGS=--cfg=web_sys_unstable_apis
CRATE_NAME="egui_demo_app"
# NOTE: persistence use up about 400kB (10%) of the WASM!
FEATURES="http,persistence,web_screen_reader"
FEATURES="web_app"
OPEN=false
OPTIMIZE=false

View File

@@ -7,6 +7,7 @@ set -x
# cargo install cargo-deny
cargo deny --all-features --log-level error --target aarch64-apple-darwin check
cargo deny --all-features --log-level error --target aarch64-linux-android check
cargo deny --all-features --log-level error --target i686-pc-windows-gnu check
cargo deny --all-features --log-level error --target i686-pc-windows-msvc check
cargo deny --all-features --log-level error --target i686-unknown-linux-gnu check

View File

@@ -3,7 +3,8 @@
"""
Summarizes recent PRs based on their GitHub labels.
The result can be copy-pasted into CHANGELOG.md, though it often needs some manual editing too.
The result can be copy-pasted into CHANGELOG.md,
though it often needs some manual editing too.
"""
import multiprocessing
@@ -89,7 +90,9 @@ def fetch_pr_info(pr_number: int) -> Optional[PrInfo]:
def get_commit_info(commit: Any) -> CommitInfo:
match = re.match(r"(.*) \(#(\d+)\)", commit.summary)
if match:
return CommitInfo(hexsha=commit.hexsha, title=str(match.group(1)), pr_number=int(match.group(2)))
title = str(match.group(1))
pr_number = int(match.group(2))
return CommitInfo(hexsha=commit.hexsha, title=title, pr_number=pr_number)
else:
return CommitInfo(hexsha=commit.hexsha, title=commit.summary, pr_number=None)
@@ -104,8 +107,9 @@ def print_section(crate: str, items: List[str]) -> None:
if 0 < len(items):
print(f"#### {crate}")
for line in items:
line = remove_prefix(line, f"{crate}: ")
line = remove_prefix(line, f"[{crate}] ")
line = remove_prefix(line, f"{crate}: ")
line = remove_prefix(line, f"`{crate}`: ")
print(f"* {line}")
print()
@@ -152,9 +156,16 @@ def main() -> None:
summary = f"{title} [{hexsha[:7]}](https://github.com/{OWNER}/{REPO}/commit/{hexsha})"
unsorted_commits.append(summary)
else:
title = pr_info.pr_title if pr_info else title # We prefer the PR title if available
# We prefer the PR title if available
title = pr_info.pr_title if pr_info else title
labels = pr_info.labels if pr_info else []
if 'exclude from changelog' in labels:
continue
if 'typo' in labels:
# We get so many typo PRs. Let's not flood the changelog with them.
continue
summary = f"{title} [#{pr_number}](https://github.com/{OWNER}/{REPO}/pull/{pr_number})"
if INCLUDE_LABELS and 0 < len(labels):
@@ -165,9 +176,6 @@ def main() -> None:
if gh_user_name not in OFFICIAL_DEVS:
summary += f" (thanks [@{gh_user_name}](https://github.com/{gh_user_name})!)"
if 'typo' in labels:
continue # We get so many typo PRs. Let's not flood the changelog with them.
added = False
for crate in crate_names: