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