Skip to content

Commit 82aa2e3

Browse files
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) <details> <summary>Click to view showcase</summary> 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<ButtonInput<MouseButton>>) { if input.pressed(MouseButton::Left) { for mut window in windows.iter_mut() { window.start_drag_move(); } } } ``` </details> --------- Co-authored-by: Alice Cecile <[email protected]>
1 parent 99b9a2f commit 82aa2e3

File tree

3 files changed

+88
-2
lines changed

3 files changed

+88
-2
lines changed

crates/bevy_window/src/window.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,22 @@ impl Window {
367367
self.internal.minimize_request = Some(minimized);
368368
}
369369

370+
/// Calling this will attempt to start a drag-move of the window.
371+
///
372+
/// There is no guarantee that this will work unless the left mouse button was
373+
/// pressed immediately before this function was called.
374+
pub fn start_drag_move(&mut self) {
375+
self.internal.drag_move_request = true;
376+
}
377+
378+
/// Calling this will attempt to start a drag-resize of the window.
379+
///
380+
/// There is no guarantee that this will work unless the left mouse button was
381+
/// pressed immediately before this function was called.
382+
pub fn start_drag_resize(&mut self, direction: ResizeDirection) {
383+
self.internal.drag_resize_request = Some(direction);
384+
}
385+
370386
/// The window's client area width in logical pixels.
371387
///
372388
/// See [`WindowResolution`] for an explanation about logical/physical sizes.
@@ -897,6 +913,32 @@ pub enum CursorGrabMode {
897913
Locked,
898914
}
899915

916+
/// Defines the orientation in which a window resize will be performed.
917+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Reflect)]
918+
#[cfg_attr(
919+
feature = "serialize",
920+
derive(serde::Serialize, serde::Deserialize),
921+
reflect(Serialize, Deserialize)
922+
)]
923+
pub enum ResizeDirection {
924+
/// Resize the window to the west.
925+
West,
926+
/// Resize the window to the north.
927+
North,
928+
/// Resize the window to the east.
929+
East,
930+
/// Resize the window to the south.
931+
South,
932+
/// Resize the window to the northwest.
933+
Northwest,
934+
/// Resize the window to the northeast.
935+
Northeast,
936+
/// Resize the window to the southwest.
937+
Southwest,
938+
/// Resize the window to the southeast.
939+
Southeast,
940+
}
941+
900942
/// Stores internal [`Window`] state that isn't directly accessible.
901943
#[derive(Default, Debug, Copy, Clone, PartialEq, Reflect)]
902944
#[cfg_attr(
@@ -910,6 +952,10 @@ pub struct InternalWindowState {
910952
minimize_request: Option<bool>,
911953
/// If this is true then next frame we will ask to maximize/un-maximize the window depending on `maximized`.
912954
maximize_request: Option<bool>,
955+
/// If this is true then next frame we will ask to drag-move the window.
956+
drag_move_request: bool,
957+
/// If this is `Some` then the next frame we will ask to drag-resize the window.
958+
drag_resize_request: Option<ResizeDirection>,
913959
/// Unscaled cursor position.
914960
physical_cursor_position: Option<DVec2>,
915961
}
@@ -924,6 +970,16 @@ impl InternalWindowState {
924970
pub fn take_minimize_request(&mut self) -> Option<bool> {
925971
self.minimize_request.take()
926972
}
973+
974+
/// Consumes the current move request, if it exists. This should only be called by window backends.
975+
pub fn take_move_request(&mut self) -> bool {
976+
core::mem::take(&mut self.drag_move_request)
977+
}
978+
979+
/// Consumes the current resize request, if it exists. This should only be called by window backends.
980+
pub fn take_resize_request(&mut self) -> Option<ResizeDirection> {
981+
self.drag_resize_request.take()
982+
}
927983
}
928984

929985
/// References a screen monitor.

crates/bevy_winit/src/converters.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use bevy_input::{
88
use bevy_math::Vec2;
99
#[cfg(feature = "custom_cursor")]
1010
use bevy_window::SystemCursorIcon;
11-
use bevy_window::{EnabledButtons, WindowLevel, WindowTheme};
11+
use bevy_window::{EnabledButtons, ResizeDirection, WindowLevel, WindowTheme};
1212
use winit::keyboard::{Key, NamedKey, NativeKey};
1313

1414
pub fn convert_keyboard_input(
@@ -706,3 +706,18 @@ pub fn convert_enabled_buttons(enabled_buttons: EnabledButtons) -> winit::window
706706
}
707707
window_buttons
708708
}
709+
710+
pub fn convert_resize_direction(
711+
resize_direction: ResizeDirection,
712+
) -> winit::window::ResizeDirection {
713+
match resize_direction {
714+
ResizeDirection::West => winit::window::ResizeDirection::West,
715+
ResizeDirection::North => winit::window::ResizeDirection::North,
716+
ResizeDirection::East => winit::window::ResizeDirection::East,
717+
ResizeDirection::South => winit::window::ResizeDirection::South,
718+
ResizeDirection::Northwest => winit::window::ResizeDirection::NorthWest,
719+
ResizeDirection::Northeast => winit::window::ResizeDirection::NorthEast,
720+
ResizeDirection::Southwest => winit::window::ResizeDirection::SouthWest,
721+
ResizeDirection::Southeast => winit::window::ResizeDirection::SouthEast,
722+
}
723+
}

crates/bevy_winit/src/system.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ use winit::platform::web::WindowExtWebSys;
2828

2929
use crate::{
3030
converters::{
31-
convert_enabled_buttons, convert_window_level, convert_window_theme, convert_winit_theme,
31+
convert_enabled_buttons, convert_resize_direction, convert_window_level,
32+
convert_window_theme, convert_winit_theme,
3233
},
3334
get_best_videomode, get_fitting_videomode, select_monitor,
3435
state::react_to_resize,
@@ -462,6 +463,20 @@ pub(crate) fn changed_windows(
462463
winit_window.set_minimized(minimized);
463464
}
464465

466+
if window.internal.take_move_request() {
467+
if let Err(e) = winit_window.drag_window() {
468+
warn!("Winit returned an error while attempting to drag the window: {e}");
469+
}
470+
}
471+
472+
if let Some(resize_direction) = window.internal.take_resize_request() {
473+
if let Err(e) =
474+
winit_window.drag_resize_window(convert_resize_direction(resize_direction))
475+
{
476+
warn!("Winit returned an error while attempting to drag resize the window: {e}");
477+
}
478+
}
479+
465480
if window.focused != cache.window.focused && window.focused {
466481
winit_window.focus_window();
467482
}

0 commit comments

Comments
 (0)