egui/
viewport.rs

1//! egui supports multiple viewports, corresponding to multiple native windows.
2//!
3//! Not all egui backends support multiple viewports, but `eframe` native does
4//! (but not on web).
5//!
6//! You can spawn a new viewport using [`Context::show_viewport_deferred`] and [`Context::show_viewport_immediate`].
7//! These needs to be called every frame the viewport should be visible.
8//!
9//! This is implemented by the native `eframe` backend, but not the web one.
10//!
11//! ## Viewport classes
12//! The viewports form a tree of parent-child relationships.
13//!
14//! There are different classes of viewports.
15//!
16//! ### Root viewport
17//! The root viewport is the original viewport, and cannot be closed without closing the application.
18//!
19//! ### Deferred viewports
20//! These are created with [`Context::show_viewport_deferred`].
21//! Deferred viewports take a closure that is called by the integration at a later time, perhaps multiple times.
22//! Deferred viewports are repainted independently of the parent viewport.
23//! This means communication with them needs to be done via channels, or `Arc/Mutex`.
24//!
25//! This is the most performant type of child viewport, though a bit more cumbersome to work with compared to immediate viewports.
26//!
27//! ### Immediate viewports
28//! These are created with [`Context::show_viewport_immediate`].
29//! Immediate viewports take a `FnOnce` closure, similar to other egui functions, and is called immediately.
30//! This makes communication with them much simpler than with deferred viewports, but this simplicity comes at a cost: whenever the parent viewports needs to be repainted, so will the child viewport, and vice versa.
31//! This means that if you have `N` viewports you are potentially doing `N` times as much CPU work. However, if all your viewports are showing animations, and thus are repainting constantly anyway, this doesn't matter.
32//!
33//! In short: immediate viewports are simpler to use, but can waste a lot of CPU time.
34//!
35//! ### Embedded viewports
36//! These are not real, independent viewports, but is a fallback mode for when the integration does not support real viewports. In your callback is called with [`ViewportClass::Embedded`] it means you need to create a [`crate::Window`] to wrap your ui in, which will then be embedded in the parent viewport, unable to escape it.
37//!
38//!
39//! ## Using the viewports
40//! Only one viewport is active at any one time, identified with [`Context::viewport_id`].
41//! You can modify the current (change the title, resize the window, etc) by sending
42//! a [`ViewportCommand`] to it using [`Context::send_viewport_cmd`].
43//! You can interact with other viewports using [`Context::send_viewport_cmd_to`].
44//!
45//! There is an example in <https://github.com/emilk/egui/tree/main/examples/multiple_viewports/src/main.rs>.
46//!
47//! You can find all available viewports in [`crate::RawInput::viewports`] and the active viewport in
48//! [`crate::InputState::viewport`]:
49//!
50//! ```no_run
51//! # let ctx = &egui::Context::default();
52//! ctx.input(|i| {
53//!     dbg!(&i.viewport()); // Current viewport
54//!     dbg!(&i.raw.viewports); // All viewports
55//! });
56//! ```
57//!
58//! ## For integrations
59//! * There is a [`crate::InputState::viewport`] with information about the current viewport.
60//! * There is a [`crate::RawInput::viewports`] with information about all viewports.
61//! * The repaint callback set by [`Context::set_request_repaint_callback`] points to which viewport should be repainted.
62//! * [`crate::FullOutput::viewport_output`] is a list of viewports which should result in their own independent windows.
63//! * To support immediate viewports you need to call [`Context::set_immediate_viewport_renderer`].
64//! * If you support viewports, you need to call [`Context::set_embed_viewports`] with `false`, or all new viewports will be embedded (the default behavior).
65//!
66//! ## Future work
67//! There are several more things related to viewports that we want to add.
68//! Read more at <https://github.com/emilk/egui/issues/3556>.
69
70use std::sync::Arc;
71
72use epaint::{Pos2, Vec2};
73
74use crate::{Context, Id};
75
76// ----------------------------------------------------------------------------
77
78/// The different types of viewports supported by egui.
79#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]
80#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
81pub enum ViewportClass {
82    /// The root viewport; i.e. the original window.
83    #[default]
84    Root,
85
86    /// A viewport run independently from the parent viewport.
87    ///
88    /// This is the preferred type of viewport from a performance perspective.
89    ///
90    /// Create these with [`crate::Context::show_viewport_deferred`].
91    Deferred,
92
93    /// A viewport run inside the parent viewport.
94    ///
95    /// This is the easier type of viewport to use, but it is less performant
96    /// at it requires both parent and child to repaint if any one of them needs repainting,
97    /// which effectively produces double work for two viewports, and triple work for three viewports, etc.
98    ///
99    /// Create these with [`crate::Context::show_viewport_immediate`].
100    Immediate,
101
102    /// The fallback, when the egui integration doesn't support viewports,
103    /// or [`crate::Context::embed_viewports`] is set to `true`.
104    Embedded,
105}
106
107// ----------------------------------------------------------------------------
108
109/// A unique identifier of a viewport.
110///
111/// This is returned by [`Context::viewport_id`] and [`Context::parent_viewport_id`].
112#[derive(Clone, Copy, Hash, PartialEq, Eq)]
113#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
114pub struct ViewportId(pub Id);
115
116// We implement `PartialOrd` and `Ord` so we can use `ViewportId` in a `BTreeMap`,
117// which allows predicatable iteration order, frame-to-frame.
118impl PartialOrd for ViewportId {
119    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
120        Some(self.cmp(other))
121    }
122}
123
124impl Ord for ViewportId {
125    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
126        self.0.value().cmp(&other.0.value())
127    }
128}
129
130impl Default for ViewportId {
131    #[inline]
132    fn default() -> Self {
133        Self::ROOT
134    }
135}
136
137impl std::fmt::Debug for ViewportId {
138    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139        self.0.short_debug_format().fmt(f)
140    }
141}
142
143impl ViewportId {
144    /// The `ViewportId` of the root viewport.
145    pub const ROOT: Self = Self(Id::NULL);
146
147    #[inline]
148    pub fn from_hash_of(source: impl std::hash::Hash) -> Self {
149        Self(Id::new(source))
150    }
151}
152
153impl From<ViewportId> for Id {
154    #[inline]
155    fn from(id: ViewportId) -> Self {
156        id.0
157    }
158}
159
160impl nohash_hasher::IsEnabled for ViewportId {}
161
162/// A fast hash set of [`ViewportId`].
163pub type ViewportIdSet = nohash_hasher::IntSet<ViewportId>;
164
165/// A fast hash map from [`ViewportId`] to `T`.
166pub type ViewportIdMap<T> = nohash_hasher::IntMap<ViewportId, T>;
167
168/// An order map from [`ViewportId`] to `T`.
169pub type OrderedViewportIdMap<T> = std::collections::BTreeMap<ViewportId, T>;
170
171// ----------------------------------------------------------------------------
172
173/// Image data for an application icon.
174///
175/// Use a square image, e.g. 256x256 pixels.
176/// You can use a transparent background.
177#[derive(Clone, Default, PartialEq, Eq)]
178#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
179pub struct IconData {
180    /// RGBA pixels, with separate/unmultiplied alpha.
181    pub rgba: Vec<u8>,
182
183    /// Image width. This should be a multiple of 4.
184    pub width: u32,
185
186    /// Image height. This should be a multiple of 4.
187    pub height: u32,
188}
189
190impl IconData {
191    #[inline]
192    pub fn is_empty(&self) -> bool {
193        self.rgba.is_empty()
194    }
195}
196
197impl std::fmt::Debug for IconData {
198    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199        f.debug_struct("IconData")
200            .field("width", &self.width)
201            .field("height", &self.height)
202            .finish_non_exhaustive()
203    }
204}
205
206impl From<IconData> for epaint::ColorImage {
207    fn from(icon: IconData) -> Self {
208        profiling::function_scope!();
209        let IconData {
210            rgba,
211            width,
212            height,
213        } = icon;
214        Self::from_rgba_premultiplied([width as usize, height as usize], &rgba)
215    }
216}
217
218impl From<&IconData> for epaint::ColorImage {
219    fn from(icon: &IconData) -> Self {
220        profiling::function_scope!();
221        let IconData {
222            rgba,
223            width,
224            height,
225        } = icon;
226        Self::from_rgba_premultiplied([*width as usize, *height as usize], rgba)
227    }
228}
229
230// ----------------------------------------------------------------------------
231
232/// A pair of [`ViewportId`], used to identify a viewport and its parent.
233#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)]
234#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
235pub struct ViewportIdPair {
236    pub this: ViewportId,
237    pub parent: ViewportId,
238}
239
240impl Default for ViewportIdPair {
241    #[inline]
242    fn default() -> Self {
243        Self::ROOT
244    }
245}
246
247impl ViewportIdPair {
248    /// The `ViewportIdPair` of the root viewport, which is its own parent.
249    pub const ROOT: Self = Self {
250        this: ViewportId::ROOT,
251        parent: ViewportId::ROOT,
252    };
253
254    #[inline]
255    pub fn from_self_and_parent(this: ViewportId, parent: ViewportId) -> Self {
256        Self { this, parent }
257    }
258}
259
260/// The user-code that shows the ui in the viewport, used for deferred viewports.
261pub type DeferredViewportUiCallback = dyn Fn(&Context) + Sync + Send;
262
263/// Render the given viewport, calling the given ui callback.
264pub type ImmediateViewportRendererCallback = dyn for<'a> Fn(&Context, ImmediateViewport<'a>);
265
266/// Control the building of a new egui viewport (i.e. native window).
267///
268/// See [`crate::viewport`] for how to build new viewports (native windows).
269///
270/// The fields are public, but you should use the builder pattern to set them,
271/// and that's where you'll find the documentation too.
272///
273/// Since egui is immediate mode, `ViewportBuilder` is accumulative in nature.
274/// Setting any option to `None` means "keep the current value",
275/// or "Use the default" if it is the first call.
276///
277/// The default values are implementation defined, so you may want to explicitly
278/// configure the size of the window, and what buttons are shown.
279#[derive(Clone, Debug, Default, Eq, PartialEq)]
280pub struct ViewportBuilder {
281    /// The title of the viewport.
282    /// `eframe` will use this as the title of the native window.
283    pub title: Option<String>,
284
285    /// This is wayland only. See [`Self::with_app_id`].
286    pub app_id: Option<String>,
287
288    /// The desired outer position of the window.
289    pub position: Option<Pos2>,
290    pub inner_size: Option<Vec2>,
291    pub min_inner_size: Option<Vec2>,
292    pub max_inner_size: Option<Vec2>,
293
294    /// Whether clamp the window's size to monitor's size. The default is `true` on linux, otherwise it is `false`.
295    ///
296    /// Note: On some Linux systems, a window size larger than the monitor causes crashes
297    pub clamp_size_to_monitor_size: Option<bool>,
298
299    pub fullscreen: Option<bool>,
300    pub maximized: Option<bool>,
301    pub resizable: Option<bool>,
302    pub transparent: Option<bool>,
303    pub decorations: Option<bool>,
304    pub icon: Option<Arc<IconData>>,
305    pub active: Option<bool>,
306    pub visible: Option<bool>,
307
308    // macOS:
309    pub fullsize_content_view: Option<bool>,
310    pub movable_by_window_background: Option<bool>,
311    pub title_shown: Option<bool>,
312    pub titlebar_buttons_shown: Option<bool>,
313    pub titlebar_shown: Option<bool>,
314    pub has_shadow: Option<bool>,
315
316    // windows:
317    pub drag_and_drop: Option<bool>,
318    pub taskbar: Option<bool>,
319
320    pub close_button: Option<bool>,
321    pub minimize_button: Option<bool>,
322    pub maximize_button: Option<bool>,
323
324    pub window_level: Option<WindowLevel>,
325
326    pub mouse_passthrough: Option<bool>,
327
328    // X11
329    pub window_type: Option<X11WindowType>,
330}
331
332impl ViewportBuilder {
333    /// Sets the initial title of the window in the title bar.
334    ///
335    /// Look at winit for more details
336    #[inline]
337    pub fn with_title(mut self, title: impl Into<String>) -> Self {
338        self.title = Some(title.into());
339        self
340    }
341
342    /// Sets whether the window should have a border, a title bar, etc.
343    ///
344    /// The default is `true`.
345    ///
346    /// Look at winit for more details
347    #[inline]
348    pub fn with_decorations(mut self, decorations: bool) -> Self {
349        self.decorations = Some(decorations);
350        self
351    }
352
353    /// Sets whether the window should be put into fullscreen upon creation.
354    ///
355    /// The default is `None`.
356    ///
357    /// Look at winit for more details
358    /// This will use borderless
359    #[inline]
360    pub fn with_fullscreen(mut self, fullscreen: bool) -> Self {
361        self.fullscreen = Some(fullscreen);
362        self
363    }
364
365    /// Request that the window is maximized upon creation.
366    ///
367    /// The default is `false`.
368    ///
369    /// Look at winit for more details
370    #[inline]
371    pub fn with_maximized(mut self, maximized: bool) -> Self {
372        self.maximized = Some(maximized);
373        self
374    }
375
376    /// Sets whether the window is resizable or not.
377    ///
378    /// The default is `true`.
379    ///
380    /// Look at winit for more details
381    #[inline]
382    pub fn with_resizable(mut self, resizable: bool) -> Self {
383        self.resizable = Some(resizable);
384        self
385    }
386
387    /// Sets whether the background of the window should be transparent.
388    ///
389    /// You should avoid having a [`crate::CentralPanel`], or make sure its frame is also transparent.
390    ///
391    /// In `eframe` you control the transparency with `eframe::App::clear_color()`.
392    ///
393    /// If this is `true`, writing colors with alpha values different than
394    /// `1.0` will produce a transparent window. On some platforms this
395    /// is more of a hint for the system and you'd still have the alpha
396    /// buffer.
397    ///
398    /// The default is `false`.
399    /// If this is not working, it's because the graphic context doesn't support transparency,
400    /// you will need to set the transparency in the eframe!
401    ///
402    /// ## Platform-specific
403    ///
404    /// **macOS:** When using this feature to create an overlay-like UI, you likely want to combine this with [`Self::with_has_shadow`] set to `false` in order to avoid ghosting artifacts.
405    #[inline]
406    pub fn with_transparent(mut self, transparent: bool) -> Self {
407        self.transparent = Some(transparent);
408        self
409    }
410
411    /// The application icon, e.g. in the Windows task bar or the alt-tab menu.
412    ///
413    /// The default icon is a white `e` on a black background (for "egui" or "eframe").
414    /// If you prefer the OS default, set this to `IconData::default()`.
415    #[inline]
416    pub fn with_icon(mut self, icon: impl Into<Arc<IconData>>) -> Self {
417        self.icon = Some(icon.into());
418        self
419    }
420
421    /// Whether the window will be initially focused or not.
422    ///
423    /// The window should be assumed as not focused by default
424    ///
425    /// ## Platform-specific:
426    ///
427    /// **Android / iOS / X11 / Wayland / Orbital:** Unsupported.
428    ///
429    /// Look at winit for more details
430    #[inline]
431    pub fn with_active(mut self, active: bool) -> Self {
432        self.active = Some(active);
433        self
434    }
435
436    /// Sets whether the window will be initially visible or hidden.
437    ///
438    /// The default is to show the window.
439    ///
440    /// Look at winit for more details
441    #[inline]
442    pub fn with_visible(mut self, visible: bool) -> Self {
443        self.visible = Some(visible);
444        self
445    }
446
447    /// macOS: Makes the window content appear behind the titlebar.
448    ///
449    /// You often want to combine this with [`Self::with_titlebar_shown`]
450    /// and [`Self::with_title_shown`].
451    #[inline]
452    pub fn with_fullsize_content_view(mut self, value: bool) -> Self {
453        self.fullsize_content_view = Some(value);
454        self
455    }
456
457    /// macOS: Set to `true` to allow the window to be moved by dragging the background.
458    /// Enabling this feature can result in unexpected behavior with draggable UI widgets such as sliders.
459    #[inline]
460    pub fn with_movable_by_background(mut self, value: bool) -> Self {
461        self.movable_by_window_background = Some(value);
462        self
463    }
464
465    /// macOS: Set to `false` to hide the window title.
466    #[inline]
467    pub fn with_title_shown(mut self, title_shown: bool) -> Self {
468        self.title_shown = Some(title_shown);
469        self
470    }
471
472    /// macOS: Set to `false` to hide the titlebar button (close, minimize, maximize)
473    #[inline]
474    pub fn with_titlebar_buttons_shown(mut self, titlebar_buttons_shown: bool) -> Self {
475        self.titlebar_buttons_shown = Some(titlebar_buttons_shown);
476        self
477    }
478
479    /// macOS: Set to `false` to make the titlebar transparent, allowing the content to appear behind it.
480    #[inline]
481    pub fn with_titlebar_shown(mut self, shown: bool) -> Self {
482        self.titlebar_shown = Some(shown);
483        self
484    }
485
486    /// macOS: Set to `false` to make the window render without a drop shadow.
487    ///
488    /// The default is `true`.
489    ///
490    /// Disabling this feature can solve ghosting issues experienced if using [`Self::with_transparent`].
491    ///
492    /// Look at winit for more details
493    #[inline]
494    pub fn with_has_shadow(mut self, has_shadow: bool) -> Self {
495        self.has_shadow = Some(has_shadow);
496        self
497    }
498
499    /// windows: Whether show or hide the window icon in the taskbar.
500    #[inline]
501    pub fn with_taskbar(mut self, show: bool) -> Self {
502        self.taskbar = Some(show);
503        self
504    }
505
506    /// Requests the window to be of specific dimensions.
507    ///
508    /// If this is not set, some platform-specific dimensions will be used.
509    ///
510    /// Should be bigger than 0
511    /// Look at winit for more details
512    #[inline]
513    pub fn with_inner_size(mut self, size: impl Into<Vec2>) -> Self {
514        self.inner_size = Some(size.into());
515        self
516    }
517
518    /// Sets the minimum dimensions a window can have.
519    ///
520    /// If this is not set, the window will have no minimum dimensions (aside
521    /// from reserved).
522    ///
523    /// Should be bigger than 0
524    /// Look at winit for more details
525    #[inline]
526    pub fn with_min_inner_size(mut self, size: impl Into<Vec2>) -> Self {
527        self.min_inner_size = Some(size.into());
528        self
529    }
530
531    /// Sets the maximum dimensions a window can have.
532    ///
533    /// If this is not set, the window will have no maximum or will be set to
534    /// the primary monitor's dimensions by the platform.
535    ///
536    /// Should be bigger than 0
537    /// Look at winit for more details
538    #[inline]
539    pub fn with_max_inner_size(mut self, size: impl Into<Vec2>) -> Self {
540        self.max_inner_size = Some(size.into());
541        self
542    }
543
544    /// Sets whether clamp the window's size to monitor's size. The default is `true` on linux, otherwise it is `false`.
545    ///
546    /// Note: On some Linux systems, a window size larger than the monitor causes crashes
547    #[inline]
548    pub fn with_clamp_size_to_monitor_size(mut self, value: bool) -> Self {
549        self.clamp_size_to_monitor_size = Some(value);
550        self
551    }
552
553    /// Does not work on X11.
554    #[inline]
555    pub fn with_close_button(mut self, value: bool) -> Self {
556        self.close_button = Some(value);
557        self
558    }
559
560    /// Does not work on X11.
561    #[inline]
562    pub fn with_minimize_button(mut self, value: bool) -> Self {
563        self.minimize_button = Some(value);
564        self
565    }
566
567    /// Does not work on X11.
568    #[inline]
569    pub fn with_maximize_button(mut self, value: bool) -> Self {
570        self.maximize_button = Some(value);
571        self
572    }
573
574    /// On Windows: enable drag and drop support. Drag and drop can
575    /// not be disabled on other platforms.
576    ///
577    /// See [winit's documentation][drag_and_drop] for information on why you
578    /// might want to disable this on windows.
579    ///
580    /// [drag_and_drop]: https://docs.rs/winit/latest/x86_64-pc-windows-msvc/winit/platform/windows/trait.WindowAttributesExtWindows.html#tymethod.with_drag_and_drop
581    #[inline]
582    pub fn with_drag_and_drop(mut self, value: bool) -> Self {
583        self.drag_and_drop = Some(value);
584        self
585    }
586
587    /// The initial "outer" position of the window,
588    /// i.e. where the top-left corner of the frame/chrome should be.
589    ///
590    /// **`eframe` notes**:
591    ///
592    /// - **iOS:** Sets the top left coordinates of the window in the screen space coordinate system.
593    /// - **Web:** Sets the top-left coordinates relative to the viewport. Doesn't account for CSS
594    ///   [`transform`].
595    /// - **Android / Wayland:** Unsupported.
596    ///
597    /// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
598    #[inline]
599    pub fn with_position(mut self, pos: impl Into<Pos2>) -> Self {
600        self.position = Some(pos.into());
601        self
602    }
603
604    /// ### On Wayland
605    /// On Wayland this sets the Application ID for the window.
606    ///
607    /// The application ID is used in several places of the compositor, e.g. for
608    /// grouping windows of the same application. It is also important for
609    /// connecting the configuration of a `.desktop` file with the window, by
610    /// using the application ID as file name. This allows e.g. a proper icon
611    /// handling under Wayland.
612    ///
613    /// See [Waylands XDG shell documentation][xdg-shell] for more information
614    /// on this Wayland-specific option.
615    ///
616    /// The `app_id` should match the `.desktop` file distributed with your program.
617    ///
618    /// For details about application ID conventions, see the
619    /// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)
620    ///
621    /// [xdg-shell]: https://wayland.app/protocols/xdg-shell#xdg_toplevel:request:set_app_id
622    ///
623    /// ### eframe
624    /// On eframe, the `app_id` of the root window is also used to determine
625    /// the storage location of persistence files.
626    #[inline]
627    pub fn with_app_id(mut self, app_id: impl Into<String>) -> Self {
628        self.app_id = Some(app_id.into());
629        self
630    }
631
632    /// Control if window is always-on-top, always-on-bottom, or neither.
633    ///
634    /// For platform compatibility see [`crate::viewport::WindowLevel`] documentation
635    #[inline]
636    pub fn with_window_level(mut self, level: WindowLevel) -> Self {
637        self.window_level = Some(level);
638        self
639    }
640
641    /// This window is always on top
642    ///
643    /// For platform compatibility see [`crate::viewport::WindowLevel`] documentation
644    #[inline]
645    pub fn with_always_on_top(self) -> Self {
646        self.with_window_level(WindowLevel::AlwaysOnTop)
647    }
648
649    /// On desktop: mouse clicks pass through the window, used for non-interactable overlays.
650    ///
651    /// Generally you would use this in conjunction with [`Self::with_transparent`]
652    /// and [`Self::with_always_on_top`].
653    #[inline]
654    pub fn with_mouse_passthrough(mut self, value: bool) -> Self {
655        self.mouse_passthrough = Some(value);
656        self
657    }
658
659    /// ### On X11
660    /// This sets the window type.
661    /// Maps directly to [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html).
662    #[inline]
663    pub fn with_window_type(mut self, value: X11WindowType) -> Self {
664        self.window_type = Some(value);
665        self
666    }
667
668    /// Update this `ViewportBuilder` with a delta,
669    /// returning a list of commands and a bool indicating if the window needs to be recreated.
670    #[must_use]
671    pub fn patch(&mut self, new_vp_builder: Self) -> (Vec<ViewportCommand>, bool) {
672        #![expect(clippy::useless_let_if_seq)] // False positive
673
674        let Self {
675            title: new_title,
676            app_id: new_app_id,
677            position: new_position,
678            inner_size: new_inner_size,
679            min_inner_size: new_min_inner_size,
680            max_inner_size: new_max_inner_size,
681            clamp_size_to_monitor_size: new_clamp_size_to_monitor_size,
682            fullscreen: new_fullscreen,
683            maximized: new_maximized,
684            resizable: new_resizable,
685            transparent: new_transparent,
686            decorations: new_decorations,
687            icon: new_icon,
688            active: new_active,
689            visible: new_visible,
690            drag_and_drop: new_drag_and_drop,
691            fullsize_content_view: new_fullsize_content_view,
692            movable_by_window_background: new_movable_by_window_background,
693            title_shown: new_title_shown,
694            titlebar_buttons_shown: new_titlebar_buttons_shown,
695            titlebar_shown: new_titlebar_shown,
696            has_shadow: new_has_shadow,
697            close_button: new_close_button,
698            minimize_button: new_minimize_button,
699            maximize_button: new_maximize_button,
700            window_level: new_window_level,
701            mouse_passthrough: new_mouse_passthrough,
702            taskbar: new_taskbar,
703            window_type: new_window_type,
704        } = new_vp_builder;
705
706        let mut commands = Vec::new();
707
708        if let Some(new_title) = new_title
709            && Some(&new_title) != self.title.as_ref()
710        {
711            self.title = Some(new_title.clone());
712            commands.push(ViewportCommand::Title(new_title));
713        }
714
715        if let Some(new_position) = new_position
716            && Some(new_position) != self.position
717        {
718            self.position = Some(new_position);
719            commands.push(ViewportCommand::OuterPosition(new_position));
720        }
721
722        if let Some(new_inner_size) = new_inner_size
723            && Some(new_inner_size) != self.inner_size
724        {
725            self.inner_size = Some(new_inner_size);
726            commands.push(ViewportCommand::InnerSize(new_inner_size));
727        }
728
729        if let Some(new_min_inner_size) = new_min_inner_size
730            && Some(new_min_inner_size) != self.min_inner_size
731        {
732            self.min_inner_size = Some(new_min_inner_size);
733            commands.push(ViewportCommand::MinInnerSize(new_min_inner_size));
734        }
735
736        if let Some(new_max_inner_size) = new_max_inner_size
737            && Some(new_max_inner_size) != self.max_inner_size
738        {
739            self.max_inner_size = Some(new_max_inner_size);
740            commands.push(ViewportCommand::MaxInnerSize(new_max_inner_size));
741        }
742
743        if let Some(new_fullscreen) = new_fullscreen
744            && Some(new_fullscreen) != self.fullscreen
745        {
746            self.fullscreen = Some(new_fullscreen);
747            commands.push(ViewportCommand::Fullscreen(new_fullscreen));
748        }
749
750        if let Some(new_maximized) = new_maximized
751            && Some(new_maximized) != self.maximized
752        {
753            self.maximized = Some(new_maximized);
754            commands.push(ViewportCommand::Maximized(new_maximized));
755        }
756
757        if let Some(new_resizable) = new_resizable
758            && Some(new_resizable) != self.resizable
759        {
760            self.resizable = Some(new_resizable);
761            commands.push(ViewportCommand::Resizable(new_resizable));
762        }
763
764        if let Some(new_transparent) = new_transparent
765            && Some(new_transparent) != self.transparent
766        {
767            self.transparent = Some(new_transparent);
768            commands.push(ViewportCommand::Transparent(new_transparent));
769        }
770
771        if let Some(new_decorations) = new_decorations
772            && Some(new_decorations) != self.decorations
773        {
774            self.decorations = Some(new_decorations);
775            commands.push(ViewportCommand::Decorations(new_decorations));
776        }
777
778        if let Some(new_icon) = new_icon {
779            let is_new = match &self.icon {
780                Some(existing) => !Arc::ptr_eq(&new_icon, existing),
781                None => true,
782            };
783
784            if is_new {
785                commands.push(ViewportCommand::Icon(Some(new_icon.clone())));
786                self.icon = Some(new_icon);
787            }
788        }
789
790        if let Some(new_visible) = new_visible
791            && Some(new_visible) != self.visible
792        {
793            self.visible = Some(new_visible);
794            commands.push(ViewportCommand::Visible(new_visible));
795        }
796
797        if let Some(new_mouse_passthrough) = new_mouse_passthrough
798            && Some(new_mouse_passthrough) != self.mouse_passthrough
799        {
800            self.mouse_passthrough = Some(new_mouse_passthrough);
801            commands.push(ViewportCommand::MousePassthrough(new_mouse_passthrough));
802        }
803
804        if let Some(new_window_level) = new_window_level
805            && Some(new_window_level) != self.window_level
806        {
807            self.window_level = Some(new_window_level);
808            commands.push(ViewportCommand::WindowLevel(new_window_level));
809        }
810
811        // --------------------------------------------------------------
812        // Things we don't have commands for require a full window recreation.
813        // The reason we don't have commands for them is that `winit` doesn't support
814        // changing them without recreating the window.
815
816        let mut recreate_window = false;
817
818        if new_clamp_size_to_monitor_size.is_some()
819            && self.clamp_size_to_monitor_size != new_clamp_size_to_monitor_size
820        {
821            self.clamp_size_to_monitor_size = new_clamp_size_to_monitor_size;
822            recreate_window = true;
823        }
824
825        if new_active.is_some() && self.active != new_active {
826            self.active = new_active;
827            recreate_window = true;
828        }
829
830        if new_app_id.is_some() && self.app_id != new_app_id {
831            self.app_id = new_app_id;
832            recreate_window = true;
833        }
834
835        if new_close_button.is_some() && self.close_button != new_close_button {
836            self.close_button = new_close_button;
837            recreate_window = true;
838        }
839
840        if new_minimize_button.is_some() && self.minimize_button != new_minimize_button {
841            self.minimize_button = new_minimize_button;
842            recreate_window = true;
843        }
844
845        if new_maximize_button.is_some() && self.maximize_button != new_maximize_button {
846            self.maximize_button = new_maximize_button;
847            recreate_window = true;
848        }
849
850        if new_title_shown.is_some() && self.title_shown != new_title_shown {
851            self.title_shown = new_title_shown;
852            recreate_window = true;
853        }
854
855        if new_titlebar_buttons_shown.is_some()
856            && self.titlebar_buttons_shown != new_titlebar_buttons_shown
857        {
858            self.titlebar_buttons_shown = new_titlebar_buttons_shown;
859            recreate_window = true;
860        }
861
862        if new_titlebar_shown.is_some() && self.titlebar_shown != new_titlebar_shown {
863            self.titlebar_shown = new_titlebar_shown;
864            recreate_window = true;
865        }
866
867        if new_has_shadow.is_some() && self.has_shadow != new_has_shadow {
868            self.has_shadow = new_has_shadow;
869            recreate_window = true;
870        }
871
872        if new_taskbar.is_some() && self.taskbar != new_taskbar {
873            self.taskbar = new_taskbar;
874            recreate_window = true;
875        }
876
877        if new_fullsize_content_view.is_some()
878            && self.fullsize_content_view != new_fullsize_content_view
879        {
880            self.fullsize_content_view = new_fullsize_content_view;
881            recreate_window = true;
882        }
883
884        if new_movable_by_window_background.is_some()
885            && self.movable_by_window_background != new_movable_by_window_background
886        {
887            self.movable_by_window_background = new_movable_by_window_background;
888            recreate_window = true;
889        }
890
891        if new_drag_and_drop.is_some() && self.drag_and_drop != new_drag_and_drop {
892            self.drag_and_drop = new_drag_and_drop;
893            recreate_window = true;
894        }
895
896        if new_window_type.is_some() && self.window_type != new_window_type {
897            self.window_type = new_window_type;
898            recreate_window = true;
899        }
900
901        (commands, recreate_window)
902    }
903}
904
905/// For winit platform compatibility, see [`winit::WindowLevel` documentation](https://docs.rs/winit/latest/winit/window/enum.WindowLevel.html#platform-specific)
906#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
907#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
908pub enum WindowLevel {
909    #[default]
910    Normal,
911    AlwaysOnBottom,
912    AlwaysOnTop,
913}
914
915#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
916#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
917pub enum X11WindowType {
918    /// This is a normal, top-level window.
919    #[default]
920    Normal,
921
922    /// A desktop feature. This can include a single window containing desktop icons with the same dimensions as the
923    /// screen, allowing the desktop environment to have full control of the desktop, without the need for proxying
924    /// root window clicks.
925    Desktop,
926
927    /// A dock or panel feature. Typically a Window Manager would keep such windows on top of all other windows.
928    Dock,
929
930    /// Toolbar windows. "Torn off" from the main application.
931    Toolbar,
932
933    /// Pinnable menu windows. "Torn off" from the main application.
934    Menu,
935
936    /// A small persistent utility window, such as a palette or toolbox.
937    Utility,
938
939    /// The window is a splash screen displayed as an application is starting up.
940    Splash,
941
942    /// This is a dialog window.
943    Dialog,
944
945    /// A dropdown menu that usually appears when the user clicks on an item in a menu bar.
946    /// This property is typically used on override-redirect windows.
947    DropdownMenu,
948
949    /// A popup menu that usually appears when the user right clicks on an object.
950    /// This property is typically used on override-redirect windows.
951    PopupMenu,
952
953    /// A tooltip window. Usually used to show additional information when hovering over an object with the cursor.
954    /// This property is typically used on override-redirect windows.
955    Tooltip,
956
957    /// The window is a notification.
958    /// This property is typically used on override-redirect windows.
959    Notification,
960
961    /// This should be used on the windows that are popped up by combo boxes.
962    /// This property is typically used on override-redirect windows.
963    Combo,
964
965    /// This indicates the window is being dragged.
966    /// This property is typically used on override-redirect windows.
967    Dnd,
968}
969
970#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
971#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
972pub enum IMEPurpose {
973    #[default]
974    Normal,
975    Password,
976    Terminal,
977}
978
979#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
980#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
981pub enum SystemTheme {
982    #[default]
983    SystemDefault,
984    Light,
985    Dark,
986}
987
988#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
989#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
990pub enum CursorGrab {
991    #[default]
992    None,
993    Confined,
994    Locked,
995}
996
997#[derive(Clone, Copy, Debug, PartialEq, Eq)]
998#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
999pub enum ResizeDirection {
1000    North,
1001    South,
1002    East,
1003    West,
1004    NorthEast,
1005    SouthEast,
1006    NorthWest,
1007    SouthWest,
1008}
1009
1010/// An output [viewport](crate::viewport)-command from egui to the backend, e.g. to change the window title or size.
1011///
1012/// You can send a [`ViewportCommand`] to the viewport with [`Context::send_viewport_cmd`].
1013///
1014/// See [`crate::viewport`] for how to build new viewports (native windows).
1015///
1016/// All coordinates are in logical points.
1017///
1018/// [`ViewportCommand`] is essentially a way to diff [`ViewportBuilder`]s.
1019///
1020/// Only commands specific to a viewport are part of [`ViewportCommand`].
1021/// Other commands should be put in [`crate::OutputCommand`].
1022#[derive(Clone, Debug, PartialEq, Eq)]
1023#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
1024pub enum ViewportCommand {
1025    /// Request this viewport to be closed.
1026    ///
1027    /// For the root viewport, this usually results in the application shutting down.
1028    /// For other viewports, the [`crate::ViewportInfo::close_requested`] flag will be set.
1029    Close,
1030
1031    /// Cancel the closing that was signaled by [`crate::ViewportInfo::close_requested`].
1032    CancelClose,
1033
1034    /// Set the window title.
1035    Title(String),
1036
1037    /// Turn the window transparent or not.
1038    Transparent(bool),
1039
1040    /// Set the visibility of the window.
1041    Visible(bool),
1042
1043    /// Moves the window with the left mouse button until the button is released.
1044    ///
1045    /// There's no guarantee that this will work unless the left mouse button was pressed
1046    /// immediately before this function is called.
1047    StartDrag,
1048
1049    /// Set the outer position of the viewport, i.e. moves the window.
1050    OuterPosition(Pos2),
1051
1052    /// Should be bigger than 0
1053    InnerSize(Vec2),
1054
1055    /// Should be bigger than 0
1056    MinInnerSize(Vec2),
1057
1058    /// Should be bigger than 0
1059    MaxInnerSize(Vec2),
1060
1061    /// Should be bigger than 0
1062    ResizeIncrements(Option<Vec2>),
1063
1064    /// Begin resizing the viewport with the left mouse button until the button is released.
1065    ///
1066    /// There's no guarantee that this will work unless the left mouse button was pressed
1067    /// immediately before this function is called.
1068    BeginResize(ResizeDirection),
1069
1070    /// Can the window be resized?
1071    Resizable(bool),
1072
1073    /// Set which window buttons are enabled
1074    EnableButtons {
1075        close: bool,
1076        minimized: bool,
1077        maximize: bool,
1078    },
1079    Minimized(bool),
1080
1081    /// Maximize or unmaximize window.
1082    Maximized(bool),
1083
1084    /// Turn borderless fullscreen on/off.
1085    Fullscreen(bool),
1086
1087    /// Show window decorations, i.e. the chrome around the content
1088    /// with the title bar, close buttons, resize handles, etc.
1089    Decorations(bool),
1090
1091    /// Set window to be always-on-top, always-on-bottom, or neither.
1092    WindowLevel(WindowLevel),
1093
1094    /// The window icon.
1095    Icon(Option<Arc<IconData>>),
1096
1097    /// Set the IME cursor editing area.
1098    IMERect(crate::Rect),
1099    IMEAllowed(bool),
1100    IMEPurpose(IMEPurpose),
1101
1102    /// Bring the window into focus (native only).
1103    ///
1104    /// This command puts the window on top of other applications and takes input focus away from them,
1105    /// which, if unexpected, will disturb the user.
1106    ///
1107    /// Has no effect on Wayland, or if the window is minimized or invisible.
1108    Focus,
1109
1110    /// If the window is unfocused, attract the user's attention (native only).
1111    ///
1112    /// Typically, this means that the window will flash on the taskbar, or bounce, until it is interacted with.
1113    ///
1114    /// When the window comes into focus, or if `None` is passed, the attention request will be automatically reset.
1115    ///
1116    /// See [winit's documentation][user_attention_details] for platform-specific effect details.
1117    ///
1118    /// [user_attention_details]: https://docs.rs/winit/latest/winit/window/enum.UserAttentionType.html
1119    RequestUserAttention(crate::UserAttentionType),
1120
1121    SetTheme(SystemTheme),
1122
1123    ContentProtected(bool),
1124
1125    /// Will probably not work as expected!
1126    CursorPosition(Pos2),
1127
1128    CursorGrab(CursorGrab),
1129
1130    CursorVisible(bool),
1131
1132    /// Enable mouse pass-through: mouse clicks pass through the window, used for non-interactable overlays.
1133    MousePassthrough(bool),
1134
1135    /// Take a screenshot of the next frame after this.
1136    ///
1137    /// The results are returned in [`crate::Event::Screenshot`].
1138    Screenshot(crate::UserData),
1139
1140    /// Request cut of the current selection
1141    ///
1142    /// This is equivalent to the system keyboard shortcut for cut (e.g. CTRL + X).
1143    RequestCut,
1144
1145    /// Request a copy of the current selection.
1146    ///
1147    /// This is equivalent to the system keyboard shortcut for copy (e.g. CTRL + C).
1148    RequestCopy,
1149
1150    /// Request a paste from the clipboard to the current focused `TextEdit` if any.
1151    ///
1152    /// This is equivalent to the system keyboard shortcut for paste (e.g. CTRL + V).
1153    RequestPaste,
1154}
1155
1156impl ViewportCommand {
1157    /// Construct a command to center the viewport on the monitor, if possible.
1158    pub fn center_on_screen(ctx: &crate::Context) -> Option<Self> {
1159        ctx.input(|i| {
1160            let outer_rect = i.viewport().outer_rect?;
1161            let size = outer_rect.size();
1162            let monitor_size = i.viewport().monitor_size?;
1163            if 1.0 < monitor_size.x && 1.0 < monitor_size.y {
1164                let x = (monitor_size.x - size.x) / 2.0;
1165                let y = (monitor_size.y - size.y) / 2.0;
1166                Some(Self::OuterPosition([x, y].into()))
1167            } else {
1168                None
1169            }
1170        })
1171    }
1172
1173    /// This command requires the parent viewport to repaint.
1174    pub fn requires_parent_repaint(&self) -> bool {
1175        self == &Self::Close
1176    }
1177}
1178
1179// ----------------------------------------------------------------------------
1180
1181/// Describes a viewport, i.e. a native window.
1182///
1183/// This is returned by [`crate::Context::run`] on each frame, and should be applied
1184/// by the integration.
1185#[derive(Clone)]
1186pub struct ViewportOutput {
1187    /// Id of our parent viewport.
1188    pub parent: ViewportId,
1189
1190    /// What type of viewport are we?
1191    ///
1192    /// This will never be [`ViewportClass::Embedded`],
1193    /// since those don't result in real viewports.
1194    pub class: ViewportClass,
1195
1196    /// The window attributes such as title, position, size, etc.
1197    ///
1198    /// Use this when first constructing the native window.
1199    /// Also check for changes in it using [`ViewportBuilder::patch`],
1200    /// and apply them as needed.
1201    pub builder: ViewportBuilder,
1202
1203    /// The user-code that shows the GUI, used for deferred viewports.
1204    ///
1205    /// `None` for immediate viewports and the ROOT viewport.
1206    pub viewport_ui_cb: Option<Arc<DeferredViewportUiCallback>>,
1207
1208    /// Commands to change the viewport, e.g. window title and size.
1209    pub commands: Vec<ViewportCommand>,
1210
1211    /// Schedule a repaint of this viewport after this delay.
1212    ///
1213    /// It is preferable to instead install a [`Context::set_request_repaint_callback`],
1214    /// but if you haven't, you can use this instead.
1215    ///
1216    /// If the duration is zero, schedule a repaint immediately.
1217    pub repaint_delay: std::time::Duration,
1218}
1219
1220impl ViewportOutput {
1221    /// Add on new output.
1222    pub fn append(&mut self, newer: Self) {
1223        let Self {
1224            parent,
1225            class,
1226            builder,
1227            viewport_ui_cb,
1228            mut commands,
1229            repaint_delay,
1230        } = newer;
1231
1232        self.parent = parent;
1233        self.class = class;
1234        let _ = self.builder.patch(builder); // we ignore the returned command, because `self.builder` will be the basis of a new patch
1235        self.viewport_ui_cb = viewport_ui_cb;
1236        self.commands.append(&mut commands);
1237        self.repaint_delay = self.repaint_delay.min(repaint_delay);
1238    }
1239}
1240
1241/// Viewport for immediate rendering.
1242pub struct ImmediateViewport<'a> {
1243    /// Id of us and our parent.
1244    pub ids: ViewportIdPair,
1245
1246    pub builder: ViewportBuilder,
1247
1248    /// The user-code that shows the GUI.
1249    pub viewport_ui_cb: Box<dyn FnMut(&Context) + 'a>,
1250}