flickerstreak@25: local ReAction = ReAction flickerstreak@25: local L = ReAction.L flickerstreak@25: local _G = _G flickerstreak@25: local CreateFrame = CreateFrame flickerstreak@33: local floor = math.floor flickerstreak@75: local fmod = math.fmod flickerstreak@75: local format = string.format flickerstreak@33: flickerstreak@77: ReAction:UpdateRevision("$Revision$") flickerstreak@25: flickerstreak@90: local Bar = { } flickerstreak@90: local proto = { __index = Bar } flickerstreak@90: local weak = { __mode = "k" } flickerstreak@75: flickerstreak@77: ReAction.Bar = Bar -- export to ReAction flickerstreak@25: flickerstreak@77: function Bar:New( name, config ) flickerstreak@25: if type(config) ~= "table" then flickerstreak@28: error("ReAction.Bar: config table required") flickerstreak@25: end flickerstreak@75: flickerstreak@90: -- create new self flickerstreak@90: self = setmetatable( flickerstreak@90: { flickerstreak@90: config = config, flickerstreak@90: name = name, flickerstreak@90: buttons = setmetatable( { }, weak ), flickerstreak@90: width = config.width or 480, flickerstreak@90: height = config.height or 40 flickerstreak@90: }, flickerstreak@90: proto ) flickerstreak@90: flickerstreak@90: -- The frame type is 'Button' in order to have an OnClick handler. However, the frame itself is flickerstreak@90: -- not mouse-clickable by the user. flickerstreak@90: local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent flickerstreak@90: local f = CreateFrame("Button", name and format("ReAction-%s",name), parent, flickerstreak@90: "SecureHandlerStateTemplate, SecureHandlerClickTemplate") flickerstreak@90: f:SetFrameStrata("MEDIUM") flickerstreak@90: f:SetWidth(self.width) flickerstreak@90: f:SetWidth(self.height) flickerstreak@90: f:Show() flickerstreak@90: f:EnableMouse(false) flickerstreak@90: f:SetClampedToScreen(true) flickerstreak@25: flickerstreak@90: -- Override the default frame accessor to provide strict read-only access flickerstreak@77: function self:GetFrame() flickerstreak@77: return f flickerstreak@77: end flickerstreak@77: flickerstreak@75: self:ApplyAnchor() flickerstreak@63: ReAction.RegisterCallback(self, "OnConfigModeChanged") flickerstreak@77: flickerstreak@77: return self flickerstreak@25: end flickerstreak@25: flickerstreak@25: function Bar:Destroy() flickerstreak@75: local f = self:GetFrame() flickerstreak@25: f:UnregisterAllEvents() flickerstreak@90: ReAction.UnregisterAllCallbacks(self) flickerstreak@25: f:Hide() flickerstreak@25: f:SetParent(UIParent) flickerstreak@25: f:ClearAllPoints() flickerstreak@25: end flickerstreak@25: flickerstreak@63: function Bar:OnConfigModeChanged(event, mode) flickerstreak@75: self:ShowControls(mode) -- Bar:ShowControls() defined in Overlay.lua flickerstreak@25: end flickerstreak@25: flickerstreak@25: function Bar:ApplyAnchor() flickerstreak@90: local f = self:GetFrame() flickerstreak@90: local c = self.config flickerstreak@90: local p = c.point flickerstreak@90: flickerstreak@90: f:SetWidth(c.width) flickerstreak@90: f:SetHeight(c.height) flickerstreak@51: f:ClearAllPoints() flickerstreak@90: flickerstreak@90: if p then flickerstreak@90: local a = f:GetParent() flickerstreak@90: if c.anchor then flickerstreak@90: local bar = ReAction:GetBar(c.anchor) flickerstreak@52: if bar then flickerstreak@90: a = bar:GetFrame() flickerstreak@52: else flickerstreak@90: a = _G[c.anchor] flickerstreak@52: end flickerstreak@25: end flickerstreak@90: local fr = a or f:GetParent() flickerstreak@90: f:SetPoint(p, a or f:GetParent(), c.relpoint, c.x or 0, c.y or 0) flickerstreak@25: else flickerstreak@25: f:SetPoint("CENTER") flickerstreak@25: end flickerstreak@25: end flickerstreak@25: flickerstreak@51: function Bar:SetAnchor(point, frame, relativePoint, x, y) flickerstreak@51: local c = self.config flickerstreak@75: c.point = point or c.point flickerstreak@75: c.anchor = frame and frame:GetName() or c.anchor flickerstreak@75: c.relpoint = relativePoint or c.relpoint flickerstreak@51: c.x = x or c.x flickerstreak@51: c.y = y or c.y flickerstreak@51: self:ApplyAnchor() flickerstreak@51: end flickerstreak@51: flickerstreak@51: function Bar:GetAnchor() flickerstreak@51: local c = self.config flickerstreak@90: return (c.point or "CENTER"), flickerstreak@90: (c.anchor or self:GetFrame():GetParent():GetName()), flickerstreak@90: (c.relpoint or c.point or "CENTER"), flickerstreak@90: (c.x or 0), flickerstreak@90: (c.y or 0) flickerstreak@25: end flickerstreak@25: flickerstreak@25: function Bar:GetSize() flickerstreak@75: local f = self:GetFrame() flickerstreak@75: return f:GetWidth(), f:GetHeight() flickerstreak@25: end flickerstreak@25: flickerstreak@25: function Bar:SetSize(w,h) flickerstreak@90: local f = self:GetFrame() flickerstreak@25: self.config.width = w flickerstreak@25: self.config.height = h flickerstreak@75: f:SetWidth(w) flickerstreak@75: f:SetHeight(h) flickerstreak@25: end flickerstreak@25: flickerstreak@25: function Bar:GetButtonSize() flickerstreak@25: local w = self.config.btnWidth or 32 flickerstreak@25: local h = self.config.btnHeight or 32 flickerstreak@25: -- TODO: get from modules? flickerstreak@25: return w,h flickerstreak@25: end flickerstreak@25: flickerstreak@25: function Bar:SetButtonSize(w,h) flickerstreak@25: if w > 0 and h > 0 then flickerstreak@25: self.config.btnWidth = w flickerstreak@25: self.config.btnHeight = h flickerstreak@25: end flickerstreak@75: ReAction:RefreshBar(self) flickerstreak@25: end flickerstreak@25: flickerstreak@25: function Bar:GetButtonGrid() flickerstreak@25: local cfg = self.config flickerstreak@25: local r = cfg.btnRows or 1 flickerstreak@25: local c = cfg.btnColumns or 1 flickerstreak@25: local s = cfg.spacing or 4 flickerstreak@25: return r,c,s flickerstreak@25: end flickerstreak@25: flickerstreak@86: function Bar:GetNumButtons() flickerstreak@86: local r,c = self:GetButtonGrid() flickerstreak@86: return r*c flickerstreak@86: end flickerstreak@86: flickerstreak@25: function Bar:SetButtonGrid(r,c,s) flickerstreak@25: if r > 0 and c > 0 and s > 0 then flickerstreak@25: local cfg = self.config flickerstreak@25: cfg.btnRows = r flickerstreak@25: cfg.btnColumns = c flickerstreak@25: cfg.spacing = s flickerstreak@25: end flickerstreak@75: ReAction:RefreshBar(self) flickerstreak@25: end flickerstreak@25: flickerstreak@94: function Bar:ClipNButtons( n ) flickerstreak@94: local cfg = self.config flickerstreak@94: local r = cfg.btnRows or 1 flickerstreak@94: local c = cfg.btnColumns or 1 flickerstreak@94: flickerstreak@94: cfg.btnRows = ceil(n/c) flickerstreak@94: cfg.btnColumns = min(n,c) flickerstreak@94: end flickerstreak@94: flickerstreak@25: function Bar:GetName() flickerstreak@25: return self.name flickerstreak@25: end flickerstreak@25: flickerstreak@77: function Bar:GetFrame() flickerstreak@77: -- this method is included for documentation purposes. It is overridden flickerstreak@90: -- for each object in the :New() method. flickerstreak@77: error("Invalid Bar object: used without initialization") flickerstreak@77: end flickerstreak@77: flickerstreak@90: -- only ReAction:RenameBar() should call this function. Calling from any other flickerstreak@90: -- context will desync the bar list in the ReAction class. flickerstreak@90: function Bar:SetName(name) flickerstreak@90: self.name = name flickerstreak@90: self:SetLabel(self.name) -- Bar:SetLabel() defined in Overlay.lua flickerstreak@77: end flickerstreak@77: flickerstreak@90: function Bar:AddButton(idx, button) flickerstreak@90: local f = self:GetFrame() flickerstreak@90: flickerstreak@90: -- store in a weak reverse-index array flickerstreak@90: self.buttons[button] = idx flickerstreak@90: flickerstreak@90: -- Store a properly wrapped reference to the child frame as an attribute flickerstreak@90: -- (accessible via "frameref-btn#") flickerstreak@90: f:SetFrameRef(format("btn%d",idx), button:GetFrame()) flickerstreak@90: end flickerstreak@90: flickerstreak@90: function Bar:RemoveButton(button) flickerstreak@90: local idx = self.buttons[button] flickerstreak@90: if idx then flickerstreak@90: self:GetFrame():SetAttribute(format("frameref-btn%d",idx),nil) flickerstreak@90: self.buttons[button] = nil flickerstreak@33: end flickerstreak@33: end flickerstreak@33: flickerstreak@90: -- iterator returns button, idx and does NOT iterate in index order flickerstreak@90: function Bar:IterateButtons() flickerstreak@75: return pairs(self.buttons) flickerstreak@75: end flickerstreak@75: flickerstreak@75: function Bar:PlaceButton(button, baseW, baseH) flickerstreak@75: local idx = self.buttons[button] flickerstreak@90: if idx then flickerstreak@90: local r, c, s = self:GetButtonGrid() flickerstreak@90: local bh, bw = self:GetButtonSize() flickerstreak@90: local row, col = floor((idx-1)/c), fmod((idx-1),c) -- zero-based flickerstreak@90: local x, y = col*bw + (col+0.5)*s, -(row*bh + (row+0.5)*s) flickerstreak@90: local scale = bw/baseW flickerstreak@90: local b = button:GetFrame() flickerstreak@25: flickerstreak@90: b:ClearAllPoints() flickerstreak@90: b:SetPoint("TOPLEFT",x/scale,y/scale) flickerstreak@90: b:SetScale(scale) flickerstreak@72: end flickerstreak@72: end flickerstreak@72: