Commit Graph

88 Commits

Author SHA1 Message Date
Jay Oster ea89c2935e
Android support for eframe (#5318)
<!--
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 test and 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!
-->

Android support is "almost there". This PR pushes it just a bit further
by allowing `eframe` to be used on Android. It works by smuggling the
`AndroidApp` required by `winit` through `NativeOptions`.

The example isn't great because it doesn't leave space on the display
for Android's top status bar or the lower navigation bar. I don't know
what to do about that, yet. This is as far as I've managed to get it
working.

Another problem is that the development environment setup is completely
awful for Android unless you happen to already be a full-time Android
developer with everything configured on your build host. As a Rustacean,
this makes me very sad.

I've had some luck moving all of that mess to a container, adapted from
https://github.com/SergioRibera/docker-rust-android. It takes care of
all of the build dependencies, Android SDK, and the `cargo-apk` patches
for bugs that I hit while getting the example to work on my device. (I
also had to install an adb driver on my host and downloaded the Android
platform-tools to get access to `adb`. An alternative is exposing the
USB device to Docker. On Windows hosts, that means [installing
`usbipd`](https://learn.microsoft.com/en-us/windows/wsl/connect-usb). A
second alternative is using an `mtp` client to upload the APK as a file
with USB file transfer enabled, then manually install it through the
device's file manager.)

I'm not including the docker stuff in this PR, but here are the files
and instructions for future reference (and it will probably simplify
manual testing and CI, FWIW!)

<details><summary><code>Dockerfile</code></summary>

```dockerfile
FROM rust:1.76.0-slim

# Variable arguments
ARG JAVA_VERSION=17
ARG NDK_VERSION=25.1.8937393
ARG BUILDTOOLS_VERSION=30.0.0
ARG PLATFORM_VERSION=android-30
ARG CLITOOLS_VERSION=8512546_latest

# Install Android requirements
RUN apt-get update -yqq && \
    apt-get install -y --no-install-recommends \
    libcurl4-openssl-dev libssl-dev pkg-config build-essential git python3 wget zip unzip openjdk-${JAVA_VERSION}-jdk && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# Install android targets
RUN rustup target add armv7-linux-androideabi aarch64-linux-android

# Install cargo-apk
RUN git clone -b fix/bin-targets-workspace-members https://github.com/parasyte/cargo-apk.git /tmp/cargo-apk && \
    cargo install --path /tmp/cargo-apk/cargo-apk

# Generate Environment Variables
ENV JAVA_VERSION=${JAVA_VERSION}
ENV ANDROID_HOME=/opt/Android
ENV NDK_HOME=/opt/Android/ndk/${NDK_VERSION}
ENV ANDROID_NDK_ROOT=${NDK_HOME}
ENV PATH=$PATH:${ANDROID_HOME}:${ANDROID_NDK_ROOT}:${ANDROID_HOME}/build-tools/${BUILDTOOLS_VERSION}:${ANDROID_HOME}/cmdline-tools/bin

# Install command line tools
RUN mkdir -p ${ANDROID_HOME}/cmdline-tools && \
    wget -qc "https://dl.google.com/android/repository/commandlinetools-linux-${CLITOOLS_VERSION}.zip" -P /tmp && \
    unzip -d ${ANDROID_HOME} /tmp/commandlinetools-linux-${CLITOOLS_VERSION}.zip && \
    rm -fr /tmp/commandlinetools-linux-${CLITOOLS_VERSION}.zip
# Install sdk requirements
RUN echo y | sdkmanager --sdk_root=${ANDROID_HOME} --install \
    "build-tools;${BUILDTOOLS_VERSION}" "ndk;${NDK_VERSION}" "platforms;${PLATFORM_VERSION}"

# Create APK keystore for debug profile
# Adapted from caa806283d/ndk-build/src/ndk.rs (L393-L423)
RUN keytool -genkey -v -keystore ${HOME}/.android/debug.keystore -storepass android -alias androiddebugkey \
    -keypass android -dname 'CN=Android Debug,O=Android,C=US' -keyalg RSA -keysize 2048 -validity 10000

# Cleanup
RUN rm -rf /tmp/*

WORKDIR /src

ENTRYPOINT [ "cargo", "apk", "build" ]
```
</details>

<details><summary><code>.dockerignore</code></summary>

```ignore
# Ignore everything, only the Dockerfile is needed to build the container
*
```
</details>

```sh
docker build -t rust-android:latest .
docker run --rm -it -v "$PWD:/src" rust-android:latest -p hello_android
adb install target/debug/apk/hello_android.apk
```

* Part of #2066
* [x] I have followed the instructions in the PR template
2024-12-12 19:24:26 +01:00
Cody Neiman 2cd3485dd4
Update MSRV from 1.76 to 1.77 (#5322)
<!--
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 test and 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!
-->

* [X] I have followed the instructions in the PR template

I am preparing a separate PR that adds support for JXL with `jxl-oxide`,
which is unlikely to be added to the `image` crate anytime soon (more
context will be provided in that PR).

`jxl-oxide` makes use of the
[`array::each_mut`](https://doc.rust-lang.org/stable/std/primitive.array.html#method.each_mut)
API which was stabilized in 1.77, which is the motivation for this MSRV
bump.

Rust 1.77 was officially released to stable on 21 March, 2024.
2024-10-30 09:06:34 +01:00
Emil Ernerfeldt 66076101e1
Add `Context::request_discard` (#5059)
* Closes https://github.com/emilk/egui/issues/4976
* Part of #4378 
* Implements parts of #843

### Background
Some widgets (like `Grid` and `Table`) needs to know the width of future
elements in order to properly size themselves. For instance, the width
of the first column of a grid may not be known until all rows of the
grid has been added, at which point it is too late. Therefore these
widgets store sizes from the previous frame. This leads to "first-frame
jitter", were the content is placed in the wrong place for one frame,
before being accurately laid out in subsequent frames.

### What
This PR adds the function `ctx.request_discard` which discards the
visual output and does another _pass_, i.e. calls the whole app UI code
once again (in eframe this means calling `App::update` again). This will
thus discard the shapes produced by the wrongly placed widgets, and
replace it with new shapes. Note that only the visual output is
discarded - all other output events are accumulated.

Calling `ctx.request_discard` should only be done in very rare
circumstances, e.g. when a `Grid` is first shown. Calling it every frame
will mean the UI code will become unnecessarily slow.

Two safe-guards are in place:

* `Options::max_passes` is by default 2, meaning egui will never do more
than 2 passes even if `request_discard` is called on every pass
* If multiple passes is done for multiple frames in a row, a warning
will be printed on the screen in debug builds:


![image](https://github.com/user-attachments/assets/c2c1e4a4-b7c9-4d7a-b3ad-abdd74bf449f)

### Breaking changes
A bunch of things that had "frame" in the name now has "pass" in them
instead:

* Functions called `begin_frame` and `end_frame` are now called
`begin_pass` and `end_pass`
* `FrameState` is now `PassState`
* etc


### TODO
* [x] Figure out good names for everything (`ctx.request_discard`)
* [x] Add API to query if we're gonna repeat this frame (to early-out
from expensive rendering)
* [x] Clear up naming confusion (pass vs frame) e.g. for `FrameState`
* [x] Figure out when to call this
* [x] Show warning on screen when there are several frames in a row with
multiple passes
* [x] Document
* [x] Default on or off?
* [x] Change `Context::frame_nr` name/docs
* [x] Rename `Context::begin_frame/end_frame` and deprecate the old ones
* [x] Test with Rerun
* [x] Document breaking changes
2024-09-13 14:20:51 +02:00
Tim Straubinger b9435541df
Allow non-`static` `eframe::App` lifetime (#5060)
I love egui! Thank you Emil <3

This request specifically enables an `eframe::App` which stores a
lifetime.

In general, I believe this is necessary because `eframe::App` currently
does not seem to provide a good place to allocate and then borrow from
long-lived data between `update()` calls. To attempt to borrow such
long-lived data from a field of the `App` itself would be to create a
self-referential struct. A hacky alternative is to allocate long-lived
data with `Box::leak`, but that's a code smell and would cause problems
if a program ever creates multiple Apps.

As a more specific motivating example, I am developing with the
[inkwell](https://github.com/TheDan64/inkwell/) crate which requires
creating a `inkwell::context::Context` instance which is then borrowed
from by a bazillion things with a dedicated `'ctx` lifetime. I need such
a `inkwell::context::Context` for the duration of my `eframe::App` but I
can't store it as a field of the app. The most natural solution to me is
to simply to lift the inkwell context outside of the App and borrow from
it, but that currently fails because the AppCreator implicitly has a
`'static` lifetime requirement due to the use of `dyn` trait objects.

Here is a simpler, self-contained motivating example adapted from the
current [hello world example](https://docs.rs/eframe/latest/eframe/):

```rust
use eframe::egui;

struct LongLivedThing {
    message: String,
}

fn main() {
    let long_lived_thing = LongLivedThing {
        message: "Hello World!".to_string(),
    };

    let native_options = eframe::NativeOptions::default();
    eframe::run_native(
        "My egui App",
        native_options,
        Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc, &long_lived_thing)))),
        //                                           ^^^^^^^^^^^^^^^^^
        //                                           BORROWING from long_lived_thing in App
    );
}

struct MyEguiApp<'a> {
    long_lived_thing: &'a LongLivedThing,
}

impl<'a> MyEguiApp<'a> {
    fn new(cc: &eframe::CreationContext<'_>, long_lived_thing: &'a LongLivedThing) -> Self {
        // Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_visuals.
        // Restore app state using cc.storage (requires the "persistence" feature).
        // Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use
        // for e.g. egui::PaintCallback.
        MyEguiApp { long_lived_thing }
    }
}

impl<'a> eframe::App for MyEguiApp<'a> {
    fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
        egui::CentralPanel::default().show(ctx, |ui| {
            ui.heading(&self.long_lived_thing.message);
        });
    }
}
```

This currently fails to compile with:
```plaintext
error[E0597]: `long_lived_thing` does not live long enough
  --> src/main.rs:16:55
   |
16 |         Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc, &long_lived_thing)))),
   |         ----------------------------------------------^^^^^^^^^^^^^^^^----
   |         |        |                                    |
   |         |        |                                    borrowed value does not live long enough
   |         |        value captured here
   |         cast requires that `long_lived_thing` is borrowed for `'static`
17 |     );
18 | }
   | - `long_lived_thing` dropped here while still borrowed
   |
   = note: due to object lifetime defaults, `Box<dyn for<'a, 'b> FnOnce(&'a CreationContext<'b>) -> Result<Box<dyn App>, Box<dyn std::error::Error + Send + Sync>>>` actually means `Box<(dyn for<'a, 'b> FnOnce(&'a CreationContext<'b>) -> Result<Box<dyn App>, Box<dyn std::error::Error + Send + Sync>> + 'static)>`
```

With the added lifetimes in this request, I'm able to compile and run
this as expected on Ubuntu + Wayland. I see the CI has been emailing me
about some build failures and I'll do what I can to address those.
Currently running the check.sh script as well.

This is intended to resolve https://github.com/emilk/egui/issues/2152

<!--
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 test and 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/2152
* [x] I have followed the instructions in the PR template
2024-09-03 09:29:19 +02:00
rustbasic 2a6a1302b8
Fix viewport not working when minimized (#5042)
Fix: The viewport stops working when the program is minimized.   

Fix: Logically, the weird parts have been normalized.
                                                               
**Issue :**
The viewport stops working when the program is minimized.
                         
* Related #3321
* Related #3877
* Related #3985
* Closes #3972
* Closes #4772
* Related #4832 
* Closes #4892
**Solution :**
When `request_redraw()` is performed in Minimized state, the occasional
screen tearing phenomenon has disappeared.
( Probably expected to be the effect of #4814 )
To address the issue of the `Immediate Viewport` not updating in
Minimized state, we can call `request_redraw()`.
2024-09-01 10:34:48 +02:00
lucasmerlin c9e00e50ad
Fix iOS build, and add iOS step to CI (#4898)
<!--
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 test and 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!
-->

This PR
- adds a pipeline to check the ios build
- removes the iOS WaitUntil workaround, which doesn't seem to be
necessary anymore after the winit update (and caused the build for iOS
to fail again because of a missing self
- ~removes a iOS workaround for window size which doesn't seem necessary
anymore~
Turns out it was still needed (but you need to actually restart the app
for the issue to show up, so I didn't catch it first)
- fixes some cargo check errors in run.rs

I've done all these changes in a single PR because otherwise the
pipeline doesn't run but I can also split them in separate PRs if that
makes it easier to review
2024-08-26 09:48:12 +02:00
Ardocrat dfbc7f0d19
Fix iOS compilation of eframe (#4851)
Fixed changed the name of function `WinitApp::get_window_winit_id` to
`WinitApp::window_id_from_viewport_id` and missed `egui::ViewportId`
import.
2024-07-31 10:18:18 +02:00
Arthur Brussee 6f2f006885
Upgrade winit to 0.30.2 (#4849)
* Closes https://github.com/emilk/egui/issues/1918
* Closes https://github.com/emilk/egui/issues/4437
* Closes https://github.com/emilk/egui/issues/4709
* [x] I have followed the instructions in the PR template

Hiya,

I need new winit for a specific fix for a android_native_actvity. There
are already two PRs, but both don't seem to have a lot of movement, or
are entirely complete:

https://github.com/emilk/egui/pull/4466
Seems to have gone stale & is missing some bits.

https://github.com/emilk/egui/pull/4702
Also seems stale (if less so), and is missing a refactor to
run_on_demand. I also *think* the accesskit integration has a mistake
and can't be enabled. I've marked them as a co-author on this as I
started from this branch. (I think! Haven't done that on git before...).

Sorry for the wall of text but just dumping some details / thoughts
here:

- There's an issue with creating child windows in winit 0.30.1 and up on
macOS. The multiple_viewports, "create immediate viewport" example
crashes on anything later 0.30.1, with a stack overflow in unsafe code.
I've create [a winit
issue](https://github.com/rust-windowing/winit/issues/3800), it *might*
already be fixed in 0.31.0 but I can't test as 0.31 will likely require
another refactoring. For now I have just pinned things to 0.30.0 exatly.

- Winit has deprecated run_on_demand, instead requiring the
ApplicationHandler interface. In 0.31.0 run_on_demand is removed. I've
refactored both the integration and the WinitApp trait to follow this
pattern. I've left user_events a bit more opaque, as it seems 0.31.0 is
doing a rework of UserEvents too.

- I've used the new lazy init approach for access kit from this branch
https://github.com/mwcampbell/egui/tree/accesskit-new-lazy-init and
marked Matt as co-author, thanks Matt!

- There was very similair but not quite the same code for run_and_return
and run_and_exit. I've merged them, but looking at the github issues
graveyard it seems vey finnicky. I *hope* this is more robust than
before but it's a bit scary.

- when receiving new_events this also used to check the redraw timing
dictionary. That doesn't seem necesarry so left this out, but that is a
slight behaviour change?

- I have reeneabled serial_windows on macOS. I wondered whether it was
fixed after this PR and does seem to be! However, even before this PR it
seems to work, so maybe winit has sorted things out before that...
Windows also works fine now without the extra hack.

- I've done a very basic test of AccessKit on Windows and screen reader
seems ok but I'm really not knowleadgable enough to say whether it's all
good or not.

- I've tested cargo tests & all examples on Windows & macOS, and ran a
basic Android app. Still, testing native platforms is wel... hard so if
anyone can test linux / iOs / older mac versions / windows 10 would
probably be a good idea!

- For consistencys sake I've made all event like functions in WinitApp
return a `Result<EventResult>`. There's quite a bit of Ok-wrapping now,
maybe too annoying? Not sure.

Thank you for having a look!

# Tested on
* [x] macOS
* [x] Windows
* [x] Wayland (thanks [SiebenCorgie](https://github.com/SiebenCorgie))
* [x] X11 (thanks
[crumblingstatue](https://github.com/crumblingstatue)!,
[SiebenCorgie](https://github.com/SiebenCorgie))


# TODO
* [x] Fix "follow system theme" not working on initial startup (winit
issue, pinning to 0.30.2 for now).
* [x] Fix `request_repaint_after`

---------

Co-authored-by: mwcampbell <mattcampbell@pobox.com>
Co-authored-by: j-axa <josef.axa@gmail.com>
Co-authored-by: DataTriny <datatriny@gmail.com>
Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2024-07-31 09:43:16 +02:00
Emil Ernerfeldt 10571e9da5
`eframe::Result` is now short for `eframe::Result<()>` (#4706) 2024-06-25 13:31:42 +02:00
Oscar Gustafsson cd45d18615
Do no use the ahash reimport (#4504)
<!--
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.
2024-05-27 16:24:50 +02:00
Emil Ernerfeldt cee790681d
Update to Rust 1.76 (#4411)
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 🤦
2024-04-25 15:51:01 +02:00
rustbasic 68eb3db648
Fix high CPU usage on Windows when app is minimized (#3985)
patch #3982
2024-02-06 13:24:49 +01:00
rustbasic e3cfcf7d2e
eframe: don't call `App::update` on minimized windows (#3877)
* Closes #3321
2024-01-25 10:15:38 +01:00
Emil Ernerfeldt 76ec982958 Lower some logging from debug -> trace 2024-01-19 13:44:04 +01:00
Fredrik Fornwall 8e5959d55d
Update to winit 0.29 (#3649)
* Closes https://github.com/emilk/egui/issues/3542
* Closes https://github.com/emilk/egui/issues/2977
* Closes https://github.com/emilk/egui/issues/3303

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2023-12-18 14:53:14 +01:00
Emil Ernerfeldt 44ff29b012
Fix closing of viewports (#3591)
This ensures the closed viewport gets a close-event, and that it and the
parent viewport gets repainting, allowing the event to be registered.
2023-11-20 17:43:40 +01:00
Emil Ernerfeldt 30ee478caf
Fix egui-wgpu performance regression (#3580)
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`
2023-11-19 21:14:13 +01:00
Emil Ernerfeldt 960b01b67a
Refactor: move code around in `eframe` (#3575)
`run.rs` was getting way too long and complex.
2023-11-19 12:12:43 +01:00
Emil Ernerfeldt a23b959fd4
eframe: Remove warm-starting (#3574)
This was an ill-supported feature with little benefit or use.
2023-11-19 11:21:38 +01:00
Emil Ernerfeldt 39e60e367f
Use `egui::ViewportBuilder` in `eframe::NativeOptions` (#3572)
* 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()
 };
```
2023-11-19 11:08:47 +01:00
Emil Ernerfeldt 1571027556
Replace `eframe::Frame` commands and `WindowInfo` with egui (#3564)
* 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.
2023-11-18 19:27:53 +01:00
Konkitoman 83aa3109d3
Multiple viewports/windows (#3172)
* 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>
2023-11-16 11:25:05 +01:00
Emil Ernerfeldt 9a947e5547 Final image API doc tweaks 2023-09-27 16:40:26 +02:00
Simon 4986b35701
Add `NativeOptions::window_builder` for more customization (#3390)
* added a window builder hook for more customization

* `EFrame` -> `eframe`

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
2023-09-27 08:52:49 +02:00
Emil Ernerfeldt fdd493d48f
Misc cleanup (#3381)
* 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
2023-09-24 09:32:31 +02:00
Barugon c07394b576
Only show on-screen-keyboard and IME when editing text (#3362)
* 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
2023-09-19 14:14:42 +02:00
Emil Ernerfeldt 4b5146d35d
Add more profiling scopes (#3332) 2023-09-13 19:32:19 +02:00
Emil Ernerfeldt fc3bddd0cf
Add more puffin profile scopes to `eframe` (#3330)
* 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
2023-09-13 09:00:38 +02:00
Emil Ernerfeldt 67168be069
Improve clippy, and add more docs (#3306)
* 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
2023-09-05 14:11:22 +02:00
Barugon 1b8e8cb38e
`eframe::Frame::info` returns a reference (#3301)
* Get a reference to `IntegrationInfo`

* Add doc comment

* Change `info` to return a reference

* Clone integration info

* Remove `&`

* Clone integration info in another place
2023-09-05 10:43:39 +02:00
Emil Ernerfeldt 5f742b9aba
Improve documentation of `eframe`, especially for wasm32 (#3295)
* Improve documentation of `eframe`, especially for wasm32

* remove dead code

* fix
2023-09-04 09:55:47 +02:00
lucasmerlin 461328f54d
Fix iOS support in `eframe` (#3241)
* 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
2023-08-22 14:35:18 +02:00
Emil Ernerfeldt 6633ecce64
Fix wrong detection of OS (#3238)
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 😬
2023-08-12 13:50:31 +02:00
Emil Ernerfeldt dd417cfc1a
eframe: Better restore Window position on Mac when on secondary monitor (#3239) 2023-08-11 16:25:22 +02:00
Emil Ernerfeldt 08fb447fb5
Increase MSRV to 1.67 (#3234)
* Bump MSRV to 1.67

* clippy fixes

* cargo clippy: inline format args

* Add `clippy::uninlined_format_args` to cranky lints

* Fix clippy on wasm

* More clippy fixes
2023-08-11 13:54:02 +02:00
Emil Ernerfeldt d568d9f5d0
Lint vertical spacing in the code (#3224)
* Lint vertical spacing in the code

* Add some vertical spacing for readability
2023-08-10 15:26:54 +02:00
Stephen M. Coakley 486cff8ac3
Fix panic with persistence without window (#3167)
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.
2023-08-09 12:42:43 +02:00
Matt Fellenz 65eecde244
Use cfg attribute (#3113) 2023-07-26 19:07:05 +02:00
icedrocket 2a2529bb9c
eframe: sleep a bit only when minimized (#3139) 2023-07-10 10:56:24 +02:00
bilabila 9774d4af2c
eframe: fix android app quit on resume with glow backend (#3080) 2023-06-15 09:05:11 +02:00
τ 073f49682d
Expose Raw Window and Display Handles (#3073)
* 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>
2023-06-11 22:18:28 +02:00
pan93412 860dac69da
eframe: Only run_return twice on Windows (#3053)
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>
2023-06-05 14:57:21 +02:00
Emil Ernerfeldt 03bb89153b
eframe: Use `NativeOptions::AppId` for the persistance location (#3014)
* eframe: Use NativeOptions::AppId for the persistance location

* Fix doclinks

* Fix typo in docs

Closes https://github.com/emilk/egui/issues/3003
2023-05-23 08:38:14 +02:00
Andreas Reich f76eefb98d
[wgpu] Expose wgpu::Adapter via RenderState (#2954)
* [wgpu] Expose adapter, better errors, better docs, more code sharing, use stencil bits

* doc fix

* remove unnecessary unsafe

* handle missing framebuffer format

* better handling of renderstate creation in winit.rs

* remove unnecessary use directive
2023-04-25 17:42:13 +02:00
Emil Ernerfeldt 7f2de426d2
eframe: Set app icon on Mac and Windows (#2940)
* eframe: Set app icon on Mac and Windows

Also: correctly set window title on Mac when launching from
another process, e.g. python.

Co-authored-by: Wumpf <andreas@rerun.io>

* lint fixes

* Fix web build

* fix typo

* Try fix windows build

---------

Co-authored-by: Wumpf <andreas@rerun.io>
2023-04-20 15:47:04 +02:00
Emil Ernerfeldt 834e2e9f50
Fix: `request_repaint_after` works even when called from background thread (#2939)
* Refactor repaint logic

* request_repaint_after also fires the request_repaint callback

* Bug fixes

* Add test to egui_demo_app

* build_demo_web: build debug unless --release is specified

* Fix the web backend too

* Run special clippy for wasm, forbidding some types/methods

* Remove wasm_bindgen_check.sh

* Fix typos

* Revert "Remove wasm_bindgen_check.sh"

This reverts commit 92dde253446a6930f34f2fcf67f76bc11669ec3b.

* Only run cranky/clippy once
2023-04-20 10:56:52 +02:00
Emil Ernerfeldt d486c76a9f
Remove dark-light dependency (#2929)
* Remove dark-light dependency

Since https://github.com/emilk/egui/pull/2750 we now get what we need
straight from `winit`.

* fix warning

* Docstring formatting

* fix typo in check.sh
2023-04-18 21:52:48 +02:00
Emil Ernerfeldt 9c9a54ce36
Replace `tracing` with `log` (#2928)
* Replace tracing crate with log

It's just so much simpler to use

* Add `bacon wasm` job

* eframe: add a WebLogger for piping log events to the web console
2023-04-18 21:11:26 +02:00
Jozef Číž 33aa4d698f
Fix typos (#2866)
* Fix typos in comments

* Fix typos in demo texts

* Fix typos in code names

* Fix typos in cargos

* Fix typos in changelogs
2023-04-18 15:52:45 +02:00
Luke Jones 7c12bb692b
eframe: add call to save_and_destroy in Exit event of run_return (#2895)
`EventResult::Exit` is hit before `Event::LoopDestroyed` is, and due to
possibly some order of operations or drops the window is never destroyed
on Linux. Adding a call to `winit_app.save_and_destroy();` where
`EventResult::Exit` is checked solves this.

Closes #2892

Signed-off-by: Luke D. Jones <luke@ljones.dev>
2023-04-18 15:08:17 +02:00