Skip to content

Commit 497de6b

Browse files
author
mergetest
committed
Pinned frames: defer protected container/header hides in the prune to combat end
The containers are implicitly protected (the secure header is a child), so PruneOrphanedSets hiding them in lockdown is a blocked action. Mirror SetEnabled's deferral: movers/labels still hide immediately, container and header hides set pendingPrune and replay on PLAYER_REGEN_ENABLED. Also hoist the per-iteration mode-suffix table.
1 parent f333a82 commit 497de6b

1 file changed

Lines changed: 48 additions & 15 deletions

File tree

Features/PinnedFrames.lua

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2604,9 +2604,25 @@ end
26042604
-- Runs at the end of Initialize (login + every rebuild) and on profile switch
26052605
-- (FullProfileRefresh). Skips live-frame writes during test mode (live frames
26062606
-- are hidden then; ExitTestMode re-asserts state on the way out). Combat-safe:
2607-
-- only non-secure frames are touched in lockdown; the secure header's Hide()
2608-
-- is deferred (it renders nothing without members anyway — the visible box is
2609-
-- always the non-secure mover/container).
2607+
-- movers/labels are genuinely non-secure and hide immediately, but the
2608+
-- CONTAINERS are implicitly protected (the secure header is a child), so —
2609+
-- like SetEnabled's pendingVisibilityUpdate — container and header hides are
2610+
-- deferred to PLAYER_REGEN_ENABLED (pendingPrune re-runs the whole prune).
2611+
local PRUNE_MODE_SUFFIXES = { "Party", "Raid" }
2612+
2613+
-- Hide a frame from the container tree. The secure header is parented to the
2614+
-- container, which makes the container implicitly protected — Hide() from
2615+
-- insecure code in lockdown is a blocked action. In combat: leave it, flag
2616+
-- the re-run at regen instead.
2617+
local function hideContainerSafe(self, f)
2618+
if not f then return end
2619+
if InCombatLockdown() then
2620+
if f:IsShown() then self.pendingPrune = true end
2621+
return
2622+
end
2623+
f:Hide()
2624+
end
2625+
26102626
function PinnedFrames:PruneOrphanedSets()
26112627
if self.testModeActive then return end
26122628
local inCombat = InCombatLockdown()
@@ -2615,10 +2631,9 @@ function PinnedFrames:PruneOrphanedSets()
26152631
for i = 1, PinnedFrames.MAX_SETS do
26162632
-- 1) Inactive-mode containers are never legitimate (runtime frames only
26172633
-- exist for the active mode) — hide by name, tracked or not.
2618-
for _, suffix in ipairs({ "Party", "Raid" }) do
2634+
for _, suffix in ipairs(PRUNE_MODE_SUFFIXES) do
26192635
if suffix ~= activeSuffix then
2620-
local c = _G["DandersPinned" .. i .. suffix .. "Container"]
2621-
if c then c:Hide() end
2636+
hideContainerSafe(self, _G["DandersPinned" .. i .. suffix .. "Container"])
26222637
end
26232638
end
26242639

@@ -2629,28 +2644,38 @@ function PinnedFrames:PruneOrphanedSets()
26292644

26302645
if not set then
26312646
-- 2) No set at this index in the current profile/mode: hide + untrack
2632-
-- everything. Header Hide() is combat-protected; defer if locked.
2633-
if self.headers[i] and not inCombat then
2634-
self.headers[i]:Hide()
2635-
self.headers[i] = nil
2647+
-- everything. Header/container hides are combat-protected; in
2648+
-- lockdown the tracking is left intact too, so the pendingPrune
2649+
-- re-run at regen repeats the full teardown.
2650+
if self.headers[i] then
2651+
if inCombat then
2652+
self.pendingPrune = true
2653+
else
2654+
self.headers[i]:Hide()
2655+
self.headers[i] = nil
2656+
end
26362657
end
26372658
if self.containers[i] then
26382659
if self.containers[i].mover then self.containers[i].mover:Hide() end
2639-
self.containers[i]:Hide()
2640-
self.containers[i] = nil
2660+
if inCombat then
2661+
self.pendingPrune = true
2662+
else
2663+
self.containers[i]:Hide()
2664+
self.containers[i] = nil
2665+
end
26412666
end
26422667
if self.labels[i] then self.labels[i]:Hide() end
2643-
self.labels[i] = nil
2668+
if not inCombat then self.labels[i] = nil end
26442669
if mover then mover:Hide() end
26452670
if label then label:Hide() end
2646-
if activeC then activeC:Hide() end
2671+
hideContainerSafe(self, activeC)
26472672
else
26482673
-- 3) Set exists: chrome may only show when the set is effectively
26492674
-- visible; the mover additionally requires the global unlock.
26502675
local visible = set.enabled and PinnedSoloAllowed(set)
26512676
if mover and (not visible or not self.moversShown) then mover:Hide() end
26522677
if label and (not visible or not set.showLabel) then label:Hide() end
2653-
if activeC and not visible then activeC:Hide() end
2678+
if activeC and not visible then hideContainerSafe(self, activeC) end
26542679
end
26552680
end
26562681
end
@@ -3059,6 +3084,14 @@ eventFrame:SetScript("OnEvent", function(self, event, arg1, ...)
30593084
PinnedFrames.pendingVisibilityUpdate = nil
30603085
end
30613086

3087+
-- Replay a prune whose container/header hides were blocked by combat
3088+
-- (PruneOrphanedSets defers protected-frame writes; see hideContainerSafe).
3089+
-- After the SetEnabled replays above, so the re-run sees final state.
3090+
if PinnedFrames.pendingPrune then
3091+
PinnedFrames.pendingPrune = nil
3092+
PinnedFrames:PruneOrphanedSets()
3093+
end
3094+
30623095
-- Replay layout changes (Direction/spacing/size/anchor) that were attempted
30633096
-- in combat. Skipped harmlessly when pendingReinitialize already ran above
30643097
-- (it returns early and re-applies every set's layout via ProcessAllSets).

0 commit comments

Comments
 (0)