fix(android): wire FiraMono to stock-empty label, strip raw safe-area px from HUD spawns, replace tofu chevrons
CR-1: apply_stock_empty_indicator now receives a Handle<Font> from FontResource
so the ↺ label uses FiraMono (Arrows block) instead of the default font.
All three callers (startup, state-change, window-resize) updated.
CR-4: spawn_hud_band, spawn_hud, spawn_hud_avatar, spawn_action_buttons no
longer add SafeAreaInsets physical-pixel values to initial Val::Px offsets.
SafeAreaAnchoredTop/Bottom systems already divide by scale_factor and apply
the correct logical-pixel offset when insets arrive; the initial spawn value
is always 0.0 at Startup on Android anyway. Removed now-unused SafeAreaInsets
import and parameter from all four Startup systems.
H-9: Difficulty section chevrons ▶/▼ (U+25BA/U+25BC, Geometric Shapes — not in
FiraMono) replaced with ASCII ">"/"v" which render correctly on Android.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1598,6 +1598,7 @@ fn apply_stock_empty_indicator<F: bevy::ecs::query::QueryFilter>(
|
||||
pile_markers: &mut Query<(Entity, &PileMarker, &mut Sprite), F>,
|
||||
label_children: &Query<(Entity, &ChildOf), With<StockEmptyLabel>>,
|
||||
layout: &Layout,
|
||||
font: Handle<Font>,
|
||||
) {
|
||||
let stock_empty = game
|
||||
.piles
|
||||
@@ -1623,7 +1624,7 @@ fn apply_stock_empty_indicator<F: bevy::ecs::query::QueryFilter>(
|
||||
b.spawn((
|
||||
StockEmptyLabel,
|
||||
Text2d::new("↺"),
|
||||
TextFont { font_size, ..default() },
|
||||
TextFont { font: font.clone(), font_size, ..default() },
|
||||
TextColor(TEXT_PRIMARY.with_alpha(0.7)),
|
||||
Transform::from_xyz(0.0, 0.0, 0.1),
|
||||
));
|
||||
@@ -1649,16 +1650,19 @@ fn update_stock_empty_indicator_startup(
|
||||
mut commands: Commands,
|
||||
game: Res<GameStateResource>,
|
||||
layout: Option<Res<LayoutResource>>,
|
||||
font_res: Option<Res<FontResource>>,
|
||||
mut pile_markers: Query<(Entity, &PileMarker, &mut Sprite)>,
|
||||
label_children: Query<(Entity, &ChildOf), With<StockEmptyLabel>>,
|
||||
) {
|
||||
let Some(layout) = layout else { return };
|
||||
let font = font_res.as_ref().map(|f| f.0.clone()).unwrap_or_default();
|
||||
apply_stock_empty_indicator(
|
||||
&mut commands,
|
||||
&game.0,
|
||||
&mut pile_markers,
|
||||
&label_children,
|
||||
&layout.0,
|
||||
font,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1669,6 +1673,7 @@ fn update_stock_empty_indicator(
|
||||
mut commands: Commands,
|
||||
game: Res<GameStateResource>,
|
||||
layout: Option<Res<LayoutResource>>,
|
||||
font_res: Option<Res<FontResource>>,
|
||||
mut pile_markers: Query<(Entity, &PileMarker, &mut Sprite)>,
|
||||
label_children: Query<(Entity, &ChildOf), With<StockEmptyLabel>>,
|
||||
) {
|
||||
@@ -1676,12 +1681,14 @@ fn update_stock_empty_indicator(
|
||||
return;
|
||||
}
|
||||
let Some(layout) = layout else { return };
|
||||
let font = font_res.as_ref().map(|f| f.0.clone()).unwrap_or_default();
|
||||
apply_stock_empty_indicator(
|
||||
&mut commands,
|
||||
&game.0,
|
||||
&mut pile_markers,
|
||||
&label_children,
|
||||
&layout.0,
|
||||
font,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1892,6 +1899,7 @@ fn snap_cards_on_window_resize(
|
||||
game: Option<Res<GameStateResource>>,
|
||||
layout: Option<Res<LayoutResource>>,
|
||||
card_images: Option<Res<CardImageSet>>,
|
||||
font_res: Option<Res<FontResource>>,
|
||||
entities: Query<
|
||||
(Entity, &CardEntity, &mut Sprite, &mut Transform),
|
||||
(Without<CardLabel>, Without<CardShadow>, Without<CardBackFrame>),
|
||||
@@ -1940,12 +1948,14 @@ fn snap_cards_on_window_resize(
|
||||
frame_query,
|
||||
);
|
||||
|
||||
let font = font_res.as_ref().map(|f| f.0.clone()).unwrap_or_default();
|
||||
apply_stock_empty_indicator(
|
||||
&mut commands,
|
||||
&game.0,
|
||||
&mut pile_markers,
|
||||
&label_children,
|
||||
&layout.0,
|
||||
font,
|
||||
);
|
||||
|
||||
throttle.last_applied_secs = now;
|
||||
|
||||
@@ -1103,7 +1103,7 @@ fn spawn_difficulty_section(parent: &mut ChildSpawnerCommands, ctx: &HomeContext
|
||||
let font_label = TextFont { font: font_handle.clone(), font_size: TYPE_BODY, ..default() };
|
||||
let font_chip = TextFont { font: font_handle, font_size: TYPE_CAPTION, ..default() };
|
||||
|
||||
let chevron = if ctx.difficulty_expanded { "▼" } else { "▶" };
|
||||
let chevron = if ctx.difficulty_expanded { "v" } else { ">" };
|
||||
|
||||
// Header row — click to toggle expand/collapse.
|
||||
parent
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::daily_challenge_plugin::DailyChallengeResource;
|
||||
use crate::progress_plugin::ProgressResource;
|
||||
use crate::settings_plugin::SettingsResource;
|
||||
use crate::layout::HUD_BAND_HEIGHT;
|
||||
use crate::safe_area::{SafeAreaAnchoredBottom, SafeAreaAnchoredTop, SafeAreaInsets};
|
||||
use crate::safe_area::{SafeAreaAnchoredBottom, SafeAreaAnchoredTop};
|
||||
use crate::ui_theme::SPACE_2;
|
||||
use crate::ui_theme::{
|
||||
scaled_duration, ACCENT_PRIMARY, ACCENT_SECONDARY, BG_ELEVATED, BG_ELEVATED_HI,
|
||||
@@ -486,13 +486,12 @@ impl Plugin for HudPlugin {
|
||||
/// The entity carries no `BackgroundColor` — the green felt shows through.
|
||||
/// A slim grey background is handled by each content section individually
|
||||
/// (the bottom action bar has its own `BG_HUD_BAND` background).
|
||||
fn spawn_hud_band(insets: Option<Res<SafeAreaInsets>>, mut commands: Commands) {
|
||||
fn spawn_hud_band(mut commands: Commands) {
|
||||
const BASE_TOP: f32 = 0.0;
|
||||
let top_inset = insets.as_deref().copied().unwrap_or_default().top;
|
||||
commands.spawn((
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
top: Val::Px(BASE_TOP + top_inset),
|
||||
top: Val::Px(BASE_TOP),
|
||||
left: Val::Px(0.0),
|
||||
width: Val::Percent(100.0),
|
||||
height: Val::Px(HUD_BAND_HEIGHT),
|
||||
@@ -525,10 +524,8 @@ fn spawn_hud_band(insets: Option<Res<SafeAreaInsets>>, mut commands: Commands) {
|
||||
/// make Score the visual protagonist.
|
||||
fn spawn_hud(
|
||||
font_res: Option<Res<FontResource>>,
|
||||
insets: Option<Res<SafeAreaInsets>>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
let top_inset = insets.as_deref().copied().unwrap_or_default().top;
|
||||
let font_handle = font_res.as_ref().map(|f| f.0.clone()).unwrap_or_default();
|
||||
let font_score = TextFont {
|
||||
font: font_handle.clone(),
|
||||
@@ -568,7 +565,7 @@ fn spawn_hud(
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
left: VAL_SPACE_3,
|
||||
top: Val::Px(SPACE_2 + top_inset),
|
||||
top: Val::Px(SPACE_2),
|
||||
flex_direction: FlexDirection::Column,
|
||||
// Cap the column at 50% of viewport so on narrow
|
||||
// (mobile) widths the inner tier rows have a bounded
|
||||
@@ -701,13 +698,11 @@ fn spawn_hud(
|
||||
/// `AvatarResource` or `SettingsResource` later changes.
|
||||
fn spawn_hud_avatar(
|
||||
font_res: Option<Res<FontResource>>,
|
||||
insets: Option<Res<SafeAreaInsets>>,
|
||||
avatar: Option<Res<AvatarResource>>,
|
||||
settings: Option<Res<SettingsResource>>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
const SIZE: f32 = 32.0;
|
||||
let top_inset = insets.as_deref().copied().unwrap_or_default().top;
|
||||
let id = commands
|
||||
.spawn((
|
||||
HudAvatar,
|
||||
@@ -715,7 +710,7 @@ fn spawn_hud_avatar(
|
||||
Tooltip::new("Your profile — tap to open."),
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
top: Val::Px(SPACE_2 + top_inset),
|
||||
top: Val::Px(SPACE_2),
|
||||
right: VAL_SPACE_3,
|
||||
width: Val::Px(SIZE),
|
||||
height: Val::Px(SIZE),
|
||||
@@ -834,10 +829,8 @@ fn handle_avatar_button(
|
||||
/// on its own visual edge.
|
||||
fn spawn_action_buttons(
|
||||
font_res: Option<Res<FontResource>>,
|
||||
insets: Option<Res<SafeAreaInsets>>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
let bottom_inset = insets.as_deref().copied().unwrap_or_default().bottom;
|
||||
let font = TextFont {
|
||||
font: font_res.as_ref().map(|f| f.0.clone()).unwrap_or_default(),
|
||||
font_size: TYPE_BODY,
|
||||
@@ -873,13 +866,13 @@ fn spawn_action_buttons(
|
||||
);
|
||||
|
||||
// Bottom bar: full-width, centered, sits above the gesture-navigation zone.
|
||||
// `bottom` is set to `bottom_inset` initially; `SafeAreaAnchoredBottom` keeps
|
||||
// it correct as Android insets arrive in later frames.
|
||||
// `SafeAreaAnchoredBottom` applies the correct logical-pixel inset once
|
||||
// Android reports it (frames 1-3); initial value is 0.0.
|
||||
commands
|
||||
.spawn((
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
bottom: Val::Px(bottom_inset),
|
||||
bottom: Val::Px(0.0),
|
||||
left: Val::Px(0.0),
|
||||
width: Val::Percent(100.0),
|
||||
flex_direction: FlexDirection::Row,
|
||||
|
||||
Reference in New Issue
Block a user