ActionButton.lua

ActionButton.lua (9.0.2.36294; unchanged since 9.0.1.36230)
  1. CURRENT_ACTIONBAR_PAGE = 1;
  2. NUM_ACTIONBAR_PAGES = 6;
  3. NUM_ACTIONBAR_BUTTONS = 12;
  4. NUM_OVERRIDE_BUTTONS = 6;
  5. ATTACK_BUTTON_FLASH_TIME = 0.4;
  6. BOTTOMLEFT_ACTIONBAR_PAGE = 6;
  7. BOTTOMRIGHT_ACTIONBAR_PAGE = 5;
  8. LEFT_ACTIONBAR_PAGE = 4;
  9. RIGHT_ACTIONBAR_PAGE = 3;
  10. RANGE_INDICATOR = "‚óŹ";
  11. COOLDOWN_TYPE_LOSS_OF_CONTROL = 1;
  12. COOLDOWN_TYPE_NORMAL = 2;
  13. -- Table of actionbar pages and whether they're viewable or not
  14. VIEWABLE_ACTION_BAR_PAGES = {1, 1, 1, 1, 1, 1};
  15. ACTION_HIGHLIGHT_MARKS = { };
  16. ON_BAR_HIGHLIGHT_MARKS = { };
  17. ACTION_BUTTON_SHOW_GRID_REASON_CVAR = 1;
  18. ACTION_BUTTON_SHOW_GRID_REASON_EVENT = 2;
  19. ACTION_BUTTON_SHOW_GRID_REASON_SPELLBOOK = 4;
  20. function IsOnPrimaryActionBar(action)
  21. return action >= 1 and action <= NUM_ACTIONBAR_BUTTONS;
  22. end
  23. function MarkNewActionHighlight(action)
  24. ACTION_HIGHLIGHT_MARKS[action] = true;
  25. end
  26. function ClearNewActionHighlight(action, preventIdenticalActionsFromClearing)
  27. ACTION_HIGHLIGHT_MARKS[action] = nil;
  28. if preventIdenticalActionsFromClearing then
  29. return;
  30. end
  31. -- If we're unhighlighting this one because it was used/moused over/etc...
  32. -- then go find all other current actions that match this one that are also
  33. -- marked for highlight and unmark them. The next time they update the highlight
  34. -- will update; may need to actually force update the action button in some cases
  35. -- and that means that ACTION_HIGHLIGHT_MARKS needs to store more information
  36. local unmarkedType, unmarkedID = GetActionInfo(action);
  37. for actionKey, markValue in pairs(ACTION_HIGHLIGHT_MARKS) do
  38. if markValue then
  39. local actionType, actionID = GetActionInfo(actionKey);
  40. if actionType == unmarkedType and actionID == unmarkedID then
  41. ACTION_HIGHLIGHT_MARKS[actionKey] = nil;
  42. end
  43. end
  44. end
  45. end
  46. function GetNewActionHighlightMark(action)
  47. return ACTION_HIGHLIGHT_MARKS[action];
  48. end
  49. function ClearOnBarHighlightMarks()
  50. ON_BAR_HIGHLIGHT_MARKS = {};
  51. end
  52. function GetOnBarHighlightMark(action)
  53. return ON_BAR_HIGHLIGHT_MARKS[action];
  54. end
  55. local function UpdateOnBarHighlightMarks(actionButtonSlots)
  56. if actionButtonSlots then
  57. ON_BAR_HIGHLIGHT_MARKS = tInvert(actionButtonSlots);
  58. else
  59. ClearOnBarHighlightMarks();
  60. end
  61. end
  62. function UpdateOnBarHighlightMarksBySpell(spellID)
  63. UpdateOnBarHighlightMarks(C_ActionBar.FindSpellActionButtons(spellID));
  64. end
  65. function UpdateOnBarHighlightMarksByFlyout(flyoutID)
  66. UpdateOnBarHighlightMarks(C_ActionBar.FindFlyoutActionButtons(flyoutID));
  67. end
  68. function UpdateOnBarHighlightMarksByPetAction(petAction)
  69. UpdateOnBarHighlightMarks(C_ActionBar.FindPetActionButtons(petAction));
  70. end
  71. function GetActionButtonForID(id)
  72. if OverrideActionBar and OverrideActionBar:IsShown() then
  73. if id > NUM_OVERRIDE_BUTTONS then
  74. return;
  75. end
  76. return _G["OverrideActionBarButton"..id];
  77. end
  78. return _G["ActionButton"..id];
  79. end
  80. local function CheckUseActionButton(button, checkingFromDown)
  81. local actionButtonUseKeyDown = GetCVarBool("ActionButtonUseKeyDown");
  82. local doAction = (checkingFromDown and actionButtonUseKeyDown) or not (checkingFromDown or actionButtonUseKeyDown);
  83. if doAction then
  84. if not button.ZoneAbilityDisabled then
  85. SecureActionButton_OnClick(button, "LeftButton");
  86. if GetNewActionHighlightMark(button.action) then
  87. ClearNewActionHighlight(button.action);
  88. button:UpdateHighlightMark();
  89. end
  90. end
  91. button:UpdateState();
  92. end
  93. end
  94. local isInPetBattle = C_PetBattles.IsInBattle;
  95. local function CheckPetActionButtonEvent(id, isDown)
  96. if isInPetBattle() and PetBattleFrame then
  97. if isDown then
  98. PetBattleFrame_ButtonDown(id);
  99. else
  100. PetBattleFrame_ButtonUp(id);
  101. end
  102. return true;
  103. end
  104. return false;
  105. end
  106. function ActionButtonDown(id)
  107. if CheckPetActionButtonEvent(id, true) then
  108. return;
  109. end
  110. local button = GetActionButtonForID(id);
  111. if button then
  112. if button:GetButtonState() == "NORMAL" then
  113. button:SetButtonState("PUSHED");
  114. end
  115. CheckUseActionButton(button, true);
  116. end
  117. end
  118. function ActionButtonUp(id)
  119. if CheckPetActionButtonEvent(id, false) then
  120. return;
  121. end
  122. local button = GetActionButtonForID(id);
  123. if button then
  124. if ( button:GetButtonState() == "PUSHED" ) then
  125. button:SetButtonState("NORMAL");
  126. CheckUseActionButton(button, false);
  127. end
  128. end
  129. end
  130. function ActionBar_PageUp()
  131. local nextPage;
  132. for i=GetActionBarPage() + 1, NUM_ACTIONBAR_PAGES do
  133. if ( VIEWABLE_ACTION_BAR_PAGES[i] ) then
  134. nextPage = i;
  135. break;
  136. end
  137. end
  138. if ( not nextPage ) then
  139. nextPage = 1;
  140. end
  141. ChangeActionBarPage(nextPage);
  142. end
  143. function ActionBar_PageDown()
  144. local prevPage;
  145. for i=GetActionBarPage() - 1, 1, -1 do
  146. if ( VIEWABLE_ACTION_BAR_PAGES[i] ) then
  147. prevPage = i;
  148. break;
  149. end
  150. end
  151. if ( not prevPage ) then
  152. for i=NUM_ACTIONBAR_PAGES, 1, -1 do
  153. if ( VIEWABLE_ACTION_BAR_PAGES[i] ) then
  154. prevPage = i;
  155. break;
  156. end
  157. end
  158. end
  159. ChangeActionBarPage(prevPage);
  160. end
  161. ActionBarButtonEventsFrameMixin = {};
  162. function ActionBarButtonEventsFrameMixin:OnLoad()
  163. self.frames = {};
  164. self:RegisterEvent("PLAYER_ENTERING_WORLD");
  165. self:RegisterEvent("ACTIONBAR_SHOWGRID");
  166. self:RegisterEvent("ACTIONBAR_HIDEGRID");
  167. self:RegisterEvent("ACTIONBAR_SLOT_CHANGED");
  168. self:RegisterEvent("UPDATE_BINDINGS");
  169. self:RegisterEvent("UPDATE_SHAPESHIFT_FORM");
  170. self:RegisterEvent("ACTIONBAR_UPDATE_COOLDOWN");
  171. self:RegisterEvent("PET_BAR_UPDATE");
  172. self:RegisterUnitEvent("UNIT_FLAGS", "pet");
  173. self:RegisterUnitEvent("UNIT_AURA", "pet");
  174. self:RegisterEvent("PLAYER_MOUNT_DISPLAY_CHANGED");
  175. end
  176. function ActionBarButtonEventsFrameMixin:OnEvent(event, ...)
  177. -- pass event down to the buttons
  178. for k, frame in pairs(self.frames) do
  179. frame:OnEvent(event, ...);
  180. end
  181. end
  182. function ActionBarButtonEventsFrameMixin:RegisterFrame(frame)
  183. tinsert(self.frames, frame);
  184. end
  185. ActionBarActionEventsFrameMixin = {};
  186. function ActionBarActionEventsFrameMixin:OnLoad()
  187. self.frames = {};
  188. --self:RegisterEvent("ACTIONBAR_UPDATE_STATE"); not updating state from lua anymore, see SetActionUIButton
  189. self:RegisterEvent("ACTIONBAR_UPDATE_USABLE");
  190. --self:RegisterEvent("ACTIONBAR_UPDATE_COOLDOWN"); not updating cooldown from lua anymore, see SetActionUIButton
  191. self:RegisterEvent("SPELL_UPDATE_CHARGES");
  192. self:RegisterEvent("UPDATE_INVENTORY_ALERTS");
  193. self:RegisterEvent("PLAYER_TARGET_CHANGED");
  194. self:RegisterEvent("TRADE_SKILL_SHOW");
  195. self:RegisterEvent("TRADE_SKILL_CLOSE");
  196. self:RegisterEvent("ARCHAEOLOGY_CLOSED");
  197. self:RegisterEvent("PLAYER_ENTER_COMBAT");
  198. self:RegisterEvent("PLAYER_LEAVE_COMBAT");
  199. self:RegisterEvent("START_AUTOREPEAT_SPELL");
  200. self:RegisterEvent("STOP_AUTOREPEAT_SPELL");
  201. self:RegisterEvent("UNIT_ENTERED_VEHICLE");
  202. self:RegisterEvent("UNIT_EXITED_VEHICLE");
  203. self:RegisterEvent("COMPANION_UPDATE");
  204. self:RegisterEvent("UNIT_INVENTORY_CHANGED");
  205. self:RegisterEvent("LEARNED_SPELL_IN_TAB");
  206. self:RegisterEvent("PET_STABLE_UPDATE");
  207. self:RegisterEvent("PET_STABLE_SHOW");
  208. self:RegisterEvent("SPELL_ACTIVATION_OVERLAY_GLOW_SHOW");
  209. self:RegisterEvent("SPELL_ACTIVATION_OVERLAY_GLOW_HIDE");
  210. self:RegisterEvent("UPDATE_SUMMONPETS_ACTION");
  211. self:RegisterEvent("LOSS_OF_CONTROL_ADDED");
  212. self:RegisterEvent("LOSS_OF_CONTROL_UPDATE");
  213. self:RegisterEvent("SPELL_UPDATE_ICON");
  214. end
  215. function ActionBarActionEventsFrameMixin:OnEvent(event, ...)
  216. if ( event == "UNIT_INVENTORY_CHANGED" ) then
  217. local unit = ...;
  218. if ( unit == "player" and self.tooltipOwner and GameTooltip:GetOwner() == self.tooltipOwner ) then
  219. self.tooltipOwner:SetTooltip();
  220. end
  221. else
  222. for k, frame in pairs(self.frames) do
  223. frame:OnEvent(event, ...);
  224. end
  225. end
  226. end
  227. function ActionBarActionEventsFrameMixin:RegisterFrame(frame)
  228. self.frames[frame] = frame;
  229. end
  230. function ActionBarActionEventsFrameMixin:UnregisterFrame(frame)
  231. self.frames[frame] = nil;
  232. end
  233. ActionBarActionButtonMixin = {};
  234. function ActionBarActionButtonMixin:OnLoad()
  235. self.flashing = 0;
  236. self.flashtime = 0;
  237. self:SetAttribute("showgrid", 0);
  238. self:SetAttribute("type", "action");
  239. self:SetAttribute("checkselfcast", true);
  240. self:SetAttribute("checkfocuscast", true);
  241. self:SetAttribute("useparent-unit", true);
  242. self:SetAttribute("useparent-actionpage", true);
  243. self:RegisterForDrag("LeftButton", "RightButton");
  244. self:RegisterForClicks("AnyUp");
  245. ActionBarButtonEventsFrame:RegisterFrame(self);
  246. self:UpdateAction();
  247. self:UpdateHotkeys(self.buttonType);
  248. end
  249. function ActionBarActionButtonMixin:UpdateHotkeys(actionButtonType)
  250. local id;
  251. if ( not actionButtonType ) then
  252. actionButtonType = "ACTIONBUTTON";
  253. id = self:GetID();
  254. else
  255. if ( actionButtonType == "MULTICASTACTIONBUTTON" ) then
  256. id = self.buttonIndex;
  257. else
  258. id = self:GetID();
  259. end
  260. end
  261. local hotkey = self.HotKey;
  262. local key = GetBindingKey(actionButtonType..id) or
  263. GetBindingKey("CLICK "..self:GetName()..":LeftButton");
  264. local text = GetBindingText(key, 1);
  265. if ( text == "" ) then
  266. hotkey:SetText(RANGE_INDICATOR);
  267. hotkey:Hide();
  268. else
  269. hotkey:SetText(text);
  270. hotkey:Show();
  271. end
  272. end
  273. function ActionBarActionButtonMixin:UpdateAction(force)
  274. local action = self:CalculateAction();
  275. if ( action ~= self.action or force ) then
  276. self.action = action;
  277. SetActionUIButton(self, action, self.cooldown);
  278. self:Update();
  279. end
  280. end
  281. function ActionBarActionButtonMixin:Update()
  282. local action = self.action;
  283. local icon = self.icon;
  284. local buttonCooldown = self.cooldown;
  285. local texture = GetActionTexture(action);
  286. self.zoneAbilityDisabled = false;
  287. icon:SetDesaturated(false);
  288. local type, id = GetActionInfo(action);
  289. if ( HasAction(action) ) then
  290. if ( not self.eventsRegistered ) then
  291. ActionBarActionEventsFrame:RegisterFrame(self);
  292. self.eventsRegistered = true;
  293. end
  294. if ( not self:GetAttribute("statehidden") ) then
  295. self:Show();
  296. end
  297. self:UpdateState();
  298. self:UpdateUsable();
  299. ActionButton_UpdateCooldown(self);
  300. self:UpdateFlash();
  301. self:UpdateHighlightMark();
  302. self:UpdateSpellHighlightMark();
  303. else
  304. if ( self.eventsRegistered ) then
  305. ActionBarActionEventsFrame:UnregisterFrame(self);
  306. self.eventsRegistered = nil;
  307. end
  308. if ( self:GetAttribute("showgrid") == 0 ) then
  309. self:Hide();
  310. else
  311. buttonCooldown:Hide();
  312. end
  313. ClearChargeCooldown(self);
  314. self:ClearFlash();
  315. self:SetChecked(false);
  316. if self.LevelLinkLockIcon then
  317. self.LevelLinkLockIcon:SetShown(false);
  318. end
  319. end
  320. -- Add a green border if button is an equipped item
  321. local border = self.Border;
  322. if border then
  323. if ( IsEquippedAction(action) ) then
  324. border:SetVertexColor(0, 1.0, 0, 0.35);
  325. border:Show();
  326. else
  327. border:Hide();
  328. end
  329. end
  330. -- Update Action Text
  331. local actionName = self.Name;
  332. if actionName then
  333. if ( not IsConsumableAction(action) and not IsStackableAction(action) and (IsItemAction(action) or GetActionCount(action) == 0) ) then
  334. actionName:SetText(GetActionText(action));
  335. else
  336. actionName:SetText("");
  337. end
  338. end
  339. -- Update icon and hotkey text
  340. if ( texture ) then
  341. icon:SetTexture(texture);
  342. icon:Show();
  343. self.rangeTimer = -1;
  344. self:UpdateCount();
  345. else
  346. self.Count:SetText("");
  347. icon:Hide();
  348. buttonCooldown:Hide();
  349. self.rangeTimer = nil;
  350. local hotkey = self.HotKey;
  351. if ( hotkey:GetText() == RANGE_INDICATOR ) then
  352. hotkey:Hide();
  353. else
  354. hotkey:SetVertexColor(0.6, 0.6, 0.6);
  355. end
  356. end
  357. -- Update flyout appearance
  358. ActionButton_UpdateFlyout(self);
  359. self:UpdateOverlayGlow();
  360. -- Update tooltip
  361. if ( GameTooltip:GetOwner() == self ) then
  362. self:SetTooltip();
  363. end
  364. self.feedback_action = action;
  365. end
  366. function ActionBarActionButtonMixin:UpdateHighlightMark()
  367. if ( self.NewActionTexture ) then
  368. self.NewActionTexture:SetShown(GetNewActionHighlightMark(self.action));
  369. end
  370. end
  371. -- Shared between the action bar and the pet bar.
  372. function SharedActionButton_RefreshSpellHighlight(button, shown)
  373. if ( shown ) then
  374. button.SpellHighlightTexture:Show();
  375. button.SpellHighlightAnim:Play();
  376. else
  377. button.SpellHighlightTexture:Hide();
  378. button.SpellHighlightAnim:Stop();
  379. end
  380. end
  381. function ActionBarActionButtonMixin:UpdateSpellHighlightMark()
  382. if ( self.SpellHighlightTexture and self.SpellHighlightAnim ) then
  383. SharedActionButton_RefreshSpellHighlight(self, GetOnBarHighlightMark(self.action));
  384. end
  385. end
  386. function ActionBarActionButtonMixin:ShowGrid(reason)
  387. assert(reason);
  388. if ( issecure() ) then
  389. self:SetAttribute("showgrid", bit.bor(self:GetAttribute("showgrid"), reason));
  390. end
  391. if ( self.NormalTexture ) then
  392. self.NormalTexture:SetVertexColor(1.0, 1.0, 1.0, 0.5);
  393. end
  394. if ( self:GetAttribute("showgrid") > 0 and not self:GetAttribute("statehidden") ) then
  395. self:Show();
  396. end
  397. end
  398. function ActionBarActionButtonMixin:HideGrid(reason)
  399. assert(reason);
  400. local showgrid = self:GetAttribute("showgrid");
  401. if ( issecure() ) then
  402. if ( showgrid > 0 ) then
  403. self:SetAttribute("showgrid", bit.band(showgrid, bit.bnot(reason)));
  404. end
  405. end
  406. if ( self:GetAttribute("showgrid") == 0 and not HasAction(self.action) ) then
  407. self:Hide();
  408. end
  409. end
  410. function ActionBarActionButtonMixin:UpdateState()
  411. local action = self.action;
  412. local isChecked = IsCurrentAction(action) or IsAutoRepeatAction(action);
  413. self:SetChecked(isChecked);
  414. end
  415. function ActionBarActionButtonMixin:UpdateUsable()
  416. local icon = self.icon;
  417. local normalTexture = self.NormalTexture;
  418. if ( not normalTexture ) then
  419. return;
  420. end
  421. local isUsable, notEnoughMana = IsUsableAction(self.action);
  422. if ( isUsable ) then
  423. icon:SetVertexColor(1.0, 1.0, 1.0);
  424. normalTexture:SetVertexColor(1.0, 1.0, 1.0);
  425. elseif ( notEnoughMana ) then
  426. icon:SetVertexColor(0.5, 0.5, 1.0);
  427. normalTexture:SetVertexColor(0.5, 0.5, 1.0);
  428. else
  429. icon:SetVertexColor(0.4, 0.4, 0.4);
  430. normalTexture:SetVertexColor(1.0, 1.0, 1.0);
  431. end
  432. local isLevelLinkLocked = C_LevelLink.IsActionLocked(self.action);
  433. if not icon:IsDesaturated() then
  434. icon:SetDesaturated(isLevelLinkLocked);
  435. end
  436. if self.LevelLinkLockIcon then
  437. self.LevelLinkLockIcon:SetShown(isLevelLinkLocked);
  438. end
  439. end
  440. function ActionBarActionButtonMixin:UpdateCount()
  441. local text = self.Count;
  442. local action = self.action;
  443. if ( IsConsumableAction(action) or IsStackableAction(action) or (not IsItemAction(action) and GetActionCount(action) > 0) ) then
  444. local count = GetActionCount(action);
  445. if ( count > (self.maxDisplayCount or 9999 ) ) then
  446. text:SetText("*");
  447. else
  448. text:SetText(count);
  449. end
  450. else
  451. local charges, maxCharges, chargeStart, chargeDuration = GetActionCharges(action);
  452. if (maxCharges > 1) then
  453. text:SetText(charges);
  454. else
  455. text:SetText("");
  456. end
  457. end
  458. end
  459. -- Shared between action bar buttons and spell flyout buttons.
  460. function ActionButton_UpdateCooldown(self)
  461. local locStart, locDuration;
  462. local start, duration, enable, charges, maxCharges, chargeStart, chargeDuration;
  463. local modRate = 1.0;
  464. local chargeModRate = 1.0;
  465. if ( self.spellID ) then
  466. locStart, locDuration = GetSpellLossOfControlCooldown(self.spellID);
  467. start, duration, enable, modRate = GetSpellCooldown(self.spellID);
  468. charges, maxCharges, chargeStart, chargeDuration, chargeModRate = GetSpellCharges(self.spellID);
  469. else
  470. locStart, locDuration = GetActionLossOfControlCooldown(self.action);
  471. start, duration, enable, modRate = GetActionCooldown(self.action);
  472. charges, maxCharges, chargeStart, chargeDuration, chargeModRate = GetActionCharges(self.action);
  473. end
  474. if ( (locStart + locDuration) > (start + duration) ) then
  475. if ( self.cooldown.currentCooldownType ~= COOLDOWN_TYPE_LOSS_OF_CONTROL ) then
  476. self.cooldown:SetEdgeTexture("Interface\\Cooldown\\edge-LoC");
  477. self.cooldown:SetSwipeColor(0.17, 0, 0);
  478. self.cooldown:SetHideCountdownNumbers(true);
  479. self.cooldown.currentCooldownType = COOLDOWN_TYPE_LOSS_OF_CONTROL;
  480. end
  481. CooldownFrame_Set(self.cooldown, locStart, locDuration, true, true, modRate);
  482. ClearChargeCooldown(self);
  483. else
  484. if ( self.cooldown.currentCooldownType ~= COOLDOWN_TYPE_NORMAL ) then
  485. self.cooldown:SetEdgeTexture("Interface\\Cooldown\\edge");
  486. self.cooldown:SetSwipeColor(0, 0, 0);
  487. self.cooldown:SetHideCountdownNumbers(false);
  488. self.cooldown.currentCooldownType = COOLDOWN_TYPE_NORMAL;
  489. end
  490. if( locStart > 0 ) then
  491. self.cooldown:SetScript("OnCooldownDone", ActionButtonCooldown_OnCooldownDone);
  492. end
  493. if ( charges and maxCharges and maxCharges > 1 and charges < maxCharges ) then
  494. StartChargeCooldown(self, chargeStart, chargeDuration, chargeModRate);
  495. else
  496. ClearChargeCooldown(self);
  497. end
  498. CooldownFrame_Set(self.cooldown, start, duration, enable, false, modRate);
  499. end
  500. end
  501. function ActionButtonCooldown_OnCooldownDone(self)
  502. self:SetScript("OnCooldownDone", nil);
  503. ActionButton_UpdateCooldown(self:GetParent());
  504. end
  505. -- Charge Cooldown stuff
  506. local numChargeCooldowns = 0;
  507. local function CreateChargeCooldownFrame(parent)
  508. numChargeCooldowns = numChargeCooldowns + 1;
  509. cooldown = CreateFrame("Cooldown", "ChargeCooldown"..numChargeCooldowns, parent, "CooldownFrameTemplate");
  510. cooldown:SetHideCountdownNumbers(true);
  511. cooldown:SetDrawSwipe(false);
  512. cooldown:SetAllPoints(parent);
  513. cooldown:SetFrameStrata("TOOLTIP");
  514. return cooldown;
  515. end
  516. function StartChargeCooldown(parent, chargeStart, chargeDuration, chargeModRate)
  517. if chargeStart == 0 then
  518. ClearChargeCooldown(parent);
  519. return;
  520. end
  521. parent.chargeCooldown = parent.chargeCooldown or CreateChargeCooldownFrame(parent);
  522. CooldownFrame_Set(parent.chargeCooldown, chargeStart, chargeDuration, true, true, chargeModRate);
  523. end
  524. function ClearChargeCooldown(parent)
  525. if parent.chargeCooldown then
  526. CooldownFrame_Clear(parent.chargeCooldown);
  527. end
  528. end
  529. --Overlay stuff
  530. local unusedOverlayGlows = {};
  531. local numOverlays = 0;
  532. function ActionButton_GetOverlayGlow()
  533. local overlay = tremove(unusedOverlayGlows);
  534. if ( not overlay ) then
  535. numOverlays = numOverlays + 1;
  536. overlay = CreateFrame("Frame", "ActionButtonOverlay"..numOverlays, UIParent, "ActionBarButtonSpellActivationAlert");
  537. end
  538. return overlay;
  539. end
  540. function ActionBarActionButtonMixin:UpdateOverlayGlow()
  541. local spellType, id, subType = GetActionInfo(self.action);
  542. if ( spellType == "spell" and IsSpellOverlayed(id) ) then
  543. ActionButton_ShowOverlayGlow(self);
  544. elseif ( spellType == "macro" ) then
  545. local spellId = GetMacroSpell(id);
  546. if ( spellId and IsSpellOverlayed(spellId) ) then
  547. ActionButton_ShowOverlayGlow(self);
  548. else
  549. ActionButton_HideOverlayGlow(self);
  550. end
  551. else
  552. ActionButton_HideOverlayGlow(self);
  553. end
  554. end
  555. -- Shared between action button and MainMenuBarMicroButton
  556. function ActionButton_ShowOverlayGlow(button)
  557. if ( button.overlay ) then
  558. if ( button.overlay.animOut:IsPlaying() ) then
  559. button.overlay.animOut:Stop();
  560. button.overlay.animIn:Play();
  561. end
  562. else
  563. button.overlay = ActionButton_GetOverlayGlow();
  564. local frameWidth, frameHeight = button:GetSize();
  565. button.overlay:SetParent(button);
  566. button.overlay:ClearAllPoints();
  567. --Make the height/width available before the next frame:
  568. button.overlay:SetSize(frameWidth * 1.4, frameHeight * 1.4);
  569. button.overlay:SetPoint("TOPLEFT", button, "TOPLEFT", -frameWidth * 0.2, frameHeight * 0.2);
  570. button.overlay:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", frameWidth * 0.2, -frameHeight * 0.2);
  571. button.overlay.animIn:Play();
  572. end
  573. end
  574. -- Shared between action button and MainMenuBarMicroButton
  575. function ActionButton_HideOverlayGlow(button)
  576. if ( button.overlay ) then
  577. if ( button.overlay.animIn:IsPlaying() ) then
  578. button.overlay.animIn:Stop();
  579. end
  580. if ( button:IsVisible() ) then
  581. button.overlay.animOut:Play();
  582. else
  583. button.overlay.animOut:OnFinished(); --We aren't shown anyway, so we'll instantly hide it.
  584. end
  585. end
  586. end
  587. ActionBarOverlayGlowAnimOutMixin = {};
  588. function ActionBarOverlayGlowAnimOutMixin:OnFinished()
  589. local overlay = self:GetParent();
  590. local actionButton = overlay:GetParent();
  591. overlay:Hide();
  592. tinsert(unusedOverlayGlows, overlay);
  593. actionButton.overlay = nil;
  594. end
  595. ActionBarOverlayGlowAnimInMixin = {};
  596. function ActionBarOverlayGlowAnimInMixin:OnPlay()
  597. local frame = self:GetParent();
  598. local frameWidth, frameHeight = frame:GetSize();
  599. frame.spark:SetSize(frameWidth, frameHeight);
  600. frame.spark:SetAlpha(0.3);
  601. frame.innerGlow:SetSize(frameWidth / 2, frameHeight / 2);
  602. frame.innerGlow:SetAlpha(1.0);
  603. frame.innerGlowOver:SetAlpha(1.0);
  604. frame.outerGlow:SetSize(frameWidth * 2, frameHeight * 2);
  605. frame.outerGlow:SetAlpha(1.0);
  606. frame.outerGlowOver:SetAlpha(1.0);
  607. frame.ants:SetSize(frameWidth * 0.85, frameHeight * 0.85)
  608. frame.ants:SetAlpha(0);
  609. frame:Show();
  610. end
  611. function ActionBarOverlayGlowAnimInMixin:OnFinished()
  612. local frame = self:GetParent();
  613. local frameWidth, frameHeight = frame:GetSize();
  614. frame.spark:SetAlpha(0);
  615. frame.innerGlow:SetAlpha(0);
  616. frame.innerGlow:SetSize(frameWidth, frameHeight);
  617. frame.innerGlowOver:SetAlpha(0.0);
  618. frame.outerGlow:SetSize(frameWidth, frameHeight);
  619. frame.outerGlowOver:SetAlpha(0.0);
  620. frame.outerGlowOver:SetSize(frameWidth, frameHeight);
  621. frame.ants:SetAlpha(1.0);
  622. end
  623. ActionBarButtonSpellActivationAlertMixin = {};
  624. function ActionBarButtonSpellActivationAlertMixin:OnUpdate(elapsed)
  625. AnimateTexCoords(self.ants, 256, 256, 48, 48, 22, elapsed, 0.01);
  626. local cooldown = self:GetParent().cooldown;
  627. -- we need some threshold to avoid dimming the glow during the gdc
  628. -- (using 1500 exactly seems risky, what if casting speed is slowed or something?)
  629. if(cooldown and cooldown:IsShown() and cooldown:GetCooldownDuration() > 3000) then
  630. self:SetAlpha(0.5);
  631. else
  632. self:SetAlpha(1.0);
  633. end
  634. end
  635. function ActionBarButtonSpellActivationAlertMixin:OnHide()
  636. if ( self.animOut:IsPlaying() ) then
  637. self.animOut:Stop();
  638. self.animOut:OnFinished();
  639. end
  640. end
  641. function ActionBarActionButtonMixin:OnEvent(event, ...)
  642. local arg1 = ...;
  643. if ((event == "UNIT_INVENTORY_CHANGED" and arg1 == "player") or event == "LEARNED_SPELL_IN_TAB") then
  644. if ( GameTooltip:GetOwner() == self ) then
  645. self:SetTooltip();
  646. end
  647. elseif ( event == "ACTIONBAR_SLOT_CHANGED" ) then
  648. if ( arg1 == 0 or arg1 == tonumber(self.action) ) then
  649. ClearNewActionHighlight(self.action, true);
  650. self:Update();
  651. end
  652. elseif ( event == "PLAYER_ENTERING_WORLD" ) then
  653. self:Update();
  654. elseif ( event == "UPDATE_SHAPESHIFT_FORM" ) then
  655. -- need to listen for UPDATE_SHAPESHIFT_FORM because attack icons change when the shapeshift form changes
  656. -- This is NOT intended to update everything about shapeshifting; most stuff should be handled by ActionBar-specific events such as UPDATE_BONUS_ACTIONBAR, UPDATE_USABLE, etc.
  657. local texture = GetActionTexture(self.action);
  658. if (texture) then
  659. self.icon:SetTexture(texture);
  660. end
  661. elseif ( event == "ACTIONBAR_SHOWGRID" ) then
  662. self:ShowGrid(ACTION_BUTTON_SHOW_GRID_REASON_EVENT);
  663. elseif ( event == "ACTIONBAR_HIDEGRID" ) then
  664. if ( not KeybindFrames_InQuickKeybindMode() ) then
  665. self:HideGrid(ACTION_BUTTON_SHOW_GRID_REASON_EVENT);
  666. end
  667. elseif ( event == "UPDATE_BINDINGS" ) then
  668. self:UpdateHotkeys(self.buttonType);
  669. elseif ( event == "PLAYER_TARGET_CHANGED" ) then -- All event handlers below this line are only set when the button has an action
  670. self.rangeTimer = -1;
  671. elseif ( event == "UNIT_FLAGS" or event == "UNIT_AURA" or event == "PET_BAR_UPDATE" ) then
  672. -- Pet actions can also change the state of action buttons.
  673. self:UpdateState();
  674. self:UpdateFlash();
  675. elseif ( (event == "ACTIONBAR_UPDATE_STATE") or
  676. ((event == "UNIT_ENTERED_VEHICLE" or event == "UNIT_EXITED_VEHICLE") and (arg1 == "player")) or
  677. ((event == "COMPANION_UPDATE") and (arg1 == "MOUNT")) ) then
  678. self:UpdateState();
  679. elseif ( event == "ACTIONBAR_UPDATE_USABLE" or event == "PLAYER_MOUNT_DISPLAY_CHANGED" ) then
  680. self:UpdateUsable();
  681. elseif ( event == "LOSS_OF_CONTROL_UPDATE" ) then
  682. ActionButton_UpdateCooldown(self);
  683. elseif ( event == "ACTIONBAR_UPDATE_COOLDOWN" or event == "LOSS_OF_CONTROL_ADDED" ) then
  684. ActionButton_UpdateCooldown(self);
  685. -- Update tooltip
  686. if ( GameTooltip:GetOwner() == self ) then
  687. self:SetTooltip();
  688. end
  689. elseif ( event == "TRADE_SKILL_SHOW" or event == "TRADE_SKILL_CLOSE" or event == "ARCHAEOLOGY_CLOSED" ) then
  690. self:UpdateState();
  691. elseif ( event == "PLAYER_ENTER_COMBAT" ) then
  692. if ( IsAttackAction(self.action) ) then
  693. self:StartFlash();
  694. end
  695. elseif ( event == "PLAYER_LEAVE_COMBAT" ) then
  696. if ( IsAttackAction(self.action) ) then
  697. self:StopFlash();
  698. end
  699. elseif ( event == "START_AUTOREPEAT_SPELL" ) then
  700. if ( IsAutoRepeatAction(self.action) ) then
  701. self:StartFlash();
  702. end
  703. elseif ( event == "STOP_AUTOREPEAT_SPELL" ) then
  704. if ( self:IsFlashing() and not IsAttackAction(self.action) ) then
  705. self:StopFlash();
  706. end
  707. elseif ( event == "PET_STABLE_UPDATE" or event == "PET_STABLE_SHOW") then
  708. -- Has to update everything for now, but this event should happen infrequently
  709. self:Update();
  710. elseif ( event == "SPELL_ACTIVATION_OVERLAY_GLOW_SHOW" ) then
  711. local actionType, id, subType = GetActionInfo(self.action);
  712. if ( actionType == "spell" and id == arg1 ) then
  713. ActionButton_ShowOverlayGlow(self);
  714. elseif ( actionType == "macro" ) then
  715. local spellId = GetMacroSpell(id);
  716. if ( spellId and spellId == arg1 ) then
  717. ActionButton_ShowOverlayGlow(self);
  718. end
  719. elseif (actionType == "flyout" and FlyoutHasSpell(id, arg1)) then
  720. ActionButton_ShowOverlayGlow(self);
  721. end
  722. elseif ( event == "SPELL_ACTIVATION_OVERLAY_GLOW_HIDE" ) then
  723. local actionType, id, subType = GetActionInfo(self.action);
  724. if ( actionType == "spell" and id == arg1 ) then
  725. ActionButton_HideOverlayGlow(self);
  726. elseif ( actionType == "macro" ) then
  727. local spellId = GetMacroSpell(id);
  728. if (spellId and spellId == arg1 ) then
  729. ActionButton_HideOverlayGlow(self);
  730. end
  731. elseif (actionType == "flyout" and FlyoutHasSpell(id, arg1)) then
  732. ActionButton_HideOverlayGlow(self);
  733. end
  734. elseif ( event == "SPELL_UPDATE_CHARGES" ) then
  735. self:UpdateCount();
  736. elseif ( event == "UPDATE_SUMMONPETS_ACTION" ) then
  737. local actionType, id = GetActionInfo(self.action);
  738. if (actionType == "summonpet") then
  739. local texture = GetActionTexture(self.action);
  740. if (texture) then
  741. self.icon:SetTexture(texture);
  742. end
  743. end
  744. elseif ( event == "SPELL_UPDATE_ICON" ) then
  745. self:Update();
  746. end
  747. end
  748. function ActionBarActionButtonMixin:SetTooltip()
  749. local inQuickKeybind = KeybindFrames_InQuickKeybindMode();
  750. if ( GetCVar("UberTooltips") == "1" or inQuickKeybind ) then
  751. GameTooltip_SetDefaultAnchor(GameTooltip, self);
  752. else
  753. local parent = self:GetParent();
  754. if ( parent == MultiBarBottomRight or parent == MultiBarRight or parent == MultiBarLeft ) then
  755. GameTooltip:SetOwner(self, "ANCHOR_LEFT");
  756. else
  757. GameTooltip:SetOwner(self, "ANCHOR_RIGHT");
  758. end
  759. end
  760. if ( GameTooltip:SetAction(self.action) ) then
  761. self.UpdateTooltip = self.SetTooltip;
  762. else
  763. self.UpdateTooltip = nil;
  764. end
  765. end
  766. function ActionBarActionButtonMixin:OnUpdate(elapsed)
  767. if ( self:IsFlashing() ) then
  768. local flashtime = self.flashtime;
  769. flashtime = flashtime - elapsed;
  770. if ( flashtime <= 0 ) then
  771. local overtime = -flashtime;
  772. if ( overtime >= ATTACK_BUTTON_FLASH_TIME ) then
  773. overtime = 0;
  774. end
  775. flashtime = ATTACK_BUTTON_FLASH_TIME - overtime;
  776. local flashTexture = self.Flash;
  777. if ( flashTexture:IsShown() ) then
  778. flashTexture:Hide();
  779. else
  780. flashTexture:Show();
  781. end
  782. end
  783. self.flashtime = flashtime;
  784. end
  785. -- Handle range indicator
  786. local rangeTimer = self.rangeTimer;
  787. if ( rangeTimer ) then
  788. rangeTimer = rangeTimer - elapsed;
  789. if ( rangeTimer <= 0 ) then
  790. local valid = IsActionInRange(self.action);
  791. local checksRange = (valid ~= nil);
  792. local inRange = checksRange and valid;
  793. ActionButton_UpdateRangeIndicator(self, checksRange, inRange);
  794. rangeTimer = TOOLTIP_UPDATE_TIME;
  795. end
  796. self.rangeTimer = rangeTimer;
  797. end
  798. end
  799. -- Shared between the action bar and the pet bar.
  800. function ActionButton_UpdateRangeIndicator(self, checksRange, inRange)
  801. if ( self.HotKey:GetText() == RANGE_INDICATOR ) then
  802. if ( checksRange ) then
  803. self.HotKey:Show();
  804. if ( inRange ) then
  805. self.HotKey:SetVertexColor(LIGHTGRAY_FONT_COLOR:GetRGB());
  806. else
  807. self.HotKey:SetVertexColor(RED_FONT_COLOR:GetRGB());
  808. end
  809. else
  810. self.HotKey:Hide();
  811. end
  812. else
  813. if ( checksRange and not inRange ) then
  814. self.HotKey:SetVertexColor(RED_FONT_COLOR:GetRGB());
  815. else
  816. self.HotKey:SetVertexColor(LIGHTGRAY_FONT_COLOR:GetRGB());
  817. end
  818. end
  819. end
  820. function ActionBarActionButtonMixin:GetPagedID()
  821. return self.action;
  822. end
  823. function ActionBarActionButtonMixin:UpdateFlash()
  824. local action = self.action;
  825. if ( (IsAttackAction(action) and IsCurrentAction(action)) or IsAutoRepeatAction(action) ) then
  826. self:StartFlash();
  827. local actionType, actionID, actionSubtype = GetActionInfo(action);
  828. if ( actionSubtype == "pet" ) then
  829. self:GetCheckedTexture():SetAlpha(0.5);
  830. else
  831. self:GetCheckedTexture():SetAlpha(1.0);
  832. end
  833. else
  834. self:StopFlash();
  835. end
  836. if ( self.AutoCastable ) then
  837. self.AutoCastable:SetShown(C_ActionBar.IsAutoCastPetAction(action));
  838. if ( C_ActionBar.IsEnabledAutoCastPetAction(action) ) then
  839. self.AutoCastShine:Show();
  840. AutoCastShine_AutoCastStart(self.AutoCastShine);
  841. else
  842. self.AutoCastShine:Hide();
  843. AutoCastShine_AutoCastStop(self.AutoCastShine);
  844. end
  845. end
  846. end
  847. function ActionBarActionButtonMixin:ClearFlash()
  848. if ( self.AutoCastable ) then
  849. self.AutoCastable:Hide();
  850. self.AutoCastShine:Hide();
  851. AutoCastShine_AutoCastStop(self.AutoCastShine);
  852. end
  853. end
  854. function ActionBarActionButtonMixin:StartFlash()
  855. self.flashing = 1;
  856. self.flashtime = 0;
  857. self:UpdateState();
  858. end
  859. function ActionBarActionButtonMixin:StopFlash()
  860. self.flashing = 0;
  861. self.Flash:Hide();
  862. self:UpdateState();
  863. end
  864. function ActionBarActionButtonMixin:IsFlashing()
  865. if ( self.flashing == 1 ) then
  866. return 1;
  867. end
  868. return nil;
  869. end
  870. -- Shared between action bar buttons and spell flyout buttons.
  871. function ActionButton_UpdateFlyout(self)
  872. if not self.FlyoutArrow then
  873. return;
  874. end
  875. local actionType = GetActionInfo(self.action);
  876. if (actionType == "flyout") then
  877. -- Update border and determine arrow position
  878. local arrowDistance;
  879. if ((SpellFlyout and SpellFlyout:IsShown() and SpellFlyout:GetParent() == self) or GetMouseFocus() == self) then
  880. self.FlyoutBorder:Show();
  881. self.FlyoutBorderShadow:Show();
  882. arrowDistance = 5;
  883. else
  884. self.FlyoutBorder:Hide();
  885. self.FlyoutBorderShadow:Hide();
  886. arrowDistance = 2;
  887. end
  888. -- Update arrow
  889. self.FlyoutArrow:Show();
  890. self.FlyoutArrow:ClearAllPoints();
  891. local direction = self:GetAttribute("flyoutDirection");
  892. if (direction == "LEFT") then
  893. self.FlyoutArrow:SetPoint("LEFT", self, "LEFT", -arrowDistance, 0);
  894. SetClampedTextureRotation(self.FlyoutArrow, 270);
  895. elseif (direction == "RIGHT") then
  896. self.FlyoutArrow:SetPoint("RIGHT", self, "RIGHT", arrowDistance, 0);
  897. SetClampedTextureRotation(self.FlyoutArrow, 90);
  898. elseif (direction == "DOWN") then
  899. self.FlyoutArrow:SetPoint("BOTTOM", self, "BOTTOM", 0, -arrowDistance);
  900. SetClampedTextureRotation(self.FlyoutArrow, 180);
  901. else
  902. self.FlyoutArrow:SetPoint("TOP", self, "TOP", 0, arrowDistance);
  903. SetClampedTextureRotation(self.FlyoutArrow, 0);
  904. end
  905. else
  906. self.FlyoutBorder:Hide();
  907. self.FlyoutBorderShadow:Hide();
  908. self.FlyoutArrow:Hide();
  909. end
  910. end
  911. function ActionBarActionButtonMixin:OnClick(button, down)
  912. if ( KeybindFrames_InQuickKeybindMode() ) then
  913. local cursorType = GetCursorInfo();
  914. if ( cursorType ) then
  915. local slotID = self:CalculateAction(button);
  916. C_ActionBar.PutActionInSlot(slotID);
  917. end
  918. else
  919. if button == "RightButton" and C_ActionBar.IsAutoCastPetAction(self.action) then
  920. C_ActionBar.ToggleAutoCastPetAction(self.action);
  921. elseif (not self.zoneAbilityDisabled) then
  922. SecureActionButton_OnClick(self, button);
  923. end
  924. end
  925. end
  926. function ActionBarActionButtonMixin:OnDragStart()
  927. if ( LOCK_ACTIONBAR ~= "1" or IsModifiedClick("PICKUPACTION") ) then
  928. SpellFlyout:Hide();
  929. PickupAction(self.action);
  930. self:UpdateState();
  931. self:UpdateFlash();
  932. end
  933. end
  934. function ActionBarActionButtonMixin:OnReceiveDrag()
  935. PlaceAction(self.action);
  936. self:UpdateState();
  937. self:UpdateFlash();
  938. end
  939. function ActionBarActionButtonMixin:OnEnter()
  940. if (self.NewActionTexture) then
  941. ClearNewActionHighlight(self.action);
  942. self:UpdateAction(true);
  943. end
  944. self:SetTooltip();
  945. ActionBarButtonEventsFrame.tooltipOwner = self;
  946. ActionBarActionEventsFrame.tooltipOwner = self;
  947. ActionButton_UpdateFlyout(self);
  948. end
  949. function ActionBarActionButtonMixin:OnLeave()
  950. GameTooltip:Hide();
  951. ActionBarButtonEventsFrame.tooltipOwner = nil;
  952. ActionBarActionEventsFrame.tooltipOwner = nil;
  953. ActionButton_UpdateFlyout(self);
  954. end