Mercurial > wow > reaction
diff modules/Action.lua @ 116:fb48811a8736
Convert to standard keybindings
author | Flick <flickerstreak@gmail.com> |
---|---|
date | Fri, 23 Jan 2009 23:44:55 +0000 |
parents | 77bb68eb402b |
children | fb6c3a642ae3 |
line wrap: on
line diff
--- a/modules/Action.lua Fri Jan 23 23:40:13 2009 +0000 +++ b/modules/Action.lua Fri Jan 23 23:44:55 2009 +0000 @@ -18,6 +18,8 @@ ReAction:UpdateRevision("$Revision$") +local weak = { __mode="k" } + -- libraries local KB = LibStub("LibKeyBound-1.0") local LBF -- initialized later @@ -267,7 +269,6 @@ }, } - local weak = { __mode="k" } local meta = { __index = Handle } function Handle:New( bar, config ) @@ -365,15 +366,7 @@ function Handle:SetKeybindMode(mode) for _, b in pairs(self.btns) do - if mode then - -- set the border for all buttons to the keybind-enable color - b.border:SetVertexColor(KB:GetColorKeyBoundMode()) - b.border:Show() - elseif IsEquippedAction(b:GetActionID()) then - b.border:SetVertexColor(0, 1.0, 0, 0.35) -- from ActionButton.lua - else - b.border:Hide() - end + b:SetKeybindMode(mode) end end @@ -763,398 +756,368 @@ end ------ Button class ------ +local frameRecycler = { } +local trash = CreateFrame("Frame") +local OnUpdate, KBAttach, GetHotkey +do + local ATTACK_BUTTON_FLASH_TIME = ATTACK_BUTTON_FLASH_TIME + local IsActionInRange = IsActionInRange -do - local frameRecycler = { } - local trash = CreateFrame("Frame") - local OnUpdate, KBAttach, GetActionName, GetHotkey, SetKey, FreeKey, ClearBindings, GetBindings - 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 buttonLookup = setmetatable({},{__mode="kv"}) - - 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 + 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; - -- Use KeyBound-1.0 for binding, but use Override bindings instead of - -- regular bindings to support multiple profile use. This is a little - -- weird with the KeyBound dialog box (which has per-char selector as well - -- as an OK/Cancel box) but it's the least amount of effort to implement. - function GetActionName(f) - local b = buttonLookup[f] - if b then - return format("%s:%s", b.bar:GetName(), b.idx) - end - end - - function GetHotkey(f) - local b = buttonLookup[f] - if b then - return KB:ToShortKey(b:GetConfig().hotkey) - end - end - - function SetKey(f, key) - local b = buttonLookup[f] - if b then - local c = b:GetConfig() - if c.hotkey then - SetOverrideBinding(f, false, c.hotkey, nil) + 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 - if key then - SetOverrideBindingClick(f, false, key, f:GetName(), nil) - end - c.hotkey = key - b:DisplayHotkey(GetHotkey(f)) - end - end - - function FreeKey(f, key) - local b = buttonLookup[f] - if b then - local c = b:GetConfig() - if c.hotkey == key then - local action = f:GetActionName() - SetOverrideBinding(f, false, c.hotkey, nil) - c.hotkey = nil - b:DisplayHotkey(nil) - return action - end - end - return ReAction:FreeOverrideHotkey(key) - end - - function ClearBindings(f) - SetKey(f, nil) - end - - function GetBindings(f) - local b = buttonLookup[f] - if b then - return b:GetConfig().hotkey - end - end - - function KBAttach( button ) - local f = button:GetFrame() - f.GetActionName = GetActionName - f.GetHotkey = GetHotkey - f.SetKey = SetKey - f.FreeKey = FreeKey - f.ClearBindings = ClearBindings - f.GetBindings = GetBindings - buttonLookup[f] = button - f:SetKey(button:GetConfig().hotkey) - ReAction:RegisterKeybindFrame(f) - if ReAction:GetKeybindMode() then - button.border:SetVertexColor(KB:GetColorKeyBoundMode()) - button.border:Show() + frame.rangeTimer = 0.1 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. We use override bindings. - 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 - - 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("mindcontrol-action", 120 + self.idx) - end - - -- set a _childupdate handler, called within the header's context - f:SetAttribute("_childupdate", - -- function _childupdate(self, snippetid, message) - [[ - local action = "default-action" - if doMindControl and GetBonusBarOffset() == 5 then - action = "mindcontrol-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 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 - - -- show the ID label if applicable - self:ShowActionIDLabel(ReAction:GetConfigMode()) - - -- attach the keybinder - KBAttach(self) - - -- 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() - 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 - 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 + local function GetActionName(f) + local b = f and f._reactionButton + if b then + return format("%s:%s", b.bar:GetName(), b.idx) 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 + function GetHotkey(f) + return KB:ToShortKey(GetBindingKey(format("CLICK %s:LeftButton",f:GetName()))) + end + + local function kb_onEnter( self ) + if ReAction:GetKeybindMode() then + KB:Set(self) 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 + function KBAttach( button ) + if not button.kbHooked then + button.kbHooked = true + local f = button:GetFrame() + f:HookScript("OnEnter", kb_onEnter) + f.GetActionName = GetActionName + f.GetHotkey = GetHotkey 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 + -- 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 - 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 +local meta = {__index = Button} - f:HookScript("OnAttributeChanged", - function(frame, attr, value) - if label:IsVisible() and attr:match("action") then - label:SetText(tostring(frame.action)) - end - end) +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("mindcontrol-action", 120 + self.idx) + end + + -- set a _childupdate handler, called within the header's context + f:SetAttribute("_childupdate", + -- function _childupdate(self, snippetid, message) + [[ + local action = "default-action" + if doMindControl and GetBonusBarOffset() == 5 then + action = "mindcontrol-action" + elseif page and state and page[state] then + action = "action-"..page[state] end - f.actionIDLabel:SetText(tostring(id)) - f.actionIDLabel:Show() - elseif f.actionIDLabel then - f.actionIDLabel:Hide() + local value = self:GetAttribute(action) + if value then + self:SetAttribute("action",value) + 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() + 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:DisplayHotkey( key ) - self.hotkey:SetText(key or "") +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 + KBAttach( self ) + -- 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