mirror of
https://github.com/emilk/egui.git
synced 2026-08-29 04:40:03 -04:00
Call logic even while browser tab is in background (#8257)
* Closes https://github.com/emilk/egui/issues/5112 --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,7 @@ syntect = ["egui_extras/syntect"]
|
||||
egui = { workspace = true, default-features = false, features = ["color-hex"] }
|
||||
egui_extras = { workspace = true, features = ["image", "svg"] }
|
||||
|
||||
log.workspace = true
|
||||
unicode_names2.workspace = true # this old version has fewer dependencies
|
||||
|
||||
#! ### Optional dependencies
|
||||
|
||||
@@ -47,6 +47,12 @@ impl DemoGroup {
|
||||
set_open(open, demo.name(), is_open);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn logic(&mut self, ctx: &egui::Context) {
|
||||
for demo in &mut self.demos {
|
||||
demo.logic(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_open(open: &mut BTreeSet<String>, key: &'static str, is_open: bool) {
|
||||
@@ -160,6 +166,11 @@ impl DemoGroups {
|
||||
demos.windows(ui, open);
|
||||
tests.windows(ui, open);
|
||||
}
|
||||
|
||||
pub fn logic(&mut self, ctx: &egui::Context) {
|
||||
self.demos.logic(ctx);
|
||||
self.tests.logic(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -212,6 +223,13 @@ impl DemoWindows {
|
||||
}
|
||||
}
|
||||
|
||||
/// Run background logic for all demos.
|
||||
///
|
||||
/// Called every frame, even when hidden, so demos can keep working in the background.
|
||||
pub fn logic(&mut self, ctx: &egui::Context) {
|
||||
self.groups.logic(ctx);
|
||||
}
|
||||
|
||||
fn about_is_open(&self) -> bool {
|
||||
self.open.contains(About::default().name())
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ pub struct MiscDemoWindow {
|
||||
tree: Tree,
|
||||
box_painting: BoxPainting,
|
||||
text_rotation: TextRotation,
|
||||
repaint: Repaint,
|
||||
|
||||
dummy_bool: bool,
|
||||
dummy_usize: usize,
|
||||
@@ -36,6 +37,7 @@ impl Default for MiscDemoWindow {
|
||||
tree: Tree::demo(),
|
||||
box_painting: Default::default(),
|
||||
text_rotation: Default::default(),
|
||||
repaint: Default::default(),
|
||||
|
||||
dummy_bool: false,
|
||||
dummy_usize: 0,
|
||||
@@ -57,6 +59,10 @@ impl Demo for MiscDemoWindow {
|
||||
.constrain_to(ui.available_rect_before_wrap())
|
||||
.show(ui, |ui| self.ui(ui));
|
||||
}
|
||||
|
||||
fn logic(&mut self, ctx: &egui::Context) {
|
||||
self.repaint.logic(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
impl View for MiscDemoWindow {
|
||||
@@ -102,6 +108,10 @@ impl View for MiscDemoWindow {
|
||||
.default_open(false)
|
||||
.show(ui, |ui| self.tree.ui(ui));
|
||||
|
||||
CollapsingHeader::new("Repaint")
|
||||
.default_open(false)
|
||||
.show(ui, |ui| self.repaint.ui(ui));
|
||||
|
||||
CollapsingHeader::new("Checkboxes")
|
||||
.default_open(false)
|
||||
.show(ui, |ui| {
|
||||
@@ -292,6 +302,134 @@ impl Widgets {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// Demonstrates [`egui::Context::request_repaint`] and
|
||||
/// [`egui::Context::request_repaint_after`].
|
||||
#[derive(PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
struct Repaint {
|
||||
/// Request a repaint every frame, so we run as fast as the integration allows.
|
||||
repaint_continuously: bool,
|
||||
|
||||
/// Request a repaint after [`Self::delay`].
|
||||
repaint_after_delay: bool,
|
||||
|
||||
/// How long to wait before the next repaint when [`Self::repaint_after_delay`] is set.
|
||||
delay: f64,
|
||||
|
||||
/// Issue the repaint requests from `logic` (which runs even while hidden) instead of `ui`.
|
||||
in_background: bool,
|
||||
|
||||
/// Log each `ui` and `logic` frame, so background activity is visible in the console.
|
||||
log_each_frame: bool,
|
||||
|
||||
/// How many times [`Self::ui`] has run since the last reset.
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
ui_count: u64,
|
||||
|
||||
/// How many times [`Self::logic`] has run since the last reset.
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
logic_count: u64,
|
||||
}
|
||||
|
||||
impl Default for Repaint {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
repaint_continuously: false,
|
||||
repaint_after_delay: false,
|
||||
delay: 1.0,
|
||||
in_background: false,
|
||||
log_each_frame: false,
|
||||
ui_count: 0,
|
||||
logic_count: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Repaint {
|
||||
fn ui(&mut self, ui: &mut Ui) {
|
||||
self.ui_count += 1;
|
||||
if self.log_each_frame {
|
||||
log::info!("Repaint demo: `ui` frame {}", self.ui_count);
|
||||
}
|
||||
|
||||
ui.label("Use this to verify if logic is correctly called while in background.");
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("Reset counts").clicked() {
|
||||
self.ui_count = 0;
|
||||
self.logic_count = 0;
|
||||
}
|
||||
ui.label(format!(
|
||||
"`ui`: {}, `logic`: {}",
|
||||
self.ui_count, self.logic_count
|
||||
))
|
||||
.on_hover_text(
|
||||
"`ui` is incremented in `App::ui` (only runs while visible), \
|
||||
`logic` in `App::logic` (runs even while hidden).",
|
||||
);
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.checkbox(
|
||||
&mut self.repaint_continuously,
|
||||
"Repaint continuously (every frame)",
|
||||
);
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.checkbox(&mut self.repaint_after_delay, "Repaint after");
|
||||
ui.add_enabled(
|
||||
self.repaint_after_delay,
|
||||
Slider::new(&mut self.delay, 0.0..=5.0)
|
||||
.suffix(" s")
|
||||
.text("delay"),
|
||||
);
|
||||
});
|
||||
|
||||
ui.checkbox(&mut self.in_background, "In the background (during logic)")
|
||||
.on_hover_text(
|
||||
"Issue the repaint requests from `App::logic` (which runs even while hidden) \
|
||||
instead of `App::ui` (which is skipped while hidden).\n\n\
|
||||
With this enabled, hide this tab for a while, then come back: \
|
||||
the `logic` count will have kept climbing.",
|
||||
);
|
||||
|
||||
ui.checkbox(&mut self.log_each_frame, "Log each frame")
|
||||
.on_hover_text("Log each `ui` and `logic` frame to the console.");
|
||||
|
||||
// When not in background mode, drive the repaints from here (`ui`), which only
|
||||
// runs while visible. Otherwise they are driven from `logic` (see below).
|
||||
if !self.in_background {
|
||||
self.request_repaint(ui.ctx());
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs even when the app is hidden, unlike [`Self::ui`].
|
||||
fn logic(&mut self, ctx: &egui::Context) {
|
||||
self.logic_count += 1;
|
||||
if self.log_each_frame {
|
||||
log::info!("Repaint demo: `logic` frame {}", self.logic_count);
|
||||
}
|
||||
|
||||
if self.in_background {
|
||||
self.request_repaint(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/// Request repaints according to the selected options.
|
||||
fn request_repaint(&self, ctx: &egui::Context) {
|
||||
if self.repaint_continuously {
|
||||
ctx.request_repaint();
|
||||
}
|
||||
if self.repaint_after_delay {
|
||||
ctx.request_repaint_after(std::time::Duration::from_secs_f64(self.delay));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[derive(PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
|
||||
@@ -62,4 +62,8 @@ pub trait Demo {
|
||||
|
||||
/// Show windows, etc
|
||||
fn show(&mut self, ui: &mut egui::Ui, open: &mut bool);
|
||||
|
||||
/// Run background logic, called every frame even when the demo window is closed
|
||||
/// or the app is hidden.
|
||||
fn logic(&mut self, _ctx: &egui::Context) {}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2909f098b5edacefef1b5c4d81982b0c84ebd27f896934f0bef92ceb283bbe79
|
||||
size 60288
|
||||
oid sha256:a825dc9c62979fbb8d1ca3d441b4d9e7dbbd234b994901026f35fe7d591ff196
|
||||
size 60217
|
||||
|
||||
Reference in New Issue
Block a user