From 82aa2e316144812ad626c80b68a6a2940c4b4c38 Mon Sep 17 00:00:00 2001 From: Isse Date: Tue, 8 Oct 2024 18:32:07 +0200 Subject: [PATCH] Add the functions `start_drag_move` and `start_drag_resize` to `Window` (#15674) # Objective Expose the `winit` functions [drag_window](https://docs.rs/winit/latest/winit/window/struct.Window.html#method.drag_window) and [resize_window](https://docs.rs/winit/latest/winit/window/struct.Window.html#method.drag_resize_window). Which allows implementing move & resize for windows without decorations. ## Solution Add the functions `start_drag_move` and `start_drag_resize` to `bevy_window::Window`, which are then assigned to fields in `InternalWindowState`, and propagated to `winit` in the `changed_windows` system. ## Testing I've tested that both functions works on x11 and wayland. Not sure if someone needs to test on windows/mac? --- ## Showcase [Screencast from 2024-10-06 11-49-58 (trimmed).webm](https://github.com/user-attachments/assets/1cdee7b1-22bd-41d3-8a0a-6872a6ebf62c) (The flickering in the video is some issue with resizing without decorations on x11)
Click to view showcase Not the same code used in the video, but simple way to test moving a window without decorations. ```rust use bevy::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins.set(WindowPlugin { primary_window: Some(Window { decorations: false, ..default() }), ..default() })) .add_systems(Update, move_windows) .run(); } fn move_windows(mut windows: Query<&mut Window>, input: Res>) { if input.pressed(MouseButton::Left) { for mut window in windows.iter_mut() { window.start_drag_move(); } } } ```
--------- Co-authored-by: Alice Cecile --- crates/bevy_window/src/window.rs | 56 +++++++++++++++++++++++++++++ crates/bevy_winit/src/converters.rs | 17 ++++++++- crates/bevy_winit/src/system.rs | 17 ++++++++- 3 files changed, 88 insertions(+), 2 deletions(-) diff --git a/crates/bevy_window/src/window.rs b/crates/bevy_window/src/window.rs index 8a6dcdc5ee423..34781501e65a8 100644 --- a/crates/bevy_window/src/window.rs +++ b/crates/bevy_window/src/window.rs @@ -367,6 +367,22 @@ impl Window { self.internal.minimize_request = Some(minimized); } + /// Calling this will attempt to start a drag-move of the window. + /// + /// There is no guarantee that this will work unless the left mouse button was + /// pressed immediately before this function was called. + pub fn start_drag_move(&mut self) { + self.internal.drag_move_request = true; + } + + /// Calling this will attempt to start a drag-resize of the window. + /// + /// There is no guarantee that this will work unless the left mouse button was + /// pressed immediately before this function was called. + pub fn start_drag_resize(&mut self, direction: ResizeDirection) { + self.internal.drag_resize_request = Some(direction); + } + /// The window's client area width in logical pixels. /// /// See [`WindowResolution`] for an explanation about logical/physical sizes. @@ -897,6 +913,32 @@ pub enum CursorGrabMode { Locked, } +/// Defines the orientation in which a window resize will be performed. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Reflect)] +#[cfg_attr( + feature = "serialize", + derive(serde::Serialize, serde::Deserialize), + reflect(Serialize, Deserialize) +)] +pub enum ResizeDirection { + /// Resize the window to the west. + West, + /// Resize the window to the north. + North, + /// Resize the window to the east. + East, + /// Resize the window to the south. + South, + /// Resize the window to the northwest. + Northwest, + /// Resize the window to the northeast. + Northeast, + /// Resize the window to the southwest. + Southwest, + /// Resize the window to the southeast. + Southeast, +} + /// Stores internal [`Window`] state that isn't directly accessible. #[derive(Default, Debug, Copy, Clone, PartialEq, Reflect)] #[cfg_attr( @@ -910,6 +952,10 @@ pub struct InternalWindowState { minimize_request: Option, /// If this is true then next frame we will ask to maximize/un-maximize the window depending on `maximized`. maximize_request: Option, + /// If this is true then next frame we will ask to drag-move the window. + drag_move_request: bool, + /// If this is `Some` then the next frame we will ask to drag-resize the window. + drag_resize_request: Option, /// Unscaled cursor position. physical_cursor_position: Option, } @@ -924,6 +970,16 @@ impl InternalWindowState { pub fn take_minimize_request(&mut self) -> Option { self.minimize_request.take() } + + /// Consumes the current move request, if it exists. This should only be called by window backends. + pub fn take_move_request(&mut self) -> bool { + core::mem::take(&mut self.drag_move_request) + } + + /// Consumes the current resize request, if it exists. This should only be called by window backends. + pub fn take_resize_request(&mut self) -> Option { + self.drag_resize_request.take() + } } /// References a screen monitor. diff --git a/crates/bevy_winit/src/converters.rs b/crates/bevy_winit/src/converters.rs index 691113bbbed8e..fc5ff5c29a10d 100644 --- a/crates/bevy_winit/src/converters.rs +++ b/crates/bevy_winit/src/converters.rs @@ -8,7 +8,7 @@ use bevy_input::{ use bevy_math::Vec2; #[cfg(feature = "custom_cursor")] use bevy_window::SystemCursorIcon; -use bevy_window::{EnabledButtons, WindowLevel, WindowTheme}; +use bevy_window::{EnabledButtons, ResizeDirection, WindowLevel, WindowTheme}; use winit::keyboard::{Key, NamedKey, NativeKey}; pub fn convert_keyboard_input( @@ -706,3 +706,18 @@ pub fn convert_enabled_buttons(enabled_buttons: EnabledButtons) -> winit::window } window_buttons } + +pub fn convert_resize_direction( + resize_direction: ResizeDirection, +) -> winit::window::ResizeDirection { + match resize_direction { + ResizeDirection::West => winit::window::ResizeDirection::West, + ResizeDirection::North => winit::window::ResizeDirection::North, + ResizeDirection::East => winit::window::ResizeDirection::East, + ResizeDirection::South => winit::window::ResizeDirection::South, + ResizeDirection::Northwest => winit::window::ResizeDirection::NorthWest, + ResizeDirection::Northeast => winit::window::ResizeDirection::NorthEast, + ResizeDirection::Southwest => winit::window::ResizeDirection::SouthWest, + ResizeDirection::Southeast => winit::window::ResizeDirection::SouthEast, + } +} diff --git a/crates/bevy_winit/src/system.rs b/crates/bevy_winit/src/system.rs index 34a4dddc51eb1..6348f050ad758 100644 --- a/crates/bevy_winit/src/system.rs +++ b/crates/bevy_winit/src/system.rs @@ -28,7 +28,8 @@ use winit::platform::web::WindowExtWebSys; use crate::{ converters::{ - convert_enabled_buttons, convert_window_level, convert_window_theme, convert_winit_theme, + convert_enabled_buttons, convert_resize_direction, convert_window_level, + convert_window_theme, convert_winit_theme, }, get_best_videomode, get_fitting_videomode, select_monitor, state::react_to_resize, @@ -462,6 +463,20 @@ pub(crate) fn changed_windows( winit_window.set_minimized(minimized); } + if window.internal.take_move_request() { + if let Err(e) = winit_window.drag_window() { + warn!("Winit returned an error while attempting to drag the window: {e}"); + } + } + + if let Some(resize_direction) = window.internal.take_resize_request() { + if let Err(e) = + winit_window.drag_resize_window(convert_resize_direction(resize_direction)) + { + warn!("Winit returned an error while attempting to drag resize the window: {e}"); + } + } + if window.focused != cache.window.focused && window.focused { winit_window.focus_window(); }