--- **LibCandyBar-3.0** provides elegant timerbars with icons for use in addons.
-- It is based of the original ideas of the CandyBar and CandyBar-2.0 library.
-- In contrary to the earlier libraries LibCandyBar-3.0 provides you with a timerbar object with a simple API.
--
-- Creating a new timerbar using the ':New' function will return a new timerbar object. This timerbar object inherits all of the barPrototype functions listed here. \\
--
-- @usage
-- local candy = LibStub("LibCandyBar-3.0")
-- local texture = "Interface\\AddOns\\MyAddOn\\statusbar"
-- local mybar = candy:New(texture, 100, 16)
-- mybar:SetLabel("Yay!")
-- mybar:SetDuration(60)
-- mybar:Start()
-- @class file
-- @name LibCandyBar-3.0
local GetTime , floor , next = GetTime , floor , next
local CreateFrame , error , setmetatable , UIParent = CreateFrame , error , setmetatable , UIParent
if not LibStub then error ( " LibCandyBar-3.0 requires LibStub. " ) end
local cbh = LibStub : GetLibrary ( " CallbackHandler-1.0 " )
if not cbh then error ( " LibCandyBar-3.0 requires CallbackHandler-1.0 " ) end
local lib = LibStub : NewLibrary ( " LibCandyBar-3.0 " , 99 ) -- Bump minor on changes
if not lib then return end
lib.callbacks = lib.callbacks or cbh : New ( lib )
local cb = lib.callbacks
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.barCache = lib.barCache or { }
local barPrototype = lib.barPrototype
local barPrototype_meta = lib.barPrototype_mt
local barCache = lib.barCache
local scripts = {
" OnUpdate " , " OnDragStart " , " OnDragStop " ,
" OnEnter " , " OnLeave " , " OnHide " ,
" OnShow " , " OnMouseDown " , " OnMouseUp " ,
" OnMouseWheel " , " OnSizeChanged " , " OnEvent "
}
local numScripts = # scripts
local GameFontHighlightSmallOutline = GameFontHighlightSmallOutline
local _fontName , _fontSize = GameFontHighlightSmallOutline : GetFont ( )
local _fontShadowX , _fontShadowY = GameFontHighlightSmallOutline : GetShadowOffset ( )
local _fontShadowR , _fontShadowG , _fontShadowB , _fontShadowA = GameFontHighlightSmallOutline : GetShadowColor ( )
local SetWidth , SetHeight , SetSize = lib.dummyFrame . SetWidth , lib.dummyFrame . SetHeight , lib.dummyFrame . SetSize
local function stopBar ( bar )
bar.updater : Stop ( )
bar.data = nil
bar.funcs = nil
bar.running = nil
bar.paused = nil
bar : Hide ( )
bar : SetParent ( UIParent )
end
local tformat1 = " %d:%02d:%02d "
local tformat2 = " %d:%02d "
local tformat3 = " %.1f "
local tformat4 = " %.0f "
local function barUpdate ( updater )
local bar = updater.parent
local t = GetTime ( )
if t >= bar.exp then
bar : Stop ( )
else
local time = bar.exp - t
bar.remaining = time
bar.candyBarBar : SetValue ( bar.fill and ( t - bar.start ) + bar.gap or time )
if time > 3599.9 then -- > 1 hour
local h = floor ( time / 3600 )
local m = floor ( ( time - ( h * 3600 ) ) / 60 )
local s = ( time - ( m * 60 ) ) - ( h * 3600 )
bar.candyBarDuration : SetFormattedText ( tformat1 , h , m , s )
elseif time > 59.9 then -- 1 minute to 1 hour
local m = floor ( time / 60 )
local s = time - ( m * 60 )
bar.candyBarDuration : SetFormattedText ( tformat2 , m , s )
elseif time < 10 then -- 0 to 10 seconds
bar.candyBarDuration : SetFormattedText ( tformat3 , time )
else -- 10 seconds to one minute
bar.candyBarDuration : SetFormattedText ( tformat4 , time )
end
if bar.funcs then
for i = 1 , # bar.funcs do
bar.funcs [ i ] ( bar )
end
end
end
end
local atformat1 = " ~%d:%02d:%02d "
local atformat2 = " ~%d:%02d "
local atformat3 = " ~%.1f "
local atformat4 = " ~%.0f "
local function barUpdateApprox ( updater )
local bar = updater.parent
local t = GetTime ( )
if t >= bar.exp then
bar : Stop ( )
else
local time = bar.exp - t
bar.remaining = time
bar.candyBarBar : SetValue ( bar.fill and ( t - bar.start ) + bar.gap or time )
if time > 3599.9 then -- > 1 hour
local h = floor ( time / 3600 )
local m = floor ( ( time - ( h * 3600 ) ) / 60 )
local s = ( time - ( m * 60 ) ) - ( h * 3600 )
bar.candyBarDuration : SetFormattedText ( atformat1 , h , m , s )
elseif time > 59.9 then -- 1 minute to 1 hour
local m = floor ( time / 60 )
local s = time - ( m * 60 )
bar.candyBarDuration : SetFormattedText ( atformat2 , m , s )
elseif time < 10 then -- 0 to 10 seconds
bar.candyBarDuration : SetFormattedText ( atformat3 , time )
else -- 10 seconds to one minute
bar.candyBarDuration : SetFormattedText ( atformat4 , time )
end
if bar.funcs then
for i = 1 , # bar.funcs do
bar.funcs [ i ] ( bar )
end
end
end
end
-- ------------------------------------------------------------------------------
-- Bar functions
--
local function restyleBar ( self )
if not self.running then return end
self.candyBarIconFrame : ClearAllPoints ( )
self.candyBarBar : ClearAllPoints ( )
-- In the past we used a :GetTexture check here, but as of WoW v5 it randomly returns nil, so use our own trustworthy variable.
if self.candyBarIconFrame . icon then
self.candyBarIconFrame : SetWidth ( self.height )
if self.iconPosition == " RIGHT " then
self.candyBarIconFrame : SetPoint ( " TOPRIGHT " , self )
self.candyBarIconFrame : SetPoint ( " BOTTOMRIGHT " , self )
self.candyBarBar : SetPoint ( " TOPRIGHT " , self.candyBarIconFrame , " TOPLEFT " )
self.candyBarBar : SetPoint ( " BOTTOMRIGHT " , self.candyBarIconFrame , " BOTTOMLEFT " )
self.candyBarBar : SetPoint ( " TOPLEFT " , self )
self.candyBarBar : SetPoint ( " BOTTOMLEFT " , self )
else
self.candyBarIconFrame : SetPoint ( " TOPLEFT " )
self.candyBarIconFrame : SetPoint ( " BOTTOMLEFT " )
self.candyBarBar : SetPoint ( " TOPLEFT " , self.candyBarIconFrame , " TOPRIGHT " )
self.candyBarBar : SetPoint ( " BOTTOMLEFT " , self.candyBarIconFrame , " BOTTOMRIGHT " )
self.candyBarBar : SetPoint ( " TOPRIGHT " , self )
self.candyBarBar : SetPoint ( " BOTTOMRIGHT " , self )
end
self.candyBarIconFrame : Show ( )
else
self.candyBarBar : SetPoint ( " TOPLEFT " , self )
self.candyBarBar : SetPoint ( " BOTTOMRIGHT " , self )
self.candyBarIconFrame : Hide ( )
end
if self.showLabel and self.candyBarLabel . text then
self.candyBarLabel : Show ( )
else
self.candyBarLabel : Hide ( )
end
if self.showTime then
self.candyBarDuration : Show ( )
else
self.candyBarDuration : Hide ( )
end
end
--- Set whether the bar should drain (default) or fill up.
-- @param fill Boolean true/false
function barPrototype : SetFill ( fill )
self.fill = fill
end
--- Adds a function to the timerbar. The function will run every update and will receive the bar as a parameter.
-- @param func Function to run every update.
-- @usage
-- -- The example below will print the time remaining to the chatframe every update. Yes, that's a whole lot of spam
-- mybar:AddUpdateFunction( function(bar) print(bar.remaining) end )
function barPrototype : AddUpdateFunction ( func ) if not self.funcs then self.funcs = { } end ; self.funcs [ # self.funcs + 1 ] = func end
--- Sets user data in the timerbar object.
-- @param key Key to use for the data storage.
-- @param data Data to store.
function barPrototype : Set ( key , data ) if not self.data then self.data = { } end ; self.data [ key ] = data end
--- Retrieves user data from the timerbar object.
-- @param key Key to retrieve
function barPrototype : Get ( key ) return self.data and self.data [ key ] end
--- Sets the color of the bar.
-- This is basically a wrapper to SetStatusBarColor.
-- @paramsig r, g, b, a
-- @param r Red component (0-1)
-- @param g Green component (0-1)
-- @param b Blue component (0-1)
-- @param a Alpha (0-1)
function barPrototype : SetColor ( ... ) self.candyBarBar : SetStatusBarColor ( ... ) end
--- Sets the color of the bar label and bar duration text.
-- @paramsig r, g, b, a
-- @param r Red component (0-1)
-- @param g Green component (0-1)
-- @param b Blue component (0-1)
-- @param a Alpha (0-1)
function barPrototype : SetTextColor ( ... )
self.candyBarLabel : SetTextColor ( ... )
self.candyBarDuration : SetTextColor ( ... )
end
--- Sets the shadow color of the bar label and bar duration text.
-- @paramsig r, g, b, a
-- @param r Red component (0-1)
-- @param g Green component (0-1)
-- @param b Blue component (0-1)
-- @param a Alpha (0-1)
function barPrototype : SetShadowColor ( ... )
self.candyBarLabel : SetShadowColor ( ... )
self.candyBarDuration : SetShadowColor ( ... )
end
--- Sets the texture of the bar.
-- This should only be needed on running bars that get changed on the fly.
-- @param texture Path to the bar texture.
function barPrototype : SetTexture ( texture )
self.candyBarBar : SetStatusBarTexture ( texture )
self.candyBarBackground : SetTexture ( texture )
end
--- Sets the width of the bar.
-- This should only be needed on running bars that get changed on the fly.
-- @param width Width of the bar.
function barPrototype : SetWidth ( width )
self.width = width
SetWidth ( self , width )
end
--- Sets the height of the bar.
-- This should only be needed on running bars that get changed on the fly.
-- @param height Height of the bar.
function barPrototype : SetHeight ( height )
self.height = height
SetHeight ( self , height )
restyleBar ( self )
end
--- Sets the size of the bar.
-- This should only be needed on running bars that get changed on the fly.
-- @param width Width of the bar.
-- @param height Height of the bar.
function barPrototype : SetSize ( width , height )
self.width = width
self.height = height
SetSize ( self , width , height )
restyleBar ( self )
end
--- Returns the label (text) currently set on the bar.
function barPrototype : GetLabel ( )
return self.candyBarLabel . text
end
--- Sets the label on the bar.
-- @param text Label text.
function barPrototype : SetLabel ( text )
self.candyBarLabel . text = text
self.candyBarLabel : SetText ( text )
if text then
self.candyBarLabel : Show ( )
else
self.candyBarLabel : Hide ( )
end
end
--- Returns the icon texture path currently set on the bar, if it has an icon set.
function barPrototype : GetIcon ( )
return self.candyBarIconFrame . icon
end
--- Sets the icon next to the bar.
-- @param icon Path to the icon texture or nil to not display an icon.
-- @param ... Optional icon coordinates for texture trimming.
function barPrototype : SetIcon ( icon , ... )
self.candyBarIconFrame . icon = icon
self.candyBarIconFrame : SetTexture ( icon )
if ... then
self.candyBarIconFrame : SetTexCoord ( ... )
else
self.candyBarIconFrame : SetTexCoord ( 0.07 , 0.93 , 0.07 , 0.93 )
end
restyleBar ( self )
end
--- Sets which side of the bar the icon should appear.
-- @param position Position of the icon according to the bar, either "LEFT" or "RIGHT" as a string. Set to "LEFT" by default.
function barPrototype : SetIconPosition ( position )
self.iconPosition = position
restyleBar ( self )
end
--- Sets wether or not the time indicator on the right of the bar should be shown.
-- Time is shown by default.
-- @param bool true to show the time, false/nil to hide the time.
function barPrototype : SetTimeVisibility ( bool )
self.showTime = bool
if bool then
self.candyBarDuration : Show ( )
else
self.candyBarDuration : Hide ( )
end
end
--- Sets wether or not the label on the left of the bar should be shown.
-- label is shown by default.
-- @param bool true to show the label, false/nil to hide the label.
function barPrototype : SetLabelVisibility ( bool )
self.showLabel = bool
if bool then
self.candyBarLabel : Show ( )
else
self.candyBarLabel : Hide ( )
end
end
--- Sets the duration of the bar.
-- This can also be used while the bar is running to adjust the time remaining, within the bounds of the original duration.
-- @param duration Duration of the bar in seconds.
-- @param isApprox Boolean. True if you wish the time display to be an approximate "~5" instead of "5"
function barPrototype : SetDuration ( duration , isApprox ) self.remaining = duration ; self.isApproximate = isApprox end
--- Shows the bar and starts it.
-- @param maxValue Number. If you don't wish your bar to start full, you can set a max value. A maxValue of 10 on a bar with a duration of 5 would start it at 50%.
function barPrototype : Start ( maxValue )
self.running = true
local time = self.remaining
self.gap = maxValue and maxValue - time or 0
restyleBar ( self )
self.start = GetTime ( )
self.exp = self.start + time
self.candyBarBar : SetMinMaxValues ( 0 , maxValue or time )
self.candyBarBar : SetValue ( self.fill and 0 or time )
if self.isApproximate then
if time > 3599.9 then -- > 1 hour
local h = floor ( time / 3600 )
local m = floor ( ( time - ( h * 3600 ) ) / 60 )
local s = ( time - ( m * 60 ) ) - ( h * 3600 )
self.candyBarDuration : SetFormattedText ( atformat1 , h , m , s )
elseif time > 59.9 then -- 1 minute to 1 hour
local m = floor ( time / 60 )
local s = time - ( m * 60 )
self.candyBarDuration : SetFormattedText ( atformat2 , m , s )
elseif time < 10 then -- 0 to 10 seconds
self.candyBarDuration : SetFormattedText ( atformat3 , time )
else -- 10 seconds to one minute
self.candyBarDuration : SetFormattedText ( atformat4 , time )
end
self.updater : SetScript ( " OnLoop " , barUpdateApprox )
else
if time > 3599.9 then -- > 1 hour
local h = floor ( time / 3600 )
local m = floor ( ( time - ( h * 3600 ) ) / 60 )
local s = ( time - ( m * 60 ) ) - ( h * 3600 )
self.candyBarDuration : SetFormattedText ( tformat1 , h , m , s )
elseif time > 59.9 then -- 1 minute to 1 hour
local m = floor ( time / 60 )
local s = time - ( m * 60 )
self.candyBarDuration : SetFormattedText ( tformat2 , m , s )
elseif time < 10 then -- 0 to 10 seconds
self.candyBarDuration : SetFormattedText ( tformat3 , time )
else -- 10 seconds to one minute
self.candyBarDuration : SetFormattedText ( tformat4 , time )
end
self.updater : SetScript ( " OnLoop " , barUpdate )
end
self.updater : Play ( )
self : Show ( )
end
--- Pauses a running bar
function barPrototype : Pause ( )
if not self.paused then
self.updater : Pause ( )
self.paused = GetTime ( )
end
end
--- Resumes a paused bar
function barPrototype : Resume ( )
if self.paused then
local t = GetTime ( )
self.exp = t + self.remaining
self.start = self.start + ( t - self.paused )
self.updater : Play ( )
self.paused = nil
end
end
--- Stops the bar.
-- This will stop the bar, fire the LibCandyBar_Stop callback, and recycle the bar into the candybar pool.
-- Note: make sure you remove all references to the bar in your addon upon receiving the LibCandyBar_Stop callback.
-- @usage
-- -- The example below shows the use of the LibCandyBar_Stop callback by printing the contents of the label in the chatframe
-- local function barstopped( callback, bar )
-- print( bar:GetLabel(), "stopped")
-- end
-- LibStub("LibCandyBar-3.0"):RegisterCallback(myaddonobject, "LibCandyBar_Stop", barstopped)
-- @param ... Optional args to pass across in the LibCandyBar_Stop callback.
function barPrototype : Stop ( ... )
cb : Fire ( " LibCandyBar_Stop " , self , ... )
stopBar ( self )
barCache [ self ] = true
end
-- ------------------------------------------------------------------------------
-- Library functions
--
--- Creates a new timerbar object and returns it. Don't forget to set the duration, label and :Start the timer bar after you get a hold of it!
-- @paramsig texture, width, height
-- @param texture Path to the texture used for the bar.
-- @param width Width of the bar.
-- @param height Height of the bar.
-- @usage
-- mybar = LibStub("LibCandyBar-3.0"):New("Interface\\AddOns\\MyAddOn\\media\\statusbar", 100, 16)
function lib : New ( texture , width , height )
local bar = next ( barCache )
if not bar then
local frame = CreateFrame ( " Frame " , nil , UIParent )
bar = setmetatable ( frame , barPrototype_meta )
local icon = bar : CreateTexture ( )
icon : SetPoint ( " TOPLEFT " )
icon : SetPoint ( " BOTTOMLEFT " )
icon : Show ( )
bar.candyBarIconFrame = icon
local statusbar = CreateFrame ( " StatusBar " , nil , bar )
statusbar : SetPoint ( " TOPRIGHT " )
statusbar : SetPoint ( " BOTTOMRIGHT " )
bar.candyBarBar = statusbar
local bg = statusbar : CreateTexture ( nil , " BACKGROUND " )
bg : SetAllPoints ( )
bar.candyBarBackground = bg
local backdrop = CreateFrame ( " Frame " , nil , bar , BackdropTemplateMixin and " BackdropTemplate " ) -- Used by bar stylers for backdrops
backdrop : SetFrameLevel ( 0 )
bar.candyBarBackdrop = backdrop
local iconBackdrop = CreateFrame ( " Frame " , nil , bar , BackdropTemplateMixin and " BackdropTemplate " ) -- Used by bar stylers for backdrops
iconBackdrop : SetFrameLevel ( 0 )
bar.candyBarIconFrameBackdrop = iconBackdrop
local duration = statusbar : CreateFontString ( nil , " OVERLAY " , GameFontHighlightSmallOutline )
duration : SetPoint ( " TOPLEFT " , statusbar , " TOPLEFT " , 2 , 0 )
duration : SetPoint ( " BOTTOMRIGHT " , statusbar , " BOTTOMRIGHT " , - 2 , 0 )
bar.candyBarDuration = duration
local label = statusbar : CreateFontString ( nil , " OVERLAY " , GameFontHighlightSmallOutline )
label : SetPoint ( " TOPLEFT " , statusbar , " TOPLEFT " , 2 , 0 )
label : SetPoint ( " BOTTOMRIGHT " , statusbar , " BOTTOMRIGHT " , - 2 , 0 )
bar.candyBarLabel = label
local updater = bar : CreateAnimationGroup ( )
updater : SetLooping ( " REPEAT " )
updater.parent = bar
local anim = updater : CreateAnimation ( )
anim : SetDuration ( 0.04 )
bar.updater = updater
bar.repeater = anim
else
barCache [ bar ] = nil
end
bar : SetFrameStrata ( " MEDIUM " )
bar : SetFrameLevel ( 100 ) -- Lots of room to create above or below this level
bar.candyBarBar : SetStatusBarTexture ( texture )
bar.candyBarBackground : SetTexture ( texture )
bar.width = width
bar.height = height
-- RESET ALL THE THINGS!
bar.fill = nil
bar.showTime = true
bar.showLabel = true
bar.iconPosition = nil
for i = 1 , numScripts do -- Update if scripts table is changed, faster than doing #scripts
bar : SetScript ( scripts [ i ] , nil )
end
bar.candyBarBackground : SetVertexColor ( 0.5 , 0.5 , 0.5 , 0.3 )
bar.candyBarBar : SetStatusBarColor ( 0.5 , 0.5 , 0.5 , 1 )
bar : ClearAllPoints ( )
SetWidth ( bar , width )
SetHeight ( bar , height )
bar : SetMovable ( 1 )
bar : SetScale ( 1 )
bar : SetAlpha ( 1 )
bar : SetClampedToScreen ( false )
bar : EnableMouse ( false )
bar.candyBarLabel : SetTextColor ( 1 , 1 , 1 , 1 )
bar.candyBarLabel : SetJustifyH ( " LEFT " )
bar.candyBarLabel : SetJustifyV ( " MIDDLE " )
bar.candyBarLabel : SetFont ( _fontName , _fontSize )
bar.candyBarLabel : SetShadowOffset ( _fontShadowX , _fontShadowY )
bar.candyBarLabel : SetShadowColor ( _fontShadowR , _fontShadowG , _fontShadowB , _fontShadowA )
bar.candyBarDuration : SetTextColor ( 1 , 1 , 1 , 1 )
bar.candyBarDuration : SetJustifyH ( " RIGHT " )
bar.candyBarDuration : SetJustifyV ( " MIDDLE " )
bar.candyBarDuration : SetFont ( _fontName , _fontSize )
bar.candyBarDuration : SetShadowOffset ( _fontShadowX , _fontShadowY )
bar.candyBarDuration : SetShadowColor ( _fontShadowR , _fontShadowG , _fontShadowB , _fontShadowA )
bar : SetLabel ( )
bar : SetIcon ( )
bar : SetDuration ( )
return bar
end