Unverified Commit 8cb0b06f authored by Rainrider's avatar Rainrider Committed by GitHub

core: re-work the event system (#472)

* unitless events need be signified as such
* the event handler is now obligatory and must be a function
* keep the list of unit events on the frame itself
* workaround for bugs with RegisterUnitEvent
* postpone unit event registration for header units
parent 340c8a8c
......@@ -27,6 +27,7 @@ local oUF = ns.oUF
local function Update(self, event)
local element = self.AssistantIndicator
local unit = self.unit
--[[ Callback: AssistantIndicator:PreUpdate()
Called before the element has been updated.
......@@ -37,7 +38,6 @@ local function Update(self, event)
element:PreUpdate()
end
local unit = self.unit
local isAssistant = UnitInRaid(unit) and UnitIsGroupAssistant(unit) and not UnitIsGroupLeader(unit)
if(isAssistant) then
element:Show()
......
......@@ -97,7 +97,7 @@ local function Disable(self)
element:Hide()
self:UnregisterEvent('PLAYER_ROLES_ASSIGNED', Path)
self:UnregisterEvent('GROUP_ROSTER_UPDATE', Path)
self:UnregisterEvent('GROUP_ROSTER_UPDATE', Path, true)
end
end
......
......@@ -27,6 +27,7 @@ local oUF = ns.oUF
local function Update(self, event)
local element = self.LeaderIndicator
local unit = self.unit
--[[ Callback: LeaderIndicator:PreUpdate()
Called before the element has been updated.
......@@ -37,7 +38,6 @@ local function Update(self, event)
element:PreUpdate()
end
local unit = self.unit
local isLeader = (UnitInParty(unit) or UnitInRaid(unit)) and UnitIsGroupLeader(unit)
if(isLeader) then
element:Show()
......
......@@ -25,7 +25,9 @@ A default texture will be applied if the widget is a Texture and doesn't have a
local _, ns = ...
local oUF = ns.oUF
local function Update(self, event)
local function Update(self, event, unit)
if(self.unit ~= unit) then return end
local element = self.PhaseIndicator
--[[ Callback: PhaseIndicator:PreUpdate()
......@@ -37,8 +39,8 @@ local function Update(self, event)
element:PreUpdate()
end
local isInSamePhase = UnitInPhase(self.unit) and not UnitIsWarModePhased(self.unit)
if(not isInSamePhase and UnitIsPlayer(self.unit) and UnitIsConnected(self.unit)) then
local isInSamePhase = UnitInPhase(unit) and not UnitIsWarModePhased(unit)
if(not isInSamePhase and UnitIsPlayer(unit) and UnitIsConnected(unit)) then
element:Show()
else
element:Hide()
......@@ -67,7 +69,7 @@ local function Path(self, ...)
end
local function ForceUpdate(element)
return Path(element.__owner, 'ForceUpdate')
return Path(element.__owner, 'ForceUpdate', element.__owner.unit)
end
local function Enable(self)
......@@ -76,7 +78,7 @@ local function Enable(self)
element.__owner = self
element.ForceUpdate = ForceUpdate
self:RegisterEvent('UNIT_PHASE', Path, true)
self:RegisterEvent('UNIT_PHASE', Path)
if(element:IsObjectType('Texture') and not element:GetTexture()) then
element:SetTexture([[Interface\TargetingFrame\UI-PhasingIcon]])
......
......@@ -107,7 +107,7 @@ local function Enable(self, unit)
self:RegisterEvent('UNIT_MODEL_CHANGED', Path)
self:RegisterEvent('UNIT_PORTRAIT_UPDATE', Path)
self:RegisterEvent('PORTRAITS_UPDATED', Path)
self:RegisterEvent('PORTRAITS_UPDATED', Path, true)
self:RegisterEvent('UNIT_CONNECTION', Path)
-- The quest log uses PARTY_MEMBER_{ENABLE,DISABLE} to handle updating of
......
......@@ -36,9 +36,10 @@ local _, ns = ...
local oUF = ns.oUF
local function Update(self, event, unit)
if(unit ~= self.unit) then return end
if(unit and unit ~= self.unit) then return end
local element = self.PvPIndicator
unit = unit or self.unit
--[[ Callback: PvPIndicator:PreUpdate(unit)
Called before the element has been updated.
......@@ -127,7 +128,7 @@ local function Enable(self)
element.ForceUpdate = ForceUpdate
self:RegisterEvent('UNIT_FACTION', Path)
self:RegisterEvent('HONOR_LEVEL_UPDATE', Path)
self:RegisterEvent('HONOR_LEVEL_UPDATE', Path, true)
return true
end
......
......@@ -29,9 +29,8 @@ local MAINTANK_ICON = [[Interface\GROUPFRAME\UI-GROUP-MAINTANKICON]]
local MAINASSIST_ICON = [[Interface\GROUPFRAME\UI-GROUP-MAINASSISTICON]]
local function Update(self, event)
local unit = self.unit
local element = self.RaidRoleIndicator
local unit = self.unit
--[[ Callback: RaidRoleIndicator:PreUpdate()
Called before the element has been updated.
......
......@@ -58,6 +58,7 @@ end
local function Update(self, event)
local element = self.ReadyCheckIndicator
local unit = self.unit
--[[ Callback: ReadyCheckIndicator:PreUpdate()
Called before the element has been updated.
......@@ -68,7 +69,6 @@ local function Update(self, event)
element:PreUpdate()
end
local unit = self.unit
local status = GetReadyCheckStatus(unit)
if(UnitExists(unit) and status) then
if(status == 'ready') then
......
......@@ -25,7 +25,9 @@ A default texture will be applied if the widget is a Texture and doesn't have a
local _, ns = ...
local oUF = ns.oUF
local function Update(self, event)
local function Update(self, event, unit)
if(self.unit ~= unit) then return end
local element = self.ResurrectIndicator
--[[ Callback: ResurrectIndicator:PreUpdate()
......@@ -37,7 +39,7 @@ local function Update(self, event)
element:PreUpdate()
end
local incomingResurrect = UnitHasIncomingResurrection(self.unit)
local incomingResurrect = UnitHasIncomingResurrection(unit)
if(incomingResurrect) then
element:Show()
else
......@@ -67,7 +69,7 @@ local function Path(self, ...)
end
local function ForceUpdate(element)
return Path(element.__owner, 'ForceUpdate')
return Path(element.__owner, 'ForceUpdate', element.__owner.unit)
end
local function Enable(self)
......
......@@ -191,7 +191,7 @@ local function Enable(self, unit)
end
end
self:RegisterEvent('PLAYER_SPECIALIZATION_CHANGED', Path, true)
self:RegisterEvent('PLAYER_SPECIALIZATION_CHANGED', Path)
self:RegisterEvent('RUNE_POWER_UPDATE', Path, true)
return true
......
......@@ -31,7 +31,9 @@ local SUMMON_STATUS_PENDING = Enum.SummonStatus.Pending or 1
local SUMMON_STATUS_ACCEPTED = Enum.SummonStatus.Accepted or 2
local SUMMON_STATUS_DECLINED = Enum.SummonStatus.Declined or 3
local function Update(self)
local function Update(self, event, unit)
if(self.unit ~= unit) then return end
local element = self.SummonIndicator
--[[ Callback: SummonIndicator:PreUpdate()
......@@ -43,7 +45,7 @@ local function Update(self)
element:PreUpdate()
end
local status = C_IncomingSummon.IncomingSummonStatus(self.unit)
local status = C_IncomingSummon.IncomingSummonStatus(unit)
if(status ~= SUMMON_STATUS_NONE) then
if(status == SUMMON_STATUS_PENDING) then
element:SetAtlas('Raid-Icon-SummonPending')
......@@ -81,7 +83,7 @@ local function Path(self, ...)
end
local function ForceUpdate(element)
return Path(element.__owner, 'ForceUpdate')
return Path(element.__owner, 'ForceUpdate', element.__owner.unit)
end
local function Enable(self)
......@@ -90,7 +92,7 @@ local function Enable(self)
element.__owner = self
element.ForceUpdate = ForceUpdate
self:RegisterEvent('INCOMING_SUMMON_CHANGED', Path, true)
self:RegisterEvent('INCOMING_SUMMON_CHANGED', Path)
return true
end
......
......@@ -4,6 +4,7 @@ local Private = oUF.Private
local argcheck = Private.argcheck
local error = Private.error
local validateUnit = Private.validateUnit
local frame_metatable = Private.frame_metatable
-- Original event methods
......@@ -12,22 +13,24 @@ local registerUnitEvent = frame_metatable.__index.RegisterUnitEvent
local unregisterEvent = frame_metatable.__index.UnregisterEvent
local isEventRegistered = frame_metatable.__index.IsEventRegistered
local unitEvents = {}
function Private.UpdateUnits(frame, unit, realUnit)
if(unit == realUnit) then
realUnit = nil
end
if(frame.unit ~= unit or frame.realUnit ~= realUnit) then
for event in next, unitEvents do
-- IsEventRegistered returns the units in case of an event
-- registered with RegisterUnitEvent
local registered, unit1 = isEventRegistered(frame, event)
if(registered and unit1 ~= unit) then
-- RegisterUnitEvent erases previously registered units so
-- do not bother to unregister it
registerUnitEvent(frame, event, unit, realUnit)
if(frame.unitEvents) then
for event in next, frame.unitEvents do
local registered, unit1 = isEventRegistered(frame, event)
-- unit event registration for header units is postponed until
-- the frame units are known
-- we don't want to re-register unitless/shared events in case
-- someone added them by hand to the unitEvents table
if(registered and unit1 and unit1 ~= unit or not registered) then
-- BUG: passing explicit nil units to RegisterUnitEvent
-- makes it silently fall back to RegisterEvent
registerUnitEvent(frame, event, unit, realUnit or '')
end
end
end
......@@ -58,12 +61,10 @@ registering events.
* self - frame that will be registered for the given event.
* event - name of the event to register (string)
* func - function that will be executed when the event fires. If a string is passed, then a function by that name
must be defined on the frame. Multiple functions can be added for the same frame and event
(string or function)
* func - a function that will be executed when the event fires. Multiple functions can be added for the same frame
and event (function)
* unitless - indicates that the event does not fire for a specific unit, so the event arguments won't be
matched to the frame unit(s). Events that do not start with UNIT_ or are not known to be unit events are
automatically considered unitless (boolean)
matched to the frame unit(s). Obligatory for unitless event (boolean)
--]]
function frame_metatable.__index:RegisterEvent(event, func, unitless)
-- Block OnUpdate polled frames from registering events except for
......@@ -72,19 +73,11 @@ function frame_metatable.__index:RegisterEvent(event, func, unitless)
if(self.__eventless and event ~= 'UNIT_PORTRAIT_UPDATE' and event ~= 'UNIT_MODEL_CHANGED') then return end
argcheck(event, 2, 'string')
if(type(func) == 'string' and type(self[func]) == 'function') then
func = self[func]
end
-- FIXME: should warn the user.
if(not unitless and not (unitEvents[event] or event:match('^UNIT_'))) then
unitless = true
end
argcheck(func, 3, 'function')
local curev = self[event]
local kind = type(curev)
if(curev and func) then
if(curev) then
if(kind == 'function' and curev ~= func) then
self[event] = setmetatable({curev, func}, event_metatable)
elseif(kind == 'table') then
......@@ -94,24 +87,32 @@ function frame_metatable.__index:RegisterEvent(event, func, unitless)
table.insert(curev, func)
end
elseif(isEventRegistered(self, event)) then
return
else
if(type(func) == 'function') then
self[event] = func
elseif(not self[event]) then
return error("Style [%s] attempted to register event [%s] on unit [%s] with a handler that doesn't exist.", self.style, event, self.unit or 'unknown')
if(unitless) then
-- re-register the event in case we have mixed registration
registerEvent(self, event)
if(self.unitEvents) then
self.unitEvents[event] = nil
end
end
else
self[event] = func
if not self:GetScript('OnEvent') then
if(not self:GetScript('OnEvent')) then
self:SetScript('OnEvent', onEvent)
end
if unitless then
if(unitless) then
registerEvent(self, event)
else
unitEvents[event] = true
registerUnitEvent(self, event, self.unit)
self.unitEvents = self.unitEvents or {}
self.unitEvents[event] = true
-- UpdateUnits will take care of unit event registration for header
-- units in case we don't have a valid unit yet
local unit = self.unit
if(unit and validateUnit(unit)) then
registerUnitEvent(self, event, unit)
end
end
end
end
......@@ -122,7 +123,7 @@ Used to remove a function from the event handler list for a game event.
* self - the frame registered for the event
* event - name of the registered event (string)
* func - function to be removed from the list of event handlers. If this is the only handler for the given event, then
the frame will be unregistered for the event
the frame will be unregistered for the event (function)
--]]
function frame_metatable.__index:UnregisterEvent(event, func)
argcheck(event, 2, 'string')
......@@ -147,6 +148,9 @@ function frame_metatable.__index:UnregisterEvent(event, func)
end
elseif(curev == func) then
self[event] = nil
if(self.unitEvents) then
self.unitEvents[event] = nil
end
unregisterEvent(self, event)
end
end
......@@ -263,7 +263,7 @@ local function initObject(unit, style, styleFunc, header, ...)
table.insert(objects, object)
-- We have to force update the frames when PEW fires.
object:RegisterEvent('PLAYER_ENTERING_WORLD', object.UpdateAllElements)
object:RegisterEvent('PLAYER_ENTERING_WORLD', object.UpdateAllElements, true)
-- Handle the case where someone has modified the unitsuffix attribute in
-- oUF-initialConfigFunction.
......@@ -280,7 +280,7 @@ local function initObject(unit, style, styleFunc, header, ...)
-- mainly because UNIT_EXITED_VEHICLE and UNIT_ENTERED_VEHICLE doesn't always
-- have pet information when they fire for party and raid units.
if(objectUnit ~= 'player') then
object:RegisterEvent('UNIT_PET', updatePet, true)
object:RegisterEvent('UNIT_PET', updatePet)
end
end
......@@ -302,7 +302,7 @@ local function initObject(unit, style, styleFunc, header, ...)
end
else
-- Used to update frames when they change position in a group.
object:RegisterEvent('GROUP_ROSTER_UPDATE', object.UpdateAllElements)
object:RegisterEvent('GROUP_ROSTER_UPDATE', object.UpdateAllElements, true)
if(num > 1) then
if(object:GetParent() == header) then
......
......@@ -24,3 +24,17 @@ end
function Private.UnitExists(unit)
return unit and (UnitExists(unit) or ShowBossFrameWhenUninteractable(unit))
end
local validator = CreateFrame('Frame')
function Private.validateUnit(unit)
local isOK, _ = pcall(validator.RegisterUnitEvent, validator, 'UNIT_HEALTH', unit)
if isOK then
_, unit = validator:IsEventRegistered('UNIT_HEALTH')
if unit then
validator:UnregisterEvent('UNIT_HEALTH')
return true
end
end
end
......@@ -166,16 +166,16 @@ end
function oUF:HandleUnit(object, unit)
local unit = object.unit or unit
if(unit == 'target') then
object:RegisterEvent('PLAYER_TARGET_CHANGED', object.UpdateAllElements)
object:RegisterEvent('PLAYER_TARGET_CHANGED', object.UpdateAllElements, true)
elseif(unit == 'mouseover') then
object:RegisterEvent('UPDATE_MOUSEOVER_UNIT', object.UpdateAllElements)
object:RegisterEvent('UPDATE_MOUSEOVER_UNIT', object.UpdateAllElements, true)
elseif(unit == 'focus') then
object:RegisterEvent('PLAYER_FOCUS_CHANGED', object.UpdateAllElements)
object:RegisterEvent('PLAYER_FOCUS_CHANGED', object.UpdateAllElements, true)
elseif(unit:match('boss%d?$')) then
object:RegisterEvent('INSTANCE_ENCOUNTER_ENGAGE_UNIT', object.UpdateAllElements, true)
object:RegisterEvent('UNIT_TARGETABLE_CHANGED', object.UpdateAllElements)
elseif(unit:match('arena%d?$')) then
object:RegisterEvent('ARENA_OPPONENT_UPDATE', object.UpdateAllElements)
object:RegisterEvent('ARENA_OPPONENT_UPDATE', object.UpdateAllElements, true)
object:RegisterEvent('ARENA_PREP_OPPONENT_SPECIALIZATIONS', updateArenaPreparation, true)
object:SetAttribute('oUF-enableArenaPrep', true)
-- the event handler only fires for visible frames, so we have to hook it for arena prep
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment