diff --git a/crates/bevy_window/src/lib.rs b/crates/bevy_window/src/lib.rs index 3ae1c083c8f2e..853fae1516643 100644 --- a/crates/bevy_window/src/lib.rs +++ b/crates/bevy_window/src/lib.rs @@ -141,7 +141,8 @@ impl Plugin for WindowPlugin { .register_type::() .register_type::() .register_type::() - .register_type::(); + .register_type::() + .register_type::(); // Register `PathBuf` as it's used by `FileDragAndDrop` app.register_type::(); diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index b5135751845f1..80aedf62287ed 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -126,13 +126,21 @@ pub struct Window { /// Note: This does not stop the program from fullscreening/setting /// the size programmatically. pub resizable: bool, + /// Specifies which window control buttons should be enabled. + /// + /// ## Platform-specific + /// + /// **`iOS`**, **`Android`**, and the **`Web`** do not have window control buttons. + /// + /// On some **`Linux`** environments these values have no effect. + pub enabled_buttons: EnabledButtons, /// Should the window have decorations enabled? /// /// (Decorations are the minimize, maximize, and close buttons on desktop apps) /// - // ## Platform-specific - // - // **`iOS`**, **`Android`**, and the **`Web`** do not have decorations. + /// ## Platform-specific + /// + /// **`iOS`**, **`Android`**, and the **`Web`** do not have decorations. pub decorations: bool, /// Should the window be transparent? /// @@ -221,6 +229,7 @@ impl Default for Window { ime_enabled: Default::default(), ime_position: Default::default(), resizable: true, + enabled_buttons: Default::default(), decorations: true, transparent: false, focused: true, @@ -963,3 +972,42 @@ pub enum WindowTheme { /// Use the dark variant. Dark, } + +/// Specifies which [`Window`] control buttons should be enabled. +/// +/// ## Platform-specific +/// +/// **`iOS`**, **`Android`**, and the **`Web`** do not have window control buttons. +/// +/// On some **`Linux`** environments these values have no effect. +#[derive(Debug, Copy, Clone, PartialEq, Reflect)] +#[cfg_attr( + feature = "serialize", + derive(serde::Serialize, serde::Deserialize), + reflect(Serialize, Deserialize) +)] +#[reflect(Debug, PartialEq, Default)] +pub struct EnabledButtons { + /// Enables the functionality of the minimize button. + pub minimize: bool, + /// Enables the functionality of the maximize button. + /// + /// macOS note: When [`Window`] `resizable` member is set to `false` + /// the maximize button will be disabled regardless of this value. + /// Additionaly, when `resizable` is set to `true` the window will + /// be maximized when its bar is double-clicked regardless of whether + /// the maximize button is enabled or not. + pub maximize: bool, + /// Enables the functionality of the close button. + pub close: bool, +} + +impl Default for EnabledButtons { + fn default() -> Self { + Self { + minimize: true, + maximize: true, + close: true, + } + } +} diff --git a/crates/bevy_winit/src/converters.rs b/crates/bevy_winit/src/converters.rs index dba71438e053c..85302ecb0d68c 100644 --- a/crates/bevy_winit/src/converters.rs +++ b/crates/bevy_winit/src/converters.rs @@ -6,7 +6,7 @@ use bevy_input::{ ButtonState, }; use bevy_math::Vec2; -use bevy_window::{CursorIcon, WindowLevel, WindowTheme}; +use bevy_window::{CursorIcon, EnabledButtons, WindowLevel, WindowTheme}; pub fn convert_keyboard_input( keyboard_input: &winit::event::KeyboardInput, @@ -293,3 +293,17 @@ pub fn convert_window_theme(theme: WindowTheme) -> winit::window::Theme { WindowTheme::Dark => winit::window::Theme::Dark, } } + +pub fn convert_enabled_buttons(enabled_buttons: EnabledButtons) -> winit::window::WindowButtons { + let mut window_buttons = winit::window::WindowButtons::empty(); + if enabled_buttons.minimize { + window_buttons.insert(winit::window::WindowButtons::MINIMIZE); + } + if enabled_buttons.maximize { + window_buttons.insert(winit::window::WindowButtons::MAXIMIZE); + } + if enabled_buttons.close { + window_buttons.insert(winit::window::WindowButtons::CLOSE); + } + window_buttons +} diff --git a/crates/bevy_winit/src/system.rs b/crates/bevy_winit/src/system.rs index 92a3ec2ca96b0..a39aa4d560de5 100644 --- a/crates/bevy_winit/src/system.rs +++ b/crates/bevy_winit/src/system.rs @@ -23,7 +23,10 @@ use winit::{ use crate::web_resize::{CanvasParentResizeEventChannel, WINIT_CANVAS_SELECTOR}; use crate::{ accessibility::{AccessKitAdapters, WinitActionHandlers}, - converters::{self, convert_window_level, convert_window_theme, convert_winit_theme}, + converters::{ + self, convert_enabled_buttons, convert_window_level, convert_window_theme, + convert_winit_theme, + }, get_best_videomode, get_fitting_videomode, WinitWindows, }; @@ -222,6 +225,10 @@ pub(crate) fn changed_window( winit_window.set_resizable(window.resizable); } + if window.enabled_buttons != cache.window.enabled_buttons { + winit_window.set_enabled_buttons(convert_enabled_buttons(window.enabled_buttons)); + } + if window.resize_constraints != cache.window.resize_constraints { let constraints = window.resize_constraints.check_constraints(); let min_inner_size = LogicalSize { diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index 00c58706680a3..0a0cd1ac01a60 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -18,7 +18,7 @@ use winit::{ use crate::{ accessibility::{AccessKitAdapters, WinitActionHandler, WinitActionHandlers}, - converters::{convert_window_level, convert_window_theme}, + converters::{convert_enabled_buttons, convert_window_level, convert_window_theme}, }; /// A resource which maps window entities to [`winit`] library windows. @@ -94,6 +94,7 @@ impl WinitWindows { .with_window_level(convert_window_level(window.window_level)) .with_theme(window.window_theme.map(convert_window_theme)) .with_resizable(window.resizable) + .with_enabled_buttons(convert_enabled_buttons(window.enabled_buttons)) .with_decorations(window.decorations) .with_transparent(window.transparent); diff --git a/examples/window/window_settings.rs b/examples/window/window_settings.rs index 158c1d9b7803a..3a7687ee92155 100644 --- a/examples/window/window_settings.rs +++ b/examples/window/window_settings.rs @@ -20,6 +20,10 @@ fn main() { // Tells wasm not to override default event handling, like F5, Ctrl+R etc. prevent_default_event_handling: false, window_theme: Some(WindowTheme::Dark), + enabled_buttons: bevy::window::EnabledButtons { + maximize: false, + ..Default::default() + }, ..default() }), ..default() @@ -34,6 +38,7 @@ fn main() { toggle_theme, toggle_cursor, toggle_vsync, + toggle_window_controls, cycle_cursor_icon, switch_level, ), @@ -76,6 +81,31 @@ fn switch_level(input: Res>, mut windows: Query<&mut Window>) { } } +/// This system toggles the window controls when pressing buttons 1, 2 and 3 +/// +/// This feature only works on some platforms. Please check the +/// [documentation](https://docs.rs/bevy/latest/bevy/prelude/struct.Window.html#structfield.enabled_buttons) +/// for more details. +fn toggle_window_controls(input: Res>, mut windows: Query<&mut Window>) { + let toggle_minimize = input.just_pressed(KeyCode::Key1); + let toggle_maximize = input.just_pressed(KeyCode::Key2); + let toggle_close = input.just_pressed(KeyCode::Key3); + + if toggle_minimize || toggle_maximize || toggle_close { + let mut window = windows.single_mut(); + + if toggle_minimize { + window.enabled_buttons.minimize = !window.enabled_buttons.minimize; + } + if toggle_maximize { + window.enabled_buttons.maximize = !window.enabled_buttons.maximize; + } + if toggle_close { + window.enabled_buttons.close = !window.enabled_buttons.close; + } + } +} + /// This system will then change the title during execution fn change_title(mut windows: Query<&mut Window>, time: Res