4 local GetActionTexture = GetActionTexture;
5 local GetActionLossOfControlCooldown = GetActionLossOfControlCooldown;
6 local GetActionCooldown, GetActionCharges = GetActionCooldown, GetActionCharges;
7 local IsConsumableAction, IsStackableAction = IsConsumableAction, IsStackableAction;
8 local IsItemAction, GetActionCount = IsItemAction, GetActionCount;
9 local HasAction, IsUsableAction = HasAction, IsUsableAction;
10 local IsCurrentAction, IsAutoRepeatAction = IsCurrentAction, IsAutoRepeatAction;
11 local RegisterStateDriver = RegisterStateDriver;
12 local CooldownFrame_Set, CooldownFrame_Clear = CooldownFrame_Set, CooldownFrame_Clear;
13 local CTimerAfter = C_Timer.After;
14 local GameTooltip = nil;
15 local COOLDOWN_TYPE_LOSS_OF_CONTROL = COOLDOWN_TYPE_LOSS_OF_CONTROL;
16 local COOLDOWN_TYPE_NORMAL = COOLDOWN_TYPE_NORMAL;
17 local CDTexture = "Interface\\Cooldown\\edge";
18 local locCDTexture = "Interface\\Cooldown\\edge-LoC";
20 local BUTTONLOCK = false; -- change to lock button dragging
86 local activeButtons = {};
88 local ActionBars = CreateFrame("Frame", "OmaActionBars", UIParent);
89 local inheritedFrames = "SecureActionButtonTemplate,SecureHandlerDragTemplate,SecureHandlerStateTemplate";
91 local numChargeCDs = 0;
92 local function createChargeCD(parent)
93 numChargeCDs = numChargeCDs + 1;
94 local frame = CreateFrame("Cooldown", "OmaChargeCD"..numChargeCDs, parent, "CooldownFrameTemplate");
95 frame:SetHideCountdownNumbers(false);
96 frame:SetDrawSwipe(false);
97 frame:SetAllPoints(parent);
98 frame:SetFrameStrata("TOOLTIP");
102 local function clearChargeCD(parent)
103 if parent.chargecd then CooldownFrame_Clear(parent.chargecd) end
106 local function startChargeCD(parent, start, duration, modrate)
108 return clearChargeCD(parent);
110 parent.chargecd = parent.chargecd or createChargeCD(parent);
111 CooldownFrame_Set(parent.chargecd, start, duration, true, true, modrate);
116 local function updateCooldown(button, slot)
117 -- CD update from FrameXML/ActionButton.lua
118 local locstart, locduration = GetActionLossOfControlCooldown(slot);
119 local start, duration, enable, modrate = GetActionCooldown(slot);
120 local charges, maxcharges, chargestart, chargeduration, chargemodrate = GetActionCharges(slot);
121 if (locstart + locduration) > (start + duration) then
122 if button.cd.currentCooldownType ~= COOLDOWN_TYPE_LOSS_OF_CONTROL then
123 button.cd:SetEdgeTexture(locCDTexture);
124 button.cd:SetSwipeColor(0.17, 0, 0);
125 button.cd:SetHideCountdownNumbers(true);
126 button.cd.currentCooldownType = COOLDOWN_TYPE_LOSS_OF_CONTROL;
129 CooldownFrame_Set(button.cd, locstart, locduration, true, true, modrate);
130 clearChargeCD(button);
132 if button.cd.currentCooldownType ~= COOLDOWN_TYPE_NORMAL then
133 button.cd:SetEdgeTexture(CDTexture);
134 button.cd:SetSwipeColor(0, 0, 0);
135 button.cd:SetHideCountdownNumbers(false);
136 button.cd.currentCooldownType = COOLDOWN_TYPE_NORMAL;
140 button.cd:SetScript("OnCooldownDone", redoCooldown);
142 if charges and maxcharges and maxcharges > 1 and charges < maxcharges then
143 startChargeCD(button, chargestart, chargeduration, chargemodrate);
145 clearChargeCD(button);
147 CooldownFrame_Set(button.cd, start, duration, enable, false, modrate);
151 local function redoCooldown(cd)
152 local button = cd:GetParent();
153 cd:SetScript("OnCooldownDone", nil);
154 updateCooldown(button, button.slot);
157 local function updateCount(button, slot)
158 if IsConsumableAction(slot) or IsStackableAction(slot) or
159 (not IsItemAction(slot) and GetActionCount(slot) > 0) then
160 local count = GetActionCount(slot);
162 button.count:SetText("*");
164 button.count:SetText(count);
167 local charges, maxcharges = GetActionCharges(slot);
168 if maxcharges > 1 then
169 button.count:SetText(charges);
171 button.count:SetText("");
176 local function updateUsable(button, slot)
177 local isUsable, noMana = IsUsableAction(slot);
179 button.icon:SetVertexColor(1, 1, 1);
181 button.icon:SetVertexColor(0, 0.5, 1);
183 button.icon:SetVertexColor(0.4, 0.4, 0.4);
187 local function updateState(button, slot)
188 if IsCurrentAction(slot) or IsAutoRepeatAction(slot) then
189 button:SetChecked(true);
191 button:SetChecked(false);
195 local function updateButton(button, slot)
196 if HasAction(slot) then
197 activeButtons[slot] = button;
199 button.icon:SetTexture(GetActionTexture(slot));
200 updateCooldown(button, slot);
201 updateUsable(button, slot);
202 updateState(button, slot);
203 updateCount(button, slot);
205 activeButtons[slot] = nil;
206 if not button.grid then button.base:Hide() end
207 button.icon:SetTexture(nil);
209 button.count:SetText("");
213 local function createActionBar(parent, name, config)
215 for slot = config.start, config.start+config.length-1 do
216 local secure = CreateFrame("CheckButton", name..slot, parent, inheritedFrames);
218 if slot == config.start then
219 secure:SetPoint("TOPLEFT", parent, "BOTTOMLEFT", config.x, config.y);
221 secure:SetPoint("TOPLEFT", prev, "TOPRIGHT");
223 secure:RegisterForClicks("AnyUp");
224 if not BUTTONLOCK then
225 secure:RegisterForDrag("LeftButton", "RightButton");
228 secure:SetHeight(32);
229 secure.base = secure:CreateTexture(nil, "BACKGROUND");
230 secure.base:SetAllPoints();
231 secure.base:SetColorTexture(0, 0, 0, 0.5);
232 secure.iconbase = secure:CreateTexture(nil, "BORDER");
233 secure.iconbase:SetPoint("TOPLEFT", secure.base, "TOPLEFT", 1, -1);
234 secure.iconbase:SetPoint("BOTTOMRIGHT", secure.base, "BOTTOMRIGHT", -1, 1);
235 secure.iconbase:SetColorTexture(0, 0, 0, 0.5);
236 secure.iconbase:Hide();
237 secure.icon = secure:CreateTexture(nil, "ARTWORK");
238 secure.icon:SetPoint("TOPLEFT", secure.iconbase, "TOPLEFT");
239 secure.icon:SetPoint("BOTTOMRIGHT", secure.iconbase, "BOTTOMRIGHT");
240 secure.icon:SetTexCoord(0.07, 0.93, 0.07, 0.93);
241 secure:SetCheckedTexture("Interface\\Buttons\\CheckButtonHilight");
242 secure.hotkey = secure:CreateFontString(nil, "OVERLAY", "NumberFontNormalGray");
243 secure.hotkey:SetPoint("TOPRIGHT", secure, "TOPRIGHT", 2, -1);
244 secure.count = secure:CreateFontString(nil, "OVERLAY", "NumberFontNormal");
245 secure.count:SetPoint("BOTTOMRIGHT", secure, "BOTTOMRIGHT", 2, -1);
246 secure.cd = CreateFrame("Cooldown", name..slot.."CD", secure, "CooldownFrameTemplate");
247 secure.cd:SetAllPoints();
248 secure:SetAttribute("type", "action");
249 secure:SetAttribute("action", slot);
250 function secure:ActionChanged()
251 self.slot = self:GetAttribute("action");
252 return updateButton(self, self.slot);
254 secure:ActionChanged(); -- initial update
255 -- FrameXML/SecureHandlers.lua has arguments and return value
256 -- args: self, button, kind, value, ... (kind, value, ... from GetCursorInfo())
257 -- returns: kind, target, detail
258 -- or: "clear", kind, target, detail
259 -- used for Pickup* functions
260 -- some of these snippets based on LibActionButton-1.0
261 secure:SetAttribute("_ondragstart", [=[
262 return "action", self:GetAttribute("action");
264 secure:SetAttribute("_onreceivedrag", [=[
265 if not kind or not value then return nil end
266 return "action", self:GetAttribute("action");
268 -- pre-wrapper can pass a message to post-wrapper
269 secure:WrapScript(secure, "OnDragStart", [=[
270 local kind, value = GetActionInfo(self:GetAttribute("action"));
271 return "message", format("%s|%s", tostring(kind), tostring(value));
273 local kind, value = GetActionInfo(self:GetAttribute("action"));
274 if message ~= format("%s|%s", tostring(kind), tostring(value)) then
275 self:CallMethod("ActionChanged");
278 secure:WrapScript(secure, "OnReceiveDrag", [=[
279 local kind, value = GetActionInfo(self:GetAttribute("action"));
280 return "message", format("%s|%s", tostring(kind), tostring(value));
282 local kind, value = GetActionInfo(self:GetAttribute("action"));
283 if message ~= format("%s|%s", tostring(kind), tostring(value)) then
284 self:CallMethod("ActionChanged");
287 secure:WrapScript(secure, "OnClick", [=[
288 local kind, value = GetActionInfo(self:GetAttribute("action"));
289 return nil, format("%s|%s", tostring(kind), tostring(value));
291 local kind, value = GetActionInfo(self:GetAttribute("action"));
292 if message ~= format("%s|%s", tostring(kind), tostring(value)) then
293 self:CallMethod("ActionChanged");
297 -- first action bar has possible states based on vehicle/possess etc.
298 secure:SetAttribute("origaction", slot);
299 secure:SetAttribute("_onstate-possess", [=[
300 if newstate == "possess" then
302 if HasVehicleActionBar() then
303 slot = (GetVehicleBarIndex()-1)*12+self:GetAttribute("origaction");
304 elseif HasOverrideActionBar() then
305 slot = (GetOverrideBarIndex()-1)*12+self:GetAttribute("origaction");
306 elseif HasTempShapeshitftActionBar() then
307 slot = (GetTempShapeshiftBarIndex()-1)*12+self:GetAttribute("origaction");
309 -- something wrong, just revert to normal
310 print("Possess bar index not found");
311 slot = self:GetAttribute("origaction");
313 self:SetAttribute("action", slot);
315 self:SetAttribute("action", self:GetAttribute("origaction"));
317 self:CallMethod("ActionChanged");
319 RegisterStateDriver(secure, "possess", "[overridebar][possessbar][shapeshift] possess; normal");
321 function secure:ShowButton() if HasAction(slot) then activeButtons[slot] = self end end
322 function secure:HideButton() activeButtons[slot] = nil end
323 -- all other action bar are hidden if with overridebar or vehicleui (not shapeshift, possessbar)
324 -- default Bartender4 options
325 secure:SetAttribute("_onstate-possess", [=[
326 if newstate == "possess" then
328 self:CallMethod("HideButton");
331 self:CallMethod("ShowButton");
334 RegisterStateDriver(secure, "possess", "[overridebar][vehicleui] possess; normal");
336 buttons[slot] = secure;
341 local function initialize()
342 ActionBars:SetFrameStrata("LOW");
343 ActionBars:SetPoint("BOTTOMLEFT");
344 ActionBars:SetWidth(1);
345 ActionBars:SetHeight(1);
346 for name, config in pairs(settings) do
347 createActionBar(ActionBars, name, config);
351 local throttleCD = false;
352 local function throttleCDDone() throttleCD = false end
355 ["ACTIONBAR_UPDATE_COOLDOWN"] = function()
356 if not throttleCD then -- only update at most once/frame
358 for _, button in pairs(activeButtons) do
359 updateCooldown(button, button.slot);
361 CTimerAfter(0.01, throttleCDDone); -- wait one frame
364 ["SPELL_UPDATE_CHARGES"] = function()
365 for _, button in pairs(activeButtons) do
366 updateCount(button, button.slot);
369 ["ACTIONBAR_SLOT_CHANGED"] = function(slot)
370 if buttons[slot] then buttons[slot]:ActionChanged() end
372 ["ACTIONBAR_SHOWGRID"] = function()
373 for _, button in pairs(buttons) do
375 button.iconbase:Show();
376 if not activeButtons[button.slot] then button.base:Show() end
379 ["ACTIONBAR_HIDEGRID"] = function()
380 for _, button in pairs(buttons) do
382 button.iconbase:Hide();
383 if not activeButtons[button.slot] then button.base:Hide() end
386 ["ACTIONBAR_UPDATE_STATE"] = function()
387 for _, button in pairs(activeButtons) do
388 updateState(button, button.slot);
391 ["ACTIONBAR_UPDATE_USABLE"] = function()
392 for _, button in pairs(activeButtons) do
393 updateUsable(button, button.slot);
396 ["UPDATE_OVERRIDE_ACTIONBAR"] = function()
397 if buttons[1] then -- called before PLAYER_LOGIN
398 for _, button in pairs(buttons) do
399 updateButton(button, button.slot);
403 ["UPDATE_BINDINGS"] = function()
405 ["UPDATE_ALL_BUTTONS"] = function()
406 for _, button in pairs(buttons) do
407 updateButton(button, button.slot);
410 ["PLAYER_LOGIN"] = function()
411 GameTooltip = _G["GameTooltip"];
415 events["LOSS_OF_CONTROL_ADDED"] = events["ACTIONBAR_UPDATE_COOLDOWN"];
416 events["LOSS_OF_CONTROL_UPDATE"] = events["ACTIONBAR_UPDATE_COOLDOWN"]; -- TODO might change once tooltips are in
417 events["PLAYER_MOUNT_DISPLAY_CHANGED"] = events["ACTIONBAR_UPDATE_USABLE"];
418 events["UPDATE_VEHICLE_ACTIONBAR"] = events["UPDATE_ALL_BUTTONS"];
419 events["SPELL_UPDATE_ICON"] = events["UPDATE_ALL_BUTTONS"];
420 events["PET_STABLE_UPDATE"] = events["UPDATE_ALL_BUTTONS"];
421 events["PET_STABLE_SHOW"] = events["UPDATE_ALL_BUTTONS"];
422 events["PLAYER_SPECIALIZATION_CHANGED"] = events["UPDATE_ALL_BUTTONS"];
423 events["UNIT_ENTERED_VEHICLE"] = function(unit)
424 if unit == "player" then events["ACTIONBAR_UPDATE_STATE"]() end
426 events["UNIT_EXITED_VEHICLE"] = events["UNIT_ENTERED_VEHICLE"];
427 -- TODO COMPANION_UPDATE needed?
428 -- TODO overlay glow ? don't exactly know what it does, proc highlight?
429 -- autorepeat, tooltips
431 ActionBars:RegisterEvent("PLAYER_LOGIN");
432 ActionBars:RegisterEvent("ACTIONBAR_UPDATE_COOLDOWN");
433 ActionBars:RegisterEvent("ACTIONBAR_UPDATE_USABLE");
434 ActionBars:RegisterEvent("ACTIONBAR_UPDATE_STATE");
435 ActionBars:RegisterEvent("ACTIONBAR_SLOT_CHANGED");
436 ActionBars:RegisterEvent("ACTIONBAR_SHOWGRID");
437 ActionBars:RegisterEvent("ACTIONBAR_HIDEGRID");
438 ActionBars:RegisterEvent("SPELL_UPDATE_ICON");
439 ActionBars:RegisterEvent("SPELL_UPDATE_CHARGES");
440 ActionBars:RegisterEvent("UPDATE_VEHICLE_ACTIONBAR");
441 ActionBars:RegisterEvent("UPDATE_OVERRIDE_ACTIONBAR");
442 ActionBars:RegisterEvent("PLAYER_MOUNT_DISPLAY_CHANGED");
443 ActionBars:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED");
444 ActionBars:RegisterEvent("UNIT_ENTERED_VEHICLE");
445 ActionBars:RegisterEvent("UNIT_EXITED_VEHICLE");
446 ActionBars:RegisterEvent("PET_STABLE_UPDATE");
447 ActionBars:RegisterEvent("PET_STABLE_SHOW");
448 ActionBars:SetScript("OnEvent", function(self, event, arg1)