Mercurial > wow > hotcorners
comparison Libs/AceTimer-3.0/AceTimer-3.0.lua @ 0:fc346da3afd9
First commit Hot Corners standalone.
| author | tercio |
|---|---|
| date | Fri, 08 Aug 2014 12:35:17 -0300 |
| parents | |
| children | c31ee4251181 |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:fc346da3afd9 |
|---|---|
| 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 | |
| 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.\\ | |
| 5 -- AceTimer is currently limited to firing timers at a frequency of 0.01s. This constant may change | |
| 6 -- in the future, but for now it's required as animations with lower frequencies are buggy. | |
| 7 -- | |
| 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. | |
| 10 -- | |
| 11 -- **AceTimer-3.0** can be embeded into your addon, either explicitly by calling AceTimer:Embed(MyAddon) or by | |
| 12 -- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object | |
| 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 | |
| 15 -- make into AceTimer. | |
| 16 -- @class file | |
| 17 -- @name AceTimer-3.0 | |
| 18 -- @release $Id: AceTimer-3.0.lua 1079 2013-02-17 19:56:06Z funkydude $ | |
| 19 | |
| 20 local MAJOR, MINOR = "AceTimer-3.0", 16 -- Bump minor on changes | |
| 21 local AceTimer, oldminor = LibStub:NewLibrary(MAJOR, MINOR) | |
| 22 | |
| 23 if not AceTimer then return end -- No upgrade needed | |
| 24 | |
| 25 AceTimer.frame = AceTimer.frame or CreateFrame("Frame", "AceTimer30Frame") -- Animation parent | |
| 26 AceTimer.inactiveTimers = AceTimer.inactiveTimers or {} -- Timer recycling storage | |
| 27 AceTimer.activeTimers = AceTimer.activeTimers or {} -- Active timer list | |
| 28 | |
| 29 -- Lua APIs | |
| 30 local type, unpack, next, error, pairs, tostring, select = type, unpack, next, error, pairs, tostring, select | |
| 31 | |
| 32 -- Upvalue our private data | |
| 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 | |
| 55 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 | |
| 68 delay = 0.01 | |
| 69 end | |
| 70 | |
| 71 timer.object = self | |
| 72 timer.func = func | |
| 73 timer.looping = loop | |
| 74 timer.args = {...} | |
| 75 timer.argsCount = select("#", ...) | |
| 76 | |
| 77 local anim = timer:GetParent() | |
| 78 if loop then | |
| 79 anim:SetLooping("REPEAT") | |
| 80 else | |
| 81 anim:SetLooping("NONE") | |
| 82 end | |
| 83 timer:SetDuration(delay) | |
| 84 | |
| 85 local id = tostring(timer.args) | |
| 86 timer.id = id | |
| 87 activeTimers[id] = timer | |
| 88 | |
| 89 anim:Play() | |
| 90 return id | |
| 91 end | |
| 92 | |
| 93 --- Schedule a new one-shot timer. | |
| 94 -- The timer will fire once in `delay` seconds, unless canceled before. | |
| 95 -- @param callback Callback function for the timer pulse (funcref or method name). | |
| 96 -- @param delay Delay for the timer, in seconds. | |
| 97 -- @param ... An optional, unlimited amount of arguments to pass to the callback function. | |
| 98 -- @usage | |
| 99 -- MyAddOn = LibStub("AceAddon-3.0"):NewAddon("MyAddOn", "AceTimer-3.0") | |
| 100 -- | |
| 101 -- function MyAddOn:OnEnable() | |
| 102 -- self:ScheduleTimer("TimerFeedback", 5) | |
| 103 -- end | |
| 104 -- | |
| 105 -- function MyAddOn:TimerFeedback() | |
| 106 -- print("5 seconds passed") | |
| 107 -- end | |
| 108 function AceTimer:ScheduleTimer(func, delay, ...) | |
| 109 if not func or not delay then | |
| 110 error(MAJOR..": ScheduleTimer(callback, delay, args...): 'callback' and 'delay' must have set values.", 2) | |
| 111 end | |
| 112 if type(func) == "string" then | |
| 113 if type(self) ~= "table" then | |
| 114 error(MAJOR..": ScheduleTimer(callback, delay, args...): 'self' - must be a table.", 2) | |
| 115 elseif not self[func] then | |
| 116 error(MAJOR..": ScheduleTimer(callback, delay, args...): Tried to register '"..func.."' as the callback, but it doesn't exist in the module.", 2) | |
| 117 end | |
| 118 end | |
| 119 return new(self, nil, func, delay, ...) | |
| 120 end | |
| 121 | |
| 122 --- Schedule a repeating timer. | |
| 123 -- The timer will fire every `delay` seconds, until canceled. | |
| 124 -- @param callback Callback function for the timer pulse (funcref or method name). | |
| 125 -- @param delay Delay for the timer, in seconds. | |
| 126 -- @param ... An optional, unlimited amount of arguments to pass to the callback function. | |
| 127 -- @usage | |
| 128 -- MyAddOn = LibStub("AceAddon-3.0"):NewAddon("MyAddOn", "AceTimer-3.0") | |
| 129 -- | |
| 130 -- function MyAddOn:OnEnable() | |
| 131 -- self.timerCount = 0 | |
| 132 -- self.testTimer = self:ScheduleRepeatingTimer("TimerFeedback", 5) | |
| 133 -- end | |
| 134 -- | |
| 135 -- function MyAddOn:TimerFeedback() | |
| 136 -- self.timerCount = self.timerCount + 1 | |
| 137 -- print(("%d seconds passed"):format(5 * self.timerCount)) | |
| 138 -- -- run 30 seconds in total | |
| 139 -- if self.timerCount == 6 then | |
| 140 -- self:CancelTimer(self.testTimer) | |
| 141 -- end | |
| 142 -- end | |
| 143 function AceTimer:ScheduleRepeatingTimer(func, delay, ...) | |
| 144 if not func or not delay then | |
| 145 error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): 'callback' and 'delay' must have set values.", 2) | |
| 146 end | |
| 147 if type(func) == "string" then | |
| 148 if type(self) ~= "table" then | |
| 149 error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): 'self' - must be a table.", 2) | |
| 150 elseif not self[func] then | |
| 151 error(MAJOR..": ScheduleRepeatingTimer(callback, delay, args...): Tried to register '"..func.."' as the callback, but it doesn't exist in the module.", 2) | |
| 152 end | |
| 153 end | |
| 154 return new(self, true, func, delay, ...) | |
| 155 end | |
| 156 | |
| 157 --- Cancels a timer with the given id, registered by the same addon object as used for `:ScheduleTimer` | |
| 158 -- 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. | |
| 160 -- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer` | |
| 161 function AceTimer:CancelTimer(id) | |
| 162 local timer = activeTimers[id] | |
| 163 if not timer then return false end | |
| 164 | |
| 165 local anim = timer:GetParent() | |
| 166 anim:Stop() | |
| 167 | |
| 168 activeTimers[id] = nil | |
| 169 timer.args = nil | |
| 170 inactiveTimers[timer] = true | |
| 171 return true | |
| 172 end | |
| 173 | |
| 174 --- Cancels all timers registered to the current addon object ('self') | |
| 175 function AceTimer:CancelAllTimers() | |
| 176 for k,v in pairs(activeTimers) do | |
| 177 if v.object == self then | |
| 178 AceTimer.CancelTimer(self, k) | |
| 179 end | |
| 180 end | |
| 181 end | |
| 182 | |
| 183 --- Returns the time left for a timer with the given id, registered by the current addon object ('self'). | |
| 184 -- This function will return 0 when the id is invalid. | |
| 185 -- @param id The id of the timer, as returned by `:ScheduleTimer` or `:ScheduleRepeatingTimer` | |
| 186 -- @return The time left on the timer. | |
| 187 function AceTimer:TimeLeft(id) | |
| 188 local timer = activeTimers[id] | |
| 189 if not timer then return 0 end | |
| 190 return timer:GetDuration() - timer:GetElapsed() | |
| 191 end | |
| 192 | |
| 193 | |
| 194 -- --------------------------------------------------------------------- | |
| 195 -- Upgrading | |
| 196 | |
| 197 -- Upgrade from old hash-bucket based timers to animation timers | |
| 198 if oldminor and oldminor < 10 then | |
| 199 -- disable old timer logic | |
| 200 AceTimer.frame:SetScript("OnUpdate", nil) | |
| 201 AceTimer.frame:SetScript("OnEvent", nil) | |
| 202 AceTimer.frame:UnregisterAllEvents() | |
| 203 -- convert timers | |
| 204 for object,timers in pairs(AceTimer.selfs) do | |
| 205 for handle,timer in pairs(timers) do | |
| 206 if type(timer) == "table" and timer.callback then | |
| 207 local id | |
| 208 if timer.delay then | |
| 209 id = AceTimer.ScheduleRepeatingTimer(timer.object, timer.callback, timer.delay, timer.arg) | |
| 210 else | |
| 211 id = AceTimer.ScheduleTimer(timer.object, timer.callback, timer.when - GetTime(), timer.arg) | |
| 212 end | |
| 213 -- change id to the old handle | |
| 214 local t = activeTimers[id] | |
| 215 activeTimers[id] = nil | |
| 216 activeTimers[handle] = t | |
| 217 t.id = handle | |
| 218 end | |
| 219 end | |
| 220 end | |
| 221 AceTimer.selfs = nil | |
| 222 AceTimer.hash = nil | |
| 223 AceTimer.debug = nil | |
| 224 elseif oldminor and oldminor < 13 then | |
| 225 for handle, id in pairs(AceTimer.hashCompatTable) do | |
| 226 local t = activeTimers[id] | |
| 227 if t then | |
| 228 activeTimers[id] = nil | |
| 229 activeTimers[handle] = t | |
| 230 t.id = handle | |
| 231 end | |
| 232 end | |
| 233 AceTimer.hashCompatTable = nil | |
| 234 end | |
| 235 | |
| 236 -- upgrade existing timers to the latest OnFinished | |
| 237 for timer in pairs(inactiveTimers) do | |
| 238 timer:SetScript("OnFinished", OnFinished) | |
| 239 end | |
| 240 | |
| 241 for _,timer in pairs(activeTimers) do | |
| 242 timer:SetScript("OnFinished", OnFinished) | |
| 243 end | |
| 244 | |
| 245 -- --------------------------------------------------------------------- | |
| 246 -- Embed handling | |
| 247 | |
| 248 AceTimer.embeds = AceTimer.embeds or {} | |
| 249 | |
| 250 local mixins = { | |
| 251 "ScheduleTimer", "ScheduleRepeatingTimer", | |
| 252 "CancelTimer", "CancelAllTimers", | |
| 253 "TimeLeft" | |
| 254 } | |
| 255 | |
| 256 function AceTimer:Embed(target) | |
| 257 AceTimer.embeds[target] = true | |
| 258 for _,v in pairs(mixins) do | |
| 259 target[v] = AceTimer[v] | |
| 260 end | |
| 261 return target | |
| 262 end | |
| 263 | |
| 264 -- AceTimer:OnEmbedDisable(target) | |
| 265 -- target (object) - target object that AceTimer is embedded in. | |
| 266 -- | |
| 267 -- cancel all timers registered for the object | |
| 268 function AceTimer:OnEmbedDisable(target) | |
| 269 target:CancelAllTimers() | |
| 270 end | |
| 271 | |
| 272 for addon in pairs(AceTimer.embeds) do | |
| 273 AceTimer:Embed(addon) | |
| 274 end |
