@@ -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+
26102626function 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
26562681end
@@ -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