Flick@276: local _, ns = ... Flick@276: local ReAction = ns.ReAction flickerstreak@122: local L = ReAction.L flickerstreak@183: local LKB = ReAction.LKB flickerstreak@122: local _G = _G flickerstreak@122: local CreateFrame = CreateFrame flickerstreak@122: local GetBindingKey = GetBindingKey flickerstreak@122: local format = string.format flickerstreak@122: flickerstreak@122: -- private flickerstreak@122: local trash = CreateFrame("Frame") flickerstreak@122: local frameList = { } flickerstreak@122: local idPools = { } flickerstreak@122: flickerstreak@122: local function kb_onEnter( frame ) flickerstreak@183: LKB:Set(frame) flickerstreak@122: end flickerstreak@122: flickerstreak@122: -- Button class flickerstreak@218: local buttonTypeID = "Button" flickerstreak@218: local Button = { flickerstreak@218: defaultBarConfig = { flickerstreak@218: type = buttonTypeID, flickerstreak@218: btnWidth = 36, flickerstreak@218: btnHeight = 36, flickerstreak@218: btnRows = 1, flickerstreak@218: btnColumns = 12, flickerstreak@218: spacing = 3 flickerstreak@218: }, Flick@277: -- barType = L["Button Bar"], -- derived classes must declare Flick@277: -- buttonTypeID = "Button" -- derived classes must declare flickerstreak@218: } flickerstreak@122: flickerstreak@122: ReAction.Button = Button -- export to ReAction flickerstreak@122: Flick@277: function Button:New( config, bar, idx, inherits, buttonType ) flickerstreak@122: buttonType = buttonType or "CheckButton" flickerstreak@122: Flick@277: local name = format("ReAction_%s_%s_%d",bar:GetName(), self.buttonTypeID, idx) Flick@277: flickerstreak@122: -- create new self flickerstreak@122: self = setmetatable( flickerstreak@122: { flickerstreak@122: bar = bar, flickerstreak@122: idx = idx, flickerstreak@129: config = config, flickerstreak@122: name = name, flickerstreak@122: }, flickerstreak@122: { __index = self } ) flickerstreak@122: flickerstreak@122: -- have to recycle frames with the same name: CreateFrame() doesn't overwrite flickerstreak@122: -- existing globals. Can't set to nil in the global because it's then tainted. flickerstreak@122: -- Caller is responsible for ensuring global uniqueness of names. flickerstreak@122: local f = name and frameList[name] flickerstreak@122: if f then flickerstreak@122: f:SetParent(bar:GetFrame()) flickerstreak@122: else flickerstreak@122: f = CreateFrame(buttonType, name, bar:GetFrame(), inherits) flickerstreak@122: if name then flickerstreak@122: frameList[name] = f flickerstreak@122: end flickerstreak@122: end flickerstreak@122: flickerstreak@122: self.frame = f flickerstreak@122: flickerstreak@131: local frames = { } flickerstreak@131: self.frames = frames flickerstreak@131: frames.icon = _G[name.."Icon"] flickerstreak@131: frames.flash = _G[name.."Flash"] flickerstreak@131: frames.hotkey = _G[name.."HotKey"] flickerstreak@131: frames.count = _G[name.."Count"] flickerstreak@131: frames.name = _G[name.."Name"] flickerstreak@131: frames.border = _G[name.."Border"] flickerstreak@131: frames.cooldown = _G[name.."Cooldown"] flickerstreak@131: frames.normalTexture = _G[name.."NormalTexture"] flickerstreak@131: flickerstreak@122: if config then flickerstreak@122: config.name = name flickerstreak@122: end flickerstreak@122: flickerstreak@122: -- install LibKeyBound handlers onto frame flickerstreak@122: function f:GetActionName() flickerstreak@122: return format("%s:%s", bar:GetName(), idx) flickerstreak@122: end flickerstreak@122: Flick@259: -- mouse and clicking Flick@259: -- set click handlers in subclasses Flick@259: f:EnableMouse(true) Flick@259: f:RegisterForClicks( bar:GetConfig().clickDown and "AnyDown" or "AnyUp" ) flickerstreak@122: local clickBinding = format("CLICK %s:LeftButton", name) flickerstreak@122: function f:GetHotkey() flickerstreak@183: return LKB:ToShortKey(GetBindingKey(clickBinding)) flickerstreak@122: end flickerstreak@122: flickerstreak@122: return self flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Button:Destroy() flickerstreak@126: local f = self:GetFrame() flickerstreak@132: f:UnregisterAllEvents() flickerstreak@132: self:ReleaseActionID(self:GetActionID()) flickerstreak@126: if f then flickerstreak@126: f:Hide() flickerstreak@126: f:SetParent(trash) flickerstreak@126: f:ClearAllPoints() flickerstreak@126: end flickerstreak@122: end flickerstreak@122: flickerstreak@128: function Button:GetBar() flickerstreak@128: return self.bar flickerstreak@128: end flickerstreak@128: flickerstreak@122: function Button:GetFrame() flickerstreak@122: return self.frame flickerstreak@122: end flickerstreak@122: Flick@234: function Button:GetIndex() Flick@234: return self.idx Flick@234: end Flick@234: flickerstreak@122: function Button:GetName() flickerstreak@122: return self.name flickerstreak@122: end flickerstreak@122: flickerstreak@218: function Button:GetDefaultBarConfig() flickerstreak@218: return self.defaultBarConfig flickerstreak@218: end flickerstreak@218: flickerstreak@218: function Button:GetBarType() flickerstreak@218: return self.barType flickerstreak@218: end flickerstreak@218: Flick@231: function Button:GetButtonTypeID() Flick@231: return self.buttonTypeID Flick@231: end Flick@231: flickerstreak@122: function Button:GetConfig() flickerstreak@122: return self.config flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Button:GetActionID() flickerstreak@122: -- derived classes should override this flickerstreak@122: return nil flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Button:SetActionIDPool( poolID, maxID ) flickerstreak@122: self.actionPoolID = poolID flickerstreak@122: self.actionMaxID = maxID flickerstreak@122: end flickerstreak@122: Flick@234: function Button:SetupBar( bar ) Flick@234: local config = bar:GetConfig() Flick@234: if not config.buttons then Flick@234: config.buttons = { } Flick@234: end Flick@234: local btnCfg = config.buttons Flick@234: Flick@234: local r, c = bar:GetButtonGrid() Flick@234: local n = r*c Flick@235: local cfgN = n Flick@234: Flick@234: local hint = nil Flick@234: local i = 1 Flick@234: repeat Flick@234: local b = bar:GetButton(i) Flick@234: if b then Flick@234: if i > n then Flick@234: bar:RemoveButton(b) Flick@234: b:Destroy() Flick@235: if i > cfgN then Flick@235: btnCfg[i] = nil Flick@235: end Flick@234: else Flick@234: b:Refresh() Flick@234: hint = b:GetActionID() Flick@234: end Flick@234: elseif i <= n then Flick@234: local cfg = btnCfg[i] or { } Flick@234: local success, r = pcall(self.New, self, cfg, bar, i, hint) -- note call semantics for derived class constructors Flick@234: if success and r then Flick@234: b = r Flick@234: bar:AddButton(i,b) Flick@234: btnCfg[i] = cfg Flick@234: b:Refresh() Flick@234: hint = b:GetActionID() Flick@234: else Flick@234: n = i - 1 Flick@234: if not success then Flick@236: bar:ClipNButtons(n) Flick@236: cfgN = n Flick@279: if r then Flick@279: geterrorhandler()(r) Flick@279: end Flick@234: end Flick@234: end Flick@234: end Flick@234: i = i + 1 Flick@234: until b == nil Flick@234: end Flick@234: flickerstreak@122: function Button:AcquireActionID( id, hint, unique ) flickerstreak@122: local poolID = self.actionPoolID flickerstreak@122: local maxID = self.actionMaxID flickerstreak@122: if not poolID or not maxID then flickerstreak@122: error("AcquireActionID: must setup pool first with SetActionIDPool") flickerstreak@122: end Flick@299: hint = tonumber(hint) Flick@299: if hint and (hint < 1 or hint > maxID) then Flick@299: hint = nil Flick@299: end flickerstreak@123: local pool = idPools[poolID] flickerstreak@122: if not pool then flickerstreak@122: pool = { nWraps = 0, useCount = { } } flickerstreak@122: for i = 1, maxID do flickerstreak@122: pool.useCount[i] = 0 flickerstreak@122: end flickerstreak@123: idPools[poolID] = pool flickerstreak@122: end flickerstreak@122: local useCount = pool.useCount flickerstreak@122: if id == nil then flickerstreak@122: repeat flickerstreak@126: local nWraps = pool.nWraps or 0 Flick@299: if hint and (useCount[hint] == 0 or useCount[hint] == nWraps) then flickerstreak@122: id = hint flickerstreak@122: else flickerstreak@122: local start = hint or 1 flickerstreak@122: for i = start, maxID do Flick@299: if useCount[i] == 0 or useCount[i] == nWraps then flickerstreak@122: id = i flickerstreak@122: break flickerstreak@122: end flickerstreak@122: end flickerstreak@122: if not id then flickerstreak@122: for i = 1, start do Flick@299: if useCount[i] == 0 or useCount[i] == nWraps then flickerstreak@122: id = i flickerstreak@122: break flickerstreak@122: end flickerstreak@122: end flickerstreak@122: end flickerstreak@122: end flickerstreak@122: if id == nil then flickerstreak@122: if unique then Flick@282: ReAction:UserError(L["All action IDs for bars of type '%s' are in use, cannot create any more buttons"]:format(self.barType)) Flick@279: error(nil) -- no error message, user has already been notified, so don't put in Lua error handler flickerstreak@122: end flickerstreak@122: pool.nWraps = nWraps + 1 flickerstreak@122: end flickerstreak@126: until id ~= nil flickerstreak@122: end flickerstreak@122: useCount[id] = (useCount[id] or 0) + 1 flickerstreak@122: return id flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Button:ReleaseActionID( id ) flickerstreak@122: local poolID = self.actionPoolID flickerstreak@122: if not poolID then flickerstreak@122: error("ReleaseActionID: must setup pool first with SetActionIDPool") flickerstreak@122: end flickerstreak@123: local pool = idPools[poolID] flickerstreak@122: if pool and id and pool.useCount[id] then flickerstreak@122: pool.useCount[id] = pool.useCount[id] - 1 flickerstreak@122: pool.nWraps = min(pool.useCount[id], pool.nWraps) flickerstreak@122: end flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Button:Refresh() flickerstreak@129: local f = self:GetFrame() flickerstreak@129: self.bar:PlaceButton( self, f:GetWidth(), f:GetHeight() ) flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Button:SetKeybindMode( mode ) flickerstreak@122: local f = self.frame flickerstreak@122: if mode then flickerstreak@122: self.oldOnEnter = f:GetScript("OnEnter") flickerstreak@122: f:SetScript("OnEnter", kb_onEnter) flickerstreak@124: elseif self.oldOnEnter then flickerstreak@122: f:SetScript("OnEnter", self.oldOnEnter) flickerstreak@122: self.oldOnEnter = nil flickerstreak@122: end flickerstreak@128: self:ShowGridTemp(mode) flickerstreak@122: self:UpdateKeybindModeDisplay( mode ) flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Button:UpdateKeybindModeDisplay( mode ) flickerstreak@131: local border = self.frames.border or _G[format("%sBorder",tostring(self:GetName()))] flickerstreak@131: if border then flickerstreak@122: if mode then flickerstreak@183: border:SetVertexColor(LKB:GetColorKeyBoundMode()) flickerstreak@131: border:Show() flickerstreak@122: else flickerstreak@131: border:Hide() flickerstreak@122: end flickerstreak@122: end flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Button:UpdateHotkey( hotkey ) flickerstreak@131: hotkey = hotkey or self.frames.hotkey flickerstreak@122: if not hotkey then flickerstreak@129: hotkey = _G[self:GetName().."HotKey"] flickerstreak@131: self.frames.hotkey = hotkey flickerstreak@122: end flickerstreak@122: if hotkey then flickerstreak@122: local txt = self.frame:GetHotkey() flickerstreak@122: hotkey:SetText( txt ) flickerstreak@122: if txt == nil or txt == "" then flickerstreak@122: hotkey:Hide() flickerstreak@122: else flickerstreak@122: hotkey:Show() flickerstreak@122: end flickerstreak@122: end flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Button:GetActionIDLabel( create ) flickerstreak@129: local f = self:GetFrame() flickerstreak@122: if not f.actionIDLabel and create then flickerstreak@122: local label = f:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") flickerstreak@122: label:SetAllPoints() flickerstreak@122: label:SetJustifyH("CENTER") flickerstreak@122: label:SetShadowColor(0,0,0,1) flickerstreak@122: label:SetShadowOffset(2,-2) flickerstreak@122: f.actionIDLabel = label -- store the label with the frame for recycling flickerstreak@122: end flickerstreak@122: return f.actionIDLabel flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Button:UpdateActionIDLabel( show ) flickerstreak@122: local label = self:GetActionIDLabel( show ) flickerstreak@122: if label then flickerstreak@122: if show then flickerstreak@122: local id = self:GetActionID() flickerstreak@122: if id then flickerstreak@122: label:SetText(tostring(id)) flickerstreak@122: label:Show() flickerstreak@122: return flickerstreak@122: end flickerstreak@122: end flickerstreak@122: label:Hide() flickerstreak@122: end flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Button:SetNormalVertexColor( r, g, b, a ) flickerstreak@213: if ReAction.LBF then flickerstreak@213: ReAction.LBF:SetNormalVertexColor(self:GetFrame(), r, g, b, a) flickerstreak@122: else flickerstreak@129: self:GetFrame():GetNormalTexture():SetVertexColor(r,g,b,a) flickerstreak@122: end flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Button:GetNormalVertexColor() flickerstreak@213: if ReAction.LBF then flickerstreak@213: return ReAction.LBF:GetNormalVertexColor(self:GetFrame()) flickerstreak@122: else flickerstreak@129: return self:GetFrame():GetNormalTexture():GetVertexColor() flickerstreak@122: end flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Button:UpdateShowGrid() flickerstreak@122: -- does nothing by default flickerstreak@122: end flickerstreak@128: flickerstreak@128: function Button:ShowGridTemp(show) flickerstreak@128: -- does nothing by default flickerstreak@128: end flickerstreak@128: flickerstreak@128: function Button:ShowGrid(show) flickerstreak@128: -- does nothing by default flickerstreak@128: end