feat(x11): Add Window::drag_resize_window (#2515)

This commit is contained in:
Michael Murphy 2023-01-11 18:07:09 +01:00 committed by GitHub
parent 08ce3af3e1
commit 9225b2812e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 280 additions and 15 deletions

View file

@ -52,6 +52,7 @@ And please only add new entries to the top of this list, right below the `# Unre
- On Wayland, `RedrawRequested` not emitted during resize.
- **Breaking:** Remove the unstable `xlib_xconnection()` function from the private interface.
- Added Orbital support for Redox OS
- On X11, added `drag_resize_window` method.
# 0.27.5

View file

@ -215,6 +215,7 @@ Legend:
|Gamepad/Joystick events |❌[#804] |❌ |❌ |❌ |❌ |❌ |❓ |**N/A** |
|Device movement events |❓ |❓ |❓ |❓ |❌ |❌ |❓ |**N/A** |
|Drag window with cursor |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A** |**N/A** |
|Resize with cursor |❌ |❌ |✔️ |❌ |**N/A**|**N/A**|**N/A** |**N/A** |
### Pending API Reworks
Changes in the API that have been agreed upon but aren't implemented across all platforms.

View file

@ -0,0 +1,141 @@
//! Demonstrates capability to create in-app draggable regions for client-side decoration support.
use simple_logger::SimpleLogger;
use winit::{
event::{
ElementState, Event, KeyboardInput, MouseButton, StartCause, VirtualKeyCode, WindowEvent,
},
event_loop::{ControlFlow, EventLoop},
window::{CursorIcon, ResizeDirection, WindowBuilder},
};
const BORDER: f64 = 8.0;
fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_inner_size(winit::dpi::LogicalSize::new(600.0, 400.0))
.with_min_inner_size(winit::dpi::LogicalSize::new(400.0, 200.0))
.with_decorations(false)
.build(&event_loop)
.unwrap();
let mut border = false;
let mut cursor_location = None;
event_loop.run(move |event, _, control_flow| match event {
Event::NewEvents(StartCause::Init) => {
eprintln!("Press 'B' to toggle borderless")
}
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::CursorMoved { position, .. } => {
if !window.is_decorated() {
let new_location =
cursor_resize_direction(window.inner_size(), position, BORDER);
if new_location != cursor_location {
cursor_location = new_location;
window.set_cursor_icon(cursor_direction_icon(cursor_location))
}
}
}
WindowEvent::MouseInput {
state: ElementState::Pressed,
button: MouseButton::Left,
..
} => {
if let Some(dir) = cursor_location {
let _res = window.drag_resize_window(dir);
}
}
WindowEvent::KeyboardInput {
input:
KeyboardInput {
state: ElementState::Released,
virtual_keycode: Some(VirtualKeyCode::B),
..
},
..
} => {
border = !border;
window.set_decorations(border);
}
_ => (),
},
_ => (),
});
}
fn cursor_direction_icon(resize_direction: Option<ResizeDirection>) -> CursorIcon {
match resize_direction {
Some(resize_direction) => match resize_direction {
ResizeDirection::East => CursorIcon::EResize,
ResizeDirection::North => CursorIcon::NResize,
ResizeDirection::NorthEast => CursorIcon::NeResize,
ResizeDirection::NorthWest => CursorIcon::NwResize,
ResizeDirection::South => CursorIcon::SResize,
ResizeDirection::SouthEast => CursorIcon::SeResize,
ResizeDirection::SouthWest => CursorIcon::SwResize,
ResizeDirection::West => CursorIcon::WResize,
},
None => CursorIcon::Default,
}
}
fn cursor_resize_direction(
win_size: winit::dpi::PhysicalSize<u32>,
position: winit::dpi::PhysicalPosition<f64>,
border_size: f64,
) -> Option<ResizeDirection> {
enum XDirection {
West,
East,
Default,
}
enum YDirection {
North,
South,
Default,
}
let xdir = if position.x < border_size {
XDirection::West
} else if position.x > (win_size.width as f64 - border_size) {
XDirection::East
} else {
XDirection::Default
};
let ydir = if position.y < border_size {
YDirection::North
} else if position.y > (win_size.height as f64 - border_size) {
YDirection::South
} else {
YDirection::Default
};
Some(match xdir {
XDirection::West => match ydir {
YDirection::North => ResizeDirection::NorthWest,
YDirection::South => ResizeDirection::SouthWest,
YDirection::Default => ResizeDirection::West,
},
XDirection::East => match ydir {
YDirection::North => ResizeDirection::NorthEast,
YDirection::South => ResizeDirection::SouthEast,
YDirection::Default => ResizeDirection::East,
},
XDirection::Default => match ydir {
YDirection::North => ResizeDirection::North,
YDirection::South => ResizeDirection::South,
YDirection::Default => return None,
},
})
}

View file

@ -24,7 +24,7 @@ use crate::{
error,
event::{self, StartCause, VirtualKeyCode},
event_loop::{self, ControlFlow, EventLoopWindowTarget as RootELW},
window::{self, CursorGrabMode, Theme, WindowButtons, WindowLevel},
window::{self, CursorGrabMode, ResizeDirection, Theme, WindowButtons, WindowLevel},
};
fn ndk_keycode_to_virtualkeycode(keycode: Keycode) -> Option<event::VirtualKeyCode> {
@ -1021,6 +1021,15 @@ impl Window {
))
}
pub fn drag_resize_window(
&self,
_direction: ResizeDirection,
) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
))
}
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),

View file

@ -26,8 +26,8 @@ use crate::{
monitor, EventLoopWindowTarget, Fullscreen, MonitorHandle,
},
window::{
CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowButtons,
WindowId as RootWindowId, WindowLevel,
CursorGrabMode, CursorIcon, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
WindowButtons, WindowId as RootWindowId, WindowLevel,
},
};
@ -200,6 +200,10 @@ impl Inner {
Err(ExternalError::NotSupported(NotSupportedError::new()))
}
pub fn drag_resize_window(&self, _direction: ResizeDirection) -> Result<(), ExternalError> {
Err(ExternalError::NotSupported(NotSupportedError::new()))
}
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), ExternalError> {
Err(ExternalError::NotSupported(NotSupportedError::new()))
}

View file

@ -34,8 +34,8 @@ use crate::{
},
icon::Icon,
window::{
CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowButtons,
WindowLevel,
CursorGrabMode, CursorIcon, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
WindowButtons, WindowLevel,
},
};
@ -424,6 +424,11 @@ impl Window {
x11_or_wayland!(match self; Window(window) => window.drag_window())
}
#[inline]
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> {
x11_or_wayland!(match self; Window(window) => window.drag_resize_window(direction))
}
#[inline]
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
x11_or_wayland!(match self; Window(w) => w.set_cursor_hittest(hittest))

View file

@ -19,7 +19,8 @@ use crate::platform_impl::{
PlatformSpecificWindowBuilderAttributes as PlatformAttributes,
};
use crate::window::{
CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowButtons,
CursorGrabMode, CursorIcon, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
WindowButtons,
};
use super::env::WindowingFeatures;
@ -560,6 +561,11 @@ impl Window {
Ok(())
}
#[inline]
pub fn drag_resize_window(&self, _direction: ResizeDirection) -> Result<(), ExternalError> {
Err(ExternalError::NotSupported(NotSupportedError::new()))
}
#[inline]
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
self.send_request(WindowRequest::PassthroughMouseInput(!hittest));

View file

@ -4,6 +4,17 @@ use once_cell::sync::Lazy;
use super::*;
// https://specifications.freedesktop.org/wm-spec/latest/ar01s04.html#idm46075117309248
pub const MOVERESIZE_TOPLEFT: isize = 0;
pub const MOVERESIZE_TOP: isize = 1;
pub const MOVERESIZE_TOPRIGHT: isize = 2;
pub const MOVERESIZE_RIGHT: isize = 3;
pub const MOVERESIZE_BOTTOMRIGHT: isize = 4;
pub const MOVERESIZE_BOTTOM: isize = 5;
pub const MOVERESIZE_BOTTOMLEFT: isize = 6;
pub const MOVERESIZE_LEFT: isize = 7;
pub const MOVERESIZE_MOVE: isize = 8;
// This info is global to the window manager.
static SUPPORTED_HINTS: Lazy<Mutex<Vec<ffi::Atom>>> =
Lazy::new(|| Mutex::new(Vec::with_capacity(0)));

View file

@ -21,8 +21,8 @@ use crate::{
PlatformSpecificWindowBuilderAttributes, VideoMode as PlatformVideoMode,
},
window::{
CursorGrabMode, CursorIcon, Icon, Theme, UserAttentionType, WindowAttributes,
WindowButtons, WindowLevel,
CursorGrabMode, CursorIcon, Icon, ResizeDirection, Theme, UserAttentionType,
WindowAttributes, WindowButtons, WindowLevel,
},
};
@ -1433,7 +1433,27 @@ impl UnownedWindow {
Err(ExternalError::NotSupported(NotSupportedError::new()))
}
/// Moves the window while it is being dragged.
pub fn drag_window(&self) -> Result<(), ExternalError> {
self.drag_initiate(util::MOVERESIZE_MOVE)
}
/// Resizes the window while it is being dragged.
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> {
self.drag_initiate(match direction {
ResizeDirection::East => util::MOVERESIZE_RIGHT,
ResizeDirection::North => util::MOVERESIZE_TOP,
ResizeDirection::NorthEast => util::MOVERESIZE_TOPRIGHT,
ResizeDirection::NorthWest => util::MOVERESIZE_TOPLEFT,
ResizeDirection::South => util::MOVERESIZE_BOTTOM,
ResizeDirection::SouthEast => util::MOVERESIZE_BOTTOMRIGHT,
ResizeDirection::SouthWest => util::MOVERESIZE_BOTTOMLEFT,
ResizeDirection::West => util::MOVERESIZE_LEFT,
})
}
/// Initiates a drag operation while the left mouse button is pressed.
fn drag_initiate(&self, action: isize) -> Result<(), ExternalError> {
let pointer = self
.xconn
.query_pointer(self.xwindow, util::VIRTUAL_CORE_POINTER)
@ -1464,7 +1484,7 @@ impl UnownedWindow {
[
(window.x as c_long + pointer.win_x as c_long),
(window.y as c_long + pointer.win_y as c_long),
8, // _NET_WM_MOVERESIZE_MOVE
action.try_into().unwrap(),
ffi::Button1 as c_long,
1,
],

View file

@ -32,8 +32,8 @@ use crate::{
Fullscreen, OsError,
},
window::{
CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowButtons,
WindowId as RootWindowId, WindowLevel,
CursorGrabMode, CursorIcon, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
WindowButtons, WindowId as RootWindowId, WindowLevel,
},
};
use core_graphics::display::{CGDisplay, CGPoint};
@ -784,6 +784,11 @@ impl WinitWindow {
Ok(())
}
#[inline]
pub fn drag_resize_window(&self, _direction: ResizeDirection) -> Result<(), ExternalError> {
Err(ExternalError::NotSupported(NotSupportedError::new()))
}
#[inline]
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
util::set_ignore_mouse_events_sync(self, !hittest);

View file

@ -348,6 +348,16 @@ impl Window {
))
}
#[inline]
pub fn drag_resize_window(
&self,
_direction: window::ResizeDirection,
) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
))
}
#[inline]
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(

View file

@ -3,8 +3,8 @@ use crate::error::{ExternalError, NotSupportedError, OsError as RootOE};
use crate::event;
use crate::icon::Icon;
use crate::window::{
CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowButtons,
WindowId as RootWI, WindowLevel,
CursorGrabMode, CursorIcon, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
WindowButtons, WindowId as RootWI, WindowLevel,
};
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, WebDisplayHandle, WebWindowHandle};
@ -267,6 +267,11 @@ impl Window {
Err(ExternalError::NotSupported(NotSupportedError::new()))
}
#[inline]
pub fn drag_resize_window(&self, _direction: ResizeDirection) -> Result<(), ExternalError> {
Err(ExternalError::NotSupported(NotSupportedError::new()))
}
#[inline]
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), ExternalError> {
Err(ExternalError::NotSupported(NotSupportedError::new()))

View file

@ -72,8 +72,8 @@ use crate::{
Fullscreen, PlatformSpecificWindowBuilderAttributes, WindowId,
},
window::{
CursorGrabMode, CursorIcon, Theme, UserAttentionType, WindowAttributes, WindowButtons,
WindowLevel,
CursorGrabMode, CursorIcon, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
WindowButtons, WindowLevel,
},
};
@ -432,6 +432,11 @@ impl Window {
Ok(())
}
#[inline]
pub fn drag_resize_window(&self, _direction: ResizeDirection) -> Result<(), ExternalError> {
Err(ExternalError::NotSupported(NotSupportedError::new()))
}
#[inline]
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
let window = self.window.clone();

View file

@ -1159,6 +1159,19 @@ impl Window {
self.window.drag_window()
}
/// Resizes the window with the left mouse button until the button is released.
///
/// There's no guarantee that this will work unless the left mouse button was pressed
/// immediately before this function is called.
///
/// ## Platform-specific
///
/// Only X11 is supported at this time.
#[inline]
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> {
self.window.drag_resize_window(direction)
}
/// Modifies whether the window catches cursor events.
///
/// If `true`, the window will catch the cursor events. If `false`, events are passed through
@ -1353,6 +1366,35 @@ impl Default for CursorIcon {
}
}
/// Defines the orientation that a window resize will be performed.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ResizeDirection {
East,
North,
NorthEast,
NorthWest,
South,
SouthEast,
SouthWest,
West,
}
impl From<ResizeDirection> for CursorIcon {
fn from(direction: ResizeDirection) -> Self {
use ResizeDirection::*;
match direction {
East => CursorIcon::EResize,
North => CursorIcon::NResize,
NorthEast => CursorIcon::NeResize,
NorthWest => CursorIcon::NwResize,
South => CursorIcon::SResize,
SouthEast => CursorIcon::SeResize,
SouthWest => CursorIcon::SwResize,
West => CursorIcon::WResize,
}
}
}
/// Fullscreen modes.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Fullscreen {