Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add window drag move and drag resize without decoration example. #15814

Merged
merged 11 commits into from
Oct 15, 2024
12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2934,6 +2934,7 @@ name = "window_fallthrough"
path = "examples/ui/window_fallthrough.rs"
doc-scrape-examples = true


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this added by mistake?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that was a mistake. Will fix.

[package.metadata.example.window_fallthrough]
name = "Window Fallthrough"
description = "Illustrates how to access `winit::window::Window`'s `hittest` functionality."
Expand Down Expand Up @@ -3271,6 +3272,17 @@ description = "Demonstrates customizing default window settings"
category = "Window"
wasm = true

[[example]]
name = "window_drag_move"
path = "examples/window/window_drag_move.rs"
doc-scrape-examples = true

[package.metadata.example.window_drag_move]
name = "Window Drag Move"
description = "Demonstrates drag move and drag resize without window decoration"
category = "Window"
wasm = false

[[example]]
name = "ambiguity_detection"
path = "tests/ecs/ambiguity_detection.rs"
Expand Down
34 changes: 4 additions & 30 deletions crates/bevy_window/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use bevy_ecs::{
entity::{Entity, VisitEntities, VisitEntitiesMut},
prelude::{Component, ReflectComponent},
};
use bevy_math::{DVec2, IVec2, UVec2, Vec2};
use bevy_math::{CompassOctant, DVec2, IVec2, UVec2, Vec2};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};

#[cfg(feature = "serialize")]
Expand Down Expand Up @@ -379,7 +379,7 @@ impl 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) {
pub fn start_drag_resize(&mut self, direction: CompassOctant) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this idea of using the existing CompassOctant 😄

self.internal.drag_resize_request = Some(direction);
}

Expand Down Expand Up @@ -913,32 +913,6 @@ 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(
Expand All @@ -955,7 +929,7 @@ pub struct InternalWindowState {
/// 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<ResizeDirection>,
drag_resize_request: Option<CompassOctant>,
/// Unscaled cursor position.
physical_cursor_position: Option<DVec2>,
}
Expand All @@ -977,7 +951,7 @@ impl InternalWindowState {
}

/// Consumes the current resize request, if it exists. This should only be called by window backends.
pub fn take_resize_request(&mut self) -> Option<ResizeDirection> {
pub fn take_resize_request(&mut self) -> Option<CompassOctant> {
self.drag_resize_request.take()
}
}
Expand Down
24 changes: 11 additions & 13 deletions crates/bevy_winit/src/converters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ use bevy_input::{
touch::{ForceTouch, TouchInput, TouchPhase},
ButtonState,
};
use bevy_math::Vec2;
use bevy_math::{CompassOctant, Vec2};
#[cfg(feature = "custom_cursor")]
use bevy_window::SystemCursorIcon;
use bevy_window::{EnabledButtons, ResizeDirection, WindowLevel, WindowTheme};
use bevy_window::{EnabledButtons, WindowLevel, WindowTheme};
use winit::keyboard::{Key, NamedKey, NativeKey};

pub fn convert_keyboard_input(
Expand Down Expand Up @@ -707,17 +707,15 @@ pub fn convert_enabled_buttons(enabled_buttons: EnabledButtons) -> winit::window
window_buttons
}

pub fn convert_resize_direction(
resize_direction: ResizeDirection,
) -> winit::window::ResizeDirection {
pub fn convert_resize_direction(resize_direction: CompassOctant) -> 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,
CompassOctant::West => winit::window::ResizeDirection::West,
CompassOctant::North => winit::window::ResizeDirection::North,
CompassOctant::East => winit::window::ResizeDirection::East,
CompassOctant::South => winit::window::ResizeDirection::South,
CompassOctant::NorthWest => winit::window::ResizeDirection::NorthWest,
CompassOctant::NorthEast => winit::window::ResizeDirection::NorthEast,
CompassOctant::SouthWest => winit::window::ResizeDirection::SouthWest,
CompassOctant::SouthEast => winit::window::ResizeDirection::SouthEast,
}
}
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,7 @@ Example | Description
[Scale Factor Override](../examples/window/scale_factor_override.rs) | Illustrates how to customize the default window settings
[Screenshot](../examples/window/screenshot.rs) | Shows how to save screenshots to disk
[Transparent Window](../examples/window/transparent_window.rs) | Illustrates making the window transparent and hiding the window decoration
[Window Drag Move](../examples/window/window_drag_move.rs) | Demonstrates drag move and drag resize without window decoration
[Window Resizing](../examples/window/window_resizing.rs) | Demonstrates resizing and responding to resizing a window
[Window Settings](../examples/window/window_settings.rs) | Demonstrates customizing default window settings

