Skip to content

Commit 0eac5aa

Browse files
committed
feat: Implement client-side window cursor resize support
1 parent 5b0dfcd commit 0eac5aa

File tree

4 files changed

+159
-3
lines changed

4 files changed

+159
-3
lines changed

src/window/settings.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ use crate::window::{Icon, Position};
33
/// The window settings of an application.
44
#[derive(Debug, Clone)]
55
pub struct Settings {
6+
/// The size of the resize-enabled border.
7+
pub border_size: u32,
8+
69
/// The initial size of the window.
710
pub size: (u32, u32),
811

@@ -37,6 +40,7 @@ pub struct Settings {
3740
impl Default for Settings {
3841
fn default() -> Settings {
3942
Settings {
43+
border_size: 8,
4044
size: (1024, 768),
4145
position: Position::default(),
4246
min_size: None,
@@ -54,6 +58,7 @@ impl Default for Settings {
5458
impl From<Settings> for iced_winit::settings::Window {
5559
fn from(settings: Settings) -> Self {
5660
Self {
61+
border_size: settings.border_size,
5762
size: settings.size,
5863
position: iced_winit::Position::from(settings.position),
5964
min_size: settings.min_size,

winit/Cargo.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@ log = "0.4"
2121
thiserror = "1.0"
2222

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

2827
[dependencies.iced_native]
2928
version = "0.6"

winit/src/application.rs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use iced_native::user_interface::{self, UserInterface};
2323
pub use iced_native::application::{Appearance, StyleSheet};
2424

2525
use std::mem::ManuallyDrop;
26+
use winit::window::{CursorIcon, ResizeDirection};
2627

2728
/// An interactive, native cross-platform application.
2829
///
@@ -121,6 +122,9 @@ where
121122
let mut debug = Debug::new();
122123
debug.startup_started();
123124

125+
// Defines width of the window border for the resize handles.
126+
let border_size = settings.window.border_size;
127+
124128
let event_loop = EventLoopBuilder::with_user_event().build();
125129
let proxy = event_loop.create_proxy();
126130

@@ -193,6 +197,7 @@ where
193197
init_command,
194198
window,
195199
settings.exit_on_close_request,
200+
border_size,
196201
));
197202

198203
let mut context = task::Context::from_waker(task::noop_waker_ref());
@@ -243,6 +248,7 @@ async fn run_instance<A, E, C>(
243248
init_command: Command<A::Message>,
244249
window: winit::window::Window,
245250
exit_on_close_request: bool,
251+
border_size: u32,
246252
) where
247253
A: Application + 'static,
248254
E: Executor + 'static,
@@ -290,6 +296,11 @@ async fn run_instance<A, E, C>(
290296
&mut debug,
291297
));
292298

299+
let mut cursor_resize_event = cursor_resize_event_func(
300+
&window,
301+
border_size as f64 * window.scale_factor(),
302+
);
303+
293304
let mut mouse_interaction = mouse::Interaction::default();
294305
let mut events = Vec::new();
295306
let mut messages = Vec::new();
@@ -475,6 +486,12 @@ async fn run_instance<A, E, C>(
475486
event: window_event,
476487
..
477488
} => {
489+
if let Some(func) = cursor_resize_event.as_mut() {
490+
if func(&window, &window_event) {
491+
continue;
492+
}
493+
}
494+
478495
if requests_exit(&window_event, state.modifiers())
479496
&& exit_on_close_request
480497
{
@@ -766,3 +783,134 @@ mod platform {
766783
event_loop.run(event_handler)
767784
}
768785
}
786+
787+
/// If supported by winit, returns a closure that implements cursor resize support.
788+
fn cursor_resize_event_func(
789+
window: &winit::window::Window,
790+
border_size: f64,
791+
) -> Option<
792+
impl FnMut(&winit::window::Window, &winit::event::WindowEvent<'_>) -> bool,
793+
> {
794+
if window.drag_resize_window(ResizeDirection::East).is_ok() {
795+
// Keep track of cursor when it is within a resizeable border.
796+
let mut cursor_prev_resize_direction = None;
797+
798+
Some(
799+
move |window: &winit::window::Window,
800+
window_event: &winit::event::WindowEvent<'_>|
801+
-> bool {
802+
// Keep track of border resize state and set cursor icon when in range
803+
match window_event {
804+
winit::event::WindowEvent::CursorMoved {
805+
position, ..
806+
} => {
807+
if !window.is_decorated() {
808+
let location = cursor_resize_direction(
809+
window.inner_size(),
810+
*position,
811+
border_size,
812+
);
813+
if location != cursor_prev_resize_direction {
814+
window.set_cursor_icon(
815+
resize_direction_cursor_icon(location),
816+
);
817+
cursor_prev_resize_direction = location;
818+
return true;
819+
}
820+
}
821+
}
822+
winit::event::WindowEvent::MouseInput {
823+
state: winit::event::ElementState::Pressed,
824+
button: winit::event::MouseButton::Left,
825+
..
826+
} => {
827+
if let Some(direction) = cursor_prev_resize_direction {
828+
let _res = window.drag_resize_window(direction);
829+
return true;
830+
}
831+
}
832+
_ => (),
833+
}
834+
835+
false
836+
},
837+
)
838+
} else {
839+
None
840+
}
841+
}
842+
843+
/// Get the cursor icon that corresponds to the resize direction.
844+
fn resize_direction_cursor_icon(
845+
resize_direction: Option<ResizeDirection>,
846+
) -> CursorIcon {
847+
match resize_direction {
848+
Some(resize_direction) => match resize_direction {
849+
ResizeDirection::East => CursorIcon::EResize,
850+
ResizeDirection::North => CursorIcon::NResize,
851+
ResizeDirection::NorthEast => CursorIcon::NeResize,
852+
ResizeDirection::NorthWest => CursorIcon::NwResize,
853+
ResizeDirection::South => CursorIcon::SResize,
854+
ResizeDirection::SouthEast => CursorIcon::SeResize,
855+
ResizeDirection::SouthWest => CursorIcon::SwResize,
856+
ResizeDirection::West => CursorIcon::WResize,
857+
},
858+
None => CursorIcon::Default,
859+
}
860+
}
861+
862+
/// Identifies resize direction based on cursor position and window dimensions.
863+
#[allow(clippy::similar_names)]
864+
fn cursor_resize_direction(
865+
win_size: winit::dpi::PhysicalSize<u32>,
866+
position: winit::dpi::PhysicalPosition<f64>,
867+
border_size: f64,
868+
) -> Option<ResizeDirection> {
869+
enum XDirection {
870+
West,
871+
East,
872+
Default,
873+
}
874+
875+
enum YDirection {
876+
North,
877+
South,
878+
Default,
879+
}
880+
881+
let xdir = if position.x < border_size {
882+
XDirection::West
883+
} else if position.x > (win_size.width as f64 - border_size) {
884+
XDirection::East
885+
} else {
886+
XDirection::Default
887+
};
888+
889+
let ydir = if position.y < border_size {
890+
YDirection::North
891+
} else if position.y > (win_size.height as f64 - border_size) {
892+
YDirection::South
893+
} else {
894+
YDirection::Default
895+
};
896+
897+
Some(match xdir {
898+
XDirection::West => match ydir {
899+
YDirection::North => ResizeDirection::NorthWest,
900+
YDirection::South => ResizeDirection::SouthWest,
901+
YDirection::Default => ResizeDirection::West,
902+
},
903+
904+
XDirection::East => match ydir {
905+
YDirection::North => ResizeDirection::NorthEast,
906+
YDirection::South => ResizeDirection::SouthEast,
907+
YDirection::Default => ResizeDirection::East,
908+
},
909+
910+
XDirection::Default => match ydir {
911+
YDirection::North => ResizeDirection::North,
912+
YDirection::South => ResizeDirection::South,
913+
YDirection::Default => return None,
914+
},
915+
})
916+
}

winit/src/settings.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ pub struct Settings<Flags> {
6161
/// The window settings of an application.
6262
#[derive(Debug, Clone)]
6363
pub struct Window {
64+
/// The size of the resize-enabled border.
65+
pub border_size: u32,
66+
6467
/// The size of the window.
6568
pub size: (u32, u32),
6669

@@ -183,6 +186,7 @@ impl Window {
183186
impl Default for Window {
184187
fn default() -> Window {
185188
Window {
189+
border_size: 8,
186190
size: (1024, 768),
187191
position: Position::default(),
188192
min_size: None,

0 commit comments

Comments
 (0)