From 23902cdc445d0218f35520c25ce9ef01416dbf79 Mon Sep 17 00:00:00 2001 From: funman300 Date: Fri, 8 May 2026 16:41:49 -0700 Subject: [PATCH] feat(replay): HC-mode coverage for keybind-footer top border MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tag the footer's border-carrying Node with `HighContrastBorder::with_default(BORDER_SUBTLE)` so the existing `apply_high_contrast_borders` system bumps the 1 px top border from `BORDER_SUBTLE` (#505050) to `BORDER_SUBTLE_HC` (#a0a0a0) when `Settings::high_contrast_mode` is on. Without this the footer reads as floating loose under HC because the border that visually anchors it to the labels row above is near-invisible at #505050 against the elevated banner background. The footer's text colours (`TEXT_SECONDARY` on both the mode-line and the hint) don't need an HC bump — `TEXT_SECONDARY` is already at `#a0a0a0`, the same luminance as `BORDER_SUBTLE_HC`. There's no `TEXT_SECONDARY_HC` constant in the palette because secondary text is already at HC-border level by design. The notch labels also use `TEXT_SECONDARY` and inherit the same "already HC-bright" property — no marker needed there either. The 1 px scrub track, notch ticks, and WIN MOVE marker render via `BackgroundColor` (not `BorderColor`) so the `HighContrastBorder` marker doesn't apply. HC coverage for those decorative pieces would need a custom settings-aware paint system (precedent: `radial_rim_outline` in `radial_menu`) and is deferred to a follow-up commit. 1 new test pinning the marker on spawn. 1243 → 1244. Clippy clean. Co-Authored-By: Claude Opus 4.7 --- solitaire_engine/src/replay_overlay.rs | 41 ++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/solitaire_engine/src/replay_overlay.rs b/solitaire_engine/src/replay_overlay.rs index cad10de..1cf42e9 100644 --- a/solitaire_engine/src/replay_overlay.rs +++ b/solitaire_engine/src/replay_overlay.rs @@ -35,8 +35,9 @@ use crate::replay_playback::{ use solitaire_data::ReplayMove; use crate::ui_modal::{spawn_modal_button, ButtonVariant}; use crate::ui_theme::{ - ACCENT_PRIMARY, BG_ELEVATED_HI, BORDER_SUBTLE, STATE_SUCCESS, TEXT_PRIMARY, TEXT_SECONDARY, - TYPE_BODY, TYPE_CAPTION, TYPE_HEADLINE, VAL_SPACE_1, VAL_SPACE_2, VAL_SPACE_4, Z_DROP_OVERLAY, + ACCENT_PRIMARY, BG_ELEVATED_HI, BORDER_SUBTLE, HighContrastBorder, STATE_SUCCESS, TEXT_PRIMARY, + TEXT_SECONDARY, TYPE_BODY, TYPE_CAPTION, TYPE_HEADLINE, VAL_SPACE_1, VAL_SPACE_2, VAL_SPACE_4, + Z_DROP_OVERLAY, }; // --------------------------------------------------------------------------- @@ -680,6 +681,14 @@ fn spawn_overlay( ..default() }, BorderColor::all(BORDER_SUBTLE), + // Marker for `apply_high_contrast_borders`: bumps + // the 1 px top border from BORDER_SUBTLE (#505050) + // to BORDER_SUBTLE_HC (#a0a0a0) when + // `Settings::high_contrast_mode` is on. Without + // this the footer reads as floating loose under + // HC because the border that visually anchors it + // to the labels row above is near-invisible. + HighContrastBorder::with_default(BORDER_SUBTLE), )) .with_children(|footer| { footer.spawn(( @@ -2008,6 +2017,34 @@ mod tests { ); } + /// Spawned footer carries `HighContrastBorder` so the existing + /// `apply_high_contrast_borders` system bumps the 1 px top + /// border under HC mode. Without this the footer reads as + /// floating loose under HC. + #[test] + fn keybind_footer_carries_high_contrast_border_marker() { + let mut app = headless_app(); + set_state( + &mut app, + ReplayPlaybackState::Playing { + replay: synthetic_replay(10), + cursor: 0, + secs_to_next: 0.5, + paused: false, + }, + ); + app.update(); + + let mut q = app + .world_mut() + .query_filtered::<&HighContrastBorder, With>(); + let marker = q.iter(app.world()).next(); + assert!( + marker.is_some(), + "footer must carry HighContrastBorder so `apply_high_contrast_borders` picks it up under HC mode", + ); + } + /// Footer shares the overlay tree's lifecycle — it despawns on /// `Playing → Inactive` along with the banner root. #[test]