Expand Down
140 changes: 140 additions & 0 deletions examples/window/window_drag_move.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
//! This example illustrates drag move and drag resize without window
//! decorations.
//!
//! When window decorations are not present, the user cannot drag the window.
//! The `start_drag_move()` function will permit the application to make the
//! window draggable. It does require that the left mouse button was pressed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is being pressed sounds better to me. Currently the tenses are conflicting.

//! when it is called.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When (inconsistent capitalization with other comments)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I apologize. I don't quite follow this suggestion. I have rewritten that paragraph. Please see if your comment still applies.

use bevy::{math::CompassOctant, prelude::*};

/// Determine what do on left click.
#[derive(Resource, Debug)]
enum LeftClickAction {
/// Do nothing.
Nothing,
/// Drag the window on left click.
Drag,
/// Resize the window on left click.
Resize,
}

/// What direction index should the window resize toward.
#[derive(Resource)]
struct ResizeDir(usize);

/// Directions that the drag resizes the window toward.
const DIRECTIONS: [CompassOctant; 8] = [
CompassOctant::North,
CompassOctant::NorthEast,
CompassOctant::East,
CompassOctant::SouthEast,
CompassOctant::South,
CompassOctant::SouthWest,
CompassOctant::West,
CompassOctant::NorthWest,
];

fn main() {
App::new()
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
decorations: false,
..default()
}),
..default()
}))
.insert_resource(ResizeDir(7))
.insert_resource(LeftClickAction::Drag)
.add_systems(Startup, setup)
.add_systems(Update, (handle_input, move_windows))
.run();
}

fn setup(mut commands: Commands) {
// Camera
commands.spawn(Camera3d::default());

// UI
commands
.spawn((
NodeBundle {
style: Style {
position_type: PositionType::Absolute,
padding: UiRect::all(Val::Px(5.0)),
..default()
},
background_color: Color::BLACK.with_alpha(0.75).into(),
..default()
},
GlobalZIndex(i32::MAX),
))
.with_children(|p| {
p.spawn(Text::default()).with_children(|p| {
p.spawn(TextSpan::new(
"Demonstrate drag move and drag resize without window decorations.\n\n",
));
p.spawn(TextSpan::new("Controls:\n"));
p.spawn(TextSpan::new("A - change left click action ["));
p.spawn(TextSpan::new("Drag"));
p.spawn(TextSpan::new("]\n"));
p.spawn(TextSpan::new("S / D - change resize direction ["));
p.spawn(TextSpan::new("NorthWest"));
p.spawn(TextSpan::new("]\n"));
});
});
}

fn handle_input(
input: Res<ButtonInput<KeyCode>>,
mut action: ResMut<LeftClickAction>,
mut dir: ResMut<ResizeDir>,
example_text: Query<Entity, With<Text>>,
mut writer: UiTextWriter,
) {
use LeftClickAction::*;
if input.just_pressed(KeyCode::KeyA) {
*action = match *action {
Drag => Resize,
Resize => Nothing,
Nothing => Drag,
};
*writer.text(example_text.single(), 4) = format!("{:?}", *action);
}

if input.just_pressed(KeyCode::KeyS) {
dir.0 = dir
.0
.checked_sub(1)
.unwrap_or(DIRECTIONS.len().saturating_sub(1));
*writer.text(example_text.single(), 7) = format!("{:?}", DIRECTIONS[dir.0]);
}

if input.just_pressed(KeyCode::KeyD) {
dir.0 = (dir.0 + 1) % DIRECTIONS.len();
*writer.text(example_text.single(), 7) = format!("{:?}", DIRECTIONS[dir.0]);
}
}

fn move_windows(
Copy link
Contributor

@MiniaczQ MiniaczQ Oct 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fn move_windows(
fn move_or_resize_window(

mut windows: Query<&mut Window>,
action: Res<LeftClickAction>,
input: Res<ButtonInput<MouseButton>>,
dir: Res<ResizeDir>,
) {
// Both `start_drag_move()` and `start_drag_resize()` must be called after a
// left mouse button press as done here.
//
// winit 0.30.5 may panic when initiated without a left mouse button press.
if input.just_pressed(MouseButton::Left) {
for mut window in windows.iter_mut() {
match *action {
LeftClickAction::Nothing => (),
LeftClickAction::Drag => window.start_drag_move(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
LeftClickAction::Drag => window.start_drag_move(),
LeftClickAction::Move => window.start_drag_move(),

this would be more consistent with drag_move vs drag_resize

LeftClickAction::Resize => {
let d = DIRECTIONS[dir.0];
window.start_drag_resize(d);
}
}
}
}
}