diff --git a/apps/vectorcraft/src/app.rs b/apps/vectorcraft/src/app.rs index 288fa6cdc..0391b7534 100644 --- a/apps/vectorcraft/src/app.rs +++ b/apps/vectorcraft/src/app.rs @@ -35,7 +35,7 @@ impl eframe::App for App { // control channel asks for, and the frames carrying input it injected, to the automation roots // (`VectorcraftApp::with_automation_roots`), which are in force elsewhere in the process. fn logic(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { - BEACON.enter(Stage::Logic); + let _timed = BEACON.enter_timed(Stage::Logic); file_access::unconfined(|| self.logic_frame(ctx, frame)); self.app.finish_ime_logic(ctx); BEACON.enter(Stage::Idle); @@ -43,7 +43,7 @@ impl eframe::App for App { /// Restore the borderless caption, apply platform menu and pen input, and hand the frame to the /// app's raw-input hook while the keyboard user is unconfined. fn raw_input_hook(&mut self, ctx: &egui::Context, raw: &mut egui::RawInput) { - BEACON.enter(Stage::Input); + let _timed = BEACON.enter_timed(Stage::Input); self.frame_start = Some(std::time::Instant::now()); // winit gives the window its caption back when it changes the window's flags (maximize, // restore, …): take it off again before the frame is laid out (#1758). @@ -68,7 +68,7 @@ impl eframe::App for App { BEACON.enter(Stage::Idle); } fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) { - BEACON.enter(Stage::Interface); + let _timed = BEACON.enter_timed(Stage::Interface); #[cfg(target_os = "macos")] { let bar = vectorcraft_ui_egui::chrome::APP_BAR_HEIGHT; diff --git a/crates/ui-egui/src/hang_beacon.rs b/crates/ui-egui/src/hang_beacon.rs index 3331a1e39..e47dbfc79 100644 --- a/crates/ui-egui/src/hang_beacon.rs +++ b/crates/ui-egui/src/hang_beacon.rs @@ -196,6 +196,14 @@ impl Beacon { self.bump(); } + /// The interface thread enters `stage` of a frame, timed whatever the activity around it, so a + /// frame the system runs while a file dialog waits for the user is timed too. The activity + /// before it applies again when the guard is dropped. + pub fn enter_timed(&self, stage: Stage) -> ActivityGuard<'_> { + self.enter(stage); + self.activity(Activity::None) + } + /// The interface thread starts `activity`; it ends when the guard is dropped, and the activity /// before it applies again. pub fn activity(&self, activity: Activity) -> ActivityGuard<'_> { @@ -538,6 +546,20 @@ mod tests { assert!(B.take_input(), "the main window's input"); } + #[test] + fn a_frame_run_while_a_dialog_waits_is_timed() { + let b = Beacon::new(); + let _dialog = b.activity(Activity::Dialog); + assert!(!b.sample().busy(), "the dialog waits for the user"); + { + let _timed = b.enter_timed(Stage::Interface); + assert!(b.sample().busy(), "a frame inside the dialog's wait"); + b.enter(Stage::Drawing); + } + assert_eq!(b.sample().activity, Activity::Dialog, "the dialog again"); + assert!(!b.sample().busy()); + } + #[test] fn a_window_shown_in_line_handed_to_the_system_is_marked() { static B: Beacon = Beacon::new();