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