fix(engine): ensure dragged card stack z-order is above all piles (closes #35)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
funman300
2026-05-27 16:01:49 -07:00
parent 04aea8595a
commit 5a71e2bc0a
+23 -2
View File
@@ -64,6 +64,16 @@ pub enum TouchDragSet {
/// Z-depth used for cards while being dragged — above all resting cards. /// Z-depth used for cards while being dragged — above all resting cards.
const DRAG_Z: f32 = 500.0; const DRAG_Z: f32 = 500.0;
/// Relative Z step between cards inside a dragged stack.
///
/// Must stay at least as large as [`STACK_FAN_FRAC`], otherwise Android's
/// per-card corner overlay children (`local_z = 0.02`) can bleed above the
/// card body stacked directly above them while dragging.
const DRAG_STACK_Z_STEP: f32 = STACK_FAN_FRAC;
fn dragged_card_z(index: usize) -> f32 {
DRAG_Z + index as f32 * DRAG_STACK_Z_STEP
}
/// Solver budgets used by the H-key hint system. /// Solver budgets used by the H-key hint system.
/// ///
@@ -638,7 +648,7 @@ fn follow_drag(
if let Some((_, mut transform, mut sprite)) = if let Some((_, mut transform, mut sprite)) =
card_transforms.iter_mut().find(|(ce, _, _)| ce.card_id == id) card_transforms.iter_mut().find(|(ce, _, _)| ce.card_id == id)
{ {
transform.translation.z = DRAG_Z + i as f32 * 0.01; transform.translation.z = dragged_card_z(i);
sprite.color.set_alpha(0.85); sprite.color.set_alpha(0.85);
} }
} }
@@ -904,7 +914,7 @@ fn touch_follow_drag(
if let Some((_, mut transform, mut sprite)) = if let Some((_, mut transform, mut sprite)) =
card_transforms.iter_mut().find(|(ce, _, _)| ce.card_id == id) card_transforms.iter_mut().find(|(ce, _, _)| ce.card_id == id)
{ {
transform.translation.z = DRAG_Z + i as f32 * 0.01; transform.translation.z = dragged_card_z(i);
sprite.color.set_alpha(0.85); sprite.color.set_alpha(0.85);
} }
} }
@@ -1659,6 +1669,17 @@ mod tests {
use crate::layout::compute_layout; use crate::layout::compute_layout;
use solitaire_core::game_state::{DrawMode, GameState}; use solitaire_core::game_state::{DrawMode, GameState};
#[test]
fn dragged_card_z_matches_resting_stack_step() {
assert!((dragged_card_z(0) - DRAG_Z).abs() < 1e-6);
let step = dragged_card_z(1) - dragged_card_z(0);
assert!(step > 0.02, "drag step must exceed Android overlay local_z, got {step}");
assert!(
step + 1e-4 >= STACK_FAN_FRAC,
"drag step must stay aligned with resting stack spacing, got {step}"
);
}
#[test] #[test]
fn point_in_rect_inside_returns_true() { fn point_in_rect_inside_returns_true() {
let center = Vec2::new(10.0, 20.0); let center = Vec2::new(10.0, 20.0);