Mercurial > wow > reaction
diff ActionButton.lua @ 257:920d17851a93 stable
Merge 1.1 beta 4 to stable
author | Flick |
---|---|
date | Tue, 12 Apr 2011 16:06:31 -0700 |
parents | 65f2805957a0 |
children | c918ff9ac787 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ActionButton.lua Tue Apr 12 16:06:31 2011 -0700 @@ -0,0 +1,806 @@ +local addonName, addonTable = ... +local ReAction = addonTable.ReAction +local L = ReAction.L +local _G = _G +local CreateFrame = CreateFrame +local format = string.format +local IsUsableAction = IsUsableAction +local IsEquippedAction = IsEquippedAction +local IsConsumableAction = IsConsumableAction +local IsStackableAction = IsStackableAction +local GetActionText = GetActionText +local GetCVar = GetCVar +local GameTooltip_SetDefaultAnchor = GameTooltip_SetDefaultAnchor +local IsCurrentAction = IsCurrentAction +local IsAutoRepeatAction = IsAutoRepeatAction +local IsUsableAction = IsUsableAction +local IsAttackAction = IsAttackAction +local CooldownFrame_SetTimer = CooldownFrame_SetTimer +local GetActionCooldown = GetActionCooldown +local GetActionTexture = GetActionTexture +local ATTACK_BUTTON_FLASH_TIME = ATTACK_BUTTON_FLASH_TIME +local TOOLTIP_UPDATE_TIME = TOOLTIP_UPDATE_TIME +local IsActionInRange = IsActionInRange +local InCombatLockdown = InCombatLockdown +local HasAction = HasAction + +-- +-- Secure snippets +-- These are run within the context of the bar's sandbox, as the +-- buttons themselves do not have their own sandbox. +-- +local _onstate_mc = -- function(self, stateid, newstate) +[[ + local oldMcVehicleState = mcVehicleState + mcVehicleState = newstate + control:ChildUpdate() + if oldMcVehicleState == "vehicle" or mcVehicleState == "vehicle" then + control:ChildUpdate("vehicle") + end +]] + +local _childupdate = -- function(self, snippetid, message) +[[ + local action = nil + if (doVehicle and mcVehicleState == "vehicle") or + (doMindControl and mcVehicleState == "mc") then + local idx = self:GetAttribute("bar-idx") + local maxN = (doVehicle and mcVehicleState == "vehicle") and 7 or 12 + if idx and idx <= maxN then + action = 120 + idx + else + action = 0 + end + elseif state and settings[state] and settings[state].page then + action = self:GetAttribute("action-"..settings[state].page) + end + if action == nil then + action = self:GetAttribute("default-action") + end + + self:SetAttribute("action",action) + + if not self:GetAttribute("showgrid") then + local tempShow = self:GetAttribute("showgrid-temp") + local evtShow = self:GetAttribute("showgrid-event") + if tempShow then tempShow = (tempShow > 0) end + if evtShow then evtShow = (evtShow > 0) end + + if tempShow or evtShow or HasAction(action) then + self:Show() + else + self:Hide() + end + end +]] + +local _childupdate_vehicleExit = -- function(self, snippetid, message) +[[ + local show = (mcVehicleState == "vehicle") + if show and doVehicle then + self:SetAttribute("type","macro") + self:Show() + else + self:SetAttribute("type","action") + end + control:CallMethod("ShowVehicleExit",show) +]] + +local _childupdate_showgrid = -- function(self, snippetid, message) +[[ + showgrid_event = message + self:SetAttribute("showgrid-event",message) + if not self:GetAttribute("showgrid") then + local count = message + (self:GetAttribute("showgrid-temp") or 0) + if count <= 0 then + local action = self:GetAttribute("action") + -- note that HasAction is not updated on the target of a drag until after ACTIONBAR_HIDEGRID + -- so, we set a flag for this button in pre-click/receive-drag and check for it here + if HasAction(action) or self:GetAttribute("showgrid-clicked") then + if not self:IsShown() then + self:Show() + end + self:SetAttribute("showgrid-clicked",false) + else + if self:IsShown() then + self:Hide() + end + end + else + self:Show() + end + end +]] + +local _onDragStart = -- function(self, button, kind, value, ...) +[[ + if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then + return kind, value, ... + else + return "action", self:GetAttribute("action") + end +]] + +local _onReceiveDrag = -- function(self, button, kind, value, ...) +[[ + if kind ~= "spell" and kind ~= "item" and kind ~= "macro" and kind ~= "flyout" then + return kind, value, ... + else + if showgrid_event and showgrid_event > 0 then + self:SetAttribute("showgrid-clicked",true) + end + return "action", self:GetAttribute("action") + end +]] + +local _onClick = -- function(self, button, down) +[[ + if showgrid_event and showgrid_event > 0 then + self:SetAttribute("showgrid-clicked",true) + end + return button +]] + +-- +-- private +-- +local eventList = { + "PLAYER_ENTERING_WORLD", + "ACTIONBAR_PAGE_CHANGED", + "ACTIONBAR_SLOT_CHANGED", + "UPDATE_BINDINGS", + "ACTIONBAR_UPDATE_STATE", + "ACTIONBAR_UPDATE_USABLE", + "ACTIONBAR_UPDATE_COOLDOWN", + "UNIT_INVENTORY_CHANGED", + "LEARNED_SPELL_IN_TAB", + "UPDATE_INVENTORY_ALERTS", + "PLAYER_TARGET_CHANGED", + "TRADE_SKILL_SHOW", + "TRADE_SKILL_CLOSE", + "PLAYER_ENTER_COMBAT", + "PLAYER_LEAVE_COMBAT", + "START_AUTOREPEAT_SPELL", + "STOP_AUTOREPEAT_SPELL", + "UNIT_ENTERED_VEHICLE", + "UNIT_EXITED_VEHICLE", + "COMPANION_UPDATE", +} + +-- +-- Action Button class +-- +local buttonTypeID = "Action" +local Super = ReAction.Button +local Action = setmetatable( + { + defaultBarConfig = { + type = buttonTypeID, + btnWidth = 36, + btnHeight = 36, + btnRows = 1, + btnColumns = 12, + spacing = 3, + buttons = { }, + }, + + barType = L["Action Bar"], + buttonTypeID = buttonTypeID + }, + { __index = Super } ) + +ReAction.Button.Action = Action +ReAction:RegisterBarType(Action, true) + +function Action:New( config, bar, idx, idHint ) + local name = format("ReAction_%s_Action_%d",bar:GetName(),idx) + + self = Super.New(self, name, config, bar, idx, "SecureActionButtonTemplate, ActionButtonTemplate" ) + self.barConfig = bar:GetConfig() + + local f = self:GetFrame() + local barFrame = bar:GetFrame() + + self.rangeTimer = TOOLTIP_UPDATE_TIME + + -- set up the base action ID + self:SetActionIDPool("action",120) + config.actionID = self:AcquireActionID(config.actionID, idHint) + self.nPages = 1 + + -- attribute setup + f:SetAttribute("type","action") + f:SetAttribute("checkselfcast", true) + f:SetAttribute("checkfocuscast", true) + f:SetAttribute("action", config.actionID) + f:SetAttribute("default-action", config.actionID) + f:SetAttribute("bar-idx",idx) + f:SetAttribute("showgrid-temp",0) + f:SetAttribute("showgrid-event",0) + f:SetAttribute("showgrid",not self:GetBarConfig().hideEmpty) + + -- non secure scripts + f:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end) + f:SetScript("OnEnter", function(frame) self:OnEnter() end) + f:SetScript("OnLeave", function(frame) self:OnLeave() end) + f:SetScript("OnAttributeChanged", function(frame, attr, value) self:OnAttributeChanged(attr, value) end) + f:SetScript("PostClick", function(frame, ...) self:PostClick(...) end) + f:SetScript("OnUpdate", function(frame, elapsed) self:OnUpdate(elapsed) end) + f:SetScript("OnDragStart", function(frame) self:OnDragStart() end) + f:SetScript("OnReceiveDrag", function(frame) self:OnReceiveDrag() end) + + -- event registration + f:EnableMouse(true) + f:RegisterForDrag("LeftButton", "RightButton") + f:RegisterForClicks("AnyUp") + for _, evt in pairs(eventList) do + f:RegisterEvent(evt) + end + + f.action = config.actionID -- need this to support silly ActionButton_UpdateFlyout. Should not taint anything anymore. + + -- secure handlers + f:SetAttribute("_childupdate", _childupdate) + f:SetAttribute("_childupdate-showgrid",_childupdate_showgrid) + barFrame:WrapScript(f, "OnDragStart", _onDragStart) + barFrame:WrapScript(f, "OnReceiveDrag", _onReceiveDrag) + barFrame:WrapScript(f, "OnClick", _onClick) + + -- attach to skinner + bar:SkinButton(self) + + -- initial display + if ReAction:GetConfigMode() then + self:ShowGridTemp(true) + end + + self:Refresh() + + return self +end + +function Action:Destroy() + local f = self:GetFrame() + local c = self:GetConfig() + + f:SetAttribute("_childupdate-vehicle",nil) + f:SetAttribute("action",c.actionID) -- so that Super.Destroy releases the right one + + if c.pageactions and #c.pageactions > 1 then + for i = 2, #c.pageactions do + self:ReleaseActionID(c.pageactions[i]) + self:ReleaseActionID(id) + end + end + + Super.Destroy(self) +end + +function Action:GetBarConfig() + -- this is the per-bar Action module config structure, + -- not the config structure of the bar itself + return self.barConfig +end + +function Action:Refresh() + Super.Refresh(self) + self:RefreshPages() + self:InstallVehicle() + self:ShowGrid(not self:GetBarConfig().hideEmpty) + self:UpdateAction() +end + +function Action:InstallVehicle() + local f = self:GetFrame() + if self.idx == 7 and self:GetBarConfig().vehicle then + if not self.vehicleInstalled then + self.vehicleInstalled = true + -- install vehicle-exit button on 7th button (only) + f:SetAttribute("_childupdate-vehicle", _childupdate_vehicleExit) + f:SetAttribute("macrotext","/run VehicleExit()") + self:GetBar():GetFrame().ShowVehicleExit = function(bar,show) + self:ShowVehicleExit(show) + end + end + -- setscale blows away tex coords + self:UpdateIcon() + elseif self.vehicleInstalled then + self.vehicleInstalled = false + f:SetAttribute("_childupdate-vehicle",nil) + f:SetAttribute("macrotext",nil) + end +end + +function Action:ShowGrid( show ) + if not InCombatLockdown() then + self.frame:SetAttribute("showgrid", show) + self:UpdateShowGrid() + end +end + +function Action:ShowGridTemp( show ) + -- This function only modifies the show-grid when out + -- of combat, and is ignored by the secure handler. Use + -- it for configuration modes. + if not InCombatLockdown() then + local count = self.showGridTempCount or 0 + if show then + count = count + 1 + else + count = count - 1 + end + if count < 0 then count = 0 end + self.showGridTempCount = count + self:GetFrame():SetAttribute("showgrid-temp",count) + self:UpdateShowGrid() + end +end + +function Action:UpdateAll() + self:UpdateActionIDLabel(ReAction:GetConfigMode()) + self:UpdateHotkey() + self:UpdateShowGrid() + self:UpdateIcon() + self:UpdateBorder() + self:UpdateMacroText() + self:UpdateCount() + self:UpdateTooltip() + self:UpdateCheckedState() + self:UpdateUsable() + self:UpdateCooldown() + self:UpdateFlash() +end + +function Action:UpdateAction() + local action = self:GetActionID() + if action ~= self.actionID then + self.actionID = action + self:UpdateAll() + end +end + +function Action:UpdateShowGrid() + if not InCombatLockdown() then + local f = self:GetFrame() + local count = (f:GetAttribute("showgrid-event") or 0) + + (self.showGridTempCount or 0) + + (f:GetAttribute("showgrid") and 1 or 0) + + if count <= 0 and not HasAction(self.actionID) then + if f:IsShown() then + f:Hide() + end + elseif not f:IsShown() then + f:Show() + end + end +end + +function Action:UpdateIcon() + local texture, tLeft, tRight, tTop, tBottom = self:GetIconTexture() + local icon = self.frames.icon + local hotkey = self.frames.hotkey + local f = self:GetFrame() + + if self.vehicleExitMode then + texture = "Interface\\Vehicles\\UI-Vehicles-Button-Exit-Up" + icon:SetTexCoord(0.140625, 0.859375, 0.140625, 0.859375) + icon:SetVertexColor(1,1,1) + else + icon:SetTexCoord(0,1,0,1) + end + + if texture then + icon:SetTexture(texture) + if tLeft then + icon:SetTexCoord(tLeft,tRight,tTop,tBottom) + end + icon:Show() + self.rangeTimer = -1 + f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2") + else + icon:Hide() + self.frames.cooldown:Hide() + self.rangeTimer = nil + f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot") + end +end + +function Action:GetIconTexture() + return GetActionTexture(self.actionID) +end + +function Action:UpdateBorder() + local action = self.actionID + if ReAction:GetKeybindMode() then + self:UpdateKeybindModeDisplay(true) + elseif IsEquippedAction(action) then + self.frames.border:SetVertexColor(0, 1.0, 0, 0.35) + self.frames.border:Show() + else + self.frames.border:Hide() + end +end + +function Action:UpdateMacroText() + local action = self.actionID + if not IsConsumableAction(action) and not IsStackableAction(action) then + self.frames.name:SetText(GetActionText(action)) + else + self.frames.name:SetText("") + end +end + +function Action:UpdateCount() + local action = self.actionID + if IsConsumableAction(action) or IsStackableAction(action) then + self.frames.count:SetText(GetActionCount(action)) + else + self.frames.count:SetText("") + end +end + +function Action:UpdateTooltip() + local f = self:GetFrame() + if GameTooltip:GetOwner() == f then + self:SetTooltip() + end +end + +function Action:SetTooltip() + local f = self:GetFrame() + if GetCVar("UberTooltips") == "1" then + GameTooltip_SetDefaultAnchor(GameTooltip, f) + else + GameTooltip:SetOwner(f,"ANCHOR_RIGHT") + end + GameTooltip:SetAction(self.actionID) +end + +function Action:UpdateCheckedState() + local action = self.actionID + if IsCurrentAction(action) or IsAutoRepeatAction(action) then + self:GetFrame():SetChecked(1) + else + self:GetFrame():SetChecked(0) + end +end + +function Action:UpdateUsable() + local isUsable, notEnoughMana = self:GetUsable() + local noRange = self:GetInRange() + + isUsable = self.vehicleExitMode or (isUsable and not noRange) + + if isUsable then + if self.usableStatus ~= "usable" then + self.frames.icon:SetVertexColor(1.0, 1.0, 1.0) + self.frames.normalTexture:SetVertexColor(1.0, 1.0, 1.0) + self.usableStatus = "usable" + end + elseif noRange then + if self.usableStatus ~= "norange" then + self.frames.icon:SetVertexColor(1.0,0.1,0.1) + self.frames.normalTexture:SetVertexColor(1.0, 1.0, 1.0) + self.usableStatus = "norange" + end + elseif notEnoughMana then + if self.usableStatus ~= "oom" then + self.frames.icon:SetVertexColor(0.5, 0.5, 1.0) + self.frames.normalTexture:SetVertexColor(0.5, 0.5, 1.0) + self.usableStatus = "oom" + end + else + if self.usableStatus ~= "unusable" then + self.frames.icon:SetVertexColor(0.4, 0.4, 0.4) + self.frames.normalTexture:SetVertexColor(1.0, 1.0, 1.0) + self.usableStatus = "unusable" + end + end +end + +function Action:GetUsable() + return IsUsableAction(self.actionID) +end + +function Action:GetInRange() + return IsActionInRange(self.actionID) == 0 +end + +function Action:UpdateCooldown() + CooldownFrame_SetTimer(self.frames.cooldown, self:GetCooldown()) +end + +function Action:GetCooldown() + return GetActionCooldown(self.actionID) +end + +function Action:UpdateFlash() + local action = self.actionID + self:SetFlash( (IsAttackAction(action) and IsCurrentAction(action)) or IsAutoRepeatAction(action) ) +end + +function Action:SetFlash(flash) + if self.flashing ~= flash then + self.flashing = flash + self.flashtime = 0 + if not flash then + self.frames.flash:Hide() + end + self:UpdateCheckedState() + end +end + +function Action:RunFlash(elapsed) + if self.flashing then + local flashtime = self.flashtime - elapsed + self.flashtime = flashtime + if flashtime <= 0 then + local overtime = -flashtime + if overtime >= ATTACK_BUTTON_FLASH_TIME then + overtime = 0 + end + flashtime = ATTACK_BUTTON_FLASH_TIME - overtime + local flash = self.frames.flash + if flash:IsShown() then + flash:Hide() + else + flash:Show() + end + end + end +end + +function Action:RunRangeFinder(elapsed) + local rangeTimer = self.rangeTimer + if rangeTimer then + rangeTimer = rangeTimer - elapsed + self.rangeTimer = rangeTimer + if rangeTimer <= 0 then + self:UpdateUsable() + self.rangeTimer = TOOLTIP_UPDATE_TIME + end + end +end + +function Action:GetActionID(page) + if page == nil then + -- get the effective ID + return self:GetFrame():GetAttribute("action") + 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 Action:SetActionID( id, page ) + id = tonumber(id) + page = tonumber(page) + if id == nil or id < 1 or id > 120 then + ReAction:UserError(L["Action ID range is 1-120"]) + return + end + if page and page ~= 1 then + if not self.config.pageactions then + self.config.pageactions = { } + end + self:ReleaseActionID(self.config.pageactions[page]) + self.config.pageactions[page] = id + self:AcquireActionID(self.config.pageactions[page]) + self.frame:SetAttribute("action-page"..page,id) + else + self:ReleaseActionID(self.config.actionID) + self.config.actionID = id + self:AcquireActionID(self.config.actionID) + self.frame:SetAttribute("action",id) + self.frame:SetAttribute("default-action",id) + if self.config.pageactions then + self.config.pageactions[1] = id + self.frame:SetAttribute("action-page1",id) + end + end +end + +function Action:RefreshPages( force ) + local nPages = self:GetBarConfig().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] = self:AcquireActionID(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"..i,c[i]) + end + for i = nPages+1, #c do + self:ReleaseActionID(c[i]) + c[i] = nil + f:SetAttribute("action-page"..i,nil) + end + self.nPages = nPages + end +end + +function Action:SetupBar( bar ) + Super.SetupBar(self,bar) + + local f = bar:GetFrame() + local config = bar:GetConfig() + f:SetAttribute("mindcontrol",config.mindcontrol) + f:SetAttribute("vehicle",config.vehicle) + f:Execute( + [[ + doMindControl = self:GetAttribute("mindcontrol") + doVehicle = self:GetAttribute("vehicle") + control:ChildUpdate() + ]]) + + f:SetAttribute("_onstate-mc", _onstate_mc) + RegisterStateDriver(f, "mc", "[vehicleui] vehicle; [bonusbar:5] mc; none") + + f:SetAttribute("lockbuttons",config.lockButtons) + f:SetAttribute("lockbuttonscombat",config.lockButtonsCombat) + f:Execute( + [[ + lockButtons = self:GetAttribute("lockbuttons") + lockButtonsCombat = self:GetAttribute("lockbuttonscombat") + ]]) +end + + +function Action:SetButtonLock( bar, lock, lockCombat ) + local f = bar:GetFrame() + f:SetAttribute("lockbuttons",lock) + f:SetAttribute("lockbuttonscombat",lockCombat) + f:Execute( + [[ + lockButtons = self:GetAttribute("lockbuttons") + lockButtonsCombat = self:GetAttribute("lockbuttonscombat") + ]]) +end + + +function Action:ShowVehicleExit(show) + self.vehicleExitMode = show and self:GetBarConfig().vehicle + self:UpdateIcon() +end + +function Action:OnEnter( ) + self:SetTooltip() +end + +function Action:OnLeave( ) + GameTooltip:Hide() +end + +function Action:OnAttributeChanged( attr, value ) + if attr ~= "statehidden" then + self:UpdateAction() + end + local f = self:GetFrame() + f.action = f:GetAttribute("action") -- support ActionButton_UpdateFlyout +end + +function Action:PostClick( ) + self:UpdateCheckedState() +end + +function Action:OnUpdate( elapsed ) + self:RunFlash(elapsed) + self:RunRangeFinder(elapsed) +end + +function Action:OnDragStart() + self:UpdateCheckedState() + self:UpdateFlash() +end + +function Action:OnReceiveDrag() + self:UpdateCheckedState() + self:UpdateFlash() +end + +function Action:OnEvent(event, ...) + if self[event] then + self[event](self, event, ...) + end +end + +function Action:ACTIONBAR_SLOT_CHANGED(event, action) + if action == 0 or action == self.actionID then + self:UpdateAll() + end +end + +function Action:PLAYER_ENTERING_WORLD() + self:UpdateAction() +end + +function Action:ACTIONBAR_PAGE_CHANGED() + self:UpdateAction() +end + +function Action:UPDATE_BONUS_ACTIONBAR() + self:UpdateAction() +end + +function Action:UPDATE_BINDINGS() + self:UpdateHotkey() +end + +function Action:PLAYER_TARGET_CHANGED() + self.rangeTimer = -1 +end + +function Action:ACTIONBAR_UPDATE_STATE() + self:UpdateCheckedState() +end + +function Action:TRADE_SKILL_SHOW() + self:UpdateCheckedState() +end +Action.TRADE_SKILL_CLOSE = Action.TRADE_SKILL_CLOSE + +function Action:UNIT_ENTERED_VEHICLE(event,unit) + if unit == "player" then + self:UpdateCheckedState() + end +end +Action.UNIT_EXITED_VEHICLE = Action.UNIT_ENTERED_VEHICLE + +function Action:COMPANION_UPDATE(event,unit) + if unit == "mount" then + self:UpdateCheckedState() + end +end + +function Action:ACTIONBAR_UPDATE_USABLE() + self:UpdateUsable() +end + +function Action:ACTIONBAR_UPDATE_COOLDOWN() + self:UpdateCooldown() +end + +function Action:PLAYER_ENTER_COMBAT() + if IsAttackAction(self.actionID) then + self:SetFlash(true) + end +end + +function Action:PLAYER_LEAVE_COMBAT() + if IsAttackAction(self.actionID) then + self:SetFlash(false) + end +end + +function Action:START_AUTOREPEAT_SPELL() + if IsAutoRepeatAction(self.actionID) then + self:SetFlash(true) + end +end + +function Action:STOP_AUTOREPEAT_SPELL() + if not IsAttackAction(self.actionID) then + self:SetFlash(false) + end +end + +function Action:UNIT_INVENTORY_CHANGED(unit) + if unit == "player" then + self:UpdateTooltip() + end +end + +function Action:LEARNED_SPELL_IN_TAB() + self:UpdateTooltip() +end