Skip to content

Commit

Permalink
feat: Implement client-side window cursor resize support
Browse files Browse the repository at this point in the history
  • Loading branch information
mmstick committed Nov 16, 2022
1 parent 5b0dfcd commit 0eac5aa
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 3 deletions.
5 changes: 5 additions & 0 deletions src/window/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ use crate::window::{Icon, Position};
/// The window settings of an application.
#[derive(Debug, Clone)]
pub struct Settings {
/// The size of the resize-enabled border.
pub border_size: u32,

/// The initial size of the window.
pub size: (u32, u32),

Expand Down Expand Up @@ -37,6 +40,7 @@ pub struct Settings {
impl Default for Settings {
fn default() -> Settings {
Settings {
border_size: 8,
size: (1024, 768),
position: Position::default(),
min_size: None,
Expand All @@ -54,6 +58,7 @@ impl Default for Settings {
impl From<Settings> for iced_winit::settings::Window {
fn from(settings: Settings) -> Self {
Self {
border_size: settings.border_size,
size: settings.size,
position: iced_winit::Position::from(settings.position),
min_size: settings.min_size,
Expand Down
5 changes: 2 additions & 3 deletions winit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ log = "0.4"
thiserror = "1.0"

[dependencies.winit]
version = "0.27"
git = "https://github.com/iced-rs/winit.git"
rev = "940457522e9fb9f5dac228b0ecfafe0138b4048c"
git = "https://github.com/pop-os/winit.git"
branch = "iced"

[dependencies.iced_native]
version = "0.6"
Expand Down
148 changes: 148 additions & 0 deletions winit/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use iced_native::user_interface::{self, UserInterface};
pub use iced_native::application::{Appearance, StyleSheet};

use std::mem::ManuallyDrop;
use winit::window::{CursorIcon, ResizeDirection};

/// An interactive, native cross-platform application.
///
Expand Down Expand Up @@ -121,6 +122,9 @@ where
let mut debug = Debug::new();
debug.startup_started();

// Defines width of the window border for the resize handles.
let border_size = settings.window.border_size;

let event_loop = EventLoopBuilder::with_user_event().build();
let proxy = event_loop.create_proxy();

Expand Down Expand Up @@ -193,6 +197,7 @@ where
init_command,
window,
settings.exit_on_close_request,
border_size,
));

let mut context = task::Context::from_waker(task::noop_waker_ref());
Expand Down Expand Up @@ -243,6 +248,7 @@ async fn run_instance<A, E, C>(
init_command: Command<A::Message>,
window: winit::window::Window,
exit_on_close_request: bool,
border_size: u32,
) where
A: Application + 'static,
E: Executor + 'static,
Expand Down Expand Up @@ -290,6 +296,11 @@ async fn run_instance<A, E, C>(
&mut debug,
));

let mut cursor_resize_event = cursor_resize_event_func(
&window,
border_size as f64 * window.scale_factor(),
);

let mut mouse_interaction = mouse::Interaction::default();
let mut events = Vec::new();
let mut messages = Vec::new();
Expand Down Expand Up @@ -475,6 +486,12 @@ async fn run_instance<A, E, C>(
event: window_event,
..
} => {
if let Some(func) = cursor_resize_event.as_mut() {
if func(&window, &window_event) {
continue;
}
}

if requests_exit(&window_event, state.modifiers())
&& exit_on_close_request
{
Expand Down Expand Up @@ -766,3 +783,134 @@ mod platform {
event_loop.run(event_handler)
}
}

/// If supported by winit, returns a closure that implements cursor resize support.
fn cursor_resize_event_func(
window: &winit::window::Window,
border_size: f64,
) -> Option<
impl FnMut(&winit::window::Window, &winit::event::WindowEvent<'_>) -> bool,
> {
if window.drag_resize_window(ResizeDirection::East).is_ok() {
// Keep track of cursor when it is within a resizeable border.
let mut cursor_prev_resize_direction = None;

Some(
move |window: &winit::window::Window,
window_event: &winit::event::WindowEvent<'_>|
-> bool {
// Keep track of border resize state and set cursor icon when in range
match window_event {
winit::event::WindowEvent::CursorMoved {
position, ..
} => {
if !window.is_decorated() {
let location = cursor_resize_direction(
window.inner_size(),
*position,
border_size,
);
if location != cursor_prev_resize_direction {
window.set_cursor_icon(
resize_direction_cursor_icon(location),
);
cursor_prev_resize_direction = location;
return true;
}
}
}
winit::event::WindowEvent::MouseInput {
state: winit::event::ElementState::Pressed,
button: winit::event::MouseButton::Left,
..
} => {
if let Some(direction) = cursor_prev_resize_direction {
let _res = window.drag_resize_window(direction);
return true;
}
}
_ => (),
}

false
},
)
} else {
None
}
}

/// Get the cursor icon that corresponds to the resize direction.
fn resize_direction_cursor_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,
}
}

/// Identifies resize direction based on cursor position and window dimensions.
#[allow(clippy::similar_names)]
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,
},
})
}
4 changes: 4 additions & 0 deletions winit/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ pub struct Settings<Flags> {
/// The window settings of an application.
#[derive(Debug, Clone)]
pub struct Window {
/// The size of the resize-enabled border.
pub border_size: u32,

/// The size of the window.
pub size: (u32, u32),

Expand Down Expand Up @@ -183,6 +186,7 @@ impl Window {
impl Default for Window {
fn default() -> Window {
Window {
border_size: 8,
size: (1024, 768),
position: Position::default(),
min_size: None,
Expand Down

0 comments on commit 0eac5aa

Please sign in to comment.