fix(engine): snap cards directly on window resize
on_window_resized was firing StateChangedEvent on every WindowResized event. That ran sync_cards_on_change → update_card_entity, which inserts a CardAnim slide tween for every card whose target moves >1 unit. During a corner drag the resize fires every frame, retargeting the slide each time from the cards' current mid-tween positions, so cards never reach steady state — the visible "snap back and forth" jitter reported during the 2026-04-29 smoke test. Replace the StateChangedEvent emit with a direct snap path: - Add LayoutSystem::UpdateOnResize SystemSet in layout.rs so cross- plugin ordering is explicit (Bevy's automatic conflict-based order only forces non-parallel execution, not a particular order). - table_plugin::on_window_resized: drop the StateChangedEvent emit; mark the system in_set(LayoutSystem::UpdateOnResize). It already snaps backgrounds and pile markers directly, so this aligns cards with the same instant-snap policy. - card_plugin: new snap_cards_on_window_resize system listens for WindowResized, runs .after(LayoutSystem::UpdateOnResize), writes fresh transforms via the existing card_positions() helper, and removes any in-flight CardAnim. It also reapplies the stock-empty indicator so the "↺" label's font_size (derived from layout.card_size.x) still rescales on resize. Other StateChangedEvent listeners — start_settle_anim, detect_auto_complete, clear_selection_on_state_change, check_no_moves, reset_hint_cycle_on_state_change, clear_right_click_highlights — no longer fire spuriously on resize. They should not fire on a layout change anyway; that was a pre-existing minor bug masked by the jitter. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,7 @@ use solitaire_core::card::Suit;
|
||||
use solitaire_core::pile::PileType;
|
||||
|
||||
use crate::events::{HintVisualEvent, StateChangedEvent};
|
||||
use crate::layout::{compute_layout, Layout, LayoutResource};
|
||||
use crate::layout::{compute_layout, Layout, LayoutResource, LayoutSystem};
|
||||
#[cfg(test)]
|
||||
use crate::layout::TABLE_COLOUR;
|
||||
use crate::settings_plugin::{SettingsChangedEvent, SettingsResource};
|
||||
@@ -69,7 +69,7 @@ impl Plugin for TablePlugin {
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
on_window_resized,
|
||||
on_window_resized.in_set(LayoutSystem::UpdateOnResize),
|
||||
apply_theme_on_settings_change,
|
||||
apply_hint_pile_highlight,
|
||||
tick_hint_pile_highlights,
|
||||
@@ -275,12 +275,10 @@ fn spawn_pile_markers(commands: &mut Commands, layout: &Layout) {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn on_window_resized(
|
||||
mut events: MessageReader<WindowResized>,
|
||||
mut layout_res: Option<ResMut<LayoutResource>>,
|
||||
mut state_changed: MessageWriter<StateChangedEvent>,
|
||||
mut backgrounds: Query<
|
||||
(&mut Sprite, &mut Transform),
|
||||
(With<TableBackground>, Without<PileMarker>),
|
||||
@@ -311,8 +309,12 @@ fn on_window_resized(
|
||||
}
|
||||
}
|
||||
|
||||
// Reposition card sprites to the new layout.
|
||||
state_changed.write(StateChangedEvent);
|
||||
// Card sprites are repositioned by `card_plugin::snap_cards_on_window_resize`
|
||||
// running `.after(LayoutSystem::UpdateOnResize)` — that system snaps card
|
||||
// transforms directly to the new layout instead of going through
|
||||
// `StateChangedEvent → sync_cards → CardAnim` which would retarget the
|
||||
// slide tween every frame during a corner drag (the visible "snap back
|
||||
// and forth" jitter).
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user