Skip to content

Commit

Permalink
eframe: several windows in series (#1919)
Browse files Browse the repository at this point in the history
* Add example of opening several eframe windows in series

* Reuse the same winit event loop

* Ignore events to the wrong window

* Run run_return again
  • Loading branch information
emilk authored Aug 15, 2022
1 parent 48e7f21 commit 9c58f12
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 22 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 1 addition & 13 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,7 @@ members = [
"emath",
"epaint",

"examples/confirm_exit",
"examples/custom_3d_glow",
"examples/custom_3d_three-d",
"examples/custom_font",
"examples/custom_font_style",
"examples/custom_window_frame",
"examples/download_image",
"examples/file_dialog",
"examples/hello_world",
"examples/puffin_profiler",
"examples/retained_image",
"examples/screenshot",
"examples/svg",
"examples/*",
]

[profile.dev]
Expand Down
52 changes: 43 additions & 9 deletions eframe/src/native/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,20 @@ trait WinitApp {
fn on_event(&mut self, event: winit::event::Event<'_, RequestRepaintEvent>) -> EventResult;
}

fn run_and_return(mut event_loop: EventLoop<RequestRepaintEvent>, mut winit_app: impl WinitApp) {
/// Access a thread-local event loop.
///
/// We reuse the event-loop so we can support closing and opening an eframe window
/// multiple times. This is just a limitation of winit.
fn with_event_loop(f: impl FnOnce(&mut EventLoop<RequestRepaintEvent>)) {
use std::cell::RefCell;
thread_local!(static EVENT_LOOP: RefCell<EventLoop<RequestRepaintEvent>> = RefCell::new(winit::event_loop::EventLoopBuilder::with_user_event().build()));

EVENT_LOOP.with(|event_loop| {
f(&mut *event_loop.borrow_mut());
});
}

fn run_and_return(event_loop: &mut EventLoop<RequestRepaintEvent>, mut winit_app: impl WinitApp) {
use winit::platform::run_return::EventLoopExtRunReturn as _;

tracing::debug!("event_loop.run_return");
Expand Down Expand Up @@ -100,6 +113,14 @@ fn run_and_return(mut event_loop: EventLoop<RequestRepaintEvent>, mut winit_app:
..
}) => EventResult::RepaintAsap,

winit::event::Event::WindowEvent { window_id, .. }
if window_id != winit_app.window().id() =>
{
// This can happen if we close a window, and then reopen a new one,
// or if we have multiple windows open.
EventResult::Wait
}

event => winit_app.on_event(event),
};

Expand Down Expand Up @@ -131,6 +152,13 @@ fn run_and_return(mut event_loop: EventLoop<RequestRepaintEvent>, mut winit_app:
tracing::debug!("eframe window closed");

winit_app.save_and_destroy();

drop(winit_app);

// Needed to clean the event_loop:
event_loop.run_return(|_, _, control_flow| {
control_flow.set_exit();
});
}

fn run_and_exit(
Expand Down Expand Up @@ -424,12 +452,15 @@ mod glow_integration {
native_options: &epi::NativeOptions,
app_creator: epi::AppCreator,
) {
let event_loop = glutin::event_loop::EventLoopBuilder::with_user_event().build();
let glow_eframe = GlowWinitApp::new(&event_loop, app_name, native_options, app_creator);

if native_options.run_and_return {
run_and_return(event_loop, glow_eframe);
with_event_loop(|event_loop| {
let glow_eframe =
GlowWinitApp::new(event_loop, app_name, native_options, app_creator);
run_and_return(event_loop, glow_eframe);
});
} else {
let event_loop = winit::event_loop::EventLoopBuilder::with_user_event().build();
let glow_eframe = GlowWinitApp::new(&event_loop, app_name, native_options, app_creator);
run_and_exit(event_loop, glow_eframe);
}
}
Expand Down Expand Up @@ -684,12 +715,15 @@ mod wgpu_integration {
native_options: &epi::NativeOptions,
app_creator: epi::AppCreator,
) {
let event_loop = winit::event_loop::EventLoopBuilder::with_user_event().build();
let wgpu_eframe = WgpuWinitApp::new(&event_loop, app_name, native_options, app_creator);

if native_options.run_and_return {
run_and_return(event_loop, wgpu_eframe);
with_event_loop(|event_loop| {
let wgpu_eframe =
WgpuWinitApp::new(event_loop, app_name, native_options, app_creator);
run_and_return(event_loop, wgpu_eframe);
});
} else {
let event_loop = winit::event_loop::EventLoopBuilder::with_user_event().build();
let wgpu_eframe = WgpuWinitApp::new(&event_loop, app_name, native_options, app_creator);
run_and_exit(event_loop, wgpu_eframe);
}
}
Expand Down
12 changes: 12 additions & 0 deletions examples/serial_windows/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "serial_windows"
version = "0.1.0"
authors = ["Emil Ernerfeldt <emil.ernerfeldt@gmail.com>"]
license = "MIT OR Apache-2.0"
edition = "2021"
rust-version = "1.61"
publish = false


[dependencies]
eframe = { path = "../../eframe" }
8 changes: 8 additions & 0 deletions examples/serial_windows/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Demonstrates how to open several windows after each other.

NOTE: this doesn't work on Mac due to <https://github.com/rust-windowing/winit/issues/2431>.
See also <https://github.com/emilk/egui/issues/1918>.

```sh
cargo run -p serial_windows
```
53 changes: 53 additions & 0 deletions examples/serial_windows/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release

use eframe::egui;

fn main() {
if cfg!(target_os = "macos") {
eprintln!("WARNING: this example does not work on Mac! See https://github.com/emilk/egui/issues/1918");
}

let options = eframe::NativeOptions {
run_and_return: true,
..Default::default()
};

eprintln!("Starting first window…");
eframe::run_native(
"First Window",
options.clone(),
Box::new(|_cc| Box::new(MyApp::default())),
);

std::thread::sleep(std::time::Duration::from_secs(2));

eprintln!("Starting second window…");
eframe::run_native(
"Second Window",
options.clone(),
Box::new(|_cc| Box::new(MyApp::default())),
);

std::thread::sleep(std::time::Duration::from_secs(2));

eprintln!("Starting third window…");
eframe::run_native(
"Third Window",
options,
Box::new(|_cc| Box::new(MyApp::default())),
);
}

#[derive(Default)]
struct MyApp {}

impl eframe::App for MyApp {
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
if ui.button("Close").clicked() {
eprintln!("Pressed Close button");
frame.quit();
}
});
}
}

0 comments on commit 9c58f12

Please sign in to comment.