mirror of
https://github.com/emilk/egui.git
synced 2026-08-30 21:30:03 -04:00
Never run an egui pass when nothing will be shown (#8387)
* Closes <https://github.com/emilk/egui/issues/8266> * Alternative to #8385 Not the most simple or beautiful code, but it works, and makes sense. What makes it complex: `app.logic` should still see some input (e.g. what viewports are visible) and emit some output (e.g. "open this link", or "focus and repaint"). ## TODO * [x] test multiple viewports ## Clanker says Instead of teaching egui to skip book-keeping during a pass where no ui is shown, we simply run no pass at all. Then there is nothing to special-case: all ui state is left untouched, and the app finds everything where it left it when the window is shown again. * New `Context::run_logic(&raw_input, f)`: ticks app logic without a pass, returning the `LogicOutput` (platform output + viewport commands) that a pass would otherwise have carried, so e.g. `ViewportCommand::Focus` still reaches the integration. * All three eframe backends (glow, wgpu, web) call `run_logic` instead of `run_ui` when the viewport is minimized/occluded (and has no visible descendant viewport) or, on web, when the tab is hidden. * `App::logic` is still called from inside the pass when the window is visible, so it sees the current frame's input. While hidden, `run_logic` fills in only the window state (`RawInput::viewports` / `focused`), so the app can tell that it is hidden. The ui input (events, time, …) is not interpreted, and is instead given to the next real pass. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -280,45 +280,65 @@ impl AppRunner {
|
||||
.and_then(|v| v.visible())
|
||||
.unwrap_or(true);
|
||||
|
||||
let full_output = self.egui_ctx.run_ui(raw_input, |ui| {
|
||||
self.app.logic(ui.ctx(), &mut self.frame);
|
||||
|
||||
if is_visible {
|
||||
if is_visible {
|
||||
let full_output = self.egui_ctx.run_ui(raw_input, |ui| {
|
||||
self.app.logic(ui.ctx(), &mut self.frame);
|
||||
self.app.ui(ui, &mut self.frame);
|
||||
}
|
||||
});
|
||||
let egui::FullOutput {
|
||||
platform_output,
|
||||
textures_delta,
|
||||
shapes,
|
||||
pixels_per_point,
|
||||
viewport_output,
|
||||
} = full_output;
|
||||
});
|
||||
let egui::FullOutput {
|
||||
platform_output,
|
||||
textures_delta,
|
||||
shapes,
|
||||
pixels_per_point,
|
||||
viewport_output,
|
||||
} = full_output;
|
||||
|
||||
if viewport_output.len() > 1 {
|
||||
log::warn!("Multiple viewports not yet supported on the web");
|
||||
}
|
||||
for (_viewport_id, viewport_output) in viewport_output {
|
||||
for command in viewport_output.commands {
|
||||
match command {
|
||||
ViewportCommand::Screenshot(user_data) => {
|
||||
self.screenshot_commands_with_frame_delay
|
||||
.push((user_data, 1));
|
||||
}
|
||||
_ => {
|
||||
// TODO(emilk): handle some of the commands
|
||||
log::warn!(
|
||||
"Unhandled egui viewport command: {command:?} - not implemented in web backend"
|
||||
);
|
||||
}
|
||||
}
|
||||
if viewport_output.len() > 1 {
|
||||
log::warn!("Multiple viewports not yet supported on the web");
|
||||
}
|
||||
}
|
||||
self.handle_viewport_commands(
|
||||
viewport_output
|
||||
.into_values()
|
||||
.flat_map(|viewport_output| viewport_output.commands),
|
||||
);
|
||||
|
||||
self.handle_platform_output(platform_output);
|
||||
if is_visible || !textures_delta.is_empty() {
|
||||
self.handle_platform_output(platform_output);
|
||||
self.textures_delta.append(textures_delta);
|
||||
self.clipped_primitives = Some(self.egui_ctx.tessellate(shapes, pixels_per_point));
|
||||
} else {
|
||||
// The tab is hidden, so we run no egui pass at all.
|
||||
// That way all ui state is left untouched, and is still there
|
||||
// when the tab is shown again.
|
||||
|
||||
let egui::LogicOutput {
|
||||
platform_output,
|
||||
viewport_commands,
|
||||
} = self.egui_ctx.run_logic(&raw_input, |ctx| {
|
||||
self.app.logic(ctx, &mut self.frame);
|
||||
});
|
||||
|
||||
// No pass consumed the input, so save it for the next one:
|
||||
self.input.raw.append(raw_input);
|
||||
|
||||
self.handle_viewport_commands(viewport_commands.into_values().flatten());
|
||||
self.handle_platform_output(platform_output);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_viewport_commands(&mut self, commands: impl Iterator<Item = ViewportCommand>) {
|
||||
for command in commands {
|
||||
match command {
|
||||
ViewportCommand::Screenshot(user_data) => {
|
||||
self.screenshot_commands_with_frame_delay
|
||||
.push((user_data, 1));
|
||||
}
|
||||
_ => {
|
||||
// TODO(emilk): handle some of the commands
|
||||
log::warn!(
|
||||
"Unhandled egui viewport command: {command:?} - not implemented in web backend"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user