feat(engine): Phase F touch action bar — thumb-reach Undo/Draw/Hint + hold-to-repeat undo
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 <noreply@anthropic.com>
This commit is contained in:
@@ -28,7 +28,7 @@ struct AvatarResource(Option<bevy::prelude::Handle<bevy::prelude::Image>>);
|
||||
use crate::challenge_plugin::CHALLENGE_UNLOCK_LEVEL;
|
||||
use crate::daily_challenge_plugin::DailyChallengeResource;
|
||||
use crate::events::{
|
||||
HelpRequestEvent, InfoToastEvent, NewGameRequestEvent, PauseRequestEvent,
|
||||
DrawRequestEvent, HelpRequestEvent, InfoToastEvent, NewGameRequestEvent, PauseRequestEvent,
|
||||
StartChallengeRequestEvent, StartDailyChallengeRequestEvent, StartTimeAttackRequestEvent,
|
||||
StartZenRequestEvent, ToggleAchievementsRequestEvent, ToggleHomeRequestEvent,
|
||||
ToggleLeaderboardRequestEvent, ToggleProfileRequestEvent, ToggleSettingsRequestEvent,
|
||||
@@ -313,6 +313,18 @@ pub struct UndoButton;
|
||||
#[derive(Component, Debug)]
|
||||
pub struct PauseButton;
|
||||
|
||||
/// Accumulated hold time on the Undo button, driving hold-to-repeat undo
|
||||
/// (Phase F). `handle_undo_button` owns the instant first undo on press;
|
||||
/// `repeat_undo_on_hold` starts firing additional [`UndoRequestEvent`]s
|
||||
/// once the hold passes `UNDO_HOLD_INITIAL_DELAY_SECS` and then every
|
||||
/// `UNDO_HOLD_REPEAT_INTERVAL_SECS`. Each repeated undo goes through the
|
||||
/// normal request path, so the existing scoring penalty applies per step.
|
||||
#[derive(Resource, Debug, Default)]
|
||||
pub struct UndoHoldState {
|
||||
/// Seconds the Undo button has been continuously held.
|
||||
held_secs: f32,
|
||||
}
|
||||
|
||||
/// Marker on the "Help" action button. Click fires [`HelpRequestEvent`],
|
||||
/// mirroring the `F1` keyboard accelerator.
|
||||
#[derive(Component, Debug)]
|
||||
@@ -323,31 +335,27 @@ pub struct HelpButton;
|
||||
#[derive(Component, Debug)]
|
||||
pub struct HintButton;
|
||||
|
||||
/// Marker on the touch action bar's "Draw" button (Phase F). Click fires
|
||||
/// [`DrawRequestEvent`] — same queue as tapping the stock pile — so the
|
||||
/// draw action is within thumb reach without stretching to the top of a
|
||||
/// tall folded screen. Touch layout only; desktop draws via stock
|
||||
/// click / `D` / `Space`.
|
||||
#[derive(Component, Debug)]
|
||||
pub struct DrawButton;
|
||||
|
||||
/// Android HUD label for the Hint button — shared with the help screen's
|
||||
/// controls reference so both always agree.
|
||||
#[cfg(target_os = "android")]
|
||||
pub(crate) const ANDROID_HINT_LABEL: &str = "Hint";
|
||||
|
||||
/// Hint label used by the touch action bar. Aliases [`ANDROID_HINT_LABEL`]
|
||||
/// on Android so the bar and the help screen's controls reference can
|
||||
/// never drift; the non-Android value only exists so the touch spawn
|
||||
/// path stays host-compilable for tests.
|
||||
#[cfg(target_os = "android")]
|
||||
const ACTION_BAR_LABELS: [&str; 7] = [
|
||||
"Menu",
|
||||
"Undo",
|
||||
"Pause",
|
||||
"Help",
|
||||
ANDROID_HINT_LABEL,
|
||||
"Mode",
|
||||
"New",
|
||||
];
|
||||
const TOUCH_HINT_LABEL: &str = ANDROID_HINT_LABEL;
|
||||
#[cfg(not(target_os = "android"))]
|
||||
const ACTION_BAR_LABELS: [&str; 7] = [
|
||||
"Menu \u{2193}",
|
||||
"Undo",
|
||||
"Pause",
|
||||
"Help",
|
||||
"Hint",
|
||||
"Modes \u{2193}",
|
||||
"New Game",
|
||||
];
|
||||
const TOUCH_HINT_LABEL: &str = "Hint";
|
||||
#[cfg(target_os = "android")]
|
||||
const ACTION_BAR_COLUMN_GAP: Val = Val::Px(4.0);
|
||||
#[cfg(not(target_os = "android"))]
|
||||
@@ -455,6 +463,7 @@ impl Plugin for HudPlugin {
|
||||
// idempotent.
|
||||
app.add_message::<NewGameRequestEvent>()
|
||||
.add_message::<UndoRequestEvent>()
|
||||
.add_message::<DrawRequestEvent>()
|
||||
.add_message::<PauseRequestEvent>()
|
||||
.add_message::<HelpRequestEvent>()
|
||||
.add_message::<StartZenRequestEvent>()
|
||||
@@ -471,6 +480,7 @@ impl Plugin for HudPlugin {
|
||||
.init_resource::<PreviousScore>()
|
||||
.init_resource::<HudActionFade>()
|
||||
.init_resource::<HudVisibility>()
|
||||
.init_resource::<UndoHoldState>()
|
||||
// Escape-close handlers for popovers read this; init defensively
|
||||
// so HudPlugin works under MinimalPlugins in tests.
|
||||
.init_resource::<ButtonInput<KeyCode>>()
|
||||
@@ -560,6 +570,14 @@ impl Plugin for HudPlugin {
|
||||
.in_set(crate::game_plugin::UndoRequestWriters)
|
||||
.ambiguous_with(crate::game_plugin::UndoRequestWriters)
|
||||
.before(GameMutation),
|
||||
repeat_undo_on_hold
|
||||
.in_set(crate::game_plugin::UndoRequestWriters)
|
||||
.ambiguous_with(crate::game_plugin::UndoRequestWriters)
|
||||
.before(GameMutation),
|
||||
handle_draw_button
|
||||
.in_set(crate::game_plugin::DrawRequestWriters)
|
||||
.ambiguous_with(crate::game_plugin::DrawRequestWriters)
|
||||
.before(GameMutation),
|
||||
handle_pause_button,
|
||||
handle_help_button,
|
||||
handle_hint_button
|
||||
|
||||
Reference in New Issue
Block a user