iced_winit/application/
drag_resize.rs

1use winit::window::{CursorIcon, ResizeDirection};
2
3#[cfg(any(
4    all(
5        unix,
6        not(target_vendor = "apple"),
7        not(target_os = "android"),
8        not(target_os = "emscripten"),
9    ),
10    target_os = "windows",
11))]
12const DRAG_RESIZE_SUPPORTED: bool = true;
13
14#[cfg(not(any(
15    all(
16        unix,
17        not(target_vendor = "apple"),
18        not(target_os = "android"),
19        not(target_os = "emscripten"),
20    ),
21    target_os = "windows",
22)))]
23const DRAG_RESIZE_SUPPORTED: bool = false;
24
25/// If supported by winit, returns a closure that implements cursor resize support.
26pub fn event_func(
27    window: &dyn winit::window::Window,
28    border_size: f64,
29) -> Option<
30    Box<
31        dyn FnMut(
32            &dyn winit::window::Window,
33            &winit::event::WindowEvent,
34        ) -> bool,
35    >,
36> {
37    if DRAG_RESIZE_SUPPORTED {
38        // Keep track of cursor when it is within a resizeable border.
39        let mut cursor_prev_resize_direction = None;
40
41        Some(Box::new(
42            move |window: &dyn winit::window::Window,
43                  window_event: &winit::event::WindowEvent|
44                  -> bool {
45                // Keep track of border resize state and set cursor icon when in range
46                match window_event {
47                    winit::event::WindowEvent::CursorMoved {
48                        position, ..
49                    } => {
50                        if !window.is_decorated() {
51                            let location = cursor_resize_direction(
52                                window.surface_size(),
53                                *position,
54                                border_size,
55                            );
56                            if location != cursor_prev_resize_direction {
57                                window.set_cursor(
58                                    resize_direction_cursor_icon(location)
59                                        .into(),
60                                );
61                                cursor_prev_resize_direction = location;
62                                return true;
63                            }
64                        }
65                    }
66                    winit::event::WindowEvent::MouseInput {
67                        state: winit::event::ElementState::Pressed,
68                        button: winit::event::MouseButton::Left,
69                        ..
70                    } => {
71                        if let Some(direction) = cursor_prev_resize_direction {
72                            let _res = window.drag_resize_window(direction);
73                            return true;
74                        }
75                    }
76                    _ => (),
77                }
78
79                false
80            },
81        ))
82    } else {
83        None
84    }
85}
86
87/// Get the cursor icon that corresponds to the resize direction.
88fn resize_direction_cursor_icon(
89    resize_direction: Option<ResizeDirection>,
90) -> CursorIcon {
91    match resize_direction {
92        Some(resize_direction) => match resize_direction {
93            ResizeDirection::East => CursorIcon::EResize,
94            ResizeDirection::North => CursorIcon::NResize,
95            ResizeDirection::NorthEast => CursorIcon::NeResize,
96            ResizeDirection::NorthWest => CursorIcon::NwResize,
97            ResizeDirection::South => CursorIcon::SResize,
98            ResizeDirection::SouthEast => CursorIcon::SeResize,
99            ResizeDirection::SouthWest => CursorIcon::SwResize,
100            ResizeDirection::West => CursorIcon::WResize,
101        },
102        None => CursorIcon::Default,
103    }
104}
105
106/// Identifies resize direction based on cursor position and window dimensions.
107#[allow(clippy::similar_names)]
108fn cursor_resize_direction(
109    win_size: winit::dpi::PhysicalSize<u32>,
110    position: winit::dpi::PhysicalPosition<f64>,
111    border_size: f64,
112) -> Option<ResizeDirection> {
113    enum XDirection {
114        West,
115        East,
116        Default,
117    }
118
119    enum YDirection {
120        North,
121        South,
122        Default,
123    }
124
125    let xdir = if position.x < border_size {
126        XDirection::West
127    } else if position.x > (win_size.width as f64 - border_size) {
128        XDirection::East
129    } else {
130        XDirection::Default
131    };
132
133    let ydir = if position.y < border_size {
134        YDirection::North
135    } else if position.y > (win_size.height as f64 - border_size) {
136        YDirection::South
137    } else {
138        YDirection::Default
139    };
140
141    Some(match xdir {
142        XDirection::West => match ydir {
143            YDirection::North => ResizeDirection::NorthWest,
144            YDirection::South => ResizeDirection::SouthWest,
145            YDirection::Default => ResizeDirection::West,
146        },
147
148        XDirection::East => match ydir {
149            YDirection::North => ResizeDirection::NorthEast,
150            YDirection::South => ResizeDirection::SouthEast,
151            YDirection::Default => ResizeDirection::East,
152        },
153
154        XDirection::Default => match ydir {
155            YDirection::North => ResizeDirection::North,
156            YDirection::South => ResizeDirection::South,
157            YDirection::Default => return None,
158        },
159    })
160}