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