feat(engine): Home picker — 2-column picture tiles with Unicode glyphs
Phase B step 2 of the MSSC-inspired Home rework. Mode cards become a wrapping 2-up grid with a centred Unicode-glyph centrepiece per tile, standing in for real per-mode artwork until that lands. - HomeMode::glyph() returns the placeholder codepoint for each mode: ♣ Classic, calendar Daily, cherry-blossom Zen, lightning Challenge, stopwatch TimeAttack. Cherry-blossom is used over lotus-position because the latter renders inconsistently across desktop fonts. - The mode-card loop is wrapped in a FlexWrap::Wrap row container. Tiles set `width: 48%` + `min_height: 180px`; the 5-mode grid wraps to a third row of one tile, mirroring the half-cell asymmetry in MSSC's screenshot. - The glyph paints in ACCENT_PRIMARY when the mode is unlocked and TEXT_DISABLED when locked, so the gate reads at a glance. - When real art lands, swap the Text node for an Image node — the rest of the tile layout, focus order, click handling, and chip rendering are unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,7 +39,7 @@ use crate::ui_modal::{
|
||||
use crate::ui_theme::{
|
||||
ACCENT_PRIMARY, BG_ELEVATED, BG_ELEVATED_HI, BORDER_STRONG, BORDER_SUBTLE, RADIUS_MD,
|
||||
STATE_INFO, TEXT_DISABLED, TEXT_PRIMARY, TEXT_SECONDARY, TYPE_BODY, TYPE_BODY_LG,
|
||||
TYPE_CAPTION, VAL_SPACE_1, VAL_SPACE_2, VAL_SPACE_3, Z_MODAL_PANEL,
|
||||
TYPE_CAPTION, TYPE_DISPLAY, VAL_SPACE_1, VAL_SPACE_2, VAL_SPACE_3, Z_MODAL_PANEL,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -112,6 +112,27 @@ impl HomeMode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Unicode glyph rendered as the picture-tile centrepiece. Stand-in
|
||||
/// for real per-mode artwork — chosen for one-glyph-tells-the-mode
|
||||
/// readability rather than visual fidelity. Swap to `Image` nodes
|
||||
/// when art lands; the rest of the tile layout doesn't change.
|
||||
fn glyph(self) -> &'static str {
|
||||
match self {
|
||||
// Black club is the densest card-suit glyph at small sizes.
|
||||
HomeMode::Classic => "\u{2663}",
|
||||
// Calendar emoji — matches the date callout below.
|
||||
HomeMode::Daily => "\u{1F4C5}",
|
||||
// Lotus flower stands in for the lotus-position emoji
|
||||
// because the latter renders inconsistently across
|
||||
// platforms; the flower is a single codepoint.
|
||||
HomeMode::Zen => "\u{1F338}",
|
||||
// High-voltage / lightning bolt for the hardest mode.
|
||||
HomeMode::Challenge => "\u{26A1}",
|
||||
// Stopwatch matches the timer concept of Time Attack.
|
||||
HomeMode::TimeAttack => "\u{23F1}",
|
||||
}
|
||||
}
|
||||
|
||||
/// The keyboard accelerator that dispatches the same launch event,
|
||||
/// shown in a small chip on the card.
|
||||
fn hotkey(self) -> &'static str {
|
||||
@@ -668,15 +689,31 @@ fn spawn_home_screen(commands: &mut Commands, ctx: HomeContext<'_>) {
|
||||
spawn_home_header_chips(card, &ctx);
|
||||
spawn_draw_mode_row(card, &ctx);
|
||||
|
||||
for mode in [
|
||||
HomeMode::Classic,
|
||||
HomeMode::Daily,
|
||||
HomeMode::Zen,
|
||||
HomeMode::Challenge,
|
||||
HomeMode::TimeAttack,
|
||||
] {
|
||||
spawn_mode_card(card, mode, &ctx);
|
||||
}
|
||||
// Mode tiles in a wrapping 2-column grid. Each tile takes 48%
|
||||
// of the row so column_gap fits comfortably; the 5 modes wrap
|
||||
// to a third row of one tile, which we leave left-aligned —
|
||||
// the asymmetry matches MSSC's "Daily Challenges / Today's
|
||||
// Event" half-cell on the right of their grid and keeps the
|
||||
// visual rhythm.
|
||||
card.spawn(Node {
|
||||
flex_direction: FlexDirection::Row,
|
||||
flex_wrap: FlexWrap::Wrap,
|
||||
row_gap: VAL_SPACE_3,
|
||||
column_gap: VAL_SPACE_3,
|
||||
width: Val::Percent(100.0),
|
||||
..default()
|
||||
})
|
||||
.with_children(|grid| {
|
||||
for mode in [
|
||||
HomeMode::Classic,
|
||||
HomeMode::Daily,
|
||||
HomeMode::Zen,
|
||||
HomeMode::Challenge,
|
||||
HomeMode::TimeAttack,
|
||||
] {
|
||||
spawn_mode_card(grid, mode, &ctx);
|
||||
}
|
||||
});
|
||||
|
||||
spawn_modal_actions(card, |actions| {
|
||||
spawn_modal_button(
|
||||
@@ -964,10 +1001,17 @@ fn spawn_mode_card(
|
||||
..default()
|
||||
};
|
||||
let font_chip = TextFont {
|
||||
font: font_handle,
|
||||
font: font_handle.clone(),
|
||||
font_size: TYPE_CAPTION,
|
||||
..default()
|
||||
};
|
||||
// Glyph rendered at display size — Unicode emoji standing in for
|
||||
// the per-mode artwork. Centred at the top of the tile.
|
||||
let font_glyph = TextFont {
|
||||
font: font_handle,
|
||||
font_size: TYPE_DISPLAY,
|
||||
..default()
|
||||
};
|
||||
|
||||
// Locked cards mute their text to communicate the disabled state at
|
||||
// a glance; the explicit "Unlocks at level N" caption underneath
|
||||
@@ -975,6 +1019,7 @@ fn spawn_mode_card(
|
||||
let title_color = if unlocked { TEXT_PRIMARY } else { TEXT_DISABLED };
|
||||
let desc_color = if unlocked { TEXT_SECONDARY } else { TEXT_DISABLED };
|
||||
let border_color = if unlocked { BORDER_SUBTLE } else { BORDER_STRONG };
|
||||
let glyph_color = if unlocked { ACCENT_PRIMARY } else { TEXT_DISABLED };
|
||||
|
||||
parent
|
||||
.spawn((
|
||||
@@ -985,9 +1030,13 @@ fn spawn_mode_card(
|
||||
Button,
|
||||
Node {
|
||||
flex_direction: FlexDirection::Column,
|
||||
row_gap: VAL_SPACE_1,
|
||||
row_gap: VAL_SPACE_2,
|
||||
padding: UiRect::all(VAL_SPACE_3),
|
||||
width: Val::Percent(100.0),
|
||||
// 48% per tile + the row's column_gap = a clean 2-up
|
||||
// grid that wraps to a single tile on the third row.
|
||||
width: Val::Percent(48.0),
|
||||
min_height: Val::Px(180.0),
|
||||
align_items: AlignItems::Center,
|
||||
border: UiRect::all(Val::Px(1.0)),
|
||||
border_radius: BorderRadius::all(Val::Px(RADIUS_MD)),
|
||||
..default()
|
||||
@@ -996,12 +1045,20 @@ fn spawn_mode_card(
|
||||
BorderColor::all(border_color),
|
||||
))
|
||||
.with_children(|c| {
|
||||
// Centerpiece glyph — placeholder for real per-mode art.
|
||||
c.spawn((
|
||||
Text::new(mode.glyph().to_string()),
|
||||
font_glyph.clone(),
|
||||
TextColor(glyph_color),
|
||||
));
|
||||
|
||||
// Title row — title text on the left, hotkey chip on the right.
|
||||
c.spawn(Node {
|
||||
flex_direction: FlexDirection::Row,
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::SpaceBetween,
|
||||
column_gap: VAL_SPACE_3,
|
||||
width: Val::Percent(100.0),
|
||||
..default()
|
||||
})
|
||||
.with_children(|row| {
|
||||
|
||||
Reference in New Issue
Block a user