You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
954 lines
38 KiB
954 lines
38 KiB
|
|
local detailsFramework = DetailsFramework
|
|
|
|
if (not detailsFramework or not DetailsFrameworkCanLoad) then
|
|
return
|
|
end
|
|
|
|
---@class df_icongeneric : table, df_icon
|
|
---@field expirationTime number
|
|
---@field CooldownBrightnessTexture texture
|
|
---@field CooldownTexture texture
|
|
---@field CountdownText fontstring
|
|
---@field CooldownEdge texture
|
|
---@field options table
|
|
---@field NextIcon number
|
|
---@field IconPool table<number, df_icongeneric> table which store the icons created for this iconrow
|
|
|
|
---@class df_iconrow_generic_options : table
|
|
---@field icon_width number? @The width of the icon.
|
|
---@field icon_height number? @The height of the icon.
|
|
---@field texcoord table? @The texture coordinates of the icon.
|
|
---@field show_text boolean? @Whether to show text on the icon.
|
|
---@field text_color table? @The color of the text.
|
|
---@field text_size number? @The size of the text.
|
|
---@field text_font string? @The font of the text.
|
|
---@field text_outline string? @The outline style of the text.
|
|
---@field text_anchor df_anchor? @The anchor point of the text.
|
|
---@field text_alpha_by_percent boolean? @Whether to change the alpha of the text by the percentage of the cooldown.
|
|
---@field desc_text boolean? @Whether to show description text.
|
|
---@field desc_text_color table? @The color of the description text.
|
|
---@field desc_text_size number? @The size of the description text.
|
|
---@field desc_text_font string? @The font of the description text.
|
|
---@field desc_text_outline string? @The outline style of the description text.
|
|
---@field desc_text_anchor string? @The anchor point of the description text.
|
|
---@field desc_text_rel_anchor string? @The relative anchor point of the description text.
|
|
---@field desc_text_x_offset number? @The x offset of the description text.
|
|
---@field desc_text_y_offset number? @The y offset of the description text.
|
|
---@field stack_text boolean? @Whether to show stack text.
|
|
---@field stack_text_color table? @The color of the stack text.
|
|
---@field stack_text_size number? @The size of the stack text.
|
|
---@field stack_text_font string? @The font of the stack text.
|
|
---@field stack_text_outline string? @The outline style of the stack text.
|
|
---@field stack_text_anchor string? @The anchor point of the stack text.
|
|
---@field stack_text_rel_anchor string? @The relative anchor point of the stack text.
|
|
---@field stack_text_x_offset number? @The x offset of the stack text.
|
|
---@field stack_text_y_offset number? @The y offset of the stack text.
|
|
---@field left_padding number? @The distance between the right and left sides.
|
|
---@field top_padding number? @The distance between the top and bottom sides.
|
|
---@field icon_padding number? @The distance between each icon.
|
|
---@field backdrop table? @The backdrop options.
|
|
---@field backdrop_color table? @The color of the backdrop.
|
|
---@field backdrop_border_color table? @The color of the backdrop border.
|
|
---@field anchor table? @The anchor options.
|
|
---@field grow_direction number? @The direction in which the icons grow.
|
|
---@field center_alignment boolean? @Whether to align the icons in the center.
|
|
---@field surpress_blizzard_cd_timer boolean? @Whether to suppress the Blizzard cooldown timer.
|
|
---@field surpress_tulla_omni_cc boolean? @Whether to suppress the Tulla OmniCC cooldown count.
|
|
---@field on_tick_cooldown_update boolean? @Whether to update cooldowns on every tick.
|
|
---@field cooldown_max_brightness number? @The maximum brightness of the cooldown, it is adjusted by the percent.
|
|
---@field decimal_timer boolean? @Whether to display the timer in decimal format.
|
|
---@field show_cooldown boolean? @Whether to show blizzard cooldown animation.
|
|
---@field cooldown_reverse boolean? @Whether to reverse the cooldown animation.
|
|
---@field cooldown_swipe_enabled boolean? @Whether to enable the cooldown swipe animation.
|
|
---@field cooldown_edge_texture string? @The texture for the cooldown edge.
|
|
---@field show_horizontal_swipe boolean? @Whether to show the horizontal swipe animation.
|
|
---@field swipe_alpha number? @The alpha value for the swipe animation.
|
|
---@field swipe_brightness number? @The brightness value for the swipe animation.
|
|
---@field swipe_progressive_color boolean? @Whether to use progressive color for the swipe animation. Start on Green and goes to Red, follows percent amount.
|
|
---@field swipe_color table? @When the color isn't progressive, this is the color of the swipe
|
|
---@field swipe_color_start number[]? @Whether to use yellow color for the swipe animation.
|
|
---@field swipe_color_end number[]? @Whether to use red color for the swipe animation.
|
|
---@field remove_on_finish boolean? @Whether to remove the icon when the cooldown finishes. Only usable if the icon has a identifier (from setting specific).
|
|
---@field first_icon_use_anchor boolean? @The anchor point for the first ico will use the anchor set in options.anchor.
|
|
|
|
---@class df_iconrow_generic : df_iconrow
|
|
---@field SetCooldown fun(self:df_iconrow_generic, iconFrame:df_icongeneric)
|
|
---@field OnIconTick fun(iconFrame:df_icongeneric)
|
|
---@field FormatCooldownTime fun(thisTime:number)
|
|
---@field FormatCooldownTimeDecimal fun(formattedTime:number)
|
|
---@field GetIconGrowDirection fun(self:df_iconrow_generic):number
|
|
---@field OnOptionChanged fun(self:df_iconrow_generic, optionName:string)
|
|
---@field CreateIcon fun(self:df_iconrow_generic, iconName:string)
|
|
---@field GetIcon fun(self:df_iconrow_generic)
|
|
---@field SetStacks fun(self:df_iconrow_generic, iconFrame:df_icongeneric, bIsShown:boolean, stacksAmount:number?)
|
|
---@field AddSpecificIcon fun(self:df_iconrow_generic, identifierKey:any, spellId:number, borderColor:table, startTime:number, duration:number, forceTexture:string, descText:string, count:number, debuffType:string, caster:string, canStealOrPurge:boolean, spellName:string, isBuff:boolean, modRate:number, iconSettings:table)
|
|
---@field AddSpecificIconWithTemplate fun(self:df_iconrow_generic, iconTemplateTable:table)
|
|
---@field IsIconShown fun(self:df_iconrow_generic, identifierKey:any)
|
|
---@field SetIcon fun(self:df_iconrow_generic, spellId:number, borderColor:table, startTime:number, duration:number, iconTexture:string, descText:string, count:number, debuffType:string, caster:string, canStealOrPurge:boolean, spellName:string, isBuff:boolean, modRate:number, iconSettings:table, expirationTime:number?)
|
|
---@field RemoveSpecificIcon fun(self:df_iconrow_generic, identifierKey:any)
|
|
---@field ClearIcons fun(self:df_iconrow_generic, resetBuffs:boolean?, resetDebuffs:boolean?)
|
|
---@field AlignAuraIcons fun(self:df_iconrow_generic)
|
|
---@field SetAuraWithIconTemplate fun(self:df_iconrow_generic, auraInfo:aurainfo, iconTemplateTable:table)
|
|
---@field IconPool table<number, df_icongeneric>
|
|
---@field NextIcon number
|
|
---@field AuraCache table<any, boolean>
|
|
---@field shownAmount number
|
|
---@field options table
|
|
---@field SetSpecificAuraWithIconTemplate fun(self:df_iconrow_generic, identifierKey:any, auraInfo:aurainfo, iconTemplateTable:table)
|
|
|
|
local unpack = unpack
|
|
local CreateFrame = CreateFrame
|
|
local PixelUtil = PixelUtil
|
|
local GetTime = GetTime
|
|
local Clamp = detailsFramework.Math.Clamp
|
|
local GetSpellInfo = GetSpellInfo or function(spellID) if not spellID then return nil end local si = C_Spell.GetSpellInfo(spellID) if si then return si.name, nil, si.iconID, si.castTime, si.minRange, si.maxRange, si.spellID, si.originalIconID end end
|
|
|
|
local spellIconCache = {}
|
|
local spellNameCache = {}
|
|
local emptyTable = {}
|
|
local white = {1, 1, 1, 1}
|
|
|
|
local sortIconByShownState = function(i1, i2)
|
|
return i1:IsShown() and not i2:IsShown()
|
|
end
|
|
|
|
local iconFrameOnHideScript = function(self)
|
|
if (self.cooldownLooper) then
|
|
self.cooldownLooper:Cancel()
|
|
end
|
|
end
|
|
|
|
local checkPointCallback = function(iconFrame)
|
|
if (iconFrame.timeRemaining < 3) then
|
|
|
|
end
|
|
return true
|
|
end
|
|
|
|
detailsFramework.IconGenericMixin = {
|
|
---create a new icon frame
|
|
---@param self df_iconrow_generic the parent frame
|
|
---@param iconName string the name of the icon frame
|
|
---@return df_icongeneric
|
|
CreateIcon = function(self, iconName)
|
|
---@type df_icongeneric
|
|
local newIcon = CreateFrame("frame", iconName, self)
|
|
|
|
---@type texture
|
|
newIcon.Texture = newIcon:CreateTexture(nil, "artwork", nil, 1)
|
|
PixelUtil.SetPoint(newIcon.Texture, "topleft", newIcon, "topleft", 1, -1)
|
|
PixelUtil.SetPoint(newIcon.Texture, "bottomright", newIcon, "bottomright", -1, 1)
|
|
|
|
---@type texture
|
|
newIcon.CooldownBrightnessTexture = newIcon:CreateTexture(nil, "artwork", nil, 2)
|
|
newIcon.CooldownBrightnessTexture:SetBlendMode("ADD")
|
|
PixelUtil.SetPoint(newIcon.CooldownBrightnessTexture, "topleft", newIcon.Texture, "topleft", 0, 0)
|
|
PixelUtil.SetPoint(newIcon.CooldownBrightnessTexture, "bottomright", newIcon.Texture, "bottomright", 0, 0)
|
|
|
|
---@type texture
|
|
newIcon.Border = newIcon:CreateTexture(nil, "background")
|
|
newIcon.Border:SetAllPoints()
|
|
newIcon.Border:SetColorTexture(0, 0, 0)
|
|
|
|
---@type fontstring
|
|
newIcon.StackText = newIcon:CreateFontString(nil, "overlay", "GameFontNormal")
|
|
newIcon.StackText:SetPoint("bottomright", newIcon, "bottomright", 0, 0)
|
|
newIcon.StackText:Hide()
|
|
newIcon.StackTextShadow = newIcon:CreateFontString(nil, "artwork", "GameFontNormal")
|
|
newIcon.StackTextShadow:SetPoint("center", newIcon.StackText, "center", 0, 0)
|
|
newIcon.StackTextShadow:SetTextColor(0, 0, 0)
|
|
newIcon.StackTextShadow:Hide()
|
|
|
|
---@type fontstring
|
|
newIcon.Desc = newIcon:CreateFontString(nil, "overlay", "GameFontNormal")
|
|
newIcon.Desc:SetPoint("bottom", newIcon, "top", 0, 2)
|
|
newIcon.Desc:Hide()
|
|
|
|
--create a overlay texture which will indicate the cooldown time
|
|
newIcon.CooldownTexture = newIcon:CreateTexture(self:GetName() .. "CooldownTexture", "overlay", nil, 6)
|
|
newIcon.CooldownTexture:SetTexture("Interface\\BUTTONS\\GreyscaleRamp64", "CLAMP", "CLAMP", "TRILINEAR")
|
|
newIcon.CooldownTexture:SetHeight(2)
|
|
newIcon.CooldownTexture:SetPoint("bottomleft", newIcon.Texture, "bottomleft", 0, 0)
|
|
newIcon.CooldownTexture:SetPoint("bottomright", newIcon.Texture, "bottomright", 0, 0)
|
|
newIcon.CooldownTexture:Hide()
|
|
|
|
newIcon.CooldownEdge = newIcon:CreateTexture(self:GetName() .. "CooldownEdge", "overlay", nil, 7)
|
|
|
|
newIcon.CountdownText = newIcon:CreateFontString(self:GetName() .. "CooldownText", "overlay", "GameFontNormal")
|
|
newIcon.CountdownText:SetPoint("center", newIcon, "center", 0, 0)
|
|
newIcon.CountdownText:Hide()
|
|
|
|
newIcon.stacks = 0
|
|
newIcon:SetScript("OnHide", iconFrameOnHideScript)
|
|
|
|
local cooldownFrame = CreateFrame("cooldown", "$parentCooldownFrame", newIcon, "CooldownFrameTemplate, BackdropTemplate")
|
|
cooldownFrame:SetAllPoints()
|
|
cooldownFrame:EnableMouse(false)
|
|
cooldownFrame:SetFrameLevel(newIcon:GetFrameLevel()+1)
|
|
cooldownFrame.CountdownText = ({cooldownFrame:GetRegions()})[1]
|
|
newIcon.Cooldown = cooldownFrame
|
|
|
|
return newIcon
|
|
end,
|
|
|
|
---get an icon frame from the pool
|
|
---@param self df_iconrow_generic the parent frame
|
|
---@return df_icongeneric
|
|
GetIcon = function(self)
|
|
---@type df_icongeneric
|
|
local iconFrame = self.IconPool[self.NextIcon]
|
|
|
|
if (not iconFrame) then
|
|
---@type df_icongeneric
|
|
iconFrame = self:CreateIcon("$parentIcon" .. self.NextIcon)
|
|
iconFrame.parentIconRow = self
|
|
|
|
iconFrame.Desc:Hide()
|
|
iconFrame.Texture:SetTexCoord(0.1, 0.9, 0.1, 0.9)
|
|
iconFrame.Border:ClearAllPoints()
|
|
iconFrame.Border:SetPoint("topleft", iconFrame, "topleft", -1, 1)
|
|
iconFrame.Border:SetPoint("bottomright", iconFrame, "bottomright", 1, -1)
|
|
iconFrame.Border:SetTexture(130759)
|
|
iconFrame.Border:SetTexCoord(0.296875, 0.5703125, 0, 0.515625)
|
|
iconFrame.Border:SetDrawLayer("overlay", 7)
|
|
|
|
iconFrame.StackText:SetTextColor(detailsFramework:ParseColors(self.options.stack_text_color))
|
|
detailsFramework:SetFontSize(iconFrame.StackText, self.options.stack_text_size)
|
|
detailsFramework:SetFontFace(iconFrame.StackText, self.options.stack_text_font)
|
|
detailsFramework:SetFontOutline(iconFrame.StackText, self.options.stack_text_outline)
|
|
detailsFramework:SetFontFace(iconFrame.StackTextShadow, self.options.stack_text_font)
|
|
detailsFramework:SetFontSize(iconFrame.StackTextShadow, self.options.stack_text_size+1)
|
|
|
|
iconFrame.StackText:ClearAllPoints()
|
|
iconFrame.StackText:SetPoint(self.options.stack_text_anchor or "center", iconFrame, self.options.stack_text_rel_anchor or "center", self.options.stack_text_x_offset or 0, self.options.stack_text_y_offset or 0)
|
|
|
|
self.IconPool[self.NextIcon] = iconFrame
|
|
iconFrame = iconFrame
|
|
end
|
|
|
|
self.NextIcon = self.NextIcon + 1
|
|
return iconFrame
|
|
end,
|
|
|
|
SetStacks = function(self, iconFrame, bIsShown, stacksAmount)
|
|
if (bIsShown) then
|
|
iconFrame.StackText:Show()
|
|
iconFrame.StackTextShadow:Show()
|
|
iconFrame.StackText:SetText(stacksAmount)
|
|
iconFrame.StackTextShadow:SetText(stacksAmount)
|
|
iconFrame.stacks = stacksAmount
|
|
else
|
|
iconFrame.StackText:Hide()
|
|
iconFrame.StackTextShadow:Hide()
|
|
iconFrame.stacks = 0
|
|
end
|
|
end,
|
|
|
|
---adds only if not existing already in the cache
|
|
---@param self df_iconrow_generic the parent frame
|
|
AddSpecificIcon = function(self, identifierKey, spellId, borderColor, startTime, duration, forceTexture, descText, count, debuffType, caster, canStealOrPurge, spellName, isBuff, modRate, iconSettings)
|
|
if (not identifierKey or identifierKey == "") then
|
|
return
|
|
end
|
|
|
|
if (not self.AuraCache[identifierKey]) then
|
|
---@type df_icongeneric
|
|
local icon = self:SetIcon(spellId, borderColor, startTime, duration, forceTexture, descText, count, debuffType, caster, canStealOrPurge, spellName, isBuff or false, modRate, iconSettings)
|
|
icon.identifierKey = identifierKey
|
|
self.AuraCache[identifierKey] = true
|
|
end
|
|
end,
|
|
|
|
AddSpecificIconWithTemplate = function(self, iconTemplateTable)
|
|
self:AddSpecificIcon(iconTemplateTable.id, iconTemplateTable.id, nil, iconTemplateTable.startTime, iconTemplateTable.duration, nil, nil, iconTemplateTable.count, nil, nil, nil, nil, nil, nil, iconTemplateTable)
|
|
end,
|
|
|
|
IsIconShown = function(self, identifierKey)
|
|
if (not identifierKey or identifierKey == "") then
|
|
return
|
|
end
|
|
if (self.AuraCache[identifierKey]) then
|
|
return true
|
|
end
|
|
end,
|
|
|
|
SetSpecificAuraWithIconTemplate = function(self, identifierKey, auraInfo, iconTemplateTable)
|
|
if (not identifierKey or identifierKey == "") then
|
|
return
|
|
end
|
|
|
|
if (not self.AuraCache[identifierKey]) then
|
|
---@type df_icongeneric
|
|
local iconFrame = self:SetAuraWithIconTemplate(auraInfo, iconTemplateTable)
|
|
iconFrame.identifierKey = identifierKey
|
|
self.AuraCache[identifierKey] = true
|
|
end
|
|
end,
|
|
|
|
---set an icon frame with a template
|
|
---@param self df_iconrow_generic the parent frame
|
|
---@param auraInfo aurainfo
|
|
---@param iconTemplateTable df_icontemplate
|
|
SetAuraWithIconTemplate = function(self, auraInfo, iconTemplateTable)
|
|
local startTime = auraInfo.expirationTime - auraInfo.duration
|
|
|
|
---@type df_icongeneric
|
|
return self:SetIcon(auraInfo.spellId, nil, startTime, auraInfo.duration, auraInfo.icon, nil, auraInfo.applications, auraInfo.dispelName, auraInfo.sourceUnit, auraInfo.isStealable, auraInfo.name, auraInfo.isHelpful, auraInfo.timeMod, iconTemplateTable, auraInfo.expirationTime)
|
|
end,
|
|
|
|
---set an icon frame with a template
|
|
---@param self df_iconrow_generic the parent frame
|
|
---@return df_icongeneric?
|
|
SetIcon = function(self, spellId, borderColor, startTime, duration, iconTexture, descText, count, debuffType, caster, canStealOrPurge, spellName, isBuff, modRate, iconSettings, expirationTime)
|
|
local actualSpellName, spellIcon = spellNameCache[spellId], spellIconCache[spellId]
|
|
|
|
iconSettings = iconSettings or emptyTable
|
|
|
|
if (not actualSpellName) then
|
|
actualSpellName, _, spellIcon = GetSpellInfo(spellId)
|
|
spellIconCache[spellId] = spellIcon
|
|
spellNameCache[spellId] = actualSpellName
|
|
end
|
|
|
|
if iconTexture then
|
|
spellIcon = iconTexture
|
|
end
|
|
|
|
if (not spellIcon) then
|
|
if (iconSettings.texture) then
|
|
spellIcon = iconSettings.texture
|
|
end
|
|
end
|
|
|
|
if (iconSettings.overrideTexture) then
|
|
spellIcon = iconSettings.overrideTexture
|
|
end
|
|
|
|
if (spellIcon) then
|
|
spellName = spellName or actualSpellName or "unknown_aura"
|
|
modRate = modRate or 1
|
|
|
|
---@type df_icongeneric
|
|
local iconFrame = self:GetIcon()
|
|
self.shownAmount = self.NextIcon - 1
|
|
|
|
iconFrame.expirationTime = expirationTime
|
|
|
|
local width = iconSettings.width or self.options.icon_width
|
|
local height = iconSettings.height or self.options.icon_height or width
|
|
|
|
--adjust the width and height by scale
|
|
local scale = iconSettings.scale or 1
|
|
width = width * scale
|
|
height = height * scale
|
|
|
|
PixelUtil.SetSize(iconFrame, width, height)
|
|
|
|
--set the texture points to be all points minus one
|
|
iconFrame.Texture:ClearAllPoints()
|
|
PixelUtil.SetPoint(iconFrame.Texture, "topleft", iconFrame, "topleft", 1, -1)
|
|
PixelUtil.SetPoint(iconFrame.Texture, "bottomright", iconFrame, "bottomright", -1, 1)
|
|
|
|
iconFrame.textureWidth = math.max(iconFrame.Texture:GetWidth(), width)
|
|
iconFrame.textureHeight = math.max(iconFrame.Texture:GetHeight(), height) --for some reason, GetHeight() was returning 0 on the first call
|
|
|
|
--cache size
|
|
iconFrame.width = width
|
|
iconFrame.height = height
|
|
|
|
iconFrame:Show()
|
|
|
|
if (iconFrame.Texture.texture ~= spellIcon or (iconSettings.coords and iconSettings.coords ~= iconFrame.currentCoords)) then
|
|
iconFrame.Texture:SetTexture(spellIcon, "CLAMP", "CLAMP", iconSettings.textureFilter or "LINEAR") --"TRILINEAR"
|
|
|
|
if (iconSettings.coords) then
|
|
iconFrame.Texture:SetTexCoord(unpack(iconSettings.coords))
|
|
iconFrame.currentCoords = iconSettings.coords
|
|
|
|
elseif (self.options.texcoord ~= iconFrame.currentCoords) then
|
|
iconFrame.Texture:SetTexCoord(unpack(self.options.texcoord))
|
|
iconFrame.currentCoords = self.options.texcoord
|
|
else
|
|
iconFrame.Texture:SetTexCoord(0, 1, 0, 1)
|
|
end
|
|
|
|
if (iconSettings.points) then
|
|
iconFrame.Texture:ClearAllPoints()
|
|
for i = 1, #iconSettings.points do
|
|
local point = iconSettings.points[i]
|
|
iconFrame.Texture:SetPoint(point[1], iconFrame, point[2], point[3], point[4])
|
|
end
|
|
iconFrame.Texture:SetSize(width, height)
|
|
end
|
|
|
|
iconFrame.Texture.texture = spellIcon
|
|
|
|
elseif (not iconSettings.coords) then
|
|
if (self.options.texcoord ~= iconFrame.currentCoords) then
|
|
iconFrame.Texture:SetTexCoord(unpack(self.options.texcoord))
|
|
iconFrame.currentCoords = self.options.texcoord
|
|
else
|
|
iconFrame.Texture:SetTexCoord(0, 1, 0, 1)
|
|
end
|
|
end
|
|
|
|
iconFrame:SetIgnoreParentAlpha(false)
|
|
|
|
if (iconSettings.color) then
|
|
local r, g, b, a = detailsFramework:ParseColors(iconSettings.color)
|
|
iconFrame.Texture:SetVertexColor(r, g, b, a)
|
|
--ignore the param alpha has the settings might have an alpha for it
|
|
iconFrame:SetIgnoreParentAlpha(true)
|
|
else
|
|
iconFrame.Texture:SetVertexColor(1, 1, 1, 1)
|
|
end
|
|
|
|
if (borderColor) then
|
|
iconFrame.Border:Show()
|
|
iconFrame.Border:SetVertexColor(unpack(borderColor))
|
|
else
|
|
if (iconSettings.borderColor) then
|
|
iconFrame.Border:Show()
|
|
iconFrame.Border:SetTexture(iconSettings.borderTexture or 130759)
|
|
iconFrame.Border:SetVertexColor(unpack(iconSettings.borderColor or white))
|
|
else
|
|
iconFrame.Border:Hide()
|
|
end
|
|
end
|
|
|
|
if (count and count > 1 and self.options.stack_text) then
|
|
self:SetStacks(iconFrame, true, count)
|
|
else
|
|
self:SetStacks(iconFrame, false)
|
|
end
|
|
|
|
iconFrame.stacks = count or 0
|
|
|
|
if (iconSettings.alpha) then
|
|
iconFrame.Texture:SetAlpha(iconSettings.alpha)
|
|
else
|
|
iconFrame.Texture:SetAlpha(1)
|
|
end
|
|
|
|
--iconFrame.Texture:SetBlendMode("ADD")
|
|
iconFrame.CooldownBrightnessTexture:SetTexture(iconFrame.Texture:GetTexture())
|
|
do
|
|
local left, top, c, bottom, right = iconFrame.Texture:GetTexCoord()
|
|
iconFrame.CooldownBrightnessTexture:SetTexCoord(left, right, top, bottom)
|
|
|
|
local coords = iconFrame.CooldownBrightnessTexture.cords
|
|
if (coords) then
|
|
coords[1] = left
|
|
coords[2] = right
|
|
coords[3] = top
|
|
coords[4] = bottom
|
|
else
|
|
iconFrame.CooldownBrightnessTexture.cords = {left, right, top, bottom}
|
|
end
|
|
|
|
iconFrame.CooldownBrightnessTexture.top = top
|
|
iconFrame.CooldownBrightnessTexture.bottom = bottom
|
|
|
|
PixelUtil.SetPoint(iconFrame.CooldownBrightnessTexture, "bottomright", iconFrame.Texture, "bottomright", 0, 0)
|
|
end
|
|
|
|
--make information available
|
|
iconFrame.spellId = spellId
|
|
iconFrame.startTime = startTime
|
|
iconFrame.duration = duration
|
|
iconFrame.endTime = (startTime and duration and startTime + duration) or 0
|
|
iconFrame.count = count
|
|
iconFrame.debuffType = debuffType
|
|
iconFrame.caster = caster
|
|
iconFrame.canStealOrPurge = canStealOrPurge
|
|
iconFrame.isBuff = isBuff
|
|
iconFrame.spellName = spellName
|
|
iconFrame.modRate = modRate
|
|
iconFrame.options = self.options
|
|
|
|
if (startTime and duration and duration > 0) then
|
|
local endTime = startTime + duration
|
|
local now = GetTime()
|
|
if (endTime > now) then
|
|
iconFrame.CooldownTexture:Show()
|
|
iconFrame.CooldownBrightnessTexture:Show()
|
|
iconFrame.CooldownTexture:SetHeight(1)
|
|
self:SetCooldown(iconFrame)
|
|
end
|
|
else
|
|
iconFrame.CooldownBrightnessTexture:Hide()
|
|
iconFrame.CooldownTexture:Hide()
|
|
end
|
|
|
|
iconFrame.identifierKey = nil -- only used for "specific" add/remove
|
|
|
|
--add the spell into the cache
|
|
self.AuraCache[spellId or -1] = true
|
|
self.AuraCache[spellName] = true
|
|
self.AuraCache.canStealOrPurge = self.AuraCache.canStealOrPurge or canStealOrPurge
|
|
self.AuraCache.hasEnrage = self.AuraCache.hasEnrage or debuffType == "" --yes, enrages are empty-string...
|
|
|
|
--show the frame
|
|
self:Show()
|
|
|
|
return iconFrame
|
|
end
|
|
end,
|
|
|
|
---@param self df_iconrow_generic the parent frame
|
|
---@param iconFrame df_icongeneric
|
|
SetCooldown = function(self, iconFrame)
|
|
if (iconFrame.cooldownLooper) then
|
|
iconFrame.cooldownLooper:Cancel()
|
|
end
|
|
|
|
local options = iconFrame.options
|
|
|
|
--iconFrame:SetScale(3) --debug
|
|
|
|
if (options.show_horizontal_swipe) then
|
|
iconFrame.CooldownEdge:Show()
|
|
iconFrame.CooldownTexture:Show()
|
|
iconFrame.CooldownBrightnessTexture:Show()
|
|
|
|
iconFrame.CooldownEdge.texture = nil
|
|
iconFrame.CooldownEdge:SetAlpha(0.834)
|
|
|
|
PixelUtil.SetSize(iconFrame.CooldownEdge, iconFrame.CooldownTexture:GetWidth(), 2)
|
|
PixelUtil.SetPoint(iconFrame.CooldownEdge, "topleft", iconFrame.CooldownTexture, "topleft", 0, 0)
|
|
PixelUtil.SetPoint(iconFrame.CooldownEdge, "topright", iconFrame.CooldownTexture, "topright", 0, 0)
|
|
PixelUtil.SetHeight(iconFrame.CooldownEdge, 8)
|
|
|
|
iconFrame.CooldownEdge:SetTexture(options.swipe_white, "CLAMP", "CLAMP", "TRILINEAR")
|
|
iconFrame.CooldownEdge.texture = options.swipe_white
|
|
|
|
local swipe_brightness = options.swipe_brightness
|
|
iconFrame.CooldownBrightnessTexture:SetAlpha(swipe_brightness)
|
|
|
|
local swipe_darkness = options.swipe_alpha
|
|
iconFrame.CooldownTexture:SetAlpha(swipe_darkness)
|
|
iconFrame.CooldownTexture:SetVertexColor(unpack(options.swipe_color))
|
|
else
|
|
iconFrame.CooldownEdge:Hide()
|
|
iconFrame.CooldownBrightnessTexture:Hide()
|
|
iconFrame.CooldownTexture:Hide()
|
|
end
|
|
|
|
if (options.show_text) then
|
|
detailsFramework:SetFontColor(iconFrame.CountdownText, self.options.text_color)
|
|
detailsFramework:SetFontSize(iconFrame.CountdownText, self.options.text_size)
|
|
detailsFramework:SetFontFace(iconFrame.CountdownText, self.options.text_font)
|
|
detailsFramework:SetFontOutline(iconFrame.CountdownText, self.options.text_outline)
|
|
detailsFramework:SetAnchor(iconFrame.CountdownText, self.options.text_anchor, iconFrame)
|
|
iconFrame.CountdownText:Show()
|
|
iconFrame.CountdownText:SetAlpha(1)
|
|
else
|
|
iconFrame.CountdownText:Hide()
|
|
end
|
|
|
|
if (options.show_cooldown) then
|
|
iconFrame.Cooldown:Show()
|
|
iconFrame.Cooldown:SetReverse(options.cooldown_reverse)
|
|
iconFrame.Cooldown:SetDrawSwipe(options.cooldown_swipe_enabled)
|
|
iconFrame.Cooldown:SetEdgeTexture(options.cooldown_edge_texture) --the yellow edge that follows the cooldown animation
|
|
iconFrame.Cooldown:SetHideCountdownNumbers(options.surpress_blizzard_cd_timer)
|
|
|
|
iconFrame.Cooldown:SetSwipeTexture([[Interface\Masks\SquareMask]], 0, 0, 0, 0.3)
|
|
--iconFrame.Cooldown:SetSwipeColor(1, 1, 1, 1)
|
|
--iconFrame.Cooldown:SetSwipeColor(0, 0, 0, 0.1)
|
|
|
|
--iconFrame.Cooldown:SetDrawEdge(true) --the same shit as above
|
|
--iconFrame.Cooldown:SetDrawSwipe(true)
|
|
--iconFrame.Cooldown:SetDrawBling(true) --edge of the animation, a thin horizontal texture
|
|
--iconFrame.Cooldown:SetEdgeScale(4) --edge of the animation, a thin horizontal texture
|
|
|
|
if (not options.surpress_blizzard_cd_timer) then
|
|
detailsFramework:SetFontColor(iconFrame.Cooldown.CountdownText, self.options.text_color)
|
|
detailsFramework:SetFontSize(iconFrame.Cooldown.CountdownText, self.options.text_size)
|
|
detailsFramework:SetFontFace(iconFrame.Cooldown.CountdownText, self.options.text_font)
|
|
detailsFramework:SetFontOutline(iconFrame.Cooldown.CountdownText, self.options.text_outline)
|
|
end
|
|
iconFrame.Cooldown.noCooldownCount = options.surpress_tulla_omni_cc
|
|
|
|
CooldownFrame_Set(iconFrame.Cooldown, iconFrame.startTime, iconFrame.duration, true, true, iconFrame.modRate)
|
|
|
|
iconFrame.CooldownBrightnessTexture:Show()
|
|
else
|
|
iconFrame.Cooldown:Hide()
|
|
end
|
|
|
|
self.OnIconTick(iconFrame)
|
|
|
|
local amountOfLoops = math.floor(iconFrame.duration / 0.25)
|
|
local loopEndCallback = nil
|
|
if (iconFrame.options.remove_on_finish) then
|
|
--increase the amount of loops in one, so the last loop will remove the icon
|
|
--otherwise it might finish
|
|
amountOfLoops = amountOfLoops + 1
|
|
local newLooper = detailsFramework.Schedules.NewLooper(0.25, self.OnIconTick, amountOfLoops, loopEndCallback, checkPointCallback, iconFrame)
|
|
iconFrame.cooldownLooper = newLooper
|
|
else
|
|
local newLooper = detailsFramework.Schedules.NewLooper(0.25, self.OnIconTick, amountOfLoops, loopEndCallback, checkPointCallback, iconFrame)
|
|
iconFrame.cooldownLooper = newLooper
|
|
end
|
|
end,
|
|
|
|
---@param iconFrame df_icongeneric
|
|
OnIconTick = function(iconFrame)
|
|
local now = GetTime()
|
|
--local percent = (now - iconFrame.startTime) / iconFrame.duration --no mod rate
|
|
local percent = (((now - iconFrame.startTime) / (iconFrame.modRate or 1)) / (iconFrame.duration / (iconFrame.modRate or 1))) or 0
|
|
local options = iconFrame.options
|
|
percent = Saturate(percent)
|
|
--percent = abs(percent - 1)
|
|
|
|
iconFrame.timeRemaining = iconFrame.duration - (now - iconFrame.startTime)
|
|
|
|
if (percent >= 1) then
|
|
--time expired
|
|
if (options.remove_on_finish) then
|
|
iconFrame:GetParent():RemoveSpecificIcon(iconFrame.identifierKey)
|
|
return
|
|
else
|
|
percent = 1
|
|
end
|
|
end
|
|
|
|
if (options.show_horizontal_swipe) then
|
|
local newHeight = math.min(iconFrame.textureHeight * percent, iconFrame.textureHeight)
|
|
iconFrame.CooldownTexture:SetHeight(newHeight)
|
|
|
|
PixelUtil.SetPoint(iconFrame.CooldownBrightnessTexture, "bottomright", iconFrame.Texture, "bottomright", 0, newHeight) --iconFrame.textureHeight -
|
|
local left, right, top, bottom = unpack(iconFrame.CooldownBrightnessTexture.cords)
|
|
local newBottomCord = Lerp(iconFrame.CooldownBrightnessTexture.top, iconFrame.CooldownBrightnessTexture.bottom, abs(percent - 1))
|
|
iconFrame.CooldownBrightnessTexture:SetTexCoord(left, right, top, newBottomCord)
|
|
|
|
--local newBrightness = Lerp(Saturate(options.cooldown_max_brightness-0.6), options.cooldown_max_brightness, percent)
|
|
--iconFrame.CooldownBrightnessTexture:SetAlpha(newBrightness)
|
|
|
|
if (options.swipe_progressive_color) then
|
|
--interpolate from green to red
|
|
--percent goes from 0 to 1, where zero is the start of the cooldown and 1 is the end
|
|
if (options.swipe_color_start and options.swipe_color_end) then
|
|
--use the first and second color
|
|
local r1, g1, b1 = unpack(options.swipe_color_start)
|
|
local r2, g2, b2 = unpack(options.swipe_color_end)
|
|
local r, g, b = detailsFramework.Math.LerpLinearColor(percent, 1, r1, g1, b1, r2, g2, b2)
|
|
iconFrame.CooldownEdge:SetVertexColor(r, g, b, 0.834)
|
|
else
|
|
--use a solid color
|
|
iconFrame.CooldownEdge:SetVertexColor(unpack(options.swipe_color))
|
|
end
|
|
|
|
--iconFrame.CooldownEdge:SetVertexColor(percent, math.abs(percent-1), 0, 0.834)
|
|
local alpha = Saturate(0.2 + percent)
|
|
iconFrame.CooldownEdge:SetAlpha(alpha)
|
|
else
|
|
--use a solid color
|
|
iconFrame.CooldownEdge:SetVertexColor(unpack(options.swipe_color))
|
|
end
|
|
end
|
|
|
|
if (options.show_cooldown) then
|
|
if (options.cooldown_max_brightness) then
|
|
iconFrame.CooldownBrightnessTexture:SetAlpha(Lerp(0, options.cooldown_max_brightness, percent))
|
|
local swipeAlpha = Saturate(Lerp(0, 1, percent))
|
|
local exponentialCurve = 0.1 * math.exp(3.5 * swipeAlpha)
|
|
exponentialCurve = Saturate(exponentialCurve)
|
|
iconFrame.Cooldown:SetSwipeColor(0, 0, 0, exponentialCurve)
|
|
end
|
|
end
|
|
|
|
--show the countdown text
|
|
if (options.show_text) then
|
|
iconFrame.CountdownText:SetText(iconFrame.parentIconRow.FormatCooldownTime((iconFrame.duration - (now - iconFrame.startTime)) / (iconFrame.modRate or 1)))
|
|
if (options.text_alpha_by_percent) then
|
|
iconFrame.CountdownText:SetAlpha(percent)
|
|
end
|
|
end
|
|
end,
|
|
|
|
FormatCooldownTime = function(thisTime)
|
|
if (thisTime >= 3600) then
|
|
thisTime = math.floor(thisTime / 3600) .. "h"
|
|
|
|
elseif (thisTime >= 60) then
|
|
thisTime = math.floor(thisTime / 60) .. "m"
|
|
|
|
else
|
|
thisTime = math.floor(thisTime)
|
|
end
|
|
return thisTime
|
|
end,
|
|
|
|
FormatCooldownTimeDecimal = function(formattedTime)
|
|
if formattedTime < 10 then
|
|
return ("%.1f"):format(formattedTime)
|
|
|
|
elseif formattedTime < 60 then
|
|
return ("%d"):format(formattedTime)
|
|
|
|
elseif formattedTime < 3600 then
|
|
return ("%d:%02d"):format(formattedTime/60%60, formattedTime%60)
|
|
|
|
elseif formattedTime < 86400 then
|
|
return ("%dh %02dm"):format(formattedTime/(3600), formattedTime/60%60)
|
|
|
|
else
|
|
return ("%dd %02dh"):format(formattedTime/86400, (formattedTime/3600) - (math.floor(formattedTime/86400) * 24))
|
|
end
|
|
end,
|
|
|
|
---@param self df_iconrow_generic the parent frame
|
|
---@param identifierKey any
|
|
RemoveSpecificIcon = function(self, identifierKey)
|
|
if (not identifierKey or identifierKey == "") then
|
|
return
|
|
end
|
|
|
|
if (not self.AuraCache[identifierKey]) then
|
|
return
|
|
end
|
|
self.AuraCache[identifierKey] = nil
|
|
|
|
local iconPool = self.IconPool
|
|
|
|
--find and hide the icon frame
|
|
for i = 1, self.NextIcon -1 do
|
|
local iconFrame = iconPool[i]
|
|
if (iconFrame.identifierKey and iconFrame.identifierKey == identifierKey) then
|
|
iconFrame:Hide()
|
|
iconFrame:ClearAllPoints()
|
|
iconFrame.identifierKey = nil
|
|
else
|
|
self.AuraCache[iconFrame.spellId] = true
|
|
self.AuraCache[iconFrame.spellName] = true
|
|
self.AuraCache.canStealOrPurge = self.AuraCache.canStealOrPurge or iconFrame.canStealOrPurge
|
|
self.AuraCache.hasEnrage = self.AuraCache.hasEnrage or iconFrame.debuffType == "" --yes, enrages are empty-string...
|
|
end
|
|
end
|
|
|
|
self:AlignAuraIcons()
|
|
end,
|
|
|
|
---@param self df_iconrow_generic the parent frame
|
|
ClearIcons = function(self, resetBuffs, resetDebuffs)
|
|
resetBuffs = resetBuffs ~= false
|
|
resetDebuffs = resetDebuffs ~= false
|
|
table.wipe(self.AuraCache)
|
|
|
|
local iconPool = self.IconPool
|
|
|
|
for i = 1, self.NextIcon -1 do
|
|
local iconFrame = iconPool[i]
|
|
if (iconFrame.isBuff == nil) then
|
|
iconFrame:Hide()
|
|
iconFrame:ClearAllPoints()
|
|
|
|
elseif (resetBuffs and iconFrame.isBuff) then
|
|
iconFrame:Hide()
|
|
iconFrame:ClearAllPoints()
|
|
|
|
elseif (resetDebuffs and not iconFrame.isBuff) then
|
|
iconFrame:Hide()
|
|
iconFrame:ClearAllPoints()
|
|
|
|
else
|
|
self.AuraCache[iconFrame.spellId] = true
|
|
self.AuraCache[iconFrame.spellName] = true
|
|
self.AuraCache.canStealOrPurge = self.AuraCache.canStealOrPurge or iconFrame.canStealOrPurge
|
|
self.AuraCache.hasEnrage = self.AuraCache.hasEnrage or iconFrame.debuffType == "" --yes, enrages are empty-string...
|
|
end
|
|
end
|
|
|
|
self:AlignAuraIcons()
|
|
end,
|
|
|
|
---@param self df_iconrow_generic the parent frame
|
|
AlignAuraIcons = function(self)
|
|
local iconPool = self.IconPool
|
|
local iconAmount = #iconPool
|
|
local countStillShown = 0
|
|
|
|
if iconAmount == 0 then
|
|
self:Hide()
|
|
else
|
|
table.sort(iconPool, sortIconByShownState)
|
|
|
|
local shownAmount = 0
|
|
for i = 1, iconAmount do
|
|
if iconPool[i]:IsShown() then
|
|
shownAmount = shownAmount + 1
|
|
end
|
|
end
|
|
|
|
local width = 0
|
|
local growDirection = self:GetIconGrowDirection()
|
|
local nWhichSide = self.options.anchor.side
|
|
local bIsCenterAligned = detailsFramework.ShouldCenterAlign[nWhichSide]
|
|
|
|
--re-anchor not hidden
|
|
for i = 1, shownAmount do
|
|
local bIsFirstIcon = i == 1
|
|
|
|
local iconFrame = iconPool[i]
|
|
local anchorTo = bIsFirstIcon and self or self.IconPool[i - 1]
|
|
local xPadding
|
|
|
|
if (bIsFirstIcon) then
|
|
xPadding = self.options.left_padding
|
|
else
|
|
xPadding = self.options.icon_padding or 1
|
|
xPadding = xPadding * (growDirection == 2 and -1 or 1)
|
|
end
|
|
|
|
countStillShown = countStillShown + (iconFrame:IsShown() and 1 or 0)
|
|
|
|
iconFrame:ClearAllPoints()
|
|
|
|
if (growDirection == 1) then --grow to right
|
|
if (bIsFirstIcon) then
|
|
if (self.options.first_icon_use_anchor) then
|
|
detailsFramework:SetAnchor(iconFrame, self.options.anchor, self)
|
|
else
|
|
local attachSide = (bIsCenterAligned and "center") or (nWhichSide and not detailsFramework.SideIsCorner[nWhichSide] and "left") or "bottomleft"
|
|
PixelUtil.SetPoint(iconFrame, attachSide, anchorTo, attachSide, 0, 0)
|
|
end
|
|
else
|
|
PixelUtil.SetPoint(iconFrame, "left", anchorTo, "right", xPadding, 0)
|
|
end
|
|
|
|
elseif (growDirection == 2) then --grow to left
|
|
if (bIsFirstIcon) then
|
|
if (self.options.first_icon_use_anchor) then
|
|
detailsFramework:SetAnchor(iconFrame, self.options.anchor, self)
|
|
else
|
|
local attachSide = (bIsCenterAligned and "center") or (nWhichSide and not detailsFramework.SideIsCorner[nWhichSide] and "right") or "bottomright"
|
|
PixelUtil.SetPoint(iconFrame, attachSide, anchorTo, attachSide, 0, 0)
|
|
end
|
|
else
|
|
PixelUtil.SetPoint(iconFrame, "right", anchorTo, "left", xPadding, 0)
|
|
end
|
|
end
|
|
|
|
width = width + ((iconFrame.width or iconFrame:GetWidth())) + 1 --* iconFrame:GetScale() removed the getscale as now the scale are applied to the width and height
|
|
end
|
|
|
|
if (bIsCenterAligned) then
|
|
self:SetWidth(width)
|
|
end
|
|
|
|
self.shownAmount = shownAmount
|
|
end
|
|
|
|
self.NextIcon = countStillShown + 1
|
|
|
|
if countStillShown > 0 then
|
|
self:Show()
|
|
end
|
|
end,
|
|
|
|
---@param self df_iconrow_generic the parent frame
|
|
GetIconGrowDirection = function(self)
|
|
local side = self.options.anchor.side
|
|
return detailsFramework.GrowDirectionBySide[side]
|
|
end,
|
|
|
|
---@param self df_iconrow_generic the parent frame
|
|
OnOptionChanged = function(self, optionName)
|
|
if (self.SetBackdropColor) then
|
|
self:SetBackdropColor(unpack(self.options.backdrop_color))
|
|
self:SetBackdropBorderColor(unpack(self.options.backdrop_border_color))
|
|
end
|
|
end,
|
|
}
|
|
|
|
---@type df_iconrow_generic_options
|
|
local default_iconrow_generic_options = {
|
|
icon_width = 20,
|
|
icon_height = 20,
|
|
texcoord = {.1, .9, .1, .9},
|
|
show_text = false,
|
|
text_color = {1, 1, 1, 1},
|
|
text_size = 12,
|
|
text_font = "Arial Narrow",
|
|
text_outline = "NONE",
|
|
text_anchor = {side = 9, x = 0, y = 0},
|
|
text_alpha_by_percent = false,
|
|
desc_text = true,
|
|
desc_text_color = {1, 1, 1, 1},
|
|
desc_text_size = 7,
|
|
desc_text_font = "Arial Narrow",
|
|
desc_text_outline = "NONE",
|
|
desc_text_anchor = "bottom",
|
|
desc_text_rel_anchor = "top",
|
|
desc_text_x_offset = 0,
|
|
desc_text_y_offset = 2,
|
|
stack_text = true,
|
|
stack_text_color = {1, 1, 1, 1},
|
|
stack_text_size = 10,
|
|
stack_text_font = "Arial Narrow",
|
|
stack_text_outline = "NONE",
|
|
stack_text_anchor = "center",
|
|
stack_text_rel_anchor = "bottomright",
|
|
stack_text_x_offset = 0,
|
|
stack_text_y_offset = 0,
|
|
left_padding = 1, --distance between right and left
|
|
top_padding = 1, --distance between top and bottom
|
|
icon_padding = 1, --distance between each icon
|
|
backdrop = {},
|
|
backdrop_color = {0, 0, 0, 0.5},
|
|
backdrop_border_color = {0, 0, 0, 1},
|
|
anchor = {side = 6, x = 2, y = 0},
|
|
grow_direction = 1, --1 = to right 2 = to left
|
|
center_alignment = false, --if true if will align the icons with grow_direction and then set the iconRow width to match the length used by all icons
|
|
|
|
show_cooldown = false,
|
|
surpress_tulla_omni_cc = false,
|
|
decimal_timer = false, --nop, not in use
|
|
cooldown_reverse = false,
|
|
cooldown_swipe_enabled = true,
|
|
cooldown_edge_texture = "Interface\\Cooldown\\edge",
|
|
cooldown_max_brightness = 0.7,
|
|
surpress_blizzard_cd_timer = false,
|
|
on_tick_cooldown_update = true, --nop, not in use
|
|
|
|
show_horizontal_swipe = true,
|
|
swipe_progressive_color = true,
|
|
swipe_alpha = 0.5,
|
|
swipe_brightness = 0.5,
|
|
swipe_color = {0, 0, 0}, --this variable is having conflicts because it's in use by other things
|
|
swipe_color_start = {0, 1, 0},
|
|
swipe_color_end = {1, 0, 0},
|
|
|
|
remove_on_finish = false,
|
|
first_icon_use_anchor = false,
|
|
}
|
|
|
|
|
|
|
|
---@param parent frame
|
|
---@param name string?
|
|
---@param options table?
|
|
---@return df_iconrow_generic
|
|
function detailsFramework:CreateIconRowGeneric(parent, name, options)
|
|
local newIconRowFrame = CreateFrame("frame", name, parent, "BackdropTemplate")
|
|
newIconRowFrame.IconPool = {}
|
|
newIconRowFrame.NextIcon = 1
|
|
newIconRowFrame.AuraCache = {}
|
|
newIconRowFrame.shownAmount = 0
|
|
|
|
detailsFramework:Mixin(newIconRowFrame, detailsFramework.IconGenericMixin)
|
|
detailsFramework:Mixin(newIconRowFrame, detailsFramework.OptionsFunctions)
|
|
|
|
newIconRowFrame:BuildOptionsTable(default_iconrow_generic_options, options)
|
|
|
|
newIconRowFrame:SetSize(1, 1)
|
|
|
|
newIconRowFrame:SetBackdrop(newIconRowFrame.options.backdrop)
|
|
newIconRowFrame:SetBackdropColor(unpack(newIconRowFrame.options.backdrop_color))
|
|
newIconRowFrame:SetBackdropBorderColor(unpack(newIconRowFrame.options.backdrop_border_color))
|
|
|
|
return newIconRowFrame
|
|
end
|