1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
//! Invisible resize handles for borderless [iced] windows.
//!
//! Setting `decorations: false` on an iced window removes the operating
//! system's window frame — and with it the native resize borders, making the
//! window impossible to resize with the mouse. This crate restores them with
//! a single overlay widget: [`ResizeHandles`] places eight invisible grab
//! zones (four edges and four larger corners) along the borders of a window.
//!
//! Hovering a zone shows the matching native resize cursor, and pressing one
//! publishes a message that the app maps to [`iced::window::drag_resize`],
//! which hands the drag to the operating system's own resize machinery:
//!
//! | Platform | Resize mechanism |
//! | --------- | ------------------------------------------------------------------- |
//! | Windows | Native size loop (`WM_NCLBUTTONDOWN` + `HT*` hit-test codes). |
//! | Linux/X11 | `_NET_WM_MOVERESIZE` — the window manager runs the resize. |
//! | Linux/Wayland | `xdg_toplevel.resize` — the compositor runs the resize. |
//! | macOS | Inert; borderless windows already edge-resize natively (winit keeps `NSWindowStyleMask::Resizable`). |
//!
//! No platform-specific window hacks are involved: the widget never touches
//! `WM_NCHITTEST`, `WS_THICKFRAME`, or anything else that could interfere
//! with native window snapping.
//!
//! [iced]: https://iced.rs
//!
//! # Usage
//!
//! Layer the handles on top of your UI in a [`stack`](iced::widget::stack) and
//! map the published message to [`iced::window::drag_resize`]:
//!
//! ```ignore
//! use iced::application;
//! use iced::widget::{stack, text};
//! use iced::window;
//! use iced_borderless_window_resize_handle_widget::{drag_resize, ResizeHandles};
//!
//! fn main() -> iced::Result {
//! application(Example::boot, update, view)
//! .window(window::Settings {
//! decorations: false, // frameless: no native resize borders
//! ..window::Settings::default()
//! })
//! .run()
//! }
//!
//! #[derive(Debug, Clone)]
//! enum Message {
//! WindowOpened(Option<window::Id>),
//! Resized(window::Direction),
//! }
//!
//! #[derive(Default)]
//! struct Example {
//! window_id: Option<window::Id>,
//! }
//!
//! impl Example {
//! fn boot() -> (Self, iced::Task<Message>) {
//! // iced has no `window::Id::MAIN`; grab the id of the first window.
//! (Self::default(), window::latest().map(Message::WindowOpened))
//! }
//! }
//!
//! fn update(example: &mut Example, message: Message) -> iced::Task<Message> {
//! match message {
//! Message::WindowOpened(id) => {
//! example.window_id = id;
//! iced::Task::none()
//! }
//! Message::Resized(direction) => example
//! .window_id
//! // `drag_resize` is a drop-in replacement for
//! // `iced::window::drag_resize` that also keeps the native resize
//! // cursor visible during the whole drag on Windows.
//! .map(|id| drag_resize(id, direction))
//! .unwrap_or_else(iced::Task::none),
//! }
//! }
//!
//! fn view(_example: &Example) -> iced::Element<'_, Message> {
//! stack![
//! text("Hello! Try resizing me from the edges."),
//! ResizeHandles::new(Message::Resized),
//! ]
//! .width(iced::Fill)
//! .height(iced::Fill)
//! .into()
//! }
//! ```
//!
//! If your message enum implements `From<window::Direction>`, you can skip the
//! closure entirely with [`ResizeHandles::from_direction`].
//!
//! For a one-liner, [`with_resize_handles`] wraps any element in the
//! `stack` for you:
//!
//! ```ignore
//! use iced_borderless_window_resize_handle_widget::with_resize_handles;
//!
//! fn view(state: &State) -> iced::Element<'_, Message> {
//! with_resize_handles(my_ui(state), Message::Resized)
//! }
//! ```
//!
//! # Notes
//!
//! * The grab zones consume clicks in the outermost [`ResizeHandles::gutter`]
//! pixels of the window — just like the invisible border of a native window
//! frame. Avoid placing interactive widgets flush against the window edges.
//! * Resizing a maximized window is ignored by the OS. If you prefer to hide
//! the handles while maximized, subscribe to the window state and use
//! [`ResizeHandles::enabled`] (see [`iced::window::is_maximized`]).
//! * In multi-window applications, capture the [`iced::window::Id`] of each
//! window (e.g. with [`iced::window::latest`] or
//! [`iced::window::open_events`]) and pass it into the message closure.
pub use drag_resize;
pub use ResizeHandles;
use renderer;
use Stack;
use window;
use ;
/// Wraps the given content in a [`Stack`] with [`ResizeHandles`] layered on
/// top, filling the available space.
///
/// This is a convenience shorthand for:
///
/// ```ignore
/// stack![
/// content,
/// ResizeHandles::new(on_resize),
/// ]
/// .width(Length::Fill)
/// .height(Length::Fill)
/// ```
///
/// # Example
///
/// ```ignore
/// use iced_borderless_window_resize_handle_widget::with_resize_handles;
///
/// fn view(state: &State) -> iced::Element<'_, Message> {
/// with_resize_handles(my_ui(state), Message::Resized)
/// }
/// ```