-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathBarStyle.lua
More file actions
355 lines (304 loc) · 13.1 KB
/
BarStyle.lua
File metadata and controls
355 lines (304 loc) · 13.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
-- Enhanced Cooldown Manager addon for World of Warcraft
-- Author: Argium
-- Licensed under the GNU General Public License v3.0
---@class ECM_BuffBarStatusBar : StatusBar Buff/external bar StatusBar with text child regions.
---@field Name FontString|nil Spell name text.
---@field Duration FontString|nil Duration text.
---@field Pip Texture|nil Pip/spark texture.
---@class ECM_BuffIconFrame : Frame Icon frame on a buff bar with application count.
---@field Applications FontString|nil Stack count text.
---@field __ecmSquareStyled boolean|nil One-time square-style flag.
local _, ns = ...
local FrameUtil = ns.FrameUtil
--------------------------------------------------------------------------------
-- Shared child-bar styling helpers (BuffBars / ExternalBars)
--
-- Stateless functions that operate on already-constructed bar widgets.
-- Not a mixin — callers invoke these directly as `BarStyle.StyleChildBar(...)`.
--------------------------------------------------------------------------------
--- Strips circular masks and hides overlay/border to produce a square icon.
--- The heavy cleanup (mask removal, pcalls, region iteration) is cached on the
--- frame via `__ecmSquareStyled` so it only runs once per icon frame.
---@param iconFrame ECM_BuffIconFrame|nil
---@param iconTexture Texture|nil
---@param iconOverlay Texture|nil
---@param debuffBorder Texture|nil
local function applySquareIconStyle(iconFrame, iconTexture, iconOverlay, debuffBorder)
if not iconFrame or iconFrame.__ecmSquareStyled or not iconTexture then
return
end
iconTexture:SetTexCoord(0, 1, 0, 1)
-- Remove circular masks from the icon texture
for i = (iconTexture:GetNumMaskTextures() or 0), 1, -1 do
local mask = iconTexture:GetMaskTexture(i)
if mask then
iconTexture:RemoveMaskTexture(mask)
mask:Hide()
end
end
-- Remove mask regions from the icon frame
for _, region in ipairs({ iconFrame:GetRegions() }) do
if region:IsObjectType("MaskTexture") then
pcall(iconTexture.RemoveMaskTexture, iconTexture, region)
region:Hide()
end
end
if iconOverlay then iconOverlay:Hide() end
if debuffBorder then debuffBorder:Hide() end
iconFrame.__ecmSquareStyled = true
end
---@param frame ECM_BuffBarMixin
---@param bar ECM_BuffBarStatusBar
---@param iconFrame ECM_BuffIconFrame|nil
---@param config table|nil
---@param globalConfig table|nil
local function styleBarHeight(frame, bar, iconFrame, config, globalConfig)
assert(frame ~= nil, "BarStyle.styleBarHeight requires a frame")
assert(bar ~= nil, "BarStyle.styleBarHeight requires a bar")
local height = (config and config.height) or (globalConfig and globalConfig.barHeight)
assert(type(height) == "number", "BarStyle.styleBarHeight requires config.height or globalConfig.barHeight")
if height <= 0 then
return
end
FrameUtil.LazySetHeight(frame, height)
FrameUtil.LazySetHeight(bar, height)
if iconFrame then
FrameUtil.LazySetHeight(iconFrame, height)
FrameUtil.LazySetWidth(iconFrame, height)
end
end
---@param frame Frame
---@param barBG Texture|nil
---@param config table|nil
---@param globalConfig table|nil
local function styleBarBackground(frame, barBG, config, globalConfig)
assert(frame ~= nil, "BarStyle.styleBarBackground requires a frame")
if not barBG then
return
end
-- One-time setup: reparent BarBG to the outer frame and hook SetPoint
-- so Blizzard cannot override our anchors. SetAllPoints does not fire
-- SetPoint hooks, so no re-entrancy guard is needed.
if not barBG.__ecmBGHooked then
---@diagnostic disable-next-line: inject-field
barBG.__ecmBGHooked = true
barBG:SetParent(frame)
hooksecurefunc(barBG, "SetPoint", function()
barBG:ClearAllPoints()
barBG:SetAllPoints(frame)
end)
end
local bgColor = (config and config.bgColor)
or (globalConfig and globalConfig.barBgColor)
assert(bgColor ~= nil, "BarStyle.styleBarBackground requires config.bgColor or globalConfig.barBgColor")
barBG:SetTexture(ns.Constants.FALLBACK_TEXTURE)
barBG:SetVertexColor(bgColor.r, bgColor.g, bgColor.b, bgColor.a)
barBG:ClearAllPoints()
barBG:SetAllPoints(frame)
barBG:SetDrawLayer("BACKGROUND", 0)
end
--- Resolves the spell color for a bar, handling secret values with retry.
--- Returns true if the module's _editLocked flag was set by this call.
---@param module table
---@param frame ECM_BuffBarMixin|Frame
---@param bar ECM_BuffBarStatusBar
---@param globalConfig table|nil
---@param spellColors ECM_SpellColorStore
---@param retryCount number|nil
---@return boolean|nil
local function styleBarColor(module, frame, bar, globalConfig, spellColors, retryCount)
assert(module ~= nil, "BarStyle.styleBarColor requires a module")
assert(type(module.Name) == "string" and module.Name ~= "", "BarStyle.styleBarColor requires module.Name")
assert(frame ~= nil, "BarStyle.styleBarColor requires a frame")
assert(bar ~= nil, "BarStyle.styleBarColor requires a bar")
assert(spellColors ~= nil, "BarStyle.styleBarColor requires an explicit spellColors store")
local currentRetryCount = retryCount or 0
local textureName = globalConfig and globalConfig.texture
FrameUtil.LazySetStatusBarTexture(bar, FrameUtil.GetTexture(textureName))
local barColor = spellColors:GetColorForBar(frame)
local spellName = bar.Name and bar.Name.GetText and bar.Name:GetText()
local spellID = frame.cooldownInfo and frame.cooldownInfo.spellID
local cooldownID = frame.cooldownID
local textureFileID = FrameUtil.GetIconTextureFileID(frame)
-- When in a raid instance, and after exiting combat, all identifying
-- values may remain secret. Lock editing only when every key is unusable.
-- With four tiers (name, spellID, cooldownID, texture) the colour lookup
-- is much more resilient to partial secrecy.
local allSecret = issecretvalue(spellName)
and issecretvalue(spellID)
and issecretvalue(cooldownID)
and issecretvalue(textureFileID)
module._editLocked = module._editLocked or allSecret
if allSecret and not InCombatLockdown() then
if currentRetryCount < 3 then
if frame._ecmColorRetryTimer then
frame._ecmColorRetryTimer:Cancel()
end
frame._ecmColorRetryTimer = C_Timer.NewTimer(1, function()
frame._ecmColorRetryTimer = nil
styleBarColor(module, frame, bar, globalConfig, spellColors, currentRetryCount + 1)
end)
-- Don't apply any colour while retries are pending — preserve
-- the bar's existing colour rather than clobbering it with the
-- default while we wait for secrets to clear.
return nil
elseif ns.IsDebugEnabled() and not module._warned then
ns.Log(module.Name, "All identifying keys are secret outside of combat.")
module._warned = true
end
end
if frame._ecmColorRetryTimer then
frame._ecmColorRetryTimer:Cancel()
frame._ecmColorRetryTimer = nil
end
if barColor == nil and not allSecret then
barColor = spellColors:GetDefaultColor()
end
if barColor then
FrameUtil.LazySetStatusBarColor(bar, barColor.r, barColor.g, barColor.b, 1.0)
end
return module._editLocked
end
local function isEmptyStatusBar(bar)
if not bar then
return false
end
local okMinMax, _, maxValue = pcall(bar.GetMinMaxValues, bar)
if not okMinMax then
return false
end
local okValue, value = pcall(bar.GetValue, bar)
if not okValue then
return false
end
if issecretvalue(maxValue) or issecretvalue(value) then
return false
end
if type(maxValue) ~= "number" or type(value) ~= "number" then
return false
end
return maxValue <= 0 and value <= 0
end
local function styleEmptyStatusBarBackground(bar, barBG, config, globalConfig)
if not barBG then
return
end
if not isEmptyStatusBar(bar) then
return
end
-- Match the foreground bar's texture so timeless auras render with the
-- user's configured statusbar texture instead of the solid fallback
-- already set by styleBarBackground. Prefer the module-level override
-- when set, otherwise fall back to the global texture.
local textureName = (config and config.texture) or (globalConfig and globalConfig.texture)
local texture = FrameUtil.GetTexture(textureName)
if texture then
barBG:SetTexture(texture)
end
local ok, r, g, b, a = pcall(bar.GetStatusBarColor, bar)
if ok then
barBG:SetVertexColor(r, g, b, a or 1)
end
end
---@param frame ECM_BuffBarMixin
---@param iconFrame ECM_BuffIconFrame|nil
---@param config table|nil
local function styleBarIcon(frame, iconFrame, config)
assert(frame ~= nil, "BarStyle.styleBarIcon requires a frame")
local showIcon = config and config.showIcon ~= false
if iconFrame then
FrameUtil.LazySetAnchors(iconFrame, {
{ "TOPLEFT", frame, "TOPLEFT", 0, 0 },
})
local iconTexture = FrameUtil.GetIconTexture(frame)
local iconOverlay = FrameUtil.GetIconOverlay(frame)
applySquareIconStyle(iconFrame, iconTexture, iconOverlay, frame.DebuffBorder)
iconFrame:SetShown(showIcon)
if iconTexture then
iconTexture:SetShown(showIcon)
end
end
if frame.DebuffBorder then
FrameUtil.LazySetAlpha(frame.DebuffBorder, 0)
frame.DebuffBorder:Hide()
end
if iconFrame and iconFrame.Applications then
FrameUtil.LazySetAlpha(iconFrame.Applications, showIcon and 1 or 0)
end
end
---@param frame ECM_BuffBarMixin
---@param bar ECM_BuffBarStatusBar
---@param iconFrame ECM_BuffIconFrame|nil
---@param config table|nil
local function styleBarAnchors(frame, bar, iconFrame, config)
assert(frame ~= nil, "BarStyle.styleBarAnchors requires a frame")
assert(bar ~= nil, "BarStyle.styleBarAnchors requires a bar")
assert(bar.Name ~= nil, "BarStyle.styleBarAnchors requires bar.Name")
local showSpellName = config and config.showSpellName ~= false
local showDuration = config and config.showDuration ~= false
if bar.Name then
bar.Name:SetShown(showSpellName)
end
if bar.Duration then
bar.Duration:SetShown(showDuration)
end
local iconVisible = iconFrame and iconFrame:IsShown()
local barLeftAnchor = iconVisible and iconFrame or frame
local barLeftPoint = iconVisible and "TOPRIGHT" or "TOPLEFT"
FrameUtil.LazySetAnchors(bar, {
{ "TOPLEFT", barLeftAnchor, barLeftPoint, 0, 0 },
{ "TOPRIGHT", frame, "TOPRIGHT", 0, 0 },
})
FrameUtil.LazySetAnchors(bar.Name, {
{ "LEFT", bar, "LEFT", ns.Constants.BUFFBARS_TEXT_PADDING, 0 },
{ "RIGHT", bar, "RIGHT", -ns.Constants.BUFFBARS_TEXT_PADDING, 0 },
})
if bar.Duration then
FrameUtil.LazySetAnchors(bar.Duration, {
{ "RIGHT", bar, "RIGHT", -ns.Constants.BUFFBARS_TEXT_PADDING, 0 },
})
end
end
--- Applies all sizing, styling, visibility, and anchoring to a single child bar.
--- Lazy setters ensure no-ops when values haven't changed.
---@param module table
---@param frame ECM_BuffBarMixin|Frame
---@param config table|nil
---@param globalConfig table|nil
---@param spellColors ECM_SpellColorStore
local function styleChildBar(module, frame, config, globalConfig, spellColors)
assert(module ~= nil, "BarStyle.styleChildBar requires a module")
assert(frame ~= nil, "BarStyle.styleChildBar requires a frame")
assert(frame.__ecmHooked, "Attempted to style a child frame that wasn't hooked.")
assert(spellColors ~= nil, "BarStyle.styleChildBar requires an explicit spellColors store")
local bar = assert(frame.Bar, "BarStyle.styleChildBar requires frame.Bar")
local iconFrame = frame.Icon
assert(bar.Pip ~= nil, "BarStyle.styleChildBar requires bar.Pip")
assert(bar.Name ~= nil, "BarStyle.styleChildBar requires bar.Name")
assert(bar.Duration ~= nil, "BarStyle.styleChildBar requires bar.Duration")
styleBarHeight(frame, bar, iconFrame, config, globalConfig)
bar.Pip:Hide()
bar.Pip:SetTexture(nil)
local barBG = FrameUtil.GetBarBackground(bar)
styleBarBackground(frame, barBG, config, globalConfig)
styleBarColor(module, frame, bar, globalConfig, spellColors, 0)
-- Only fill the background solid for bars with an active aura. cooldownID
-- identifies the configured slot, including inactive visible slots.
if frame.auraInstanceID then
styleEmptyStatusBarBackground(bar, barBG, config, globalConfig)
end
FrameUtil.ApplyFont(bar.Name, globalConfig, config)
FrameUtil.ApplyFont(bar.Duration, globalConfig, config)
styleBarIcon(frame, iconFrame, config)
styleBarAnchors(frame, bar, iconFrame, config)
end
local BarStyle = {
ApplySquareIconStyle = applySquareIconStyle,
StyleBarHeight = styleBarHeight,
StyleBarBackground = styleBarBackground,
StyleBarColor = styleBarColor,
StyleBarIcon = styleBarIcon,
StyleBarAnchors = styleBarAnchors,
StyleChildBar = styleChildBar,
}
ns.BarStyle = BarStyle