port LibGetFrame to newest
depends on AceTimer, can have regressions due to lack of testing
This commit is contained in:
@@ -0,0 +1,327 @@
|
||||
--- **AceTimer-3.0** provides a central facility for registering timers.
|
||||
-- AceTimer supports one-shot timers and repeating timers. All timers are stored in an efficient
|
||||
-- data structure that allows easy dispatching and fast rescheduling. Timers can be registered
|
||||
-- or canceled at any time, even from within a running timer, without conflict or large overhead.\\
|
||||
-- AceTimer is currently limited to firing timers at a frequency of 0.01s.
|
||||
--
|
||||
-- All `:Schedule` functions will return a handle to the current timer, which you will need to store if you
|
||||
-- need to cancel the timer you just registered.
|
||||
--
|
||||
-- **AceTimer-3.0** can be embeded into your addon, either explicitly by calling AceTimer:Embed(MyAddon) or by
|
||||
-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
|
||||
-- and can be accessed directly, without having to explicitly call AceTimer itself.\\
|
||||
-- It is recommended to embed AceTimer, otherwise you'll have to specify a custom `self` on all calls you
|
||||
-- make into AceTimer.
|
||||
-- @class file
|
||||
-- @name AceTimer-3.0
|
||||
-- @release $Id$
|
||||
|
||||
local MAJOR, MINOR = "AceTimer-3.0", 1017 -- Bump minor on changes
|
||||
local AceTimer, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
|
||||
|
||||
if not AceTimer then return end -- No upgrade needed
|
||||
AceTimer.frame = AceTimer.frame or CreateFrame("Frame", "AceTimer30Frame")
|
||||
AceTimer.activeTimers = AceTimer.activeTimers or {} -- Active timer list
|
||||
local activeTimers = AceTimer.activeTimers -- Upvalue our private data
|
||||
|
||||
-- Lua APIs
|
||||
local assert, loadstring, rawset, tconcat = assert, loadstring, rawset, table.concat
|
||||
local type, unpack, next, error, select = type, unpack, next, error, select
|
||||
-- WoW APIs
|
||||
local GetTime = GetTime
|
||||
|
||||
--[[
|
||||
xpcall safecall implementation
|
||||
]]
|
||||
local xpcall = xpcall
|
||||
|
||||
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, ...)
|
||||
return Dispatchers[select("#", ...)](func, ...)
|
||||
end
|
||||
|
||||
local function new(self, loop, func, delay, ...)
|
||||
if delay < 0.01 then
|
||||
delay = 0.01 -- Restrict to the lowest time
|
||||
end
|
||||
|
||||
local timer = {
|
||||
object = self,
|
||||
func = func,
|
||||
looping = loop,
|
||||
argsCount = select("#", ...),
|
||||
delay = delay,
|
||||
timeleft = delay,
|
||||
ends = GetTime() + delay,
|
||||
...
|
||||
}
|
||||
|
||||
activeTimers[timer] = timer
|
||||
|
||||
return timer
|
||||
end
|
||||
|
||||
--- Schedule a new one-shot timer.
|
||||
-- The timer will fire once in `delay` seconds, unless canceled before.
|
||||
-- @param callback Callback function for the timer pulse (funcref or method name).
|
||||
-- @param delay Delay for the timer, in seconds.
|
||||
-- @param ... An optional, unlimited amount of arguments to pass to the callback function.
|
||||
-- @usage
|
||||
-- MyAddOn = LibStub("AceAddon-3.0"):NewAddon("MyAddOn", "AceTimer-3.0")
|
||||
--
|
||||
-- function MyAddOn:OnEnable()
|
||||
-- self:ScheduleTimer("TimerFeedback", 5)
|
||||
-- end
|
||||
--
|
||||
-- function MyAddOn:TimerFeedback()
|
||||
-- print("5 seconds passed")
|
||||
-- end
|
||||
function AceTimer:ScheduleTimer(func, delay, ...)
|
||||
if not func or not delay then
|
||||
error(MAJOR..": ScheduleTimer(callback, delay, args...): 'callback' and 'delay' must have set values.", 2)
|
||||
end
|
||||
if type(func) == "string" then
|
||||
if type(self) ~= "table" then
|
||||
error(MAJOR..": ScheduleTimer(callback, delay, args...): 'self' - must be a table.", 2)
|
||||
elseif not self[func] then
|
||||
error(MAJOR..": ScheduleTimer(callback, delay, args...): Tried to register '"..func.."' as the callback, but it doesn't exist in the module.", 2)
|
||||
end
|
||||
end
|
||||
return new(self, nil, func, delay, ...)
|
||||
end
|
||||
|
||||
--- Schedule a repeating timer.
|
||||
-- The timer will fire every `delay` seconds, until canceled.
|
||||
-- @param callback Callback function for the timer pulse (funcref or method name).
|
||||
-- @param delay Delay for the timer, in seconds.
|
||||
-- @param ... An optional, unlimited amount of arguments to pass to the callback function.
|
||||
-- @usage
|
||||
-- MyAddOn = LibStub("AceAddon-3.0"):NewAddon("MyAddOn", "AceTimer-3.0")
|
||||
--
|
||||
-- function MyAddOn:OnEnable()
|
||||
-- self.timerCount = 0
|
||||
-- self.testTimer = self:ScheduleRepeatingTimer("TimerFeedback", 5)
|
||||
-- end
|
||||
--
|
||||
-- function MyAddOn:TimerFeedback()
|
||||
-- self.timerCount = self.timerCount + 1
|
||||
-- print(("%d seconds passed"):format(5 * self.timerCount))
|
||||
-- -- run 30 seconds in total
|
||||
-- if self.timerCount == 6 then
|
||||
-- self:CancelTimer(self.testTimer)
|
||||
-- end
|
||||
-- end
|
||||
function AceTimer:ScheduleRepeatingTimer(func, delay, ...)
|
||||
if not func or not delay then
|
||||
error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): 'callback' and 'delay' must have set values.", 2)
|
||||
end
|
||||
if type(func) == "string" then
|
||||
if type(self) ~= "table" then
|
||||
error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): 'self' - must be a table.", 2)
|
||||
elseif not self[func] then
|
||||
error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): Tried to register '"..func.."' as the callback, but it doesn't exist in the module.", 2)
|
||||
end
|
||||
end
|
||||
return new(self, true, func, delay, ...)
|
||||
end
|
||||
|
||||
--- Cancels a timer with the given id, registered by the same addon object as used for `:ScheduleTimer`
|
||||
-- Both one-shot and repeating timers can be canceled with this function, as long as the `id` is valid
|
||||
-- and the timer has not fired yet or was canceled before.
|
||||
-- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
|
||||
function AceTimer:CancelTimer(id)
|
||||
local timer = activeTimers[id]
|
||||
|
||||
if not timer then
|
||||
return false
|
||||
else
|
||||
timer.cancelled = true
|
||||
activeTimers[id] = nil
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
--- Cancels all timers registered to the current addon object ('self')
|
||||
function AceTimer:CancelAllTimers()
|
||||
for k,v in next, activeTimers do
|
||||
if v.object == self then
|
||||
AceTimer.CancelTimer(self, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Returns the time left for a timer with the given id, registered by the current addon object ('self').
|
||||
-- This function will return 0 when the id is invalid.
|
||||
-- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer`
|
||||
-- @return The time left on the timer.
|
||||
function AceTimer:TimeLeft(id)
|
||||
local timer = activeTimers[id]
|
||||
if not timer then
|
||||
return
|
||||
else
|
||||
return timer.ends - GetTime()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- Upgrading
|
||||
|
||||
-- Upgrade from old hash-bucket based timers to C_Timer.After timers.
|
||||
if oldminor and oldminor < 10 then
|
||||
-- disable old timer logic
|
||||
AceTimer.frame:SetScript("OnUpdate", nil)
|
||||
AceTimer.frame:SetScript("OnEvent", nil)
|
||||
AceTimer.frame:UnregisterAllEvents()
|
||||
-- convert timers
|
||||
for object,timers in next, AceTimer.selfs do
|
||||
for handle,timer in next, timers do
|
||||
if type(timer) == "table" and timer.callback then
|
||||
local newTimer
|
||||
if timer.delay then
|
||||
newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.callback, timer.delay, timer.arg)
|
||||
else
|
||||
newTimer = AceTimer.ScheduleTimer(timer.object, timer.callback, timer.when - GetTime(), timer.arg)
|
||||
end
|
||||
-- Use the old handle for old timers
|
||||
activeTimers[newTimer] = nil
|
||||
activeTimers[handle] = newTimer
|
||||
newTimer.handle = handle
|
||||
end
|
||||
end
|
||||
end
|
||||
AceTimer.selfs = nil
|
||||
AceTimer.hash = nil
|
||||
AceTimer.debug = nil
|
||||
elseif oldminor and oldminor < 17 then
|
||||
-- Upgrade from old animation based timers to C_Timer.After timers.
|
||||
AceTimer.inactiveTimers = nil
|
||||
local oldTimers = AceTimer.activeTimers
|
||||
-- Clear old timer table and update upvalue
|
||||
AceTimer.activeTimers = {}
|
||||
activeTimers = AceTimer.activeTimers
|
||||
for handle, timer in next, oldTimers do
|
||||
local newTimer
|
||||
-- Stop the old timer animation
|
||||
local duration, elapsed = timer:GetDuration(), timer:GetElapsed()
|
||||
timer:GetParent():Stop()
|
||||
if timer.looping then
|
||||
newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.func, duration, unpack(timer.args, 1, timer.argsCount))
|
||||
else
|
||||
newTimer = AceTimer.ScheduleTimer(timer.object, timer.func, duration - elapsed, unpack(timer.args, 1, timer.argsCount))
|
||||
end
|
||||
-- Use the old handle for old timers
|
||||
activeTimers[newTimer] = nil
|
||||
activeTimers[handle] = newTimer
|
||||
newTimer.handle = handle
|
||||
end
|
||||
|
||||
-- Migrate transitional handles
|
||||
if oldminor < 13 and AceTimer.hashCompatTable then
|
||||
for handle, id in next, AceTimer.hashCompatTable do
|
||||
local t = activeTimers[id]
|
||||
if t then
|
||||
activeTimers[id] = nil
|
||||
activeTimers[handle] = t
|
||||
t.handle = handle
|
||||
end
|
||||
end
|
||||
AceTimer.hashCompatTable = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- Embed handling
|
||||
|
||||
AceTimer.embeds = AceTimer.embeds or {}
|
||||
|
||||
local mixins = {
|
||||
"ScheduleTimer", "ScheduleRepeatingTimer",
|
||||
"CancelTimer", "CancelAllTimers",
|
||||
"TimeLeft"
|
||||
}
|
||||
|
||||
function AceTimer:Embed(target)
|
||||
AceTimer.embeds[target] = true
|
||||
for _,v in next, mixins do
|
||||
target[v] = AceTimer[v]
|
||||
end
|
||||
return target
|
||||
end
|
||||
|
||||
-- AceTimer:OnEmbedDisable(target)
|
||||
-- target (object) - target object that AceTimer is embedded in.
|
||||
--
|
||||
-- cancel all timers registered for the object
|
||||
function AceTimer:OnEmbedDisable(target)
|
||||
target:CancelAllTimers()
|
||||
end
|
||||
|
||||
for addon in next, AceTimer.embeds do
|
||||
AceTimer:Embed(addon)
|
||||
end
|
||||
|
||||
AceTimer.frame:SetScript("OnUpdate", function(self, elapsed)
|
||||
for _, timer in next, activeTimers do
|
||||
if not timer.cancelled then
|
||||
if timer.timeleft > elapsed then
|
||||
timer.timeleft = timer.timeleft - elapsed
|
||||
else
|
||||
if type(timer.func) == "string" then
|
||||
-- We manually set the unpack count to prevent issues with an arg set that contains nil and ends with nil
|
||||
-- e.g. local t = {1, 2, nil, 3, nil} print(#t) will result in 2, instead of 5. This fixes said issue.
|
||||
safecall(timer.object[timer.func], timer.object, unpack(timer, 1, timer.argsCount))
|
||||
else
|
||||
safecall(timer.func, unpack(timer, 1, timer.argsCount))
|
||||
end
|
||||
|
||||
if timer.looping and not timer.cancelled then
|
||||
-- Compensate delay to get a perfect average delay, even if individual times don't match up perfectly
|
||||
-- due to fps differences
|
||||
local time = GetTime()
|
||||
local delay = timer.delay - (time - timer.ends)
|
||||
-- Ensure the delay doesn't go below the threshold
|
||||
if delay < 0.01 then delay = 0.01 end
|
||||
timer.ends = time + delay
|
||||
timer.timeleft = timer.delay
|
||||
else
|
||||
activeTimers[timer.handle or timer] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,4 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\FrameXML\UI.xsd">
|
||||
<Script file="AceTimer-3.0.lua"/>
|
||||
</Ui>
|
||||
@@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
@@ -1,342 +1,724 @@
|
||||
local MAJOR_VERSION = "LibGetFrame-1.0"
|
||||
local MINOR_VERSION = 26
|
||||
if not LibStub then error(MAJOR_VERSION .. " requires LibStub.") end
|
||||
local MINOR_VERSION = 63
|
||||
if not LibStub then
|
||||
error(MAJOR_VERSION .. " requires LibStub.")
|
||||
end
|
||||
local lib = LibStub:NewLibrary(MAJOR_VERSION, MINOR_VERSION)
|
||||
if not lib then return end
|
||||
if not lib then
|
||||
return
|
||||
end
|
||||
|
||||
lib.callbacks = lib.callbacks or LibStub("CallbackHandler-1.0"):New(lib)
|
||||
lib.timer = lib.timer or LibStub("AceTimer-3.0")
|
||||
local callbacks = lib.callbacks
|
||||
|
||||
local GetPlayerInfoByGUID, UnitExists, IsAddOnLoaded, UnitIsUnit, SecureButton_GetUnit = GetPlayerInfoByGUID, UnitExists, IsAddOnLoaded, UnitIsUnit, SecureButton_GetUnit
|
||||
local GetPlayerInfoByGUID, UnitExists, UnitIsUnit, SecureButton_GetUnit, IsAddOnLoaded =
|
||||
GetPlayerInfoByGUID, UnitExists, UnitIsUnit, SecureButton_GetUnit, IsAddOnLoaded
|
||||
local tinsert, CopyTable, wipe = tinsert, CopyTable, wipe
|
||||
|
||||
function lib.Mixin(object, ...)
|
||||
for i = 1, select("#", ...) do
|
||||
local mixin = select(i, ...);
|
||||
for k, v in pairs(mixin) do
|
||||
object[k] = v;
|
||||
end
|
||||
end
|
||||
return object;
|
||||
end
|
||||
|
||||
local maxDepth = 50
|
||||
|
||||
local defaultFramePriorities = {
|
||||
-- raid frames
|
||||
"^Vd1", -- vuhdo
|
||||
"^Vd2", -- vuhdo
|
||||
"^Vd3", -- vuhdo
|
||||
"^Vd4", -- vuhdo
|
||||
"^Vd5", -- vuhdo
|
||||
"^Vd", -- vuhdo
|
||||
"^HealBot", -- healbot
|
||||
"^GridLayout", -- grid
|
||||
"^Grid2Layout", -- grid2
|
||||
"^PlexusLayout", -- plexus
|
||||
"^ElvUF_RaidGroup", -- elv
|
||||
"^oUF_bdGrid", -- bdgrid
|
||||
"^oUF_.-Raid", -- generic oUF
|
||||
"^LimeGroup", -- lime
|
||||
"^SUFHeaderraid", -- suf
|
||||
-- party frames
|
||||
"^AleaUI_GroupHeader", -- Alea
|
||||
"^SUFHeaderparty", --suf
|
||||
"^ElvUF_PartyGroup", -- elv
|
||||
"^oUF_.-Party", -- generic oUF
|
||||
"^PitBull4_Groups_Party", -- pitbull4
|
||||
"^CompactRaid", -- blizz
|
||||
"^PartyMemberFrame", -- blizz
|
||||
-- player frame
|
||||
"^SUFUnitplayer",
|
||||
"^PitBull4_Frames_Player",
|
||||
"^ElvUF_Player",
|
||||
"^oUF_.-Player",
|
||||
"^PlayerFrame",
|
||||
-- raid frames
|
||||
"^Vd1", -- vuhdo
|
||||
"^Vd2", -- vuhdo
|
||||
"^Vd3", -- vuhdo
|
||||
"^Vd4", -- vuhdo
|
||||
"^Vd5", -- vuhdo
|
||||
"^Vd", -- vuhdo
|
||||
"^HealBot_HealUnit", -- healbot
|
||||
"^hbPet_HealUnit", -- healbot
|
||||
"^HealBot", -- healbot
|
||||
"^GridLayout", -- grid
|
||||
"^Grid2Layout", -- grid2
|
||||
"^NugRaid%d+UnitButton%d+", -- Aptechka
|
||||
"^PlexusLayout", -- plexus
|
||||
"^ElvUF_Raid%d*Group", -- elv
|
||||
"^ElvUF_RaidGroup", -- elv
|
||||
"^oUF_bdGrid", -- bdgrid
|
||||
"^oUF_.-Raid", -- generic oUF
|
||||
"^LimeGroup", -- lime
|
||||
"^InvenRaidFrames3Group%dUnitButton", -- InvenRaidFrames3
|
||||
"^SUFHeaderraid", -- suf
|
||||
"^LUFHeaderraid", -- luf
|
||||
"^AshToAshUnit%d+Unit%d+", -- AshToAsh
|
||||
"^Cell", -- Cell
|
||||
-- party frames
|
||||
"^AleaUI_GroupHeader", -- Alea
|
||||
"^SUFHeaderparty", -- suf
|
||||
"^LUFHeaderparty", -- luf
|
||||
"^ElvUF_PartyGroup", -- elv
|
||||
"^oUF_.-Party", -- generic oUF
|
||||
"^PitBull4_Groups_Party", -- pitbull4
|
||||
"^CompactRaid", -- blizz
|
||||
"^CompactParty", -- blizz
|
||||
"^PartyFrame", -- blizz
|
||||
"^PartyMemberFrame", -- blizz
|
||||
-- player frame
|
||||
"^InvenUnitFrames_Player",
|
||||
"^SUFUnitplayer",
|
||||
"^LUFUnitplayer",
|
||||
"^PitBull4_Frames_Player",
|
||||
"^ElvUF_Player",
|
||||
"^oUF_.-Player",
|
||||
"^PlayerFrame",
|
||||
}
|
||||
local getDefaultFramePriorities = function()
|
||||
return CopyTable(defaultFramePriorities)
|
||||
end
|
||||
lib.getDefaultFramePriorities = getDefaultFramePriorities
|
||||
|
||||
local defaultPlayerFrames = {
|
||||
"SUFUnitplayer",
|
||||
"PitBull4_Frames_Player",
|
||||
"ElvUF_Player",
|
||||
"oUF_.-Player",
|
||||
"oUF_PlayerPlate",
|
||||
"PlayerFrame",
|
||||
}
|
||||
local defaultTargetFrames = {
|
||||
"SUFUnittarget",
|
||||
"PitBull4_Frames_Target",
|
||||
"ElvUF_Target",
|
||||
"oUF_.-Target",
|
||||
"TargetFrame",
|
||||
}
|
||||
local defaultTargettargetFrames = {
|
||||
"SUFUnittargetarget",
|
||||
"PitBull4_Frames_Target's target",
|
||||
"ElvUF_TargetTarget",
|
||||
"oUF_.-TargetTarget",
|
||||
"oUF_ToT",
|
||||
"TargetTargetFrame",
|
||||
}
|
||||
local defaultPartyFrames = {
|
||||
"^AleaUI_GroupHeader",
|
||||
"^SUFHeaderparty",
|
||||
"^ElvUF_PartyGroup",
|
||||
"^oUF_.-Party",
|
||||
"^PitBull4_Groups_Party",
|
||||
"^PartyMemberFrame",
|
||||
}
|
||||
local defaultPartyTargetFrames = {
|
||||
"SUFChildpartytarget%d",
|
||||
}
|
||||
local defaultRaidFrames = {
|
||||
"^Vd",
|
||||
"^HealBot",
|
||||
"^GridLayout",
|
||||
"^Grid2Layout",
|
||||
"^PlexusLayout",
|
||||
"^ElvUF_RaidGroup",
|
||||
"^oUF_.-Raid",
|
||||
"^LimeGroup",
|
||||
"^SUFHeaderraid",
|
||||
"^RaidPullout",
|
||||
"^InvenUnitFrames_Player",
|
||||
"SUFUnitplayer",
|
||||
"LUFUnitplayer",
|
||||
"PitBull4_Frames_Player",
|
||||
"ElvUF_Player",
|
||||
"oUF_.-Player",
|
||||
"oUF_PlayerPlate",
|
||||
"PlayerFrame",
|
||||
}
|
||||
local getDefaultPlayerFrames = function()
|
||||
return CopyTable(defaultPlayerFrames)
|
||||
end
|
||||
lib.getDefaultPlayerFrames = getDefaultPlayerFrames
|
||||
|
||||
local GetFramesCache = {}
|
||||
local FrameToUnitFresh = {}
|
||||
local FrameToUnit = {}
|
||||
local UpdatedFrames = {}
|
||||
local defaultTargetFrames = {
|
||||
"^InvenUnitFrames_Target",
|
||||
"SUFUnittarget",
|
||||
"LUFUnittarget",
|
||||
"PitBull4_Frames_Target",
|
||||
"ElvUF_Target",
|
||||
"oUF_.-Target",
|
||||
"TargetFrame",
|
||||
"^hbExtra_HealUnit",
|
||||
}
|
||||
local getDefaultTargetFrames = function()
|
||||
return CopyTable(defaultTargetFrames)
|
||||
end
|
||||
lib.getDefaultTargetFrames = getDefaultTargetFrames
|
||||
|
||||
local defaultTargettargetFrames = {
|
||||
"^InvenUnitFrames_TargetTarget",
|
||||
"SUFUnittargetarget",
|
||||
"LUFUnittargetarget",
|
||||
"PitBull4_Frames_Target's target",
|
||||
"ElvUF_TargetTarget",
|
||||
"oUF_.-TargetTarget",
|
||||
"oUF_ToT",
|
||||
"TargetTargetFrame",
|
||||
}
|
||||
local getDefaultTargettargetFrames = function()
|
||||
return CopyTable(defaultTargettargetFrames)
|
||||
end
|
||||
lib.getDefaultTargettargetFrames = getDefaultTargettargetFrames
|
||||
|
||||
local defaultPartyFrames = {
|
||||
"^InvenUnitFrames_Party%d",
|
||||
"^AleaUI_GroupHeader",
|
||||
"^SUFHeaderparty",
|
||||
"^LUFHeaderparty",
|
||||
"^ElvUF_PartyGroup",
|
||||
"^oUF_.-Party",
|
||||
"^PitBull4_Groups_Party",
|
||||
"^PartyFrame",
|
||||
"^CompactParty",
|
||||
"^PartyMemberFrame",
|
||||
}
|
||||
local getDefaultPartyFrames = function()
|
||||
return CopyTable(defaultPartyFrames)
|
||||
end
|
||||
lib.getDefaultPartyFrames = getDefaultPartyFrames
|
||||
|
||||
local defaultPartyTargetFrames = {
|
||||
"SUFChildpartytarget%d",
|
||||
}
|
||||
local getDefaultPartyTargetFrames = function()
|
||||
return CopyTable(defaultPartyTargetFrames)
|
||||
end
|
||||
lib.getDefaultPartyTargetFrames = getDefaultPartyTargetFrames
|
||||
|
||||
local defaultFocusFrames = {
|
||||
"^InvenUnitFrames_Focus",
|
||||
"ElvUF_FocusTarget",
|
||||
"LUFUnitfocus",
|
||||
"FocusFrame",
|
||||
"^hbExtra_HealUnit",
|
||||
}
|
||||
local getDefaultFocusFrames = function()
|
||||
return CopyTable(defaultFocusFrames)
|
||||
end
|
||||
lib.getDefaultFocusFrames = getDefaultFocusFrames
|
||||
|
||||
local defaultRaidFrames = {
|
||||
"^Vd",
|
||||
"^HealBot_HealUnit",
|
||||
"^hbPet_HealUnit",
|
||||
"^HealBot",
|
||||
"^GridLayout",
|
||||
"^Grid2Layout",
|
||||
"^PlexusLayout",
|
||||
"^InvenRaidFrames3Group%dUnitButton",
|
||||
"^ElvUF_Raid%d*Group",
|
||||
"^ElvUF_RaidGroup",
|
||||
"^oUF_.-Raid",
|
||||
"^AshToAsh",
|
||||
"^Cell",
|
||||
"^LimeGroup",
|
||||
"^SUFHeaderraid",
|
||||
"^LUFHeaderraid",
|
||||
"^CompactRaid",
|
||||
"^RaidPullout",
|
||||
}
|
||||
local getDefaultRaidFrames = function()
|
||||
return CopyTable(defaultRaidFrames)
|
||||
end
|
||||
lib.getDefaultRaidFrames = getDefaultRaidFrames
|
||||
--
|
||||
local CacheMonitorMixin = {}
|
||||
function CacheMonitorMixin:Init(makeDiff)
|
||||
self.data = {}
|
||||
self.cache = {}
|
||||
if makeDiff then
|
||||
self.makeDiff = makeDiff
|
||||
self.added = {}
|
||||
self.updated = {}
|
||||
self.removed = {}
|
||||
end
|
||||
end
|
||||
-- fill cache, added, updated
|
||||
function CacheMonitorMixin:Add(key, ...)
|
||||
local args = select("#", ...)
|
||||
if args > 1 then
|
||||
if self.makeDiff then
|
||||
if type(self.data[key]) == "table" then
|
||||
for i = 1, args do
|
||||
local arg = select(i, ...)
|
||||
if self.data[key][i] ~= arg then
|
||||
self.updated[key] = self.data[key]
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
self.added[key] = true
|
||||
end
|
||||
end
|
||||
self.cache[key] = {...}
|
||||
else
|
||||
local value = ...
|
||||
if self.makeDiff then
|
||||
if self.data[key] ~= value then
|
||||
if self.data[key] == nil then
|
||||
self.added[key] = true
|
||||
else
|
||||
self.updated[key] = self.data[key]
|
||||
end
|
||||
end
|
||||
end
|
||||
self.cache[key] = value
|
||||
end
|
||||
end
|
||||
function CacheMonitorMixin:CalcRemoved()
|
||||
if not self.makeDiff then return end
|
||||
for key, value in pairs(self.data) do
|
||||
if self.cache[key] == nil then
|
||||
self.removed[key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
function CacheMonitorMixin:WriteCache()
|
||||
local tmp = self.data
|
||||
self.data = self.cache
|
||||
self.cache = tmp
|
||||
wipe(self.cache)
|
||||
end
|
||||
function CacheMonitorMixin:Reset()
|
||||
if self.makeDiff then
|
||||
wipe(self.updated)
|
||||
wipe(self.removed)
|
||||
wipe(self.added)
|
||||
end
|
||||
end
|
||||
--
|
||||
local FrameToFrameName = {} -- frame adress => frame name
|
||||
local FrameToUnit = {} -- frame adress => unitToken
|
||||
lib.Mixin(FrameToFrameName, CacheMonitorMixin)
|
||||
lib.Mixin(FrameToUnit, CacheMonitorMixin)
|
||||
FrameToFrameName:Init()
|
||||
FrameToUnit:Init(true)
|
||||
|
||||
local profiling = false
|
||||
local profileData
|
||||
|
||||
local function doNothing()
|
||||
end
|
||||
|
||||
local StartProfiling = doNothing
|
||||
local StopProfiling = doNothing
|
||||
|
||||
local function _StartProfiling(id)
|
||||
if not profileData[id] then
|
||||
profileData[id] = {}
|
||||
profileData[id].count = 1
|
||||
profileData[id].start = debugprofilestop()
|
||||
profileData[id].elapsed = 0
|
||||
profileData[id].spike = 0
|
||||
return
|
||||
end
|
||||
|
||||
if profileData[id].count == 0 then
|
||||
profileData[id].count = 1
|
||||
profileData[id].start = debugprofilestop()
|
||||
else
|
||||
profileData[id].count = profileData[id].count + 1
|
||||
end
|
||||
end
|
||||
|
||||
local function _StopProfiling(id)
|
||||
profileData[id].count = profileData[id].count - 1
|
||||
if profileData[id].count == 0 then
|
||||
local elapsed = debugprofilestop() - profileData[id].start
|
||||
profileData[id].elapsed = profileData[id].elapsed + elapsed
|
||||
if elapsed > profileData[id].spike then
|
||||
profileData[id].spike = elapsed
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function lib.StartProfile()
|
||||
if profiling then
|
||||
print(MAJOR_VERSION, " (StartProfile) Profiling already started")
|
||||
return false
|
||||
end
|
||||
profiling = true
|
||||
profileData = {}
|
||||
StartProfiling = _StartProfiling
|
||||
StopProfiling = _StopProfiling
|
||||
end
|
||||
|
||||
function lib.StopProfile()
|
||||
if not profiling then
|
||||
print(MAJOR_VERSION, " (StopProfile) Profiling not running")
|
||||
return false
|
||||
end
|
||||
profiling = false
|
||||
StartProfiling = doNothing
|
||||
StopProfiling = doNothing
|
||||
end
|
||||
|
||||
function lib.GetProfileData()
|
||||
return profileData or {}
|
||||
end
|
||||
|
||||
-- if frame doesn't have a name, try to use the key from it's parent
|
||||
local function recurseGetName(frame)
|
||||
local name = frame.GetName and frame:GetName() or nil
|
||||
if name then
|
||||
return name
|
||||
end
|
||||
local parent = frame.GetParent and frame:GetParent()
|
||||
if parent then
|
||||
local parentKey
|
||||
for key, child in pairs(parent) do
|
||||
if child == frame then
|
||||
parentKey = key
|
||||
break
|
||||
end
|
||||
end
|
||||
if parentKey then
|
||||
return (recurseGetName(parent) or "") .. "." .. parentKey
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--local notAUnitFrameTypeAttribute = {
|
||||
-- cancelaura = true
|
||||
--}
|
||||
|
||||
local function ScanFrames(depth, frame, ...)
|
||||
if not frame then return end
|
||||
if depth < maxDepth then
|
||||
local frameType = frame:GetObjectType()
|
||||
if frameType == "Frame" or frameType == "Button" then
|
||||
ScanFrames(depth + 1, frame:GetChildren())
|
||||
end
|
||||
if frameType == "Button" then
|
||||
local unit = SecureButton_GetUnit(frame)
|
||||
local name = frame:GetName()
|
||||
if unit and frame:IsVisible() and name then
|
||||
GetFramesCache[frame] = name
|
||||
if unit ~= FrameToUnit[frame] then
|
||||
FrameToUnit[frame] = unit
|
||||
UpdatedFrames[frame] = unit
|
||||
end
|
||||
FrameToUnitFresh[frame] = unit
|
||||
end
|
||||
end
|
||||
coroutine.yield()
|
||||
if not frame then
|
||||
return
|
||||
end
|
||||
if depth < maxDepth then
|
||||
local frameType = frame:GetObjectType()
|
||||
if frameType == "Frame" or frameType == "Button" then
|
||||
ScanFrames(depth + 1, frame:GetChildren())
|
||||
end
|
||||
ScanFrames(depth, ...)
|
||||
if frameType == "Button" then
|
||||
local typeAttribute = frame:GetAttribute("type")
|
||||
--if not notAUnitFrameTypeAttribute[typeAttribute] then
|
||||
local unit = SecureButton_GetUnit(frame)
|
||||
if unit and frame:IsVisible() then
|
||||
local name = recurseGetName(frame)
|
||||
if name then
|
||||
FrameToFrameName:Add(frame, name)
|
||||
FrameToUnit:Add(frame, unit)
|
||||
end
|
||||
end
|
||||
--end
|
||||
end
|
||||
end
|
||||
ScanFrames(depth, ...)
|
||||
end
|
||||
|
||||
local wait = false
|
||||
local status = "ready"
|
||||
local co
|
||||
local coroutineFrame = CreateFrame("Frame")
|
||||
coroutineFrame:Hide()
|
||||
|
||||
local function doScanForUnitFrames()
|
||||
wait = false
|
||||
wipe(UpdatedFrames)
|
||||
wipe(GetFramesCache)
|
||||
wipe(FrameToUnitFresh)
|
||||
ScanFrames(0, UIParent)
|
||||
callbacks:Fire("GETFRAME_REFRESH")
|
||||
for frame, unit in pairs(UpdatedFrames) do
|
||||
callbacks:Fire("FRAME_UNIT_UPDATE", frame, unit)
|
||||
end
|
||||
for frame, unit in pairs(FrameToUnit) do
|
||||
if FrameToUnitFresh[frame] ~= unit then
|
||||
callbacks:Fire("FRAME_UNIT_REMOVED", frame, unit)
|
||||
FrameToUnit[frame] = nil
|
||||
end
|
||||
end
|
||||
if not coroutineFrame:IsShown() then
|
||||
status = "scanning"
|
||||
co = coroutine.create(ScanFrames)
|
||||
coroutineFrame:Show()
|
||||
end
|
||||
end
|
||||
|
||||
local waitFrame = CreateFrame("Frame")
|
||||
local function waitFrame_OnUpdate(self, elapsed)
|
||||
self.delay = (self.delay or 1) - elapsed
|
||||
if self.delay < 0 then
|
||||
doScanForUnitFrames()
|
||||
self:SetScript("OnUpdate", nil)
|
||||
self.delay = nil
|
||||
end
|
||||
end
|
||||
coroutineFrame:SetScript("OnUpdate", function()
|
||||
local start = debugprofilestop()
|
||||
-- Limit to 5ms per frame
|
||||
StartProfiling("scan frames")
|
||||
while debugprofilestop() - start < 5 and coroutine.status(co) ~= "dead" do
|
||||
coroutine.resume(co, 0, UIParent)
|
||||
end
|
||||
StopProfiling("scan frames")
|
||||
if coroutine.status(co) == "dead" then
|
||||
StartProfiling("callbacks")
|
||||
FrameToFrameName:WriteCache()
|
||||
FrameToUnit:CalcRemoved()
|
||||
FrameToUnit:WriteCache()
|
||||
StartProfiling("callback GETFRAME_REFRESH")
|
||||
callbacks:Fire("GETFRAME_REFRESH")
|
||||
StopProfiling("callback GETFRAME_REFRESH")
|
||||
-- FrameToUnit
|
||||
if next(FrameToUnit.added) then
|
||||
StartProfiling("callback FRAME_UNIT_ADDED")
|
||||
for frame in pairs(FrameToUnit.added) do
|
||||
callbacks:Fire("FRAME_UNIT_ADDED", frame, FrameToUnit.data[frame])
|
||||
end
|
||||
StopProfiling("callback FRAME_UNIT_ADDED")
|
||||
end
|
||||
if next(FrameToUnit.updated) then
|
||||
StartProfiling("callback FRAME_UNIT_UPDATE")
|
||||
for frame, previousUnit in pairs(FrameToUnit.updated) do
|
||||
callbacks:Fire("FRAME_UNIT_UPDATE", frame, FrameToUnit.data[frame], previousUnit)
|
||||
end
|
||||
StopProfiling("callback FRAME_UNIT_UPDATE")
|
||||
end
|
||||
if next(FrameToUnit.removed) then
|
||||
StartProfiling("callback FRAME_UNIT_REMOVED")
|
||||
for frame, unit in pairs(FrameToUnit.removed) do
|
||||
callbacks:Fire("FRAME_UNIT_REMOVED", frame, unit)
|
||||
end
|
||||
StopProfiling("callback FRAME_UNIT_REMOVED")
|
||||
end
|
||||
coroutineFrame:Hide()
|
||||
FrameToFrameName:Reset()
|
||||
FrameToUnit:Reset()
|
||||
StopProfiling("callbacks")
|
||||
if status == "scan_queued" then
|
||||
doScanForUnitFrames("queued")
|
||||
else
|
||||
status = "ready"
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local function ScanForUnitFrames(noDelay)
|
||||
if status == "ready" then
|
||||
if noDelay then
|
||||
doScanForUnitFrames()
|
||||
else
|
||||
status = "scan_delay"
|
||||
lib.timer:ScheduleTimer(function()
|
||||
doScanForUnitFrames()
|
||||
elseif not wait then
|
||||
wait = true
|
||||
waitFrame.delay = 1
|
||||
waitFrame:SetScript("OnUpdate", waitFrame_OnUpdate)
|
||||
end, 1)
|
||||
end
|
||||
elseif status == "scanning" then
|
||||
status = "scan_queued"
|
||||
end
|
||||
end
|
||||
|
||||
function lib.ScanForUnitFrames()
|
||||
ScanForUnitFrames(true)
|
||||
end
|
||||
|
||||
local function isFrameFiltered(name, ignoredFrames)
|
||||
for _, filter in pairs(ignoredFrames) do
|
||||
if name:find(filter) then
|
||||
return true
|
||||
end
|
||||
for _, filter in pairs(ignoredFrames) do
|
||||
if name:find(filter) then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function GetUnitFrames(target, ignoredFrames)
|
||||
if not UnitExists(target) then
|
||||
if type(target) ~= "string" then
|
||||
return
|
||||
end
|
||||
local B = tonumber(target:sub(5, 5), 16)
|
||||
if B and B % 8 == 0 then
|
||||
target = select(6, GetPlayerInfoByGUID(target))
|
||||
else
|
||||
target = target:gsub(" .*", "")
|
||||
end
|
||||
if not UnitExists(target) then
|
||||
if type(target) ~= "string" then return end
|
||||
local B = tonumber(target:sub(5, 5), 16)
|
||||
if B and B % 8 == 0 then
|
||||
target = select(6, GetPlayerInfoByGUID(target))
|
||||
else
|
||||
target = target:gsub(" .*", "")
|
||||
end
|
||||
if not UnitExists(target) then
|
||||
return
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local frames
|
||||
for frame, frameName in pairs(GetFramesCache) do
|
||||
local unit = SecureButton_GetUnit(frame)
|
||||
if unit and UnitIsUnit(unit, target)
|
||||
and not isFrameFiltered(frameName, ignoredFrames)
|
||||
then
|
||||
frames = frames or {}
|
||||
frames[frame] = frameName
|
||||
end
|
||||
local frames
|
||||
for frame, frameName in pairs(FrameToFrameName.data) do
|
||||
local unit = SecureButton_GetUnit(frame)
|
||||
if unit and UnitIsUnit(unit, target) and not isFrameFiltered(frameName, ignoredFrames) then
|
||||
frames = frames or {}
|
||||
frames[frame] = frameName
|
||||
end
|
||||
return frames
|
||||
end
|
||||
return frames
|
||||
end
|
||||
|
||||
local function ElvuiWorkaround(frame)
|
||||
if IsAddOnLoaded("ElvUI") and frame and frame:GetName():find("^ElvUF_") and frame.Health then
|
||||
return frame.Health
|
||||
else
|
||||
return frame
|
||||
end
|
||||
if IsAddOnLoaded("ElvUI") and frame and frame:GetName() and frame:GetName():find("^ElvUF_") and frame.Health then
|
||||
return frame.Health
|
||||
else
|
||||
return frame
|
||||
end
|
||||
end
|
||||
|
||||
local function CellGetUnitFrames(target, frames, framePriorities)
|
||||
if not IsAddOnLoaded("Cell") or not Cell.GetUnitFramesForLGF then
|
||||
return frames
|
||||
end
|
||||
return Cell.GetUnitFramesForLGF(target, frames, framePriorities)
|
||||
end
|
||||
|
||||
local defaultOptions = {
|
||||
framePriorities = defaultFramePriorities,
|
||||
ignorePlayerFrame = true,
|
||||
ignoreTargetFrame = true,
|
||||
ignoreTargettargetFrame = true,
|
||||
ignorePartyFrame = false,
|
||||
ignorePartyTargetFrame = true,
|
||||
ignoreRaidFrame = false,
|
||||
playerFrames = defaultPlayerFrames,
|
||||
targetFrames = defaultTargetFrames,
|
||||
targettargetFrames = defaultTargettargetFrames,
|
||||
partyFrames = defaultPartyFrames,
|
||||
partyTargetFrames = defaultPartyTargetFrames,
|
||||
raidFrames = defaultRaidFrames,
|
||||
ignoreFrames = {
|
||||
"PitBull4_Frames_Target's target's target",
|
||||
"ElvUF_PartyGroup%dUnitButton%dTarget",
|
||||
"ElvUF_FocusTarget",
|
||||
"RavenButton"
|
||||
},
|
||||
returnAll = false,
|
||||
framePriorities = defaultFramePriorities,
|
||||
ignorePlayerFrame = true,
|
||||
ignoreTargetFrame = true,
|
||||
ignoreTargettargetFrame = true,
|
||||
ignorePartyFrame = false,
|
||||
ignorePartyTargetFrame = true,
|
||||
ignoreFocusFrame = true,
|
||||
ignoreRaidFrame = false,
|
||||
playerFrames = defaultPlayerFrames,
|
||||
targetFrames = defaultTargetFrames,
|
||||
targettargetFrames = defaultTargettargetFrames,
|
||||
partyFrames = defaultPartyFrames,
|
||||
partyTargetFrames = defaultPartyTargetFrames,
|
||||
focusFrames = defaultFocusFrames,
|
||||
raidFrames = defaultRaidFrames,
|
||||
ignoreFrames = {
|
||||
"PitBull4_Frames_Target's target's target",
|
||||
"ElvUF_PartyGroup%dUnitButton%dTarget",
|
||||
"RavenButton",
|
||||
"RavenOverlay",
|
||||
"AshToAshUnit%d+ShadowGroupHeaderUnitButton%d+",
|
||||
"InvenUnitFrames_TargetTargetTarget",
|
||||
"CellQuickCastButton",
|
||||
},
|
||||
skipCellOverrides = false,
|
||||
returnAll = false,
|
||||
}
|
||||
local getDefaultOptions = function()
|
||||
return CopyTable(defaultOptions)
|
||||
end
|
||||
lib.getDefaultOptions = getDefaultOptions
|
||||
|
||||
local IterateGroupMembers = function(reversed, forceParty)
|
||||
local unit = (not forceParty and GetNumRaidMembers() > 0) and 'raid' or 'party'
|
||||
local numGroupMembers = unit == 'party' and GetNumPartyMembers() or GetNumRaidMembers()
|
||||
local i = reversed and numGroupMembers or (unit == 'party' and 0 or 1)
|
||||
return function()
|
||||
local ret
|
||||
if i == 0 and unit == 'party' then
|
||||
ret = 'player'
|
||||
elseif i <= numGroupMembers and i > 0 then
|
||||
ret = unit .. i
|
||||
end
|
||||
i = i + (reversed and -1 or 1)
|
||||
return ret
|
||||
end
|
||||
end
|
||||
|
||||
local unitPetState = {} -- track if unit's pet exists
|
||||
|
||||
local saveGetUnitFrame
|
||||
local function fixGetUnitFrameIntegrity()
|
||||
lib.GetUnitFrame = saveGetUnitFrame
|
||||
lib.GetFrame = saveGetUnitFrame
|
||||
if WeakAuras and WeakAuras.GetUnitFrame then
|
||||
WeakAuras.GetUnitFrame = saveGetUnitFrame
|
||||
end
|
||||
end
|
||||
|
||||
local GetFramesCacheListener
|
||||
local function Init(noDelay)
|
||||
GetFramesCacheListener = CreateFrame("Frame")
|
||||
GetFramesCacheListener:RegisterEvent("PLAYER_REGEN_DISABLED")
|
||||
GetFramesCacheListener:RegisterEvent("PLAYER_REGEN_ENABLED")
|
||||
GetFramesCacheListener:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
GetFramesCacheListener:RegisterEvent("PARTY_MEMBERS_CHANGED")
|
||||
GetFramesCacheListener:RegisterEvent("RAID_ROSTER_UPDATE")
|
||||
GetFramesCacheListener:SetScript("OnEvent", function() ScanForUnitFrames(false) end)
|
||||
ScanForUnitFrames(noDelay)
|
||||
GetFramesCacheListener = CreateFrame("Frame")
|
||||
GetFramesCacheListener:RegisterEvent("PLAYER_REGEN_DISABLED")
|
||||
GetFramesCacheListener:RegisterEvent("PLAYER_REGEN_ENABLED")
|
||||
GetFramesCacheListener:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
GetFramesCacheListener:RegisterEvent("RAID_ROSTER_UPDATE")
|
||||
GetFramesCacheListener:RegisterEvent("PARTY_MEMBERS_CHANGED")
|
||||
GetFramesCacheListener:RegisterEvent("UNIT_PET")
|
||||
GetFramesCacheListener:RegisterEvent("INSTANCE_ENCOUNTER_ENGAGE_UNIT")
|
||||
GetFramesCacheListener:SetScript("OnEvent", function(self, event, unit, ...)
|
||||
fixGetUnitFrameIntegrity()
|
||||
if event == "RAID_ROSTER_UPDATE" or "PARTY_MEMBERS_CHANGED" then
|
||||
wipe(unitPetState)
|
||||
for member in IterateGroupMembers() do
|
||||
unitPetState[member] = UnitExists(member .. "pet") and true or nil
|
||||
end
|
||||
end
|
||||
if event == "UNIT_PET" then
|
||||
if not (UnitIsUnit("player", unit) or UnitInParty(unit) or UnitInRaid(unit)) then
|
||||
return
|
||||
end
|
||||
-- skip if unit's pet existance has not changed
|
||||
local exists = UnitExists(unit .. "pet") and true or nil
|
||||
if unitPetState[unit] == exists then
|
||||
return
|
||||
else
|
||||
unitPetState[unit] = exists
|
||||
end
|
||||
end
|
||||
ScanForUnitFrames(false)
|
||||
end)
|
||||
ScanForUnitFrames(noDelay)
|
||||
end
|
||||
|
||||
function lib.GetUnitFrame(target, opt)
|
||||
if type(GetFramesCacheListener) ~= "table" then Init(true) end
|
||||
opt = opt or {}
|
||||
setmetatable(opt, { __index = defaultOptions })
|
||||
if type(GetFramesCacheListener) ~= "table" then
|
||||
Init(true)
|
||||
end
|
||||
opt = opt or {}
|
||||
setmetatable(opt, { __index = defaultOptions })
|
||||
|
||||
if not target then return end
|
||||
if not target then
|
||||
return
|
||||
end
|
||||
|
||||
local ignoredFrames = CopyTable(opt.ignoreFrames)
|
||||
if opt.ignorePlayerFrame then
|
||||
for _,v in pairs(opt.playerFrames) do
|
||||
tinsert(ignoredFrames, v)
|
||||
end
|
||||
local ignoredFrames = CopyTable(opt.ignoreFrames)
|
||||
if opt.ignorePlayerFrame then
|
||||
for _, v in pairs(opt.playerFrames) do
|
||||
tinsert(ignoredFrames, v)
|
||||
end
|
||||
if opt.ignoreTargetFrame then
|
||||
for _,v in pairs(opt.targetFrames) do
|
||||
tinsert(ignoredFrames, v)
|
||||
end
|
||||
end
|
||||
if opt.ignoreTargetFrame then
|
||||
for _, v in pairs(opt.targetFrames) do
|
||||
tinsert(ignoredFrames, v)
|
||||
end
|
||||
if opt.ignoreTargettargetFrame then
|
||||
for _,v in pairs(opt.targettargetFrames) do
|
||||
tinsert(ignoredFrames, v)
|
||||
end
|
||||
end
|
||||
if opt.ignoreTargettargetFrame then
|
||||
for _, v in pairs(opt.targettargetFrames) do
|
||||
tinsert(ignoredFrames, v)
|
||||
end
|
||||
if opt.ignorePartyFrame then
|
||||
for _,v in pairs(opt.partyFrames) do
|
||||
tinsert(ignoredFrames, v)
|
||||
end
|
||||
end
|
||||
if opt.ignorePartyFrame then
|
||||
for _, v in pairs(opt.partyFrames) do
|
||||
tinsert(ignoredFrames, v)
|
||||
end
|
||||
if opt.ignorePartyTargetFrame then
|
||||
for _,v in pairs(opt.partyTargetFrames) do
|
||||
tinsert(ignoredFrames, v)
|
||||
end
|
||||
end
|
||||
if opt.ignorePartyTargetFrame then
|
||||
for _, v in pairs(opt.partyTargetFrames) do
|
||||
tinsert(ignoredFrames, v)
|
||||
end
|
||||
if opt.ignoreRaidFrame then
|
||||
for _,v in pairs(opt.raidFrames) do
|
||||
tinsert(ignoredFrames, v)
|
||||
end
|
||||
end
|
||||
if opt.ignoreFocusFrame then
|
||||
for _, v in pairs(opt.focusFrames) do
|
||||
tinsert(ignoredFrames, v)
|
||||
end
|
||||
end
|
||||
if opt.ignoreRaidFrame then
|
||||
for _, v in pairs(opt.raidFrames) do
|
||||
tinsert(ignoredFrames, v)
|
||||
end
|
||||
end
|
||||
|
||||
local frames = GetUnitFrames(target, ignoredFrames)
|
||||
if not frames then return end
|
||||
local frames = GetUnitFrames(target, ignoredFrames)
|
||||
|
||||
if not opt.returnAll then
|
||||
for i = 1, #opt.framePriorities do
|
||||
for frame, frameName in pairs(frames) do
|
||||
if frameName:find(opt.framePriorities[i]) then
|
||||
return ElvuiWorkaround(frame)
|
||||
end
|
||||
end
|
||||
if not (opt.ignoreRaidFrame or opt.skipCellOverrides) then
|
||||
frames = CellGetUnitFrames(target, frames, opt.framePriorities)
|
||||
end
|
||||
|
||||
if not frames then
|
||||
return
|
||||
end
|
||||
|
||||
if not opt.returnAll then
|
||||
for i = 1, #opt.framePriorities do
|
||||
for frame, frameName in pairs(frames) do
|
||||
if frameName:find(opt.framePriorities[i]) then
|
||||
return ElvuiWorkaround(frame)
|
||||
end
|
||||
local next = next
|
||||
return ElvuiWorkaround(next(frames))
|
||||
else
|
||||
for frame in pairs(frames) do
|
||||
frames[frame] = ElvuiWorkaround(frame)
|
||||
end
|
||||
return frames
|
||||
end
|
||||
end
|
||||
local next = next
|
||||
return ElvuiWorkaround(next(frames))
|
||||
else
|
||||
for frame in pairs(frames) do
|
||||
frames[frame] = ElvuiWorkaround(frame)
|
||||
end
|
||||
return frames
|
||||
end
|
||||
end
|
||||
saveGetUnitFrame = lib.GetUnitFrame
|
||||
lib.GetFrame = lib.GetUnitFrame -- compatibility
|
||||
|
||||
-- nameplates
|
||||
function lib.GetUnitNameplate(unit)
|
||||
if not unit then
|
||||
return
|
||||
end
|
||||
local nameplate = C_NamePlate.GetNamePlateForUnit(unit)
|
||||
if nameplate then
|
||||
-- credit to Exality for https://wago.io/explosiveorbs
|
||||
if nameplate.UnitFrame and nameplate.UnitFrame.Health then
|
||||
-- elvui
|
||||
return nameplate.UnitFrame.Health
|
||||
elseif nameplate.unitFramePlater and nameplate.unitFramePlater.healthBar then
|
||||
-- plater
|
||||
return nameplate.unitFramePlater.healthBar
|
||||
elseif nameplate.kui and nameplate.kui.HealthBar then
|
||||
-- kui
|
||||
return nameplate.kui.HealthBar
|
||||
elseif nameplate.extended and nameplate.extended.visual and nameplate.extended.visual.healthbar then
|
||||
-- tidyplates
|
||||
return nameplate.extended.visual.healthbar
|
||||
elseif nameplate.TPFrame and nameplate.TPFrame.visual and nameplate.TPFrame.visual.healthbar then
|
||||
-- tidyplates: threat plates
|
||||
return nameplate.TPFrame.visual.healthbar
|
||||
elseif nameplate.unitFrame and nameplate.unitFrame.Health then
|
||||
-- bdui nameplates
|
||||
return nameplate.unitFrame.Health
|
||||
elseif nameplate.ouf and nameplate.ouf.Health then
|
||||
-- bdNameplates
|
||||
return nameplate.ouf.Health
|
||||
elseif nameplate.slab and nameplate.slab.components and nameplate.slab.components.healthBar and nameplate.slab.components.healthBar.frame then
|
||||
-- Slab
|
||||
return nameplate.slab.components.healthBar.frame
|
||||
elseif nameplate.UnitFrame and nameplate.UnitFrame.healthBar then
|
||||
-- default
|
||||
return nameplate.UnitFrame.healthBar
|
||||
else
|
||||
return nameplate
|
||||
end
|
||||
if not unit then
|
||||
return
|
||||
end
|
||||
local nameplate = C_NamePlate.GetNamePlateForUnit(unit)
|
||||
if nameplate then
|
||||
-- credit to Exality for https://wago.io/explosiveorbs
|
||||
if nameplate.UnitFrame and nameplate.UnitFrame.Health then
|
||||
-- elvui bunny
|
||||
return nameplate.UnitFrame.Health
|
||||
elseif nameplate.unitFrame and nameplate.unitFrame.Health then
|
||||
-- elvui someday
|
||||
return nameplate.unitFrame.Health
|
||||
elseif nameplate.unitFramePlater and nameplate.unitFramePlater.healthBar then
|
||||
-- plater
|
||||
-- fallback to default nameplate in case plater is not on screen and uses blizzard default (module disabled, force-blizzard functionality)
|
||||
return nameplate.unitFramePlater.PlaterOnScreen and nameplate.unitFramePlater.healthBar or (nameplate.UnitFrame and nameplate.UnitFrame.healthBar) or nameplate
|
||||
elseif nameplate.kui and nameplate.kui.HealthBar then
|
||||
-- kui
|
||||
return nameplate.kui.HealthBar
|
||||
elseif nameplate.extended and nameplate.extended.visual and nameplate.extended.visual.healthbar then
|
||||
-- tidyplates
|
||||
return nameplate.extended.visual.healthbar
|
||||
elseif nameplate.TPFrame and nameplate.TPFrame.visual and nameplate.TPFrame.visual.healthbar then
|
||||
-- tidyplates: threat plates
|
||||
return nameplate.TPFrame.visual.healthbar
|
||||
elseif nameplate.unitFrame and nameplate.unitFrame.Health then
|
||||
-- bdui nameplates
|
||||
return nameplate.unitFrame.Health
|
||||
elseif nameplate.ouf and nameplate.ouf.Health then
|
||||
-- bdNameplates
|
||||
return nameplate.ouf.Health
|
||||
elseif nameplate.slab and nameplate.slab.components and nameplate.slab.components.healthBar and nameplate.slab.components.healthBar.frame then
|
||||
-- Slab
|
||||
return nameplate.slab.components.healthBar.frame
|
||||
elseif nameplate.UnitFrame and nameplate.UnitFrame.healthBar then
|
||||
-- default
|
||||
return nameplate.UnitFrame.healthBar
|
||||
else
|
||||
return nameplate
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
## Interface: 80200
|
||||
## Interface: 33000
|
||||
## Title: Lib: GetFrame
|
||||
## Notes: Get unit frame for a unit
|
||||
## Author: Buds
|
||||
## X-Category: Library
|
||||
## X-License: BSD
|
||||
## Version: 03cd52d
|
||||
## Version: 1.6.3
|
||||
## DefaultState: Enabled
|
||||
## LoadOnDemand: 0
|
||||
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
..\FrameXML\UI.xsd">
|
||||
<Script file = "LibStub\LibStub.lua"/>
|
||||
<Include file ="CallbackHandler-1.0\CallbackHandler-1.0.xml"/>
|
||||
<Script file = "AceTimer-3.0\AceTimer-3.0.xml"/>
|
||||
<Script file = "LibGetFrame-1.0.lua"/>
|
||||
</Ui>
|
||||
|
||||
@@ -7,92 +7,91 @@ Return unit frame for a given unit
|
||||
```Lua
|
||||
local LGF = LibStub("LibGetFrame-1.0")
|
||||
local frame = LGF.GetUnitFrame(unit , options)
|
||||
|
||||
local callback = function(event, frame, unit)
|
||||
if event == "GETFRAME_REFRESH" then
|
||||
-- cache was refreshed
|
||||
end
|
||||
if event == "FRAME_UNIT_UPDATE" then
|
||||
-- 'frame' was updated and is now a match for 'unit'
|
||||
end
|
||||
if event == "FRAME_UNIT_REMOVED" then
|
||||
-- 'frame' was updated and is no longer a match for 'unit'
|
||||
end
|
||||
end
|
||||
|
||||
LGF.RegisterCallback("MyAddonName", "GETFRAME_REFRESH", callback)
|
||||
LGF.RegisterCallback("MyAddonName", "FRAME_UNIT_UPDATE", callback)
|
||||
LGF.RegisterCallback("MyAddonName", "FRAME_UNIT_REMOVED", callback)
|
||||
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
- framePriorities : array, default :
|
||||
## Public functions
|
||||
|
||||
```Lua
|
||||
{
|
||||
-- raid frames
|
||||
[1] = "^Vd1", -- vuhdo
|
||||
[2] = "^Vd2", -- vuhdo
|
||||
[3] = "^Vd3", -- vuhdo
|
||||
[4] = "^Vd4", -- vuhdo
|
||||
[5] = "^Vd5", -- vuhdo
|
||||
[6] = "^Vd", -- vuhdo
|
||||
[7] = "^HealBot", -- healbot
|
||||
[8] = "^GridLayout", -- grid
|
||||
[9] = "^Grid2Layout", -- grid2
|
||||
[10] = "^ElvUF_RaidGroup", -- elv
|
||||
[11] = "^oUF_bdGrid", -- bdgrid
|
||||
[12] = "^oUF.*raid", -- generic oUF
|
||||
[13] = "^LimeGroup", -- lime
|
||||
[14] = "^SUFHeaderraid", -- suf
|
||||
[15] = "^CompactRaid", -- blizz
|
||||
-- party frames
|
||||
[16] = "^SUFHeaderparty", --suf
|
||||
[17] = "^ElvUF_PartyGroup", -- elv
|
||||
[18] = "^oUF.*party", -- generic oUF
|
||||
[19] = "^PitBull4_Groups_Party", -- pitbull4
|
||||
[20] = "^CompactParty", -- blizz
|
||||
-- player frame
|
||||
[21] = "^SUFUnitplayer",
|
||||
[22] = "^PitBull4_Frames_Player",
|
||||
[23] = "^ElvUF_Player",
|
||||
[24] = "^oUF.*player",
|
||||
[25] = "^PlayerFrame",
|
||||
}
|
||||
LGF:GetUnitFrame(unit, options)
|
||||
```
|
||||
|
||||
Options:
|
||||
|
||||
- framePriorities : array
|
||||
|
||||
- ignorePlayerFrame : boolean (default true)
|
||||
- ignoreTargetFrame : boolean (default true)
|
||||
- ignoreTargettargetFrame : boolean (default true)
|
||||
- playerFrames : array, default :
|
||||
|
||||
```Lua
|
||||
{
|
||||
"SUFUnitplayer",
|
||||
"PitBull4_Frames_Player",
|
||||
"ElvUF_Player",
|
||||
"oUF_TukuiPlayer",
|
||||
"PlayerFrame",
|
||||
}
|
||||
```
|
||||
|
||||
- targetFrames : array, default :
|
||||
|
||||
```Lua
|
||||
{
|
||||
"SUFUnittarget",
|
||||
"PitBull4_Frames_Target",
|
||||
"ElvUF_Target",
|
||||
"TargetFrame",
|
||||
"oUF_TukuiTarget",
|
||||
}
|
||||
```
|
||||
|
||||
- targettargetFrames : array, default :
|
||||
|
||||
```Lua
|
||||
{
|
||||
"SUFUnittargetarget",
|
||||
"PitBull4_Frames_TargetTarget",
|
||||
"ElvUF_TargetTarget",
|
||||
"TargetTargetFrame",
|
||||
"oUF_TukuiTargetTarget",
|
||||
}
|
||||
```
|
||||
|
||||
- ignoreFrames : array, default :
|
||||
|
||||
```Lua
|
||||
{ }
|
||||
```
|
||||
- ignorePartyFrame : boolean (default false)
|
||||
- ignorePartyTargetFrame : boolean (default true)
|
||||
- ignoreRaidFrame : boolean (default false)
|
||||
|
||||
- playerFrames : array
|
||||
- targetFrames : array
|
||||
- targettargetFrames : array
|
||||
- partyFrames : array
|
||||
- partyTargetFrames : array
|
||||
- raidFrames : array
|
||||
- ignoreFrames : array
|
||||
- returnAll : boolean (default false)
|
||||
|
||||
If returnAll is false, GetUnitFrame will return a single best match
|
||||
|
||||
For arrays check LibGetFrame-1.0.lua code for defaults
|
||||
|
||||
```Lua
|
||||
LGF:ScanForUnitFrames()
|
||||
```
|
||||
|
||||
Ask lib to do a new scan of frames.
|
||||
|
||||
This scan can take a few frames to be completed.
|
||||
|
||||
You should not expect the cache use by LGF:GetUnitFrame to be updated in the same frame as this ScanForUnitFrames call.
|
||||
|
||||
Use lib's callbacks to know when the cache is refresh.
|
||||
|
||||
```Lua
|
||||
LGF:GetUnitNameplate(unit)
|
||||
```
|
||||
|
||||
Return health bar for a nameplate unit, works with a variety of addons
|
||||
|
||||
|
||||
## Callbacks
|
||||
|
||||
```Lua
|
||||
-- Fired after a scan complete and cache refreshed
|
||||
LGF.RegisterCallback("MyAddonName", "GETFRAME_REFRESH", function(event) end)
|
||||
```
|
||||
|
||||
```Lua
|
||||
-- Fired when a frame is a new match for a unit (it does not test if it is the BEST match!)
|
||||
LGF.RegisterCallback("MyAddonName", "FRAME_UNIT_UPDATE", function(event, frame, unit) end)
|
||||
```
|
||||
|
||||
```Lua
|
||||
-- Fired when a frame is not a new match for a unit anymore
|
||||
LGF.RegisterCallback("MyAddonName", "FRAME_UNIT_REMOVED", function(event, frame, unit) end)
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Glow player frame
|
||||
@@ -115,15 +114,15 @@ local LGF = LibStub("LibGetFrame-1.0")
|
||||
local LCG = LibStub("LibCustomGlow-1.0")
|
||||
|
||||
local frames = LGF.GetUnitFrame("target", {
|
||||
ignorePlayerFrame = false,
|
||||
ignoreTargetFrame = false,
|
||||
ignoreTargettargetFrame = false,
|
||||
returnAll = true,
|
||||
ignorePlayerFrame = false,
|
||||
ignoreTargetFrame = false,
|
||||
ignoreTargettargetFrame = false,
|
||||
returnAll = true,
|
||||
})
|
||||
|
||||
for _, frame in pairs(frames) do
|
||||
LCG.ButtonGlow_Start(frame)
|
||||
--LCG.ButtonGlow_Stop(frame)
|
||||
LCG.ButtonGlow_Start(frame)
|
||||
--LCG.ButtonGlow_Stop(frame)
|
||||
end
|
||||
```
|
||||
|
||||
@@ -131,8 +130,58 @@ end
|
||||
|
||||
```Lua
|
||||
local frame = LGF.GetUnitFrame("player", {
|
||||
ignoreFrames = { "Vd2.*", "Vd3.*" }
|
||||
ignoreFrames = { "Vd2.*", "Vd3.*" }
|
||||
})
|
||||
```
|
||||
|
||||
### Glow specific units and update glow when frames changes
|
||||
|
||||
```Lua
|
||||
local LGF = LibStub("LibGetFrame-1.0")
|
||||
local LCG = LibStub("LibCustomGlow-1.0")
|
||||
|
||||
-- list of units i want glowing
|
||||
local glow_units = {
|
||||
player = true
|
||||
}
|
||||
-- track which frame is glowing per unit
|
||||
local glow_unit_frames = {}
|
||||
|
||||
-- glow them using current cache
|
||||
for unit in pairs(glow_units) do
|
||||
local frame = LGF.GetUnitFrame("player")
|
||||
if frame then
|
||||
LCG.ButtonGlow_Start(frame)
|
||||
glow_unit_frames[unit] = frame
|
||||
end
|
||||
end
|
||||
|
||||
local callback = function(event, frame, unit)
|
||||
if not glow_units[unit] then
|
||||
return
|
||||
end
|
||||
-- new match for GetUnitFrame(unit), check if it's different from previous "best match" returned
|
||||
local new_best_match = LGF.GetUnitFrame(unit)
|
||||
if new_best_match == nil then
|
||||
-- didn't found a best match for this unit
|
||||
if glow_unit_frames[unit] then
|
||||
-- stop previous glow
|
||||
LCG.ButtonGlow_Stop(glow_unit_frames[unit])
|
||||
glow_unit_frames[unit] = nil
|
||||
end
|
||||
elseif new_best_match ~= glow_unit_frames[unit] then
|
||||
-- best match found, but different from previous one
|
||||
if glow_unit_frames[unit] then
|
||||
-- stop previous glow
|
||||
LCG.ButtonGlow_Stop(glow_unit_frames[unit])
|
||||
end
|
||||
LCG.ButtonGlow_Start(new_best_match)
|
||||
glow_unit_frames[unit] = new_best_match
|
||||
end
|
||||
end
|
||||
|
||||
LGF.RegisterCallback("MyAddonName", "FRAME_UNIT_UPDATE", callback)
|
||||
LGF.RegisterCallback("MyAddonName", "FRAME_UNIT_REMOVED", callback)
|
||||
```
|
||||
|
||||
[GitHub Project](https://github.com/mrbuds/LibGetFrame)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
indent_type = "Spaces"
|
||||
indent_width = 2
|
||||
column_width = 180
|
||||
line_endings = "Unix"
|
||||
quote_style = "ForceDouble"
|
||||
@@ -4,7 +4,7 @@ local AddonName, Private = ...
|
||||
local WeakAuras = WeakAuras
|
||||
local L = WeakAuras.L
|
||||
local prettyPrint = WeakAuras.prettyPrint
|
||||
--local LGF = LibStub("LibGetFrame-1.0")
|
||||
local LGF = LibStub("LibGetFrame-1.0")
|
||||
|
||||
local profileData = {}
|
||||
profileData.systems = {}
|
||||
@@ -245,7 +245,7 @@ function WeakAuras.StartProfile(startType)
|
||||
Private.StopProfileSystem = StopProfileSystem
|
||||
Private.StopProfileAura = StopProfileAura
|
||||
Private.StopProfileUID = StopProfileUID
|
||||
--LGF.StartProfile()
|
||||
LGF.StartProfile()
|
||||
end
|
||||
|
||||
local function doNothing()
|
||||
@@ -268,7 +268,7 @@ function WeakAuras.StopProfile()
|
||||
Private.StopProfileSystem = doNothing
|
||||
Private.StopProfileAura = doNothing
|
||||
Private.StopProfileUID = doNothing
|
||||
--LGF.StopProfile()
|
||||
LGF.StopProfile()
|
||||
|
||||
currentProfileState = nil
|
||||
RealTimeProfilingWindow:UnregisterAllEvents()
|
||||
@@ -442,11 +442,11 @@ function WeakAuras.PrintProfile()
|
||||
end
|
||||
end
|
||||
|
||||
--popup:AddText("")
|
||||
--popup:AddText("|cff9900ffLibGetFrame:|r")
|
||||
--for id, map in pairs(LGF.GetProfileData()) do
|
||||
-- PrintOneProfile(popup, id, map)
|
||||
--end
|
||||
popup:AddText("")
|
||||
popup:AddText("|cff9900ffLibGetFrame:|r")
|
||||
for id, map in pairs(LGF.GetProfileData()) do
|
||||
PrintOneProfile(popup, id, map)
|
||||
end
|
||||
|
||||
popup:Show()
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user