From b121585460b76ce101697fba62b4fc68f7a43bdf Mon Sep 17 00:00:00 2001 From: Glyalith Date: Thu, 6 Aug 2026 13:55:54 -0600 Subject: [PATCH] fix(unlockmode): Exit Without Saving now reverts a moved fallback anchor Reported by @grouch on 8.7.6: throw a fallback anchor far away to reach something underneath it, then Exit Without Saving, and it stays where it was thrown. This is ours, not an interaction with another addon. RevertPositions restored nine anchor fields from the snapshot and then deliberately re-attached the LIVE fallback on top. That carry-over existed because the snapshot never captured `fallback`, so without it a revert would have destroyed every fallback including ones from earlier sessions. It also meant a fallback edited during the session survived the discard, which is exactly the report: the anchor reverts, and then the edited fallback is put back over it. Fallbacks are positional. _NudgeSelectedFallbackGhost does fb.offsetX = (fb.offsetX or 0) + dx fb.offsetY = (fb.offsetY or 0) + dy so it mutates the table IN PLACE. The snapshot therefore has to copy it, not reference it, or the edit would drag the snapshot along and the revert would be a no-op. Snapshot `fallback` by copy, restore it from the snapshot by copy, and drop the carry-over it made redundant. Pre-session fallbacks are still preserved, which is what the carry-over was protecting; session edits now revert like every other position. The existing rule that a link reverting away takes its fallback with it still holds, because an entry with no snapshot is not recreated at all. --- EUI_UnlockMode.lua | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/EUI_UnlockMode.lua b/EUI_UnlockMode.lua index c59ac726..58991de6 100644 --- a/EUI_UnlockMode.lua +++ b/EUI_UnlockMode.lua @@ -9955,6 +9955,10 @@ local function SnapshotPositions() refX = info.refX, refY = info.refY, edgeOffX = info.edgeOffX, edgeOffY = info.edgeOffY, refFor = info.refFor, + -- COPY, not a reference: _NudgeSelectedFallbackGhost mutates + -- fb.offsetX/offsetY in place, so a shared table would drag the + -- snapshot along with the edit and make the revert a no-op. + fallback = info.fallback and CopyTable(info.fallback) or nil, } end end @@ -10271,18 +10275,14 @@ local function RevertPositions() -- 2) Restore anchor data before repositioning local anchorDB = GetAnchorDB() if anchorDB then - -- Fallback links live OUTSIDE the position transaction: setting or - -- adjusting one is an explicit action that survives a discard. The - -- snapshot never carried the field, so without this carry-over a - -- revert would silently destroy every fallback -- including ones - -- from previous sessions. - local liveFallbacks - for childKey, info in pairs(anchorDB) do - if info.fallback then - liveFallbacks = liveFallbacks or {} - liveFallbacks[childKey] = info.fallback - end - end + -- Fallbacks used to be carried over from the LIVE table here, because + -- the snapshot did not capture the field and a revert would otherwise + -- have destroyed every fallback including ones from earlier sessions. + -- The snapshot carries it now, which preserves those AND discards + -- edits made this session -- so the carry-over is gone. It was the + -- reason a fallback ghost dragged during a session kept its new + -- position after Exit Without Saving: the revert put the anchor back + -- and then re-attached the edited fallback on top of it. wipe(anchorDB) for childKey, info in pairs(snapshotAnchors) do anchorDB[childKey] = { @@ -10291,16 +10291,12 @@ local function RevertPositions() refX = info.refX, refY = info.refY, edgeOffX = info.edgeOffX, edgeOffY = info.edgeOffY, refFor = info.refFor, + -- Restored from the snapshot, so a fallback MOVED this session + -- reverts like every other position. Fresh copy so the next + -- session's nudges cannot reach back into the snapshot. + fallback = info.fallback and CopyTable(info.fallback) or nil, } end - if liveFallbacks then - for childKey, fb in pairs(liveFallbacks) do - local entry = anchorDB[childKey] - -- Re-attach only where an anchor link still exists: a link - -- that reverted away takes its fallback with it. - if entry then entry.fallback = fb end - end - end end -- 3) Restore element sizes