* Fixes https://github.com/rerun-io/rerun/issues/6638
* Related? https://github.com/emilk/egui/issues/4563
This improves how an eframe canvas works inside of a larger web page,
and how it works when there are multiple eframe apps in the same page.
`eframe` will set `tabindex="0"` on the canvas automatically, making it
focusable.
It will also set `outline: none` on the CSS, so the focused canvas won't
have an ugly outline.
## Breaking changes
You may wanna add this to your `index.html` to give the canvas focus on
startup:
```js
document.getElementById("the_canvas_id").focus();
```
## Test setup
```sh
./scripts/build_demo_web.sh
./scripts/start_server.sh
open http://localhost:8888/multiple_apps.html
```
Then open the "Input Event History" and "Text Edit" windows
## Tested
* Chromium
* [x] drag-and-drop of files
* Test both when a `TextEdit` is focused and when it is not:
* [x] `Event::Key`
* [x] `Event::Text`
* [x] copy-cut-paste
* [x] Wheel scroll
* [x] `Event::PointerGone`
* [x] Mouse drag
* [x] Mouse click
* [x] Mouse right-click
* [x] Defocus all eframe canvas, and then start typing text
* [x] Firefox (all of the above)
* [x] Desktop Safari (all of the above)
* [x] Mobile Safari
## Future work (pre-existing issues)
* https://github.com/emilk/egui/issues/4723
* https://github.com/emilk/egui/issues/4724
* https://github.com/emilk/egui/issues/4725
* https://github.com/emilk/egui/issues/4726
`directories-next` was created because `directories` was not maintained
at the time. However, `directories` has gotten active maintainership
since, and it has received more updates than `directories-next`.
`directories` also has more recent downloads than its `next`
counterpart, so it might make sense to switch to it, to avoid
unnecessary duplicate dependencies, where a project depends transitively
on both `directories` and `directories-next`.
The main question is whether we depend on any specific behavior from
`directories-next`.
- Closes https://github.com/emilk/egui/issues/4060 - no longer aligned
to top
- Closes https://github.com/emilk/egui/issues/4479 - `canvas.style` is
not set anywhere anymore
- Closes https://github.com/emilk/egui/issues/2231 - same as #4060
- Closes https://github.com/emilk/egui/issues/3618 - there is now one
`<input>` per `eframe` app, and it's removed transitively by
`WebRunner::destroy -> AppRunner::drop -> TextAgent::drop`
This PR improves the text agent to make fewer assumptions about how
`egui` is embedded into the page:
- Text agent no longer sets the canvas position
- There is now a text agent for each instance of `WebRunner`
- The input element is now moved to the correct position, so the OS can
display the IME window in the correct place. Before it would typically
be outside of the viewport
The best way to test this is to build & server the web demo locally:
```
scripts/build_demo_web.sh && scripts/start_server.sh
```
Then open the EasyMark editor, and try using IME to input some emojis:
http://localhost:8888/#EasyMarkEditor
To open the emoji keyboard use:
- <kbd>win + .</kbd> on Windows
- <kbd>ctrl + cmd + space</kbd> on Mac
Tested on:
- [x] Windows
- [x] Linux
- [x] MacOS
- [x] Android
- [x] iOS
## Migration guide
The canvas no longer controls its own size/position on the page. This
means that those properties can now be controlled entirely via HTML and
CSS, and multiple separate `eframe` apps can coexist better on a single
page.
To match the old behavior, set the `canvas` width and height to 100% of
the `body` element:
```html
<html>
<body>
<canvas></canvas>
</body>
</html>
```
```css
/* remove default margins and use full viewport */
html, body {
margin: 0;
width: 100%;
height: 100%;
}
canvas {
/* match parent element size */
width: 100%;
height: 100%;
}
```
Note that there is no need to set `position: absolute`/`left: 50%;
transform: translateX(-50%)`/etc., and setting those properties may
poorly affect the sharpness of `egui`-rendered text.
Because `eframe` no longer updates the canvas style in any way, it also
means that on mobile, the canvas no longer collapses upwards to make
space for a mobile keyboard. This should be solved in other ways:
https://github.com/emilk/egui/issues/4572
The closure passed to `eframe::run_native` now returns a `Result`,
allowing you to return an error during app creation, which will be
returned to the caller of `run_native`.
This means you need to wrap your `Box::new(MyApp::new(…))` in an
`Ok(…)`.
* Closes https://github.com/emilk/egui/issues/4474
The closure is now stored in `WebRunner` and dropped in the next
`request_animation_frame` instead of being "leaked" via `forget`
---------
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
Currently, if the size of the canvas element changes independently of
the size of the browser window (e.g. due to its parent element
shrinking), then no repaints are scheduled.
This PR replaces the `resize` event with a `ResizeObserver`, which
ensures that _any_ resize of the canvas element (including those caused
by browser window resizes) trigger a repaint. The repaint is done
synchronously as part of the resize event, to reduce any potential
flickering.
The result seems to pass the rendering tests on most platform+browser
combinations. We tested:
- Chrome, Firefox, Safari on macOS
- Chrome, Firefox on Linux (ubuntu and arch, both running wayland)
- Chrome, Firefox on Windows
Firefox still has some antialiasing issues on Linux platforms, but this
antialiasing also happens on `master`, so this PR is not a regression
there.
The code setting `canvas.style.width` and `canvas.style.height` at the
start of `AppRunner::logic` was also removed - the canvas _display_ size
is now fully controlled by CSS, e.g. by setting `canvas { width: 100%;
height: 100%; }`.
The approach used here is described in
https://webglfundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html
Note: The only remaining place where egui updates the style of the
canvas it is rendering to is some of the IME/mobile input handling code.
Fixing that is out of scope for this PR, and will be done in a followup
PR.
This allows customizing the persistence path in NativeOptions.
Previously, persistence wouldn't work with android because
directories-next doesn't support android so eframe would just fail to
find a place where it could store its config.
* Closes#4098 (android users can now specify a path that works with
android, by e.g. using app_dirs2, which supports android)
<!--
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.
* The PR title is what ends up in the changelog, so make it descriptive!
* 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 clippy`.
* 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!
-->
Related to #3482
Not sure what the "best practice" is, to me it seems like one should
import from "the original location" if possible, but now it should at
least be possible to not re-export ahash without any breakage in the
egui code base (but possibly in projects using egui, so one should
probably deprecate it if one would like to go that path). It also seems
like epaint re-exports ahash.
For integrations: just emit `egui::Event::MouseWheel` (like before).
egui will interpret that as zoom or pan.
On the way towards https://github.com/emilk/egui/issues/4401
<!--
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.
* The PR title is what ends up in the changelog, so make it descriptive!
* 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 clippy`.
* 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/4442>
* Refactors active monitor detection so it can be called from multiple
locations.
Compare this gif to the one on the issue report.

