feat(replay): add HC bump for WIN MOVE scrub-bar marker; extend HighContrastBackground
HighContrastBackground gains an optional hc_color field so sites can specify a domain-specific HC variant rather than always bumping to BORDER_SUBTLE_HC (gray). with_default() fills hc_color = BORDER_SUBTLE_HC preserving all existing behaviour; new with_hc(default, hc) lets callers specify both ends. update_high_contrast_backgrounds reads marker.hc_color instead of the hardcoded constant. STATE_SUCCESS_HC (#c8e862, L≈0.73) added to ui_theme — a brighter lime that maintains the success hue while standing out from bumped notch ticks (BORDER_SUBTLE_HC gray, L≈0.60) under HC mode. WIN MOVE marker now carries HighContrastBackground::with_hc(STATE_SUCCESS, STATE_SUCCESS_HC): lime stays lime under HC instead of turning gray. Unit test pins both the default and hc color fields on the spawned marker. 1276 tests pass / 0 failing. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -38,8 +38,8 @@ use solitaire_data::ReplayMove;
|
||||
use crate::ui_modal::{spawn_modal_button, ButtonVariant};
|
||||
use crate::ui_theme::{
|
||||
ACCENT_PRIMARY, BG_ELEVATED_HI, BORDER_SUBTLE, HighContrastBackground, HighContrastBorder,
|
||||
STATE_SUCCESS, TEXT_PRIMARY, TEXT_PRIMARY_HC, TEXT_SECONDARY, TYPE_BODY, TYPE_CAPTION,
|
||||
TYPE_HEADLINE, VAL_SPACE_1, VAL_SPACE_2, VAL_SPACE_4, Z_DROP_OVERLAY,
|
||||
STATE_SUCCESS, STATE_SUCCESS_HC, TEXT_PRIMARY, TEXT_PRIMARY_HC, TEXT_SECONDARY, TYPE_BODY,
|
||||
TYPE_CAPTION, TYPE_HEADLINE, VAL_SPACE_1, VAL_SPACE_2, VAL_SPACE_4, Z_DROP_OVERLAY,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -779,6 +779,11 @@ fn spawn_overlay(
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(STATE_SUCCESS),
|
||||
// HC bump: lime → brighter lime so the win
|
||||
// marker reads clearly above the bumped
|
||||
// notch ticks (BORDER_SUBTLE_HC gray) under
|
||||
// high-contrast mode.
|
||||
HighContrastBackground::with_hc(STATE_SUCCESS, STATE_SUCCESS_HC),
|
||||
));
|
||||
}
|
||||
// Fixed quarter-mark notches: five 1px vertical
|
||||
@@ -2349,6 +2354,44 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// The WIN MOVE marker carries `HighContrastBackground::with_hc(
|
||||
/// STATE_SUCCESS, STATE_SUCCESS_HC)` so the lime bumps to brighter
|
||||
/// lime under HC mode rather than to a neutral gray. Pin the
|
||||
/// presence of the marker so a future refactor can't accidentally
|
||||
/// drop it and silently regress HC legibility.
|
||||
#[test]
|
||||
fn win_move_marker_carries_hc_background_marker() {
|
||||
let mut app = headless_app();
|
||||
set_state(
|
||||
&mut app,
|
||||
ReplayPlaybackState::Playing {
|
||||
replay: synthetic_replay(8).with_win_move_index(Some(7)),
|
||||
cursor: 0,
|
||||
secs_to_next: 0.5,
|
||||
paused: false,
|
||||
},
|
||||
);
|
||||
app.update();
|
||||
|
||||
let mut q = app
|
||||
.world_mut()
|
||||
.query_filtered::<&HighContrastBackground, With<ReplayOverlayWinMoveMarker>>();
|
||||
let marker = q
|
||||
.iter(app.world())
|
||||
.next()
|
||||
.expect("WIN MOVE marker must carry HighContrastBackground");
|
||||
assert_eq!(
|
||||
marker.default_color,
|
||||
STATE_SUCCESS,
|
||||
"default colour must be STATE_SUCCESS"
|
||||
);
|
||||
assert_eq!(
|
||||
marker.hc_color,
|
||||
STATE_SUCCESS_HC,
|
||||
"HC colour must be STATE_SUCCESS_HC (brighter lime, not gray)"
|
||||
);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// scrub_notch_positions + ReplayOverlayScrubNotch spawn behaviour
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
@@ -701,7 +701,7 @@ pub(crate) fn update_high_contrast_backgrounds(
|
||||
let high_contrast = settings.0.high_contrast_mode;
|
||||
for (marker, mut bg) in backgrounds.iter_mut() {
|
||||
let target = if high_contrast {
|
||||
BORDER_SUBTLE_HC
|
||||
marker.hc_color
|
||||
} else {
|
||||
marker.default_color
|
||||
};
|
||||
|
||||
@@ -93,6 +93,13 @@ pub const ACCENT_SECONDARY: Color = Color::srgb(0.882, 0.639, 0.933);
|
||||
/// from base16-eighties. `#acc267`.
|
||||
pub const STATE_SUCCESS: Color = Color::srgb(0.675, 0.761, 0.404);
|
||||
|
||||
/// High-contrast variant of [`STATE_SUCCESS`] — `#c8e862`. Brighter
|
||||
/// lime that maintains the success hue while lifting luminance from
|
||||
/// ~0.51 → ~0.73 so the WIN MOVE scrub-bar marker stands out from
|
||||
/// the bumped notch ticks (`BORDER_SUBTLE_HC` `#a0a0a0`, L≈0.60) in
|
||||
/// high-contrast mode.
|
||||
pub const STATE_SUCCESS_HC: Color = Color::srgb(0.784, 0.910, 0.384);
|
||||
|
||||
/// Warning — penalty signal, daily-seed expiry countdown, sync-pending
|
||||
/// status. Gold from base16-eighties. **Both** Undo and Recycle
|
||||
/// counters use this when non-zero. `#ddb26f`.
|
||||
@@ -260,24 +267,45 @@ impl HighContrastBorder {
|
||||
/// often render as tiny full-bleed `Node`s, not as borders, so the
|
||||
/// border-marker pattern doesn't apply.
|
||||
///
|
||||
/// `default_color` records the off-state colour the entity was
|
||||
/// spawned with so the system can revert when HC is toggled back
|
||||
/// off. The accompanying paint system is
|
||||
/// [`update_high_contrast_backgrounds`](crate::settings_plugin::update_high_contrast_backgrounds).
|
||||
/// `default_color` records the off-state colour; `hc_color` the on-
|
||||
/// state colour. [`with_default`] fills `hc_color` with
|
||||
/// [`BORDER_SUBTLE_HC`] so the 90 % of sites that just need the
|
||||
/// standard subtle-border bump can continue using a one-argument
|
||||
/// constructor. [`with_hc`] overrides the HC colour for the rare
|
||||
/// site (currently only the WIN MOVE scrub-bar marker) that needs a
|
||||
/// domain-specific HC variant (`STATE_SUCCESS_HC` instead of a gray).
|
||||
///
|
||||
/// [`with_default`]: HighContrastBackground::with_default
|
||||
/// [`with_hc`]: HighContrastBackground::with_hc
|
||||
/// [`BackgroundColor`]: bevy::prelude::BackgroundColor
|
||||
#[derive(bevy::prelude::Component, Debug, Clone, Copy)]
|
||||
pub struct HighContrastBackground {
|
||||
/// Background colour to use when high-contrast mode is *off* —
|
||||
/// the site's normal idle / active-state colour.
|
||||
pub default_color: bevy::prelude::Color,
|
||||
/// Background colour to use when high-contrast mode is *on*.
|
||||
/// Defaults to [`BORDER_SUBTLE_HC`] via [`with_default`].
|
||||
///
|
||||
/// [`with_default`]: HighContrastBackground::with_default
|
||||
pub hc_color: bevy::prelude::Color,
|
||||
}
|
||||
|
||||
impl HighContrastBackground {
|
||||
/// Convenience constructor —
|
||||
/// `HighContrastBackground::with_default(BORDER_SUBTLE)`.
|
||||
/// Convenience constructor — HC colour defaults to
|
||||
/// [`BORDER_SUBTLE_HC`].
|
||||
pub const fn with_default(default_color: bevy::prelude::Color) -> Self {
|
||||
Self { default_color }
|
||||
Self { default_color, hc_color: BORDER_SUBTLE_HC }
|
||||
}
|
||||
|
||||
/// Constructor for sites whose HC colour differs from the standard
|
||||
/// [`BORDER_SUBTLE_HC`]. Currently used by the WIN MOVE scrub-bar
|
||||
/// marker which bumps `STATE_SUCCESS` → `STATE_SUCCESS_HC` rather
|
||||
/// than to a neutral gray.
|
||||
pub const fn with_hc(
|
||||
default_color: bevy::prelude::Color,
|
||||
hc_color: bevy::prelude::Color,
|
||||
) -> Self {
|
||||
Self { default_color, hc_color }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user