_sync_layout_controls() fires 3 concurrent hyprctl calls, and refresh()
can trigger it twice per open (once from the clients query, once from
the activeworkspace one) — each rebuilding a fresh set of layout
widgets. Two bugs followed:
1. The 3 callbacks each toggled the shared self._syncing flag
independently (True at start, False at end). Whichever resolved
first dropped the guard while the others — notably the tab-
selection one, which programmatically flips the visible stack
child — were still in flight. That let _on_tab_switch/_on_opt_change
misfire as if the user had clicked, re-applying stale layout data
via layouts.set() to whatever workspace was active by the time the
callback landed.
2. A second _sync_layout_controls() call (from the redundant rebuild)
could land mid-flight of the first, so a stale callback from sync
#1 could decrement sync #2's guard or write sync #1's data into
sync #2's freshly built widgets.
Fixed with a generation token: any sync superseded by a newer one is
invalidated outright, and self._syncing only lifts once the *current*
generation's 3 calls have all resolved. This is what "query the
current workspace's layout when opened, so it doesn't break switching
workspaces" needed — refresh() already re-queried on open, the gap was
in trusting stale in-flight results.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TLz6HWsXCwzQ97LrLt2em6