Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 102 additions & 12 deletions EllesmereUIActionBars/EllesmereUIActionBars.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1717,6 +1717,10 @@ _secureHandler:SetAttribute("_onattributechanged", [=[
-- Register all buttons and bar frames as refs on the secure handler.
-- Must be called AFTER SetupBar creates buttons (barButtons is populated).
local _secureRefsReady = false
-- One pass over every button that exists. Lazily built bars add buttons after
-- it has already run, so the reveal path in RefreshRuntimeVisibility clears
-- _secureRefsReady and calls this again; indices are reassigned consistently in
-- the same pass, and the only readers of btn._secureSlotIdx run after a full one.
local function SecureSetupHandler_PrepareRefs()
if _secureRefsReady then return end
_secureRefsReady = true
Expand Down Expand Up @@ -2987,7 +2991,13 @@ end
-------------------------------------------------------------------------------
-- Bar Setup creates frames and buttons for each bar
-------------------------------------------------------------------------------
local function SetupBar(info, skipProtected)
-- Build a bar's buttons into an EXISTING bar frame and record its base size.
-- Split out of SetupBar so a bar whose buttons were skipped at load can have
-- them built later, at the reveal edge: CreateBarFrame always creates a frame,
-- so re-running SetupBar there would leave a second one behind.
-- On ns rather than a file local: this file is at Lua 5.1's 200-local cap for
-- the main chunk, and a second top-level local here overflows it.
ns.BuildBarButtons = function(info, frame, skipProtected)
-- Shrink the clickable area to match a custom visual shape so a square
-- hit rect can't steal clicks from diamond/circle/etc neighbours. Insets
-- are a fraction of button size; "none" resets to full square.
Expand All @@ -3013,7 +3023,6 @@ local function SetupBar(info, skipProtected)
end

local key = info.key
local frame = CreateBarFrame(info)
local buttons = {}
local buttonShape = EAB and EAB.db and EAB.db.profile and EAB.db.profile.bars[key]
and EAB.db.profile.bars[key].buttonShape or "none"
Expand Down Expand Up @@ -3121,6 +3130,7 @@ local function SetupBar(info, skipProtected)
end

barButtons[key] = buttons
ns._eabBarNoButtons[key] = nil

-- Store original button size before any shape/scale modifications.
-- StanceButtons and PetActionButtons are 30x30; action buttons are 45x45.
Expand All @@ -3132,7 +3142,27 @@ local function SetupBar(info, skipProtected)
h = math.floor((btn1 and btn1:GetHeight() or 45) + 0.5),
}

return frame, buttons
return buttons
end

local function SetupBar(info, skipProtected)
local key = info.key
local frame = CreateBarFrame(info)
-- A bar that can never become visible gets no buttons at load: the button
-- loop is 95 % of such a bar's setup cost, and most of that is Blizzard's
-- CreateFrame on the action button template, which only a lower button
-- count can reach. The frame IS still built, so barFrames[key] stays
-- non-nil for its 52 indexing sites and barButtons[key] is empty, not nil.
-- RefreshRuntimeVisibility builds them when the bar leaves the Never set.
if ns.IsNeverBar(info) then
barButtons[key] = {}
ns._eabBarNoButtons[key] = true
-- Placeholder only, same fallback the button-derived value would take
-- with no buttons. BuildBarButtons recomputes it from the real button.
barBaseSize[key] = { w = 45, h = 45 }
return frame, barButtons[key]
end
return frame, ns.BuildBarButtons(info, frame, skipProtected)
end

-------------------------------------------------------------------------------
Expand Down Expand Up @@ -3568,21 +3598,34 @@ ns._eabBarDormant = {}
-- mid-combat, where a heavier reconcile would spike).
ns._eabBarNever = {}
ns._eabBarNeverWas = {}
-- Bars whose buttons were skipped at load because they were Never bars then.
-- Cleared by BuildBarButtons. Drives the lazy build in RefreshRuntimeVisibility.
ns._eabBarNoButtons = {}
-- Single source of truth for "this bar can never become visible through any
-- runtime condition". RecomputeNeverBars and the load-time button skip both
-- read it, so the two cannot drift apart -- a separately maintained restore
-- predicate already drifted once in this file (see RestoreGridSurfacedBars).
ns.IsNeverBar = function(info)
if info.isStance or info.isPetBar or info.visibilityOnly then return false end
local bars = EAB.db and EAB.db.profile and EAB.db.profile.bars
local s = bars and bars[info.key]
local never = s and (s.alwaysHidden or s.enabled == false) or false
-- Toggle override wins both ways: hiding an Always bar hard-disables its UI
-- work; showing a Never bar wakes it. Action bindings stay live.
local override = EAB._visOverride and EAB._visOverride[info.key]
if override == "never" then never = true
elseif override == "always" then never = false end
return never and true or false
end
ns.RecomputeNeverBars = function()
local bars = EAB.db and EAB.db.profile and EAB.db.profile.bars
if not bars then return end
local map = ns._eabBarNever
local changed = false
for _, info in ipairs(BAR_CONFIG) do
if not info.isStance and not info.isPetBar and not info.visibilityOnly then
local s = bars[info.key]
local override = EAB._visOverride and EAB._visOverride[info.key]
local never = s and (s.alwaysHidden or s.enabled == false) or false
-- Toggle override wins both ways: hiding an Always bar hard-disables
-- its UI work; showing a Never bar wakes it. Action bindings stay live.
if override == "never" then never = true
elseif override == "always" then never = false end
never = never and true or nil
-- nil rather than false so the map stays sparse for its readers.
local never = ns.IsNeverBar(info) or nil
if map[info.key] ~= never then
if map[info.key] and not never then
-- Leaving Never: remember to run the one heal the gates skipped (AlwaysShow grid).
Expand Down Expand Up @@ -5923,7 +5966,12 @@ local function LayoutBar(key)
fillS = table.concat(tf)
end
_lbStamp = table.concat({
tostring(nIcoS), tostring(s.overrideNumRows or s.numRows or 1),
-- #buttons, not just the configured count: a Never bar is built with
-- none and gains them at the reveal edge, and every other stamp input
-- can be identical across that edge (the base size falls back to the
-- same 45x45 the real button reports).
tostring(nIcoS), tostring(#buttons),
tostring(s.overrideNumRows or s.numRows or 1),
tostring(s.buttonPadding or 2), tostring(s.orientation), growDirS or "-",
tostring(s.buttonShape), tostring(s.buttonWidth), tostring(s.buttonHeight),
tostring(s._matchExtraPixels), tostring(s._matchExtraPixelsH),
Expand Down Expand Up @@ -9466,6 +9514,42 @@ function EAB:RefreshRuntimeVisibility()
-- through here (this is where drivers re-derive), so this is the single
-- recompute site for the hard-dormancy map the event walks gate on.
ns.RecomputeNeverBars()
-- Build the buttons of any bar that has left the Never set but had them
-- skipped at load (see SetupBar). Deliberately state-based rather than
-- edge-based: an options write taken in combat flips the map here but
-- cannot create secure frames, and the PLAYER_REGEN_ENABLED healer
-- re-enters this function with the map ALREADY flipped, so a transition
-- test would never fire again and the bar would stay buttonless until the
-- next reload. Testing the state instead means the deferred run heals it.
if next(ns._eabBarNoButtons) and not InCombatLockdown() then
local built = false
for _, info in ipairs(BAR_CONFIG) do
local frame = barFrames[info.key]
if frame and ns._eabBarNoButtons[info.key]
and not ns._eabBarNever[info.key] then
ns.BuildBarButtons(info, frame, false)
LayoutBar(info.key)
built = true
end
end
if built then
-- The refs pass already ran without these buttons in it.
_secureRefsReady = false
SecureSetupHandler_PrepareRefs()
-- ~200 override bindings are built from BAR_CONFIG x barButtons, so
-- a revealed bar has none until this runs. Defers itself in combat.
if _G._EAB_UpdateKeybinds then _G._EAB_UpdateKeybinds() end
-- Everything cosmetic (borders, shapes, fonts, backgrounds, button
-- art, cooldown visuals, range colouring) converges through the
-- full apply. Next frame, not inline: ApplyAll calls back into this
-- function, and the marker is already cleared by then so it finds
-- nothing to build. On ns, not on EAB, so the EUI_UnlockMode hook
-- on EAB.ApplyAll stays as it was.
C_Timer.After(0, function()
if ns._eabApplyAll then ns._eabApplyAll() end
end)
end
end
self:_RefreshSoftTargetGate()
for _, info in ipairs(ALL_BARS) do
local key = info.key
Expand Down Expand Up @@ -12377,6 +12461,12 @@ local function ApplyAll()
_isApplyingAll = false
end

-- Handle for the lazy bar build in RefreshRuntimeVisibility, which sits above
-- this definition and cannot see the local. On ns and not on EAB deliberately:
-- the EUI_UnlockMode hook watches EAB.ApplyAll, and putting it there would
-- start firing that hook for the first time.
ns._eabApplyAll = ApplyAll

-------------------------------------------------------------------------------
-- Position Save/Restore
-------------------------------------------------------------------------------
Expand Down