Files
ElvUIModernized/ElvUI/Libraries/oUF/ouf.lua
T
2018-07-11 18:35:38 -05:00

698 lines
18 KiB
Lua

local ns = oUF
local oUF = ns.oUF
local Private = oUF.Private
local argcheck = Private.argcheck
local print = Private.print
local error = Private.error
local gsub, lower, match, split, upper = string.gsub, string.lower, string.match, string.split, string.upper
local insert, remove = table.insert, table.remove
local styles, style = {}
local callback, units, objects, headers = {}, {}, {}, {}
local elements = {}
local activeElements = {}
-- updating of "invalid" units.
local function enableTargetUpdate(object)
object.onUpdateFrequency = object.onUpdateFrequency or .5
object.__eventless = true
local total = 0
object:SetScript("OnUpdate", function()
if not this.unit then
return
elseif total > this.onUpdateFrequency then
this:UpdateAllElements("OnUpdate")
total = 0
end
total = total + GetTime()
end)
end
Private.enableTargetUpdate = enableTargetUpdate
local function updateActiveUnit(self, event, unit)
return true
end
local function iterateChildren(...)
for i = 1, arg.n do
local obj = arg[i]
if type(obj) == "table" and obj.isChild then
updateActiveUnit(obj, "iterateChildren")
end
end
end
local function onAttributeChanged(self, name, value)
if name == "unit" and value then
if self.hasChildren then
iterateChildren(self:GetChildren())
end
if not self.onlyProcessChildren then
updateActiveUnit(self, "OnAttributeChanged")
end
if self.unit and self.unit == value then
return
else
if self.hasChildren then
iterateChildren(self:GetChildren())
end
end
end
end
local frame_metatable = {
__index = CreateFrame("Button")
}
Private.frame_metatable = frame_metatable
for k, v in next, {
UpdateElement = function(self, name)
local unit = self.unit
if not unit or not UnitExists(unit) then return end
local element = elements[name]
if not element or not self:IsElementEnabled(name) or not activeElements[self] then return end
if element.update then
element.update(self, "OnShow", unit)
end
end,
UpdateElement = function(self, name)
local unit = self.unit
if not unit or not UnitExists(unit) then return end
local element = elements[name]
if not element or not self:IsElementEnabled(name) or not activeElements[self] then return end
if element.update then
element.update(self, "OnShow", unit)
end
end,
--[[ frame:EnableElement(name, unit)
Used to activate an element for the given unit frame.
* self - unit frame for which the element should be enabled
* name - name of the element to be enabled (string)
* unit - unit to be passed to the element"s Enable function. Defaults to the frame"s unit (string?)
--]]
EnableElement = function(self, name, unit)
local unit = unit or self.unit
if not unit then return end
argcheck(name, 2, "string")
argcheck(unit or self.unit, 3, "string", "nil")
local element = elements[name]
if not element or self:IsElementEnabled(name) or not activeElements[self] then return end
if element.enable(self, unit or self.unit) then
activeElements[self][name] = true
if element.update then
insert(self.__elements, element.update)
end
end
end,
--[[ frame:DisableElement(name)
Used to deactivate an element for the given unit frame.
* self - unit frame for which the element should be disabled
* name - name of the element to be disabled (string)
--]]
DisableElement = function(self, name)
argcheck(name, 2, "string")
local enabled = self:IsElementEnabled(name)
if not enabled then return end
local update = elements[name].update
for k, func in next, self.__elements do
if func == update then
remove(self.__elements, k)
break
end
end
activeElements[self][name] = nil
-- We need to run a new update cycle in-case we knocked ourself out of sync.
-- The main reason we do this is to make sure the full update is completed
-- if an element for some reason removes itself _during_ the update
-- progress.
self:UpdateAllElements("DisableElement")
return elements[name].disable(self)
end,
--[[ frame:IsElementEnabled(name)
Used to check if an element is enabled on the given frame.
* self - unit frame
* name - name of the element (string)
--]]
IsElementEnabled = function(self, name)
argcheck(name, 2, "string")
local element = elements[name]
if not element then return end
local active = activeElements[self]
return active and active[name]
end,
--[[ frame:Enable(asState)
Used to toggle the visibility of a unit frame based on the existence of its unit. This is a reference to
`RegisterUnitWatch`.
* self - unit frame
* asState - if true, the frame"s "state-unitexists" attribute will be set to a boolean value denoting whether the
unit exists; if false, the frame will be shown if its unit exists, and hidden if it does not (boolean)
--]]
Enable = RegisterUnitWatch,
--[[ frame:Disable()
Used to UnregisterUnitWatch for the given frame and hide it.
* self - unit frame
--]]
Disable = function(self)
UnregisterUnitWatch(self)
self:Hide()
end,
--[[ frame:UpdateAllElements(event)
Used to update all enabled elements on the given frame.
* self - unit frame
* event - event name to pass to the elements" update functions (string)
--]]
UpdateAllElements = function(self, event)
local unit = self.unit
if not UnitExists(unit) then return end
assert(type(event) == "string", "Invalid argument 'event' in UpdateAllElements.")
if self.PreUpdate then
self:PreUpdate(event)
end
for _, func in next, self.__elements do
func(self, event, unit)
end
if self.PostUpdate then
self:PostUpdate(event)
end
end,
SetUnit = function(self, unit)
if unit ~= unit then
self.unit = unit
end
end,
} do
frame_metatable.__index[k] = v
end
local secureDropdown
local function InitializeSecureMenu()
local unit = SecureTemplatesDropdown.unit
if not unit then return end
local unitType = match(unit, "^([a-z]+)[0-9]+$") or unit
local menu
if unitType == "party" then
menu = "PARTY"
elseif UnitIsUnit(unit, "player") then
menu = "SELF"
elseif UnitIsUnit(unit, "pet") then
menu = "PET"
elseif UnitIsPlayer(unit) then
if UnitInRaid(unit) or UnitInParty(unit) then
menu = "PARTY"
else
menu = "PLAYER"
end
elseif UnitIsUnit(unit, "target") then
menu = "RAID_TARGET_ICON"
end
if menu then
UnitPopup_ShowMenu(SecureTemplatesDropdown, menu, unit)
end
end
local function togglemenu(self, unit)
if not secureDropdown then
secureDropdown = CreateFrame("Frame", "SecureTemplatesDropdown", nil, "UIDropDownMenuTemplate")
secureDropdown:SetID(1)
insert(UnitPopupFrames, secureDropdown:GetName())
UIDropDownMenu_Initialize(secureDropdown, InitializeSecureMenu, "MENU")
end
if secureDropdown.openedFor and secureDropdown.openedFor ~= self then
CloseDropDownMenus()
end
secureDropdown.unit = lower(unit)
secureDropdown.openedFor = self
ToggleDropDownMenu(1, nil, secureDropdown, "cursor")
end
local function onShow(self)
return self:UpdateAllElements("OnShow")
end
local function initObject(unit, style, styleFunc, header, ...)
for i = 1, arg.n do
local object = arg[i]
local objectUnit = object.guessUnit or unit
local suffix = objectUnit and match(objectUnit or unit, "%w+target")
object.__elements = {}
object.__registeredEvents = {}
object.style = style
object = setmetatable(object, frame_metatable)
-- Expose the frame through oUF.objects.
insert(objects, object)
-- We have to force update the frames when PEW fires.
object:RegisterEvent("PLAYER_ENTERING_WORLD", object.UpdateAllElements)
object:SetScript("OnClick", function()
if arg1 == "RightButton" then
togglemenu(this, object.unit)
else
TargetUnit(this.unit)
end
end)
if not header then
-- Other target units are handled by :HandleUnit().
if suffix == "target" then
enableTargetUpdate(object)
else
oUF:HandleUnit(object, unit)
end
else
-- Used to update frames when they change position in a group.
object:RegisterEvent("RAID_ROSTER_UPDATE", object.UpdateAllElements)
if arg.n > 1 then
if(object:GetParent() == header) then
object.hasChildren = true
else
object.isChild = true
end
end
if suffix == "target" then
enableTargetUpdate(object)
end
end
Private.UpdateUnits(object, objectUnit)
styleFunc(object, objectUnit, not header)
object:SetScript("OnShow", function() onShow(this) end)
activeElements[object] = {}
for element in next, elements do
object:EnableElement(element, objectUnit)
end
for _, func in next, callback do
func(object)
end
-- Make Clique kinda happy
_G.ClickCastFrames = ClickCastFrames or {}
ClickCastFrames[object] = true
end
end
local function walkObject(object, unit)
local parent = object:GetParent()
local style = parent.style or style
local styleFunc = styles[style]
local header = parent.headerType and parent
-- Check if we should leave the main frame blank.
if object.onlyProcessChildren then
object.hasChildren = true
return initObject(unit, style, styleFunc, header, object:GetChildren())
end
return initObject(unit, style, styleFunc, header, object, object:GetChildren())
end
--[[ oUF:RegisterInitCallback(func)
Used to add a function to a table to be executed upon unit frame/header initialization.
* self - the global oUF object
* func - function to be added
--]]
function oUF:RegisterInitCallback(func)
insert(callback, func)
end
--[[ oUF:RegisterMetaFunction(name, func)
Used to make a (table of) function(s) available to all unit frames.
* self - the global oUF object
* name - unique name of the function (string)
* func - function or a table of functions (function or table)
--]]
function oUF:RegisterMetaFunction(name, func)
argcheck(name, 2, "string")
argcheck(func, 3, "function", "table")
if frame_metatable.__index[name] then
return
end
frame_metatable.__index[name] = func
end
--[[ oUF:RegisterStyle(name, func)
Used to register a style with oUF. This will also set the active style if it hasn"t been set yet.
* self - the global oUF object
* name - name of the style
* func - function(s) defining the style (function or table)
--]]
function oUF:RegisterStyle(name, func)
argcheck(name, 2, "string")
argcheck(func, 3, "function", "table")
if styles[name] then return error("Style [%s] already registered.", name) end
if not style then style = name end
styles[name] = func
end
--[[ oUF:SetActiveStyle(name)
Used to set the active style.
* self - the global oUF object
* name - name of the style (string)
--]]
function oUF:SetActiveStyle(name)
argcheck(name, 2, "string")
if not styles[name] then return error("Style [%s] does not exist.", name) end
style = name
end
do
local function iter(_, n)
-- don"t expose the style functions.
return (next(styles, n))
end
--[[ oUF:IterateStyles()
Returns an iterator over all registered styles.
* self - the global oUF object
--]]
function oUF.IterateStyles()
return iter, nil, nil
end
end
local getCondition
do
local conditions = {
raid40 = "[target=raid26,exists] show;",
raid25 = "[target=raid11,exists] show;",
raid10 = "[target=raid6,exists] show;",
raid = "[group:raid] show;",
party = "[group:party,nogroup:raid] show;",
solo = "[target=player,exists,nogroup:party] show;",
}
function getCondition(...)
local cond = ""
for i = 1, arg.n do
local short = select(i, unpack(arg))
local condition = conditions[short]
if condition then
cond = cond .. condition
end
end
return cond .. "hide"
end
end
local function generateName(unit, ...)
local name = "oUF_" .. gsub(style, gsub("^oUF_?", ""), gsub("[^%a%d_]+", ""))
local raid, party, groupFilter
for i = 1, arg.n, 2 do
local att, val = select(i, unpack(arg))
if att == "showRaid" then
raid = true
elseif att == "showParty" then
party = true
elseif att == "groupFilter" then
groupFilter = val
end
end
local append
if raid then
if groupFilter then
if type(groupFilter) == "number" and groupFilter > 0 then
append = groupFilter
elseif match(groupFilter, "TANK") then
append = "MainTank"
elseif match(groupFilter, "ASSIST") then
append = "MainAssist"
else
local _, count = gsub(groupFilter, ",", "")
if count == 0 then
append = "Raid" .. groupFilter
else
append = "Raid"
end
end
else
append = "Raid"
end
elseif party then
append = "Party"
elseif unit then
append = gsub(unit, "^%l", upper)
end
if append then
name = name .. append
end
local base = name
local i = 2
while _G[name] do
name = base .. i
i = i + 1
end
return name
end
do
local function styleProxy(self, frame, ...)
return walkObject(_G[frame])
end
-- There has to be an easier way to do this.
local initialConfigFunction = function(self)
local header = self:GetParent()
local unit
if not self.onlyProcessChildren then
local groupFilter = header:GetAttribute("groupFilter")
if header:GetAttribute("showRaid") then
unit = "raid"
elseif header:GetAttribute("showParty") then
unit = "party"
end
self.menu = togglemenu
--self:SetAttribute("type1", "target")
--self:SetAttribute("type2", "menu")
self.guessUnit = unit
self.unit = "player"
-- self.onlyProcessChildren =true
end
styleProxy(nil, self:GetName())
end
local setAttribute = function(self, name, value)
if self.attributes[name] ~= value then
self.attributes[name] = value
if self:IsVisible() then
SecureGroupHeader_Update(self)
end
end
end
local getAttribute = function(self, name)
return self.attributes[name]
end
--[[ oUF:SpawnHeader(overrideName, template, visibility, ...)
Used to create a group header and apply the currently active style to it.
* self - the global oUF object
* overrideName - unique global name to be used for the header. Defaults to an auto-generated name based on the name
of the active style and other arguments passed to `:SpawnHeader` (string?)
* template - name of a template to be used for creating the header. Defaults to `"oUF_GroupHeaderTemplate"`
(string?)
* visibility - macro conditional(s) which define when to display the header (string).
* ... - further argument pairs. Consult [Group Headers](http://wowprogramming.com/docs/secure_template/Group_Headers)
for possible values.
In addition to the standard group headers, oUF implements some of its own attributes. These can be supplied by the
layout, but are optional.
* oUF-initialConfigFunction - can contain code that will be securely run at the end of the initial secure
configuration (string?)
* oUF-onlyProcessChildren - can be used to force headers to only process children (boolean?)
--]]
function oUF:SpawnHeader(overrideName, template, visibility, ...)
if not style then return error("Unable to create frame. No styles have been registered.") end
template = (template or "oUF_GroupHeaderTemplate")
local isPetHeader = match(template, "PetHeader")
local name = overrideName or generateName(nil, unpack(arg))
local header = CreateFrame("Frame", name, UIParent, template)
header:Hide()
header.attributes = {}
header.SetAttribute = setAttribute
header.GetAttribute = getAttribute
--header:SetAttribute("template", "SecureUnitButtonTemplate")
for i = 1, arg.n, 2 do
local att, val = select(i, unpack(arg))
if not att then break end
header:SetAttribute(att, val)
end
header.style = style
header.styleFunction = styleProxy
header.visibility = visibility
-- Expose the header through oUF.headers.
insert(headers, header)
header.initialConfigFunction = initialConfigFunction
header.headerType = isPetHeader and "pet" or "group"
if header:GetAttribute("showParty") then
self:DisableBlizzard("party")
end
if visibility then
local type, list = split(" ", visibility, 2)
if list and type == "custom" then
RegisterStateDriver(header, "visibility", list)
header.visibility = list
else
local condition = getCondition(split(",", visibility))
RegisterStateDriver(header, "visibility", condition)
header.visibility = condition
end
end
return header
end
end
--[[ oUF:Spawn(unit, overrideName)
Used to create a single unit frame and apply the currently active style to it.
* self - the global oUF object
* unit - the frame"s unit (string)
* overrideName - unique global name to use for the unit frame. Defaults to an auto-generated name based on the unit
(string?)
--]]
function oUF:Spawn(unit, overrideName)
argcheck(unit, 2, "string")
if not style then return error("Unable to create frame. No styles have been registered.") end
unit = lower(unit)
local name = overrideName or generateName(unit)
local object = CreateFrame("Button", name, UIParent)
object:Hide()
Private.UpdateUnits(object, unit)
self:DisableBlizzard(unit)
units[unit] = object
walkObject(object, unit)
object.unit = unit
return object
end
--[[ oUF:AddElement(name, update, enable, disable)
Used to register an element with oUF.
* self - the global oUF object
* name - unique name of the element (string)
* update - used to update the element (function?)
* enable - used to enable the element for a given unit frame and unit (function?)
* disable - used to disable the element for a given unit frame (function?)
--]]
function oUF:AddElement(name, update, enable, disable)
argcheck(name, 2, "string")
argcheck(update, 3, "function", "nil")
argcheck(enable, 4, "function", "nil")
argcheck(disable, 5, "function", "nil")
if elements[name] then return error("Element [%s] is already registered.", name) end
elements[name] = {
update = update;
enable = enable;
disable = disable;
}
end
--[[ oUF.units
Array containing all unit frames with existing units created by oUF:Spawn.
--]]
oUF.units = units
--[[ oUF.objects
Array containing all unit frames created by `oUF:Spawn`.
--]]
oUF.objects = objects
--[[ oUF.headers
Array containing all group headers created by `oUF:SpawnHeader`.
--]]
oUF.headers = headers