bevy/examples/window/window_settings.rs
Aceeri ddfafab971 Windows as Entities (#5589)
# Objective

Fix https://github.com/bevyengine/bevy/issues/4530

- Make it easier to open/close/modify windows by setting them up as `Entity`s with a `Window` component.
- Make multiple windows very simple to set up. (just add a `Window` component to an entity and it should open)

## Solution

- Move all properties of window descriptor to ~components~ a component.
- Replace `WindowId` with `Entity`.
- ~Use change detection for components to update backend rather than events/commands. (The `CursorMoved`/`WindowResized`/... events are kept for user convenience.~
  Check each field individually to see what we need to update, events are still kept for user convenience.

---

## Changelog

- `WindowDescriptor` renamed to `Window`.
    - Width/height consolidated into a `WindowResolution` component.
    - Requesting maximization/minimization is done on the [`Window::state`] field.
- `WindowId` is now `Entity`.

## Migration Guide

- Replace `WindowDescriptor` with `Window`.
    - Change `width` and `height` fields in a `WindowResolution`, either by doing
      ```rust
      WindowResolution::new(width, height) // Explicitly
      // or using From<_> for tuples for convenience
      (1920., 1080.).into()
      ```
- Replace any `WindowCommand` code to just modify the `Window`'s fields directly  and creating/closing windows is now by spawning/despawning an entity with a `Window` component like so:
  ```rust
  let window = commands.spawn(Window { ... }).id(); // open window
  commands.entity(window).despawn(); // close window
  ```

## Unresolved
- ~How do we tell when a window is minimized by a user?~
  ~Currently using the `Resize(0, 0)` as an indicator of minimization.~
  No longer attempting to tell given how finnicky this was across platforms, now the user can only request that a window be maximized/minimized.
  
 ## Future work
 - Move `exit_on_close` functionality out from windowing and into app(?)
 - https://github.com/bevyengine/bevy/issues/5621
 - https://github.com/bevyengine/bevy/issues/7099
 - https://github.com/bevyengine/bevy/issues/7098


Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2023-01-19 00:38:28 +00:00

117 lines
3.7 KiB
Rust

//! Illustrates how to change window settings and shows how to affect
//! the mouse pointer in various ways.
use bevy::{
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
prelude::*,
window::{CursorGrabMode, PresentMode},
};
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "I am a window!".into(),
resolution: (500., 300.).into(),
present_mode: PresentMode::AutoVsync,
// Tells wasm to resize the window according to the available canvas
fit_canvas_to_parent: true,
..default()
}),
..default()
}))
.add_plugin(LogDiagnosticsPlugin::default())
.add_plugin(FrameTimeDiagnosticsPlugin)
.add_system(change_title)
.add_system(toggle_cursor)
.add_system(toggle_vsync)
.add_system(cycle_cursor_icon)
.add_system(toggle_always_on_top)
.run();
}
/// This system toggles the vsync mode when pressing the button V.
/// You'll see fps increase displayed in the console.
fn toggle_vsync(input: Res<Input<KeyCode>>, mut windows: Query<&mut Window>) {
if input.just_pressed(KeyCode::V) {
let mut window = windows.single_mut();
window.present_mode = if matches!(window.present_mode, PresentMode::AutoVsync) {
PresentMode::AutoNoVsync
} else {
PresentMode::AutoVsync
};
info!("PRESENT_MODE: {:?}", window.present_mode);
}
}
/// This system toggles whether the window is always on top when pressing the T button
/// You'll notice it won't be covered by other windows.
///
/// This feature only works on some platforms. Please check the
/// [documentation](https://docs.rs/bevy/latest/bevy/prelude/struct.WindowDescriptor.html#structfield.always_on_top)
/// for more details.
fn toggle_always_on_top(input: Res<Input<KeyCode>>, mut windows: Query<&mut Window>) {
if input.just_pressed(KeyCode::T) {
let mut window = windows.single_mut();
window.always_on_top = !window.always_on_top;
if window.always_on_top {
info!("LOCKING WINDOW ON TOP");
} else {
info!("UNLOCKING WINDOW");
}
}
}
/// This system will then change the title during execution
fn change_title(mut windows: Query<&mut Window>, time: Res<Time>) {
let mut window = windows.single_mut();
window.title = format!(
"Seconds since startup: {}",
time.elapsed().as_secs_f32().round()
);
}
fn toggle_cursor(mut windows: Query<&mut Window>, input: Res<Input<KeyCode>>) {
if input.just_pressed(KeyCode::Space) {
let mut window = windows.single_mut();
window.cursor.visible = !window.cursor.visible;
window.cursor.grab_mode = match window.cursor.grab_mode {
CursorGrabMode::None => CursorGrabMode::Locked,
CursorGrabMode::Locked | CursorGrabMode::Confined => CursorGrabMode::None,
};
}
}
/// This system cycles the cursor's icon through a small set of icons when clicking
fn cycle_cursor_icon(
mut windows: Query<&mut Window>,
input: Res<Input<MouseButton>>,
mut index: Local<usize>,
) {
let mut window = windows.single_mut();
const ICONS: &[CursorIcon] = &[
CursorIcon::Default,
CursorIcon::Hand,
CursorIcon::Wait,
CursorIcon::Text,
CursorIcon::Copy,
];
if input.just_pressed(MouseButton::Left) {
*index = (*index + 1) % ICONS.len();
} else if input.just_pressed(MouseButton::Right) {
*index = if *index == 0 {
ICONS.len() - 1
} else {
*index - 1
};
}
window.cursor.icon = ICONS[*index];
}