mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-23 13:51:30 +11:00
feat(x11): Add Window::drag_resize_window (#2515)
This commit is contained in:
parent
08ce3af3e1
commit
9225b2812e
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
141
examples/window_drag_resize.rs
Normal file
141
examples/window_drag_resize.rs
Normal 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,
|
||||
},
|
||||
})
|
||||
}
|
|
@ -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(),
|
||||
|
|
|
@ -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()))
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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,
|
||||
],
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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()))
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue