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.

1779 lines
46 KiB

local MAJOR = "LibBars-1.0"
local MINOR = 90000 + tonumber(("$Revision: 24 $"):match("%d+")) -- Rarity changed this version to 24 to force an upgrade
local lib, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
if not lib then return end -- No Upgrade needed.
local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0")
local GetTime = _G.GetTime
local sin, cos, rad = _G.math.sin, _G.math.cos, _G.math.rad
local abs, min, max, floor = _G.math.abs, _G.math.min, _G.math.max, _G.math.floor
local table_sort, tinsert, tremove, tconcat = _G.table.sort, tinsert, tremove, _G.table.concat
local next, pairs, assert, error, type, xpcall = next, pairs, assert, error, type, xpcall
--[[
xpcall safecall implementation
]]
local function errorhandler(err)
return geterrorhandler()(err)
end
local function CreateDispatcher(argCount)
local code = [[
local xpcall, eh = ...
local method, ARGS
local function call() return method(ARGS) end
local function dispatch(func, ...)
method = func
if not method then return end
ARGS = ...
return xpcall(call, eh)
end
return dispatch
]]
local ARGS = {}
for i = 1, argCount do ARGS[i] = "arg"..i end
code = code:gsub("ARGS", tconcat(ARGS, ", "))
return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler)
end
local Dispatchers = setmetatable({}, {__index=function(self, argCount)
local dispatcher = CreateDispatcher(argCount)
rawset(self, argCount, dispatcher)
return dispatcher
end})
Dispatchers[0] = function(func)
return xpcall(func, errorhandler)
end
local function safecall(func, ...)
-- we check to see if the func is passed is actually a function here and don't error when it isn't
-- this safecall is used for optional functions like OnInitialize OnEnable etc. When they are not
-- present execution should continue without hinderance
if type(func) == "function" then
return Dispatchers[select('#', ...)](func, ...)
end
end
local dummyFrame, barFrameMT, barPrototype, barPrototype_mt, barListPrototype
local barListPrototype_mt
lib.LEFT_TO_RIGHT = 1
lib.BOTTOM_TO_TOP = 2
lib.RIGHT_TO_LEFT = 3
lib.TOP_TO_BOTTOM = 4
lib.dummyFrame = lib.dummyFrame or CreateFrame("Frame")
lib.barFrameMT = lib.barFrameMT or {__index = lib.dummyFrame}
lib.barPrototype = lib.barPrototype or setmetatable({}, lib.barFrameMT)
lib.barPrototype_mt = lib.barPrototype_mt or {__index = lib.barPrototype}
lib.barListPrototype = lib.barListPrototype or setmetatable({}, lib.barFrameMT)
lib.barListPrototype_mt = lib.barListPrototype_mt or {__index = lib.barListPrototype}
dummyFrame = lib.dummyFrame
barFrameMT = lib.barFrameMT
barPrototype = lib.barPrototype
barPrototype_mt = lib.barPrototype_mt
barListPrototype = lib.barListPrototype
barListPrototype_mt = lib.barListPrototype_mt
barPrototype.prototype = barPrototype
barPrototype.metatable = barPrototype_mt
barPrototype.super = dummyFrame
barListPrototype.prototype = barListPrototype
barListPrototype.metatable = barListPrototype_mt
barListPrototype.super = dummyFrame
lib.bars = lib.bars or {}
lib.barLists = lib.barLists or {}
lib.recycledBars = lib.recycledBars or {}
lib.embeds = lib.embeds or {}
local bars = lib.bars
local barLists = lib.barLists
local recycledBars = lib.recycledBars
local frame_defaults = {
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
inset = 4,
edgeSize = 8,
tile = true,
insets = {left = 2, right = 2, top = 2, bottom = 2}
}
do
local mixins = { "NewCounterBar", "NewTimerBar", "NewBarFromPrototype", "GetBar", "GetBars", "HasBar", "IterateBars", "NewBarGroup", "ReleaseBar", "GetBarGroup", "GetBarGroups" }
function lib:Embed(target)
for k, v in pairs( mixins ) do
target[v] = self[v]
end
lib.embeds[target] = true
return target
end
end
local ComputeGradient
do
local new, del
do
local list = lib.garbageList or setmetatable({}, {__mode='k'})
lib.garbageList = list
-- new is always called with the exact same arguments, no need to
-- iterate over a vararg
function new(a1, a2, a3, a4, a5)
local t = next(list)
if t then
list[t] = nil
t[1] = a1
t[2] = a2
t[3] = a3
t[4] = a4
t[5] = a5
else
t = {a1, a2, a3, a4, a5}
end
return t
end
-- del is called over the same tables produced from new, no need for
-- fancy stuff
function del(t)
t[1] = nil
t[2] = nil
t[3] = nil
t[4] = nil
t[5] = nil
t[''] = true
t[''] = nil
list[t] = true
return nil
end
end
local function sort_colors(a, b)
return a[1] < b[1]
end
local colors = {}
local function getColor(point)
local lowerBound = colors[1]
local upperBound = colors[#colors]
local lowerBoundIndex, upperBoundIndex = 0, 1
for i = 1, #colors do
if colors[i][1] >= point then
if i > 1 then
lowerBound = colors[i-1]
lowerBoundIndex = colors[i-1][1]
end
upperBound = colors[i]
upperBoundIndex = colors[i][1]
break
end
end
local diff = (upperBoundIndex - lowerBoundIndex)
local pct = 1
if diff ~= 0 then
pct = (point - lowerBoundIndex) / diff
end
local r = lowerBound[2] + ((upperBound[2] - lowerBound[2]) * pct)
local g = lowerBound[3] + ((upperBound[3] - lowerBound[3]) * pct)
local b = lowerBound[4] + ((upperBound[4] - lowerBound[4]) * pct)
local a = lowerBound[5] + ((upperBound[5] - lowerBound[5]) * pct)
return r, g, b, a
end
function ComputeGradient(self)
self.gradMap = self.gradMap or {}
if not self.colors then return end
if #self.colors == 0 then
for k in pairs(self.gradMap) do
self.gradMap[k] = nil
end
return
end
for i = 1, #colors do
del(tremove(colors))
end
for i = 1, #self.colors, 5 do
tinsert(colors, new(self.colors[i], self.colors[i+1], self.colors[i+2], self.colors[i+3], self.colors[i+4]))
end
table_sort(colors, sort_colors)
for i = 0, 200 do
local r, g, b, a = getColor(i / 200)
self.gradMap[(i*4)] = r
self.gradMap[(i*4)+1] = g
self.gradMap[(i*4)+2] = b
self.gradMap[(i*4)+3] = a
end
end
end
function lib:GetBar(name)
return bars[self] and bars[self][name]
end
function lib:GetBars(name)
return bars[self]
end
function lib:HasAnyBar()
return not not (bars[self] and next(bars[self]))
end
do
local function NOOP() end
function lib:IterateBars()
if bars[self] then
return pairs(bars[self])
else
return NOOP
end
end
end
-- Convenient method to create a new, empty bar prototype
function lib:NewBarPrototype(super)
assert(super == nil or (type(super) == "table" and type(super.metatable) == "table"),
"!NewBarPrototype: super must either be nil or a valid prototype")
super = super or barPrototype
local prototype = setmetatable({}, super.metatable)
prototype.prototype = prototype
prototype.super = super
prototype.metatable = { __index = prototype }
return prototype
end
--[[ Individual bars ]]--
function lib:NewBarFromPrototype(prototype, name, ...)
assert(self ~= lib, "You may only call :NewBar as an embedded function")
assert(type(prototype) == "table" and type(prototype.metatable) == "table", "Invalid bar prototype")
bars[self] = bars[self] or {}
local bar = bars[self][name]
local isNew = false
if not bar then
isNew = true
bar = tremove(recycledBars)
if not bar then
bar = CreateFrame("Frame")
else
bar:Show()
end
end
bar = setmetatable(bar, prototype.metatable)
bar.name = name
bar:Create(...)
bar:SetFont(self.font, self.fontSize, self.fontFlags)
bars[self][name] = bar
return bar, isNew
end
function lib:NewCounterBar(name, text, value, maxVal, icon, orientation, length, thickness, isTimer)
return self:NewBarFromPrototype(barPrototype, name, text, value, maxVal, icon, orientation, length, thickness, isTimer)
end
function lib:NewTimerBar(name, text, time, maxTime, icon, orientation,length, thickness)
return self:NewBarFromPrototype(barPrototype, name, text, time, maxTime, icon, orientation, length, thickness, true)
end
function lib:ReleaseBar(name)
if not bars[self] then return end
local bar
if type(name) == "string" then
bar = bars[self][name]
elseif type(name) == "table" then
if name.name and bars[self][name.name] == name then
bar = name
end
end
if bar then
bar:OnBarReleased()
bars[self][bar.name] = nil
tinsert(recycledBars, bar)
end
end
---[[ Bar Groups ]]---
do
local function move(self)
if not self:GetParent().locked then
self.startX = self:GetParent():GetLeft()
self.startY = self:GetParent():GetTop()
self:GetParent():StartMoving()
end
end
local function stopMove(self)
if not self:GetParent().locked then
self:GetParent():StopMovingOrSizing()
local endX = self:GetParent():GetLeft()
local endY = self:GetParent():GetTop()
if self.startX ~= endX or self.startY ~= endY then
self:GetParent().callbacks:Fire("AnchorMoved", self:GetParent(), endX, endY)
end
end
end
local function buttonClick(self, button)
self:GetParent().callbacks:Fire("AnchorClicked", self:GetParent(), button)
end
local DEFAULT_TEXTURE = [[Interface\TARGETINGFRAME\UI-StatusBar]]
function lib:NewBarGroup(name, orientation, length, thickness, frameName)
if self == lib then
error("You may only call :NewBarGroup as an embedded function")
end
barLists[self] = barLists[self] or {}
if barLists[self][name] then
error("A bar list named " .. name .. " already exists.")
end
orientation = orientation or lib.LEFT_TO_RIGHT
orientation = orientation == "LEFT" and lib.LEFT_TO_RIGHT or orientation
orientation = orientation == "RIGHT" and lib.RIGHT_TO_LEFT or orientation
local list = setmetatable(CreateFrame("Frame", frameName, UIParent, BackdropTemplateMixin and "BackdropTemplate"), barListPrototype_mt)
list:SetMovable(true)
list:SetClampedToScreen(true)
list.callbacks = list.callbacks or CallbackHandler:New(list)
barLists[self][name] = list
list.name = name
-- list:SetBackdrop({
-- bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
-- edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
-- inset = 0,
-- edgeSize = 12,
-- tile = true
-- })
list.button = CreateFrame("Button", nil, list, BackdropTemplateMixin and "BackdropTemplate")
list.button:SetBackdrop(frame_defaults)
list.button:SetNormalFontObject(ChatFontSmall)
list.length = length or 200
list.thickness = thickness or 15
list:SetOrientation(orientation)
list:UpdateOrientationLayout()
list.button:SetScript("OnMouseDown", move)
list.button:SetScript("OnMouseUp", stopMove)
list.button:SetBackdropColor(0,0,0,1)
list.button:RegisterForClicks("LeftButtonUp", "RightButtonUp", "MiddleButtonUp", "Button4Up", "Button5Up")
list.button:SetScript("OnClick", buttonClick)
list:SetPoint("TOPLEFT", UIParent, "CENTER")
list:ReverseGrowth(false)
list.showIcon = true
list.showLabel = true
list.showTimerLabel = true
list.lastBar = list
list.locked = false
list.texture = DEFAULT_TEXTURE
list.spacing = 0
return list
end
end
function lib:GetBarGroups()
return barLists[self]
end
function lib:GetBarGroup(name)
return barLists[self] and barLists[self][name]
end
--[[ BarList prototype ]]--
function barListPrototype:NewBarFromPrototype(prototype, ...)
local bar, isNew = lib.NewBarFromPrototype(self, prototype, ...)
bar:SetTexture(self.texture)
bar:SetFill(self.fill)
-- if isNew then bar:SetValue(0) end
if self.showIcon then bar:ShowIcon() else bar:HideIcon(bar) end
if self.showLabel then bar:ShowLabel() else bar:HideLabel(bar) end
if self.showTimerLabel then bar:ShowTimerLabel() else bar:HideTimerLabel(bar) end
self:SortBars()
bar.ownerGroup = self
bar.RegisterCallback(self, "FadeFinished")
bar.RegisterCallback(self, "TimerFinished")
bar:SetParent(self)
return bar, isNew
end
function barListPrototype:SetWidth(width)
if self:IsVertical() then
self:SetThickness(width)
else
self:SetLength(width)
end
end
function barListPrototype:SetHeight(height)
if self:IsVertical() then
self:SetLength(height)
else
self:SetThickness(height)
end
end
function barListPrototype:NewCounterBar(name, text, value, maxVal, icon, isTimer)
return self:NewBarFromPrototype(barPrototype, name, text, value, maxVal, icon, self.orientation, self.length, self.thickness, isTimer)
end
local function startFlashing(bar, time)
if not bar.flashing then
bar:Flash(bar.ownerGroup.flashPeriod)
end
end
function barListPrototype:NewTimerBar(name, text, time, maxTime, icon, flashTrigger)
local bar, isNew = self:NewBarFromPrototype(barPrototype, name, text, time, maxTime, icon, self.orientation, self.length, self.thickness, true)
bar:RegisterTimeLeftTrigger(flashTrigger or bar.ownerGroup.flashTrigger or 5, startFlashing)
return bar, isNew
end
function barListPrototype:Lock()
self.locked = true
end
function barListPrototype:Unlock()
self.locked = false
end
function barListPrototype:IsLocked()
return self.locked
end
-- Max number of bars to display. nil to display all.
function barListPrototype:SetMaxBars(num)
self.maxBars = num
end
function barListPrototype:GetMaxBars()
return self.maxBars
end
function barListPrototype:SetFlashTrigger(t)
self.flashTrigger = t
end
function barListPrototype:SetFlashPeriod(p)
self.flashPeriod = p
end
function barListPrototype:SetTexture(tex)
self.texture = tex
if bars[self] then
for k, v in pairs(bars[self]) do
v:SetTexture(tex)
end
end
end
function barListPrototype:SetFont(f, s, m)
self.font, self.fontSize, self.fontFlags = f, s, m
if bars[self] then
for k, v in pairs(bars[self]) do
v:SetFont(f, s, m)
end
end
end
function barListPrototype:SetFill(fill)
self.fill = fill
if bars[self] then
for k, v in pairs(bars[self]) do
v:SetFill(fill)
end
end
end
function barListPrototype:IsFilling()
return self.fill
end
function barListPrototype:ShowIcon()
self.showIcon = true
if not bars[self] then return end
for name,bar in pairs(bars[self]) do
bar:ShowIcon()
end
end
function barListPrototype:HideIcon()
self.showIcon = false
if not bars[self] then return end
for name, bar in pairs(bars[self]) do
bar:HideIcon()
end
end
function barListPrototype:IsIconShown()
return self.showIcon
end
function barListPrototype:ShowLabel()
self.showLabel = true
for name,bar in pairs(bars[self]) do
bar:ShowLabel()
end
end
function barListPrototype:HideLabel()
self.showLabel = false
for name,bar in pairs(bars[self]) do
bar:HideLabel()
end
end
function barListPrototype:IsLabelShown()
return self.showLabel
end
function barListPrototype:ShowTimerLabel()
self.showTimerLabel = true
for name,bar in pairs(bars[self]) do
bar:ShowTimerLabel()
end
end
function barListPrototype:HideTimerLabel()
self.showTimerLabel = false
for name,bar in pairs(bars[self]) do
bar:HideTimerLabel()
end
end
function barListPrototype:IsValueLabelShown()
return self.showTimerLabel
end
function barListPrototype:SetSpacing(spacing)
self.spacing = spacing
self:SortBars()
end
function barListPrototype:GetSpacing()
return self.spacing
end
barListPrototype.GetBar = lib.GetBar
barListPrototype.GetBars = lib.GetBars
barListPrototype.HasAnyBar = lib.HasAnyBar
barListPrototype.IterateBars = lib.IterateBars
function barListPrototype:MoveBarToGroup(bar, group)
if type(bar) ~= "table" then
bar = bars[self][bar]
end
if not bar then
error("Cannot find bar passed to MoveBarToGroup")
end
bars[group] = bars[group] or {}
if bars[group][bar.name] then
error("Cannot move " .. bar.name .. " to this group; a bar with that name already exists.")
end
for k, v in pairs(bars[self]) do
if v == bar then
bars[self][k] = nil
bar = v
break
end
end
bar:SetParent(group)
bar.ownerGroup = group
bars[group][bar.name] = bar
end
function barListPrototype:RemoveBar(bar)
lib.ReleaseBar(self, bar)
end
function barListPrototype:SetDisplayMax(val)
self.displayMax = val
end
function barListPrototype:UpdateColors()
-- Force a color update on all the bars, particularly the counter bars
if bars[self] then
for k, v in pairs(bars[self]) do
v:UpdateColor()
-- if not v.isTimer then
-- v:UpdateColor()
-- end
end
end
end
function barListPrototype:SetColorAt(at, r, g, b, a)
self.colors = self.colors or {}
tinsert(self.colors, at)
tinsert(self.colors, r)
tinsert(self.colors, g)
tinsert(self.colors, b)
tinsert(self.colors, a)
ComputeGradient(self)
self:UpdateColors()
end
function barListPrototype:UnsetColorAt(at)
if not self.colors then return end
for i = 1, #self.colors, 5 do
if self.colors[i] == at then
for j = 1, 5 do
tremove(self.colors, i)
end
ComputeGradient(self)
self:UpdateColors()
return
end
end
end
function barListPrototype:UnsetAllColors()
if not self.colors then return end
for i = 1, #self.colors do
tremove(self.colors)
end
return
end
function barListPrototype:TimerFinished(evt, bar, name)
bar.ownerGroup.callbacks:Fire("TimerFinished", bar.ownerGroup, bar, name)
bar:Fade()
end
function barListPrototype:FadeFinished(evt, bar, name)
local group = bar.ownerGroup
lib.ReleaseBar(group, bar)
group:SortBars()
end
function barListPrototype:ShowAnchor()
self.button:Show()
self:SortBars()
end
function barListPrototype:HideAnchor()
self.button:Hide()
self:SortBars()
end
function barListPrototype:IsAnchorVisible()
return self.button:IsVisible()
end
function barListPrototype:ToggleAnchor()
if self.button:IsVisible() then
self.button:Hide()
else
self.button:Show()
end
self:SortBars()
end
function barListPrototype:GetBarAttachPoint()
local vertical, growup, lastBar = (self.orientation % 2 == 0), self.growup, self.lastBar
if vertical then
if growup then
return lastBar:GetLeft() - lastBar:GetWidth(), lastBar:GetTop()
else
return lastBar:GetRight() + lastBar:GetWidth(), lastBar:GetTop()
end
else
if growup then
return lastBar:GetLeft(), lastBar:GetTop() + lastBar:GetHeight()
else
return lastBar:GetLeft(), lastBar:GetBottom() - lastBar:GetHeight()
end
end
end
function barListPrototype:ReverseGrowth(reverse)
self.growup = reverse
self.button:ClearAllPoints()
if self.orientation % 2 == 0 then
if reverse then
self.button:SetPoint("TOPRIGHT", self, "TOPRIGHT")
self.button:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT")
else
self.button:SetPoint("TOPLEFT", self, "TOPLEFT")
self.button:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT")
end
else
if reverse then
self.button:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT")
self.button:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT")
else
self.button:SetPoint("TOPLEFT", self, "TOPLEFT")
self.button:SetPoint("TOPRIGHT", self, "TOPRIGHT")
end
end
self:SortBars()
end
function barListPrototype:HasReverseGrowth()
return self.growup
end
function barListPrototype:UpdateOrientationLayout()
local vertical, length, thickness = (self.orientation % 2 == 0), self.length, self.thickness
if vertical then
barListPrototype.super.SetWidth(self, thickness)
barListPrototype.super.SetHeight(self, length)
self.button:SetWidth(thickness)
self.button:SetHeight(length)
else
barListPrototype.super.SetWidth(self, length)
barListPrototype.super.SetHeight(self, thickness)
self.button:SetWidth(length)
self.button:SetHeight(thickness)
end
self.button:SetText(vertical and "" or self.name)
self:ReverseGrowth(self.growup)
-- self.button:SetWidth(vertical and 15 or length)
-- self.button:SetHeight(vertical and length or 15)
-- self:SortBars()
end
function barListPrototype:SetLength(length)
self.length = length
if bars[self] then
for k, v in pairs(bars[self]) do
v:SetLength(length)
end
end
self:UpdateOrientationLayout()
end
function barListPrototype:GetLength()
return self.length
end
function barListPrototype:SetThickness(thickness)
self.thickness = thickness
if bars[self] then
for k, v in pairs(bars[self]) do
v:SetThickness(thickness)
end
end
self:UpdateOrientationLayout()
end
function barListPrototype:GetThickness()
return self.thickness
end
function barListPrototype:SetOrientation(orientation)
self.orientation = orientation
if bars[self] then
for k, v in pairs(bars[self]) do
v:SetOrientation(orientation)
end
end
self:UpdateOrientationLayout()
end
function barListPrototype:GetOrientation()
return self.orientation
end
function barListPrototype:IsVertical()
return self.orientation % 2 == 0
end
function barListPrototype:SetSortFunction(func)
assert(type(func) == "function")
self.sortFunc = func
end
-- group:SetSortFunction(group.NOOP) to disable sorting
function barListPrototype.NOOP() end
do
local values = {}
local function sortFunc(a, b)
if a.isTimer ~= b.isTimer then
return a.isTimer
end
local apct, bpct = a.value / a.maxValue, b.value / b.maxValue
if apct == bpct then
if a.maxValue == b.maxValue then
return a.name > b.name
else
return a.maxValue > b.maxValue
end
else
return apct > bpct
end
end
function barListPrototype:SortBars()
local lastBar = self.button:IsVisible() and self.button or self
local ct = 0
if not bars[self] then return end
for k, v in pairs(bars[self]) do
if not v.isAnimating then
ct = ct + 1
values[ct] = v
end
end
for i = ct + 1, #values do
values[i] = nil
end
table_sort(values, self.sortFunc or sortFunc)
local orientation = self.orientation
local vertical = orientation % 2 == 0
local growup = self.growup
local spacing = self.spacing
local from, to
local thickness, showIcon = self.thickness, self.showIcon
local x1, y1, x2, y2 = 0, 0, 0, 0
if vertical then
if growup then
from = "RIGHT"
to = "LEFT"
x1, x2 = -spacing, -spacing
else
from = "LEFT"
to = "RIGHT"
x1, x2 = spacing, spacing
end
else
if growup then
from = "BOTTOM"
to = "TOP"
y1, y2 = spacing, spacing
else
from = "TOP"
to = "BOTTOM"
y1, y2 = -spacing, -spacing
end
end
local totalHeight = 0
for i = 1, #values do
local origTo = to
local v = values[i]
if lastBar == self or lastBar == self.button then
if lastBar == self then
to = from
end
if vertical then
if orientation == 2 then
y1, y2 = 0, (v.showIcon and thickness or 0)
else
y1, y2 = (v.showIcon and -thickness or 0), 0
end
else
if orientation == 1 then
x1, x2 = (v.showIcon and thickness or 0), 0
else
x1, x2 = 0, (v.showIcon and -thickness or 0)
end
end
else
if vertical then
y1, y2 = 0, 0
else
x1, x2 = 0, 0
end
end
v:ClearAllPoints()
if self.maxBars and i > self.maxBars then
v:Hide()
else
v:Show()
if vertical then
totalHeight = totalHeight + v:GetWidth() + x1
v:SetPoint("TOP"..from, lastBar, "TOP"..to, x1, y1)
v:SetPoint("BOTTOM"..from, lastBar, "BOTTOM"..to, x2, y2)
else
totalHeight = totalHeight + v:GetHeight() + y1
v:SetPoint(from.."LEFT", lastBar, to.."LEFT", x1, y1)
v:SetPoint(from.."RIGHT", lastBar, to.."RIGHT", x2, y2)
end
lastBar = v
end
to = origTo
end
self.lastBar = lastBar
-- Todo - use another frame for this; anchoring needs to be left alone
-- if vertical then
-- self.super.SetWidth(self, 20)
-- else
-- self.super.SetHeight(self, 20)
-- end
end
end
--[[
****************************************************************
*** Bar methods
****************************************************************
]]--
--[[ Bar Prototype ]]--
local DEFAULT_ICON = [[Interface\ICONS\INV_Misc_QuestionMark]]
function barPrototype:Create(text, value, maxVal, icon, orientation, length, thickness, isTimer)
self.callbacks = self.callbacks or CallbackHandler:New(self)
self:SetScript("OnSizeChanged", self.OnSizeChanged)
self.texture = self.texture or self:CreateTexture(nil, "ARTWORK")
if self.timeLeftTriggers then
for k, v in pairs(self.timeLeftTriggers) do
self.timeLeftTriggers[k] = false
end
end
if not self.spark then
self.spark = self:CreateTexture(nil, "OVERLAY")
self.spark:SetTexture([[Interface\CastingBar\UI-CastingBar-Spark]])
self.spark:SetWidth(10)
self.spark:SetHeight(10)
self.spark:SetBlendMode("ADD")
end
self.bgtexture = self.bgtexture or self:CreateTexture(nil, "BACKGROUND")
self.bgtexture:SetAllPoints()
self.bgtexture:SetVertexColor(0.3, 0.3, 0.3, 0.6)
self.icon = self.icon or self:CreateTexture(nil, "OVERLAY")
self.icon:SetPoint("LEFT", self, "LEFT", 0, 0)
self:SetIcon(icon or DEFAULT_ICON)
self:ShowIcon()
self.label = self.label or self:CreateFontString(nil, "OVERLAY", "ChatFontNormal")
self.label:SetText(text)
self.label:ClearAllPoints()
self.label:SetPoint("LEFT", self, "LEFT", 3, 0)
self:ShowLabel()
local f, s, m = self.label:GetFont()
self.label:SetFont(f, s or 10, m)
self.timerLabel = self.timerLabel or self:CreateFontString(nil, "OVERLAY", "ChatFontNormal")
self:SetTimerLabel("")
self.timerLabel:ClearAllPoints()
self.timerLabel:SetPoint("RIGHT", self, "RIGHT", -6, 0)
self:HideTimerLabel()
local f, s, m = self.timerLabel:GetFont()
self.timerLabel:SetFont(f, s or 10, m)
self.timerFuncs = self.timerFuncs or {}
for i = 1, #self.timerFuncs do
tremove(self.timerFuncs)
end
self:SetScale(1)
self:SetAlpha(1)
--[[
self.texture:SetAlpha(1)
self.bgtexture:SetAlpha(0.6)
self.icon:SetAlpha(1)
]]--
self.flashing = false
self.length = length or 200
self.thickness = thickness or 15
self:SetOrientation(orientation or 1)
value = value or 1
maxVal = maxVal or value
self.value = value
self.maxValue = maxVal
self.isTimer = isTimer
if not isTimer then
self:SetMaxValue(maxVal)
else
self:SetTimer(value, maxVal)
end
self:SetValue(value)
end
barPrototype.SetWidth = barListPrototype.SetWidth
barPrototype.SetHeight = barListPrototype.SetHeight
function barPrototype:OnBarReleased()
self:StopTimer()
self:StopFlash()
self:StopFade()
self.callbacks:Fire('BarReleased', self, self.name)
-- Reset our attributes
self.isAnimating = false
self.isTimer = false
self.ownerGroup = nil
self.fill = false
if self.colors then
for k, v in pairs(self.colors) do
self.colors[k] = nil
end
end
if self.gradMap then
for k, v in pairs(self.gradMap) do
self.gradMap[k] = nil
end
end
if self.timeLeftTriggers then
for k, v in pairs(self.timeLeftTriggers) do
self.timeLeftTriggers[k] = nil
end
end
-- Reset widget
self.texture:SetVertexColor(1, 1, 1, 0)
self:SetScript("OnUpdate", nil)
self:SetParent(UIParent)
self:ClearAllPoints()
self:Hide()
local f, s, m = ChatFontNormal:GetFont()
self.label:SetFont(f, s or 10, m)
self.timerLabel:SetFont(f, s or 10, m)
-- Cancel all registered callbacks. CBH doesn't seem to provide a method to do this.
if self.callbacks.insertQueue then
for eventname, callbacks in pairs(self.callbacks.insertQueue) do
for k, v in pairs(callbacks) do
callbacks[k] = nil
end
end
end
for eventname, callbacks in pairs(self.callbacks.events) do
for k, v in pairs(callbacks) do
callbacks[k] = nil
end
if self.callbacks.OnUnused then
self.callbacks.OnUnused(self.callbacks, target, eventname)
end
end
end
function barPrototype:GetGroup()
return self.ownerGroup
end
function barPrototype:OnSizeChanged()
self:SetValue(self.value)
end
function barPrototype:SetFont(newFont, newSize, newFlags)
local t, font, size, flags
t = self.label
font, size, flags = t:GetFont()
t:SetFont(newFont or font, newSize or size, newFlags or flags)
t = self.timerLabel
font, size, flags = t:GetFont()
t:SetFont(newFont or font, newSize or size, newFlags or flags)
end
function barPrototype:AddOnUpdate(f)
tinsert(self.timerFuncs, f)
self:SetScript("OnUpdate", self.OnUpdate)
end
function barPrototype:RemoveOnUpdate(f)
local timerFuncs = self.timerFuncs
for i = 1, #timerFuncs do
if f == timerFuncs[i] then
tremove(timerFuncs, i)
if #timerFuncs == 0 then
self:SetScript("OnUpdate", nil)
end
return
end
end
end
function barPrototype.OnUpdate(f, t)
local timerFuncs = f.timerFuncs
for i = 1, #timerFuncs do
local func = timerFuncs[i]
if func then
func(f, t)
end
end
end
function barPrototype:SetIcon(icon)
if icon then
-- Starting in Legion (WoW 7.x), SetTexture can accept texture IDs directly. This translation via GetSpellInfo does not work any longer, especially since GetItemInfo now returns a texture ID.
--if type(icon) == "number" then
-- icon = select(3, GetSpellInfo(icon))
--end
self.icon:SetTexture(icon)
if self.showIcon then
self.icon:Show()
end
else
self.icon:Hide()
end
self.iconTexture = icon or nil
end
function barPrototype:ShowIcon()
self.showIcon = true
if self.iconTexture then
self.icon:Show()
end
end
function barPrototype:HideIcon()
self.showIcon = false
self.icon:Hide()
end
function barPrototype:IsIconShown()
return self.showIcon
end
function barPrototype:OnAnimateFinished()
self.callbacks:Fire("AnimateFinished", self, self.name)
end
local function animate(self, elapsed)
self.aniST = self.aniST + elapsed
local amt = min(1, self.aniST / self.aniT)
local x = self.aniSX + ((self.aniX - self.aniSX) * amt)
local y = self.aniSY + ((self.aniY - self.aniSY) * amt)
local s = self.aniSS + ((self.aniS - self.aniSS) * amt)
self:ClearAllPoints()
self:SetPoint("TOPLEFT", UIParent, "TOPLEFT", x, y)
self:SetScale(s)
if amt == 1 then
self.isAnimating = false
self:RemoveOnUpdate(animate)
safecall(self.OnAnimateFinished, self)
if self.ownerGroup then
self:ClearAllPoints()
self.ownerGroup:SortBars()
self:UpdateColor()
self:SetParent(self.ownerGroup)
self:SetScale(1)
end
end
end
function barPrototype:AnimateTo(x, y, scale, t)
self.isAnimating = true
self.aniSX, self.aniSY, self.aniSS, self.aniST = self:GetLeft(), self:GetTop(), self:GetScale(), 0
self.aniX, self.aniY, self.aniS, self.aniT = x, y, scale, t
self:AddOnUpdate(animate)
animate(0)
end
function barPrototype:AnimateToGroup(group)
self.isAnimating = true
self.ownerGroup:SortBars()
self.ownerGroup:MoveBarToGroup(self, group)
self:SetParent(UIParent)
local x, y = group:GetBarAttachPoint()
x = x / UIParent:GetScale()
y = y / UIParent:GetScale()
self:AnimateTo(x, y, group:GetScale(), 0.75)
end
function barPrototype:SetLabel(text)
self.label:SetText(text)
end
function barPrototype:GetLabel(text)
return self.label:GetText(text)
end
barPrototype.SetText = barPrototype.SetLabel -- for API compatibility
barPrototype.GetText = barPrototype.GetLabel -- for API compatibility
function barPrototype:ShowLabel()
self.showLabel = true
self.label:Show()
end
function barPrototype:HideLabel()
self.showLabel = false
self.label:Hide()
end
function barPrototype:IsLabelShown()
return self.showLabel
end
function barPrototype:SetTimerLabel(text)
self.timerLabel:SetText(text)
end
function barPrototype:GetTimerLabel(text)
return self.timerLabel:GetText(text)
end
function barPrototype:ShowTimerLabel()
self.showTimerLabel = true
self.timerLabel:Show()
end
function barPrototype:HideTimerLabel()
self.showTimerLabel = false
self.timerLabel:Hide()
end
function barPrototype:IsValueLabelShown()
return self.showTimerLabel
end
function barPrototype:SetTexture(texture)
self.texture:SetTexture(texture)
self.bgtexture:SetTexture(texture)
end
-- Added by Ulic
-- Allows for the setting of background colors for a specific bar
-- Someday I'll figure out to do it at the group level
function barPrototype:SetBackgroundColor(r, g, b, a)
a = a or .6
if r and g and b and a then
self.bgtexture:SetVertexColor(r, g, b, a)
end
end
function barPrototype:SetColorAt(at, r, g, b, a)
self.colors = self.colors or {}
tinsert(self.colors, at)
tinsert(self.colors, r)
tinsert(self.colors, g)
tinsert(self.colors, b)
tinsert(self.colors, a)
ComputeGradient(self)
self:UpdateColor()
end
function barPrototype:UnsetColorAt(at)
if not self.colors then return end
for i = 1, #self.colors, 5 do
if self.colors[i] == at then
for j = 1, 5 do
tremove(self.colors, i)
end
ComputeGradient(self)
self:UpdateColor()
return
end
end
end
function barPrototype:UnsetAllColors()
if not self.colors then return end
for i = 1, #self.colors do
tremove(self.colors)
end
end
do
function barPrototype:UpdateOrientationLayout()
local o = self.orientation
local t
if o == lib.LEFT_TO_RIGHT then
self.icon:ClearAllPoints()
self.icon:SetPoint("RIGHT", self, "LEFT", 0, 0)
t = self.spark
t:ClearAllPoints()
t:SetPoint("TOP", self.texture, "TOPRIGHT", 0, 7)
t:SetPoint("BOTTOM", self.texture, "BOTTOMRIGHT", 0, -7)
t:SetTexCoord(0, 1, 0, 1)
t = self.texture
t.SetValue = t.SetWidth
t:ClearAllPoints()
t:SetPoint("TOPLEFT", self, "TOPLEFT")
t:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT")
-- t:SetTexCoord(0, 1, 0, 1)
t = self.timerLabel
t:ClearAllPoints()
t:SetPoint("RIGHT", self, "RIGHT", -6, 0)
t:SetJustifyH("RIGHT")
t:SetJustifyV("MIDDLE")
t = self.label
t:ClearAllPoints()
t:SetPoint("LEFT", self, "LEFT", 6, 0)
t:SetPoint("RIGHT", self.timerLabel, "LEFT", 0, 0)
t:SetJustifyH("LEFT")
t:SetJustifyV("MIDDLE")
self.bgtexture:SetTexCoord(0, 1, 0, 1)
elseif o == lib.BOTTOM_TO_TOP then
self.icon:ClearAllPoints()
self.icon:SetPoint("TOP", self, "BOTTOM", 0, 0)
t = self.spark
t:ClearAllPoints()
t:SetPoint("LEFT", self.texture, "TOPLEFT", -7, 0)
t:SetPoint("RIGHT", self.texture, "TOPRIGHT", 7, 0)
t:SetTexCoord(0, 1, 1, 1, 0, 0, 1, 0)
t = self.texture
t.SetValue = t.SetHeight
t:ClearAllPoints()
t:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT")
t:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT")
-- t:SetTexCoord(0, 1, 1, 1, 0, 0, 1, 0)
t = self.timerLabel
t:ClearAllPoints()
t:SetPoint("TOPLEFT", self, "TOPLEFT", 3, -3)
t:SetPoint("TOPRIGHT", self, "TOPRIGHT", -3, -3)
t:SetJustifyH("CENTER")
t:SetJustifyV("TOP")
t = self.label
t:ClearAllPoints()
t:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", -3, 3)
t:SetPoint("TOPLEFT", self.timerLabel, "BOTTOMLEFT", 0, 0)
t:SetJustifyH("CENTER")
t:SetJustifyV("BOTTOM")
self.bgtexture:SetTexCoord(0, 1, 1, 1, 0, 0, 1, 0)
elseif o == lib.RIGHT_TO_LEFT then
self.icon:ClearAllPoints()
self.icon:SetPoint("LEFT", self, "RIGHT", 0, 0)
t = self.spark
t:ClearAllPoints()
t:SetPoint("TOP", self.texture, "TOPLEFT", 0, 7)
t:SetPoint("BOTTOM", self.texture, "BOTTOMLEFT", 0, -7)
t:SetTexCoord(0, 1, 0, 1)
t = self.texture
t.SetValue = t.SetWidth
t:ClearAllPoints()
t:SetPoint("TOPRIGHT", self, "TOPRIGHT")
t:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT")
-- t:SetTexCoord(0, 1, 0, 1)
t = self.timerLabel
t:ClearAllPoints()
t:SetPoint("LEFT", self, "LEFT", 6, 0)
t:SetJustifyH("LEFT")
t:SetJustifyV("MIDDLE")
t = self.label
t:ClearAllPoints()
t:SetPoint("RIGHT", self, "RIGHT", -6, 0)
t:SetPoint("LEFT", self.timerLabel, "RIGHT", 0, 0)
t:SetJustifyH("RIGHT")
t:SetJustifyV("MIDDLE")
self.bgtexture:SetTexCoord(0, 1, 0, 1)
elseif o == lib.TOP_TO_BOTTOM then
self.icon:ClearAllPoints()
self.icon:SetPoint("BOTTOM", self, "TOP", 0, 0)
t = self.spark
t:ClearAllPoints()
t:SetPoint("LEFT", self.texture, "BOTTOMLEFT", -7, 0)
t:SetPoint("RIGHT", self.texture, "BOTTOMRIGHT", 7, 0)
t:SetTexCoord(0, 1, 1, 1, 0, 0, 1, 0)
t = self.texture
t.SetValue = t.SetHeight
t:ClearAllPoints()
t:SetPoint("TOPLEFT", self, "TOPLEFT")
t:SetPoint("TOPRIGHT", self, "TOPRIGHT")
-- t:SetTexCoord(0, 1, 1, 1, 0, 0, 1, 0)
t = self.timerLabel
t:ClearAllPoints()
t:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT", 3, 3)
t:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", -3, 3)
t:SetJustifyH("CENTER")
t:SetJustifyV("BOTTOM")
t = self.label
t:ClearAllPoints()
t:SetPoint("TOPLEFT", self, "TOPLEFT", 3, -3)
t:SetPoint("BOTTOMRIGHT", self.timerLabel, "TOPRIGHT", 0, 0)
t:SetJustifyH("CENTER")
t:SetJustifyV("TOP")
self.bgtexture:SetTexCoord(0, 1, 1, 1, 0, 0, 1, 0)
end
self:SetValue(self.value or 0)
end
end
function barPrototype:GetLength()
return self.length
end
do
local function updateSize(self)
local vertical, thickness, length = self.orientation % 2 == 0, self.thickness, self.length
local iconSize = self.showIcon and (vertical and length or thickness) or 0
local width = vertical and thickness or max(0.0001, length - iconSize)
local height = vertical and max(0.00001,length - iconSize) or thickness
barPrototype.super.SetWidth(self, width)
barPrototype.super.SetHeight(self, height)
self.icon:SetWidth(thickness)
self.icon:SetHeight(thickness)
end
function barPrototype:SetLength(length)
self.length = length
updateSize(self)
end
function barPrototype:SetThickness(thickness)
self.thickness = thickness
updateSize(self)
end
end
function barPrototype:GetThickness()
return self.thickness
end
function barPrototype:SetOrientation(orientation)
self.orientation = orientation
self:UpdateOrientationLayout()
self:SetThickness(self.thickness)
end
function barPrototype:GetOrientation()
return self.orientation
end
function barPrototype:IsVertical()
return self.orientation % 2 == 0
end
function barPrototype:SetValue(val, maxValue)
assert(val ~= nil, "Value cannot be nil!")
self.value = val
if maxValue ~= nil then
self.maxValue = maxValue
end
if not self.maxValue or val > self.maxValue then
self.maxValue = val
end
local ownerGroup = self.ownerGroup
local displayMax = ownerGroup and ownerGroup.displayMax or self.displayMax
if displayMax then
displayMax = min(displayMax, self.maxValue)
else
displayMax = self.maxValue
end
local amt
if val == 0 then
amt = 0
else
amt = min(1, val / displayMax)
end
if amt == 1 or amt == 0 then
self.spark:Hide()
else
self.spark:Show()
end
local dist = (ownerGroup and ownerGroup:GetLength()) or self.length
self:SetTextureValue(max(amt, 0.000001), dist)
self:UpdateColor()
end
function barPrototype:SetTextureValue(amt, dist)
dist = max(0.0001, dist - (self.showIcon and self.thickness or 0))
local t, o = self.texture, self.orientation
t:SetValue(amt * dist)
if o == 1 then
t:SetTexCoord(0, amt, 0, 1)
elseif o == 2 then
t:SetTexCoord(1 - amt, 1, 1, 1, 1 - amt, 0, 1, 0)
elseif o == 3 then
t:SetTexCoord(1 - amt, 1, 0, 1)
elseif o == 4 then
t:SetTexCoord(0, 1, amt, 1, 0, 0, amt, 0)
end
end
function barPrototype:SetDisplayMax(val)
self.displayMax = val
end
function barPrototype:SetMaxValue(val)
self:SetValue(self.value, val)
end
function barPrototype:RegisterTimeLeftTrigger(time, func)
if time > 0 then
self.timeLeftTriggers = self.timeLeftTriggers or {}
self.timeLeftTriggerFuncs = self.timeLeftTriggerFuncs or {}
self.timeLeftTriggers[time] = false
self.timeLeftTriggerFuncs[time] = func
end
end
function barPrototype:OnTimerStarted()
self.callbacks:Fire("TimerStarted", self, self.name)
end
function barPrototype:OnTimerStopped()
self.callbacks:Fire("TimerStopped", self, self.name)
end
function barPrototype:OnTimerFinished()
self.callbacks:Fire("TimerFinished", self, self.name)
end
function barPrototype:SetTimer(remaining, maxVal)
if not self.isTimer then return end
self:StopFade()
self.maxValue = maxVal or self.maxValue
self:SetValue(self.fill and self.maxValue - remaining or remaining)
self.timerLabel:Show()
self.startTime = GetTime() - (self.maxValue - remaining)
self.lastElapsed = 0
self.updateDelay = min(max(self.maxValue, 1) / self.length, 0.05)
self:UpdateTimer()
if remaining > 0 then
self:RemoveOnUpdate(self.UpdateTimer)
self:AddOnUpdate(self.UpdateTimer)
if not self.isTimerRunning then
self.isTimerRunning = true
safecall(self.OnTimerStarted, self)
end
end
end
function barPrototype:StopTimer()
if self.isTimer and self.isTimerRunning then
self:RemoveOnUpdate(self.UpdateTimer)
self.isTimerRunning = false
safecall(self.OnTimerStopped, self)
end
end
function barPrototype:SetFill(fill)
self.fill = fill
end
function barPrototype:UpdateColor()
local amt = 1
if self.maxValue ~= 0 then
amt = floor(self.value / self.maxValue * 200) * 4
end
local map
if self.gradMap and #self.gradMap > 0 then
map = self.gradMap
elseif self.ownerGroup and self.ownerGroup.gradMap and #self.ownerGroup.gradMap > 0 then
map = self.ownerGroup.gradMap
end
if map then
self.texture:SetVertexColor(map[amt], map[amt+1], map[amt+2], map[amt+3])
end
end
function barPrototype:UpdateTimer(t)
local t = GetTime()
local elapsed, elapsedClamped = t - self.startTime, floor(t) - floor(self.startTime)
self.lastElapsed = self.lastElapsed or 0
if elapsed - self.lastElapsed <= self.updateDelay then
return
end
self.lastElapsed = elapsed
local maxvalue = self.maxValue
local value, valueClamped, remaining, texcoord
if not self.fill then
value = maxvalue - elapsed
remaining = value
valueClamped = maxvalue - elapsedClamped
texcoord = 1 - (elapsed / maxvalue)
else
value = elapsed
remaining = maxvalue - value
valueClamped = elapsedClamped
texcoord = elapsed / maxvalue
end
if self.timeLeftTriggers then
for k, v in pairs(self.timeLeftTriggers) do
if not v and remaining < k then
self.timeLeftTriggers[k] = true
self.timeLeftTriggerFuncs[k](self, k, remaining)
end
end
end
if remaining <= 0 then
self:RemoveOnUpdate(self.UpdateTimer)
self.isTimerRunning = false
safecall(self.OnTimerFinished, self)
end
if valueClamped >= 3600 then
local h, m, s
h = floor(valueClamped / 3600)
m = floor((valueClamped - (h * 3600)) / 60)
s = floor((valueClamped - (h * 3600)) - (m * 60))
self:SetTimerLabel(("%02.0f:%02.0f:%02.0f"):format(h, m, s))
elseif valueClamped >= 60 then
local m, s
m = floor(valueClamped / 60)
s = floor(valueClamped - (m * 60))
self:SetTimerLabel(("%02.0f:%02.0f"):format(m, s))
elseif valueClamped > 10 then
self:SetTimerLabel(("%02.0f"):format(valueClamped))
else
self:SetTimerLabel(("%02.1f"):format(abs(value)))
end
self:SetValue(value)
local o = self.orientation
if o == lib.LEFT_TO_RIGHT then
self.texture:SetTexCoord(0, value/maxvalue, 0, 1)
elseif o == lib.RIGHT_TO_LEFT then
self.texture:SetTexCoord(1-(value/maxvalue), 1, 0, 1)
elseif o == lib.BOTTOM_TO_TOP then
self.texture:SetTexCoord(1-(value/maxvalue), 1, 1, 1, 1-value/maxvalue, 0, 1, 0)
elseif o == lib.TOP_TO_BOTTOM then
self.texture:SetTexCoord(0, 1, value/maxvalue, 1, 0, 0, value/maxvalue, 0)
end
end
function barPrototype:OnFadeStarted()
self.callbacks:Fire("FadeStarted", self, self.name)
end
function barPrototype:OnFadeFinished()
self.callbacks:Fire("FadeFinished", self, self.name)
end
function barPrototype:OnFadeStopped()
self.callbacks:Fire("FadeStopped", self, self.name)
end
do
local function fade(self, elapsed)
self.fadeElapsed = (self.fadeElapsed or 0) + elapsed
self:SetAlpha(self.fadeAlpha * (1 - min(1, max(0, self.fadeElapsed / self.fadeTotal))))
if self.fadeElapsed > self.fadeTotal then
self:RemoveOnUpdate(fade)
self.fadeElapsed, self.fadeTotal, self.fadeAlpha, self.fading = nil, nil, nil, false
safecall(self.OnFadeFinished, self)
end
end
function barPrototype:Fade(t)
if self.fading then return end
self:StopTimer()
self.fading = true
t = t or 0.5
self.fadeTotal = t
self.fadeElapsed = 0
self.fadeAlpha = self.flashAlpha or self:GetAlpha()
self:AddOnUpdate(fade)
fade(self, 0)
safecall(self.OnFadeStarted, self)
end
function barPrototype:StopFade()
if self.fading then
self:RemoveOnUpdate(fade)
self:SetAlpha(self.fadeAlpha)
self.fadeElapsed, self.fadeTotal, self.fadeAlpha, self.fading = nil, nil, nil, false
safecall(self.OnFadeStopped, self)
end
end
function barPrototype:IsFading()
return self.fading
end
end
function barPrototype:OnFlashStarted()
self.callbacks:Fire("FlashStarted", self, self.name)
end
function barPrototype:OnFlashStopped()
self.callbacks:Fire("FlashStopped", self, self.name)
end
do
local TWOPI = _G.math.pi * 2
local function flash(self, t)
self.flashTime = self.flashTime + t
if self.flashTime > TWOPI then
self.flashTime = self.flashTime - TWOPI
if self.flashTimes then
self.flashedTimes = self.flashedTimes + 1
if self.flashedTimes >= self.flashTimes then
self:StopFlash()
end
end
end
local amt = self.flashAlpha * (cos(self.flashTime / self.flashPeriod) + 1) / 2
self:SetAlpha(amt)
end
function barPrototype:Flash(period, times)
self.flashTimes = times
self.flashTime = 0
self.flashedTimes = 0
self.flashPeriod = (period or 1 / 5) or 0.1
if not self.flashing then
self.flashing = true
self.flashAlpha = self.fadeAlpha or self:GetAlpha()
self:SetAlpha(self.flashAlpha)
self:AddOnUpdate(flash)
safecall(self.OnFlashStarted, self)
end
end
function barPrototype:StopFlash()
if self.flashing then
self:SetAlpha(self.flashAlpha)
self.flashing, self.flashAlpha = false, nil
self:RemoveOnUpdate(flash)
safecall(self.OnFlashStopped, self)
end
end
end
--- Finally: upgrade our old embeds
for target, v in pairs(lib.embeds) do
lib:Embed(target)
end