3f2b013 - Add pixel border to TellMeWhen icons
[wowui.git] / OmaAB / ActionBars.lua
1 -- ActionBars.lua
2 local _;
3 local pairs = pairs;
4 local format = string.format;
5 local ssub = string.sub;
6 local GetActionInfo, GetActionTexture = GetActionInfo, GetActionTexture;
7 local GetActionLossOfControlCooldown = GetActionLossOfControlCooldown;
8 local GetActionCooldown, GetActionCharges = GetActionCooldown, GetActionCharges;
9 local IsConsumableAction, IsStackableAction = IsConsumableAction, IsStackableAction;
10 local IsItemAction, GetActionCount = IsItemAction, GetActionCount;
11 local IsSpellOverlayed, GetMacroSpell = IsSpellOverlayed, GetMacroSpell;
12 local IsMounted = IsMounted;
13 local HasAction, IsUsableAction = HasAction, IsUsableAction;
14 local IsCurrentAction, IsAutoRepeatAction = IsCurrentAction, IsAutoRepeatAction;
15 local CreateFrame = CreateFrame;
16 local RegisterStateDriver = RegisterStateDriver;
17 local CooldownFrame_Set, CooldownFrame_Clear = CooldownFrame_Set, CooldownFrame_Clear;
18 local CTimerAfter = C_Timer.After;
19 local GameTooltip = nil;
20 local COOLDOWN_TYPE_LOSS_OF_CONTROL = COOLDOWN_TYPE_LOSS_OF_CONTROL;
21 local COOLDOWN_TYPE_NORMAL = COOLDOWN_TYPE_NORMAL;
22 local CDTexture = "Interface\\Cooldown\\edge";
23 local locCDTexture = "Interface\\Cooldown\\edge-LoC";
24
25 local BUTTONLOCK = true; -- change to lock button dragging
26
27 local settings = {
28     ["Oma1"] = {
29         bar = 1,
30         start = 1,
31         length = 12,
32         columns = 4,
33         size = 40,
34         x = 580,
35         y = 300,
36         nomouse = true,
37     },
38     ["Oma2"] = {
39         bar = 2,
40         start = 13,
41         length = 12,
42         columns = 4,
43         size = 40,
44         x = 580,
45         y = 180,
46         nomouse = true,
47     },
48     ["Oma3"] = {
49         bar = 3,
50         start = 25,
51         length = 12,
52         columns = 3,
53         x = 1824,
54         y = 128,
55         flyout = "LEFT",
56     },
57     ["Oma4"] = {
58         bar = 4,
59         start = 37,
60         length = 12,
61         columns = 3,
62         x = 1824,
63         y = 256,
64     },
65     ["Oma5"] = {
66         bar = 5,
67         start = 49,
68         length = 12,
69         columns = 3,
70         x = 1000,
71         y = 840,
72     },
73     ["Oma6"] = {
74         bar = 6,
75         start = 61,
76         length = 12,
77         x = 1000,
78         y = 600,
79     },
80     -- used as bonus bars for some classes
81     ["Oma7"] = {
82         bar = 7,
83         start = 73,
84         length = 12,
85         x = 1000,
86         y = 760,
87     },
88     ["Oma8"] = {
89         bar = 8,
90         start = 85,
91         length = 12,
92         x = 1000,
93         y = 720,
94     },
95     ["Oma9"] = {
96         bar = 9,
97         start = 97,
98         length = 12,
99         x = 1000,
100         y = 680,
101     },
102     ["Oma10"] = {
103         bar = 10,
104         start = 109,
105         length = 12,
106         x = 1000,
107         y = 640,
108     },
109 };
110
111 local usingBonusbars = {
112     --["WARRIOR"] = {[7]=true, [8]=true, [9]=true}, -- not using stance separated actionbars
113     ["DRUID"] = {[7]=true, [8]=true, [9]=true}, -- moonkin form page is usable anyway
114     --["DRUID"] = {[7]=true, [8]=true, [9]=true,[10]=true},
115     ["ROGUE"] = {[7]=true},
116     --["PRIEST"] = {[7]=true}, -- shadowform doesn't change abilities
117 };
118
119 local chars = {
120     ["Stormreaver"] = {
121         ["Vildan"] = {1, 2, 3, 4,},
122         ["Gedren"] = {1, 2, 3, 4,},
123     },
124 };
125
126 local buttons = {};
127 local activeButtons = {};
128
129 local ActionBars = CreateFrame("Frame", "OmaActionBars", UIParent);
130 local inheritedFrames =
131 "SecureActionButtonTemplate,SecureHandlerDragTemplate,SecureHandlerStateTemplate";
132
133 local numChargeCDs = 0;
134 local function createChargeCD(parent)
135     numChargeCDs = numChargeCDs + 1;
136     local frame = CreateFrame("Cooldown", "OmaChargeCD"..numChargeCDs, parent, "CooldownFrameTemplate");
137     frame:SetHideCountdownNumbers(false);
138     frame:SetDrawSwipe(false);
139     frame:SetAllPoints(parent);
140     frame:SetFrameStrata("TOOLTIP");
141     return frame;
142 end
143
144 local function clearChargeCD(parent)
145     if parent.chargecd then CooldownFrame_Clear(parent.chargecd) end
146 end
147
148 local function startChargeCD(parent, start, duration, modrate)
149     if start == 0 then
150         return clearChargeCD(parent);
151     end
152     parent.chargecd = parent.chargecd or createChargeCD(parent);
153     CooldownFrame_Set(parent.chargecd, start, duration, true, true, modrate);
154 end
155
156 local redoCooldown;
157
158 local function updateCooldown(button, slot)
159     -- CD update from FrameXML/ActionButton.lua
160     local locstart, locduration = GetActionLossOfControlCooldown(slot);
161     local start, duration, enable, modrate = GetActionCooldown(slot);
162     local charges, maxcharges, chargestart, chargeduration, chargemodrate = GetActionCharges(slot);
163     if (locstart + locduration) > (start + duration) then
164         if button.cd.currentCooldownType ~= COOLDOWN_TYPE_LOSS_OF_CONTROL then
165             button.cd:SetEdgeTexture(locCDTexture);
166             button.cd:SetSwipeColor(0.17, 0, 0);
167             button.cd:SetHideCountdownNumbers(true);
168             button.cd.currentCooldownType = COOLDOWN_TYPE_LOSS_OF_CONTROL;
169         end
170
171         CooldownFrame_Set(button.cd, locstart, locduration, true, true, modrate);
172         clearChargeCD(button);
173     else
174         if button.cd.currentCooldownType ~= COOLDOWN_TYPE_NORMAL then
175             button.cd:SetEdgeTexture(CDTexture);
176             button.cd:SetSwipeColor(0, 0, 0);
177             button.cd:SetHideCountdownNumbers(false);
178             button.cd.currentCooldownType = COOLDOWN_TYPE_NORMAL;
179         end
180
181         if locstart > 0 then
182             button.cd:SetScript("OnCooldownDone", redoCooldown);
183         end
184         if charges and maxcharges and maxcharges > 1 and charges < maxcharges then
185             startChargeCD(button, chargestart, chargeduration, chargemodrate);
186         else
187             clearChargeCD(button);
188         end
189         CooldownFrame_Set(button.cd, start, duration, enable, false, modrate);
190     end
191 end
192
193 local function redoCooldown(cd)
194     local button = cd:GetParent();
195     cd:SetScript("OnCooldownDone", nil);
196     updateCooldown(button, button.slot);
197 end
198
199 local function updateCount(button, slot)
200     if IsConsumableAction(slot) or IsStackableAction(slot) or
201             (not IsItemAction(slot) and GetActionCount(slot) > 0) then
202         local count = GetActionCount(slot);
203         if count > 99 then
204             button.count:SetText("*");
205         else
206             button.count:SetText(count);
207         end
208         button.count:Show();
209     else
210         local charges, maxcharges = GetActionCharges(slot);
211         if maxcharges > 1 then
212             button.count:SetText(charges);
213             button.count:Show();
214         else
215             button.count:Hide();
216         end
217     end
218 end
219
220 local function updateUsable(button, slot)
221     local isUsable, noMana = IsUsableAction(slot);
222     if isUsable then
223         button.icon:SetVertexColor(1, 1, 1);
224     elseif noMana then
225         button.icon:SetVertexColor(0, 0.5, 1);
226     else
227         button.icon:SetVertexColor(0.4, 0.4, 0.4);
228     end
229 end
230
231 local function updateState(button, slot)
232     button:SetChecked(IsCurrentAction(slot) or IsAutoRepeatAction(slot));
233 end
234
235 local function updateGlow(button, slot, spell, hide)
236     local stype, id, _ = GetActionInfo(slot);
237     if stype == "spell" and (spell and id == spell or IsSpellOverlayed(id)) then
238         if hide then
239             button.glow:Hide();
240         else
241             button.glow:Show();
242         end
243     elseif stype == "macro" then
244         local _, _, macroid = GetMacroSpell(id);
245         if macroid and (spell and macroid == spell or IsSpellOverlayed(macroid)) then
246             if hide then
247                 button.glow:Hide();
248             else
249                 button.glow:Show();
250             end
251         else
252             button.glow:Hide();
253         end
254     else -- TODO FlyoutHasSpell glow
255         button.glow:Hide();
256     end
257 end
258
259 local function updateButton(button, slot)
260     if HasAction(slot) then
261         activeButtons[slot] = button;
262         button.base:Show();
263         button.icon:SetTexture(GetActionTexture(slot));
264         updateCooldown(button, slot);
265         updateUsable(button, slot);
266         updateState(button, slot);
267         updateCount(button, slot);
268         updateGlow(button, slot);
269         if not IsConsumableAction(slot) and not IsStackableAction(slot) then
270             button.text:SetText(ssub(GetActionText(slot) or "", 1, 4));
271             button.text:Show();
272         end
273         if button.hotkey.shown then button.hotkey:Show() end
274     else
275         activeButtons[slot] = nil;
276         if not button.grid then button.base:Hide() end
277         button.icon:SetTexture(nil);
278         button.cd:Hide();
279         button.count:Hide();
280         button.hotkey:Hide();
281         button.text:Hide();
282         button.glow:Hide();
283         button:SetChecked(false);
284     end
285 end
286
287 local function updateHotkeys(button)
288     local key = GetBindingKey(format("CLICK %s:LeftButton", button:GetName()));
289     if key and key ~= "" then
290         -- from LibKeyBound-1.0
291         key = key:upper();
292         key = key:gsub(" ", "");
293         key = key:gsub("ALT%-", "a");
294         key = key:gsub("CTRL%-", "c");
295         key = key:gsub("SHIFT%-", "s");
296         key = key:gsub("NUMPAD", "n");
297         button.hotkey:SetText(key);
298         button.hotkey.shown = true;
299         button.hotkey:Show();
300     else
301         button.hotkey.shown = nil;
302         button.hotkey:Hide();
303     end
304 end
305
306 local mainbartoggle = "[overridebar][possessbar][shapeshift]possess;";
307 mainbartoggle = mainbartoggle.."[bonusbar:1,stealth:1]bonusbar2;"; -- prowl
308 mainbartoggle = mainbartoggle.."[bonusbar:1]bonusbar1;[bonusbar:2]bonusbar2;"; -- cat form, unused
309 mainbartoggle = mainbartoggle.."[bonusbar:3]bonusbar3;[bonusbar:4]bonusbar4;"; -- bear form, moonkin form
310 mainbartoggle = mainbartoggle.."normal";
311 local function setupSnippets(secure, slot)
312     -- FrameXML/SecureHandlers.lua has arguments and return value
313     -- args: self, button, kind, value, ... (kind, value, ... from GetCursorInfo())
314     -- returns: kind, target, detail
315     -- or: "clear", kind, target, detail
316     -- used for Pickup* functions
317     -- some of these snippets based on LibActionButton-1.0
318     secure:SetAttribute("_ondragstart", [=[
319         return "action", self:GetAttribute("action");
320     ]=]);
321     secure:SetAttribute("_onreceivedrag", [=[
322         if not kind or not value then return nil end
323         return "action", self:GetAttribute("action");
324     ]=]);
325     -- pre-wrapper can pass a message to post-wrapper
326     secure:WrapScript(secure, "OnDragStart", [=[
327         local kind, value = GetActionInfo(self:GetAttribute("action"));
328         return "message", format("%s|%s", tostring(kind), tostring(value));
329     ]=], [=[
330         local kind, value = GetActionInfo(self:GetAttribute("action"));
331         if message ~= format("%s|%s", tostring(kind), tostring(value)) then
332             self:CallMethod("ActionChanged");
333         end
334     ]=]);
335     secure:WrapScript(secure, "OnReceiveDrag", [=[
336         local kind, value = GetActionInfo(self:GetAttribute("action"));
337         return "message", format("%s|%s", tostring(kind), tostring(value));
338     ]=], [=[
339         local kind, value = GetActionInfo(self:GetAttribute("action"));
340         if message ~= format("%s|%s", tostring(kind), tostring(value)) then
341             self:CallMethod("ActionChanged");
342         end
343     ]=]);
344     function secure:UpdateState()
345         return updateState(self, self.slot);
346     end
347     secure:WrapScript(secure, "OnClick", [=[
348         local kind, value = GetActionInfo(self:GetAttribute("action"));
349         return nil, format("%s|%s", tostring(kind), tostring(value));
350     ]=], [=[
351         local kind, value = GetActionInfo(self:GetAttribute("action"));
352         if message ~= format("%s|%s", tostring(kind), tostring(value)) then
353             self:CallMethod("ActionChanged");
354         else
355             self:CallMethod("UpdateState");
356         end
357     ]=]);
358     if slot < 13 then
359         -- first action bar has possible states based on vehicle/possess etc.
360         secure:SetAttribute("origaction", slot);
361         secure:SetAttribute("_onstate-possess", [=[
362             local oldslot = self:GetAttribute("action");
363             if newstate == "possess" then
364                 local slot;
365                 if HasVehicleActionBar() then
366                     slot = (GetVehicleBarIndex()-1)*12+self:GetAttribute("origaction");
367                 elseif HasOverrideActionBar() then
368                     slot = (GetOverrideBarIndex()-1)*12+self:GetAttribute("origaction");
369                 elseif HasTempShapeshiftActionBar() then
370                     slot = (GetTempShapeshiftBarIndex()-1)*12+self:GetAttribute("origaction");
371                 else
372                     -- something wrong, just revert to normal
373                     print("Possess bar index not found");
374                     slot = self:GetAttribute("origaction");
375                 end
376                 self:SetAttribute("action", slot);
377             elseif newstate == "bonusbar1" then
378                 self:SetAttribute("action", 72+self:GetAttribute("origaction"));
379             elseif newstate == "bonusbar2" then
380                 self:SetAttribute("action", 84+self:GetAttribute("origaction"));
381             elseif newstate == "bonusbar3" then
382                 self:SetAttribute("action", 96+self:GetAttribute("origaction"));
383             elseif newstate == "bonusbar4" then
384                 --self:SetAttribute("action", 108+self:GetAttribute("origaction"));
385                 -- moonkin form, don't change actionbar
386                 self:SetAttribute("action", self:GetAttribute("origaction"));
387             else
388                 self:SetAttribute("action", self:GetAttribute("origaction"));
389             end
390             self:CallMethod("ActionChanged", oldslot);
391         ]=]);
392         RegisterStateDriver(secure, "possess", mainbartoggle);
393     else
394         function secure:ShowButton() if HasAction(slot) then activeButtons[slot] = self end end
395         function secure:HideButton() activeButtons[slot] = nil end
396         -- all other action bar are hidden if with overridebar or vehicleui (not shapeshift, possessbar)
397         -- default Bartender4 options
398         secure:SetAttribute("_onstate-possess", [=[
399             if newstate == "possess" then
400                 self:Hide();
401                 self:CallMethod("HideButton");
402             else
403                 self:Show();
404                 self:CallMethod("ShowButton");
405             end
406         ]=]);
407         RegisterStateDriver(secure, "possess", "[overridebar][vehicleui] possess; normal");
408     end
409 end
410
411 local function createActionBar(parent, config)
412     local prev;
413     local i = 0;
414     local bar = CreateFrame("Frame", "OmaBTBar"..config.bar, parent, "SecureFrameTemplate");
415     bar:SetPoint("TOPLEFT", parent, "BOTTOMLEFT", config.x, config.y);
416     bar:SetWidth(1);
417     bar:SetHeight(1);
418     if config.hidden then
419         bar:Hide();
420     end
421     for slot = config.start, config.start+config.length-1 do
422         local secure = CreateFrame("CheckButton", "OmaBT"..slot, bar, inheritedFrames);
423         secure.slot = slot;
424         if slot == config.start then
425             secure:SetPoint("TOPLEFT");
426         elseif config.columns and i % config.columns == 0 then
427             secure:SetPoint("TOPLEFT", _G["OmaBT"..(slot-config.columns)], "BOTTOMLEFT");
428         else
429             secure:SetPoint("TOPLEFT", prev, "TOPRIGHT");
430         end
431         secure:RegisterForClicks("AnyUp");
432         if not BUTTONLOCK then
433             secure:RegisterForDrag("LeftButton", "RightButton");
434         end
435         if config.nomouse then
436             secure:EnableMouse(false);
437         end
438         secure:SetWidth(config.size or 32);
439         secure:SetHeight(config.size or 32);
440         secure.base = secure:CreateTexture(nil, "BACKGROUND");
441         secure.base:SetAllPoints();
442         secure.base:SetColorTexture(0, 0, 0, 0.5);
443         secure.iconbase = secure:CreateTexture(nil, "BORDER");
444         secure.iconbase:SetPoint("TOPLEFT", secure.base, "TOPLEFT", 1, -1);
445         secure.iconbase:SetPoint("BOTTOMRIGHT", secure.base, "BOTTOMRIGHT", -1, 1);
446         secure.iconbase:SetColorTexture(0, 0, 0, 0.5);
447         secure.iconbase:Hide();
448         secure.icon = secure:CreateTexture(nil, "ARTWORK");
449         secure.icon:SetPoint("TOPLEFT", secure.iconbase, "TOPLEFT");
450         secure.icon:SetPoint("BOTTOMRIGHT", secure.iconbase, "BOTTOMRIGHT");
451         secure.icon:SetTexCoord(0.07, 0.93, 0.07, 0.93);
452         secure:SetCheckedTexture("Interface\\Buttons\\CheckButtonHilight");
453         secure.autocastable = secure:CreateTexture(nil, "OVERLAY");
454         secure.autocastable:SetPoint("CENTER");
455         secure.autocastable:SetWidth(58);
456         secure.autocastable:SetHeight(58);
457         secure.autocastable:SetTexture("Interface\\Buttons\\UI-AutoCastableOverlay");
458         secure.autocastable:Hide();
459         secure.glow = secure:CreateTexture(nil, "OVERLAY", nil, 1);
460         secure.glow:SetPoint("CENTER");
461         secure.glow:SetWidth(config.size and config.size+20 or 52);
462         secure.glow:SetHeight(config.size and config.size+20 or 52);
463         secure.glow:SetTexture("Interface\\SpellActivationOverlay\\IconAlert");
464         secure.glow:SetTexCoord(0.00781250, 0.50781250, 0.27734375, 0.52634375);
465         secure.glow:Hide();
466         secure.hotkey = secure:CreateFontString(nil, "OVERLAY", "NumberFontNormalGray");
467         secure.hotkey:SetPoint("TOPRIGHT", secure, "TOPRIGHT", 2, -1);
468         secure.count = secure:CreateFontString(nil, "OVERLAY", "NumberFontNormal");
469         secure.count:SetPoint("BOTTOMRIGHT", secure, "BOTTOMRIGHT", 2, -1);
470         secure.text = secure:CreateFontString(nil, "OVERLAY", "NumberFontNormal");
471         secure.text:SetPoint("BOTTOMLEFT", secure, "BOTTOMLEFT", 2, -1);
472         secure.text:Hide();
473         secure.cd = CreateFrame("Cooldown", "OmaBTCD"..slot, secure, "CooldownFrameTemplate");
474         secure.cd:SetAllPoints();
475         secure:SetAttribute("type", "action");
476         secure:SetAttribute("action", slot);
477         if config.flyout then
478             secure:SetAttribute("flyoutDirection", config.flyout);
479         end
480         function secure:ActionChanged(oldslot)
481             if oldslot then activeButtons[oldslot] = nil end
482             self.slot = self:GetAttribute("action");
483             return updateButton(self, self.slot);
484         end
485         secure:ActionChanged(); -- initial update
486         setupSnippets(secure, slot);
487         updateHotkeys(secure);
488         buttons[slot] = secure;
489         prev = secure;
490         i = i + 1;
491     end
492 end
493
494 local function initialize()
495     local _, class = UnitClass("player");
496     local name, realm = UnitFullName("player");
497     ActionBars:SetFrameStrata("LOW");
498     ActionBars:SetPoint("BOTTOMLEFT");
499     ActionBars:SetWidth(1);
500     ActionBars:SetHeight(1);
501     for _, config in pairs(settings) do
502         if (not usingBonusbars[class] or not usingBonusbars[class][config.bar]) and
503            (not chars[realm] or not chars[realm][name] or chars[realm][name][config.bar]) then
504             createActionBar(ActionBars, config);
505         end
506     end
507 end
508
509 local function setupBindings()
510     BINDING_HEADER_OmaAB = "Oma Action Bar";
511     for i = 1,10 do
512         _G["BINDING_HEADER_OMAABBLANK"..i] = "Bar "..i;
513         for j = 1,12 do
514             _G[format("BINDING_NAME_CLICK OmaBT%d:LeftButton", (i-1)*12+j)] = format("Bar %d Button %d", i, j);
515         end
516     end
517 end
518
519 local mounted = false;
520 local throttleCD = false;
521 local function throttleCDDone()
522     throttleCD = false
523     -- update CD once more to confirm newest CD change is taken in even with some throttling
524     for _, button in pairs(activeButtons) do
525         updateCooldown(button, button.slot);
526     end
527 end
528
529 local events = {
530     ["ACTIONBAR_UPDATE_COOLDOWN"] = function()
531         if not throttleCD then -- only update at most once/frame
532             throttleCD = true;
533             for _, button in pairs(activeButtons) do
534                 updateCooldown(button, button.slot);
535             end
536             CTimerAfter(0.01, throttleCDDone); -- wait one frame
537         end
538     end,
539     ["SPELL_UPDATE_CHARGES"] = function()
540         for _, button in pairs(activeButtons) do
541             updateCount(button, button.slot);
542         end
543     end,
544     ["ACTIONBAR_SLOT_CHANGED"] = function(slot)
545         if buttons[slot] then buttons[slot]:ActionChanged() end
546     end,
547     ["ACTIONBAR_SHOWGRID"] = function()
548         for _, button in pairs(buttons) do
549             button.grid = true;
550             button.iconbase:Show();
551             if not activeButtons[button.slot] then button.base:Show() end
552         end
553     end,
554     ["ACTIONBAR_HIDEGRID"] = function()
555         for _, button in pairs(buttons) do
556             button.grid = nil;
557             button.iconbase:Hide();
558             if not activeButtons[button.slot] then button.base:Hide() end
559         end
560     end,
561     ["ACTIONBAR_UPDATE_STATE"] = function()
562         for _, button in pairs(activeButtons) do
563             updateState(button, button.slot);
564         end
565     end,
566     ["ACTIONBAR_UPDATE_USABLE"] = function()
567         for _, button in pairs(activeButtons) do
568             updateUsable(button, button.slot);
569         end
570     end,
571     ["UPDATE_OVERRIDE_ACTIONBAR"] = function()
572         if buttons[1] then -- called before PLAYER_LOGIN
573             for _, button in pairs(buttons) do
574                 updateButton(button, button.slot);
575             end
576         end
577     end,
578     ["START_AUTOREPEAT_SPELL"] = function()
579         for _, button in pairs(activeButtons) do
580             if IsAutoRepeatAction(button.slot) then
581                 button.autorepeating = true;
582                 button.icon:SetVertexColor(0, 1, 0.5);
583             end
584         end
585     end,
586     ["STOP_AUTOREPEAT_SPELL"] = function()
587         for _, button in pairs(activeButtons) do
588             if button.autorepeating then
589                 button.autorepeating = nil;
590                 updateUsable(button, button.slot);
591             end
592         end
593     end,
594     ["SPELL_ACTIVATION_OVERLAY_GLOW_SHOW"] = function(spell)
595         -- TODO create mapping from spellIDs to buttons
596         for _, button in pairs(activeButtons) do
597             updateGlow(button, button.slot, spell);
598             -- CD update might be throttled, force it
599             updateCooldown(button, button.slot);
600         end
601     end,
602     ["SPELL_ACTIVATION_OVERLAY_GLOW_HIDE"] = function(spell)
603         -- TODO create mapping from spellIDs to buttons
604         for _, button in pairs(activeButtons) do
605             updateGlow(button, button.slot, spell, true);
606             -- CD update might be throttled, force it
607             updateCooldown(button, button.slot);
608         end
609     end,
610     ["UPDATE_BINDINGS"] = function()
611         for _, button in pairs(buttons) do
612             updateHotkeys(button);
613         end
614     end,
615     ["UNIT_AURA"] = function(unit)
616         -- using UNIT_AURA instead of COMPANION_UPDATE to not update every time
617         -- someone mounts, tracking player mount status with COMPANION_UPDATE is
618         -- inconsistent
619         if (not mounted and IsMounted()) or (mounted and not IsMounted()) then
620             mounted = not mounted;
621             for _, button in pairs(activeButtons) do
622                 updateState(button, button.slot);
623             end
624         end
625     end,
626     ["UPDATE_ALL_BUTTONS"] = function()
627         for _, button in pairs(buttons) do
628             updateButton(button, button.slot);
629         end
630     end,
631     ["PLAYER_LOGIN"] = function()
632         GameTooltip = _G["GameTooltip"]; -- TODO use PLAYER_ENTERING_WORLD with MoveAnything
633         initialize();
634     end,
635     ["ADDON_LOADED"] = function(addon)
636         if addon == "OmaAB" then
637             setupBindings();
638             ActionBars:UnregisterEvent("ADDON_LOADED");
639         end
640     end,
641 };
642 events["LOSS_OF_CONTROL_ADDED"] = events["ACTIONBAR_UPDATE_COOLDOWN"];
643 events["LOSS_OF_CONTROL_UPDATE"] = events["ACTIONBAR_UPDATE_COOLDOWN"]; -- TODO might change once tooltips are in
644 events["PLAYER_MOUNT_DISPLAY_CHANGED"] = events["ACTIONBAR_UPDATE_USABLE"];
645 events["TRADE_SKILL_SHOW"] = events["ACTIONBAR_UPDATE_STATE"];
646 events["TRADE_SKILL_CLOSE"] = events["ACTIONBAR_UPDATE_STATE"];
647 events["ARCHAEOLOGY_CLOSED"] = events["ACTIONBAR_UPDATE_STATE"];
648 events["PLAYER_ENTERING_WORLD"] = events["UPDATE_ALL_BUTTONS"];
649 events["UPDATE_VEHICLE_ACTIONBAR"] = events["UPDATE_ALL_BUTTONS"];
650 events["UPDATE_SHAPESHIFT_FORM"] = events["UPDATE_ALL_BUTTONS"];
651 events["SPELL_UPDATE_ICON"] = events["UPDATE_ALL_BUTTONS"];
652 events["PET_STABLE_UPDATE"] = events["UPDATE_ALL_BUTTONS"];
653 events["PET_STABLE_SHOW"] = events["UPDATE_ALL_BUTTONS"];
654 events["PLAYER_SPECIALIZATION_CHANGED"] = events["UPDATE_ALL_BUTTONS"];
655 events["UNIT_ENTERED_VEHICLE"] = function(unit)
656     if unit == "player" then events["ACTIONBAR_UPDATE_STATE"]() end
657 end
658 events["UNIT_EXITED_VEHICLE"] = events["UNIT_ENTERED_VEHICLE"];
659 -- TODO overlay glow ? don't exactly know what it does, proc highlight?
660 -- tooltips
661
662 ActionBars:RegisterEvent("ADDON_LOADED");
663 ActionBars:RegisterEvent("PLAYER_LOGIN");
664 ActionBars:RegisterEvent("ACTIONBAR_UPDATE_COOLDOWN");
665 ActionBars:RegisterEvent("ACTIONBAR_UPDATE_USABLE");
666 ActionBars:RegisterEvent("ACTIONBAR_UPDATE_STATE");
667 ActionBars:RegisterEvent("ACTIONBAR_SLOT_CHANGED");
668 ActionBars:RegisterEvent("ACTIONBAR_SHOWGRID");
669 ActionBars:RegisterEvent("ACTIONBAR_HIDEGRID");
670 ActionBars:RegisterEvent("SPELL_UPDATE_ICON");
671 ActionBars:RegisterEvent("SPELL_UPDATE_CHARGES");
672 ActionBars:RegisterEvent("SPELL_ACTIVATION_OVERLAY_GLOW_SHOW");
673 ActionBars:RegisterEvent("SPELL_ACTIVATION_OVERLAY_GLOW_HIDE");
674 ActionBars:RegisterEvent("UPDATE_VEHICLE_ACTIONBAR");
675 ActionBars:RegisterEvent("UPDATE_OVERRIDE_ACTIONBAR");
676 ActionBars:RegisterEvent("PLAYER_MOUNT_DISPLAY_CHANGED");
677 ActionBars:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED");
678 ActionBars:RegisterEvent("UNIT_ENTERED_VEHICLE");
679 ActionBars:RegisterEvent("UNIT_EXITED_VEHICLE");
680 ActionBars:RegisterEvent("PET_STABLE_UPDATE");
681 ActionBars:RegisterEvent("PET_STABLE_SHOW");
682 ActionBars:RegisterEvent("UPDATE_BINDINGS");
683 ActionBars:RegisterUnitEvent("UNIT_AURA", "player");
684 ActionBars:SetScript("OnEvent", function(self, event, arg1)
685     events[event](arg1);
686 end);