flickerstreak@122: local ReAction = ReAction flickerstreak@122: local L = ReAction.L flickerstreak@122: local _G = _G flickerstreak@122: local CreateFrame = CreateFrame flickerstreak@122: local floor = math.floor flickerstreak@122: local fmod = math.fmod flickerstreak@122: local format = string.format flickerstreak@122: flickerstreak@122: ReAction:UpdateRevision("$Revision$") flickerstreak@122: flickerstreak@147: local KB = LibStub("LibKeyBound-1.0") flickerstreak@147: flickerstreak@147: ---- Bar class ---- flickerstreak@122: local Bar = { } flickerstreak@122: local weak = { __mode = "k" } flickerstreak@147: local frameList = { } flickerstreak@122: flickerstreak@122: ReAction.Bar = Bar -- export to ReAction flickerstreak@122: flickerstreak@122: function Bar:New( name, config ) flickerstreak@122: if type(config) ~= "table" then flickerstreak@122: error("ReAction.Bar: config table required") flickerstreak@122: end flickerstreak@122: flickerstreak@122: -- create new self flickerstreak@122: self = setmetatable( flickerstreak@122: { flickerstreak@122: config = config, flickerstreak@122: name = name, flickerstreak@122: buttons = setmetatable( { }, weak ), flickerstreak@122: width = config.width or 480, flickerstreak@147: height = config.height or 40, flickerstreak@122: }, flickerstreak@147: {__index = self} ) flickerstreak@122: flickerstreak@122: -- The frame type is 'Button' in order to have an OnClick handler. However, the frame itself is flickerstreak@122: -- not mouse-clickable by the user. flickerstreak@122: local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent flickerstreak@147: name = name and "ReAction-"..name flickerstreak@147: local f = name and frameList[name] flickerstreak@147: if not f then flickerstreak@147: f = CreateFrame("Button", name, parent, "SecureHandlerStateTemplate, SecureHandlerClickTemplate") flickerstreak@147: if name then flickerstreak@147: frameList[name] = f flickerstreak@147: end flickerstreak@147: end flickerstreak@122: f:SetFrameStrata("MEDIUM") flickerstreak@122: f:SetWidth(self.width) flickerstreak@146: f:SetHeight(self.height) flickerstreak@122: f:SetAlpha(config.alpha or 1.0) flickerstreak@122: f:Show() flickerstreak@122: f:EnableMouse(false) flickerstreak@122: f:SetClampedToScreen(true) flickerstreak@122: flickerstreak@122: f:SetAttribute("_onstate-showgrid", flickerstreak@122: -- function(self,stateid,newstate) flickerstreak@122: [[ flickerstreak@122: control:ChildUpdate(stateid,newstate) flickerstreak@122: control:CallMethod("UpdateShowGrid") flickerstreak@122: ]]) flickerstreak@122: f.UpdateShowGrid = function(frame) flickerstreak@122: for button in self:IterateButtons() do flickerstreak@122: button:UpdateShowGrid() flickerstreak@122: end flickerstreak@122: end flickerstreak@122: ReAction.gridProxy:AddFrame(f) flickerstreak@122: flickerstreak@122: -- Override the default frame accessor to provide strict read-only access flickerstreak@122: function self:GetFrame() flickerstreak@122: return f flickerstreak@122: end flickerstreak@122: flickerstreak@122: self:ApplyAnchor() flickerstreak@147: self:SetConfigMode(ReAction:GetConfigMode()) flickerstreak@147: self:SetKeybindMode(ReAction:GetKeybindMode()) flickerstreak@147: flickerstreak@122: ReAction.RegisterCallback(self, "OnConfigModeChanged") flickerstreak@147: KB.RegisterCallback(self, "LIBKEYBOUND_ENABLED") flickerstreak@147: KB.RegisterCallback(self, "LIBKEYBOUND_DISABLED") flickerstreak@147: KB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED") flickerstreak@122: flickerstreak@122: return self flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:Destroy() flickerstreak@122: local f = self:GetFrame() flickerstreak@122: f:UnregisterAllEvents() flickerstreak@125: self:ShowControls(false) flickerstreak@122: ReAction.UnregisterAllCallbacks(self) flickerstreak@147: KB.UnregisterAllCallbacks(self) flickerstreak@122: ReAction.gridProxy:RemoveFrame(f) flickerstreak@122: f:SetParent(UIParent) flickerstreak@122: f:ClearAllPoints() flickerstreak@147: f:Hide() flickerstreak@122: end flickerstreak@122: flickerstreak@147: -- flickerstreak@147: -- Events flickerstreak@147: -- flickerstreak@147: flickerstreak@147: function Bar:OnConfigModeChanged(event, mode) flickerstreak@147: self:SetConfigMode(mode) flickerstreak@123: end flickerstreak@123: flickerstreak@147: function Bar:LIBKEYBOUND_ENABLED(evt) flickerstreak@147: self:SetKeybindMode(true) flickerstreak@147: end flickerstreak@147: flickerstreak@147: function Bar:LIBKEYBOUND_DISABLED(evt) flickerstreak@147: self:SetKeybindMode(false) flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:ApplyAnchor() flickerstreak@122: local f = self:GetFrame() flickerstreak@122: local c = self.config flickerstreak@122: local p = c.point flickerstreak@122: flickerstreak@122: f:SetWidth(c.width) flickerstreak@122: f:SetHeight(c.height) flickerstreak@122: f:ClearAllPoints() flickerstreak@122: flickerstreak@122: if p then flickerstreak@122: local a = f:GetParent() flickerstreak@122: if c.anchor then flickerstreak@122: local bar = ReAction:GetBar(c.anchor) flickerstreak@122: if bar then flickerstreak@122: a = bar:GetFrame() flickerstreak@122: else flickerstreak@122: a = _G[c.anchor] flickerstreak@122: end flickerstreak@122: end flickerstreak@122: local fr = a or f:GetParent() flickerstreak@122: f:SetPoint(p, a or f:GetParent(), c.relpoint, c.x or 0, c.y or 0) flickerstreak@122: else flickerstreak@122: f:SetPoint("CENTER") flickerstreak@122: end flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:SetAnchor(point, frame, relativePoint, x, y) flickerstreak@122: local c = self.config flickerstreak@122: c.point = point or c.point flickerstreak@122: c.anchor = frame or c.anchor flickerstreak@122: c.relpoint = relativePoint or c.relpoint flickerstreak@122: c.x = x or c.x flickerstreak@122: c.y = y or c.y flickerstreak@122: self:ApplyAnchor() flickerstreak@122: ReAction:RefreshBar(self) flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:GetAnchor() flickerstreak@122: local c = self.config flickerstreak@122: return (c.point or "CENTER"), flickerstreak@122: (c.anchor or self:GetFrame():GetParent():GetName()), flickerstreak@122: (c.relpoint or c.point or "CENTER"), flickerstreak@122: (c.x or 0), flickerstreak@122: (c.y or 0) flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:GetSize() flickerstreak@122: local f = self:GetFrame() flickerstreak@122: return f:GetWidth(), f:GetHeight() flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:SetSize(w,h) flickerstreak@122: local f = self:GetFrame() flickerstreak@122: self.config.width = w flickerstreak@122: self.config.height = h flickerstreak@122: f:SetWidth(w) flickerstreak@122: f:SetHeight(h) flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:GetButtonSize() flickerstreak@122: local w = self.config.btnWidth or 32 flickerstreak@122: local h = self.config.btnHeight or 32 flickerstreak@122: -- TODO: get from modules? flickerstreak@122: return w,h flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:SetButtonSize(w,h) flickerstreak@122: if w > 0 and h > 0 then flickerstreak@122: self.config.btnWidth = w flickerstreak@122: self.config.btnHeight = h flickerstreak@122: end flickerstreak@122: ReAction:RefreshBar(self) flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:GetButtonGrid() flickerstreak@122: local cfg = self.config flickerstreak@122: local r = cfg.btnRows or 1 flickerstreak@122: local c = cfg.btnColumns or 1 flickerstreak@122: local s = cfg.spacing or 4 flickerstreak@122: return r,c,s flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:GetNumButtons() flickerstreak@122: local r,c = self:GetButtonGrid() flickerstreak@122: return r*c flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:SetButtonGrid(r,c,s) flickerstreak@122: if r > 0 and c > 0 and s > 0 then flickerstreak@122: local cfg = self.config flickerstreak@122: cfg.btnRows = r flickerstreak@122: cfg.btnColumns = c flickerstreak@122: cfg.spacing = s flickerstreak@122: end flickerstreak@122: ReAction:RefreshBar(self) flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:ClipNButtons( n ) flickerstreak@122: local cfg = self.config flickerstreak@122: local r = cfg.btnRows or 1 flickerstreak@122: local c = cfg.btnColumns or 1 flickerstreak@122: flickerstreak@122: cfg.btnRows = ceil(n/c) flickerstreak@122: cfg.btnColumns = min(n,c) flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:GetName() flickerstreak@122: return self.name flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:GetFrame() flickerstreak@122: -- this method is included for documentation purposes. It is overridden flickerstreak@122: -- for each object in the :New() method. flickerstreak@122: error("Invalid Bar object: used without initialization") flickerstreak@122: end flickerstreak@122: flickerstreak@122: -- only ReAction:RenameBar() should call this function. Calling from any other flickerstreak@122: -- context will desync the bar list in the ReAction class. flickerstreak@122: function Bar:SetName(name) flickerstreak@122: self.name = name flickerstreak@122: self:SetLabel(self.name) -- Bar:SetLabel() defined in Overlay.lua flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:GetAlpha() flickerstreak@122: return self.config.alpha or 1.0 flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:SetAlpha(value) flickerstreak@122: self.config.alpha = value flickerstreak@122: self:GetFrame():SetAlpha(value or 1.0) flickerstreak@122: ReAction:RefreshBar(self) flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:AddButton(idx, button) flickerstreak@122: local f = self:GetFrame() flickerstreak@122: flickerstreak@122: -- store in a weak reverse-index array flickerstreak@122: self.buttons[button] = idx flickerstreak@122: flickerstreak@122: -- Store a properly wrapped reference to the child frame as an attribute flickerstreak@122: -- (accessible via "frameref-btn#") flickerstreak@122: f:SetFrameRef(format("btn%d",idx), button:GetFrame()) flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:RemoveButton(button) flickerstreak@122: local idx = self.buttons[button] flickerstreak@122: if idx then flickerstreak@122: self:GetFrame():SetAttribute(format("frameref-btn%d",idx),nil) flickerstreak@122: self.buttons[button] = nil flickerstreak@122: end flickerstreak@122: end flickerstreak@122: flickerstreak@122: -- iterator returns button, idx and does NOT iterate in index order flickerstreak@122: function Bar:IterateButtons() flickerstreak@122: return pairs(self.buttons) flickerstreak@122: end flickerstreak@122: flickerstreak@147: function Bar:SetConfigMode(mode) flickerstreak@147: self:ShowControls(mode) flickerstreak@148: if self.unitwatch then flickerstreak@148: if mode then flickerstreak@148: UnregisterUnitWatch(self:GetFrame()) flickerstreak@148: self:GetFrame():Show() flickerstreak@148: else flickerstreak@148: RegisterUnitWatch(self:GetFrame()) flickerstreak@148: end flickerstreak@148: end flickerstreak@147: for b in self:IterateButtons() do flickerstreak@147: b:ShowGridTemp(mode) flickerstreak@147: b:UpdateActionIDLabel(mode) flickerstreak@147: end flickerstreak@147: end flickerstreak@147: flickerstreak@147: function Bar:SetKeybindMode(mode) flickerstreak@148: if self.unitwatch then flickerstreak@148: if mode then flickerstreak@148: UnregisterUnitWatch(self:GetFrame()) flickerstreak@148: self:GetFrame():Show() flickerstreak@148: else flickerstreak@148: RegisterUnitWatch(self:GetFrame()) flickerstreak@148: end flickerstreak@148: end flickerstreak@147: for b in self:IterateButtons() do flickerstreak@147: b:SetKeybindMode(mode) flickerstreak@147: end flickerstreak@147: end flickerstreak@147: flickerstreak@122: function Bar:PlaceButton(button, baseW, baseH) flickerstreak@122: local idx = self.buttons[button] flickerstreak@122: if idx then flickerstreak@122: local r, c, s = self:GetButtonGrid() flickerstreak@122: local bh, bw = self:GetButtonSize() flickerstreak@122: local row, col = floor((idx-1)/c), fmod((idx-1),c) -- zero-based flickerstreak@122: local x, y = col*bw + (col+0.5)*s, -(row*bh + (row+0.5)*s) flickerstreak@122: local scale = bw/baseW flickerstreak@122: local b = button:GetFrame() flickerstreak@122: flickerstreak@122: b:ClearAllPoints() flickerstreak@122: b:SetPoint("TOPLEFT",x/scale,y/scale) flickerstreak@122: b:SetScale(scale) flickerstreak@122: end flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:SkinButton() flickerstreak@122: -- does nothing by default flickerstreak@147: end flickerstreak@148: flickerstreak@148: -- pass unit=nil to set up the unit elsewhere, if you want something more complex flickerstreak@148: function Bar:RegisterUnitWatch( unit, enable ) flickerstreak@148: local f = self:GetFrame() flickerstreak@148: if unit then flickerstreak@148: f:SetAttribute("unit",unit) flickerstreak@148: end flickerstreak@148: if not ReAction:GetConfigMode() then flickerstreak@148: if enable then flickerstreak@148: RegisterUnitWatch(f) flickerstreak@148: elseif self.unitwatch then flickerstreak@148: UnregisterUnitWatch(f) flickerstreak@148: end flickerstreak@148: end flickerstreak@148: self.unitwatch = enable flickerstreak@148: end flickerstreak@148: