1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-29 04:40:03 -04:00

Add egui_inspection::Request::Settle (#8344)

This allows inspection clients to know if the app is in a settled state
and/or run the app until it settles (similar to egui_kittests
`run`/`run_ok`).
This commit is contained in:
Lucas Meurer
2026-07-28 09:47:22 +02:00
committed by GitHub
parent 2cb071f7f6
commit 8fc323fbcf
2 changed files with 57 additions and 2 deletions

View File

@@ -56,6 +56,9 @@ enum Phase {
/// A screenshot was dispatched with this `user_data` id; reply when the matching /// A screenshot was dispatched with this `user_data` id; reply when the matching
/// [`egui::Event::Screenshot`] arrives. /// [`egui::Event::Screenshot`] arrives.
AwaitScreenshot { id: u64 }, AwaitScreenshot { id: u64 },
/// Watching for the app to go idle.
Settle { steps_taken: u64, max_steps: u64 },
} }
struct InFlight { struct InFlight {
@@ -116,7 +119,12 @@ impl InspectionPlugin {
/// While requests are still in flight, keep the UI loop spinning — reactive apps would /// While requests are still in flight, keep the UI loop spinning — reactive apps would
/// otherwise go idle between hooks before a screenshot round-trips. /// otherwise go idle between hooks before a screenshot round-trips.
fn maybe_repaint(&self, ctx: &Context) { fn maybe_repaint(&self, ctx: &Context) {
if !self.in_flight.is_empty() { // Don't repaint if there's only a `Request::Settle`.
if self
.in_flight
.iter()
.any(|item| !matches!(item.req, Request::Settle { .. }))
{
ctx.request_repaint(); ctx.request_repaint();
} }
} }
@@ -236,6 +244,13 @@ impl egui::Plugin for InspectionPlugin {
item.phase = Phase::AwaitScreenshot { id }; item.phase = Phase::AwaitScreenshot { id };
true true
} }
Request::Settle { max_steps } => {
item.phase = Phase::Settle {
steps_taken: 0,
max_steps: *max_steps,
};
true
}
} }
}); });
self.next_screenshot_id = next_id; self.next_screenshot_id = next_id;
@@ -249,9 +264,14 @@ impl egui::Plugin for InspectionPlugin {
return; return;
} }
let immediate_repaint = output
.viewport_output
.values()
.any(|viewport| viewport.repaint_delay == Duration::ZERO);
let step = self.step; let step = self.step;
self.in_flight self.in_flight
.retain_mut(|item| match (&item.phase, &item.req) { .retain_mut(|item| match (&mut item.phase, &item.req) {
(Phase::AwaitOutput, Request::GetTree) => { (Phase::AwaitOutput, Request::GetTree) => {
if let Some(reply) = item.reply.take() { if let Some(reply) = item.reply.take() {
reply(Response::Tree { reply(Response::Tree {
@@ -268,6 +288,27 @@ impl egui::Plugin for InspectionPlugin {
} }
false false
} }
(
Phase::Settle {
steps_taken,
max_steps,
},
Request::Settle { .. },
) => {
*steps_taken += 1;
let steps_exceeded = *steps_taken >= *max_steps;
if !immediate_repaint || steps_exceeded {
if let Some(reply) = item.reply.take() {
reply(Response::Settled {
settled: !immediate_repaint,
steps: *steps_taken,
});
}
false
} else {
true
}
}
_ => true, _ => true,
}); });

View File

@@ -63,6 +63,11 @@ pub enum Request {
/// (via [`egui::ViewportCommand::InnerSize`]). Reply: [`Response::Done`]. This is the one /// (via [`egui::ViewportCommand::InnerSize`]). Reply: [`Response::Done`]. This is the one
/// action that isn't expressible as an [`egui::Event`]. /// action that isn't expressible as an [`egui::Event`].
Resize { width: u32, height: u32 }, Resize { width: u32, height: u32 },
/// Wait until the app goes idle, then reply [`Response::Settled`].
///
/// Will wait for at most `max_steps`.
Settle { max_steps: u64 },
} }
/// Sent peer → inspector, exactly one per [`Request`]. /// Sent peer → inspector, exactly one per [`Request`].
@@ -99,6 +104,15 @@ pub enum Response {
/// (not merely received): the events were processed by a frame, or the resize dispatched. /// (not merely received): the events were processed by a frame, or the resize dispatched.
Done, Done,
/// Reply to [`Request::Settle`].
Settled {
/// Did we settle within `max_steps`?
settled: bool,
/// How many frames did we run until we settled?
steps: u64,
},
/// The peer failed to service the request (recoverable; the connection stays open). /// The peer failed to service the request (recoverable; the connection stays open).
Error { message: String }, Error { message: String },
} }