### Investigation notes
- [`WindowSettings.inner_position_pixels` and
`WindowSettings.outer_position_pixels`](https://github.com/emilk/egui/blob/master/crates/egui-winit/src/window_settings.rs#L8-L12)
are stored in physical/pixel coordinates.
- `ViewportBuilder::with_position` expects to be passed a position in
_logical_ coordinates.
- Prior to this PR, the position was being passed from `WindowSettings`
to `with_position` [without any
scaling](https://github.com/emilk/egui/blob/master/crates/egui-winit/src/window_settings.rs#L61-L68).
This was the root cause of the issue.
- The fix is to first convert the position to logical coordinates,
respecting the scaling factor of the active monitor. This requires us to
first determine the active monitor, so I factored out some of the logic
in
[`clamp_pos_to_monitor`](https://github.com/emilk/egui/blob/master/crates/egui-winit/src/window_settings.rs#L130)
to find the active monitor.
* Related #4337
* Closes#4409
Fix : In Windows, the 'egui_demo_app' screen does not appear After the
#4337 update.
---------
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
Motivation: I want to replace `cargo-cranky` with workspace lints, first
available in Rust 1.74.
However, `cargo doc` would hange on `wgpu` and `wgpu-core` on 1.74 and
1.75… so now we're on 1.76.
I think this is fine - when 1.78 is released next week we're still two
versions behind the bleeding edge.
…and the branch name is just wrong 🤦
These are a replacement to the `objc` and `cocoa` crates.
This PR prevents:
- An extra copy when creating `NSData`
- A memory leak when creating `NSImage`
- A memory leak when creating `NSString`
And is generally a readability improvement.
Note that we define `NSApp` manually for now, the implementation in
`objc2-app-kit` is currently suboptimal and wouldn't allow you to check
whether the NSApplication has been created or not.
Related: https://github.com/emilk/egui/issues/4219, this should nicely
coincide with the Winit `0.30` release.
---------
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
Added clamp_size_to_monitor_size field on ViewportBuilder, which means
whether clamp the window's size to monitor's size. (default to `true`)
* Closes https://github.com/emilk/egui/issues/3389
### simple example
```rust
pub struct MyApp {}
impl MyApp {
pub fn new() -> MyApp {
MyApp {}
}
}
impl eframe::App for MyApp {
fn update(&mut self, ctx: &Context, frame: &mut eframe::Frame) {
egui::CentralPanel::default()
.frame(Frame::none().fill(Color32::DARK_GRAY))
.show(ctx, |ui| {
if ctx.input(|i| i.key_pressed(Key::Escape)) {
ctx.send_viewport_cmd(ViewportCommand::Close);
}
});
}
}
pub fn main() {
let option = eframe::NativeOptions {
viewport: ViewportBuilder::default()
.with_position([10.0, 10.0])
.with_inner_size([3000.0, 2000.0])
.with_clamp_size_to_monitor_size(false),
..Default::default()
};
eframe::run_native(
"a large window app",
option,
Box::new(|ctx| Box::new(MyApp::new())),
).unwrap();
}
```
It works on my windows (with 3 monitors), but I don't have a test
environment for macos
---------
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
* Closes#4354
Fix: can't repeat input chinese words
AND
For Windows :
ImeEnable
ImeDisable
---------
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
### Motivation
We want to offer our users a context menu with `Cut`, `Copy` and `Paste`
actions. `Paste` is not possible out of the box.
### Changes
This PR adds `ViewportCommand::RequestCut`,
`ViewportCommand::RequestCopy` and `ViewportCommand::RequestPaste`. They
are routed and handled after the example of
`ViewportCommand::Screenshot` and result in the same code being executed
as when the user uses `CTRL+V` style keyboard commands.
### Reasoning
In our last release we used an instance of
`egui_winit:📋:Clipboard` in order to get the `Paste`
functionality.
However Linux users on Wayland complained about broken clipboard
interaction (https://github.com/mikedilger/gossip/issues/617). After a
while of digging I could not find the issue although I have found
references to problems with multiple clipboards per handle before
(https://gitlab.gnome.org/GNOME/mutter/-/issues/1250) but I compared
mutter with weston and the problem occured on both.
So to solve this I set out to extend egui to access the clipboard
instance already present in egui_winit. Since there was no trivial way
to reach the instance of `egui_winit::State` I felt the best approach
was to follow the logic of the new `ViewportCommand::Screenshot`.
### Variations
It could make sense to make the introduced `enum ActionRequested` a part
of crates/egui/src/viewport.rs and to then wrap them into one single
`ViewportCommand::ActionRequest(ActionRequested)`.
### Example
```Rust
let mut text = String::new();
let response = ui.text_edit_singleline(&mut text);
if ui.button("Paste").clicked() {
response.request_focus();
ui.ctx().send_viewport_cmd(ViewportCommand::RequestPaste);
}
```
---------
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
* Closes https://github.com/emilk/egui/issues/4241
I would love some more testers of this.
I'm not sure if we really need the round-to-even code, but I'm hesitant
to out-right revert https://github.com/emilk/egui/pull/151 when I cannot
reproduce its problem. Keeping it seems quite safe though.
---
# Testing
Checkout the branch and run:
* `./scripts/start_server.sh`
* `./scripts/build_demo_web.sh` and then open
`http://localhost:8888/index.html#Rendering`
* `./scripts/build_demo_web.sh --wgpu` and then open
`http://localhost:8888/index.html#Rendering`
Check the "Rendering test" that the squares in the pixel alignment test
are perfectly sharp, like this:
<img width="576" alt="Screenshot 2024-04-01 at 13 27 20"
src="https://github.com/emilk/egui/assets/1148717/fb6c4824-9e25-4304-bc0c-3c50fbd44a52">
If it looks something like this, something is WRONG:
<img width="488" alt="Screenshot 2024-04-01 at 13 29 07"
src="https://github.com/emilk/egui/assets/1148717/04bd93ff-2108-40c5-95f6-76e3bcb9cd7f">
Please try it on different zoom levels in different browsers, and if
possible on different monitors with different native dpi scaling. Report
back the results!
### Mac
I have tested on a high-DPI Mac:
* Chromium (Brave): ✅ Can reproduce problem on `master`, and it's now
fixed
* Firefox: ✅ Can reproduce problem on `master`, and it's now fixed
* Safari: ❌ Can't get it to work; giving up for now
Before, when setting the `zoom_factor`, the website was already
enlarged, but the zoom was ignored when calculating the logical window
size and mouse position, causing an offset between the actual cursor and
selected elements. That is addressed here
---------
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
<!--
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.
* The PR title is what ends up in the changelog, so make it descriptive!
* 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/4243
---------
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
Previously, any frames in flight (`requestAnimationFrame`) on web were
not being cancelled (`cancelAnimationFrame`) when `WebRunner::destroy`
was called. If a user called `destroy`, then immediately removed the
canvas from the DOM, eframe could panic with a "failed to find (canvas)
element by id" error message.
This PR changes two things:
- The canvas element is directly referenced everywhere it's needed
instead of being looked up by `canvas_id`[^1]
- The RAF handle is stored in `WebRunner` and `cancelAnimationFrame` is
called on it inside of `WebRunner::destroy`[^2]
[^1]: The WebGL/WGPU backends were already holding onto the canvas (and
associated GPU context), so the change is just converting all the
`get_element_by_id` lookups to retrieve the canvas from the web runner
handle.
[^2]: There is only ever one frame in flight, so we store it directly as
a scalar field.
<!--
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.
* The PR title is what ends up in the changelog, so make it descriptive!
* 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/4144>
Update modifier state from web mouse events. This allows modifiers to be
correctly updated when the window is not in focus but the mouse is still
moving over the window.
# What's New
* eframe: Added `App::raw_input_hook` allows for the manipulation or
filtering of raw input events
A filter applied to raw input before [`Self::update`]
This allows for the manipulation or filtering of input events before
they are processed by egui.
This can be used to exclude specific keyboard shortcuts, mouse events,
etc.
Additionally, it can be used to add custom keyboard or mouse events
generated by a virtual keyboard.
* examples: Added an example to demonstrates how to implement a custom
virtual keyboard.
[eframe-custom-keypad.webm](https://github.com/emilk/egui/assets/1274171/a9dc8e34-2c35-4172-b7ef-41010b794fb8)
Raw mouse movement is unaccelerated and unclamped by screen boundaries,
and does not relate to any position on the screen.
It is useful in certain situations such as draggable values and 3D
cameras, where screen position does not matter.
https://github.com/emilk/egui/assets/1700581/1400e6a6-0573-41b9-99a1-a9cd305aa1a3
Added `Event::MouseMoved` for integrations to supply raw mouse movement.
Added `Response:drag_motion` to get the raw mouse movement, but will
fall back to delta in case the integration does not supply it.
Nothing should be breaking, but third-party integrations that can send
`Event::MouseMoved` should be updated to do so.
Based on #1614 but updated to the current version, and with better
fallback behaviour.
* Closes#1611
* Supersedes #1614
This should help slightly with CPU/GPU parallelism when vsync is on.
I also return the time spent on vsync, which can help users figure out
how much CPU wall-time was used on non-vsync stuff
Addition for <https://github.com/emilk/egui/pull/3847>
In previous one i only fixed crash occurring with Wgpu backend. This
fixes crash with Glow backend as well.
I only tested this change with android so most things i changed are
behind ```#[cfg(target_os = "android")]```.
Both fixes are dirty thought. As
<https://github.com/emilk/egui/pull/3172> says that "The root viewport
is the original viewport, and cannot be closed without closing the
application.". So they break rules i guess? But i can't think about
better solution for now.
Closes <https://github.com/emilk/egui/issues/3861>.
<!--
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!
-->
Added Viewport reinitialization and Window recreation for Android on
resume event.
Closes <https://github.com/emilk/egui/issues/3674>.
https://github.com/emilk/egui/assets/95014675/7bc51ea5-3f63-4422-b5a0-ce8291612982
<!--
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!
-->
Small spelling error - changed `Addintional` to `Additional`
I broke this in https://github.com/emilk/egui/pull/3665
For some reason, when using multiple viewports, the first "clear"
doesn't take.
I don't have time to investigate further, so I am adding a hack here
which:
* For single-viewport apps, the clear is done before `App::update` so
user can paint there
* For multi-viewport apps, the clear is done after `App::update`, so
that it works
This means painting in `App::update` won't work with multi-viewports.
Ideally, users should use paint callbacks instead of painting directly
in `App::update` anyways.
It required some ugly code in `egui-winit` in order to fix this without
breaking sem-ver (I want this to land in a 0.24.1 patch release). I'll
clean up in time for 0.25.
There is still a font rendering bug when using immediate viewports
across multiple viewports, but that's harder to fix.
We were using [`tts`](https://github.com/ndarilek/tts-rs) for the
web-only screen reader. This was overkill, to say the least. It is now
replaced with ten lines of `web-sys` calls.
This is a bug that can occur on Windows, previously it was fixed by only
showing the window after the first frame was rendered, but the bug
appeared again with egui 0.24.0. This commit fixes this bug by making
the window invisible on startup.
## Before

## After

<!--
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/3625.
* Follow-up to https://github.com/emilk/egui/pull/3621 and
https://github.com/emilk/egui/pull/3513
To work around a Safari limitation, we run the app logic in the event
handler of copy, cut, and mouse up and down.
Previously the output of that frame was discarded, but in this PR it is
now saved to be used in the next requestAnimationFrame.
The result is noticeable more distinct clicks on buttons (one more frame
of highlight)
Bonus: also fix auto-save of a sleeping web app
* 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.
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.
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>.
* 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)
* eframe README: explain how to enable copy/paste
* Implement Debug for a couple of structs
* Code cleanup
* Better docs
* profile ron serialization
* CI: Allow "exclude from changelog" as the only label
* Give credit to recent big-time contributors in the main README.md
* Better named profiling scopes
* Document everything in memory.rs
* Better doc-strings
* Add a section about dependencies to the main README.md
* Improve egui_extras docs
* fix typos
* Remove calls to `set_ime_allowed`
* Allow IME if `text_cursor_pos` is `Some`
* Only call `Window::set_ime_allowed` when necessary
* allow_ime doesn't need to be atomic
* Remove unused imports
* Fix assignment
When exiting my application, it would freeze for around a minute waiting to finish writing ca. 40MB to file storage.
Some quick profiling revealed that it was spending all that time in `File::write`,
presumably making a large number of system calls writing tiny amounts of data at a time.
We can avoid this by buffering writes using a `BufWriter`, just like we already do with `BufReader`.
With this change, my application takes around 1-2 seconds to exit, with the majority of that time spent serializing `ron`.
I'm sure there are further potential performance improvements there, but this is already an order of magnitude or two better.
I also fixed the call to `log::warn!`, which was using incorrect formatting syntax and not including the error message.
* Add puffin profile scopes to the startup and running of eframe
* puffin_profiler example: start puffin right away
* cargo format let-else statements
* More profile scopes
* Add some `#[inline]`
* Standardize puffin profile scope definitions
* standardize again
* Silence warning when puffin is disabled
* Silence a few clippy warnings
* Use named threads
* Remove some deprecated functions
* Document Context and Ui fully
* Use `parking_lot::Mutex` in `eframe`
* Expand clippy.toml files
* build fix
* Get a reference to `IntegrationInfo`
* Add doc comment
* Change `info` to return a reference
* Clone integration info
* Remove `&`
* Clone integration info in another place
* Fix the app only taking up half the screen size on iPad
* Fix request_repaint not working on iOS
* Always use run_and_exit on iOS since run_and_return is not supported by winit on iOS right now.
* Fix typo
* Fix eframe glow on ios
* Handle more cases
We had a bunch of `cfg!(windows)` and `cfg!(macos)` which should
have been `cfg!(target_os = "windows")`.
I wonder what the effects of this PR will be fore Windows 😬
In `0.22.0`, `eframe::start_web` has been replaced with `eframe::WebRunner`, which also installs a nice panic hook (no need for `console_error_panic_hook`).
A window may not always be available and may have already been closed by the time an eframe app is closing. An example of this is Android, where the main activity window may have been stopped or discarded because the app is no longer in the foreground, and then the user decides to close your app without resuming it using the multitasking view.
In this case, skip the window persistence step if it does not exist anymore by the time we are saving the persistence data. Currently eframe will panic with `winit window doesn't exist` instead.
* Triage for GL backend
* And cargo-fmt
* Changelog update with PR and issue
* Update crates/eframe/src/epi/mod.rs
Co-authored-by: Andreas Reich <r_andreas2@web.de>
* Update crates/egui-wgpu/src/winit.rs
Co-authored-by: Andreas Reich <r_andreas2@web.de>
* Add "supports_screenshot" to surface state
* Cranky fix
* fmt
---------
Co-authored-by: Andreas Reich <r_andreas2@web.de>
* Expose raw window and display handles in eframe
* Ensure that no one implements `Clone` in the future
* Cleanup
---------
Co-authored-by: Matti Virkkunen <mvirkkunen@gmail.com>
The approach of #1889 may remove observers in a view
twice, which produces the Obj-C Exception:
Cannot remove an observer <...> for the key path
"nextResponder" from <WinitView ...> because
it is not registered as an observer.
The above message can only be seen when attaching the
application to debugger. Users normally see:
[1] *** trace trap cargo run
This commit fixes it by only running `event_loop.run_return()`
twice on Windows. Besides:
* We have set `ControlFlow::Exit` on `Event::LoopDestroyed`,
`EventResult::Exit` and on error; therefore, it is safe
to not calling `set_exit()`.
* This commit also fix the persistence function in macOS.
It can't store the content in Memory due to this exception.
Fixed: #2768 (eframe: "App quit unexpectedly" on macOS)
Signed-off-by: pan93412 <pan93412@gmail.com>
* Add an optional app_id field to eframe's NativeOptions (#1600).
This is used in the window builder to set the application ID, which is e.g. important for a proper configuration in `.desktop` files under Wayland.
When no application ID is explicitly set, it defaults to the title of the window.
* Only enable NativeOptions::app_id under Linux.
The wayland feature is not sufficent as constraint and it won't compile e.g. under Windows.
While Wayland could also be used on other Unix-Systems like FreeBSD, this would probably need some specific testing. Winit uses the following definition as "wayland_platform" and on which the required packages are available:
> wayland_platform: { all(feature = "wayland", free_unix, not(wasm), not(redox)) },
* Do not use title as default application ID under Wayland.
The title might be used to also communicate state (opened file, ...) to the user and this might have unforeseen consequences for the application ID. It seems to be better to use the old behavior of not setting an application ID in this case. Also add an example on how to set the application ID in the documentation.
* Avoid as_deref(), which was a left-over of a previous version
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
---------
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
* Detect panics during initialization and show them to the user
* PanicHandler now also logs the panics
* Add example of how to call into your app from JS
* Refactor: break out AppRunner and AppRunnerRef to own files
* Hide AppRunner
* Simplify user code
* AppRunnerRef -> WebRunner
* Better docs
* Don't paint until first animation frame
* Update multiple_apps.html
* Update web demo
* Cleanup and fixes
* left-align panic message in html
* Add Pointer::is_decidedly_dragging and could_any_button_be_click
This allows users to distinguish between click and drags
while they are not yet done.
* Fix warning in eframe
* fix typo
* Log warning instead of error when failing to decode RON in storage
* New web demo
* Clean up some warn/error logging
* Avoid deadlock that could happen on crash
* Log errors using console.warn, because console.error can cause crashes
* Use patched version of wasm-bindgen-cli, allowing >2GB memory
* New web demo