1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-01 22:30:03 -04:00

Show how to close a child viewport in the example

This commit is contained in:
Emil Ernerfeldt
2023-11-15 17:49:42 +01:00
parent ccf96ceb93
commit 6f98994a7c
8 changed files with 72 additions and 36 deletions

View File

@@ -123,6 +123,7 @@ pub trait App {
/// ///
/// This is called for the root viewport ([`egui::ViewportId::ROOT`]). /// This is called for the root viewport ([`egui::ViewportId::ROOT`]).
/// Use [`egui::Context::show_viewport`] to spawn additional viewports (windows). /// Use [`egui::Context::show_viewport`] to spawn additional viewports (windows).
/// (A "viewport" in egui means an native OS window).
fn update(&mut self, ctx: &egui::Context, frame: &mut Frame); fn update(&mut self, ctx: &egui::Context, frame: &mut Frame);
/// Get a handle to the app. /// Get a handle to the app.

View File

@@ -85,7 +85,7 @@ pub fn window_builder<E>(
.. ..
} = native_options; } = native_options;
let mut viewport_builder = egui::ViewportBuilder::ROOT let mut viewport_builder = egui::ViewportBuilder::DEFAULTS
.with_title(title) .with_title(title)
.with_decorations(*decorated) .with_decorations(*decorated)
.with_fullscreen(*fullscreen) .with_fullscreen(*fullscreen)

View File

