From dd304913df053adad0cff3c4ee08b762084b3042 Mon Sep 17 00:00:00 2001 From: funman300 Date: Mon, 13 Jul 2026 15:49:15 -0700 Subject: [PATCH 1/2] =?UTF-8?q?docs(ui):=20record=20Phase=20F=20decision?= =?UTF-8?q?=205=20=E2=80=94=20bottom=20action=20bar=20is=20touch-only?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- docs/ui-redesign-2026-07.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/ui-redesign-2026-07.md b/docs/ui-redesign-2026-07.md index 8e64db8..94e016a 100644 --- a/docs/ui-redesign-2026-07.md +++ b/docs/ui-redesign-2026-07.md @@ -205,4 +205,7 @@ slimming), then **B**, then **F/G/H** as independent follow-ups. **DECIDED 2026-07-09: top-level cards, symmetric 2×3 grid.** 4. ~~Stitch mockups for Phase B, or iterate directly in-engine~~ — **DECIDED 2026-07-09: directly in-engine.** -5. Phase F bottom bar: touch-only (proposed) or also desktop? +5. ~~Phase F bottom bar: touch-only vs. also desktop~~ — + **DECIDED 2026-07-13: touch-only.** Desktop keeps the current top + band; the bar is additive per §3.3 so a later desktop rollout stays + a settings flag away. From fa54c58bc4dce54e17e8f25937ab42d95308cb74 Mon Sep 17 00:00:00 2001 From: funman300 Date: Mon, 13 Jul 2026 16:07:10 -0700 Subject: [PATCH 2/2] =?UTF-8?q?feat(engine):=20Phase=20F=20touch=20action?= =?UTF-8?q?=20bar=20=E2=80=94=20thumb-reach=20Undo/Draw/Hint=20+=20hold-to?= =?UTF-8?q?-repeat=20undo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Touch layout (USE_TOUCH_UI_LAYOUT) restructures the bottom action bar to five buttons: an enlarged Undo / Draw / Hint trio (96x64px targets, 1.35x labels) between compact Menu and Pause. Draw is new — it fires the same DrawRequestEvent as tapping the stock, so the most frequent action no longer needs a reach to the top of a tall folded screen. Help, Modes, and New Game leave the touch bar (they live in Menu -> System, the Home grid, and Home's hero respectively). Desktop keeps the seven-button bar unchanged (decision 5: touch-only). Holding Undo now steps back repeatedly after a 0.45s delay (5.5/s), each step through the normal request path so the scoring penalty applies. New self-ambiguous DrawRequestWriters set keeps the ambiguity gate at zero with the fourth DrawRequestEvent writer. 6 new hud_plugin tests; workspace suite + clippy green. Co-Authored-By: Claude Fable 5 --- solitaire_engine/src/game_plugin/mod.rs | 8 + .../src/hud_plugin/interaction.rs | 80 +++++ solitaire_engine/src/hud_plugin/mod.rs | 56 ++-- solitaire_engine/src/hud_plugin/spawn.rs | 277 +++++++++++++----- solitaire_engine/src/hud_plugin/tests.rs | 156 +++++++++- solitaire_engine/src/hud_plugin/updates.rs | 24 +- solitaire_engine/src/input_plugin/mod.rs | 17 +- 7 files changed, 514 insertions(+), 104 deletions(-) diff --git a/solitaire_engine/src/game_plugin/mod.rs b/solitaire_engine/src/game_plugin/mod.rs index b9579db..9a46862 100644 --- a/solitaire_engine/src/game_plugin/mod.rs +++ b/solitaire_engine/src/game_plugin/mod.rs @@ -86,6 +86,14 @@ pub struct NewGameRequestWriters; #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] pub struct UndoRequestWriters; +/// Self-ambiguous set for writers of `DrawRequestEvent` — same rationale as +/// [`NewGameRequestWriters`]: consumers drain the queue, append order is +/// meaningless (#143). Members: keyboard `D`/`Space`, stock click, touch +/// stock tap (input_plugin), and the touch action bar's Draw button +/// (hud_plugin, Phase F). +#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] +pub struct DrawRequestWriters; + /// Self-ambiguous set for writers of `InfoToastEvent` — toasts queue in /// arrival order and any same-frame order is fine (#143). #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] diff --git a/solitaire_engine/src/hud_plugin/interaction.rs b/solitaire_engine/src/hud_plugin/interaction.rs index 128200c..e2cf890 100644 --- a/solitaire_engine/src/hud_plugin/interaction.rs +++ b/solitaire_engine/src/hud_plugin/interaction.rs @@ -30,6 +30,86 @@ pub(super) fn handle_undo_button( } } +/// Seconds the Undo button must be continuously held before hold-to-repeat +/// kicks in. Long enough that a normal tap (press + release inside one or +/// two frames) never triggers a second undo. +const UNDO_HOLD_INITIAL_DELAY_SECS: f32 = 0.45; + +/// Interval between repeated undos while the hold continues. ~5.5 undos/s — +/// fast enough to unwind a long line, slow enough to release in time when +/// the board reaches the state the player wants. +const UNDO_HOLD_REPEAT_INTERVAL_SECS: f32 = 0.18; + +/// Hold-to-repeat undo (Phase F): while the Undo button stays pressed, +/// fire additional [`UndoRequestEvent`]s after an initial delay, one per +/// repeat interval. The plain tap path stays in [`handle_undo_button`] +/// (its `Changed` filter fires exactly once per press); +/// this system only adds events once the hold outlives the delay, so a +/// tap never double-undoes. +/// +/// Each repeat goes through the normal request queue — the scoring +/// penalty and No-Undo-mode gating in the consumer apply to every step. +pub(super) fn repeat_undo_on_hold( + time: Res