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}