Skip to content

Commit

Permalink
Add support to bevy_window for closing windows
Browse files Browse the repository at this point in the history
Make the rest of bevy work with multiple windows

Remove exit_on_esc

Add suggested docs
  • Loading branch information
DJMcNab committed Feb 14, 2022
1 parent d8974e7 commit 1f835c1
Show file tree
Hide file tree
Showing 16 changed files with 148 additions and 51 deletions.
1 change: 0 additions & 1 deletion crates/bevy_input/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ pub mod gamepad;
mod input;
pub mod keyboard;
pub mod mouse;
pub mod system;
pub mod touch;

pub use axis::*;
Expand Down
20 changes: 0 additions & 20 deletions crates/bevy_input/src/system.rs

This file was deleted.

6 changes: 5 additions & 1 deletion crates/bevy_pbr/src/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,9 +371,13 @@ pub fn add_clusters(

pub fn update_clusters(windows: Res<Windows>, mut views: Query<(&Camera, &mut Clusters)>) {
for (camera, mut clusters) in views.iter_mut() {
// If the window doesn't exist, we won't render anything to it, so don't need to calculate the clusters for it
let window = match windows.get(camera.window) {
Some(window) => window,
_ => continue,
};
let is_orthographic = camera.projection_matrix.w_axis.w == 1.0;
let inverse_projection = camera.projection_matrix.inverse();
let window = windows.get(camera.window).unwrap();
let screen_size_u32 = UVec2::new(window.physical_width(), window.physical_height());
// Don't update clusters if screen size is 0.
if screen_size_u32.x == 0 || screen_size_u32.y == 0 {
Expand Down
11 changes: 9 additions & 2 deletions crates/bevy_render/src/view/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
use bevy_app::{App, Plugin};
use bevy_ecs::prelude::*;
use bevy_utils::{tracing::debug, HashMap, HashSet};
use bevy_window::{PresentMode, RawWindowHandleWrapper, WindowId, Windows};
use bevy_window::{PresentMode, RawWindowHandleWrapper, WindowClosed, WindowId, Windows};
use std::ops::{Deref, DerefMut};
use wgpu::TextureFormat;

Expand Down Expand Up @@ -67,7 +67,11 @@ impl DerefMut for ExtractedWindows {
}
}

fn extract_windows(mut render_world: ResMut<RenderWorld>, windows: Res<Windows>) {
fn extract_windows(
mut render_world: ResMut<RenderWorld>,
mut closed: EventReader<WindowClosed>,
windows: Res<Windows>,
) {
let mut extracted_windows = render_world.get_resource_mut::<ExtractedWindows>().unwrap();
for window in windows.iter() {
let (new_width, new_height) = (
Expand Down Expand Up @@ -105,6 +109,9 @@ fn extract_windows(mut render_world: ResMut<RenderWorld>, windows: Res<Windows>)
extracted_window.physical_height = new_height;
}
}
for closed_window in closed.iter() {
extracted_windows.remove(&closed_window.id);
}
}

#[derive(Default)]
Expand Down
3 changes: 3 additions & 0 deletions crates/bevy_window/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ keywords = ["bevy"]

[dependencies]
# bevy
bevy_ecs = { path = "../bevy_ecs", version = "0.6.0" }
# Used for close_on_esc
bevy_input = { path = "../bevy_input", version = "0.6.0" }
bevy_app = { path = "../bevy_app", version = "0.6.0" }
bevy_math = { path = "../bevy_math", version = "0.6.0" }
bevy_utils = { path = "../bevy_utils", version = "0.6.0" }
Expand Down
21 changes: 15 additions & 6 deletions crates/bevy_window/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@ pub struct CreateWindow {
pub descriptor: WindowDescriptor,
}

/// An event that indicates a window should be closed.
#[derive(Debug, Clone)]
pub struct CloseWindow {
pub id: WindowId,
}

/// An event that is sent whenever a new window is created.
#[derive(Debug, Clone)]
pub struct WindowCreated {
Expand All @@ -34,11 +28,26 @@ pub struct WindowCreated {

/// An event that is sent whenever a close was requested for a window. For example: when the "close"
/// button is pressed on a window.
///
/// By default, these events are handled by closing the corresponding [`crate::Window`].
/// To disable this behaviour, set `close_when_requested` on the [`crate::WindowPlugin`] to `false`
#[derive(Debug, Clone)]
pub struct WindowCloseRequested {
pub id: WindowId,
}

/// An event that is sent whenever a window is closed.
/// This will only be sent in response to the [`Window::close`] method.
///
/// By default, when no windows are open, the app will close.
/// To disable this behaviour, set `exit_on_all_closed` on the [`crate::WindowPlugin`] to `false`
///
/// [`Window::close`]: crate::Window::close
#[derive(Debug, Clone)]
pub struct WindowClosed {
pub id: WindowId,
}

#[derive(Debug, Clone)]
pub struct CursorMoved {
pub id: WindowId,
Expand Down
24 changes: 19 additions & 5 deletions crates/bevy_window/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,25 @@ use bevy_app::{prelude::*, Events};

pub struct WindowPlugin {
pub add_primary_window: bool,
pub exit_on_close: bool,
/// Whether to close the app when there are no open windows.
/// If disabling this, consider ensuring that you send a [`bevy_app::AppExit`] event yourself
/// when the app should exit; otherwise you will create headless processes, which would be
/// surprising for your users.
///
/// This setting controls whether this plugin adds [`exit_on_all_closed`]
pub exit_on_all_closed: bool,
/// Whether to close windows when they are requested to be closed (i.e. when the close button is pressed)
///
/// This setting controls whether this plugin adds [`close_when_requested`]
pub close_when_requested: bool,
}

impl Default for WindowPlugin {
fn default() -> Self {
WindowPlugin {
add_primary_window: true,
exit_on_close: true,
exit_on_all_closed: true,
close_when_requested: true,
}
}
}
Expand All @@ -41,8 +52,8 @@ impl Plugin for WindowPlugin {
app.add_event::<WindowResized>()
.add_event::<CreateWindow>()
.add_event::<WindowCreated>()
.add_event::<WindowClosed>()
.add_event::<WindowCloseRequested>()
.add_event::<CloseWindow>()
.add_event::<CursorMoved>()
.add_event::<CursorEntered>()
.add_event::<CursorLeft>()
Expand Down Expand Up @@ -70,8 +81,11 @@ impl Plugin for WindowPlugin {
});
}

if self.exit_on_close {
app.add_system(exit_on_window_close_system);
if self.exit_on_all_closed {
app.add_system(exit_on_all_closed);
}
if self.close_when_requested {
app.add_system(close_when_requested);
}
}
}
56 changes: 50 additions & 6 deletions crates/bevy_window/src/system.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,55 @@
use crate::WindowCloseRequested;
use crate::{Window, WindowCloseRequested, WindowFocused, WindowId, Windows};
use bevy_app::{AppExit, EventReader, EventWriter};
use bevy_ecs::prelude::*;
use bevy_input::{keyboard::KeyCode, Input};

pub fn exit_on_window_close_system(
mut app_exit_events: EventWriter<AppExit>,
mut window_close_requested_events: EventReader<WindowCloseRequested>,
) {
if window_close_requested_events.iter().next().is_some() {
/// Whether to exit the application when there are no open windows.
///
/// By default, this system is added by the [`crate::WindowPlugin`].
/// To disable this behaviour, set `close_when_requested` (on the [`crate::WindowPlugin`]) to `false`.
/// Please ensure that you read the caveats documented on that field.

pub fn exit_on_all_closed(mut app_exit_events: EventWriter<AppExit>, windows: Res<Windows>) {
if windows.iter().count() == 0 {
app_exit_events.send(AppExit);
}
}

/// Whether to close windows when they are requested to be closed (i.e. when the close button is pressed).
/// Not adding this system (without replacement) will lead to the close button having no effect.
///
/// By default, this system is added by the [`crate::WindowPlugin`].
/// To disable this behaviour, set `close_when_requested` (on the [`crate::WindowPlugin`]) to `false`
pub fn close_when_requested(
mut windows: ResMut<Windows>,
mut closed: EventReader<WindowCloseRequested>,
) {
for event in closed.iter() {
windows.get_mut(event.id).map(Window::close);
}
}

// TODO: Consider using the kbd tag here for escape: <kbd>esc</kbd>
// Currently, it isn't rendered by vscode's hover markdown provider (and the contents are lost)
/// Close the focused window whenever the escape key is pressed
///
/// This is useful for examples
pub fn close_on_esc(
mut focused: Local<Option<WindowId>>,
mut focused_events: EventReader<WindowFocused>,
mut windows: ResMut<Windows>,
input: Res<Input<KeyCode>>,
) {
// TODO: Track this in e.g. a resource to ensure consistent behaviour across similar systems
for event in focused_events.iter() {
*focused = event.focused.then(|| event.id);
}

if let Some(focused) = &*focused {
if input.just_pressed(KeyCode::Escape) {
if let Some(window) = windows.get_mut(*focused) {
window.close();
}
}
}
}
5 changes: 5 additions & 0 deletions crates/bevy_window/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ pub enum WindowCommand {
SetResizeConstraints {
resize_constraints: WindowResizeConstraints,
},
Close,
}

/// Defines the way a window is displayed
Expand Down Expand Up @@ -571,6 +572,10 @@ impl Window {
});
}

pub fn close(&mut self) {
self.command_queue.push(WindowCommand::Close);
}

#[inline]
pub fn drain_commands(&mut self) -> impl Iterator<Item = WindowCommand> + '_ {
self.command_queue.drain(..)
Expand Down
4 changes: 4 additions & 0 deletions crates/bevy_window/src/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,8 @@ impl Windows {
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Window> {
self.windows.values_mut()
}

pub fn remove(&mut self, id: WindowId) -> Option<Window> {
self.windows.remove(&id)
}
}
29 changes: 24 additions & 5 deletions crates/bevy_winit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ pub use winit_windows::*;
use bevy_app::{App, AppExit, CoreStage, Events, ManualEventReader, Plugin};
use bevy_ecs::{system::IntoExclusiveSystem, world::World};
use bevy_math::{ivec2, DVec2, Vec2};
use bevy_utils::tracing::{error, trace, warn};
use bevy_utils::tracing::{error, info, trace, warn};
use bevy_window::{
CreateWindow, CursorEntered, CursorLeft, CursorMoved, FileDragAndDrop, ReceivedCharacter,
WindowBackendScaleFactorChanged, WindowCloseRequested, WindowCreated, WindowFocused,
WindowMoved, WindowResized, WindowScaleFactorChanged, Windows,
WindowBackendScaleFactorChanged, WindowCloseRequested, WindowClosed, WindowCreated,
WindowFocused, WindowMoved, WindowResized, WindowScaleFactorChanged, Windows,
};
use winit::{
dpi::PhysicalPosition,
Expand All @@ -43,8 +43,9 @@ impl Plugin for WinitPlugin {

fn change_window(world: &mut World) {
let world = world.cell();
let winit_windows = world.get_resource::<WinitWindows>().unwrap();
let mut winit_windows = world.get_resource_mut::<WinitWindows>().unwrap();
let mut windows = world.get_resource_mut::<Windows>().unwrap();
let mut removed_windows = Vec::new();

for bevy_window in windows.iter_mut() {
let id = bevy_window.id();
Expand Down Expand Up @@ -160,9 +161,26 @@ fn change_window(world: &mut World) {
window.set_max_inner_size(Some(max_inner_size));
}
}
bevy_window::WindowCommand::Close => {
let window = winit_windows.remove_window(id);
// Close the window
drop(window);
// Since we borrow `windows` here to iterate through them, we can't mutate it here.
// Add it to the 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() {
let mut events = world.get_resource_mut::<Events<WindowClosed>>().unwrap();
for id in removed_windows {
windows.remove(id);
events.send(WindowClosed { id });
}
}
}

fn run<F>(event_loop: EventLoop<()>, event_handler: F) -> !
Expand Down Expand Up @@ -280,7 +298,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;
};

Expand Down
6 changes: 6 additions & 0 deletions crates/bevy_winit/src/winit_windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,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
2 changes: 1 addition & 1 deletion examples/2d/rotation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn main() {
.with_system(snap_to_player_system)
.with_system(rotate_to_player_system),
)
.add_system(bevy::input::system::exit_on_esc_system)
.add_system(bevy::window::close_on_esc)
.run();
}

Expand Down
3 changes: 2 additions & 1 deletion examples/game/alien_cake_addict.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use bevy::{
core::FixedTimestep, ecs::schedule::SystemSet, prelude::*, render::camera::CameraPlugin,
window::close_on_esc,
};
use rand::Rng;

Expand Down Expand Up @@ -33,7 +34,7 @@ fn main() {
.with_run_criteria(FixedTimestep::step(5.0))
.with_system(spawn_bonus),
)
.add_system(bevy::input::system::exit_on_esc_system)
.add_system(close_on_esc)
.run();
}

Expand Down
3 changes: 2 additions & 1 deletion examples/game/breakout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use bevy::{
core::FixedTimestep,
prelude::*,
sprite::collide_aabb::{collide, Collision},
window::close_on_esc,
};

/// An implementation of the classic game "Breakout"
Expand All @@ -20,7 +21,7 @@ fn main() {
.with_system(ball_movement_system),
)
.add_system(scoreboard_system)
.add_system(bevy::input::system::exit_on_esc_system)
.add_system(close_on_esc)
.run();
}

Expand Down
Loading

0 comments on commit 1f835c1

Please sign in to comment.