From 7820825ea10bd2ab67af1a12280d4e12b91aa70e Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sun, 25 Oct 2026 17:09:48 -0700 Subject: [PATCH] M5: controller buttons pressed with no frame shown are dropped A controller button pressed while the player has no frame to show, for example after the frame is deleted, no longer plays the prototype's gamepad trigger once the frame comes back. --- crates/ui-egui/src/present.rs | 5 +++++ crates/ui-egui/src/present_tests.rs | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/crates/ui-egui/src/present.rs b/crates/ui-egui/src/present.rs index b5c7eb0f..b1947a68 100644 --- a/crates/ui-egui/src/present.rs +++ b/crates/ui-egui/src/present.rs @@ -2198,6 +2198,9 @@ fn player(p: &mut Presenter, ui: &mut Ui, rect: Rect, now: f64, chrome: Chrome) p.restart(p.start, now); } if p.doc.as_ref().is_none_or(|d| !d.contains(p.current)) { + // Controller buttons pressed with no frame to show are dropped, as they are while a link + // notice shows. + p.pads.clear(); if !chrome.pane { return Some(BarAction::Close); } @@ -2228,6 +2231,8 @@ fn player(p: &mut Presenter, ui: &mut Ui, rect: Rect, now: f64, chrome: Chrome) // the prototype takes neither keys nor the pointer (Escape there cancels the notice). let keys = chrome.keys && p.link_notice.is_none() && (!chrome.pane || over); input(p, &ctx, &l, over && p.link_notice.is_none(), keys, chrome.pane, now); + } else { + p.pads.clear(); } let Some(l) = layout(p, area, chrome) else { return action }; p.shown = Some((p.current, l.place, l.clip.intersect(area))); diff --git a/crates/ui-egui/src/present_tests.rs b/crates/ui-egui/src/present_tests.rs index 93729922..4a3819c0 100644 --- a/crates/ui-egui/src/present_tests.rs +++ b/crates/ui-egui/src/present_tests.rs @@ -137,6 +137,26 @@ fn preview_pane_opens_follows_edits_and_the_selection() { assert!(u.app.ui.preview.is_none()); } +/// A controller button pressed while the pane has no frame to show is dropped, and does not play +/// once the frame comes back. +#[test] +fn controller_buttons_pressed_with_no_frame_shown_are_dropped() { + let (mut u, a, b, _) = setup(); + let to_b = json!({"type": "navigate", "dest": b}); + u.run("proto.interaction.add", json!({"id": a, "trigger": {"type": "keyPress", "key": "Gamepad A"}, "action": to_b})); + u.run("proto.preview", json!({"start": a})); + u.run("edit.delete", json!({"ids": [a]})); + let ctx = u.ctx.clone(); + crate::control::handle(&mut u.app, &ctx, "ui.input", &json!({"events": [{"gamepad": "A"}]})).unwrap(); + u.frames(2, Vec::new); + u.run("edit.undo", json!({})); + u.frames(2, Vec::new); + assert_eq!(u.preview()["current"], json!(a), "the button pressed with no frame shown did not play"); + crate::control::handle(&mut u.app, &ctx, "ui.input", &json!({"events": [{"gamepad": "A"}]})).unwrap(); + u.frames(2, Vec::new); + assert_eq!(u.preview()["current"], json!(b), "a button pressed with the frame shown plays"); +} + /// Asserts the preview's device viewport and scroll range, wheel and drag scrolling, the /// end clamp with spring-back, and that navigation resets scroll to the top. #[test]