diff --git a/docs/android/PLAYABILITY_TODO.md b/docs/android/PLAYABILITY_TODO.md index 2b8a22c..ecab1b9 100644 --- a/docs/android/PLAYABILITY_TODO.md +++ b/docs/android/PLAYABILITY_TODO.md @@ -36,9 +36,14 @@ rewrites required. change-detection fix-up system re-applies `base_top + insets.top` whenever the resource updates. Bottom inset is captured but not yet consumed (waits for bottom-anchored UI). -- [ ] **Mobile HUD layout.** Wrap to two rows, drop redundant text, or - move secondary actions (Help, Modes) into a hamburger / drawer. - Current single-row layout requires desktop width. +- [x] **Mobile HUD layout.** *Closed 2026-05-10.* Both the left HUD + column and the right action button row are now capped at + `max_width: 50 %` and the button row + tier-row child Nodes carry + `flex_wrap: Wrap`. On a 360 dp viewport the 6-button row breaks + to multiple lines (right-justified) and the tier rows wrap + individually instead of overflowing into the action column. On + desktop (≥ 1280 px) the 50 % cap is wider than any natural row + width so the existing single-line layout is unchanged. - [x] **Card-back asset not rendering.** *Closed 2026-05-10 by `fcc7337`.* `AssetPlugin::file_path = "../assets"` was set unconditionally to fix the desktop `cargo run -p solitaire_app` diff --git a/solitaire_engine/src/hud_plugin.rs b/solitaire_engine/src/hud_plugin.rs index dfcdf43..cbb9916 100644 --- a/solitaire_engine/src/hud_plugin.rs +++ b/solitaire_engine/src/hud_plugin.rs @@ -444,6 +444,16 @@ fn spawn_hud( let row_node = || Node { flex_direction: FlexDirection::Row, column_gap: VAL_SPACE_3, + // On a narrow viewport the four tier rows (Score/Moves/Timer, + // Mode/Challenge/Draw-cycle/Won-previously, Undos/Recycles/ + // Auto-complete, selection chip) can collectively be wider than + // the available space and overflow into the action-button column + // on the right. `flex_wrap: Wrap` lets each tier soft-wrap onto + // a second line; on a desktop window the rows stay single-line + // because the parent column has no width cap and the row never + // exceeds the natural line width. + flex_wrap: FlexWrap::Wrap, + row_gap: VAL_SPACE_1, align_items: AlignItems::Baseline, ..default() }; @@ -455,6 +465,14 @@ fn spawn_hud( left: VAL_SPACE_3, top: Val::Px(SPACE_2 + top_inset), flex_direction: FlexDirection::Column, + // Cap the column at 50% of viewport so on narrow + // (mobile) widths the inner tier rows have a bounded + // width to wrap against, and the column can't bleed + // into the right-anchored action button row (also + // capped at 50%). On desktop 50% of 1920 = 960 px, + // wider than any tier row's natural width, so the + // visible layout is unaffected. + max_width: Val::Percent(50.0), row_gap: VAL_SPACE_1, ..default() }, @@ -603,7 +621,21 @@ fn spawn_action_buttons( right: VAL_SPACE_3, top: Val::Px(SPACE_2 + top_inset), flex_direction: FlexDirection::Row, + // 6 buttons total ~510 px wide; on a desktop window + // (typically >= 1280 px) `max_width: 50%` is >= 640 px + // and the row stays a single line. On a 360 dp phone + // 50% is 180 px and the row wraps to two-three lines — + // which keeps the buttons out of the left HUD column's + // horizontal range and prevents the off-screen-left + // clipping seen in the v0.22.3 hardware screenshot. + max_width: Val::Percent(50.0), + flex_wrap: FlexWrap::Wrap, + // When the row wraps, buttons pack to the *end* of each + // line so the row stays visually right-aligned (matches + // the `right: VAL_SPACE_3` anchor). + justify_content: JustifyContent::FlexEnd, column_gap: VAL_SPACE_2, + row_gap: VAL_SPACE_2, align_items: AlignItems::Center, ..default() },