@@ -201,18 +201,16 @@ impl State {
let screen_size_in_pixels = screen_size_in_pixels(window); let screen_size_in_pixels = screen_size_in_pixels(window);
let screen_size_in_points = screen_size_in_pixels / pixels_per_point; let screen_size_in_points = screen_size_in_pixels / pixels_per_point;
let getting_info = !window.is_minimized().unwrap_or_else(|| { self.egui_input.screen_rect = (screen_size_in_points.x > 0.0
eprintln!("Cannot determine the Viewport/native window minimized state"); && screen_size_in_points.y > 0.0)
true .then(|| Rect::from_min_size(Pos2::ZERO, screen_size_in_points));
});
self.egui_input.screen_rect = if getting_info { let has_a_position = match window.is_minimized() {
Some(egui::Rect::from_min_size(Pos2::ZERO, screen_size_in_points)) None | Some(true) => false,
} else { Some(false) => true,
None
}; };
let inner_pos_px = if getting_info { let inner_pos_px = if has_a_position {
window window
.inner_position() .inner_position()
.map(|pos| Pos2::new(pos.x as f32, pos.y as f32)) .map(|pos| Pos2::new(pos.x as f32, pos.y as f32))
@@ -221,7 +219,7 @@ impl State {
None None
}; };
let outer_pos_px = if getting_info { let outer_pos_px = if has_a_position {
window window
.outer_position() .outer_position()
.map(|pos| Pos2::new(pos.x as f32, pos.y as f32)) .map(|pos| Pos2::new(pos.x as f32, pos.y as f32))
@@ -230,14 +228,14 @@ impl State {
None None
}; };
let inner_size_px = if getting_info { let inner_size_px = if has_a_position {
let size = window.inner_size(); let size = window.inner_size();
Some(Vec2::new(size.width as f32, size.height as f32)) Some(Vec2::new(size.width as f32, size.height as f32))
} else { } else {
None None
}; };
let outer_size_px = if getting_info { let outer_size_px = if has_a_position {
let size = window.outer_size(); let size = window.outer_size();
Some(Vec2::new(size.width as f32, size.height as f32)) Some(Vec2::new(size.width as f32, size.height as f32))
} else { } else {

View File

@@ -75,6 +75,7 @@ pub struct RawInput {
impl Default for RawInput { impl Default for RawInput {
fn default() -> Self { fn default() -> Self {
Self { Self {
viewport: ViewportInfo::default(),
screen_rect: None, screen_rect: None,
pixels_per_point: None, pixels_per_point: None,
max_texture_side: None, max_texture_side: None,
@@ -85,7 +86,6 @@ impl Default for RawInput {
hovered_files: Default::default(), hovered_files: Default::default(),
dropped_files: Default::default(), dropped_files: Default::default(),
focused: true, // integrations opt into global focus tracking focused: true, // integrations opt into global focus tracking
viewport: ViewportInfo::default(),
} }
} }
} }
@@ -97,6 +97,7 @@ impl RawInput {
/// * [`Self::dropped_files`] is moved. /// * [`Self::dropped_files`] is moved.
pub fn take(&mut self) -> RawInput { pub fn take(&mut self) -> RawInput {
RawInput { RawInput {
viewport: self.viewport.take(),
screen_rect: self.screen_rect.take(), screen_rect: self.screen_rect.take(),
pixels_per_point: self.pixels_per_point.take(), pixels_per_point: self.pixels_per_point.take(),
max_texture_side: self.max_texture_side.take(), max_texture_side: self.max_texture_side.take(),
@@ -107,13 +108,13 @@ impl RawInput {
hovered_files: self.hovered_files.clone(), hovered_files: self.hovered_files.clone(),
dropped_files: std::mem::take(&mut self.dropped_files), dropped_files: std::mem::take(&mut self.dropped_files),
focused: self.focused, focused: self.focused,
viewport: self.viewport.take(),
} }
} }
/// Add on new input. /// Add on new input.
pub fn append(&mut self, newer: Self) { pub fn append(&mut self, newer: Self) {
let Self { let Self {
viewport,
screen_rect, screen_rect,
pixels_per_point, pixels_per_point,
max_texture_side, max_texture_side,
@@ -124,13 +125,12 @@ impl RawInput {
mut hovered_files, mut hovered_files,
mut dropped_files, mut dropped_files,
focused, focused,
viewport,
} = newer; } = newer;
self.viewport = viewport;
self.screen_rect = screen_rect.or(self.screen_rect); self.screen_rect = screen_rect.or(self.screen_rect);
self.pixels_per_point = pixels_per_point.or(self.pixels_per_point); self.pixels_per_point = pixels_per_point.or(self.pixels_per_point);
self.max_texture_side = max_texture_side.or(self.max_texture_side); self.max_texture_side = max_texture_side.or(self.max_texture_side);
self.viewport = viewport;
self.time = time; // use latest time self.time = time; // use latest time
self.predicted_dt = predicted_dt; // use latest dt self.predicted_dt = predicted_dt; // use latest dt
self.modifiers = modifiers; // use latest self.modifiers = modifiers; // use latest

View File

@@ -79,7 +79,7 @@ pub struct Memory {
#[cfg_attr(feature = "persistence", serde(skip))] #[cfg_attr(feature = "persistence", serde(skip))]
pub(crate) new_font_definitions: Option<epaint::text::FontDefinitions>, pub(crate) new_font_definitions: Option<epaint::text::FontDefinitions>,
// Current viewport // Current active viewport
#[cfg_attr(feature = "persistence", serde(skip))] #[cfg_attr(feature = "persistence", serde(skip))]
pub(crate) viewport_id: ViewportId, pub(crate) viewport_id: ViewportId,
@@ -96,7 +96,6 @@ pub struct Memory {
// ------------------------------------------------- // -------------------------------------------------
// Per-viewport: // Per-viewport:
#[cfg_attr(feature = "persistence", serde(skip))]
areas: ViewportIdMap<Areas>, areas: ViewportIdMap<Areas>,
#[cfg_attr(feature = "persistence", serde(skip))] #[cfg_attr(feature = "persistence", serde(skip))]

View File

@@ -135,7 +135,7 @@ pub struct ViewportBuilder {
impl ViewportBuilder { impl ViewportBuilder {
/// Default settings for the root viewport. /// Default settings for the root viewport.
pub const ROOT: Self = Self { pub const DEFAULTS: Self = Self {
title: None, title: None,
name: None, name: None,
position: None, position: None,
@@ -154,18 +154,12 @@ impl ViewportBuilder {
min_inner_size: None, min_inner_size: None,
max_inner_size: None, max_inner_size: None,
drag_and_drop: Some(true), drag_and_drop: Some(true),
close_button: Some(false), // We disable the close button by default because we haven't implemented closing of child viewports yet close_button: Some(true),
minimize_button: Some(true), minimize_button: Some(true),
maximize_button: Some(true), maximize_button: Some(true),
hittest: Some(true), hittest: Some(true),
}; };
/// Default settings for a new child viewport.
pub const CHILD: Self = Self {
close_button: Some(false), // We disable the close button by default because we haven't implemented closing of child viewports yet
..Self::ROOT
};
/// Empty settings for everything. /// Empty settings for everything.
/// ///
/// If used the first frame, backend-specific defaults will be used. /// If used the first frame, backend-specific defaults will be used.

View File

@@ -1,5 +1,10 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use eframe::egui; use eframe::egui;
fn main() -> Result<(), eframe::Error> { fn main() -> Result<(), eframe::Error> {
@@ -9,7 +14,7 @@ fn main() -> Result<(), eframe::Error> {
..Default::default() ..Default::default()
}; };
eframe::run_native( eframe::run_native(
"Confirm exit", "Multiple viewports",
options, options,
Box::new(|_cc| Box::<MyApp>::default()), Box::new(|_cc| Box::<MyApp>::default()),
) )
@@ -17,7 +22,15 @@ fn main() -> Result<(), eframe::Error> {
#[derive(Default)] #[derive(Default)]
struct MyApp { struct MyApp {
show_child_viewport: bool, /// Immediate viewports are show immediately, so passing state to/from them is easy.
/// The downside is that their painting is linked with the parent viewport:
/// if either needs repainting, they are both repainted.
show_immediate_viewport: bool,
/// Deferred viewports run independant of the parent viewport, which can save
/// CPU if only some of the viewports require repainting.
/// However, this requires passing state with `Arc` and locks.
show_deferred_viewport: Arc<AtomicBool>,
} }
impl eframe::App for MyApp { impl eframe::App for MyApp {
@@ -25,17 +38,48 @@ impl eframe::App for MyApp {
egui::CentralPanel::default().show(ctx, |ui| { egui::CentralPanel::default().show(ctx, |ui| {
ui.label("Hello from the root viewport"); ui.label("Hello from the root viewport");
ui.checkbox(&mut self.show_child_viewport, "Show secondary viewport"); ui.checkbox(
&mut self.show_immediate_viewport,
"Show immediate child viewport",
);
let mut show_deferred_viewport = self.show_deferred_viewport.load(Ordering::Relaxed);
ui.checkbox(&mut show_deferred_viewport, "Show deferred child viewport");
self.show_deferred_viewport
.store(show_deferred_viewport, Ordering::Relaxed);
}); });
if self.show_child_viewport { if self.show_immediate_viewport {
ctx.show_viewport( ctx.show_viewport_immediate(
egui::ViewportId::from_hash_of("secondary_viewport"), egui::ViewportId::from_hash_of("immediate_viewport"),
egui::ViewportBuilder::CHILD.with_title("Secondary Viewport"), egui::ViewportBuilder::DEFAULTS.with_title("Immediate Viewport"),
|ctx| { |ctx| {
egui::CentralPanel::default().show(ctx, |ui| { egui::CentralPanel::default().show(ctx, |ui| {
ui.label("Hello from secondary viewport"); ui.label("Hello from immediate viewport");
}); });
if ctx.input(|i| i.raw.viewport.close_requested) {
// Tell parent viewport that we should not show next frame:
self.show_immediate_viewport = false;
ctx.request_repaint(); // make sure there is a next frame
}
},
);
}
if self.show_deferred_viewport.load(Ordering::Relaxed) {
let show_deferred_viewport = self.show_deferred_viewport.clone();
ctx.show_viewport_immediate(
egui::ViewportId::from_hash_of("deferred_viewport"),
egui::ViewportBuilder::DEFAULTS.with_title("Deferred Viewport"),
|ctx| {
egui::CentralPanel::default().show(ctx, |ui| {
ui.label("Hello from deferred viewport");
});
if ctx.input(|i| i.raw.viewport.close_requested) {
// Tell parent to close use
show_deferred_viewport.store(false, Ordering::Relaxed);
ctx.request_repaint(); // make sure there is a next frame
}
}, },
); );
} }

View File

@@ -65,7 +65,7 @@ impl ViewportState {
let immediate = vp_state.read().immediate; let immediate = vp_state.read().immediate;
let title = vp_state.read().title.clone(); let title = vp_state.read().title.clone();
let viewport = ViewportBuilder::ROOT let viewport = ViewportBuilder::DEFAULTS
.with_title(&title) .with_title(&title)
.with_inner_size(Some(egui::vec2(450.0, 400.0))); .with_inner_size(Some(egui::vec2(450.0, 400.0)));