window_drag_move/
window_drag_move.rs

1//! This example illustrates drag move and drag resize without window
2//! decorations.
3//!
4//! When window decorations are not present, the user cannot drag a window by
5//! its titlebar to change its position. The `start_drag_move()` function
6//! permits a user to drag a window by left clicking anywhere in the window;
7//! left click must be pressed and other constraints can be imposed. For
8//! instance an application could require a user to hold down alt and left click
9//! to drag a window.
10//!
11//! The `start_drag_resize()` function behaves similarly but permits a window to
12//! be resized.
13use bevy::{math::CompassOctant, prelude::*};
14
15/// Determine what do on left click.
16#[derive(Resource, Debug)]
17enum LeftClickAction {
18    /// Do nothing.
19    Nothing,
20    /// Move the window on left click.
21    Move,
22    /// Resize the window on left click.
23    Resize,
24}
25
26/// What direction index should the window resize toward.
27#[derive(Resource)]
28struct ResizeDir(usize);
29
30/// Directions that the drag resizes the window toward.
31const DIRECTIONS: [CompassOctant; 8] = [
32    CompassOctant::North,
33    CompassOctant::NorthEast,
34    CompassOctant::East,
35    CompassOctant::SouthEast,
36    CompassOctant::South,
37    CompassOctant::SouthWest,
38    CompassOctant::West,
39    CompassOctant::NorthWest,
40];
41
42fn main() {
43    App::new()
44        .add_plugins(DefaultPlugins.set(WindowPlugin {
45            primary_window: Some(Window {
46                decorations: false,
47                ..default()
48            }),
49            ..default()
50        }))
51        .insert_resource(ResizeDir(7))
52        .insert_resource(LeftClickAction::Move)
53        .add_systems(Startup, setup)
54        .add_systems(Update, (handle_input, move_or_resize_windows))
55        .run();
56}
57
58fn setup(mut commands: Commands) {
59    // Camera
60    commands.spawn(Camera3d::default());
61
62    // UI
63    commands.spawn((
64        Node {
65            position_type: PositionType::Absolute,
66            padding: UiRect::all(px(5)),
67            ..default()
68        },
69        BackgroundColor(Color::BLACK.with_alpha(0.75)),
70        GlobalZIndex(i32::MAX),
71        children![(
72            Text::default(),
73            children![
74                TextSpan::new(
75                    "Demonstrate drag move and drag resize without window decorations.\n\n",
76                ),
77                TextSpan::new("Controls:\n"),
78                TextSpan::new("A - change left click action ["),
79                TextSpan::new("Move"),
80                TextSpan::new("]\n"),
81                TextSpan::new("S / D - change resize direction ["),
82                TextSpan::new("NorthWest"),
83                TextSpan::new("]\n"),
84            ]
85        )],
86    ));
87}
88
89fn handle_input(
90    input: Res<ButtonInput<KeyCode>>,
91    mut action: ResMut<LeftClickAction>,
92    mut dir: ResMut<ResizeDir>,
93    example_text: Query<Entity, With<Text>>,
94    mut writer: TextUiWriter,
95) -> Result {
96    use LeftClickAction::*;
97    if input.just_pressed(KeyCode::KeyA) {
98        *action = match *action {
99            Move => Resize,
100            Resize => Nothing,
101            Nothing => Move,
102        };
103        *writer.text(example_text.single()?, 4) = format!("{:?}", *action);
104    }
105
106    if input.just_pressed(KeyCode::KeyS) {
107        dir.0 = dir
108            .0
109            .checked_sub(1)
110            .unwrap_or(DIRECTIONS.len().saturating_sub(1));
111        *writer.text(example_text.single()?, 7) = format!("{:?}", DIRECTIONS[dir.0]);
112    }
113
114    if input.just_pressed(KeyCode::KeyD) {
115        dir.0 = (dir.0 + 1) % DIRECTIONS.len();
116        *writer.text(example_text.single()?, 7) = format!("{:?}", DIRECTIONS[dir.0]);
117    }
118
119    Ok(())
120}
121
122fn move_or_resize_windows(
123    mut windows: Query<&mut Window>,
124    action: Res<LeftClickAction>,
125    input: Res<ButtonInput<MouseButton>>,
126    dir: Res<ResizeDir>,
127) {
128    // Both `start_drag_move()` and `start_drag_resize()` must be called after a
129    // left mouse button press as done here.
130    //
131    // winit 0.30.5 may panic when initiated without a left mouse button press.
132    if input.just_pressed(MouseButton::Left) {
133        for mut window in windows.iter_mut() {
134            match *action {
135                LeftClickAction::Nothing => (),
136                LeftClickAction::Move => window.start_drag_move(),
137                LeftClickAction::Resize => {
138                    let d = DIRECTIONS[dir.0];
139                    window.start_drag_resize(d);
140                }
141            }
142        }
143    }
144}