-----------------------------------------------------------------------
-- Upvalued Lua API.
-----------------------------------------------------------------------
local _G = getfenv ( 0 )
-- Functions
local error = _G.error
local pairs = _G.pairs
-- Libraries
local table = _G.table
-----------------------------------------------------------------------
-- Library namespace.
-----------------------------------------------------------------------
local LibStub = _G.LibStub
local MAJOR = " LibToast-1.0 "
_G.assert ( LibStub , MAJOR .. " requires LibStub " )
local MINOR = 15 -- Should be manually increased
local LibToast , previousMinorVersion = LibStub : NewLibrary ( MAJOR , MINOR )
if not LibToast then
return
end -- No upgrade needed
-----------------------------------------------------------------------
-- Migrations.
-----------------------------------------------------------------------
LibToast.active_toasts = LibToast.active_toasts or { }
LibToast.queued_toasts = LibToast.queued_toasts or { }
LibToast.templates = LibToast.templates or { }
LibToast.unique_templates = LibToast.unique_templates or { }
LibToast.button_heap = LibToast.button_heap or { }
LibToast.toast_heap = LibToast.toast_heap or { }
LibToast.addon_names = LibToast.addon_names or { }
LibToast.registered_sink = LibToast.registered_sink
LibToast.sink_icons = LibToast.sink_icons or { }
LibToast.sink_template = LibToast.sink_template or { } -- Cheating here, since users can only use strings.
LibToast.sink_titles = LibToast.sink_titles or { }
-----------------------------------------------------------------------
-- Variables.
-----------------------------------------------------------------------
local CurrentToast
local IsInternalCall
local CallingObject
-----------------------------------------------------------------------
-- Constants.
-----------------------------------------------------------------------
local ActiveToasts = LibToast.active_toasts
local QueuedToasts = LibToast.queued_toasts
local ToastHeap = LibToast.toast_heap
local ButtonHeap = LibToast.button_heap
local ToastProxy = { }
local METHOD_USAGE_FORMAT = MAJOR .. " :%s() - %s. "
local MAX_ACTIVE_TOASTS = 10
local DEFAULT_FADE_HOLD_TIME = 5
local DEFAULT_FADE_IN_TIME = 0.5
local DEFAULT_FADE_OUT_TIME = 1.2
local DEFAULT_TOAST_WIDTH = 250
local DEFAULT_TOAST_HEIGHT = 50
local DEFAULT_GLOW_WIDTH = 252
local DEFAULT_GLOW_HEIGHT = 56
local DEFAULT_ICON_SIZE = 30
local DEFAULT_OS_SPAWN_POINT = _G.IsMacClient ( ) and " TOPRIGHT " or " BOTTOMRIGHT "
local DEFAULT_TOAST_BACKDROP = {
bgFile = [[Interface\FriendsFrame\UI-Toast-Background]] ,
edgeFile = [[Interface\FriendsFrame\UI-Toast-Border]] ,
tile = true ,
tileSize = 12 ,
edgeSize = 12 ,
insets = {
left = 5 ,
right = 5 ,
top = 5 ,
bottom = 5 ,
} ,
}
local DEFAULT_BACKGROUND_COLORS = {
r = 0 ,
g = 0 ,
b = 0 ,
}
local DEFAULT_TITLE_COLORS = {
r = 0.510 ,
g = 0.773 ,
b = 1 ,
}
local DEFAULT_TEXT_COLORS = {
r = 0.486 ,
g = 0.518 ,
b = 0.541
}
local TOAST_BUTTONS = {
primary_button = true ,
secondary_button = true ,
tertiary_button = true ,
}
local TOAST_BUTTON_HEIGHT = 18
local POINT_TRANSLATION = {
CENTER = DEFAULT_OS_SPAWN_POINT ,
BOTTOM = " BOTTOMRIGHT " ,
BOTTOMLEFT = " BOTTOMLEFT " ,
BOTTOMRIGHT = " BOTTOMRIGHT " ,
LEFT = " TOPLEFT " ,
RIGHT = " TOPRIGHT " ,
TOP = " TOPRIGHT " ,
TOPLEFT = " TOPLEFT " ,
TOPRIGHT = " TOPRIGHT " ,
}
local SIBLING_ANCHORS = {
TOPRIGHT = " BOTTOMRIGHT " ,
TOPLEFT = " BOTTOMLEFT " ,
BOTTOMRIGHT = " TOPRIGHT " ,
BOTTOMLEFT = " TOPLEFT " ,
}
local OFFSET_X = {
TOPRIGHT = - 20 ,
TOPLEFT = 20 ,
BOTTOMRIGHT = - 20 ,
BOTTOMLEFT = 20 ,
}
local OFFSET_Y = {
TOPRIGHT = - 30 ,
TOPLEFT = - 30 ,
BOTTOMRIGHT = 30 ,
BOTTOMLEFT = 30 ,
}
local SIBLING_OFFSET_Y = {
TOPRIGHT = - 10 ,
TOPLEFT = - 10 ,
BOTTOMRIGHT = 10 ,
BOTTOMLEFT = 10 ,
}
local L_TOAST = " Toast "
local L_TOAST_DESC = " Shows messages in a toast window. "
local LOCALE = _G.GetLocale ( )
if LOCALE == " esMX " or LOCALE == " esES " then
L_TOAST = " Información emergente "
L_TOAST_DESC = " Muestra mensajes de información en una ventana emergente "
elseif LOCALE == " frFR " then
L_TOAST_DESC = " Montrer les messages dans une fenêtre \" toast \" . "
elseif LOCALE == " deDE " then
L_TOAST_DESC = " Zeigt Nachrichten in einem Toast-Fenster "
elseif LOCALE == " itIT " then
-- Nothing yet.
elseif LOCALE == " koKR " then
-- Nothing yet.
elseif LOCALE == " ptBR " then
L_TOAST = " Brinde "
L_TOAST_DESC = " Mostrar mensagems em uma janela externa "
elseif LOCALE == " ruRU " then
L_TOAST = " Всплывающее "
L_TOAST_DESC = " Показывать сообщения во всплывающем окне "
elseif LOCALE == " zhCN " then
L_TOAST = " 弹出窗口 "
L_TOAST_DESC = " 在弹出窗口显示信息。 "
elseif LOCALE == " zhTW " then
L_TOAST = " 彈出視窗 "
L_TOAST_DESC = " 在彈出視窗顯示訊息。 "
end
-----------------------------------------------------------------------
-- Variables.
-----------------------------------------------------------------------
local QueuedAddOnName
-----------------------------------------------------------------------
-- Settings functions.
-----------------------------------------------------------------------
local function ToastSpawnPoint ( )
return _G.Toaster and _G.Toaster : SpawnPoint ( ) or DEFAULT_OS_SPAWN_POINT
end
local function ToastOffsetX ( )
return ( _G.Toaster and _G.Toaster . SpawnOffsetX ) and _G.Toaster : SpawnOffsetX ( ) or nil
end
local function ToastOffsetY ( )
return ( _G.Toaster and _G.Toaster . SpawnOffsetY ) and _G.Toaster : SpawnOffsetY ( ) or nil
end
local function ToastTitleColors ( urgencyLevel )
if _G.Toaster then
return _G.Toaster : TitleColors ( urgencyLevel )
else
return DEFAULT_TITLE_COLORS.r , DEFAULT_TITLE_COLORS.g , DEFAULT_TITLE_COLORS.b
end
end
local function ToastTextColors ( urgencyLevel )
if _G.Toaster then
return _G.Toaster : TextColors ( urgencyLevel )
else
return DEFAULT_TEXT_COLORS.r , DEFAULT_TEXT_COLORS.g , DEFAULT_TEXT_COLORS.b
end
end
local function ToastBackgroundColors ( urgencyLevel )
if _G.Toaster then
return _G.Toaster : BackgroundColors ( urgencyLevel )
else
return DEFAULT_BACKGROUND_COLORS.r , DEFAULT_BACKGROUND_COLORS.g , DEFAULT_BACKGROUND_COLORS.b
end
end
local function ToastDuration ( addonName )
return _G.Toaster and _G.Toaster : Duration ( addonName ) or DEFAULT_FADE_HOLD_TIME
end
local function ToastOpacity ( addonName )
return _G.Toaster and _G.Toaster : Opacity ( addonName ) or 0.75
end
local function ToastHasFloatingIcon ( addonName )
return _G.Toaster and _G.Toaster : FloatingIcon ( addonName )
end
local function ToastsAreSuppressed ( addonName )
return _G.Toaster and ( _G.Toaster : HideToasts ( ) or _G.Toaster : HideToastsFromSource ( addonName ) )
end
local function ToastsAreMuted ( addonName )
return _G.Toaster and ( _G.Toaster : MuteToasts ( ) or _G.Toaster : MuteToastsFromSource ( addonName ) )
end
-----------------------------------------------------------------------
-- Helper functions.
-----------------------------------------------------------------------
local function AnimationHideParent ( animation )
animation : GetParent ( ) : Hide ( )
end
local function GetEffectiveSpawnPoint ( frame )
local x , y = frame : GetCenter ( )
if not x or not y then
return DEFAULT_OS_SPAWN_POINT
end
local horizontalName = ( x > _G.UIParent : GetWidth ( ) * 2 / 3 ) and " RIGHT " or ( x < _G.UIParent : GetWidth ( ) / 3 ) and " LEFT " or " "
local verticalName = ( y > _G.UIParent : GetHeight ( ) / 2 ) and " TOP " or " BOTTOM "
return verticalName .. horizontalName
end
local function GetCallingObject ( )
return CallingObject
end
local function StringValue ( input )
local inputType = _G.type ( input )
if inputType == " function " then
local output = input ( )
if _G.type ( output ) ~= " string " or output == " " then
return
end
return output
elseif inputType == " string " then
return input
end
end
if not LibToast.templates [ LibToast.sink_template ] then
LibToast.templates [ LibToast.sink_template ] = function ( toast , text , _ , _ , _ , _ , _ , _ , _ , _ , iconTexture )
local callingObject = GetCallingObject ( )
toast : SetTitle ( StringValue ( LibToast.sink_titles [ callingObject ] ) )
toast : SetText ( text )
toast : SetIconTexture ( iconTexture or StringValue ( LibToast.sink_icons [ callingObject ] ) )
end
end
local function _positionToastIcon ( toast )
toast.icon : ClearAllPoints ( )
if ToastHasFloatingIcon ( toast.addonName ) then
local lowercasedPointName = POINT_TRANSLATION [ GetEffectiveSpawnPoint ( toast ) ] : lower ( )
if lowercasedPointName : find ( " right " ) then
toast.icon : SetPoint ( " TOPRIGHT " , toast , " TOPLEFT " , - 5 , - 10 )
elseif lowercasedPointName : find ( " left " ) then
toast.icon : SetPoint ( " TOPLEFT " , toast , " TOPRIGHT " , 5 , - 10 )
end
else
toast.icon : SetPoint ( " TOPLEFT " , toast , " TOPLEFT " , 10 , - 10 )
end
end
local function _reclaimButton ( button )
button : Hide ( )
button : ClearAllPoints ( )
button : SetParent ( nil )
button : SetText ( nil )
table.insert ( ButtonHeap , button )
end
local function _reclaimToast ( toast )
for buttonName in pairs ( TOAST_BUTTONS ) do
local button = toast [ buttonName ]
if button then
toast [ buttonName ] = nil
_reclaimButton ( button )
end
end
toast.is_persistent = nil
toast.templateName = nil
toast.payload = nil
toast.sound_file = nil
toast : Hide ( )
table.insert ( ToastHeap , toast )
local removalIndex
for index = 1 , # ActiveToasts do
if ActiveToasts [ index ] == toast then
removalIndex = index
break
end
end
if removalIndex then
table.remove ( ActiveToasts , removalIndex ) : ClearAllPoints ( )
end
local spawnPoint = ToastSpawnPoint ( )
local offsetX = ToastOffsetX ( ) or OFFSET_X [ spawnPoint ]
local offsetY = ToastOffsetY ( ) or OFFSET_Y [ spawnPoint ]
for index = 1 , # ActiveToasts do
local indexedToast = ActiveToasts [ index ]
indexedToast : ClearAllPoints ( )
_positionToastIcon ( indexedToast )
if index == 1 then
indexedToast : SetPoint ( spawnPoint , _G.UIParent , spawnPoint , offsetX , offsetY )
else
spawnPoint = POINT_TRANSLATION [ GetEffectiveSpawnPoint ( ActiveToasts [ 1 ] ) ]
indexedToast : SetPoint ( spawnPoint , ActiveToasts [ index - 1 ] , SIBLING_ANCHORS [ spawnPoint ] , 0 , SIBLING_OFFSET_Y [ spawnPoint ] )
end
end
local toastData = table.remove ( QueuedToasts , 1 )
if toastData and toastData.addonName and _G.type ( toastData.template ) == " string " and toastData.template ~= " " then
QueuedAddOnName = toastData.addonName
LibToast : Spawn ( toastData.template , _G.unpack ( toastData.payload ) )
end
end
local function AnimationDismissToast ( animation )
_reclaimToast ( animation.toast )
end
local function Focus_OnEnter ( frame , motion )
local toast = frame.toast
toast.dismiss_button : Show ( )
if not toast.is_persistent then
toast.waitAndAnimateOut : Stop ( )
toast.waitAndAnimateOut . animateOut : SetStartDelay ( 1 )
end
end
local function Focus_OnLeave ( frame , motion )
local toast = frame.toast
if not toast.dismiss_button : IsMouseOver ( ) then
toast.dismiss_button : Hide ( )
end
if not toast.is_persistent then
toast.waitAndAnimateOut : Play ( )
end
end
local function _dismissToast ( frame , button , down )
_reclaimToast ( frame : GetParent ( ) )
end
local function _acquireToast ( addonName )
local toast = table.remove ( ToastHeap )
if not toast then
toast = _G.CreateFrame ( " Button " , nil , _G.UIParent )
toast : SetFrameStrata ( " DIALOG " )
toast : Hide ( )
local icon = toast : CreateTexture ( nil , " BORDER " )
icon : SetSize ( DEFAULT_ICON_SIZE , DEFAULT_ICON_SIZE )
toast.icon = icon
local title = toast : CreateFontString ( nil , " BORDER " , " FriendsFont_Normal " )
title : SetJustifyH ( " LEFT " )
title : SetJustifyV ( " MIDDLE " )
title : SetWordWrap ( true )
title : SetPoint ( " TOPLEFT " , toast , " TOPLEFT " , 44 , - 10 )
title : SetPoint ( " RIGHT " , toast , " RIGHT " , - 20 , 10 )
toast.title = title
local focus = _G.CreateFrame ( " Frame " , nil , toast )
focus : SetAllPoints ( toast )
focus : SetScript ( " OnEnter " , Focus_OnEnter )
focus : SetScript ( " OnLeave " , Focus_OnLeave )
focus : SetScript ( " OnShow " , Focus_OnLeave )
focus.toast = toast
local dismissButton = _G.CreateFrame ( " Button " , nil , toast )
dismissButton : SetSize ( 18 , 18 )
dismissButton : SetPoint ( " TOPRIGHT " , toast , " TOPRIGHT " , - 4 , - 4 )
dismissButton : SetFrameStrata ( " DIALOG " )
dismissButton : SetFrameLevel ( toast : GetFrameLevel ( ) + 2 )
dismissButton : SetNormalTexture ( [[Interface\FriendsFrame\UI-Toast-CloseButton-Up]] )
dismissButton : SetPushedTexture ( [[Interface\FriendsFrame\UI-Toast-CloseButton-Down]] )
dismissButton : SetHighlightTexture ( [[Interface\FriendsFrame\UI-Toast-CloseButton-Highlight]] )
dismissButton : Hide ( )
dismissButton : SetScript ( " OnClick " , _dismissToast )
toast.dismiss_button = dismissButton
local text = toast : CreateFontString ( nil , " BORDER " , " FriendsFont_Normal " )
text : SetJustifyH ( " LEFT " )
text : SetJustifyV ( " MIDDLE " )
text : SetWordWrap ( true )
text : SetPoint ( " TOPLEFT " , title , " BOTTOMLEFT " , 0 , - 4 )
toast.text = text
local toastAnimateIn = toast : CreateAnimationGroup ( )
toast.animateIn = toastAnimateIn
local toastAnimateInFirst = toastAnimateIn : CreateAnimation ( " Alpha " )
toastAnimateInFirst : SetOrder ( 1 )
toastAnimateInFirst : SetFromAlpha ( 1 )
toastAnimateInFirst : SetToAlpha ( 0 )
toastAnimateInFirst : SetDuration ( 0 )
local toastAnimateInSecond = toastAnimateIn : CreateAnimation ( " Alpha " )
toastAnimateInSecond : SetOrder ( 2 )
toastAnimateInSecond : SetFromAlpha ( 0 )
toastAnimateInSecond : SetToAlpha ( 1 )
toastAnimateInSecond : SetDuration ( 0.2 )
local toastWaitAndAnimateOut = toast : CreateAnimationGroup ( )
toast.waitAndAnimateOut = toastWaitAndAnimateOut
local toastAnimateOut = toastWaitAndAnimateOut : CreateAnimation ( " Alpha " )
toastAnimateOut : SetStartDelay ( DEFAULT_FADE_HOLD_TIME )
toastAnimateOut : SetFromAlpha ( 1 )
toastAnimateOut : SetToAlpha ( 0 )
toastAnimateOut : SetDuration ( DEFAULT_FADE_OUT_TIME )
toastAnimateOut : SetScript ( " OnFinished " , AnimationDismissToast )
toastAnimateOut.toast = toast
toastWaitAndAnimateOut.animateOut = toastAnimateOut
local glowFrame = _G.CreateFrame ( " Frame " , nil , toast )
glowFrame : SetAllPoints ( toast )
toast.glowFrame = glowFrame
local glowTexture = glowFrame : CreateTexture ( nil , " OVERLAY " )
glowTexture : SetSize ( DEFAULT_GLOW_WIDTH , DEFAULT_GLOW_HEIGHT )
glowTexture : SetPoint ( " TOPLEFT " , - 1 , 3 )
glowTexture : SetPoint ( " BOTTOMRIGHT " , 1 , - 3 )
glowTexture : SetTexture ( [[Interface\FriendsFrame\UI-Toast-Flair]] )
glowTexture : SetBlendMode ( " ADD " )
glowTexture : Hide ( )
glowFrame.glow = glowTexture
local glowAnimateIn = glowTexture : CreateAnimationGroup ( )
glowAnimateIn : SetScript ( " OnFinished " , AnimationHideParent )
glowTexture.animateIn = glowAnimateIn
local glowAnimateInFirst = glowAnimateIn : CreateAnimation ( " Alpha " )
glowAnimateInFirst : SetOrder ( 1 )
glowAnimateInFirst : SetFromAlpha ( 0 )
glowAnimateInFirst : SetToAlpha ( 1 )
glowAnimateInFirst : SetDuration ( 0.2 )
local glowAnimateInSecond = glowAnimateIn : CreateAnimation ( " Alpha " )
glowAnimateInSecond : SetOrder ( 2 )
glowAnimateInSecond : SetFromAlpha ( 1 )
glowAnimateInSecond : SetToAlpha ( 0 )
glowAnimateInSecond : SetDuration ( 0.5 )
end
toast : SetSize ( DEFAULT_TOAST_WIDTH , DEFAULT_TOAST_HEIGHT )
toast : SetBackdrop ( DEFAULT_TOAST_BACKDROP )
toast : SetBackdropBorderColor ( 1 , 1 , 1 )
if _G.Toaster then
local iconSize = _G.Toaster : IconSize ( addonName )
toast.icon : SetSize ( iconSize , iconSize )
end
return toast
end
-----------------------------------------------------------------------
-- Library methods.
-----------------------------------------------------------------------
function LibToast : Register ( templateName , constructor , isUnique )
local isLib = ( self == LibToast )
if _G.type ( templateName ) ~= " string " or templateName == " " then
error ( METHOD_USAGE_FORMAT : format ( isLib and " Register " or " RegisterToast " , " templateName must be a non-empty string " ) , 2 )
end
if _G.type ( constructor ) ~= " function " then
error ( METHOD_USAGE_FORMAT : format ( isLib and " Register " or " RegisterToast " , " constructor must be a function " ) , 2 )
end
LibToast.templates [ templateName ] = constructor
LibToast.unique_templates [ templateName ] = isUnique or nil
end
function LibToast : Spawn ( templateName , ... )
local isLib = ( self == LibToast )
if not templateName or ( not IsInternalCall and ( _G.type ( templateName ) ~= " string " or templateName == " " ) ) then
error ( METHOD_USAGE_FORMAT : format ( isLib and " Spawn " or " SpawnToast " , " templateName must be a non-empty string " ) , 2 )
end
if not LibToast.templates [ templateName ] then
error ( METHOD_USAGE_FORMAT : format ( isLib and " Spawn " or " SpawnToast " , ( " \" %s \" does not match a registered template " ) : format ( templateName ) ) , 2 )
end
local addonName
if QueuedAddOnName then
addonName = QueuedAddOnName
QueuedAddOnName = nil
elseif isLib then
addonName = _G.select ( 3 , ( [[\]] ) : split ( _G.debugstack ( 2 ) ) )
else
addonName = LibToast.addon_names [ self ] or _G.UNKNOWN
end
if ToastsAreSuppressed ( addonName ) then
return
end
if LibToast.unique_templates [ templateName ] then
for index = 1 , # ActiveToasts do
if ActiveToasts [ index ] . templateName == templateName then
return
end
end
end
if # ActiveToasts >= MAX_ACTIVE_TOASTS then
table.insert ( QueuedToasts , {
addonName = addonName ,
template = templateName ,
payload = { ... }
} )
return
end
CurrentToast = _acquireToast ( addonName )
CurrentToast.templateName = templateName
CurrentToast.addonName = addonName
-----------------------------------------------------------------------
-- Reset defaults.
-----------------------------------------------------------------------
CurrentToast.title : SetText ( nil )
CurrentToast.text : SetText ( nil )
CurrentToast.icon : SetTexture ( nil )
CurrentToast.icon : SetTexCoord ( 0 , 1 , 0 , 1 )
-----------------------------------------------------------------------
-- Run constructor.
-----------------------------------------------------------------------
CallingObject = self
LibToast.templates [ templateName ] ( ToastProxy , ... )
if not CurrentToast.title : GetText ( ) and not CurrentToast.text : GetText ( ) and not CurrentToast.icon : GetTexture ( ) then
_reclaimToast ( CurrentToast )
return
end
-----------------------------------------------------------------------
-- Finalize layout.
-----------------------------------------------------------------------
local urgency = CurrentToast.urgency_level
CurrentToast.title : SetTextColor ( ToastTitleColors ( urgency ) )
CurrentToast.text : SetTextColor ( ToastTextColors ( urgency ) )
local opacity = ToastOpacity ( addonName )
local r , g , b = ToastBackgroundColors ( urgency )
CurrentToast : SetBackdropColor ( r , g , b , opacity )
r , g , b = CurrentToast : GetBackdropBorderColor ( )
CurrentToast : SetBackdropBorderColor ( r , g , b , opacity )
if ToastHasFloatingIcon ( addonName ) or not CurrentToast.icon : GetTexture ( ) then
CurrentToast.title : SetPoint ( " TOPLEFT " , CurrentToast , " TOPLEFT " , 10 , - 10 )
else
CurrentToast.title : SetPoint ( " TOPLEFT " , CurrentToast , " TOPLEFT " , CurrentToast.icon : GetWidth ( ) + 15 , - 10 )
end
if CurrentToast.title : GetText ( ) then
CurrentToast.title : SetWidth ( CurrentToast : GetWidth ( ) - CurrentToast.icon : GetWidth ( ) - 20 )
CurrentToast.title : Show ( )
else
CurrentToast.title : Hide ( )
end
if CurrentToast.text : GetText ( ) then
CurrentToast.text : SetWidth ( CurrentToast : GetWidth ( ) - CurrentToast.icon : GetWidth ( ) - 20 )
CurrentToast.text : Show ( )
else
CurrentToast.text : Hide ( )
end
local buttonHeight = ( CurrentToast.primary_button or CurrentToast.secondary_button or CurrentToast.tertiary_button ) and TOAST_BUTTON_HEIGHT or 0
CurrentToast : SetHeight ( CurrentToast.text : GetStringHeight ( ) + CurrentToast.title : GetStringHeight ( ) + buttonHeight + 25 )
-----------------------------------------------------------------------
-- Anchor and spawn.
-----------------------------------------------------------------------
local spawnPoint = ToastSpawnPoint ( )
local offsetX = ToastOffsetX ( ) or OFFSET_X [ spawnPoint ]
local offsetY = ToastOffsetY ( ) or OFFSET_Y [ spawnPoint ]
if # ActiveToasts > 0 then
spawnPoint = POINT_TRANSLATION [ GetEffectiveSpawnPoint ( ActiveToasts [ 1 ] ) ]
CurrentToast : SetPoint ( spawnPoint , ActiveToasts [ # ActiveToasts ] , SIBLING_ANCHORS [ spawnPoint ] , 0 , SIBLING_OFFSET_Y [ spawnPoint ] )
else
CurrentToast : SetPoint ( spawnPoint , _G.UIParent , spawnPoint , offsetX , offsetY )
end
ActiveToasts [ # ActiveToasts + 1 ] = CurrentToast
_positionToastIcon ( CurrentToast )
if CurrentToast.sound_file and not ToastsAreMuted ( addonName ) then
_G.PlaySoundFile ( CurrentToast.sound_file )
end
CurrentToast : Show ( )
CurrentToast.animateIn : Play ( )
CurrentToast.glowFrame . glow : Show ( )
CurrentToast.glowFrame . glow.animateIn : Play ( )
CurrentToast.waitAndAnimateOut : Stop ( ) -- Stop prior fade attempt.
if not CurrentToast.is_persistent then
if CurrentToast : IsMouseOver ( ) then
CurrentToast.waitAndAnimateOut . animateOut : SetStartDelay ( 1 )
else
CurrentToast.waitAndAnimateOut . animateOut : SetStartDelay ( ToastDuration ( addonName ) )
CurrentToast.waitAndAnimateOut : Play ( )
end
end
end
function LibToast : DefineSink ( displayName , texturePath )
local isLib = ( self == LibToast )
local texturePathType = _G.type ( texturePath )
local displayNameType = _G.type ( displayName )
if texturePath and ( texturePathType ~= " function " and ( texturePathType ~= " string " or texturePath == " " ) ) then
error ( METHOD_USAGE_FORMAT : format ( isLib and " DefineSink " or " DefineSinkToast " , " texturePath must be a non-empty string, a function that returns one, or nil " ) , 2 )
end
if displayName and ( displayNameType ~= " function " and ( displayNameType ~= " string " or displayName == " " ) ) then
error ( METHOD_USAGE_FORMAT : format ( isLib and " DefineSink " or " DefineSinkToast " , " displayName must be a non-empty string, a function that returns one, or nil " ) , 2 )
end
local addonName = _G.select ( 3 , ( [[\]] ) : split ( _G.debugstack ( 2 ) ) )
LibToast.addon_names [ self ] = addonName or _G.UNKNOWN
LibToast.sink_icons [ self ] = texturePath
LibToast.sink_titles [ self ] = displayName
if not LibToast.registered_sink then
local LibSink = LibStub ( " LibSink-2.0 " )
if not LibSink then
return
end
LibSink : RegisterSink ( " LibToast-1.0 " , L_TOAST , L_TOAST_DESC , function ( caller , text , ... )
IsInternalCall = true
local func
if caller.SpawnToast then
func = caller.SpawnToast
else
caller = LibToast
func = LibToast.Spawn
end
func ( caller , LibToast.sink_template , text , ... )
IsInternalCall = nil
end )
LibToast.registered_sink = true
end
end
-----------------------------------------------------------------------
-- Proxy methods.
-----------------------------------------------------------------------
local TOAST_URGENCIES = {
very_low = true ,
moderate = true ,
normal = true ,
high = true ,
emergency = true ,
}
function ToastProxy : SetUrgencyLevel ( urgencyLevel )
urgencyLevel = urgencyLevel : gsub ( " " , " _ " ) : lower ( )
if not TOAST_URGENCIES [ urgencyLevel ] then
error ( ( " \" %s \" is not a valid toast urgency level " ) : format ( urgencyLevel ) , 2 )
end
CurrentToast.urgency_level = urgencyLevel
end
function ToastProxy : UrgencyLevel ( )
return CurrentToast.urgency_level
end
function ToastProxy : SetTitle ( title )
CurrentToast.title : SetText ( title )
end
function ToastProxy : SetFormattedTitle ( title , ... )
CurrentToast.title : SetFormattedText ( title , ... )
end
function ToastProxy : SetText ( text )
CurrentToast.text : SetText ( text )
end
function ToastProxy : SetFormattedText ( text , ... )
CurrentToast.text : SetFormattedText ( text , ... )
end
function ToastProxy : SetIconAtlas ( ... )
CurrentToast.icon : SetAtlas ( ... )
end
function ToastProxy : SetIconTexture ( texture )
CurrentToast.icon : SetTexture ( texture )
end
function ToastProxy : SetIconTexCoord ( ... )
CurrentToast.icon : SetTexCoord ( ... )
end
local _initializedToastButton
do
local BUTTON_NAME_FORMAT = " LibToast_Button%d "
local button_count = 0
local function _buttonCallbackHandler ( button , mouseButtonName , isPress )
button.handler ( button.id , mouseButtonName , isPress , button.toast . payload )
_reclaimToast ( button.toast )
end
local function _acquireToastButton ( toast )
local button = table.remove ( ButtonHeap )
if not button then
button_count = button_count + 1
button = _G.CreateFrame ( " Button " , BUTTON_NAME_FORMAT : format ( button_count ) , toast , " UIMenuButtonStretchTemplate " )
button : SetHeight ( TOAST_BUTTON_HEIGHT )
button : SetFrameStrata ( " DIALOG " )
button : SetScript ( " OnClick " , _buttonCallbackHandler )
local fontString = button : GetFontString ( )
fontString : SetJustifyH ( " CENTER " )
fontString : SetJustifyV ( " CENTER " )
end
button : SetParent ( toast )
button : SetFrameLevel ( toast : GetFrameLevel ( ) + 2 )
return button
end
function _initializedToastButton ( buttonID , label , handler )
if not label or not handler then
error ( " label and handler are required " , 3 )
return
end
local button = CurrentToast [ buttonID ]
if not button then
button = _acquireToastButton ( CurrentToast )
CurrentToast [ buttonID ] = button
end
button.id = buttonID : gsub ( " _button " , " " )
button.handler = handler
button.toast = CurrentToast
button : Show ( )
button : SetText ( label )
button : SetWidth ( button : GetFontString ( ) : GetStringWidth ( ) + 15 )
return button
end
end -- do-block
function ToastProxy : SetPrimaryCallback ( label , handler )
local button = _initializedToastButton ( " primary_button " , label , handler )
button : SetPoint ( " BOTTOMLEFT " , CurrentToast , " BOTTOMLEFT " , 3 , 4 )
button : SetPoint ( " BOTTOMRIGHT " , CurrentToast , " BOTTOMRIGHT " , - 3 , 4 )
CurrentToast : SetHeight ( CurrentToast : GetHeight ( ) + button : GetHeight ( ) + 5 )
if button : GetWidth ( ) > CurrentToast : GetWidth ( ) then
CurrentToast : SetWidth ( button : GetWidth ( ) + 5 )
end
end
function ToastProxy : SetSecondaryCallback ( label , handler )
if not CurrentToast.primary_button then
error ( " primary button must be defined first " , 2 )
end
CurrentToast.primary_button : ClearAllPoints ( )
CurrentToast.primary_button : SetPoint ( " BOTTOMLEFT " , CurrentToast , " BOTTOMLEFT " , 3 , 4 )
local button = _initializedToastButton ( " secondary_button " , label , handler )
button : SetPoint ( " BOTTOMRIGHT " , CurrentToast , " BOTTOMRIGHT " , - 3 , 4 )
if button : GetWidth ( ) + CurrentToast.primary_button : GetWidth ( ) > CurrentToast : GetWidth ( ) then
CurrentToast : SetWidth ( button : GetWidth ( ) + CurrentToast.primary_button : GetWidth ( ) + 5 )
end
end
function ToastProxy : SetTertiaryCallback ( label , handler )
if not CurrentToast.primary_button or not CurrentToast.secondary_button then
error ( " primary and secondary buttons must be defined first " , 2 )
end
CurrentToast.secondary_button : ClearAllPoints ( )
CurrentToast.secondary_button : SetPoint ( " LEFT " , CurrentToast.primary_button , " RIGHT " , 0 , 0 )
local button = _initializedToastButton ( " tertiary_button " , label , handler )
button : SetPoint ( " LEFT " , CurrentToast.secondary_button , " RIGHT " , 0 , 0 )
if button : GetWidth ( ) + CurrentToast.primary_button : GetWidth ( ) + CurrentToast.secondary_button : GetWidth ( ) > CurrentToast : GetWidth ( ) then
CurrentToast : SetWidth ( button : GetWidth ( ) + CurrentToast.primary_button : GetWidth ( ) + CurrentToast.secondary_button : GetWidth ( ) + 5 )
end
end
function ToastProxy : SetPayload ( ... )
CurrentToast.payload = { ... }
end
function ToastProxy : Payload ( )
return _G.unpack ( CurrentToast.payload )
end
function ToastProxy : MakePersistent ( )
CurrentToast.is_persistent = true
end
function ToastProxy : SetSoundFile ( filePath )
CurrentToast.sound_file = filePath
end
-----------------------------------------------------------------------
-- Embed handling.
-----------------------------------------------------------------------
LibToast.embeds = LibToast.embeds or { }
local mixins = {
" DefineSink " ,
" Register " ,
" Spawn " ,
}
function LibToast : Embed ( target )
LibToast.embeds [ target ] = true
for index = 1 , # mixins do
local method = mixins [ index ]
target [ method .. " Toast " ] = LibToast [ method ]
end
return target
end
for addon in pairs ( LibToast.embeds ) do
LibToast : Embed ( addon )
end