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 |
