iced_runtime/window.rs
1//! Build window-based GUI applications.
2use crate::core::time::Instant;
3use crate::core::window::{
4 Direction, Event, Icon, Id, Level, Mode, Screenshot, Settings,
5 UserAttention,
6};
7use crate::core::{Point, Size};
8use crate::futures::Subscription;
9use crate::futures::event;
10use crate::futures::futures::channel::oneshot;
11use crate::task::{self, Task};
12
13pub use raw_window_handle;
14
15use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
16
17/// An operation to be performed on some window.
18pub enum Action {
19 /// Open a new window with some [`Settings`].
20 Open(Id, Settings, oneshot::Sender<Id>),
21
22 /// Close the window and exits the application.
23 Close(Id),
24
25 /// Gets the [`Id`] of the oldest window.
26 GetOldest(oneshot::Sender<Option<Id>>),
27
28 /// Gets the [`Id`] of the latest window.
29 GetLatest(oneshot::Sender<Option<Id>>),
30
31 /// Move the window with the left mouse button until the button is
32 /// released.
33 ///
34 /// There's no guarantee that this will work unless the left mouse
35 /// button was pressed immediately before this function is called.
36 Drag(Id),
37
38 /// Resize the window with the left mouse button until the button is
39 /// released.
40 ///
41 /// There's no guarantee that this will work unless the left mouse
42 /// button was pressed immediately before this function is called.
43 DragResize(Id, Direction),
44
45 /// Resize the window to the given logical dimensions.
46 Resize(Id, Size),
47
48 /// Get the current logical dimensions of the window.
49 GetSize(Id, oneshot::Sender<Size>),
50
51 /// Get if the current window is maximized or not.
52 GetMaximized(Id, oneshot::Sender<bool>),
53
54 /// Set the window to maximized or back
55 Maximize(Id, bool),
56
57 /// Get if the current window is minimized or not.
58 ///
59 /// ## Platform-specific
60 /// - **Wayland:** Always `None`.
61 GetMinimized(Id, oneshot::Sender<Option<bool>>),
62
63 /// Set the window to minimized or back
64 Minimize(Id, bool),
65
66 /// Get the current logical coordinates of the window.
67 GetPosition(Id, oneshot::Sender<Option<Point>>),
68
69 /// Get the current scale factor (DPI) of the window.
70 GetScaleFactor(Id, oneshot::Sender<f32>),
71
72 /// Move the window to the given logical coordinates.
73 ///
74 /// Unsupported on Wayland.
75 Move(Id, Point),
76
77 /// Change the [`Mode`] of the window.
78 SetMode(Id, Mode),
79
80 /// Get the current [`Mode`] of the window.
81 GetMode(Id, oneshot::Sender<Mode>),
82
83 /// Toggle the window to maximized or back
84 ToggleMaximize(Id),
85
86 /// Toggle whether window has decorations.
87 ///
88 /// ## Platform-specific
89 /// - **X11:** Not implemented.
90 /// - **Web:** Unsupported.
91 ToggleDecorations(Id),
92
93 /// Request user attention to the window, this has no effect if the application
94 /// is already focused. How requesting for user attention manifests is platform dependent,
95 /// see [`UserAttention`] for details.
96 ///
97 /// Providing `None` will unset the request for user attention. Unsetting the request for
98 /// user attention might not be done automatically by the WM when the window receives input.
99 ///
100 /// ## Platform-specific
101 ///
102 /// - **iOS / Android / Web:** Unsupported.
103 /// - **macOS:** `None` has no effect.
104 /// - **X11:** Requests for user attention must be manually cleared.
105 /// - **Wayland:** Requires `xdg_activation_v1` protocol, `None` has no effect.
106 RequestUserAttention(Id, Option<UserAttention>),
107
108 /// Bring the window to the front and sets input focus. Has no effect if the window is
109 /// already in focus, minimized, or not visible.
110 ///
111 /// This method steals input focus from other applications. Do not use this method unless
112 /// you are certain that's what the user wants. Focus stealing can cause an extremely disruptive
113 /// user experience.
114 ///
115 /// ## Platform-specific
116 ///
117 /// - **Web / Wayland:** Unsupported.
118 GainFocus(Id),
119
120 /// Change the window [`Level`].
121 SetLevel(Id, Level),
122
123 /// Show the system menu at cursor position.
124 ///
125 /// ## Platform-specific
126 /// Android / iOS / macOS / Orbital / Web / X11: Unsupported.
127 ShowSystemMenu(Id),
128
129 /// Get the raw identifier unique to the window.
130 GetRawId(Id, oneshot::Sender<u64>),
131
132 /// Change the window [`Icon`].
133 ///
134 /// On Windows and X11, this is typically the small icon in the top-left
135 /// corner of the titlebar.
136 ///
137 /// ## Platform-specific
138 ///
139 /// - **Web / Wayland / macOS:** Unsupported.
140 ///
141 /// - **Windows:** Sets `ICON_SMALL`. The base size for a window icon is 16x16, but it's
142 /// recommended to account for screen scaling and pick a multiple of that, i.e. 32x32.
143 ///
144 /// - **X11:** Has no universal guidelines for icon sizes, so you're at the whims of the WM. That
145 /// said, it's usually in the same ballpark as on Windows.
146 SetIcon(Id, Icon),
147
148 /// Runs the closure with a reference to the [`Window`] with the given [`Id`].
149 Run(Id, Box<dyn FnOnce(&dyn Window) + Send>),
150
151 /// Screenshot the viewport of the window.
152 Screenshot(Id, oneshot::Sender<Screenshot>),
153
154 /// Enable mouse passthrough for the given window.
155 ///
156 /// This disables mouse events for the window and passes mouse events
157 /// through to whatever window is underneath.
158 EnableMousePassthrough(Id),
159
160 /// Disable mouse passthrough for the given window.
161 ///
162 /// This enables mouse events for the window and stops mouse events
163 /// from being passed to whatever is underneath.
164 DisableMousePassthrough(Id),
165
166 /// Set the minimum inner window size.
167 SetMinSize(Id, Option<Size>),
168
169 /// Set the maximum inner window size.
170 SetMaxSize(Id, Option<Size>),
171
172 /// Set the window to be resizable or not.
173 SetResizable(Id, bool),
174
175 /// Set the window size increment.
176 SetResizeIncrements(Id, Option<Size>),
177
178 /// Get the logical dimensions of the monitor containing the window with the given [`Id`].
179 GetMonitorSize(Id, oneshot::Sender<Option<Size>>),
180
181 /// Set whether the system can automatically organize windows into tabs.
182 ///
183 /// See <https://developer.apple.com/documentation/appkit/nswindow/1646657-allowsautomaticwindowtabbing>
184 SetAllowAutomaticTabbing(bool),
185
186 /// Redraw all the windows.
187 RedrawAll,
188
189 /// Recompute the layouts of all the windows.
190 RelayoutAll,
191}
192
193/// A window managed by iced.
194///
195/// It implements both [`HasWindowHandle`] and [`HasDisplayHandle`].
196pub trait Window: HasWindowHandle + HasDisplayHandle {}
197
198impl<T> Window for T where T: HasWindowHandle + HasDisplayHandle {}
199
200/// Subscribes to the frames of the window of the running application.
201///
202/// The resulting [`Subscription`] will produce items at a rate equal to the
203/// refresh rate of the first application window. Note that this rate may be variable, as it is
204/// normally managed by the graphics driver and/or the OS.
205///
206/// In any case, this [`Subscription`] is useful to smoothly draw application-driven
207/// animations without missing any frames.
208pub fn frames() -> Subscription<Instant> {
209 event::listen_raw(|event, _status, _window| match event {
210 crate::core::Event::Window(Event::RedrawRequested(at)) => Some(at),
211 _ => None,
212 })
213}
214
215/// Subscribes to all window events of the running application.
216pub fn events() -> Subscription<(Id, Event)> {
217 event::listen_with(|event, _status, id| {
218 if let crate::core::Event::Window(event) = event {
219 Some((id, event))
220 } else {
221 None
222 }
223 })
224}
225
226/// Subscribes to all [`Event::Opened`] occurrences in the running application.
227pub fn open_events() -> Subscription<Id> {
228 event::listen_with(|event, _status, id| {
229 if let crate::core::Event::Window(Event::Opened { .. }) = event {
230 Some(id)
231 } else {
232 None
233 }
234 })
235}
236
237/// Subscribes to all [`Event::Closed`] occurrences in the running application.
238pub fn close_events() -> Subscription<Id> {
239 event::listen_with(|event, _status, id| {
240 if let crate::core::Event::Window(Event::Closed) = event {
241 Some(id)
242 } else {
243 None
244 }
245 })
246}
247
248/// Subscribes to all [`Event::Resized`] occurrences in the running application.
249pub fn resize_events() -> Subscription<(Id, Size)> {
250 event::listen_with(|event, _status, id| {
251 if let crate::core::Event::Window(Event::Resized(size)) = event {
252 Some((id, size))
253 } else {
254 None
255 }
256 })
257}
258
259/// Subscribes to all [`Event::CloseRequested`] occurrences in the running application.
260pub fn close_requests() -> Subscription<Id> {
261 event::listen_with(|event, _status, id| {
262 if let crate::core::Event::Window(Event::CloseRequested) = event {
263 Some(id)
264 } else {
265 None
266 }
267 })
268}
269
270/// Opens a new window with the given [`Settings`]; producing the [`Id`]
271/// of the new window on completion.
272pub fn open(settings: Settings) -> (Id, Task<Id>) {
273 let id = Id::unique();
274
275 (
276 id,
277 task::oneshot(|channel| {
278 crate::Action::Window(Action::Open(id, settings, channel))
279 }),
280 )
281}
282
283/// Closes the window with `id`.
284pub fn close<T>(id: Id) -> Task<T> {
285 task::effect(crate::Action::Window(Action::Close(id)))
286}
287
288/// Gets the window [`Id`] of the oldest window.
289pub fn oldest() -> Task<Option<Id>> {
290 task::oneshot(|channel| crate::Action::Window(Action::GetOldest(channel)))
291}
292
293/// Gets the window [`Id`] of the latest window.
294pub fn latest() -> Task<Option<Id>> {
295 task::oneshot(|channel| crate::Action::Window(Action::GetLatest(channel)))
296}
297
298/// Begins dragging the window while the left mouse button is held.
299pub fn drag<T>(id: Id) -> Task<T> {
300 task::effect(crate::Action::Window(Action::Drag(id)))
301}
302
303/// Begins resizing the window while the left mouse button is held.
304pub fn drag_resize<T>(id: Id, direction: Direction) -> Task<T> {
305 task::effect(crate::Action::Window(Action::DragResize(id, direction)))
306}
307
308/// Resizes the window to the given logical dimensions.
309pub fn resize<T>(id: Id, new_size: Size) -> Task<T> {
310 task::effect(crate::Action::Window(Action::Resize(id, new_size)))
311}
312
313/// Set the window to be resizable or not.
314pub fn set_resizable<T>(id: Id, resizable: bool) -> Task<T> {
315 task::effect(crate::Action::Window(Action::SetResizable(id, resizable)))
316}
317
318/// Set the inner maximum size of the window.
319pub fn set_max_size<T>(id: Id, size: Option<Size>) -> Task<T> {
320 task::effect(crate::Action::Window(Action::SetMaxSize(id, size)))
321}
322
323/// Set the inner minimum size of the window.
324pub fn set_min_size<T>(id: Id, size: Option<Size>) -> Task<T> {
325 task::effect(crate::Action::Window(Action::SetMinSize(id, size)))
326}
327
328/// Set the window size increment.
329///
330/// This is usually used by apps such as terminal emulators that need "blocky" resizing.
331pub fn set_resize_increments<T>(id: Id, increments: Option<Size>) -> Task<T> {
332 task::effect(crate::Action::Window(Action::SetResizeIncrements(
333 id, increments,
334 )))
335}
336
337/// Gets the window size in logical dimensions.
338pub fn size(id: Id) -> Task<Size> {
339 task::oneshot(move |channel| {
340 crate::Action::Window(Action::GetSize(id, channel))
341 })
342}
343
344/// Gets the maximized state of the window with the given [`Id`].
345pub fn is_maximized(id: Id) -> Task<bool> {
346 task::oneshot(move |channel| {
347 crate::Action::Window(Action::GetMaximized(id, channel))
348 })
349}
350
351/// Maximizes the window.
352pub fn maximize<T>(id: Id, maximized: bool) -> Task<T> {
353 task::effect(crate::Action::Window(Action::Maximize(id, maximized)))
354}
355
356/// Gets the minimized state of the window with the given [`Id`].
357pub fn is_minimized(id: Id) -> Task<Option<bool>> {
358 task::oneshot(move |channel| {
359 crate::Action::Window(Action::GetMinimized(id, channel))
360 })
361}
362
363/// Minimizes the window.
364pub fn minimize<T>(id: Id, minimized: bool) -> Task<T> {
365 task::effect(crate::Action::Window(Action::Minimize(id, minimized)))
366}
367
368/// Gets the position in logical coordinates of the window with the given [`Id`].
369pub fn position(id: Id) -> Task<Option<Point>> {
370 task::oneshot(move |channel| {
371 crate::Action::Window(Action::GetPosition(id, channel))
372 })
373}
374
375/// Gets the scale factor of the window with the given [`Id`].
376pub fn scale_factor(id: Id) -> Task<f32> {
377 task::oneshot(move |channel| {
378 crate::Action::Window(Action::GetScaleFactor(id, channel))
379 })
380}
381
382/// Moves the window to the given logical coordinates.
383pub fn move_to<T>(id: Id, position: Point) -> Task<T> {
384 task::effect(crate::Action::Window(Action::Move(id, position)))
385}
386
387/// Gets the current [`Mode`] of the window.
388pub fn mode(id: Id) -> Task<Mode> {
389 task::oneshot(move |channel| {
390 crate::Action::Window(Action::GetMode(id, channel))
391 })
392}
393
394/// Changes the [`Mode`] of the window.
395pub fn set_mode<T>(id: Id, mode: Mode) -> Task<T> {
396 task::effect(crate::Action::Window(Action::SetMode(id, mode)))
397}
398
399/// Toggles the window to maximized or back.
400pub fn toggle_maximize<T>(id: Id) -> Task<T> {
401 task::effect(crate::Action::Window(Action::ToggleMaximize(id)))
402}
403
404/// Toggles the window decorations.
405pub fn toggle_decorations<T>(id: Id) -> Task<T> {
406 task::effect(crate::Action::Window(Action::ToggleDecorations(id)))
407}
408
409/// Requests user attention to the window. This has no effect if the application
410/// is already focused. How requesting for user attention manifests is platform dependent,
411/// see [`UserAttention`] for details.
412///
413/// Providing `None` will unset the request for user attention. Unsetting the request for
414/// user attention might not be done automatically by the WM when the window receives input.
415pub fn request_user_attention<T>(
416 id: Id,
417 user_attention: Option<UserAttention>,
418) -> Task<T> {
419 task::effect(crate::Action::Window(Action::RequestUserAttention(
420 id,
421 user_attention,
422 )))
423}
424
425/// Brings the window to the front and sets input focus. Has no effect if the window is
426/// already in focus, minimized, or not visible.
427///
428/// This [`Task`] steals input focus from other applications. Do not use this method unless
429/// you are certain that's what the user wants. Focus stealing can cause an extremely disruptive
430/// user experience.
431pub fn gain_focus<T>(id: Id) -> Task<T> {
432 task::effect(crate::Action::Window(Action::GainFocus(id)))
433}
434
435/// Changes the window [`Level`].
436pub fn set_level<T>(id: Id, level: Level) -> Task<T> {
437 task::effect(crate::Action::Window(Action::SetLevel(id, level)))
438}
439
440/// Shows the [system menu] at cursor position.
441///
442/// [system menu]: https://en.wikipedia.org/wiki/Common_menus_in_Microsoft_Windows#System_menu
443pub fn show_system_menu<T>(id: Id) -> Task<T> {
444 task::effect(crate::Action::Window(Action::ShowSystemMenu(id)))
445}
446
447/// Gets an identifier unique to the window, provided by the underlying windowing system. This is
448/// not to be confused with [`Id`].
449pub fn raw_id<Message>(id: Id) -> Task<u64> {
450 task::oneshot(|channel| {
451 crate::Action::Window(Action::GetRawId(id, channel))
452 })
453}
454
455/// Changes the [`Icon`] of the window.
456pub fn set_icon<T>(id: Id, icon: Icon) -> Task<T> {
457 task::effect(crate::Action::Window(Action::SetIcon(id, icon)))
458}
459
460/// Runs the given callback with a reference to the [`Window`] with the given [`Id`].
461///
462/// Note that if the window closes before this call is processed the callback will not be run.
463pub fn run<T>(
464 id: Id,
465 f: impl FnOnce(&dyn Window) -> T + Send + 'static,
466) -> Task<T>
467where
468 T: Send + 'static,
469{
470 task::oneshot(move |channel| {
471 crate::Action::Window(Action::Run(
472 id,
473 Box::new(move |handle| {
474 let _ = channel.send(f(handle));
475 }),
476 ))
477 })
478}
479
480/// Captures a [`Screenshot`] from the window.
481pub fn screenshot(id: Id) -> Task<Screenshot> {
482 task::oneshot(move |channel| {
483 crate::Action::Window(Action::Screenshot(id, channel))
484 })
485}
486
487/// Enables mouse passthrough for the given window.
488///
489/// This disables mouse events for the window and passes mouse events
490/// through to whatever window is underneath.
491pub fn enable_mouse_passthrough<Message>(id: Id) -> Task<Message> {
492 task::effect(crate::Action::Window(Action::EnableMousePassthrough(id)))
493}
494
495/// Disables mouse passthrough for the given window.
496///
497/// This enables mouse events for the window and stops mouse events
498/// from being passed to whatever is underneath.
499pub fn disable_mouse_passthrough<Message>(id: Id) -> Task<Message> {
500 task::effect(crate::Action::Window(Action::DisableMousePassthrough(id)))
501}
502
503/// Gets the logical dimensions of the monitor containing the window with the given [`Id`].
504pub fn monitor_size(id: Id) -> Task<Option<Size>> {
505 task::oneshot(move |channel| {
506 crate::Action::Window(Action::GetMonitorSize(id, channel))
507 })
508}
509
510/// Sets whether the system can automatically organize windows into tabs.
511///
512/// See <https://developer.apple.com/documentation/appkit/nswindow/1646657-allowsautomaticwindowtabbing>
513pub fn allow_automatic_tabbing<T>(enabled: bool) -> Task<T> {
514 task::effect(crate::Action::Window(Action::SetAllowAutomaticTabbing(
515 enabled,
516 )))
517}