flickerstreak@24: --[[ flickerstreak@53: ReAction Action button module. flickerstreak@24: flickerstreak@24: The button module implements standard action button functionality by wrapping Blizzard's flickerstreak@87: ActionBarButtonTemplate frame and associated functions. flickerstreak@77: flickerstreak@87: It also provides action remapping support for multiple pages and possessed targets flickerstreak@90: (Mind Control, Eyes of the Beast, Karazhan Chess event, various quests, etc). flickerstreak@24: --]] flickerstreak@24: flickerstreak@24: -- local imports flickerstreak@24: local ReAction = ReAction flickerstreak@24: local L = ReAction.L flickerstreak@24: local _G = _G flickerstreak@24: local CreateFrame = CreateFrame flickerstreak@88: local format = string.format flickerstreak@92: local wipe = wipe flickerstreak@24: flickerstreak@87: ReAction:UpdateRevision("$Revision$") flickerstreak@77: flickerstreak@88: -- libraries flickerstreak@88: local KB = LibStub("LibKeyBound-1.0") flickerstreak@88: flickerstreak@24: -- module declaration flickerstreak@24: local moduleID = "Action" flickerstreak@28: local module = ReAction:NewModule( moduleID ) flickerstreak@24: flickerstreak@90: -- Class declarations flickerstreak@77: local Button = { } flickerstreak@90: local Handle = { } flickerstreak@90: local PropHandler = { } flickerstreak@87: flickerstreak@77: -- Event handlers flickerstreak@24: function module:OnInitialize() flickerstreak@28: self.db = ReAction.db:RegisterNamespace( moduleID, flickerstreak@24: { flickerstreak@28: profile = { flickerstreak@75: bars = { }, flickerstreak@28: } flickerstreak@24: } flickerstreak@24: ) flickerstreak@90: self.handles = setmetatable({ }, weak) flickerstreak@49: flickerstreak@63: ReAction:RegisterBarOptionGenerator(self, "GetBarOptions") flickerstreak@63: flickerstreak@90: ReAction.RegisterCallback(self, "OnCreateBar") flickerstreak@90: ReAction.RegisterCallback(self, "OnRefreshBar") flickerstreak@63: ReAction.RegisterCallback(self, "OnDestroyBar") flickerstreak@63: ReAction.RegisterCallback(self, "OnEraseBar") flickerstreak@63: ReAction.RegisterCallback(self, "OnRenameBar") flickerstreak@63: ReAction.RegisterCallback(self, "OnConfigModeChanged") flickerstreak@63: flickerstreak@88: KB.RegisterCallback(self, "LIBKEYBOUND_ENABLED") flickerstreak@88: KB.RegisterCallback(self, "LIBKEYBOUND_DISABLED") flickerstreak@88: KB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED") flickerstreak@24: end flickerstreak@24: flickerstreak@24: function module:OnEnable() flickerstreak@53: ReAction:RegisterBarType(L["Action Bar"], flickerstreak@53: { flickerstreak@53: type = moduleID, flickerstreak@91: defaultButtonSize = 36, flickerstreak@53: defaultBarRows = 1, flickerstreak@53: defaultBarCols = 12, flickerstreak@53: defaultBarSpacing = 3 flickerstreak@53: }, true) flickerstreak@90: ReAction:GetModule("State"):RegisterStateProperty("page", nil, PropHandler.GetOptions(), PropHandler) flickerstreak@24: end flickerstreak@24: flickerstreak@24: function module:OnDisable() flickerstreak@53: ReAction:UnregisterBarType(L["Action Bar"]) flickerstreak@90: ReAction:GetModule("State"):UnregisterStateProperty("page") flickerstreak@90: end flickerstreak@90: flickerstreak@90: function module:OnCreateBar(event, bar, name) flickerstreak@90: if bar.config.type == moduleID then flickerstreak@90: local profile = self.db.profile flickerstreak@90: if profile.bars[name] == nil then flickerstreak@90: profile.bars[name] = { flickerstreak@90: buttons = { } flickerstreak@90: } flickerstreak@90: end flickerstreak@90: if self.handles[bar] == nil then flickerstreak@90: self.handles[bar] = Handle:New(bar, profile.bars[name]) flickerstreak@90: end flickerstreak@90: end flickerstreak@24: end flickerstreak@24: flickerstreak@63: function module:OnRefreshBar(event, bar, name) flickerstreak@90: if self.handles[bar] then flickerstreak@90: self.handles[bar]:Refresh() flickerstreak@24: end flickerstreak@24: end flickerstreak@24: flickerstreak@63: function module:OnDestroyBar(event, bar, name) flickerstreak@90: if self.handles[bar] then flickerstreak@90: self.handles[bar]:Destroy() flickerstreak@90: self.handles[bar] = nil flickerstreak@24: end flickerstreak@24: end flickerstreak@24: flickerstreak@63: function module:OnEraseBar(event, bar, name) flickerstreak@75: self.db.profile.bars[name] = nil flickerstreak@24: end flickerstreak@24: flickerstreak@63: function module:OnRenameBar(event, bar, oldname, newname) flickerstreak@75: b = self.db.profile.bars flickerstreak@75: b[newname], b[oldname] = b[oldname], nil flickerstreak@48: end flickerstreak@48: flickerstreak@63: function module:OnConfigModeChanged(event, mode) flickerstreak@90: for _, h in pairs(self.handles) do flickerstreak@90: h:SetConfigMode(mode) flickerstreak@24: end flickerstreak@24: end flickerstreak@24: flickerstreak@88: function module:LIBKEYBOUND_ENABLED(evt) flickerstreak@90: for _, h in pairs(self.handles) do flickerstreak@93: h:ShowGrid(true) flickerstreak@90: h:SetKeybindMode(true) flickerstreak@88: end flickerstreak@88: end flickerstreak@88: flickerstreak@88: function module:LIBKEYBOUND_DISABLED(evt) flickerstreak@90: for _, h in pairs(self.handles) do flickerstreak@93: h:ShowGrid(false) flickerstreak@90: h:SetKeybindMode(false) flickerstreak@88: end flickerstreak@88: end flickerstreak@88: flickerstreak@50: flickerstreak@90: ---- Interface ---- flickerstreak@90: function module:GetBarOptions(bar) flickerstreak@90: local h = self.handles[bar] flickerstreak@90: if h then flickerstreak@90: return h:GetOptions() flickerstreak@90: end flickerstreak@90: end flickerstreak@90: flickerstreak@90: flickerstreak@90: ---- Bar Handle ---- flickerstreak@90: flickerstreak@87: do flickerstreak@87: local options = { flickerstreak@87: hideEmpty = { flickerstreak@87: name = L["Hide Empty Buttons"], flickerstreak@87: order = 1, flickerstreak@87: type = "toggle", flickerstreak@87: width = "double", flickerstreak@87: get = "GetHideEmpty", flickerstreak@87: set = "SetHideEmpty", flickerstreak@87: }, flickerstreak@102: lockButtons = { flickerstreak@102: name = L["Lock Buttons"], flickerstreak@102: desc = L["Prevents picking up/dragging actions.|nNOTE: This setting is overridden by the global setting in Blizzard's Action Buttons tab"], flickerstreak@102: order = 2, flickerstreak@102: type = "toggle", flickerstreak@102: disabled = "LockButtonsDisabled", flickerstreak@102: get = "GetLockButtons", flickerstreak@102: set = "SetLockButtons", flickerstreak@102: }, flickerstreak@102: lockOnlyCombat = { flickerstreak@102: name = L["Only in Combat"], flickerstreak@102: desc = L["Only lock the buttons when in combat"], flickerstreak@102: order = 3, flickerstreak@102: type = "toggle", flickerstreak@102: disabled = "LockButtonsCombatDisabled", flickerstreak@102: get = "GetLockButtonsCombat", flickerstreak@102: set = "SetLockButtonsCombat", flickerstreak@102: }, flickerstreak@87: pages = { flickerstreak@87: name = L["# Pages"], flickerstreak@87: desc = L["Use the Dynamic State tab to specify page transitions"], flickerstreak@102: order = 4, flickerstreak@87: type = "range", flickerstreak@87: min = 1, flickerstreak@87: max = 10, flickerstreak@87: step = 1, flickerstreak@87: get = "GetNumPages", flickerstreak@87: set = "SetNumPages", flickerstreak@87: }, flickerstreak@90: mindcontrol = { flickerstreak@90: name = L["Mind Control Support"], flickerstreak@90: desc = L["When possessing a target (e.g. via Mind Control), map the first 12 buttons of this bar to the possessed target's actions."], flickerstreak@102: order = 5, flickerstreak@90: type = "toggle", flickerstreak@90: width = "double", flickerstreak@90: set = "SetMindControl", flickerstreak@90: get = "GetMindControl", flickerstreak@90: }, flickerstreak@87: actions = { flickerstreak@87: name = L["Edit Action IDs"], flickerstreak@102: order = 6, flickerstreak@87: type = "group", flickerstreak@87: inline = true, flickerstreak@87: args = { flickerstreak@87: method = { flickerstreak@87: name = L["Assign"], flickerstreak@87: order = 1, flickerstreak@87: type = "select", flickerstreak@87: width = "double", flickerstreak@87: values = { [0] = L["Choose Method..."], flickerstreak@87: [1] = L["Individually"], flickerstreak@87: [2] = L["All at Once"], }, flickerstreak@87: get = "GetActionEditMethod", flickerstreak@87: set = "SetActionEditMethod", flickerstreak@87: }, flickerstreak@87: rowSelect = { flickerstreak@87: name = L["Row"], flickerstreak@87: desc = L["Rows are numbered top to bottom"], flickerstreak@87: order = 2, flickerstreak@87: type = "select", flickerstreak@87: width = "half", flickerstreak@87: hidden = "IsButtonSelectHidden", flickerstreak@87: values = "GetRowList", flickerstreak@87: get = "GetSelectedRow", flickerstreak@87: set = "SetSelectedRow", flickerstreak@87: }, flickerstreak@87: colSelect = { flickerstreak@87: name = L["Col"], flickerstreak@87: desc = L["Columns are numbered left to right"], flickerstreak@87: order = 3, flickerstreak@87: type = "select", flickerstreak@87: width = "half", flickerstreak@87: hidden = "IsButtonSelectHidden", flickerstreak@87: values = "GetColumnList", flickerstreak@87: get = "GetSelectedColumn", flickerstreak@87: set = "SetSelectedColumn", flickerstreak@87: }, flickerstreak@87: pageSelect = { flickerstreak@87: name = L["Page"], flickerstreak@87: order = 4, flickerstreak@87: type = "select", flickerstreak@87: width = "half", flickerstreak@87: hidden = "IsPageSelectHidden", flickerstreak@87: values = "GetPageList", flickerstreak@87: get = "GetSelectedPage", flickerstreak@87: set = "SetSelectedPage", flickerstreak@87: }, flickerstreak@87: single = { flickerstreak@87: name = L["Action ID"], flickerstreak@87: usage = L["Specify ID 1-120"], flickerstreak@87: order = 5, flickerstreak@87: type = "input", flickerstreak@87: width = "half", flickerstreak@87: hidden = "IsButtonSelectHidden", flickerstreak@87: get = "GetActionID", flickerstreak@87: set = "SetActionID", flickerstreak@87: validate = "ValidateActionID", flickerstreak@87: }, flickerstreak@87: multi = { flickerstreak@87: name = L["ID List"], flickerstreak@87: usage = L["Specify a comma-separated list of IDs for each button in the bar (in order). Separate multiple pages with semicolons (;)"], flickerstreak@87: order = 6, flickerstreak@87: type = "input", flickerstreak@87: multiline = true, flickerstreak@87: width = "double", flickerstreak@87: hidden = "IsMultiIDHidden", flickerstreak@87: get = "GetMultiID", flickerstreak@87: set = "SetMultiID", flickerstreak@87: validate = "ValidateMultiID", flickerstreak@90: }, flickerstreak@90: }, flickerstreak@87: }, flickerstreak@87: } flickerstreak@77: flickerstreak@90: local weak = { __mode="k" } flickerstreak@90: local meta = { __index = Handle } flickerstreak@90: flickerstreak@90: function Handle:New( bar, config ) flickerstreak@90: local self = setmetatable( flickerstreak@90: { flickerstreak@90: bar = bar, flickerstreak@90: config = config, flickerstreak@90: btns = { } flickerstreak@90: }, flickerstreak@90: meta) flickerstreak@90: flickerstreak@90: if self.config.buttons == nil then flickerstreak@90: self.config.buttons = { } flickerstreak@90: end flickerstreak@90: self:Refresh() flickerstreak@92: self:SetKeybindMode(ReAction:GetKeybindMode()) flickerstreak@90: return self flickerstreak@90: end flickerstreak@90: flickerstreak@90: function Handle:Refresh() flickerstreak@90: local r, c = self.bar:GetButtonGrid() flickerstreak@90: local n = r*c flickerstreak@90: local btnCfg = self.config.buttons flickerstreak@90: if n ~= #self.btns then flickerstreak@90: for i = 1, n do flickerstreak@90: if btnCfg[i] == nil then flickerstreak@90: btnCfg[i] = {} flickerstreak@90: end flickerstreak@90: if self.btns[i] == nil then flickerstreak@90: local b = Button:New(self, i, btnCfg[i], self.config) flickerstreak@90: self.btns[i] = b flickerstreak@90: self.bar:AddButton(i,b) flickerstreak@90: end flickerstreak@90: end flickerstreak@90: for i = n+1, #self.btns do flickerstreak@90: if self.btns[i] then flickerstreak@90: self.bar:RemoveButton(self.btns[i]) flickerstreak@90: self.btns[i]:Destroy() flickerstreak@90: self.btns[i] = nil flickerstreak@90: btnCfg[i] = nil flickerstreak@90: end flickerstreak@90: end flickerstreak@90: end flickerstreak@102: local f = self.bar:GetFrame() flickerstreak@90: for _, b in ipairs(self.btns) do flickerstreak@90: b:Refresh() flickerstreak@90: end flickerstreak@90: f:SetAttribute("mindcontrol",self.config.mindcontrol) flickerstreak@90: f:Execute( flickerstreak@90: [[ flickerstreak@90: doMindControl = self:GetAttribute("mindcontrol") flickerstreak@90: control:ChildUpdate() flickerstreak@90: ]]) flickerstreak@90: flickerstreak@90: f:SetAttribute("_onstate-mindcontrol", flickerstreak@90: -- function _onstate-mindcontrol(self, stateid, newstate) flickerstreak@90: [[ flickerstreak@90: control:ChildUpdate() flickerstreak@90: ]]) flickerstreak@90: RegisterStateDriver(f, "mindcontrol", "[bonusbar:5] mc; none") flickerstreak@102: self:UpdateButtonLock() flickerstreak@90: end flickerstreak@90: flickerstreak@90: function Handle:Destroy() flickerstreak@90: for _,b in pairs(self.btns) do flickerstreak@90: if b then flickerstreak@90: b:Destroy() flickerstreak@90: end flickerstreak@90: end flickerstreak@90: end flickerstreak@90: flickerstreak@90: function Handle:SetConfigMode(mode) flickerstreak@90: for _, b in pairs(self.btns) do flickerstreak@90: b:ShowGrid(mode) flickerstreak@90: b:ShowActionIDLabel(mode) flickerstreak@90: end flickerstreak@90: end flickerstreak@90: flickerstreak@93: function Handle:ShowGrid(show) flickerstreak@93: for _, b in pairs(self.btns) do flickerstreak@93: b:ShowGrid(show) flickerstreak@93: end flickerstreak@93: end flickerstreak@93: flickerstreak@102: function Handle:UpdateButtonLock() flickerstreak@102: local f = self.bar:GetFrame() flickerstreak@102: f:SetAttribute("lockbuttons",self.config.lockButtons) flickerstreak@102: f:SetAttribute("lockbuttonscombat",self.config.lockButtonsCombat) flickerstreak@102: f:Execute( flickerstreak@102: [[ flickerstreak@102: lockButtons = self:GetAttribute("lockbuttons") flickerstreak@102: lockButtonsCombat = self:GetAttribute("lockbuttonscombat") flickerstreak@102: ]]) flickerstreak@102: end flickerstreak@102: flickerstreak@90: function Handle:SetKeybindMode(mode) flickerstreak@90: for _, b in pairs(self.btns) do flickerstreak@90: if mode then flickerstreak@90: -- set the border for all buttons to the keybind-enable color flickerstreak@92: b.border:SetVertexColor(KB:GetColorKeyBoundMode()) flickerstreak@90: b.border:Show() flickerstreak@92: elseif IsEquippedAction(b:GetActionID()) then flickerstreak@92: b.border:SetVertexColor(0, 1.0, 0, 0.35) -- from ActionButton.lua flickerstreak@90: else flickerstreak@90: b.border:Hide() flickerstreak@90: end flickerstreak@90: end flickerstreak@90: end flickerstreak@90: flickerstreak@90: function Handle:GetLastButton() flickerstreak@90: return self.btns[#self.btns] flickerstreak@90: end flickerstreak@90: flickerstreak@90: -- options handlers flickerstreak@90: function Handle:GetOptions() flickerstreak@87: return { flickerstreak@87: type = "group", flickerstreak@87: name = L["Action Buttons"], flickerstreak@90: handler = self, flickerstreak@87: args = options flickerstreak@87: } flickerstreak@77: end flickerstreak@77: flickerstreak@90: function Handle:SetHideEmpty(info, value) flickerstreak@90: if value ~= self.config.hideEmpty then flickerstreak@90: self.config.hideEmpty = value flickerstreak@102: self:ShowGrid(not value) flickerstreak@77: end flickerstreak@77: end flickerstreak@77: flickerstreak@90: function Handle:GetHideEmpty() flickerstreak@90: return self.config.hideEmpty flickerstreak@77: end flickerstreak@87: flickerstreak@102: function Handle:GetLockButtons() flickerstreak@102: return LOCK_ACTIONBAR == "1" or self.config.lockButtons flickerstreak@102: end flickerstreak@102: flickerstreak@102: function Handle:SetLockButtons(info, value) flickerstreak@102: self.config.lockButtons = value flickerstreak@102: self:UpdateButtonLock() flickerstreak@102: end flickerstreak@102: flickerstreak@102: function Handle:LockButtonsDisabled() flickerstreak@102: return LOCK_ACTIONBAR == "1" flickerstreak@102: end flickerstreak@102: flickerstreak@102: function Handle:GetLockButtonsCombat() flickerstreak@102: return self.config.lockButtonsCombat flickerstreak@102: end flickerstreak@102: flickerstreak@102: function Handle:SetLockButtonsCombat(info, value) flickerstreak@102: self.config.lockButtonsCombat = value flickerstreak@102: self:UpdateButtonLock() flickerstreak@102: end flickerstreak@102: flickerstreak@102: function Handle:LockButtonsCombatDisabled() flickerstreak@102: return LOCK_ACTIONBAR == "1" or not self.config.lockButtons flickerstreak@102: end flickerstreak@102: flickerstreak@90: function Handle:GetNumPages() flickerstreak@90: return self.config.nPages flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:SetNumPages(info, value) flickerstreak@90: self.config.nPages = value flickerstreak@90: self:Refresh() flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:GetMindControl() flickerstreak@90: return self.config.mindcontrol flickerstreak@90: end flickerstreak@90: flickerstreak@90: function Handle:SetMindControl(info, value) flickerstreak@90: self.config.mindcontrol = value flickerstreak@90: self:Refresh() flickerstreak@90: end flickerstreak@90: flickerstreak@90: function Handle:GetActionEditMethod() flickerstreak@87: return self.editMethod or 0 flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:SetActionEditMethod(info, value) flickerstreak@87: self.editMethod = value flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:IsButtonSelectHidden() flickerstreak@87: return self.editMethod ~= 1 flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:GetRowList() flickerstreak@87: local r,c = self.bar:GetButtonGrid() flickerstreak@87: if self.rowList == nil or #self.rowList ~= r then flickerstreak@87: local list = { } flickerstreak@87: for i = 1, r do flickerstreak@87: table.insert(list,i) flickerstreak@87: end flickerstreak@87: self.rowList = list flickerstreak@87: end flickerstreak@87: return self.rowList flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:GetSelectedRow() flickerstreak@87: local r, c = self.bar:GetButtonGrid() flickerstreak@87: local row = self.selectedRow or 1 flickerstreak@87: if row > r then flickerstreak@87: row = 1 flickerstreak@87: end flickerstreak@87: self.selectedRow = row flickerstreak@87: return row flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:SetSelectedRow(info, value) flickerstreak@87: self.selectedRow = value flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:GetColumnList() flickerstreak@87: local r,c = self.bar:GetButtonGrid() flickerstreak@87: if self.columnList == nil or #self.columnList ~= c then flickerstreak@87: local list = { } flickerstreak@87: for i = 1, c do flickerstreak@87: table.insert(list,i) flickerstreak@87: end flickerstreak@87: self.columnList = list flickerstreak@87: end flickerstreak@87: return self.columnList flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:GetSelectedColumn() flickerstreak@87: local r, c = self.bar:GetButtonGrid() flickerstreak@87: local col = self.selectedColumn or 1 flickerstreak@87: if col > c then flickerstreak@87: col = 1 flickerstreak@87: end flickerstreak@87: self.selectedColumn = col flickerstreak@87: return col flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:SetSelectedColumn(info, value) flickerstreak@87: self.selectedColumn = value flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:IsPageSelectHidden() flickerstreak@90: return self.editMethod ~= 1 or (self.config.nPages or 1) < 2 flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:GetPageList() flickerstreak@90: local n = self.config.nPages or 1 flickerstreak@87: if self.pageList == nil or #self.pageList ~= n then flickerstreak@87: local p = { } flickerstreak@87: for i = 1, n do flickerstreak@87: table.insert(p,i) flickerstreak@87: end flickerstreak@87: self.pageList = p flickerstreak@87: end flickerstreak@87: return self.pageList flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:GetSelectedPage() flickerstreak@87: local p = self.selectedPage or 1 flickerstreak@90: if p > (self.config.nPages or 1) then flickerstreak@87: p = 1 flickerstreak@87: end flickerstreak@87: self.selectedPage = p flickerstreak@87: return p flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:SetSelectedPage(info, value) flickerstreak@87: self.selectedPage = value flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:GetActionID() flickerstreak@87: local row = self.selectedRow or 1 flickerstreak@87: local col = self.selectedColumn or 1 flickerstreak@87: local r, c = self.bar:GetButtonGrid() flickerstreak@87: local n = (row-1) * c + col flickerstreak@90: local btn = self.btns[n] flickerstreak@87: if btn then flickerstreak@87: return tostring(btn:GetActionID(self.selectedPage or 1)) flickerstreak@87: end flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:SetActionID(info, value) flickerstreak@87: local row = self.selectedRow or 1 flickerstreak@87: local col = self.selectedColumn or 1 flickerstreak@87: local r, c = self.bar:GetButtonGrid() flickerstreak@87: local n = (row-1) * c + col flickerstreak@90: local btn = self.btns[n] flickerstreak@87: if btn then flickerstreak@87: btn:SetActionID(tonumber(value), self.selectedPage or 1) flickerstreak@87: end flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:ValidateActionID(info, value) flickerstreak@87: value = tonumber(value) flickerstreak@87: if value == nil or value < 1 or value > 120 then flickerstreak@87: return L["Specify ID 1-120"] flickerstreak@87: end flickerstreak@87: return true flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:IsMultiIDHidden() flickerstreak@87: return self.editMethod ~= 2 flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:GetMultiID() flickerstreak@87: local p = { } flickerstreak@90: for i = 1, self.config.nPages or 1 do flickerstreak@87: local b = { } flickerstreak@90: for _, btn in ipairs(self.btns) do flickerstreak@87: table.insert(b, btn:GetActionID(i)) flickerstreak@87: end flickerstreak@87: table.insert(p, table.concat(b,",")) flickerstreak@87: end flickerstreak@87: return table.concat(p,";\n") flickerstreak@87: end flickerstreak@87: flickerstreak@87: flickerstreak@87: local function ParseMultiID(nBtns, nPages, s) flickerstreak@87: if s:match("[^%d%s,;]") then flickerstreak@87: ReAction:Print("items other than digits, spaces, commas, and semicolons in string",s) flickerstreak@87: return nil flickerstreak@87: end flickerstreak@87: local p = { } flickerstreak@87: for list in s:gmatch("[^;]+") do flickerstreak@87: local pattern = ("^%s?$"):format(("%s*(%d+)%s*,"):rep(nBtns)) flickerstreak@87: local ids = { list:match(pattern) } flickerstreak@87: if #ids ~= nBtns then flickerstreak@87: ReAction:Print("found",#ids,"buttons instead of",nBtns) flickerstreak@87: return nil flickerstreak@87: end flickerstreak@87: table.insert(p,ids) flickerstreak@87: end flickerstreak@87: if #p ~= nPages then flickerstreak@87: ReAction:Print("found",#p,"pages instead of",nPages) flickerstreak@87: return nil flickerstreak@87: end flickerstreak@87: return p flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:SetMultiID(info, value) flickerstreak@91: local p = ParseMultiID(#self.btns, self.config.nPages or 1, value) flickerstreak@87: for page, b in ipairs(p) do flickerstreak@87: for button, id in ipairs(b) do flickerstreak@90: self.btns[button]:SetActionID(id, page) flickerstreak@87: end flickerstreak@87: end flickerstreak@87: end flickerstreak@87: flickerstreak@90: function Handle:ValidateMultiID(info, value) flickerstreak@87: local bad = L["Invalid action ID list string"] flickerstreak@90: if value == nil or ParseMultiID(#self.btns, self.config.nPages or 1, value) == nil then flickerstreak@87: return bad flickerstreak@87: end flickerstreak@87: return true flickerstreak@87: end flickerstreak@77: end flickerstreak@77: flickerstreak@77: flickerstreak@87: ------ State property options ------ flickerstreak@87: do flickerstreak@87: local pageOptions = { flickerstreak@87: page = { flickerstreak@92: name = L["Show Page #"], flickerstreak@92: order = 11, flickerstreak@92: type = "select", flickerstreak@92: width = "half", flickerstreak@87: disabled = "IsPageDisabled", flickerstreak@87: hidden = "IsPageHidden", flickerstreak@87: values = "GetPageValues", flickerstreak@87: set = "SetProp", flickerstreak@87: get = "GetPage", flickerstreak@87: }, flickerstreak@87: } flickerstreak@50: flickerstreak@90: local function GetBarConfig(bar) flickerstreak@90: return module.db.profile.bars[bar:GetName()] flickerstreak@87: end flickerstreak@87: flickerstreak@90: function PropHandler.GetOptions() flickerstreak@90: return pageOptions flickerstreak@87: end flickerstreak@87: flickerstreak@90: function PropHandler:IsPageDisabled() flickerstreak@90: local c = GetBarConfig(self.bar) flickerstreak@90: local n = c and c.nPages or 1 flickerstreak@90: return not (n > 1) flickerstreak@87: end flickerstreak@87: flickerstreak@90: function PropHandler:IsPageHidden() flickerstreak@87: return not GetBarConfig(self.bar) flickerstreak@87: end flickerstreak@87: flickerstreak@90: function PropHandler:GetPageValues() flickerstreak@92: if not self._pagevalues then flickerstreak@92: self._pagevalues = { } flickerstreak@92: end flickerstreak@87: local c = GetBarConfig(self.bar) flickerstreak@87: if c then flickerstreak@87: local n = c.nPages flickerstreak@92: -- cache the results flickerstreak@87: if self._npages ~= n then flickerstreak@87: self._npages = n flickerstreak@92: wipe(self._pagevalues) flickerstreak@87: for i = 1, n do flickerstreak@90: self._pagevalues["page"..i] = i flickerstreak@87: end flickerstreak@87: end flickerstreak@87: end flickerstreak@92: return self._pagevalues flickerstreak@87: end flickerstreak@87: flickerstreak@90: function PropHandler:GetPage(info) flickerstreak@87: return self:GetProp(info) or 1 flickerstreak@87: end flickerstreak@90: flickerstreak@87: end flickerstreak@87: flickerstreak@87: ------ ActionID allocation ------ flickerstreak@87: -- this needs to be high performance when requesting new IDs, flickerstreak@87: -- or certain controls will become sluggish. However, the new-request flickerstreak@87: -- infrastructure can be built lazily the first time that a new request flickerstreak@87: -- comes in (which will only happen at user config time: at static startup flickerstreak@87: -- config time all actionIDs should already have been assigned and stored flickerstreak@87: -- in the config file) flickerstreak@87: flickerstreak@87: local IDAlloc flickerstreak@87: do flickerstreak@87: local n = 120 flickerstreak@87: flickerstreak@87: IDAlloc = setmetatable({ wrap = 1, freecount = n }, {__index = function() return 0 end}) flickerstreak@87: flickerstreak@87: function IDAlloc:Acquire(id, hint) flickerstreak@87: id = tonumber(id) flickerstreak@87: hint = tonumber(hint) flickerstreak@87: if id and (id < 1 or id > n) then flickerstreak@87: id = nil flickerstreak@87: end flickerstreak@87: if hint and (hint < 1 or hint > n) then flickerstreak@87: hint = nil flickerstreak@87: end flickerstreak@87: if id == nil then flickerstreak@87: -- get a free ID flickerstreak@87: if hint and self[hint] == 0 then flickerstreak@87: -- use the hint if it's free flickerstreak@87: id = hint flickerstreak@87: elseif self.freecount > 0 then flickerstreak@87: -- if neither the id nor the hint are defined or free, but flickerstreak@87: -- the list is known to have free IDs, then start searching flickerstreak@87: -- at the hint for a free one flickerstreak@87: for i = hint or 1, n do flickerstreak@87: if self[i] == 0 then flickerstreak@87: id = i flickerstreak@87: break flickerstreak@87: end flickerstreak@87: end flickerstreak@87: -- self.wrap the search flickerstreak@87: if id == nil and hint and hint > 1 then flickerstreak@87: for i = 1, hint - 1 do flickerstreak@87: if self[i] == 0 then flickerstreak@87: id = i flickerstreak@87: break flickerstreak@87: end flickerstreak@87: end flickerstreak@87: end flickerstreak@87: end flickerstreak@87: if id == nil then flickerstreak@87: -- if there are no free IDs, start wrapping at 1 flickerstreak@87: id = self.wrap flickerstreak@87: self.wrap = id + 1 flickerstreak@87: if self.wrap > n then flickerstreak@87: self.wrap = 1 flickerstreak@87: end flickerstreak@24: end flickerstreak@24: end flickerstreak@87: if self[id] == 0 then flickerstreak@87: self.freecount = self.freecount - 1 flickerstreak@87: end flickerstreak@87: self[id] = self[id] + 1 flickerstreak@87: return id flickerstreak@24: end flickerstreak@24: flickerstreak@87: function IDAlloc:Release(id) flickerstreak@87: id = tonumber(id) flickerstreak@87: if id and (id >= 1 or id <= n) then flickerstreak@87: self[id] = self[id] - 1 flickerstreak@87: if self[id] == 0 then flickerstreak@87: self.freecount = self.freecount + 1 flickerstreak@87: self.wrap = 1 flickerstreak@87: end flickerstreak@87: end flickerstreak@87: end flickerstreak@87: end flickerstreak@87: flickerstreak@88: ------ Button class ------ flickerstreak@88: flickerstreak@88: do flickerstreak@90: local frameRecycler = { } flickerstreak@90: local trash = CreateFrame("Frame") flickerstreak@90: local OnUpdate, KBAttach, GetActionName, GetHotkey, SetKey, FreeKey, ClearBindings, GetBindings flickerstreak@90: do flickerstreak@90: local ATTACK_BUTTON_FLASH_TIME = ATTACK_BUTTON_FLASH_TIME flickerstreak@90: local IsActionInRange = IsActionInRange flickerstreak@87: flickerstreak@90: local buttonLookup = setmetatable({},{__mode="kv"}) flickerstreak@88: flickerstreak@90: function OnUpdate(frame, elapsed) flickerstreak@90: -- note: This function taints frame.flashtime and frame.rangeTimer. Both of these flickerstreak@90: -- are only read by ActionButton_OnUpdate (which this function replaces). In flickerstreak@90: -- all other places they're just written, so it doesn't taint any secure code. flickerstreak@90: if frame.flashing == 1 then flickerstreak@90: frame.flashtime = frame.flashtime - elapsed flickerstreak@90: if frame.flashtime <= 0 then flickerstreak@90: local overtime = -frame.flashtime flickerstreak@90: if overtime >= ATTACK_BUTTON_FLASH_TIME then flickerstreak@90: overtime = 0 flickerstreak@90: end flickerstreak@90: frame.flashtime = ATTACK_BUTTON_FLASH_TIME - overtime flickerstreak@90: flickerstreak@90: local flashTexture = frame.flash flickerstreak@90: if flashTexture:IsShown() then flickerstreak@90: flashTexture:Hide() flickerstreak@90: else flickerstreak@90: flashTexture:Show() flickerstreak@90: end flickerstreak@88: end flickerstreak@90: end flickerstreak@90: flickerstreak@90: if frame.rangeTimer then flickerstreak@90: frame.rangeTimer = frame.rangeTimer - elapsed; flickerstreak@88: flickerstreak@90: if frame.rangeTimer <= 0 then flickerstreak@90: if IsActionInRange(frame.action) == 0 then flickerstreak@90: frame.icon:SetVertexColor(1.0,0.1,0.1) flickerstreak@90: else flickerstreak@90: ActionButton_UpdateUsable(frame) flickerstreak@90: end flickerstreak@90: frame.rangeTimer = 0.1 flickerstreak@88: end flickerstreak@88: end flickerstreak@88: end flickerstreak@90: flickerstreak@90: -- Use KeyBound-1.0 for binding, but use Override bindings instead of flickerstreak@90: -- regular bindings to support multiple profile use. This is a little flickerstreak@90: -- weird with the KeyBound dialog box (which has per-char selector as well flickerstreak@90: -- as an OK/Cancel box) but it's the least amount of effort to implement. flickerstreak@90: function GetActionName(f) flickerstreak@90: local b = buttonLookup[f] flickerstreak@90: if b then flickerstreak@90: return format("%s:%s", b.bar:GetName(), b.idx) flickerstreak@90: end flickerstreak@90: end flickerstreak@90: flickerstreak@90: function GetHotkey(f) flickerstreak@90: local b = buttonLookup[f] flickerstreak@90: if b then flickerstreak@90: return KB:ToShortKey(b:GetConfig().hotkey) flickerstreak@90: end flickerstreak@90: end flickerstreak@90: flickerstreak@90: function SetKey(f, key) flickerstreak@90: local b = buttonLookup[f] flickerstreak@90: if b then flickerstreak@90: local c = b:GetConfig() flickerstreak@90: if c.hotkey then flickerstreak@90: SetOverrideBinding(f, false, c.hotkey, nil) flickerstreak@90: end flickerstreak@90: if key then flickerstreak@90: SetOverrideBindingClick(f, false, key, f:GetName(), nil) flickerstreak@90: end flickerstreak@90: c.hotkey = key flickerstreak@90: b:DisplayHotkey(GetHotkey(f)) flickerstreak@90: end flickerstreak@90: end flickerstreak@90: flickerstreak@90: function FreeKey(f, key) flickerstreak@90: local b = buttonLookup[f] flickerstreak@90: if b then flickerstreak@90: local c = b:GetConfig() flickerstreak@90: if c.hotkey == key then flickerstreak@90: local action = f:GetActionName() flickerstreak@90: SetOverrideBinding(f, false, c.hotkey, nil) flickerstreak@90: c.hotkey = nil flickerstreak@90: b:DisplayHotkey(nil) flickerstreak@90: return action flickerstreak@90: end flickerstreak@90: end flickerstreak@90: return ReAction:FreeOverrideHotkey(key) flickerstreak@90: end flickerstreak@90: flickerstreak@90: function ClearBindings(f) flickerstreak@90: SetKey(f, nil) flickerstreak@90: end flickerstreak@90: flickerstreak@90: function GetBindings(f) flickerstreak@90: local b = buttonLookup[f] flickerstreak@90: if b then flickerstreak@90: return b:GetConfig().hotkey flickerstreak@90: end flickerstreak@90: end flickerstreak@90: flickerstreak@90: function KBAttach( button ) flickerstreak@90: local f = button:GetFrame() flickerstreak@90: f.GetActionName = GetActionName flickerstreak@90: f.GetHotkey = GetHotkey flickerstreak@90: f.SetKey = SetKey flickerstreak@90: f.FreeKey = FreeKey flickerstreak@90: f.ClearBindings = ClearBindings flickerstreak@90: f.GetBindings = GetBindings flickerstreak@90: buttonLookup[f] = button flickerstreak@90: f:SetKey(button:GetConfig().hotkey) flickerstreak@90: ReAction:RegisterKeybindFrame(f) flickerstreak@92: if ReAction:GetKeybindMode() then flickerstreak@92: button.border:SetVertexColor(KB:GetColorKeyBoundMode()) flickerstreak@92: button.border:Show() flickerstreak@92: end flickerstreak@90: end flickerstreak@90: end flickerstreak@90: flickerstreak@90: local meta = {__index = Button} flickerstreak@90: flickerstreak@90: function Button:New( handle, idx, config, barConfig ) flickerstreak@90: local bar = handle.bar flickerstreak@90: flickerstreak@90: -- create new self flickerstreak@90: self = setmetatable( flickerstreak@90: { flickerstreak@90: bar = bar, flickerstreak@90: idx = idx, flickerstreak@90: config = config, flickerstreak@90: barConfig = barConfig, flickerstreak@90: }, meta ) flickerstreak@90: flickerstreak@90: local name = config.name or ("ReAction_%s_%s_%d"):format(bar:GetName(),moduleID,idx) flickerstreak@90: self.name = name flickerstreak@90: config.name = name flickerstreak@90: local lastButton = handle:GetLastButton() flickerstreak@90: config.actionID = IDAlloc:Acquire(config.actionID, lastButton and lastButton.config.actionID) -- gets a free one if none configured flickerstreak@90: self.nPages = 1 flickerstreak@88: flickerstreak@90: -- have to recycle frames with the same name: CreateFrame() doesn't overwrite flickerstreak@90: -- existing globals. Can't set to nil in the global because it's then tainted. flickerstreak@90: local parent = bar:GetFrame() flickerstreak@90: local f = frameRecycler[name] flickerstreak@90: if f then flickerstreak@90: f:SetParent(parent) flickerstreak@90: else flickerstreak@90: f = CreateFrame("CheckButton", name, parent, "ActionBarButtonTemplate") flickerstreak@90: -- ditch the old hotkey text because it's tied in ActionButton_Update() to the flickerstreak@90: -- standard binding. We use override bindings. flickerstreak@90: local hotkey = _G[name.."HotKey"] flickerstreak@90: hotkey:SetParent(trash) flickerstreak@90: hotkey = f:CreateFontString(nil, "ARTWORK", "NumberFontNormalSmallGray") flickerstreak@90: hotkey:SetWidth(36) flickerstreak@90: hotkey:SetHeight(18) flickerstreak@90: hotkey:SetJustifyH("RIGHT") flickerstreak@90: hotkey:SetJustifyV("TOP") flickerstreak@90: hotkey:SetPoint("TOPLEFT",f,"TOPLEFT",-2,-2) flickerstreak@90: f.hotkey = hotkey flickerstreak@90: f.icon = _G[name.."Icon"] flickerstreak@90: f.flash = _G[name.."Flash"] flickerstreak@90: f:SetScript("OnUpdate",OnUpdate) flickerstreak@90: end flickerstreak@88: flickerstreak@90: self.hotkey = f.hotkey flickerstreak@90: self.border = _G[name.."Border"] flickerstreak@90: flickerstreak@90: f:SetAttribute("action", config.actionID) flickerstreak@90: -- install mind control actions for all buttons just for simplicity flickerstreak@90: if self.idx <= 12 then flickerstreak@90: f:SetAttribute("*action-mc", 120 + self.idx) flickerstreak@90: end flickerstreak@90: flickerstreak@90: -- set a _childupdate handler, called within the header's context flickerstreak@90: f:SetAttribute("_childupdate", flickerstreak@90: -- function _childupdate(self, snippetid, message) flickerstreak@90: [[ flickerstreak@90: local action = "action" flickerstreak@90: if doMindControl and GetBonusBarOffset() == 5 then flickerstreak@90: action = "*action-mc" flickerstreak@90: elseif page and state and page[state] then flickerstreak@90: action = "*action-"..page[state] flickerstreak@90: end flickerstreak@90: local value = self:GetAttribute(action) flickerstreak@92: self:SetAttribute("action",value) flickerstreak@90: ]]) flickerstreak@90: flickerstreak@102: -- install drag wrappers to lock buttons flickerstreak@102: bar:GetFrame():WrapScript(f, "OnDragStart", flickerstreak@102: -- OnDragStart(self, button, kind, value, ...) flickerstreak@102: [[ flickerstreak@102: if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then flickerstreak@102: return "clear" flickerstreak@102: end flickerstreak@102: ]]) flickerstreak@102: flickerstreak@90: self.frame = f flickerstreak@90: self.normalTexture = getglobal(format("%sNormalTexture",f:GetName())) flickerstreak@90: flickerstreak@90: -- initialize the hide state flickerstreak@90: f:SetAttribute("showgrid",0) flickerstreak@90: self:ShowGrid(not barConfig.hideEmpty) flickerstreak@90: if ReAction:GetConfigMode() then flickerstreak@90: self:ShowGrid(true) flickerstreak@90: end flickerstreak@90: flickerstreak@90: -- show the ID label if applicable flickerstreak@90: self:ShowActionIDLabel(ReAction:GetConfigMode()) flickerstreak@90: flickerstreak@90: -- attach the keybinder flickerstreak@90: KBAttach(self) flickerstreak@90: flickerstreak@90: self:Refresh() flickerstreak@90: return self flickerstreak@90: end flickerstreak@90: flickerstreak@90: function Button:Destroy() flickerstreak@90: local f = self.frame flickerstreak@90: f:UnregisterAllEvents() flickerstreak@90: f:Hide() flickerstreak@90: f:SetParent(UIParent) flickerstreak@90: f:ClearAllPoints() flickerstreak@90: if self.name then flickerstreak@90: frameRecycler[self.name] = f flickerstreak@90: end flickerstreak@90: if self.config.actionID then flickerstreak@90: IDAlloc:Release(self.config.actionID) flickerstreak@90: end flickerstreak@90: if self.config.pageactions then flickerstreak@90: for _, id in ipairs(self.config.pageactions) do flickerstreak@90: IDAlloc:Release(id) flickerstreak@90: end flickerstreak@90: end flickerstreak@90: self.frame = nil flickerstreak@90: self.config = nil flickerstreak@90: self.bar = nil flickerstreak@90: end flickerstreak@90: flickerstreak@90: function Button:Refresh() flickerstreak@90: local f = self.frame flickerstreak@90: self.bar:PlaceButton(self, 36, 36) flickerstreak@90: self:RefreshPages() flickerstreak@90: end flickerstreak@90: flickerstreak@90: function Button:GetFrame() flickerstreak@90: return self.frame flickerstreak@90: end flickerstreak@90: flickerstreak@90: function Button:GetName() flickerstreak@90: return self.name flickerstreak@90: end flickerstreak@90: flickerstreak@90: function Button:GetConfig() flickerstreak@90: return self.config flickerstreak@90: end flickerstreak@90: flickerstreak@90: function Button:GetActionID(page) flickerstreak@90: if page == nil then flickerstreak@90: -- get the effective ID flickerstreak@90: return self.frame.action -- kept up-to-date by Blizzard's ActionButton_CalculateAction() flickerstreak@90: else flickerstreak@90: if page == 1 then flickerstreak@90: return self.config.actionID flickerstreak@90: else flickerstreak@90: return self.config.pageactions and self.config.pageactions[page] or self.config.actionID flickerstreak@88: end flickerstreak@88: end flickerstreak@88: end flickerstreak@88: flickerstreak@90: function Button:SetActionID( id, page ) flickerstreak@90: id = tonumber(id) flickerstreak@90: page = tonumber(page) flickerstreak@90: if id == nil or id < 1 or id > 120 then flickerstreak@90: error("Button:SetActionID - invalid action ID") flickerstreak@90: end flickerstreak@90: if page and page ~= 1 then flickerstreak@90: if not self.config.pageactions then flickerstreak@90: self.config.pageactions = { } flickerstreak@90: end flickerstreak@90: if self.config.pageactions[page] then flickerstreak@90: IDAlloc:Release(self.config.pageactions[page]) flickerstreak@90: end flickerstreak@90: self.config.pageactions[page] = id flickerstreak@90: IDAlloc:Acquire(self.config.pageactions[page]) flickerstreak@90: self.frame:SetAttribute(("*action-page%d"):format(page),id) flickerstreak@90: else flickerstreak@90: IDAlloc:Release(self.config.actionID) flickerstreak@90: self.config.actionID = id flickerstreak@90: IDAlloc:Acquire(self.config.actionID) flickerstreak@90: self.frame:SetAttribute("action",id) flickerstreak@90: if self.config.pageactions then flickerstreak@90: self.config.pageactions[1] = id flickerstreak@90: self.frame:SetAttribute("*action-page1",id) flickerstreak@90: end flickerstreak@88: end flickerstreak@88: end flickerstreak@88: flickerstreak@90: function Button:RefreshPages( force ) flickerstreak@90: local nPages = self.barConfig.nPages flickerstreak@90: if nPages and (nPages ~= self.nPages or force) then flickerstreak@90: local f = self:GetFrame() flickerstreak@90: local c = self.config.pageactions flickerstreak@90: if nPages > 1 and not c then flickerstreak@90: c = { } flickerstreak@90: self.config.pageactions = c flickerstreak@90: end flickerstreak@90: for i = 1, nPages do flickerstreak@90: if i > 1 then flickerstreak@90: c[i] = IDAlloc:Acquire(c[i], self.config.actionID + (i-1)*self.bar:GetNumButtons()) flickerstreak@90: else flickerstreak@90: c[i] = self.config.actionID -- page 1 is the same as the base actionID flickerstreak@90: end flickerstreak@90: f:SetAttribute(("*action-page%d"):format(i),c[i]) flickerstreak@90: end flickerstreak@90: for i = nPages+1, #c do flickerstreak@90: IDAlloc:Release(c[i]) flickerstreak@90: c[i] = nil flickerstreak@90: f:SetAttribute(("*action-page%d"):format(i),nil) flickerstreak@90: end flickerstreak@90: self.nPages = nPages flickerstreak@88: end flickerstreak@88: end flickerstreak@88: flickerstreak@90: function Button:ShowGrid( show ) flickerstreak@90: if not InCombatLockdown() then flickerstreak@90: local f = self.frame flickerstreak@90: local count = f:GetAttribute("showgrid") flickerstreak@90: if show then flickerstreak@90: count = count + 1 flickerstreak@90: else flickerstreak@90: count = count - 1 flickerstreak@88: end flickerstreak@90: if count < 0 then flickerstreak@90: count = 0 flickerstreak@88: end flickerstreak@90: f:SetAttribute("showgrid",count) flickerstreak@90: flickerstreak@90: if count >= 1 and not f:GetAttribute("statehidden") then flickerstreak@90: self.normalTexture:SetVertexColor(1.0, 1.0, 1.0, 0.5); flickerstreak@90: f:Show() flickerstreak@90: elseif count < 1 and not HasAction(self:GetActionID()) then flickerstreak@90: f:Hide() flickerstreak@90: end flickerstreak@88: end flickerstreak@88: end flickerstreak@88: flickerstreak@90: function Button:ShowActionIDLabel( show ) flickerstreak@90: local f = self:GetFrame() flickerstreak@90: if show then flickerstreak@90: local id = self:GetActionID() flickerstreak@90: if not f.actionIDLabel then flickerstreak@90: local label = f:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") flickerstreak@90: label:SetAllPoints() flickerstreak@90: label:SetJustifyH("CENTER") flickerstreak@90: label:SetShadowColor(0,0,0,1) flickerstreak@90: label:SetShadowOffset(2,-2) flickerstreak@90: f.actionIDLabel = label -- store the label with the frame for recycling flickerstreak@90: flickerstreak@90: f:HookScript("OnAttributeChanged", flickerstreak@90: function(frame, attr, value) flickerstreak@90: if label:IsVisible() and attr:match("action") then flickerstreak@90: label:SetText(tostring(frame.action)) flickerstreak@90: end flickerstreak@90: end) flickerstreak@88: end flickerstreak@90: f.actionIDLabel:SetText(tostring(id)) flickerstreak@90: f.actionIDLabel:Show() flickerstreak@90: elseif f.actionIDLabel then flickerstreak@90: f.actionIDLabel:Hide() flickerstreak@88: end flickerstreak@88: end flickerstreak@88: flickerstreak@90: function Button:DisplayHotkey( key ) flickerstreak@90: self.hotkey:SetText(key or "") flickerstreak@88: end flickerstreak@88: end