Merge pull request 'fix(engine): resize pile-marker outline and watermark children on relayout' (#131) from fix/pile-marker-child-resize into master
Build and Deploy / build-and-push (push) Successful in 2m19s
Web WASM Rebuild / rebuild (push) Successful in 8m12s
Android Release / build-apk (push) Successful in 5m6s

This commit was merged in pull request #131.
This commit is contained in:
2026-07-06 21:28:23 +00:00
+86 -2
View File
@@ -362,7 +362,15 @@ fn on_window_resized(
(&mut Sprite, &mut Transform),
(With<TableBackground>, Without<PileMarker>),
>,
mut markers: Query<(&PileMarker, &mut Sprite, &mut Transform), Without<TableBackground>>,
mut markers: Query<
(&PileMarker, &mut Sprite, &mut Transform, Option<&Children>),
Without<TableBackground>,
>,
mut marker_outlines: Query<
&mut Sprite,
(Without<PileMarker>, Without<TableBackground>, Without<Text2d>),
>,
mut marker_labels: Query<&mut TextFont, With<Text2d>>,
) {
let Some(ev) = events.read().last() else {
return;
@@ -373,6 +381,14 @@ fn on_window_resized(
let safe_area_top = insets.top / scale;
let safe_area_bottom = insets.bottom / scale;
let hud_visible = hud_vis.as_deref().copied().unwrap_or_default() == HudVisibility::Visible;
// Android surface sizes after fold/unfold are unreliable (winit does not
// forward every content-rect change), so make each relayout observable
// in logcat — this is the primary evidence channel for foldable bugs.
#[cfg(target_os = "android")]
info!(
"layout: resize to {:.0}x{:.0} (insets top={:.0} bottom={:.0}, hud={hud_visible})",
window_size.x, window_size.y, insets.top, insets.bottom
);
let mut new_layout = compute_layout(window_size, safe_area_top, safe_area_bottom, hud_visible);
// compute_layout sizes the fan for a worst-case column; refine it to the
@@ -394,11 +410,27 @@ fn on_window_resized(
transform.translation.y = 0.0;
}
for (marker, mut sprite, mut transform) in &mut markers {
for (marker, mut sprite, mut transform, children) in &mut markers {
if let Some(pos) = new_layout.pile_positions.get(&marker.0) {
sprite.custom_size = Some(new_layout.card_size);
transform.translation.x = pos.x;
transform.translation.y = pos.y;
// Children carry the outline frame and the "A"/"K" watermark.
// They are sized at spawn from that layout's card size, so they
// must be re-derived here too or a resize (fold/unfold, rotation)
// leaves them at the stale size — visible as oversized grey
// frames on empty piles.
let outline_size =
new_layout.card_size + Vec2::splat(PILE_MARKER_OUTLINE_WIDTH * 2.0);
let font_size = new_layout.card_size.x * 0.28;
for child in children.into_iter().flatten() {
if let Ok(mut outline) = marker_outlines.get_mut(*child) {
outline.custom_size = Some(outline_size);
}
if let Ok(mut label) = marker_labels.get_mut(*child) {
label.font_size = font_size;
}
}
}
}
@@ -623,6 +655,58 @@ mod tests {
assert_eq!(types.len(), 12);
}
#[test]
fn resize_updates_marker_outline_and_watermark_children() {
// Regression (found on Galaxy Fold 7, v0.41.0): `on_window_resized`
// resized the marker fill sprite but never its children, so after a
// fold/unfold the outline frame and "A"/"K" watermark stayed at
// spawn-time size — rendering as oversized grey slabs on empty piles.
let mut app = headless_app();
// Shrink the window well below the 1280×800 spawn default.
app.world_mut().write_message(WindowResized {
window: Entity::PLACEHOLDER,
width: 640.0,
height: 480.0,
});
app.update();
let card_size = app.world().resource::<LayoutResource>().0.card_size;
let expected_outline = card_size + Vec2::splat(PILE_MARKER_OUTLINE_WIDTH * 2.0);
let expected_font = card_size.x * 0.28;
let marker_children: Vec<Entity> = {
let mut q = app.world_mut().query::<(&PileMarker, &Children)>();
q.iter(app.world())
.flat_map(|(_, c)| c.iter().collect::<Vec<_>>())
.collect()
};
assert!(
!marker_children.is_empty(),
"pile markers must have outline/watermark children"
);
let mut outlines = 0;
let mut labels = 0;
for child in marker_children {
if let Some(sprite) = app.world().entity(child).get::<Sprite>() {
assert_eq!(
sprite.custom_size,
Some(expected_outline),
"outline child must track the resized card size",
);
outlines += 1;
}
if let Some(font) = app.world().entity(child).get::<TextFont>() {
assert!(
(font.font_size - expected_font).abs() < 0.01,
"watermark font must track the resized card size",
);
labels += 1;
}
}
assert_eq!(outlines, 12, "all 12 markers carry an outline child");
assert!(labels >= 11, "tableau + foundation markers carry watermarks");
}
#[test]
fn pile_markers_hide_when_pile_is_occupied() {
// After a fresh deal: the 7 tableau piles + the stock pile are