Mercurial > wow > reaction
diff 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 |
line wrap: on
line diff
--- a/modules/Action.lua Wed Mar 04 21:19:32 2009 +0000 +++ b/modules/Action.lua Thu Mar 05 01:28:48 2009 +0000 @@ -1,14 +1,3 @@ ---[[ - ReAction Action button module. - - The button module implements standard action button functionality by wrapping Blizzard's - ActionBarButtonTemplate frame and associated functions. - - It also provides action remapping support for multiple pages and possessed targets - (Mind Control, Eyes of the Beast, Karazhan Chess event, various quests, etc). ---]] - --- local imports local ReAction = ReAction local L = ReAction.L local _G = _G @@ -22,14 +11,13 @@ -- libraries local KB = LibStub("LibKeyBound-1.0") -local LBF -- initialized later -- module declaration local moduleID = "Action" local module = ReAction:NewModule( moduleID ) -- Class declarations -local Button = { } +local Button = ReAction.Button.Action -- see /classes/ActionButton.lua local Handle = { } local PropHandler = { } @@ -53,8 +41,6 @@ ReAction.RegisterCallback(self, "OnRenameBar") ReAction.RegisterCallback(self, "OnConfigModeChanged") - LBF = LibStub("LibButtonFacade",true) - KB.RegisterCallback(self, "LIBKEYBOUND_ENABLED") KB.RegisterCallback(self, "LIBKEYBOUND_DISABLED") KB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED") @@ -121,14 +107,12 @@ function module:LIBKEYBOUND_ENABLED(evt) for _, h in pairs(self.handles) do - h:ShowGrid(true) h:SetKeybindMode(true) end end function module:LIBKEYBOUND_DISABLED(evt) for _, h in pairs(self.handles) do - h:ShowGrid(false) h:SetKeybindMode(false) end end @@ -160,7 +144,6 @@ desc = L["Prevents picking up/dragging actions.|nNOTE: This setting is overridden by the global setting in Blizzard's Action Buttons tab"], order = 2, type = "toggle", - disabled = "LockButtonsDisabled", get = "GetLockButtons", set = "SetLockButtons", }, @@ -307,7 +290,9 @@ btnCfg[i] = {} end if self.btns[i] == nil then - local b = Button:New(self, i, btnCfg[i], self.config) + local lastButton = self:GetLastButton() + local hint = lastButton and lastButton.config.actionID + local b = Button:New(i, self.config, self.bar, hint) self.btns[i] = b self.bar:AddButton(i,b) end @@ -321,31 +306,10 @@ end end end - local f = self.bar:GetFrame() for _, b in ipairs(self.btns) do b:Refresh() end - f:SetAttribute("mindcontrol",self.config.mindcontrol) - f:SetAttribute("vehicle",self.config.vehicle) - f:Execute( - [[ - doMindControl = self:GetAttribute("mindcontrol") - doVehicle = self:GetAttribute("vehicle") - control:ChildUpdate() - ]]) - - f:SetAttribute("_onstate-mc", - -- function _onstate-mc(self, stateid, newstate) - [[ - local oldMcVehicleState = mcVehicleState - mcVehicleState = newstate - control:ChildUpdate() - if oldMcVehicleState == "vehicle" or mcVehicleState == "vehicle" then - control:ChildUpdate("vehicle") - end - ]]) - RegisterStateDriver(f, "mc", "[target=vehicle,exists] vehicle; [bonusbar:5] mc; none") - + Button.SetupBarHeader(self.bar,self.config) self:UpdateButtonLock() end @@ -359,26 +323,12 @@ function Handle:SetConfigMode(mode) for _, b in pairs(self.btns) do - b:ShowGrid(mode) - b:ShowActionIDLabel(mode) - end - end - - function Handle:ShowGrid(show) - for _, b in pairs(self.btns) do - b:ShowGrid(show) + b:UpdateActionIDLabel(mode) end end function Handle:UpdateButtonLock() - local f = self.bar:GetFrame() - f:SetAttribute("lockbuttons",self.config.lockButtons) - f:SetAttribute("lockbuttonscombat",self.config.lockButtonsCombat) - f:Execute( - [[ - lockButtons = self:GetAttribute("lockbuttons") - lockButtonsCombat = self:GetAttribute("lockbuttonscombat") - ]]) + Button.SetButtonLock(self.bar, self.config.lockButtons, self.config.lockButtonsCombat) end function Handle:SetKeybindMode(mode) @@ -404,7 +354,9 @@ function Handle:SetHideEmpty(info, value) if value ~= self.config.hideEmpty then self.config.hideEmpty = value - self:ShowGrid(not value) + for _, b in pairs(self.btns) do + b:ShowGrid(not value) + end end end @@ -413,7 +365,7 @@ end function Handle:GetLockButtons() - return LOCK_ACTIONBAR == "1" or self.config.lockButtons + return self.config.lockButtons end function Handle:SetLockButtons(info, value) @@ -421,10 +373,6 @@ self:UpdateButtonLock() end - function Handle:LockButtonsDisabled() - return LOCK_ACTIONBAR == "1" - end - function Handle:GetLockButtonsCombat() return self.config.lockButtonsCombat end @@ -435,7 +383,7 @@ end function Handle:LockButtonsCombatDisabled() - return LOCK_ACTIONBAR == "1" or not self.config.lockButtons + return not self.config.lockButtons end function Handle:GetNumPages() @@ -705,495 +653,3 @@ end ------- ActionID allocation ------ --- this needs to be high performance when requesting new IDs, --- or certain controls will become sluggish. However, the new-request --- infrastructure can be built lazily the first time that a new request --- comes in (which will only happen at user config time: at static startup --- config time all actionIDs should already have been assigned and stored --- in the config file) - -local IDAlloc -do - local n = 120 - - IDAlloc = setmetatable({ wrap = 1, freecount = n }, {__index = function() return 0 end}) - - function IDAlloc:Acquire(id, hint) - id = tonumber(id) - hint = tonumber(hint) - if id and (id < 1 or id > n) then - id = nil - end - if hint and (hint < 1 or hint > n) then - hint = nil - end - if id == nil then - -- get a free ID - if hint and self[hint] == 0 then - -- use the hint if it's free - id = hint - elseif self.freecount > 0 then - -- if neither the id nor the hint are defined or free, but - -- the list is known to have free IDs, then start searching - -- at the hint for a free one - for i = hint or 1, n do - if self[i] == 0 then - id = i - break - end - end - -- self.wrap the search - if id == nil and hint and hint > 1 then - for i = 1, hint - 1 do - if self[i] == 0 then - id = i - break - end - end - end - end - if id == nil then - -- if there are no free IDs, start wrapping at 1 - id = self.wrap - self.wrap = id + 1 - if self.wrap > n then - self.wrap = 1 - end - end - end - if self[id] == 0 then - self.freecount = self.freecount - 1 - end - self[id] = self[id] + 1 - return id - end - - function IDAlloc:Release(id) - id = tonumber(id) - if id and (id >= 1 or id <= n) then - self[id] = self[id] - 1 - if self[id] == 0 then - self.freecount = self.freecount + 1 - self.wrap = 1 - end - end - end -end - ------- Button class ------ -local frameRecycler = { } -local trash = CreateFrame("Frame") -local OnUpdate, GetActionName, GetHotkey -do - local ATTACK_BUTTON_FLASH_TIME = ATTACK_BUTTON_FLASH_TIME - local IsActionInRange = IsActionInRange - - function OnUpdate(frame, elapsed) - -- note: This function taints frame.flashtime and frame.rangeTimer. Both of these - -- are only read by ActionButton_OnUpdate (which this function replaces). In - -- all other places they're just written, so it doesn't taint any secure code. - if frame.flashing == 1 then - frame.flashtime = frame.flashtime - elapsed - if frame.flashtime <= 0 then - local overtime = -frame.flashtime - if overtime >= ATTACK_BUTTON_FLASH_TIME then - overtime = 0 - end - frame.flashtime = ATTACK_BUTTON_FLASH_TIME - overtime - - local flashTexture = frame.flash - if flashTexture:IsShown() then - flashTexture:Hide() - else - flashTexture:Show() - end - end - end - - if frame.rangeTimer then - frame.rangeTimer = frame.rangeTimer - elapsed; - - if frame.rangeTimer <= 0 then - if IsActionInRange(frame.action) == 0 then - frame.icon:SetVertexColor(1.0,0.1,0.1) - else - ActionButton_UpdateUsable(frame) - end - frame.rangeTimer = 0.1 - end - end - end - - function GetActionName(f) - local b = f and f._reactionButton - if b then - return format("%s:%s", b.bar:GetName(), b.idx) - end - end - - function GetHotkey(f) - return KB:ToShortKey(GetBindingKey(format("CLICK %s:LeftButton",f:GetName()))) - end - - -- This is a bit hokey : install a bare hook on ActionButton_UpdateHotkey because - -- even though it's secure it's never called in a way that can cause taint. This is - -- for performance reasons to avoid having to hook frame:OnEvent securely. - local UpdateHotkey_old = ActionButton_UpdateHotkeys - ActionButton_UpdateHotkeys = function( frame, ... ) - local b = frame._reactionButton - if b then - b.hotkey:SetText( GetHotkey(frame) ) - else - return UpdateHotkey_old(frame, ...) - end - end -end - -local meta = {__index = Button} - -function Button:New( handle, idx, config, barConfig ) - local bar = handle.bar - - -- create new self - self = setmetatable( - { - bar = bar, - idx = idx, - config = config, - barConfig = barConfig, - }, meta ) - - local name = config.name or ("ReAction_%s_%s_%d"):format(bar:GetName(),moduleID,idx) - self.name = name - config.name = name - local lastButton = handle:GetLastButton() - config.actionID = IDAlloc:Acquire(config.actionID, lastButton and lastButton.config.actionID) -- gets a free one if none configured - self.nPages = 1 - - -- have to recycle frames with the same name: CreateFrame() doesn't overwrite - -- existing globals. Can't set to nil in the global because it's then tainted. - local parent = bar:GetFrame() - local f = frameRecycler[name] - if f then - f:SetParent(parent) - else - f = CreateFrame("CheckButton", name, parent, "ActionBarButtonTemplate") - -- ditch the old hotkey text because it's tied in ActionButton_Update() to the - -- standard binding. - local hotkey = _G[name.."HotKey"] - hotkey:SetParent(trash) - hotkey = f:CreateFontString(nil, "ARTWORK", "NumberFontNormalSmallGray") - hotkey:SetWidth(36) - hotkey:SetHeight(18) - hotkey:SetJustifyH("RIGHT") - hotkey:SetJustifyV("TOP") - hotkey:SetPoint("TOPLEFT",f,"TOPLEFT",-2,-2) - f.hotkey = hotkey - f.icon = _G[name.."Icon"] - f.flash = _G[name.."Flash"] - f:SetScript("OnUpdate",OnUpdate) - end - - f._reactionButton = self - - self.hotkey = f.hotkey - self.border = _G[name.."Border"] - - f:SetAttribute("action", config.actionID) - f:SetAttribute("default-action", config.actionID) - -- install mind control actions for all buttons just for simplicity - if self.idx <= 12 then - f:SetAttribute("mc-action", 120 + self.idx) - end - - -- set a tooltip onEnter - f:SetScript("OnEnter", - function(frame) - if ReAction:GetKeybindMode() then - KB:Set(frame) - elseif frame.vehicleExitMode then - GameTooltip_AddNewbieTip(frame, LEAVE_VEHICLE, 1.0, 1.0, 1.0, nil); - else - ActionButton_SetTooltip(frame) - end - end) - - -- set a _childupdate handler, called within the header's context - f:SetAttribute("_childupdate", - -- function _childupdate(self, snippetid, message) - [[ - local action = "default-action" - if (doVehicle and mcVehicleState == "vehicle") or - (doMindControl and mcVehicleState == "mc") then - action = "mc-action" - elseif page and state and page[state] then - action = "action-"..page[state] - end - - local value = self:GetAttribute(action) - if value then - self:SetAttribute("action",value) - end - ]]) - - -- Install a handler for the 7th button (only) to show/hide a - -- vehicle exit button. This is more than a little bit hack-ish and - -- will be replaced in the next iteration with the reimplementation - -- of action button functionality. - if idx == 7 then - local barFrame = bar:GetFrame() - function barFrame:ShowVehicleExit(show) - local tx = f.vehicleExitTexture - if show then - if not tx then - tx = f:CreateTexture(nil,"ARTWORK") - tx:SetAllPoints() - -- copied from Blizzard/VehicleMenuBar.lua SkinsData - tx:SetTexture("Interface\\Vehicles\\UI-Vehicles-Button-Exit-Up") - tx:SetTexCoord(0.140625, 0.859375, 0.140625, 0.859375) - f.vehicleExitTexture = tx - end - tx:Show() - f.vehicleExitMode = true - elseif tx then - tx:SetTexCoord(0,1,0,1) - tx:Hide() - f.vehicleExitMode = false - end - end - - f:SetAttribute("macrotext","/run VehicleExit()") - f:SetAttribute("_childupdate-vehicle", - -- function _childupdate-vehicle(self, snippetid, message) - [[ - local show = (mcVehicleState == "vehicle") - if show then - self:SetAttribute("type","macro") - self:SetAttribute("showgrid",self:GetAttribute("showgrid")+1) - self:Show() - else - self:SetAttribute("type","action") - local showgrid = self:GetAttribute("showgrid") - showgrid = showgrid - 1 - if showgrid < 0 then showgrid = 0 end - self:SetAttribute("showgrid",self:GetAttribute("showgrid")-1) - if showgrid <= 0 then - self:Hide() - end - end - control:CallMethod("ShowVehicleExit",show) - ]]) - end - - -- install drag wrappers to lock buttons - bar:GetFrame():WrapScript(f, "OnDragStart", - -- OnDragStart(self, button, kind, value, ...) - [[ - if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then - return "clear" - end - ]]) - - self.frame = f - - -- initialize the hide state - f:SetAttribute("showgrid",0) - self:ShowGrid(not barConfig.hideEmpty) - if ReAction:GetConfigMode() then - self:ShowGrid(true) - end - - -- set the hotkey text - self.hotkey:SetText( GetHotkey(self.frame) ) - - -- show the ID label if applicable - self:ShowActionIDLabel(ReAction:GetConfigMode()) - - -- attach to skinner - bar:SkinButton(self, - { - HotKey = self.hotkey, - } - ) - - self:Refresh() - return self -end - -function Button:Destroy() - local f = self.frame - f:UnregisterAllEvents() - f:Hide() - f:SetParent(UIParent) - f:ClearAllPoints() - f:SetAttribute("_childupdate",nil) - f:SetAttribute("_childupdate-vehicle",nil) - if self.name then - frameRecycler[self.name] = f - end - if self.config.actionID then - IDAlloc:Release(self.config.actionID) - end - if self.config.pageactions then - for _, id in ipairs(self.config.pageactions) do - IDAlloc:Release(id) - end - end - f._reactionButton = nil - self.frame = nil - self.config = nil - self.bar = nil -end - -function Button:Refresh() - local f = self.frame - self.bar:PlaceButton(self, 36, 36) - self:RefreshPages() -end - -function Button:GetFrame() - return self.frame -end - -function Button:GetName() - return self.name -end - -function Button:GetConfig() - return self.config -end - -function Button:GetActionID(page) - if page == nil then - -- get the effective ID - return self.frame.action -- kept up-to-date by Blizzard's ActionButton_CalculateAction() - else - if page == 1 then - return self.config.actionID - else - return self.config.pageactions and self.config.pageactions[page] or self.config.actionID - end - end -end - -function Button:SetActionID( id, page ) - id = tonumber(id) - page = tonumber(page) - if id == nil or id < 1 or id > 120 then - error("Button:SetActionID - invalid action ID") - end - if page and page ~= 1 then - if not self.config.pageactions then - self.config.pageactions = { } - end - if self.config.pageactions[page] then - IDAlloc:Release(self.config.pageactions[page]) - end - self.config.pageactions[page] = id - IDAlloc:Acquire(self.config.pageactions[page]) - self.frame:SetAttribute(("action-page%d"):format(page),id) - else - IDAlloc:Release(self.config.actionID) - self.config.actionID = id - IDAlloc:Acquire(self.config.actionID) - self.frame:SetAttribute("action",id) - if self.config.pageactions then - self.config.pageactions[1] = id - self.frame:SetAttribute("action-page1",id) - end - end -end - -function Button:RefreshPages( force ) - local nPages = self.barConfig.nPages - if nPages and (nPages ~= self.nPages or force) then - local f = self:GetFrame() - local c = self.config.pageactions - if nPages > 1 and not c then - c = { } - self.config.pageactions = c - end - for i = 1, nPages do - if i > 1 then - c[i] = IDAlloc:Acquire(c[i], self.config.actionID + (i-1)*self.bar:GetNumButtons()) - else - c[i] = self.config.actionID -- page 1 is the same as the base actionID - end - f:SetAttribute(("action-page%d"):format(i),c[i]) - end - for i = nPages+1, #c do - IDAlloc:Release(c[i]) - c[i] = nil - f:SetAttribute(("action-page%d"):format(i),nil) - end - self.nPages = nPages - end -end - -function Button:ShowGrid( show ) - if not InCombatLockdown() then - local f = self.frame - local count = f:GetAttribute("showgrid") - if show then - count = count + 1 - else - count = count - 1 - end - if count < 0 then - count = 0 - end - f:SetAttribute("showgrid",count) - - if count >= 1 and not f:GetAttribute("statehidden") then - if LBF then - LBF:SetNormalVertexColor(self.frame, 1.0, 1.0, 1.0, 0.5) - else - self.frame:GetNormalTexture():SetVertexColor(1.0, 1.0, 1.0, 0.5); - end - f:Show() - elseif count < 1 and not HasAction(self:GetActionID()) then - f:Hide() - end - end -end - -function Button:ShowActionIDLabel( show ) - local f = self:GetFrame() - if show then - local id = self:GetActionID() - if not f.actionIDLabel then - local label = f:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") - label:SetAllPoints() - label:SetJustifyH("CENTER") - label:SetShadowColor(0,0,0,1) - label:SetShadowOffset(2,-2) - f.actionIDLabel = label -- store the label with the frame for recycling - - f:HookScript("OnAttributeChanged", - function(frame, attr, value) - if label:IsVisible() and attr:match("action") then - label:SetText(tostring(frame.action)) - end - end) - end - f.actionIDLabel:SetText(tostring(id)) - f.actionIDLabel:Show() - elseif f.actionIDLabel then - f.actionIDLabel:Hide() - end -end - -function Button:SetKeybindMode( mode ) - if mode then - self.frame.GetActionName = GetActionName - self.frame.GetHotkey = GetHotkey - -- set the border for all buttons to the keybind-enable color - self.border:SetVertexColor(KB:GetColorKeyBoundMode()) - self.border:Show() - elseif IsEquippedAction(self:GetActionID()) then - self.border:SetVertexColor(0, 1.0, 0, 0.35) -- from ActionButton.lua - else - self.border:Hide() - end -end \ No newline at end of file