fix(ux): 14 cross-platform UX/UI fixes from 500-game audit
Web client (game.js): - Restart game timer after undo exits auto-complete sequence - Pause timer while browser tab is hidden (visibilitychange) - Validate URL seed — NaN / negative falls back to randomSeed() - Guard onBoardClick/onBoardDblClick during win (snap.is_won) - Delay win overlay 320 ms so last card CSS transition finishes - Force reflow in flashIllegal() to restart shake on rapid re-trigger Android (safe_area.rs): - Preserve last-known insets on app resume instead of zeroing them; eliminates double layout flash on every foreground cycle All clients — Bevy engine: - Radial menu: clamp icon anchors to viewport bounds so icons are never placed off-screen on narrow phones - Auto-complete: deactivate state.active when is_auto_completable goes false (undo mid-sequence) to stop perpetual background retry - Touch selection: gate highlight rebuild on is_changed() — was despawning/respawning entities every frame unnecessarily - Input: fire "Tap a pile to move" InfoToast on first tap in TapToSelect mode; document cursor_world 1:1 viewport invariant - Drag threshold: raise desktop from 4 → 6 px to prevent accidental drags from cursor jitter on HiDPI displays Desktop / Android (solitaire_app): - Call cleanup_orphaned_tmp_files() at startup to remove .tmp files left by crashes between atomic write and rename Design clarification (klondike_adapter.rs): - Doc comment: Draw-1 recycling is penalty-only by design (never blocked) to avoid creating unwinnable positions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -253,24 +253,24 @@ mod android {
|
||||
}
|
||||
}
|
||||
|
||||
/// Resets the inset poller and clears cached insets on
|
||||
/// `AppLifecycle::WillResume` so that `refresh_insets` re-queries JNI in the
|
||||
/// frames immediately after the app returns to the foreground.
|
||||
/// Resets the inset poller on `AppLifecycle::WillResume` so that
|
||||
/// `refresh_insets` re-queries JNI in the frames immediately after the app
|
||||
/// returns to the foreground.
|
||||
///
|
||||
/// Clearing `SafeAreaInsets` to the default (all-zero) fires
|
||||
/// `on_safe_area_changed` in `table_plugin`, which emits a synthetic
|
||||
/// `WindowResized`. `on_window_resized` then recomputes the layout;
|
||||
/// once `refresh_insets` resolves the real values a second synthetic
|
||||
/// `WindowResized` fires and the layout converges to the correct position.
|
||||
/// The cached `SafeAreaInsets` are intentionally **not** zeroed here.
|
||||
/// Zeroing them would cause two layout recomputes on every resume:
|
||||
/// once with zero insets (wrong position) and again when JNI resolves the
|
||||
/// real values — visible as a flash. By preserving the last-known values
|
||||
/// the layout remains stable; if JNI returns a different value (e.g. after
|
||||
/// a rotation) the single update that fires when `SafeAreaInsets` actually
|
||||
/// changes is enough.
|
||||
pub(super) fn rearm_on_resumed(
|
||||
mut lifecycle: MessageReader<AppLifecycle>,
|
||||
mut poll: ResMut<SafeAreaPollTries>,
|
||||
mut insets: ResMut<SafeAreaInsets>,
|
||||
) {
|
||||
for event in lifecycle.read() {
|
||||
if matches!(event, AppLifecycle::WillResume) {
|
||||
poll.0 = 0;
|
||||
*insets = SafeAreaInsets::default();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user