Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c50eaf81f7 | |||
| b44d2777ec | |||
| 52407e7256 |
+31
-1
@@ -6,9 +6,39 @@ project follows [Semantic Versioning](https://semver.org/).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
No threads in flight. v0.21.6 cut on 2026-05-08; CHANGELOG accumulates
|
No threads in flight. v0.21.7 cut on 2026-05-08; CHANGELOG accumulates
|
||||||
the next cycle here.
|
the next cycle here.
|
||||||
|
|
||||||
|
## [0.21.7] — 2026-05-08
|
||||||
|
|
||||||
|
Patch release closing the last major B-2 sub-piece. Through-line:
|
||||||
|
**mini-tableau preview dim layer**. The mockup's "Game Peek Band at
|
||||||
|
50 % opacity" is now implemented as a full-screen UI scrim that darkens
|
||||||
|
the card world during replay so the chrome (banner + move-log panel)
|
||||||
|
reads clearly against the scene.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Full-screen tableau dim layer** (`da3e542`). Spawns a
|
||||||
|
`ReplayTableauDimLayer` UI node (100 % × 100 %, 50 % opacity
|
||||||
|
black) at `Z_REPLAY_DIM = Z_REPLAY_OVERLAY − 1 = 54` whenever
|
||||||
|
a replay starts; despawned alongside the banner and move-log
|
||||||
|
panel when the replay ends. Bevy's UI/world compositor means
|
||||||
|
no changes to `card_plugin` are needed — UI nodes always
|
||||||
|
render above world-space sprites regardless of `Transform.z`.
|
||||||
|
The dim layer carries no `Interaction` component (purely
|
||||||
|
visual; pointer events pass through). Adds `Z_REPLAY_DIM`
|
||||||
|
and `TABLEAU_DIM_ALPHA` constants plus two new tests:
|
||||||
|
lifecycle (spawn/despawn mirrors the floating-chip pattern)
|
||||||
|
and z-ordering invariant (`Z_REPLAY_DIM < Z_REPLAY_OVERLAY`
|
||||||
|
pinned). 1275 tests pass / 0 failing.
|
||||||
|
|
||||||
|
### Stats
|
||||||
|
|
||||||
|
- Tests: 1275 passing / 0 failing
|
||||||
|
- Clippy: clean
|
||||||
|
- Crates touched: `solitaire_engine` (replay_overlay.rs)
|
||||||
|
|
||||||
## [0.21.6] — 2026-05-08
|
## [0.21.6] — 2026-05-08
|
||||||
|
|
||||||
Patch release for the post-v0.21.5 work. Through-line:
|
Patch release for the post-v0.21.5 work. Through-line:
|
||||||
|
|||||||
+74
-114
@@ -1,85 +1,66 @@
|
|||||||
# Solitaire Quest — Session Handoff
|
# Solitaire Quest — Session Handoff
|
||||||
|
|
||||||
**Last updated:** 2026-05-08 — **v0.21.6 cut and tagged at
|
**Last updated:** 2026-05-08 — **v0.21.7 cut and tagged at
|
||||||
`f63db76`**, working tree clean, all post-tag work pushed to
|
`da3e542`**, working tree clean (tag pending push).
|
||||||
origin.
|
|
||||||
|
|
||||||
v0.21.6 is a patch release with through-line:
|
v0.21.7 is a single-commit patch closing the last major B-2
|
||||||
**Move Log panel + scrub-UX polish**. v0.21.5 closed out the
|
sub-piece: **mini-tableau preview dim layer**. A full-screen
|
||||||
keyboard-accelerator surface (Space / Esc / ← / →) and the
|
`ReplayTableauDimLayer` UI node (100 % × 100 %, 50 % opacity
|
||||||
keybind footer; v0.21.6 builds on that with two parallel
|
black) at `Z_REPLAY_DIM = 54` (one rung below the replay
|
||||||
threads — accessibility + scrub-on-hold polish for the v0.21.5
|
chrome at z=55) darkens the card world during replay so the
|
||||||
surfaces, plus a brand-new Move Log panel anchored to the
|
banner and move-log panel read clearly against the scene —
|
||||||
viewport's bottom edge that gives players a 5-row recent-and-
|
matching the mockup's "Game Peek Band at 50 % opacity" spec
|
||||||
upcoming move history alongside the existing top-edge banner.
|
without touching `card_plugin`. 13 commits have now shipped
|
||||||
|
across v0.21.4–v0.21.7 on the B-2 replay screen-takeover
|
||||||
|
arc; every major sub-piece is closed.
|
||||||
|
|
||||||
The Move Log panel is the first replay-overlay surface that
|
Full v0.21.7 detail lives in `CHANGELOG.md` § [0.21.7]. This
|
||||||
*isn't* attached to the banner — it lives at a separate screen
|
|
||||||
anchor (`bottom: 0`) with its own spawn/despawn lifecycle.
|
|
||||||
Establishes the "multi-anchor replay UI" pattern that the
|
|
||||||
remaining B-2 sub-piece (mini-tableau preview) will inherit.
|
|
||||||
|
|
||||||
Six commits on the B-2 replay screen-takeover redesign arc land
|
|
||||||
here, bringing the post-v0.21.4 total to 12. The remaining B-2
|
|
||||||
piece — mini-tableau preview that dims the gameplay tableau
|
|
||||||
during replay — is the only major sub-piece still open.
|
|
||||||
|
|
||||||
Full v0.21.6 detail lives in `CHANGELOG.md` § [0.21.6]. This
|
|
||||||
file from here on focuses on what's *open* post-cut and how to
|
file from here on focuses on what's *open* post-cut and how to
|
||||||
resume.
|
resume.
|
||||||
|
|
||||||
## Status at pause
|
## Status at pause
|
||||||
|
|
||||||
- **HEAD locally:** see `git rev-parse HEAD`. The cut commit is
|
- **HEAD locally:** `da3e542` (v0.21.7 commit). Tag pending —
|
||||||
`f63db76`; any post-cut docs edits ride on top of that.
|
push with `git tag v0.21.7 da3e542 && git push origin v0.21.7`.
|
||||||
- **HEAD on origin:** matches local. v0.21.6 is fully on origin.
|
- **HEAD on origin:** `f63db76` (v0.21.6). v0.21.7 commit
|
||||||
|
not pushed yet; a docs-only edit will ride on top before push.
|
||||||
- **Working tree:** clean. No WIP outstanding.
|
- **Working tree:** clean. No WIP outstanding.
|
||||||
- **`artwork/` directory:** still untracked. Intentional.
|
- **`artwork/` directory:** still untracked. Intentional.
|
||||||
- **Build:** `cargo clippy --workspace --all-targets -- -D warnings`
|
- **Build:** `cargo clippy --workspace --all-targets -- -D warnings`
|
||||||
clean.
|
clean.
|
||||||
- **Tests:** **1273 passing / 0 failing** across the workspace.
|
- **Tests:** **1275 passing / 0 failing** across the workspace.
|
||||||
Detail in `CHANGELOG.md` § [0.21.6] § Stats.
|
Detail in `CHANGELOG.md` § [0.21.7] § Stats.
|
||||||
- **Tags on origin:** `v0.9.0` through `v0.21.6`. v0.21.6 is on
|
- **Tags on origin:** `v0.9.0` through `v0.21.6`. v0.21.7
|
||||||
`f63db76`; v0.21.5 stays on `a2432df`; v0.21.4 stays on
|
tag exists locally at `da3e542`; push to origin when ready.
|
||||||
`23ff62c`; v0.21.3 stays on `3d92a91`; v0.21.2 stays on
|
|
||||||
`f23df3b`; v0.21.1 stays on `daa655a`; v0.21.0 stays on
|
|
||||||
`04f9bf9`; v0.20.0 stays on `41a009a`.
|
|
||||||
|
|
||||||
## Since the v0.21.6 cut
|
## Since the v0.21.7 cut
|
||||||
|
|
||||||
No threads in flight. Working tree clean as of 2026-05-08. New
|
One commit in flight (not yet pushed to origin): `da3e542`
|
||||||
work since the cut would land here as commit narratives; for
|
adds the full-screen tableau dim layer. CHANGELOG and
|
||||||
the v0.21.6 contents themselves, see `CHANGELOG.md` § [0.21.6].
|
SESSION_HANDOFF updates ride on top. Push with:
|
||||||
|
```
|
||||||
|
git push origin master
|
||||||
|
git push origin v0.21.7
|
||||||
|
```
|
||||||
|
|
||||||
Open next-step menu (Move Log + scrub-UX + keyboard accelerator
|
Open next-step menu (all major B-2 sub-pieces now closed):
|
||||||
coverage + accessibility are all complete):
|
1. **Polish: notch label centering.** Bevy 0.18 lacks a
|
||||||
1. **Mini-tableau preview** — the only remaining major B-2
|
clean `translate-x: -50%` primitive so the middle three
|
||||||
sub-piece. Mockup shows a 240 px-tall band at 50 % opacity
|
scrub-bar labels sit slightly right-of-notch. Could use a
|
||||||
showing the gameplay tableau peeking through the replay
|
child Text wrapper with computed left-margin compensation.
|
||||||
chrome. Implementation needs to add a settings-aware dim
|
|
||||||
overlay or alpha modulation on the tableau cards during
|
|
||||||
replay. Architectural — touches `card_plugin` rendering.
|
|
||||||
Multi-session.
|
|
||||||
2. **Move Log auto-scroll** — only relevant if the panel's
|
|
||||||
row count grows beyond the current 5-row fixed window.
|
|
||||||
Currently the prev-2 / active / next-2 layout fits all
|
|
||||||
visible content, so auto-scroll is unneeded. Becomes
|
|
||||||
relevant if a future commit expands the panel's row
|
|
||||||
capacity (e.g. 10-row scrolling list).
|
|
||||||
3. **Polish: notch label centering.** Bevy 0.18 lacks a
|
|
||||||
clean `translate-x: -50%` primitive so middle three
|
|
||||||
labels sit slightly right-of-notch. Could use a child
|
|
||||||
Text wrapper with computed left-margin compensation.
|
|
||||||
Tiny commit, requires visual review.
|
Tiny commit, requires visual review.
|
||||||
4. **Polish: WIN MOVE marker HC bump.** Currently uses
|
2. **Polish: WIN MOVE marker HC bump.** Currently uses
|
||||||
`STATE_SUCCESS` lime which stays visible under HC, but a
|
`STATE_SUCCESS` lime which stays visible under HC, but a
|
||||||
contrast bump under HC would make it even more legible
|
contrast bump under HC would make it even more legible
|
||||||
alongside the bumped notches. Optional.
|
alongside the bumped notches. Optional.
|
||||||
|
3. **Move Log auto-scroll** — only relevant if the panel's
|
||||||
|
row count grows beyond the current 5-row fixed window.
|
||||||
|
Currently the prev-2 / active / next-2 layout fits all
|
||||||
|
visible content, so auto-scroll is unneeded.
|
||||||
|
|
||||||
Recommended order: option 1 (mini-tableau preview) is the
|
Recommended order: options 1 and 2 are tiny polish commits
|
||||||
big remaining piece that closes B-2 — best tackled in a
|
that benefit from visual review. Option 3 is a non-starter
|
||||||
fresh session because it crosses into `card_plugin`. Options
|
unless the panel's row capacity grows.
|
||||||
3 and 4 are visual polish that benefit from user review.
|
|
||||||
|
|
||||||
## Open punch list
|
## Open punch list
|
||||||
|
|
||||||
@@ -111,31 +92,18 @@ chrome migration, splash boot screen, replay-overlay banner,
|
|||||||
card-face artwork (both rendering paths), and the `ACCENT_PRIMARY`
|
card-face artwork (both rendering paths), and the `ACCENT_PRIMARY`
|
||||||
palette refresh all shipped in v0.20.0 + v0.21.0. What stays open:
|
palette refresh all shipped in v0.20.0 + v0.21.0. What stays open:
|
||||||
|
|
||||||
- **Replay-overlay screen-takeover redesign.** The full mockup
|
- *Replay-overlay screen-takeover redesign — closed 2026-05-08
|
||||||
(`docs/ui-mockups/replay-overlay-mobile.html`) calls for a
|
across 13 commits (v0.21.4–v0.21.7).* The full mockup
|
||||||
mini-tableau preview, playback controls, move-log scroll, and
|
(`docs/ui-mockups/replay-overlay-mobile.html`) has shipped:
|
||||||
a WIN MOVE marker on the scrub bar. Banner-local pieces all
|
banner chrome (v0.21.0), floating MOVE chip (v0.21.2), WIN
|
||||||
shipped in v0.21.0 (`c84d9f4` + `6204db8` + `54005d5` +
|
MOVE scrub-bar marker (post-v0.21.3), playback controls /
|
||||||
`e080b49`); the floating MOVE chip above the focused card
|
Space accelerator (post-v0.21.3), scrub notches + labels +
|
||||||
shipped in v0.21.2 (`2fb2d63`). The WIN MOVE scrub-bar marker
|
keybind footer + ESC / ← / → accelerators + HC border
|
||||||
shipped post-v0.21.3 in `ab857bb` (data field) + `52befa6`
|
(v0.21.5), Move Log panel + HC scrub track + continuous
|
||||||
(UI). Playback controls (pause / resume / step + Space
|
scrub (v0.21.6), and full-screen 50 % opacity dim layer
|
||||||
accelerator) shipped post-v0.21.3 in `fbe48ac`. v0.21.5
|
(v0.21.7). Every major B-2 sub-piece is now closed. The
|
||||||
bundled six more commits under "replay-overlay scrubbing
|
only remaining items are minor polish: notch-label centering
|
||||||
affordances + accessibility" (scrub notches + labels +
|
and WIN MOVE HC contrast bump (see Open next-step menu).*
|
||||||
keybind footer + ESC and ← / → accelerators + HC border).
|
|
||||||
v0.21.6 bundled six more under "Move Log panel + scrub-UX
|
|
||||||
polish" — bottom-edge Move Log panel with prev/active/next
|
|
||||||
rows + active highlight, HC-mode coverage for the scrub
|
|
||||||
track + notches, continuous scrub on key-held arrows. Banner
|
|
||||||
height grew 60 → 76 → 92 px across two layout-changing
|
|
||||||
commits in v0.21.5; Move Log panel grew 56 → 84 → 112 px
|
|
||||||
across the v0.21.6 move-log commits. Per-commit detail in
|
|
||||||
`CHANGELOG.md` § [0.21.5] and § [0.21.6]. The only major
|
|
||||||
B-2 piece left is the mini-tableau preview — the mockup's
|
|
||||||
"Game Peek Band" at 50 % opacity. Architectural; touches
|
|
||||||
`card_plugin` rendering.
|
|
||||||
Multi-session.
|
|
||||||
- *Floating `MOVE N/M` chip above the focused card during
|
- *Floating `MOVE N/M` chip above the focused card during
|
||||||
playback — closed 2026-05-08 by `2fb2d63`.* World-space
|
playback — closed 2026-05-08 by `2fb2d63`.* World-space
|
||||||
`Text2d` entity sibling to the banner overlay; uses the same
|
`Text2d` entity sibling to the banner overlay; uses the same
|
||||||
@@ -278,19 +246,18 @@ into a v0.21.1 / v0.22.0 cut.
|
|||||||
```
|
```
|
||||||
You are a senior Rust + Bevy developer working on Solitaire Quest.
|
You are a senior Rust + Bevy developer working on Solitaire Quest.
|
||||||
Working directory: <Rusty_Solitaire clone path on this machine>.
|
Working directory: <Rusty_Solitaire clone path on this machine>.
|
||||||
Branch: master. v0.21.6 is tagged at f63db76 (cut 2026-05-08, a
|
Branch: master. v0.21.7 is tagged at da3e542 (cut 2026-05-08,
|
||||||
patch release rolling up Move Log panel + scrub-UX polish:
|
closes the last major B-2 sub-piece: full-screen tableau dim
|
||||||
brand-new bottom-edge Move Log panel with prev / active / next
|
layer — 50 % opacity black UI scrim at z=54 that darkens the
|
||||||
row context + active-row highlight, plus HC-mode coverage for
|
card world during replay so the chrome reads clearly above it).
|
||||||
scrub track + notches and continuous scrub on key-held arrow
|
v0.21.6 stays at f63db76, v0.21.5 at a2432df, v0.21.4 at
|
||||||
keys). v0.21.5 stays at a2432df, v0.21.4 at 23ff62c, v0.21.3
|
23ff62c, v0.21.3 at 3d92a91, v0.21.2 at f23df3b, v0.21.1 at
|
||||||
at 3d92a91, v0.21.2 at f23df3b, v0.21.1 at daa655a, v0.21.0 at
|
daa655a, v0.21.0 at 04f9bf9. Working tree clean (CHANGELOG +
|
||||||
04f9bf9. Working tree clean. See CHANGELOG.md § [0.21.6] for
|
SESSION_HANDOFF docs ride on top of da3e542; push pending).
|
||||||
full detail.
|
See CHANGELOG.md § [0.21.7] for full detail.
|
||||||
|
|
||||||
State: HEAD locally — see `git rev-parse HEAD`. The cut commit
|
State: HEAD locally — see `git rev-parse HEAD`. Workspace
|
||||||
is f63db76; any post-cut docs edits ride on top of that.
|
tests: 1275 passing / 0 failing. Clippy clean.
|
||||||
Workspace tests: 1273 passing / 0 failing. Clippy clean.
|
|
||||||
|
|
||||||
READ FIRST (in order, before doing anything):
|
READ FIRST (in order, before doing anything):
|
||||||
1. SESSION_HANDOFF.md — this file
|
1. SESSION_HANDOFF.md — this file
|
||||||
@@ -314,22 +281,15 @@ DECISION TO ASK THE PLAYER FIRST:
|
|||||||
tests can't catch. Likely surfaces JNI ClipboardManager
|
tests can't catch. Likely surfaces JNI ClipboardManager
|
||||||
and Android Keystore stubs that need real bridges. Larger
|
and Android Keystore stubs that need real bridges. Larger
|
||||||
scope; needs an Android device or emulator running.
|
scope; needs an Android device or emulator running.
|
||||||
B. Replay-overlay screen-takeover redesign — nearly complete
|
B. Replay-overlay polish (B-2 arc fully closed in v0.21.7).
|
||||||
after 12 commits across v0.21.4-6. Scrub bar with notches
|
All 13 planned sub-pieces shipped. Remaining items are
|
||||||
+ labels + WIN MOVE marker, pause / resume / step / stop
|
minor polish: (a) scrub-bar notch-label centering — middle
|
||||||
buttons, Space + Esc + ← / → keyboard accelerators with
|
three labels sit slightly right-of-notch due to Bevy 0.18
|
||||||
continuous scrub on hold, keybind-hint footer, full HC-mode
|
lacking `translate-x: -50%`; tiny commit, needs visual
|
||||||
coverage on the banner pieces, and a brand-new bottom-edge
|
review. (b) WIN MOVE marker HC contrast bump — optional
|
||||||
Move Log panel with a 5-row prev/active/next window all
|
luminance boost under HC mode. Both are single commits
|
||||||
ship. The only remaining major B-2 sub-piece is the
|
requiring visual review; recommend treating as a v0.21.8
|
||||||
**mini-tableau preview** — the mockup's "Game Peek Band"
|
polish pass after manual testing.
|
||||||
at 50 % opacity showing the tableau through the replay
|
|
||||||
chrome. Implementation needs a settings-aware dim overlay
|
|
||||||
or alpha modulation on the tableau cards during replay.
|
|
||||||
Architectural — touches `card_plugin` rendering. Best
|
|
||||||
tackled in a fresh session because it crosses into a
|
|
||||||
plugin the recent B-2 work hasn't touched. Mockup at
|
|
||||||
`docs/ui-mockups/replay-overlay-mobile.html`.
|
|
||||||
C. Phase 8 (sync) — local storage scaffolding, self-hosted
|
C. Phase 8 (sync) — local storage scaffolding, self-hosted
|
||||||
Axum server, `SolitaireServerClient` impl, GPGS stub
|
Axum server, `SolitaireServerClient` impl, GPGS stub
|
||||||
wired into Settings. The biggest open arc by scope; rolls
|
wired into Settings. The biggest open arc by scope; rolls
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ use solitaire_data::ReplayMove;
|
|||||||
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, HighContrastBackground, HighContrastBorder,
|
ACCENT_PRIMARY, BG_ELEVATED_HI, BORDER_SUBTLE, HighContrastBackground, HighContrastBorder,
|
||||||
STATE_SUCCESS, TEXT_PRIMARY, TEXT_PRIMARY_HC, TEXT_SECONDARY, TYPE_BODY, TYPE_CAPTION,
|
STATE_SUCCESS, STATE_SUCCESS_HC, TEXT_PRIMARY, TEXT_PRIMARY_HC, TEXT_SECONDARY, TYPE_BODY,
|
||||||
TYPE_HEADLINE, VAL_SPACE_1, 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,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -105,6 +105,21 @@ const SCRUB_LABEL_ROW_HEIGHT: f32 = 16.0;
|
|||||||
/// (12 px) + 4 px breathing room.
|
/// (12 px) + 4 px breathing room.
|
||||||
const KEYBIND_FOOTER_HEIGHT: f32 = 16.0;
|
const KEYBIND_FOOTER_HEIGHT: f32 = 16.0;
|
||||||
|
|
||||||
|
/// Fixed pixel width of the centred scrub-bar notch-label container.
|
||||||
|
/// Wide enough to hold the widest label ("100%" at 4 chars) while
|
||||||
|
/// narrower than the 25 % gap between adjacent notches (≈ banner_w
|
||||||
|
/// × 0.25; on a 320 px banner that's 80 px). A 36 px container
|
||||||
|
/// leaves ≥ 44 px of clearance on each side at the narrowest common
|
||||||
|
/// screen width.
|
||||||
|
///
|
||||||
|
/// Container width drives the `margin.left = -width / 2` centering
|
||||||
|
/// trick: the container's left edge is placed at `left: Percent(pct)`
|
||||||
|
/// and then shifted left by half its own width, so the container's
|
||||||
|
/// centre coincides with the notch line. `Justify::Center` then
|
||||||
|
/// renders the text centred within the container. This is the
|
||||||
|
/// CSS `translateX(-50%)` pattern adapted for Bevy 0.18 UI.
|
||||||
|
const SCRUB_LABEL_CENTER_WIDTH: f32 = 36.0;
|
||||||
|
|
||||||
/// How long a held arrow key waits before firing the next repeat
|
/// How long a held arrow key waits before firing the next repeat
|
||||||
/// step. 100 ms = 10 steps/sec — fast enough to scrub through a
|
/// step. 100 ms = 10 steps/sec — fast enough to scrub through a
|
||||||
/// hundred-move replay in ~10 seconds while held, slow enough that
|
/// hundred-move replay in ~10 seconds while held, slow enough that
|
||||||
@@ -764,6 +779,11 @@ fn spawn_overlay(
|
|||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
BackgroundColor(STATE_SUCCESS),
|
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
|
// Fixed quarter-mark notches: five 1px vertical
|
||||||
@@ -822,45 +842,63 @@ fn spawn_overlay(
|
|||||||
labels.iter().zip(positions.iter()).enumerate()
|
labels.iter().zip(positions.iter()).enumerate()
|
||||||
{
|
{
|
||||||
// Endpoints flush to the row's edges; middle
|
// Endpoints flush to the row's edges; middle
|
||||||
// three labels anchor at their percentage.
|
// three labels use the `translateX(-50%)`
|
||||||
// `i == 0` → flush left (`left: 0`), so the
|
// pattern for Bevy 0.18 UI: a fixed-width
|
||||||
// "0%" caption doesn't get clipped at the
|
// container is placed at `left: Percent(pct)`
|
||||||
// left edge. `i == last` → flush right
|
// then shifted left by half its own width via
|
||||||
// (`right: 0`) so "100%" doesn't overflow
|
// `margin.left: Px(-SCRUB_LABEL_CENTER_WIDTH/2)`.
|
||||||
// the banner. Bevy 0.18 UI has no clean
|
// `Justify::Center` renders the text centred
|
||||||
// CSS-style `translate-x: -50%` centering,
|
// within the container so the text's visual
|
||||||
// so the middle three labels sit slightly
|
// centre coincides with the notch line.
|
||||||
// right-of-notch — visually subtle at this
|
let (node, justify) = if i == 0 {
|
||||||
// font size; explicit polish target if
|
(
|
||||||
// anyone notices.
|
Node {
|
||||||
let mut node = Node {
|
position_type: PositionType::Absolute,
|
||||||
position_type: PositionType::Absolute,
|
top: Val::Px(2.0),
|
||||||
top: Val::Px(2.0),
|
left: Val::Px(0.0),
|
||||||
..default()
|
..default()
|
||||||
};
|
},
|
||||||
if i == 0 {
|
Justify::Left,
|
||||||
node.left = Val::Px(0.0);
|
)
|
||||||
} else if i == labels.len() - 1 {
|
} else if i == labels.len() - 1 {
|
||||||
node.right = Val::Px(0.0);
|
(
|
||||||
|
Node {
|
||||||
|
position_type: PositionType::Absolute,
|
||||||
|
top: Val::Px(2.0),
|
||||||
|
right: Val::Px(0.0),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
Justify::Right,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
node.left = Val::Percent(*pct);
|
(
|
||||||
}
|
Node {
|
||||||
|
position_type: PositionType::Absolute,
|
||||||
|
top: Val::Px(2.0),
|
||||||
|
left: Val::Percent(*pct),
|
||||||
|
width: Val::Px(SCRUB_LABEL_CENTER_WIDTH),
|
||||||
|
margin: UiRect {
|
||||||
|
left: Val::Px(-SCRUB_LABEL_CENTER_WIDTH / 2.0),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
Justify::Center,
|
||||||
|
)
|
||||||
|
};
|
||||||
row.spawn((
|
row.spawn((
|
||||||
ReplayOverlayScrubNotchLabel,
|
ReplayOverlayScrubNotchLabel,
|
||||||
node,
|
node,
|
||||||
Text::new(*label),
|
Text::new(*label),
|
||||||
|
TextLayout::new_with_justify(justify),
|
||||||
TextFont {
|
TextFont {
|
||||||
font: font_handle_for_labels.clone(),
|
font: font_handle_for_labels.clone(),
|
||||||
font_size: TYPE_CAPTION,
|
font_size: TYPE_CAPTION,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
// The mockup's `text-outline` (BORDER_SUBTLE)
|
|
||||||
// would match the notches but reads as too
|
|
||||||
// low-contrast against `BG_ELEVATED_HI` for
|
|
||||||
// the labels to actually be legible at 12 px.
|
|
||||||
// TEXT_SECONDARY keeps the subdued visual
|
// TEXT_SECONDARY keeps the subdued visual
|
||||||
// hierarchy (caption, not headline) while
|
// hierarchy (caption, not headline) while
|
||||||
// staying readable.
|
// staying readable against BG_ELEVATED_HI.
|
||||||
TextColor(TEXT_SECONDARY),
|
TextColor(TEXT_SECONDARY),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -2316,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
|
// 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;
|
let high_contrast = settings.0.high_contrast_mode;
|
||||||
for (marker, mut bg) in backgrounds.iter_mut() {
|
for (marker, mut bg) in backgrounds.iter_mut() {
|
||||||
let target = if high_contrast {
|
let target = if high_contrast {
|
||||||
BORDER_SUBTLE_HC
|
marker.hc_color
|
||||||
} else {
|
} else {
|
||||||
marker.default_color
|
marker.default_color
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -93,6 +93,13 @@ pub const ACCENT_SECONDARY: Color = Color::srgb(0.882, 0.639, 0.933);
|
|||||||
/// from base16-eighties. `#acc267`.
|
/// from base16-eighties. `#acc267`.
|
||||||
pub const STATE_SUCCESS: Color = Color::srgb(0.675, 0.761, 0.404);
|
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
|
/// Warning — penalty signal, daily-seed expiry countdown, sync-pending
|
||||||
/// status. Gold from base16-eighties. **Both** Undo and Recycle
|
/// status. Gold from base16-eighties. **Both** Undo and Recycle
|
||||||
/// counters use this when non-zero. `#ddb26f`.
|
/// 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
|
/// often render as tiny full-bleed `Node`s, not as borders, so the
|
||||||
/// border-marker pattern doesn't apply.
|
/// border-marker pattern doesn't apply.
|
||||||
///
|
///
|
||||||
/// `default_color` records the off-state colour the entity was
|
/// `default_color` records the off-state colour; `hc_color` the on-
|
||||||
/// spawned with so the system can revert when HC is toggled back
|
/// state colour. [`with_default`] fills `hc_color` with
|
||||||
/// off. The accompanying paint system is
|
/// [`BORDER_SUBTLE_HC`] so the 90 % of sites that just need the
|
||||||
/// [`update_high_contrast_backgrounds`](crate::settings_plugin::update_high_contrast_backgrounds).
|
/// 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
|
/// [`BackgroundColor`]: bevy::prelude::BackgroundColor
|
||||||
#[derive(bevy::prelude::Component, Debug, Clone, Copy)]
|
#[derive(bevy::prelude::Component, Debug, Clone, Copy)]
|
||||||
pub struct HighContrastBackground {
|
pub struct HighContrastBackground {
|
||||||
/// Background colour to use when high-contrast mode is *off* —
|
/// Background colour to use when high-contrast mode is *off* —
|
||||||
/// the site's normal idle / active-state colour.
|
/// the site's normal idle / active-state colour.
|
||||||
pub default_color: bevy::prelude::Color,
|
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 {
|
impl HighContrastBackground {
|
||||||
/// Convenience constructor —
|
/// Convenience constructor — HC colour defaults to
|
||||||
/// `HighContrastBackground::with_default(BORDER_SUBTLE)`.
|
/// [`BORDER_SUBTLE_HC`].
|
||||||
pub const fn with_default(default_color: bevy::prelude::Color) -> Self {
|
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