Mercurial > wow > reaction
comparison modules/Action.lua @ 128:729232aeeb5e
Action Button rewrite. (note: pet actions are probably slightly broken right now, they haven't been updated yet)
author | Flick <flickerstreak@gmail.com> |
---|---|
date | Thu, 05 Mar 2009 01:28:48 +0000 |
parents | fb6c3a642ae3 |
children | 901c91dc1bf2 |
comparison
equal
deleted
inserted
replaced
127:29dacbecdb52 | 128:729232aeeb5e |
---|---|
1 --[[ | |
2 ReAction Action button module. | |
3 | |
4 The button module implements standard action button functionality by wrapping Blizzard's | |
5 ActionBarButtonTemplate frame and associated functions. | |
6 | |
7 It also provides action remapping support for multiple pages and possessed targets | |
8 (Mind Control, Eyes of the Beast, Karazhan Chess event, various quests, etc). | |
9 --]] | |
10 | |
11 -- local imports | |
12 local ReAction = ReAction | 1 local ReAction = ReAction |
13 local L = ReAction.L | 2 local L = ReAction.L |
14 local _G = _G | 3 local _G = _G |
15 local CreateFrame = CreateFrame | 4 local CreateFrame = CreateFrame |
16 local format = string.format | 5 local format = string.format |
20 | 9 |
21 local weak = { __mode="k" } | 10 local weak = { __mode="k" } |
22 | 11 |
23 -- libraries | 12 -- libraries |
24 local KB = LibStub("LibKeyBound-1.0") | 13 local KB = LibStub("LibKeyBound-1.0") |
25 local LBF -- initialized later | |
26 | 14 |
27 -- module declaration | 15 -- module declaration |
28 local moduleID = "Action" | 16 local moduleID = "Action" |
29 local module = ReAction:NewModule( moduleID ) | 17 local module = ReAction:NewModule( moduleID ) |
30 | 18 |
31 -- Class declarations | 19 -- Class declarations |
32 local Button = { } | 20 local Button = ReAction.Button.Action -- see /classes/ActionButton.lua |
33 local Handle = { } | 21 local Handle = { } |
34 local PropHandler = { } | 22 local PropHandler = { } |
35 | 23 |
36 -- Event handlers | 24 -- Event handlers |
37 function module:OnInitialize() | 25 function module:OnInitialize() |
51 ReAction.RegisterCallback(self, "OnDestroyBar") | 39 ReAction.RegisterCallback(self, "OnDestroyBar") |
52 ReAction.RegisterCallback(self, "OnEraseBar") | 40 ReAction.RegisterCallback(self, "OnEraseBar") |
53 ReAction.RegisterCallback(self, "OnRenameBar") | 41 ReAction.RegisterCallback(self, "OnRenameBar") |
54 ReAction.RegisterCallback(self, "OnConfigModeChanged") | 42 ReAction.RegisterCallback(self, "OnConfigModeChanged") |
55 | 43 |
56 LBF = LibStub("LibButtonFacade",true) | |
57 | |
58 KB.RegisterCallback(self, "LIBKEYBOUND_ENABLED") | 44 KB.RegisterCallback(self, "LIBKEYBOUND_ENABLED") |
59 KB.RegisterCallback(self, "LIBKEYBOUND_DISABLED") | 45 KB.RegisterCallback(self, "LIBKEYBOUND_DISABLED") |
60 KB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED") | 46 KB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED") |
61 end | 47 end |
62 | 48 |
119 end | 105 end |
120 end | 106 end |
121 | 107 |
122 function module:LIBKEYBOUND_ENABLED(evt) | 108 function module:LIBKEYBOUND_ENABLED(evt) |
123 for _, h in pairs(self.handles) do | 109 for _, h in pairs(self.handles) do |
124 h:ShowGrid(true) | |
125 h:SetKeybindMode(true) | 110 h:SetKeybindMode(true) |
126 end | 111 end |
127 end | 112 end |
128 | 113 |
129 function module:LIBKEYBOUND_DISABLED(evt) | 114 function module:LIBKEYBOUND_DISABLED(evt) |
130 for _, h in pairs(self.handles) do | 115 for _, h in pairs(self.handles) do |
131 h:ShowGrid(false) | |
132 h:SetKeybindMode(false) | 116 h:SetKeybindMode(false) |
133 end | 117 end |
134 end | 118 end |
135 | 119 |
136 | 120 |
158 lockButtons = { | 142 lockButtons = { |
159 name = L["Lock Buttons"], | 143 name = L["Lock Buttons"], |
160 desc = L["Prevents picking up/dragging actions.|nNOTE: This setting is overridden by the global setting in Blizzard's Action Buttons tab"], | 144 desc = L["Prevents picking up/dragging actions.|nNOTE: This setting is overridden by the global setting in Blizzard's Action Buttons tab"], |
161 order = 2, | 145 order = 2, |
162 type = "toggle", | 146 type = "toggle", |
163 disabled = "LockButtonsDisabled", | |
164 get = "GetLockButtons", | 147 get = "GetLockButtons", |
165 set = "SetLockButtons", | 148 set = "SetLockButtons", |
166 }, | 149 }, |
167 lockOnlyCombat = { | 150 lockOnlyCombat = { |
168 name = L["Only in Combat"], | 151 name = L["Only in Combat"], |
305 for i = 1, n do | 288 for i = 1, n do |
306 if btnCfg[i] == nil then | 289 if btnCfg[i] == nil then |
307 btnCfg[i] = {} | 290 btnCfg[i] = {} |
308 end | 291 end |
309 if self.btns[i] == nil then | 292 if self.btns[i] == nil then |
310 local b = Button:New(self, i, btnCfg[i], self.config) | 293 local lastButton = self:GetLastButton() |
294 local hint = lastButton and lastButton.config.actionID | |
295 local b = Button:New(i, self.config, self.bar, hint) | |
311 self.btns[i] = b | 296 self.btns[i] = b |
312 self.bar:AddButton(i,b) | 297 self.bar:AddButton(i,b) |
313 end | 298 end |
314 end | 299 end |
315 for i = n+1, #self.btns do | 300 for i = n+1, #self.btns do |
319 self.btns[i] = nil | 304 self.btns[i] = nil |
320 btnCfg[i] = nil | 305 btnCfg[i] = nil |
321 end | 306 end |
322 end | 307 end |
323 end | 308 end |
324 local f = self.bar:GetFrame() | |
325 for _, b in ipairs(self.btns) do | 309 for _, b in ipairs(self.btns) do |
326 b:Refresh() | 310 b:Refresh() |
327 end | 311 end |
328 f:SetAttribute("mindcontrol",self.config.mindcontrol) | 312 Button.SetupBarHeader(self.bar,self.config) |
329 f:SetAttribute("vehicle",self.config.vehicle) | |
330 f:Execute( | |
331 [[ | |
332 doMindControl = self:GetAttribute("mindcontrol") | |
333 doVehicle = self:GetAttribute("vehicle") | |
334 control:ChildUpdate() | |
335 ]]) | |
336 | |
337 f:SetAttribute("_onstate-mc", | |
338 -- function _onstate-mc(self, stateid, newstate) | |
339 [[ | |
340 local oldMcVehicleState = mcVehicleState | |
341 mcVehicleState = newstate | |
342 control:ChildUpdate() | |
343 if oldMcVehicleState == "vehicle" or mcVehicleState == "vehicle" then | |
344 control:ChildUpdate("vehicle") | |
345 end | |
346 ]]) | |
347 RegisterStateDriver(f, "mc", "[target=vehicle,exists] vehicle; [bonusbar:5] mc; none") | |
348 | |
349 self:UpdateButtonLock() | 313 self:UpdateButtonLock() |
350 end | 314 end |
351 | 315 |
352 function Handle:Destroy() | 316 function Handle:Destroy() |
353 for _,b in pairs(self.btns) do | 317 for _,b in pairs(self.btns) do |
357 end | 321 end |
358 end | 322 end |
359 | 323 |
360 function Handle:SetConfigMode(mode) | 324 function Handle:SetConfigMode(mode) |
361 for _, b in pairs(self.btns) do | 325 for _, b in pairs(self.btns) do |
362 b:ShowGrid(mode) | 326 b:UpdateActionIDLabel(mode) |
363 b:ShowActionIDLabel(mode) | |
364 end | |
365 end | |
366 | |
367 function Handle:ShowGrid(show) | |
368 for _, b in pairs(self.btns) do | |
369 b:ShowGrid(show) | |
370 end | 327 end |
371 end | 328 end |
372 | 329 |
373 function Handle:UpdateButtonLock() | 330 function Handle:UpdateButtonLock() |
374 local f = self.bar:GetFrame() | 331 Button.SetButtonLock(self.bar, self.config.lockButtons, self.config.lockButtonsCombat) |
375 f:SetAttribute("lockbuttons",self.config.lockButtons) | |
376 f:SetAttribute("lockbuttonscombat",self.config.lockButtonsCombat) | |
377 f:Execute( | |
378 [[ | |
379 lockButtons = self:GetAttribute("lockbuttons") | |
380 lockButtonsCombat = self:GetAttribute("lockbuttonscombat") | |
381 ]]) | |
382 end | 332 end |
383 | 333 |
384 function Handle:SetKeybindMode(mode) | 334 function Handle:SetKeybindMode(mode) |
385 for _, b in pairs(self.btns) do | 335 for _, b in pairs(self.btns) do |
386 b:SetKeybindMode(mode) | 336 b:SetKeybindMode(mode) |
402 end | 352 end |
403 | 353 |
404 function Handle:SetHideEmpty(info, value) | 354 function Handle:SetHideEmpty(info, value) |
405 if value ~= self.config.hideEmpty then | 355 if value ~= self.config.hideEmpty then |
406 self.config.hideEmpty = value | 356 self.config.hideEmpty = value |
407 self:ShowGrid(not value) | 357 for _, b in pairs(self.btns) do |
358 b:ShowGrid(not value) | |
359 end | |
408 end | 360 end |
409 end | 361 end |
410 | 362 |
411 function Handle:GetHideEmpty() | 363 function Handle:GetHideEmpty() |
412 return self.config.hideEmpty | 364 return self.config.hideEmpty |
413 end | 365 end |
414 | 366 |
415 function Handle:GetLockButtons() | 367 function Handle:GetLockButtons() |
416 return LOCK_ACTIONBAR == "1" or self.config.lockButtons | 368 return self.config.lockButtons |
417 end | 369 end |
418 | 370 |
419 function Handle:SetLockButtons(info, value) | 371 function Handle:SetLockButtons(info, value) |
420 self.config.lockButtons = value | 372 self.config.lockButtons = value |
421 self:UpdateButtonLock() | 373 self:UpdateButtonLock() |
422 end | 374 end |
423 | 375 |
424 function Handle:LockButtonsDisabled() | |
425 return LOCK_ACTIONBAR == "1" | |
426 end | |
427 | |
428 function Handle:GetLockButtonsCombat() | 376 function Handle:GetLockButtonsCombat() |
429 return self.config.lockButtonsCombat | 377 return self.config.lockButtonsCombat |
430 end | 378 end |
431 | 379 |
432 function Handle:SetLockButtonsCombat(info, value) | 380 function Handle:SetLockButtonsCombat(info, value) |
433 self.config.lockButtonsCombat = value | 381 self.config.lockButtonsCombat = value |
434 self:UpdateButtonLock() | 382 self:UpdateButtonLock() |
435 end | 383 end |
436 | 384 |
437 function Handle:LockButtonsCombatDisabled() | 385 function Handle:LockButtonsCombatDisabled() |
438 return LOCK_ACTIONBAR == "1" or not self.config.lockButtons | 386 return not self.config.lockButtons |
439 end | 387 end |
440 | 388 |
441 function Handle:GetNumPages() | 389 function Handle:GetNumPages() |
442 return self.config.nPages | 390 return self.config.nPages |
443 end | 391 end |
703 return self:GetProp(info) or 1 | 651 return self:GetProp(info) or 1 |
704 end | 652 end |
705 | 653 |
706 end | 654 end |
707 | 655 |
708 ------ ActionID allocation ------ | |
709 -- this needs to be high performance when requesting new IDs, | |
710 -- or certain controls will become sluggish. However, the new-request | |
711 -- infrastructure can be built lazily the first time that a new request | |
712 -- comes in (which will only happen at user config time: at static startup | |
713 -- config time all actionIDs should already have been assigned and stored | |
714 -- in the config file) | |
715 | |
716 local IDAlloc | |
717 do | |
718 local n = 120 | |
719 | |
720 IDAlloc = setmetatable({ wrap = 1, freecount = n }, {__index = function() return 0 end}) | |
721 | |
722 function IDAlloc:Acquire(id, hint) | |
723 id = tonumber(id) | |
724 hint = tonumber(hint) | |
725 if id and (id < 1 or id > n) then | |
726 id = nil | |
727 end | |
728 if hint and (hint < 1 or hint > n) then | |
729 hint = nil | |
730 end | |
731 if id == nil then | |
732 -- get a free ID | |
733 if hint and self[hint] == 0 then | |
734 -- use the hint if it's free | |
735 id = hint | |
736 elseif self.freecount > 0 then | |
737 -- if neither the id nor the hint are defined or free, but | |
738 -- the list is known to have free IDs, then start searching | |
739 -- at the hint for a free one | |
740 for i = hint or 1, n do | |
741 if self[i] == 0 then | |
742 id = i | |
743 break | |
744 end | |
745 end | |
746 -- self.wrap the search | |
747 if id == nil and hint and hint > 1 then | |
748 for i = 1, hint - 1 do | |
749 if self[i] == 0 then | |
750 id = i | |
751 break | |
752 end | |
753 end | |
754 end | |
755 end | |
756 if id == nil then | |
757 -- if there are no free IDs, start wrapping at 1 | |
758 id = self.wrap | |
759 self.wrap = id + 1 | |
760 if self.wrap > n then | |
761 self.wrap = 1 | |
762 end | |
763 end | |
764 end | |
765 if self[id] == 0 then | |
766 self.freecount = self.freecount - 1 | |
767 end | |
768 self[id] = self[id] + 1 | |
769 return id | |
770 end | |
771 | |
772 function IDAlloc:Release(id) | |
773 id = tonumber(id) | |
774 if id and (id >= 1 or id <= n) then | |
775 self[id] = self[id] - 1 | |
776 if self[id] == 0 then | |
777 self.freecount = self.freecount + 1 | |
778 self.wrap = 1 | |
779 end | |
780 end | |
781 end | |
782 end | |
783 | |
784 ------ Button class ------ | |
785 local frameRecycler = { } | |
786 local trash = CreateFrame("Frame") | |
787 local OnUpdate, GetActionName, GetHotkey | |
788 do | |
789 local ATTACK_BUTTON_FLASH_TIME = ATTACK_BUTTON_FLASH_TIME | |
790 local IsActionInRange = IsActionInRange | |
791 | |
792 function OnUpdate(frame, elapsed) | |
793 -- note: This function taints frame.flashtime and frame.rangeTimer. Both of these | |
794 -- are only read by ActionButton_OnUpdate (which this function replaces). In | |
795 -- all other places they're just written, so it doesn't taint any secure code. | |
796 if frame.flashing == 1 then | |
797 frame.flashtime = frame.flashtime - elapsed | |
798 if frame.flashtime <= 0 then | |
799 local overtime = -frame.flashtime | |
800 if overtime >= ATTACK_BUTTON_FLASH_TIME then | |
801 overtime = 0 | |
802 end | |
803 frame.flashtime = ATTACK_BUTTON_FLASH_TIME - overtime | |
804 | |
805 local flashTexture = frame.flash | |
806 if flashTexture:IsShown() then | |
807 flashTexture:Hide() | |
808 else | |
809 flashTexture:Show() | |
810 end | |
811 end | |
812 end | |
813 | |
814 if frame.rangeTimer then | |
815 frame.rangeTimer = frame.rangeTimer - elapsed; | |
816 | |
817 if frame.rangeTimer <= 0 then | |
818 if IsActionInRange(frame.action) == 0 then | |
819 frame.icon:SetVertexColor(1.0,0.1,0.1) | |
820 else | |
821 ActionButton_UpdateUsable(frame) | |
822 end | |
823 frame.rangeTimer = 0.1 | |
824 end | |
825 end | |
826 end | |
827 | |
828 function GetActionName(f) | |
829 local b = f and f._reactionButton | |
830 if b then | |
831 return format("%s:%s", b.bar:GetName(), b.idx) | |
832 end | |
833 end | |
834 | |
835 function GetHotkey(f) | |
836 return KB:ToShortKey(GetBindingKey(format("CLICK %s:LeftButton",f:GetName()))) | |
837 end | |
838 | |
839 -- This is a bit hokey : install a bare hook on ActionButton_UpdateHotkey because | |
840 -- even though it's secure it's never called in a way that can cause taint. This is | |
841 -- for performance reasons to avoid having to hook frame:OnEvent securely. | |
842 local UpdateHotkey_old = ActionButton_UpdateHotkeys | |
843 ActionButton_UpdateHotkeys = function( frame, ... ) | |
844 local b = frame._reactionButton | |
845 if b then | |
846 b.hotkey:SetText( GetHotkey(frame) ) | |
847 else | |
848 return UpdateHotkey_old(frame, ...) | |
849 end | |
850 end | |
851 end | |
852 | |
853 local meta = {__index = Button} | |
854 | |
855 function Button:New( handle, idx, config, barConfig ) | |
856 local bar = handle.bar | |
857 | |
858 -- create new self | |
859 self = setmetatable( | |
860 { | |
861 bar = bar, | |
862 idx = idx, | |
863 config = config, | |
864 barConfig = barConfig, | |
865 }, meta ) | |
866 | |
867 local name = config.name or ("ReAction_%s_%s_%d"):format(bar:GetName(),moduleID,idx) | |
868 self.name = name | |
869 config.name = name | |
870 local lastButton = handle:GetLastButton() | |
871 config.actionID = IDAlloc:Acquire(config.actionID, lastButton and lastButton.config.actionID) -- gets a free one if none configured | |
872 self.nPages = 1 | |
873 | |
874 -- have to recycle frames with the same name: CreateFrame() doesn't overwrite | |
875 -- existing globals. Can't set to nil in the global because it's then tainted. | |
876 local parent = bar:GetFrame() | |
877 local f = frameRecycler[name] | |
878 if f then | |
879 f:SetParent(parent) | |
880 else | |
881 f = CreateFrame("CheckButton", name, parent, "ActionBarButtonTemplate") | |
882 -- ditch the old hotkey text because it's tied in ActionButton_Update() to the | |
883 -- standard binding. | |
884 local hotkey = _G[name.."HotKey"] | |
885 hotkey:SetParent(trash) | |
886 hotkey = f:CreateFontString(nil, "ARTWORK", "NumberFontNormalSmallGray") | |
887 hotkey:SetWidth(36) | |
888 hotkey:SetHeight(18) | |
889 hotkey:SetJustifyH("RIGHT") | |
890 hotkey:SetJustifyV("TOP") | |
891 hotkey:SetPoint("TOPLEFT",f,"TOPLEFT",-2,-2) | |
892 f.hotkey = hotkey | |
893 f.icon = _G[name.."Icon"] | |
894 f.flash = _G[name.."Flash"] | |
895 f:SetScript("OnUpdate",OnUpdate) | |
896 end | |
897 | |
898 f._reactionButton = self | |
899 | |
900 self.hotkey = f.hotkey | |
901 self.border = _G[name.."Border"] | |
902 | |
903 f:SetAttribute("action", config.actionID) | |
904 f:SetAttribute("default-action", config.actionID) | |
905 -- install mind control actions for all buttons just for simplicity | |
906 if self.idx <= 12 then | |
907 f:SetAttribute("mc-action", 120 + self.idx) | |
908 end | |
909 | |
910 -- set a tooltip onEnter | |
911 f:SetScript("OnEnter", | |
912 function(frame) | |
913 if ReAction:GetKeybindMode() then | |
914 KB:Set(frame) | |
915 elseif frame.vehicleExitMode then | |
916 GameTooltip_AddNewbieTip(frame, LEAVE_VEHICLE, 1.0, 1.0, 1.0, nil); | |
917 else | |
918 ActionButton_SetTooltip(frame) | |
919 end | |
920 end) | |
921 | |
922 -- set a _childupdate handler, called within the header's context | |
923 f:SetAttribute("_childupdate", | |
924 -- function _childupdate(self, snippetid, message) | |
925 [[ | |
926 local action = "default-action" | |
927 if (doVehicle and mcVehicleState == "vehicle") or | |
928 (doMindControl and mcVehicleState == "mc") then | |
929 action = "mc-action" | |
930 elseif page and state and page[state] then | |
931 action = "action-"..page[state] | |
932 end | |
933 | |
934 local value = self:GetAttribute(action) | |
935 if value then | |
936 self:SetAttribute("action",value) | |
937 end | |
938 ]]) | |
939 | |
940 -- Install a handler for the 7th button (only) to show/hide a | |
941 -- vehicle exit button. This is more than a little bit hack-ish and | |
942 -- will be replaced in the next iteration with the reimplementation | |
943 -- of action button functionality. | |
944 if idx == 7 then | |
945 local barFrame = bar:GetFrame() | |
946 function barFrame:ShowVehicleExit(show) | |
947 local tx = f.vehicleExitTexture | |
948 if show then | |
949 if not tx then | |
950 tx = f:CreateTexture(nil,"ARTWORK") | |
951 tx:SetAllPoints() | |
952 -- copied from Blizzard/VehicleMenuBar.lua SkinsData | |
953 tx:SetTexture("Interface\\Vehicles\\UI-Vehicles-Button-Exit-Up") | |
954 tx:SetTexCoord(0.140625, 0.859375, 0.140625, 0.859375) | |
955 f.vehicleExitTexture = tx | |
956 end | |
957 tx:Show() | |
958 f.vehicleExitMode = true | |
959 elseif tx then | |
960 tx:SetTexCoord(0,1,0,1) | |
961 tx:Hide() | |
962 f.vehicleExitMode = false | |
963 end | |
964 end | |
965 | |
966 f:SetAttribute("macrotext","/run VehicleExit()") | |
967 f:SetAttribute("_childupdate-vehicle", | |
968 -- function _childupdate-vehicle(self, snippetid, message) | |
969 [[ | |
970 local show = (mcVehicleState == "vehicle") | |
971 if show then | |
972 self:SetAttribute("type","macro") | |
973 self:SetAttribute("showgrid",self:GetAttribute("showgrid")+1) | |
974 self:Show() | |
975 else | |
976 self:SetAttribute("type","action") | |
977 local showgrid = self:GetAttribute("showgrid") | |
978 showgrid = showgrid - 1 | |
979 if showgrid < 0 then showgrid = 0 end | |
980 self:SetAttribute("showgrid",self:GetAttribute("showgrid")-1) | |
981 if showgrid <= 0 then | |
982 self:Hide() | |
983 end | |
984 end | |
985 control:CallMethod("ShowVehicleExit",show) | |
986 ]]) | |
987 end | |
988 | |
989 -- install drag wrappers to lock buttons | |
990 bar:GetFrame():WrapScript(f, "OnDragStart", | |
991 -- OnDragStart(self, button, kind, value, ...) | |
992 [[ | |
993 if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then | |
994 return "clear" | |
995 end | |
996 ]]) | |
997 | |
998 self.frame = f | |
999 | |
1000 -- initialize the hide state | |
1001 f:SetAttribute("showgrid",0) | |
1002 self:ShowGrid(not barConfig.hideEmpty) | |
1003 if ReAction:GetConfigMode() then | |
1004 self:ShowGrid(true) | |
1005 end | |
1006 | |
1007 -- set the hotkey text | |
1008 self.hotkey:SetText( GetHotkey(self.frame) ) | |
1009 | |
1010 -- show the ID label if applicable | |
1011 self:ShowActionIDLabel(ReAction:GetConfigMode()) | |
1012 | |
1013 -- attach to skinner | |
1014 bar:SkinButton(self, | |
1015 { | |
1016 HotKey = self.hotkey, | |
1017 } | |
1018 ) | |
1019 | |
1020 self:Refresh() | |
1021 return self | |
1022 end | |
1023 | |
1024 function Button:Destroy() | |
1025 local f = self.frame | |
1026 f:UnregisterAllEvents() | |
1027 f:Hide() | |
1028 f:SetParent(UIParent) | |
1029 f:ClearAllPoints() | |
1030 f:SetAttribute("_childupdate",nil) | |
1031 f:SetAttribute("_childupdate-vehicle",nil) | |
1032 if self.name then | |
1033 frameRecycler[self.name] = f | |
1034 end | |
1035 if self.config.actionID then | |
1036 IDAlloc:Release(self.config.actionID) | |
1037 end | |
1038 if self.config.pageactions then | |
1039 for _, id in ipairs(self.config.pageactions) do | |
1040 IDAlloc:Release(id) | |
1041 end | |
1042 end | |
1043 f._reactionButton = nil | |
1044 self.frame = nil | |
1045 self.config = nil | |
1046 self.bar = nil | |
1047 end | |
1048 | |
1049 function Button:Refresh() | |
1050 local f = self.frame | |
1051 self.bar:PlaceButton(self, 36, 36) | |
1052 self:RefreshPages() | |
1053 end | |
1054 | |
1055 function Button:GetFrame() | |
1056 return self.frame | |
1057 end | |
1058 | |
1059 function Button:GetName() | |
1060 return self.name | |
1061 end | |
1062 | |
1063 function Button:GetConfig() | |
1064 return self.config | |
1065 end | |
1066 | |
1067 function Button:GetActionID(page) | |
1068 if page == nil then | |
1069 -- get the effective ID | |
1070 return self.frame.action -- kept up-to-date by Blizzard's ActionButton_CalculateAction() | |
1071 else | |
1072 if page == 1 then | |
1073 return self.config.actionID | |
1074 else | |
1075 return self.config.pageactions and self.config.pageactions[page] or self.config.actionID | |
1076 end | |
1077 end | |
1078 end | |
1079 | |
1080 function Button:SetActionID( id, page ) | |
1081 id = tonumber(id) | |
1082 page = tonumber(page) | |
1083 if id == nil or id < 1 or id > 120 then | |
1084 error("Button:SetActionID - invalid action ID") | |
1085 end | |
1086 if page and page ~= 1 then | |
1087 if not self.config.pageactions then | |
1088 self.config.pageactions = { } | |
1089 end | |
1090 if self.config.pageactions[page] then | |
1091 IDAlloc:Release(self.config.pageactions[page]) | |
1092 end | |
1093 self.config.pageactions[page] = id | |
1094 IDAlloc:Acquire(self.config.pageactions[page]) | |
1095 self.frame:SetAttribute(("action-page%d"):format(page),id) | |
1096 else | |
1097 IDAlloc:Release(self.config.actionID) | |
1098 self.config.actionID = id | |
1099 IDAlloc:Acquire(self.config.actionID) | |
1100 self.frame:SetAttribute("action",id) | |
1101 if self.config.pageactions then | |
1102 self.config.pageactions[1] = id | |
1103 self.frame:SetAttribute("action-page1",id) | |
1104 end | |
1105 end | |
1106 end | |
1107 | |
1108 function Button:RefreshPages( force ) | |
1109 local nPages = self.barConfig.nPages | |
1110 if nPages and (nPages ~= self.nPages or force) then | |
1111 local f = self:GetFrame() | |
1112 local c = self.config.pageactions | |
1113 if nPages > 1 and not c then | |
1114 c = { } | |
1115 self.config.pageactions = c | |
1116 end | |
1117 for i = 1, nPages do | |
1118 if i > 1 then | |
1119 c[i] = IDAlloc:Acquire(c[i], self.config.actionID + (i-1)*self.bar:GetNumButtons()) | |
1120 else | |
1121 c[i] = self.config.actionID -- page 1 is the same as the base actionID | |
1122 end | |
1123 f:SetAttribute(("action-page%d"):format(i),c[i]) | |
1124 end | |
1125 for i = nPages+1, #c do | |
1126 IDAlloc:Release(c[i]) | |
1127 c[i] = nil | |
1128 f:SetAttribute(("action-page%d"):format(i),nil) | |
1129 end | |
1130 self.nPages = nPages | |
1131 end | |
1132 end | |
1133 | |
1134 function Button:ShowGrid( show ) | |
1135 if not InCombatLockdown() then | |
1136 local f = self.frame | |
1137 local count = f:GetAttribute("showgrid") | |
1138 if show then | |
1139 count = count + 1 | |
1140 else | |
1141 count = count - 1 | |
1142 end | |
1143 if count < 0 then | |
1144 count = 0 | |
1145 end | |
1146 f:SetAttribute("showgrid",count) | |
1147 | |
1148 if count >= 1 and not f:GetAttribute("statehidden") then | |
1149 if LBF then | |
1150 LBF:SetNormalVertexColor(self.frame, 1.0, 1.0, 1.0, 0.5) | |
1151 else | |
1152 self.frame:GetNormalTexture():SetVertexColor(1.0, 1.0, 1.0, 0.5); | |
1153 end | |
1154 f:Show() | |
1155 elseif count < 1 and not HasAction(self:GetActionID()) then | |
1156 f:Hide() | |
1157 end | |
1158 end | |
1159 end | |
1160 | |
1161 function Button:ShowActionIDLabel( show ) | |
1162 local f = self:GetFrame() | |
1163 if show then | |
1164 local id = self:GetActionID() | |
1165 if not f.actionIDLabel then | |
1166 local label = f:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") | |
1167 label:SetAllPoints() | |
1168 label:SetJustifyH("CENTER") | |
1169 label:SetShadowColor(0,0,0,1) | |
1170 label:SetShadowOffset(2,-2) | |
1171 f.actionIDLabel = label -- store the label with the frame for recycling | |
1172 | |
1173 f:HookScript("OnAttributeChanged", | |
1174 function(frame, attr, value) | |
1175 if label:IsVisible() and attr:match("action") then | |
1176 label:SetText(tostring(frame.action)) | |
1177 end | |
1178 end) | |
1179 end | |
1180 f.actionIDLabel:SetText(tostring(id)) | |
1181 f.actionIDLabel:Show() | |
1182 elseif f.actionIDLabel then | |
1183 f.actionIDLabel:Hide() | |
1184 end | |
1185 end | |
1186 | |
1187 function Button:SetKeybindMode( mode ) | |
1188 if mode then | |
1189 self.frame.GetActionName = GetActionName | |
1190 self.frame.GetHotkey = GetHotkey | |
1191 -- set the border for all buttons to the keybind-enable color | |
1192 self.border:SetVertexColor(KB:GetColorKeyBoundMode()) | |
1193 self.border:Show() | |
1194 elseif IsEquippedAction(self:GetActionID()) then | |
1195 self.border:SetVertexColor(0, 1.0, 0, 0.35) -- from ActionButton.lua | |
1196 else | |
1197 self.border:Hide() | |
1198 end | |
1199 end |