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