feat(engine): restyle replay progress text as Terminal MOVE chip
Closes the centre-text half of the replay-overlay enrichments arc. The plain "Move N of M" text becomes a 1px ACCENT_PRIMARY-bordered chip containing "MOVE N/M" — uppercase + slash separator reads as a Terminal output line and matches the floating-chip motif in docs/ui-mockups/replay-overlay-mobile.html. The chip lives in-banner rather than floating above the focused card; the screen-takeover treatment that requires plumbing cursor → card identity remains deferred per SESSION_HANDOFF. Implementation: the centre Text spawn is now wrapped in a Node with 1px border + axes(VAL_SPACE_2, VAL_SPACE_1) padding and no background fill (Terminal aesthetic gets depth from borders + tonal layering, not shadows). The ReplayOverlayProgressText marker stays on the inner Text so update_progress_text continues to repaint contents unchanged. format_progress now returns "MOVE N/M" for Playing and "REPLAY COMPLETE" for Completed (uppercase to match the chip's typographic treatment); Inactive still returns "" since the overlay shouldn't be spawned in that state. Used BorderColor::all(ACCENT_PRIMARY) — Bevy's BorderColor is per-side in 0.18, no longer the tuple struct it was earlier. Module-level docstring + ReplayOverlayScrubFill doc comment both updated to quote the new "MOVE N/M" string. Test overlay_progress_text_reflects_cursor swapped its assertion to match. 1182 tests still pass; clippy clean. This closes Option C from the SESSION_HANDOFF Resume prompt's banner- local enrichments. The full screen-takeover redesign (mini-tableau, playback controls, move-log scroll, WIN MOVE marker requiring a win_move_index field on Replay) remains the multi-session item.
This commit is contained in:
@@ -4,8 +4,9 @@
|
|||||||
//!
|
//!
|
||||||
//! - A "▌ replay" label on the left so the player knows the surface is
|
//! - A "▌ replay" label on the left so the player knows the surface is
|
||||||
//! under playback control rather than live input.
|
//! under playback control rather than live input.
|
||||||
//! - A "Move N of M" progress indicator in the centre, recomputed every
|
//! - A "MOVE N/M" progress chip in the centre, recomputed every frame
|
||||||
//! frame the cursor advances.
|
//! the cursor advances and bordered in cyan ACCENT_PRIMARY so it
|
||||||
|
//! reads as a discrete callout.
|
||||||
//! - A "Stop" button on the right that aborts playback and returns
|
//! - A "Stop" button on the right that aborts playback and returns
|
||||||
//! control to the player.
|
//! control to the player.
|
||||||
//!
|
//!
|
||||||
@@ -30,7 +31,7 @@ use crate::replay_playback::{stop_replay_playback, ReplayPlaybackState};
|
|||||||
use crate::ui_modal::{spawn_modal_button, ButtonVariant};
|
use crate::ui_modal::{spawn_modal_button, ButtonVariant};
|
||||||
use crate::ui_theme::{
|
use crate::ui_theme::{
|
||||||
ACCENT_PRIMARY, BG_ELEVATED_HI, BORDER_SUBTLE, TEXT_PRIMARY, TEXT_SECONDARY, TYPE_BODY,
|
ACCENT_PRIMARY, BG_ELEVATED_HI, BORDER_SUBTLE, TEXT_PRIMARY, TEXT_SECONDARY, TYPE_BODY,
|
||||||
TYPE_CAPTION, TYPE_HEADLINE, VAL_SPACE_2, VAL_SPACE_4, Z_DROP_OVERLAY,
|
TYPE_CAPTION, TYPE_HEADLINE, VAL_SPACE_1, VAL_SPACE_2, VAL_SPACE_4, Z_DROP_OVERLAY,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -110,7 +111,7 @@ pub struct ReplayOverlayGameCaption;
|
|||||||
/// continuous visual cue of how far through the replay they are.
|
/// continuous visual cue of how far through the replay they are.
|
||||||
///
|
///
|
||||||
/// Distinct from the simpler text-based `ReplayOverlayProgressText`
|
/// Distinct from the simpler text-based `ReplayOverlayProgressText`
|
||||||
/// (which spells out "Move N of M"): the scrub fill gives immediate
|
/// (which spells out "MOVE N/M" in a chip): the scrub fill gives immediate
|
||||||
/// at-a-glance positioning; the text gives the exact numbers. Both
|
/// at-a-glance positioning; the text gives the exact numbers. Both
|
||||||
/// surfaces stay together because they answer the same question for
|
/// surfaces stay together because they answer the same question for
|
||||||
/// players with different scanning preferences.
|
/// players with different scanning preferences.
|
||||||
@@ -284,10 +285,23 @@ fn spawn_overlay(
|
|||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Centre: progress readout — neutral primary text
|
// Centre: progress readout, wrapped in a 1 px
|
||||||
// colour so the eye treats it as data, not a
|
// ACCENT_PRIMARY-bordered chip so it reads as a
|
||||||
// callout.
|
// discrete callout rather than free-floating
|
||||||
|
// text. No fill — the Terminal aesthetic gets
|
||||||
|
// depth from borders + tonal layering, not
|
||||||
|
// shadows. The marker stays on the inner Text so
|
||||||
|
// `update_progress_text` keeps working unchanged.
|
||||||
row.spawn((
|
row.spawn((
|
||||||
|
Node {
|
||||||
|
border: UiRect::all(Val::Px(1.0)),
|
||||||
|
padding: UiRect::axes(VAL_SPACE_2, VAL_SPACE_1),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
BorderColor::all(ACCENT_PRIMARY),
|
||||||
|
))
|
||||||
|
.with_children(|chip| {
|
||||||
|
chip.spawn((
|
||||||
ReplayOverlayProgressText,
|
ReplayOverlayProgressText,
|
||||||
Text::new(progress_label),
|
Text::new(progress_label),
|
||||||
TextFont {
|
TextFont {
|
||||||
@@ -297,6 +311,7 @@ fn spawn_overlay(
|
|||||||
},
|
},
|
||||||
TextColor(TEXT_PRIMARY),
|
TextColor(TEXT_PRIMARY),
|
||||||
));
|
));
|
||||||
|
});
|
||||||
|
|
||||||
// Right: Stop button. Tertiary variant — the
|
// Right: Stop button. Tertiary variant — the
|
||||||
// action is available but not the loudest element
|
// action is available but not the loudest element
|
||||||
@@ -452,8 +467,11 @@ fn format_game_caption(state: &ReplayPlaybackState) -> Option<String> {
|
|||||||
/// path produce the exact same string.
|
/// path produce the exact same string.
|
||||||
fn format_progress(state: &ReplayPlaybackState) -> String {
|
fn format_progress(state: &ReplayPlaybackState) -> String {
|
||||||
match state.progress() {
|
match state.progress() {
|
||||||
Some((cursor, total)) => format!("Move {cursor} of {total}"),
|
// `MOVE N/M` (uppercase + slash) reads as a Terminal output
|
||||||
None if state.is_completed() => "Replay complete".to_string(),
|
// line and matches the floating-chip motif in the mockup at
|
||||||
|
// `docs/ui-mockups/replay-overlay-mobile.html`.
|
||||||
|
Some((cursor, total)) => format!("MOVE {cursor}/{total}"),
|
||||||
|
None if state.is_completed() => "REPLAY COMPLETE".to_string(),
|
||||||
None => String::new(),
|
None => String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -604,7 +622,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
app.update();
|
app.update();
|
||||||
|
|
||||||
assert_eq!(progress_text(&mut app), "Move 5 of 10");
|
assert_eq!(progress_text(&mut app), "MOVE 5/10");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pressing the Stop button resets the state back to `Inactive` and
|
/// Pressing the Stop button resets the state back to `Inactive` and
|
||||||
|
|||||||
Reference in New Issue
Block a user