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