diff --git a/Cargo.lock b/Cargo.lock index d6e25b668617..7c779ccdd6db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3438,6 +3438,13 @@ dependencies = [ "syn", ] +[[package]] +name = "serial_windows" +version = "0.1.0" +dependencies = [ + "eframe", +] + [[package]] name = "servo-fontconfig" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index b05a4925b3fb..8dd656f77ed3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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] diff --git a/eframe/src/native/run.rs b/eframe/src/native/run.rs index b67271a6e421..217d2f720837 100644 --- a/eframe/src/native/run.rs +++ b/eframe/src/native/run.rs @@ -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, 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)) { + use std::cell::RefCell; + thread_local!(static EVENT_LOOP: RefCell> = 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, mut winit_app: impl WinitApp) { use winit::platform::run_return::EventLoopExtRunReturn as _; tracing::debug!("event_loop.run_return"); @@ -100,6 +113,14 @@ fn run_and_return(mut event_loop: EventLoop, 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), }; @@ -131,6 +152,13 @@ fn run_and_return(mut event_loop: EventLoop, 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( @@ -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); } } @@ -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); } } diff --git a/examples/serial_windows/Cargo.toml b/examples/serial_windows/Cargo.toml new file mode 100644 index 000000000000..a73860800592 --- /dev/null +++ b/examples/serial_windows/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "serial_windows" +version = "0.1.0" +authors = ["Emil Ernerfeldt "] +license = "MIT OR Apache-2.0" +edition = "2021" +rust-version = "1.61" +publish = false + + +[dependencies] +eframe = { path = "../../eframe" } diff --git a/examples/serial_windows/README.md b/examples/serial_windows/README.md new file mode 100644 index 000000000000..4941ce2702a6 --- /dev/null +++ b/examples/serial_windows/README.md @@ -0,0 +1,8 @@ +Demonstrates how to open several windows after each other. + +NOTE: this doesn't work on Mac due to . +See also . + +```sh +cargo run -p serial_windows +``` diff --git a/examples/serial_windows/src/main.rs b/examples/serial_windows/src/main.rs new file mode 100644 index 000000000000..79362ca16fbc --- /dev/null +++ b/examples/serial_windows/src/main.rs @@ -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(); + } + }); + } +}