Skip to content

Commit

Permalink
Allow closing windows at runtime (#3575)
Browse files Browse the repository at this point in the history
# Objective

Fixes #3180, builds from bevyengine/bevy#2898

## Solution

Support requesting a window to be closed and closing a window in `bevy_window`, and handle this in `bevy_winit`.

This is a stopgap until we move to windows as entites, which I'm sure I'll get around to eventually.

## Changelog

### Added

- `Window::close` to allow closing windows.
- `WindowClosed` to allow reacting to windows being closed.

### Changed

Replaced `bevy::system::exit_on_esc_system` with `bevy::window::close_on_esc`.

## Fixed

The app no longer exits when any window is closed. This difference is only observable when there are multiple windows. 

## Migration Guide

`bevy::input::system::exit_on_esc_system` has been removed. Use `bevy::window::close_on_esc` instead.
`CloseWindow` has been removed. Use `Window::close` instead.
The `Close` variant has been added to `WindowCommand`. Handle this by closing the relevant window.
  • Loading branch information
DJMcNab committed May 5, 2022
1 parent fb0aae7 commit 9a70f7e
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 15 deletions.
48 changes: 33 additions & 15 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,38 @@ mod converters;
mod winit_config;
mod winit_windows;

use bevy_input::{
keyboard::KeyboardInput,
mouse::{MouseButtonInput, MouseMotion, MouseScrollUnit, MouseWheel},
touch::TouchInput,
};
pub use winit_config::*;
pub use winit_windows::*;

use bevy_app::{App, AppExit, CoreStage, Plugin};
use bevy_ecs::prelude::*;
use bevy_ecs::{
event::{EventWriter, Events, ManualEventReader},
schedule::ParallelSystemDescriptorCoercion,
system::{NonSend, ResMut},
event::{Events, ManualEventReader},
world::World,
};
use bevy_input::{
keyboard::KeyboardInput,
mouse::{MouseButtonInput, MouseMotion, MouseScrollUnit, MouseWheel},
touch::TouchInput,
};
use bevy_math::{ivec2, DVec2, Vec2};
use bevy_utils::{
tracing::{error, trace, warn},
tracing::{error, info, trace, warn},
Instant,
};
use bevy_window::{
CreateWindow, CursorEntered, CursorLeft, CursorMoved, FileDragAndDrop, ModifiesWindows,
ReceivedCharacter, RequestRedraw, WindowBackendScaleFactorChanged, WindowCloseRequested,
WindowCreated, WindowFocused, WindowMoved, WindowResized, WindowScaleFactorChanged, Windows,
WindowClosed, WindowCreated, WindowFocused, WindowMoved, WindowResized,
WindowScaleFactorChanged, Windows,
};

use winit::{
dpi::PhysicalPosition,
dpi::{LogicalSize, PhysicalPosition},
event::{self, DeviceEvent, Event, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
};

use winit::dpi::LogicalSize;

#[derive(Default)]
pub struct WinitPlugin;

Expand All @@ -51,10 +50,12 @@ impl Plugin for WinitPlugin {
}

fn change_window(
winit_windows: NonSend<WinitWindows>,
mut winit_windows: NonSendMut<WinitWindows>,
mut windows: ResMut<Windows>,
mut window_dpi_changed_events: EventWriter<WindowScaleFactorChanged>,
mut window_close_events: EventWriter<WindowClosed>,
) {
let mut removed_windows = vec![];
for bevy_window in windows.iter_mut() {
let id = bevy_window.id();
for command in bevy_window.drain_commands() {
Expand Down Expand Up @@ -166,9 +167,25 @@ fn change_window(
window.set_max_inner_size(Some(max_inner_size));
}
}
bevy_window::WindowCommand::Close => {
// Since we have borrowed `windows` to iterate through them, we can't remove the window from it.
// Add the removal requests to a queue to solve this
removed_windows.push(id);
// No need to run any further commands - this drops the rest of the commands, although the `bevy_window::Window` will be dropped later anyway
break;
}
}
}
}
if !removed_windows.is_empty() {
for id in removed_windows {
// Close the OS window. (The `Drop` impl actually closes the window)
let _ = winit_windows.remove_window(id);
// Clean up our own data structures
windows.remove(id);
window_close_events.send(WindowClosed { id });
}
}
}

fn run<F>(event_loop: EventLoop<()>, event_handler: F) -> !
Expand Down Expand Up @@ -316,7 +333,8 @@ pub fn winit_runner_with(mut app: App) {
let window = if let Some(window) = windows.get_mut(window_id) {
window
} else {
warn!("Skipped event for unknown Window Id {:?}", winit_window_id);
// If we're here, this window was previously opened
info!("Skipped event for closed window: {:?}", window_id);
return;
};
winit_state.low_power_event = true;
Expand Down
6 changes: 6 additions & 0 deletions src/winit_windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,12 @@ impl WinitWindows {
pub fn get_window_id(&self, id: winit::window::WindowId) -> Option<WindowId> {
self.winit_to_window_id.get(&id).cloned()
}

pub fn remove_window(&mut self, id: WindowId) -> Option<winit::window::Window> {
let winit_id = self.window_id_to_winit.remove(&id)?;
// Don't remove from winit_to_window_id, to track that we used to know about this winit window
self.windows.remove(&winit_id)
}
}

pub fn get_fitting_videomode(
Expand Down

0 comments on commit 9a70f7e

Please sign in to comment.