* Fixes https://github.com/rerun-io/rerun/issues/4297
* tested against a very hasty and incomplete port of egui/trunk, found
[here](https://github.com/rerun-io/rerun/tree/andreas/experimental-egui-trunk)
In rare cases in can happen that the viewport returned by
`PaintCallbackInfo` is outside the bounds of the screen.
for at least [wgpu/webgpu in
particular](https://www.w3.org/TR/webgpu/#dom-gpurenderpassencoder-setviewport)
this is invalid usage, other backends might be affected as well.
Since this happened due to a float rounding error (in one repro case I
had I got (width==1126.5625) + (offset=715.4376) = 1842.0001 for a
resolution of 1842) I decided to do away with fractional values on the
viewport alltogether. They _technically_ make sense since a viewport is
only specifying the NDC to pixel coordinate conversion, but practically
this may lead to surprising sub-sampling issues.
---------
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
* Closes https://github.com/emilk/egui/issues/3602
You can now zoom any egui app by pressing Cmd+Plus, Cmd+Minus or Cmd+0,
just like in a browser. This will change the current `zoom_factor`
(default 1.0) which is persisted in the egui memory, and is the same for
all viewports.
You can turn off the keyboard shortcuts with `ctx.options_mut(|o|
o.zoom_with_keyboard = false);`
`zoom_factor` can also be explicitly read/written with
`ctx.zoom_factor()` and `ctx.set_zoom_factor()`.
This redefines `pixels_per_point` as `zoom_factor *
native_pixels_per_point`, where `native_pixels_per_point` is whatever is
the native scale factor for the monitor that the current viewport is in.
This adds some complexity to the interaction with winit, since we need
to know the current `zoom_factor` in a lot of places, because all egui
IO is done in ui points. I'm pretty sure this PR fixes a bunch of subtle
bugs though that used to be in this code.
`egui::gui_zoom::zoom_with_keyboard_shortcuts` is now gone, and is no
longer needed, as this is now the default behavior.
`Context::set_pixels_per_point` is still there, but it is recommended
you use `Context::set_zoom_factor` instead.
* Closes https://github.com/emilk/egui/issues/3494
This makes sure it is always up-to-date.
Previously (years back) this caused a deadlock on Mac, but this seems to
have been fixed
This is required for Rust 1.72 (for unknown reasons; see
https://github.com/emilk/egui/pull/3595), but also for updating to glow
0.13, where the `glow::Context` is not longer `Sync+Send`
* Closes <https://github.com/emilk/egui/issues/3480>
I've tested this on Safari and Chrome on macOS Sonoma 14.0.
Could be improved to only call `event.preventDefault()` if
`runner.logic()` actually performed a copy, but I don't see a way to get
that information out with the current API.
* Closes#1974
<!--
Please read the "Making a PR" section of
[`CONTRIBUTING.md`](https://github.com/emilk/egui/blob/master/CONTRIBUTING.md)
before opening a Pull Request!
* Keep your PR:s small and focused.
* If applicable, add a screenshot or gif.
* If it is a non-trivial addition, consider adding a demo for it to
`egui_demo_lib`, or a new example.
* Do NOT open PR:s from your `master` branch, as that makes it hard for
maintainers to add commits to your PR.
* Remember to run `cargo fmt` and `cargo cranky`.
* Open the PR as a draft until you have self-reviewed it and run
`./scripts/check.sh`.
* When you have addressed a PR comment, mark it as resolved.
Please be patient! I will review you PR, but my time is limited!
-->
---------
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
Part 1 of 2 of adding a better API for egui_plot's auto-bounds feature.
In this PR:
- change the `Plot` builder struct field to `default_auto_bounds` (was
`auto_bounds`)
- change the `Plot` state field to `auto_bounds` (was `bounds_modified`)
- minor improvements to `Vec2b`
<!--
Please read the "Making a PR" section of
[`CONTRIBUTING.md`](https://github.com/emilk/egui/blob/master/CONTRIBUTING.md)
before opening a Pull Request!
* Keep your PR:s small and focused.
* If applicable, add a screenshot or gif.
* If it is a non-trivial addition, consider adding a demo for it to
`egui_demo_lib`, or a new example.
* Do NOT open PR:s from your `master` branch, as that makes it hard for
maintainers to add commits to your PR.
* Remember to run `cargo fmt` and `cargo cranky`.
* Open the PR as a draft until you have self-reviewed it and run
`./scripts/check.sh`.
* When you have addressed a PR comment, mark it as resolved.
Please be patient! I will review your PR, but my time is limited!
-->
Closes <https://github.com/emilk/egui/issues/3576>.
Introduced in the recent multi-viewports work, we accidentally recreated
the wgpu surfaces every frame. This is now fixed.
I found this while improving the profiling of `eframe`
* Part of https://github.com/emilk/egui/issues/3556
This PR replaces a bunch of options in `eframe::NativeOptions` with
`egui::ViewportBuilder`. For instance:
``` diff
let options = eframe::NativeOptions {
- initial_window_size: Some(egui::vec2(320.0, 240.0)),
- drag_and_drop_support: true,
+ viewport: egui::ViewportBuilder::default()
+ .with_inner_size([320.0, 240.0])
+ .with_drag_and_drop(true),
centered: true,
..Default::default()
};
```
* Part of https://github.com/emilk/egui/issues/3556
## In short
You now almost never need to use `eframe::Frame` - instead use
`ui.input(|i| i.viewport())` for information about the current viewport
(native window), and use `ctx.send_viewport_cmd` to modify it.
## In detail
This PR removes most commands from `eframe::Frame`, and replaces them
with `ViewportCommand`.
So `frame.close()` becomes
`ctx.send_viewport_cmd(ViewportCommand::Close)`, etc.
`frame.info().window_info` is now also gone, replaced with `ui.input(|i|
i.viewport())`.
`frame.info().native_pixels_per_point` is replaced with `ui.input(|i|
i.raw.native_pixels_per_point)`.
`RawInput` now contains one `ViewportInfo` for each viewport.
Screenshots are taken with
`ctx.send_viewport_cmd(ViewportCommand::Screenshots)` and are returned
in `egui::Event` which you can check with:
``` ust
ui.input(|i| {
for event in &i.raw.events {
if let egui::Event::Screenshot { viewport_id, image } = event {
// handle it here
}
}
});
```
### Motivation
You no longer need to pass around the `&eframe::Frame` everywhere.
This also opens the door for other integrations to use the same API of
`ViewportCommand`s.
<!--
Please read the "Making a PR" section of
[`CONTRIBUTING.md`](https://github.com/emilk/egui/blob/master/CONTRIBUTING.md)
before opening a Pull Request!
* Keep your PR:s small and focused.
* If applicable, add a screenshot or gif.
* If it is a non-trivial addition, consider adding a demo for it to
`egui_demo_lib`, or a new example.
* Do NOT open PR:s from your `master` branch, as that makes it hard for
maintainers to add commits to your PR.
* Remember to run `cargo fmt` and `cargo cranky`.
* Open the PR as a draft until you have self-reviewed it and run
`./scripts/check.sh`.
* When you have addressed a PR comment, mark it as resolved.
Please be patient! I will review your PR, but my time is limited!
-->
Closes <https://github.com/emilk/egui/issues/3474>.
The goal is to host the egui web-demo on its own `gh-pages` branch, and
have the CI build and publish a new demo on each merged CI.
Let's see if I can get this to work…
Was a bit confused why the max_size API isn't exposed on `Window`, when
it's perfectly functional in `Resize`. Anyway here's the main thing that
it affects:
```rs
let screen = ctx.available_rect();
let size = screen.size();
egui::Window::new(self.name())
.resizable(true)
.resize(|resize| resize.max_size(size)) // Before
.max_size(size) // After
.show(ctx, |ui| todo!());
```
I also added some other relevant helpers for consistency.
This PR doesn't change any logic, only forwards along some helper
functions that are already public for consistency.
* Closes#1044
---
(new PR description written by @emilk)
## Overview
This PR introduces the concept of `Viewports`, which on the native
eframe backend corresponds to native OS windows.
You can spawn a new viewport using `Context::show_viewport` and
`Cotext::show_viewport_immediate`.
These needs to be called every frame the viewport should be visible.
This is implemented by the native `eframe` backend, but not the web one.
## Viewport classes
The viewports form a tree of parent-child relationships.
There are different classes of viewports.
### Root vieport
The root viewport is the original viewport, and cannot be closed without
closing the application.
### Deferred viewports
These are created with `Context::show_viewport`.
Deferred viewports take a closure that is called by the integration at a
later time, perhaps multiple times.
Deferred viewports are repainted independenantly of the parent viewport.
This means communication with them need to done via channels, or
`Arc/Mutex`.
This is the most performant type of child viewport, though a bit more
cumbersome to work with compared to immediate viewports.
### Immediate viewports
These are created with `Context::show_viewport_immediate`.
Immediate viewports take a `FnOnce` closure, similar to other egui
functions, and is called immediately. This makes communication with them
much simpler than with deferred viewports, but this simplicity comes at
a cost: whenever tha parent viewports needs to be repainted, so will the
child viewport, and vice versa. This means that if you have `N`
viewports you are poentially doing `N` times as much CPU work. However,
if all your viewports are showing animations, and thus are repainting
constantly anyway, this doesn't matter.
In short: immediate viewports are simpler to use, but can waste a lot of
CPU time.
### Embedded viewports
These are not real, independenant viewports, but is a fallback mode for
when the integration does not support real viewports. In your callback
is called with `ViewportClass::Embedded` it means you need to create an
`egui::Window` to wrap your ui in, which will then be embedded in the
parent viewport, unable to escape it.
## Using the viewports
Only one viewport is active at any one time, identified wth
`Context::viewport_id`.
You can send commands to other viewports using
`Context::send_viewport_command_to`.
There is an example in
<https://github.com/emilk/egui/tree/master/examples/multiple_viewports/src/main.rs>.
## For integrations
There are several changes relevant to integrations.
* There is a [`crate::RawInput::viewport`] with information about the
current viewport.
* The repaint callback set by `Context::set_request_repaint_callback`
now points to which viewport should be repainted.
* `Context::run` now returns a list of viewports in `FullOutput` which
should result in their own independant windows
* There is a new `Context::set_immediate_viewport_renderer` for setting
up the immediate viewport integration
* If you support viewports, you need to call
`Context::set_embed_viewports(false)`, or all new viewports will be
embedded (the default behavior).
## Future work
* Make it easy to wrap child viewports in the same chrome as
`egui::Window`
* Automatically show embedded viewports using `egui::Window`
* Use the new `ViewportBuilder` in `eframe::NativeOptions`
* Automatically position new viewport windows (they currently cover each
other)
* Add a `Context` method for listing all existing viewports
Find more at https://github.com/emilk/egui/issues/3556
---
<details>
<summary>
Outdated PR description by @konkitoman
</summary>
## Inspiration
- Godot because the app always work desktop or single_window because of
embedding
- Dear ImGui viewport system
## What is a Viewport
A Viewport is a egui isolated component!
Can be used by the egui integration to create native windows!
When you create a Viewport is possible that the backend do not supports
that!
So you need to check if the Viewport was created or you are in the
normal egui context!
This is how you can do that:
```rust
if ctx.viewport_id() != ctx.parent_viewport_id() {
// In here you add the code for the viewport context, like
egui::CentralPanel::default().show(ctx, |ui|{
ui.label("This is in a native window!");
});
}else{
// In here you add the code for when viewport cannot be created!
// You cannot use CentralPanel in here because you will override the app CentralPanel
egui::Window::new("Virtual Viewport").show(ctx, |ui|{
ui.label("This is without a native window!\nThis is in a embedded viewport");
});
}
```
This PR do not support for drag and drop between Viewports!
After this PR is accepted i will begin work to intregrate the Viewport
system in `egui::Window`!
The `egui::Window` i want to behave the same on desktop and web
The `egui::Window` will be like Godot Window
## Changes and new
These are only public structs and functions!
<details>
<summary>
## New
</summary>
- `egui::ViewportId`
- `egui::ViewportBuilder`
This is like winit WindowBuilder
- `egui::ViewportCommand`
With this you can set any winit property on a viewport, when is a native
window!
- `egui::Context::new`
- `egui::Context::create_viewport`
- `egui::Context::create_viewport_sync`
- `egui::Context::viewport_id`
- `egui::Context::parent_viewport_id`
- `egui::Context::viewport_id_pair`
- `egui::Context::set_render_sync_callback`
- `egui::Context::is_desktop`
- `egui::Context::force_embedding`
- `egui::Context::set_force_embedding`
- `egui::Context::viewport_command`
- `egui::Context::send_viewport_command_to`
- `egui::Context::input_for`
- `egui::Context::input_mut_for`
- `egui::Context::frame_nr_for`
- `egui::Context::request_repaint_for`
- `egui::Context::request_repaint_after_for`
- `egui::Context::requested_repaint_last_frame`
- `egui::Context::requested_repaint_last_frame_for`
- `egui::Context::requested_repaint`
- `egui::Context::requested_repaint_for`
- `egui::Context::inner_rect`
- `egui::Context::outer_rect`
- `egui::InputState::inner_rect`
- `egui::InputState::outer_rect`
- `egui::WindowEvent`
</details>
<details>
<summary>
## Changes
</summary>
- `egui::Context::run`
Now needs the viewport that we want to render!
- `egui::Context::begin_frame`
Now needs the viewport that we want to render!
- `egui::Context::tessellate`
Now needs the viewport that we want to render!
- `egui::FullOutput`
```diff
- repaint_after
+ viewports
+ viewport_commands
```
- `egui::RawInput`
```diff
+ inner_rect
+ outer_rect
```
- `egui::Event`
```diff
+ WindowEvent
```
</details>
### Async Viewport
Async means that is independent from other viewports!
Is created by `egui::Context::create_viewport`
To be used you will need to wrap your state in `Arc<RwLock<T>>`
Look at viewports example to understand how to use it!
### Sync Viewport
Sync means that is dependent on his parent!
Is created by `egui::Context::create_viewport_sync`
This will pause the parent then render itself the resumes his parent!
#### ⚠️ This currently will make the fps/2 for every sync
viewport
### Common
#### ⚠️ Attention
You will need to do this when you render your content
```rust
ctx.create_viewport(ViewportBuilder::new("Simple Viewport"), | ctx | {
let content = |ui: &mut egui::Ui|{
ui.label("Content");
};
// This will make the content a popup if cannot create a native window
if ctx.viewport_id() != ctx.parent_viewport_id() {
egui::CentralPanel::default().show(ctx, content);
} else {
egui::Area::new("Simple Viewport").show(ctx, |ui| {
egui::Frame::popup(ui.style()).show(ui, content);
});
};
});
````
## What you need to know as egui user
### If you are using eframe
You don't need to change anything!
### If you have a manual implementation
Now `egui::run` or `egui::begin` and `egui::tessellate` will need the
current viewport id!
You cannot create a `ViewportId` only `ViewportId::MAIN`
If you make a single window app you will set the viewport id to be
`egui::ViewportId::MAIN` or see the `examples/pure_glow`
If you want to have multiples window support look at `crates/eframe`
glow or wgpu implementations!
## If you want to try this
- cargo run -p viewports
## This before was wanted to change
This will probably be in feature PR's
### egui::Window
To create a native window when embedded was set to false
You can try that in viewports example before:
[78a0ae8](78a0ae879e)
### egui popups, context_menu, tooltip
To be a native window
</details>
---------
Co-authored-by: Konkitoman <konkitoman@users.noreply.github.com>
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
Co-authored-by: Pablo Sichert <mail@pablosichert.com>
Tested on M1 Mac:
* native
* webgl, firefox
* webgpu, chrome
all looking normal
Updated minor ahash version because 0.8.1 got yanked. Added some deny
exceptions for now - we'll have to update winit soon to resolve glow
related cargo deny errors (not a big issue though since we don't expect
wgpu and glow backends to be used at the same time)