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