Mercurial > wow > hotcorners
comparison Libs/AceTimer-3.0/AceTimer-3.0.lua @ 5:c31ee4251181
Libs Update
| author | tercio |
|---|---|
| date | Tue, 25 Nov 2014 21:15:10 -0200 |
| parents | fc346da3afd9 |
| children | 9ad7f3c634f1 |
comparison
equal
deleted
inserted
replaced
| 4:453c68ff5d72 | 5:c31ee4251181 |
|---|---|
| 1 --- **AceTimer-3.0** provides a central facility for registering timers. | 1 --- **AceTimer-3.0** provides a central facility for registering timers. |
| 2 -- AceTimer supports one-shot timers and repeating timers. All timers are stored in an efficient | 2 -- AceTimer supports one-shot timers and repeating timers. All timers are stored in an efficient |
| 3 -- data structure that allows easy dispatching and fast rescheduling. Timers can be registered | 3 -- data structure that allows easy dispatching and fast rescheduling. Timers can be registered |
| 4 -- or canceled at any time, even from within a running timer, without conflict or large overhead.\\ | 4 -- or canceled at any time, even from within a running timer, without conflict or large overhead.\\ |
| 5 -- AceTimer is currently limited to firing timers at a frequency of 0.01s. This constant may change | 5 -- AceTimer is currently limited to firing timers at a frequency of 0.01s as this is what the WoW timer API |
| 6 -- in the future, but for now it's required as animations with lower frequencies are buggy. | 6 -- restricts us to. |
| 7 -- | 7 -- |
| 8 -- All `:Schedule` functions will return a handle to the current timer, which you will need to store if you | 8 -- All `:Schedule` functions will return a handle to the current timer, which you will need to store if you |
| 9 -- need to cancel the timer you just registered. | 9 -- need to cancel the timer you just registered. |
| 10 -- | 10 -- |
| 11 -- **AceTimer-3.0** can be embeded into your addon, either explicitly by calling AceTimer:Embed(MyAddon) or by | 11 -- **AceTimer-3.0** can be embeded into your addon, either explicitly by calling AceTimer:Embed(MyAddon) or by |
| 13 -- and can be accessed directly, without having to explicitly call AceTimer itself.\\ | 13 -- and can be accessed directly, without having to explicitly call AceTimer itself.\\ |
| 14 -- It is recommended to embed AceTimer, otherwise you'll have to specify a custom `self` on all calls you | 14 -- It is recommended to embed AceTimer, otherwise you'll have to specify a custom `self` on all calls you |
| 15 -- make into AceTimer. | 15 -- make into AceTimer. |
| 16 -- @class file | 16 -- @class file |
| 17 -- @name AceTimer-3.0 | 17 -- @name AceTimer-3.0 |
| 18 -- @release $Id: AceTimer-3.0.lua 1079 2013-02-17 19:56:06Z funkydude $ | 18 -- @release $Id: AceTimer-3.0.lua 1119 2014-10-14 17:23:29Z nevcairiel $ |
| 19 | 19 |
| 20 local MAJOR, MINOR = "AceTimer-3.0", 16 -- Bump minor on changes | 20 local MAJOR, MINOR = "AceTimer-3.0", 17 -- Bump minor on changes |
| 21 local AceTimer, oldminor = LibStub:NewLibrary(MAJOR, MINOR) | 21 local AceTimer, oldminor = LibStub:NewLibrary(MAJOR, MINOR) |
| 22 | 22 |
| 23 if not AceTimer then return end -- No upgrade needed | 23 if not AceTimer then return end -- No upgrade needed |
| 24 | 24 AceTimer.activeTimers = AceTimer.activeTimers or {} -- Active timer list |
| 25 AceTimer.frame = AceTimer.frame or CreateFrame("Frame", "AceTimer30Frame") -- Animation parent | 25 local activeTimers = AceTimer.activeTimers -- Upvalue our private data |
| 26 AceTimer.inactiveTimers = AceTimer.inactiveTimers or {} -- Timer recycling storage | |
| 27 AceTimer.activeTimers = AceTimer.activeTimers or {} -- Active timer list | |
| 28 | 26 |
| 29 -- Lua APIs | 27 -- Lua APIs |
| 30 local type, unpack, next, error, pairs, tostring, select = type, unpack, next, error, pairs, tostring, select | 28 local type, unpack, next, error, select = type, unpack, next, error, select |
| 31 | 29 -- WoW APIs |
| 32 -- Upvalue our private data | 30 local GetTime, C_TimerAfter = GetTime, C_Timer.After |
| 33 local inactiveTimers = AceTimer.inactiveTimers | |
| 34 local activeTimers = AceTimer.activeTimers | |
| 35 | |
| 36 local function OnFinished(self) | |
| 37 local id = self.id | |
| 38 if type(self.func) == "string" then | |
| 39 -- We manually set the unpack count to prevent issues with an arg set that contains nil and ends with nil | |
| 40 -- e.g. local t = {1, 2, nil, 3, nil} print(#t) will result in 2, instead of 5. This fixes said issue. | |
| 41 self.object[self.func](self.object, unpack(self.args, 1, self.argsCount)) | |
| 42 else | |
| 43 self.func(unpack(self.args, 1, self.argsCount)) | |
| 44 end | |
| 45 | |
| 46 -- If the id is different it means that the timer was already cancelled | |
| 47 -- and has been used to create a new timer during the OnFinished callback. | |
| 48 if not self.looping and id == self.id then | |
| 49 activeTimers[self.id] = nil | |
| 50 self.args = nil | |
| 51 inactiveTimers[self] = true | |
| 52 end | |
| 53 end | |
| 54 | 31 |
| 55 local function new(self, loop, func, delay, ...) | 32 local function new(self, loop, func, delay, ...) |
| 56 local timer = next(inactiveTimers) | |
| 57 if timer then | |
| 58 inactiveTimers[timer] = nil | |
| 59 else | |
| 60 local anim = AceTimer.frame:CreateAnimationGroup() | |
| 61 timer = anim:CreateAnimation() | |
| 62 timer:SetScript("OnFinished", OnFinished) | |
| 63 end | |
| 64 | |
| 65 -- Very low delays cause the animations to fail randomly. | |
| 66 -- A limited resolution of 0.01 seems reasonable. | |
| 67 if delay < 0.01 then | 33 if delay < 0.01 then |
| 68 delay = 0.01 | 34 delay = 0.01 -- Restrict to the lowest time that the C_Timer API allows us |
| 69 end | 35 end |
| 70 | 36 |
| 37 local timer = {...} | |
| 71 timer.object = self | 38 timer.object = self |
| 72 timer.func = func | 39 timer.func = func |
| 73 timer.looping = loop | 40 timer.looping = loop |
| 74 timer.args = {...} | |
| 75 timer.argsCount = select("#", ...) | 41 timer.argsCount = select("#", ...) |
| 76 | 42 timer.delay = delay |
| 77 local anim = timer:GetParent() | 43 timer.ends = GetTime() + delay |
| 78 if loop then | 44 |
| 79 anim:SetLooping("REPEAT") | 45 activeTimers[timer] = timer |
| 80 else | 46 |
| 81 anim:SetLooping("NONE") | 47 -- Create new timer closure to wrap the "timer" object |
| 82 end | 48 timer.callback = function() |
| 83 timer:SetDuration(delay) | 49 if not timer.cancelled then |
| 84 | 50 if type(timer.func) == "string" then |
| 85 local id = tostring(timer.args) | 51 -- We manually set the unpack count to prevent issues with an arg set that contains nil and ends with nil |
| 86 timer.id = id | 52 -- e.g. local t = {1, 2, nil, 3, nil} print(#t) will result in 2, instead of 5. This fixes said issue. |
| 87 activeTimers[id] = timer | 53 timer.object[timer.func](timer.object, unpack(timer, 1, timer.argsCount)) |
| 88 | 54 else |
| 89 anim:Play() | 55 timer.func(unpack(timer, 1, timer.argsCount)) |
| 90 return id | 56 end |
| 57 | |
| 58 if timer.looping and not timer.cancelled then | |
| 59 -- Compensate delay to get a perfect average delay, even if individual times don't match up perfectly | |
| 60 -- due to fps differences | |
| 61 local time = GetTime() | |
| 62 local delay = timer.delay - (time - timer.ends) | |
| 63 -- Ensure the delay doesn't go below the threshold | |
| 64 if delay < 0.01 then delay = 0.01 end | |
| 65 C_TimerAfter(delay, timer.callback) | |
| 66 timer.ends = time + delay | |
| 67 else | |
| 68 activeTimers[timer.handle or timer] = nil | |
| 69 end | |
| 70 end | |
| 71 end | |
| 72 | |
| 73 C_TimerAfter(delay, timer.callback) | |
| 74 return timer | |
| 91 end | 75 end |
| 92 | 76 |
| 93 --- Schedule a new one-shot timer. | 77 --- Schedule a new one-shot timer. |
| 94 -- The timer will fire once in `delay` seconds, unless canceled before. | 78 -- The timer will fire once in `delay` seconds, unless canceled before. |
| 95 -- @param callback Callback function for the timer pulse (funcref or method name). | 79 -- @param callback Callback function for the timer pulse (funcref or method name). |
| 158 -- Both one-shot and repeating timers can be canceled with this function, as long as the `id` is valid | 142 -- Both one-shot and repeating timers can be canceled with this function, as long as the `id` is valid |
| 159 -- and the timer has not fired yet or was canceled before. | 143 -- and the timer has not fired yet or was canceled before. |
| 160 -- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer` | 144 -- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer` |
| 161 function AceTimer:CancelTimer(id) | 145 function AceTimer:CancelTimer(id) |
| 162 local timer = activeTimers[id] | 146 local timer = activeTimers[id] |
| 163 if not timer then return false end | 147 |
| 164 | 148 if not timer then |
| 165 local anim = timer:GetParent() | 149 return false |
| 166 anim:Stop() | 150 else |
| 167 | 151 timer.cancelled = true |
| 168 activeTimers[id] = nil | 152 activeTimers[id] = nil |
| 169 timer.args = nil | 153 return true |
| 170 inactiveTimers[timer] = true | 154 end |
| 171 return true | |
| 172 end | 155 end |
| 173 | 156 |
| 174 --- Cancels all timers registered to the current addon object ('self') | 157 --- Cancels all timers registered to the current addon object ('self') |
| 175 function AceTimer:CancelAllTimers() | 158 function AceTimer:CancelAllTimers() |
| 176 for k,v in pairs(activeTimers) do | 159 for k,v in pairs(activeTimers) do |
| 184 -- This function will return 0 when the id is invalid. | 167 -- This function will return 0 when the id is invalid. |
| 185 -- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer` | 168 -- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer` |
| 186 -- @return The time left on the timer. | 169 -- @return The time left on the timer. |
| 187 function AceTimer:TimeLeft(id) | 170 function AceTimer:TimeLeft(id) |
| 188 local timer = activeTimers[id] | 171 local timer = activeTimers[id] |
| 189 if not timer then return 0 end | 172 if not timer then |
| 190 return timer:GetDuration() - timer:GetElapsed() | 173 return 0 |
| 174 else | |
| 175 return timer.ends - GetTime() | |
| 176 end | |
| 191 end | 177 end |
| 192 | 178 |
| 193 | 179 |
| 194 -- --------------------------------------------------------------------- | 180 -- --------------------------------------------------------------------- |
| 195 -- Upgrading | 181 -- Upgrading |
| 196 | 182 |
| 197 -- Upgrade from old hash-bucket based timers to animation timers | 183 -- Upgrade from old hash-bucket based timers to C_Timer.After timers. |
| 198 if oldminor and oldminor < 10 then | 184 if oldminor and oldminor < 10 then |
| 199 -- disable old timer logic | 185 -- disable old timer logic |
| 200 AceTimer.frame:SetScript("OnUpdate", nil) | 186 AceTimer.frame:SetScript("OnUpdate", nil) |
| 201 AceTimer.frame:SetScript("OnEvent", nil) | 187 AceTimer.frame:SetScript("OnEvent", nil) |
| 202 AceTimer.frame:UnregisterAllEvents() | 188 AceTimer.frame:UnregisterAllEvents() |
| 203 -- convert timers | 189 -- convert timers |
| 204 for object,timers in pairs(AceTimer.selfs) do | 190 for object,timers in pairs(AceTimer.selfs) do |
| 205 for handle,timer in pairs(timers) do | 191 for handle,timer in pairs(timers) do |
| 206 if type(timer) == "table" and timer.callback then | 192 if type(timer) == "table" and timer.callback then |
| 207 local id | 193 local newTimer |
| 208 if timer.delay then | 194 if timer.delay then |
| 209 id = AceTimer.ScheduleRepeatingTimer(timer.object, timer.callback, timer.delay, timer.arg) | 195 newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.callback, timer.delay, timer.arg) |
| 210 else | 196 else |
| 211 id = AceTimer.ScheduleTimer(timer.object, timer.callback, timer.when - GetTime(), timer.arg) | 197 newTimer = AceTimer.ScheduleTimer(timer.object, timer.callback, timer.when - GetTime(), timer.arg) |
| 212 end | 198 end |
| 213 -- change id to the old handle | 199 -- Use the old handle for old timers |
| 214 local t = activeTimers[id] | 200 activeTimers[newTimer] = nil |
| 215 activeTimers[id] = nil | 201 activeTimers[handle] = newTimer |
| 216 activeTimers[handle] = t | 202 newTimer.handle = handle |
| 217 t.id = handle | |
| 218 end | 203 end |
| 219 end | 204 end |
| 220 end | 205 end |
| 221 AceTimer.selfs = nil | 206 AceTimer.selfs = nil |
| 222 AceTimer.hash = nil | 207 AceTimer.hash = nil |
| 223 AceTimer.debug = nil | 208 AceTimer.debug = nil |
| 224 elseif oldminor and oldminor < 13 then | 209 elseif oldminor and oldminor < 17 then |
| 225 for handle, id in pairs(AceTimer.hashCompatTable) do | 210 -- Upgrade from old animation based timers to C_Timer.After timers. |
| 226 local t = activeTimers[id] | 211 AceTimer.inactiveTimers = nil |
| 227 if t then | 212 AceTimer.frame = nil |
| 228 activeTimers[id] = nil | 213 local oldTimers = AceTimer.activeTimers |
| 229 activeTimers[handle] = t | 214 -- Clear old timer table and update upvalue |
| 230 t.id = handle | 215 AceTimer.activeTimers = {} |
| 231 end | 216 activeTimers = AceTimer.activeTimers |
| 232 end | 217 for handle, timer in pairs(oldTimers) do |
| 233 AceTimer.hashCompatTable = nil | 218 local newTimer |
| 234 end | 219 -- Stop the old timer animation |
| 235 | 220 local duration, elapsed = timer:GetDuration(), timer:GetElapsed() |
| 236 -- upgrade existing timers to the latest OnFinished | 221 timer:GetParent():Stop() |
| 237 for timer in pairs(inactiveTimers) do | 222 if timer.looping then |
| 238 timer:SetScript("OnFinished", OnFinished) | 223 newTimer = AceTimer.ScheduleRepeatingTimer(timer.object, timer.func, duration, unpack(timer.args, 1, timer.argsCount)) |
| 239 end | 224 else |
| 240 | 225 newTimer = AceTimer.ScheduleTimer(timer.object, timer.func, duration - elapsed, unpack(timer.args, 1, timer.argsCount)) |
| 241 for _,timer in pairs(activeTimers) do | 226 end |
| 242 timer:SetScript("OnFinished", OnFinished) | 227 -- Use the old handle for old timers |
| 228 activeTimers[newTimer] = nil | |
| 229 activeTimers[handle] = newTimer | |
| 230 newTimer.handle = handle | |
| 231 end | |
| 232 | |
| 233 -- Migrate transitional handles | |
| 234 if oldminor < 13 and AceTimer.hashCompatTable then | |
| 235 for handle, id in pairs(AceTimer.hashCompatTable) do | |
| 236 local t = activeTimers[id] | |
| 237 if t then | |
| 238 activeTimers[id] = nil | |
| 239 activeTimers[handle] = t | |
| 240 t.handle = handle | |
| 241 end | |
| 242 end | |
| 243 AceTimer.hashCompatTable = nil | |
| 244 end | |
| 243 end | 245 end |
| 244 | 246 |
| 245 -- --------------------------------------------------------------------- | 247 -- --------------------------------------------------------------------- |
| 246 -- Embed handling | 248 -- Embed handling |
| 247 | 249 |
