mirror of
https://github.com/emilk/egui.git
synced 2026-08-29 04:40:03 -04:00
Add ViewportInfo::occluded and visible (#7948)
* Part of https://github.com/emilk/egui/issues/5112 * Part of https://github.com/emilk/egui/issues/5113 * Part of https://github.com/emilk/egui/issues/5136 Once we support calling `App::logic` when an app is occluded or minimized, it is useful to know that it is, in fact, occluded or minimized. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -4303,6 +4303,14 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "test_background_logic"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"eframe",
|
||||||
|
"env_logger",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "test_egui_extras_compilation"
|
name = "test_egui_extras_compilation"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
@@ -822,6 +822,14 @@ impl GlowWinitRunning<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
winit::event::WindowEvent::Occluded(is_occluded) => {
|
||||||
|
if let Some(viewport_id) = viewport_id
|
||||||
|
&& let Some(viewport) = glutin.viewports.get_mut(&viewport_id)
|
||||||
|
{
|
||||||
|
viewport.info.occluded = Some(*is_occluded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
winit::event::WindowEvent::CloseRequested => {
|
winit::event::WindowEvent::CloseRequested => {
|
||||||
if viewport_id == Some(ViewportId::ROOT) && self.integration.should_close() {
|
if viewport_id == Some(ViewportId::ROOT) && self.integration.should_close() {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
|
|||||||
@@ -852,6 +852,14 @@ impl WgpuWinitRunning<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
winit::event::WindowEvent::Occluded(is_occluded) => {
|
||||||
|
if let Some(viewport_id) = viewport_id
|
||||||
|
&& let Some(viewport) = shared.viewports.get_mut(&viewport_id)
|
||||||
|
{
|
||||||
|
viewport.info.occluded = Some(*is_occluded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
winit::event::WindowEvent::CloseRequested => {
|
winit::event::WindowEvent::CloseRequested => {
|
||||||
if viewport_id == Some(ViewportId::ROOT) && integration.should_close() {
|
if viewport_id == Some(ViewportId::ROOT) && integration.should_close() {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
|
|||||||
@@ -31,11 +31,18 @@ impl WebInput {
|
|||||||
time: Some(super::now_sec()),
|
time: Some(super::now_sec()),
|
||||||
..self.raw.take()
|
..self.raw.take()
|
||||||
};
|
};
|
||||||
raw_input
|
let viewport = raw_input
|
||||||
.viewports
|
.viewports
|
||||||
.entry(egui::ViewportId::ROOT)
|
.entry(egui::ViewportId::ROOT)
|
||||||
.or_default()
|
.or_default();
|
||||||
.native_pixels_per_point = Some(super::native_pixels_per_point());
|
viewport.native_pixels_per_point = Some(super::native_pixels_per_point());
|
||||||
|
|
||||||
|
// A hidden browser tab is effectively occluded.
|
||||||
|
let hidden = web_sys::window()
|
||||||
|
.and_then(|w| w.document())
|
||||||
|
.is_some_and(|doc| doc.hidden());
|
||||||
|
viewport.occluded = Some(hidden);
|
||||||
|
|
||||||
raw_input
|
raw_input
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -253,9 +253,28 @@ pub struct ViewportInfo {
|
|||||||
///
|
///
|
||||||
/// This should be the same as [`RawInput::focused`].
|
/// This should be the same as [`RawInput::focused`].
|
||||||
pub focused: Option<bool>,
|
pub focused: Option<bool>,
|
||||||
|
|
||||||
|
/// Is the window fully occluded (completely covered) by another window?
|
||||||
|
///
|
||||||
|
/// Not all platforms support this.
|
||||||
|
/// On platforms that don't, this will be `None` or `Some(false)`.
|
||||||
|
pub occluded: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ViewportInfo {
|
impl ViewportInfo {
|
||||||
|
/// Is the window considered visible for rendering purposes?
|
||||||
|
///
|
||||||
|
/// A window is not visible if it is minimized or occluded.
|
||||||
|
/// When not visible, the UI is not painted and rendering is skipped,
|
||||||
|
/// but application logic may still be executed by some integrations.
|
||||||
|
pub fn visible(&self) -> Option<bool> {
|
||||||
|
match (self.minimized, self.occluded) {
|
||||||
|
(Some(true), _) | (_, Some(true)) => Some(false),
|
||||||
|
(Some(false), Some(false)) => Some(true),
|
||||||
|
(_, None) | (None, _) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This viewport has been told to close.
|
/// This viewport has been told to close.
|
||||||
///
|
///
|
||||||
/// If this is the root viewport, the application will exit
|
/// If this is the root viewport, the application will exit
|
||||||
@@ -282,6 +301,7 @@ impl ViewportInfo {
|
|||||||
maximized: self.maximized,
|
maximized: self.maximized,
|
||||||
fullscreen: self.fullscreen,
|
fullscreen: self.fullscreen,
|
||||||
focused: self.focused,
|
focused: self.focused,
|
||||||
|
occluded: self.occluded,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,6 +318,7 @@ impl ViewportInfo {
|
|||||||
maximized,
|
maximized,
|
||||||
fullscreen,
|
fullscreen,
|
||||||
focused,
|
focused,
|
||||||
|
occluded,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
crate::Grid::new("viewport_info").show(ui, |ui| {
|
crate::Grid::new("viewport_info").show(ui, |ui| {
|
||||||
@@ -345,6 +366,16 @@ impl ViewportInfo {
|
|||||||
ui.label(opt_as_str(focused));
|
ui.label(opt_as_str(focused));
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
|
|
||||||
|
ui.label("Occluded:");
|
||||||
|
ui.label(opt_as_str(occluded));
|
||||||
|
ui.end_row();
|
||||||
|
|
||||||
|
let visible = self.visible();
|
||||||
|
|
||||||
|
ui.label("Visible:");
|
||||||
|
ui.label(opt_as_str(&visible));
|
||||||
|
ui.end_row();
|
||||||
|
|
||||||
fn opt_rect_as_string(v: &Option<Rect>) -> String {
|
fn opt_rect_as_string(v: &Option<Rect>) -> String {
|
||||||
v.as_ref().map_or(String::new(), |r| {
|
v.as_ref().map_or(String::new(), |r| {
|
||||||
format!("Pos: {:?}, size: {:?}", r.min, r.size())
|
format!("Pos: {:?}, size: {:?}", r.min, r.size())
|
||||||
|
|||||||
14
tests/test_background_logic/Cargo.toml
Normal file
14
tests/test_background_logic/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "test_background_logic"
|
||||||
|
version = "0.1.0"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
edition = "2024"
|
||||||
|
rust-version = "1.92"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
eframe = { workspace = true, features = ["default"] }
|
||||||
|
env_logger = { workspace = true, features = ["auto-color", "humantime"] }
|
||||||
64
tests/test_background_logic/src/main.rs
Normal file
64
tests/test_background_logic/src/main.rs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
#![expect(rustdoc::missing_crate_level_docs)]
|
||||||
|
#![allow(clippy::print_stderr)]
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use eframe::egui::{self, ViewportInfo};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
let _ = eframe::run_native(
|
||||||
|
"Background Logic Test",
|
||||||
|
eframe::NativeOptions {
|
||||||
|
viewport: egui::ViewportBuilder::default().with_inner_size([400.0, 200.0]),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
Box::new(|_cc| Ok(Box::new(App))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct App;
|
||||||
|
|
||||||
|
impl eframe::App for App {
|
||||||
|
fn logic(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
|
eprintln!("App::logic called {}", viewport_info(ctx));
|
||||||
|
ctx.request_repaint_after(Duration::from_secs(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
|
||||||
|
eprintln!("App::ui called {}", viewport_info(ui.ctx()));
|
||||||
|
ui.centered_and_justified(|ui| {
|
||||||
|
ui.heading("Minimize this window");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn viewport_info(ctx: &egui::Context) -> String {
|
||||||
|
ctx.input(|i| {
|
||||||
|
let ViewportInfo {
|
||||||
|
minimized,
|
||||||
|
focused,
|
||||||
|
occluded,
|
||||||
|
..
|
||||||
|
} = i.viewport();
|
||||||
|
|
||||||
|
let visible = i.viewport().visible();
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
|
||||||
|
let flags = [
|
||||||
|
("focused", focused),
|
||||||
|
("occluded", occluded),
|
||||||
|
("minimized", minimized),
|
||||||
|
("visible", &visible),
|
||||||
|
];
|
||||||
|
for (name, value) in flags {
|
||||||
|
if let Some(value) = value {
|
||||||
|
s += &format!(" {name}={value}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user