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@71: local SecureStateHeader_Refresh = SecureStateHeader_Refresh flickerstreak@33: flickerstreak@33: flickerstreak@25: -- update ReAction revision if this file is newer flickerstreak@33: local revision = tonumber(("$Revision$"):match("%d+")) flickerstreak@25: if revision > ReAction.revision then flickerstreak@52: ReAction.revision = revision flickerstreak@25: end flickerstreak@25: flickerstreak@75: flickerstreak@28: ------ BAR CLASS ------ flickerstreak@28: local Bar = { _classID = {} } flickerstreak@25: flickerstreak@28: local function Constructor( self, name, config ) flickerstreak@25: if type(config) ~= "table" then flickerstreak@28: error("ReAction.Bar: config table required") flickerstreak@25: end flickerstreak@75: config.width = config.width or 480 flickerstreak@75: config.height = config.height or 40 flickerstreak@75: flickerstreak@75: self.name, self.config = name, config flickerstreak@75: self.buttons = setmetatable({},{__mode="k"}) flickerstreak@75: self.statedrivers = { } flickerstreak@75: self.keybinds = { } flickerstreak@25: flickerstreak@54: local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent flickerstreak@75: local f = CreateFrame("Button",name and format("ReAction-%s",name),parent,"SecureStateHeaderTemplate, SecureActionButtonTemplate") flickerstreak@75: flickerstreak@75: -- The frame itself is read-only flickerstreak@75: function self:GetFrame() flickerstreak@75: return f flickerstreak@75: end flickerstreak@75: flickerstreak@75: -- The bar itself is also a Button derived from SecureActionButtonTemplate, so it has an OnClick handler flickerstreak@75: -- which we can use as a virtual button for keybinds, which will send attribute-value changes to itself. flickerstreak@75: -- However, we don't ever want the user to be able to click it directly. flickerstreak@75: f:EnableMouse(false) flickerstreak@75: f:SetAttribute("type","attribute") flickerstreak@25: f:SetFrameStrata("MEDIUM") flickerstreak@25: f:SetWidth(config.width) flickerstreak@25: f:SetWidth(config.height) flickerstreak@75: f:Show() flickerstreak@25: flickerstreak@75: self:ApplyAnchor() flickerstreak@63: ReAction.RegisterCallback(self, "OnConfigModeChanged") flickerstreak@25: end flickerstreak@25: flickerstreak@25: function Bar:Destroy() flickerstreak@75: local f = self:GetFrame() flickerstreak@25: f:UnregisterAllEvents() flickerstreak@25: f:Hide() flickerstreak@25: f:SetParent(UIParent) flickerstreak@25: f:ClearAllPoints() flickerstreak@63: ReAction.UnregisterAllCallbacks(self) flickerstreak@75: for driver in pairs(self.statedrivers) do flickerstreak@75: UnregisterStateDriver(f, driver) flickerstreak@72: end flickerstreak@25: self.labelString = nil flickerstreak@25: self.controlFrame = nil flickerstreak@25: self.config = nil 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@75: local f, config = self:GetFrame(), self.config flickerstreak@25: f:SetWidth(config.width) flickerstreak@25: f:SetHeight(config.height) flickerstreak@75: local point = config.point flickerstreak@51: f:ClearAllPoints() flickerstreak@75: if point then flickerstreak@75: local anchor = f:GetParent() flickerstreak@75: if config.anchor then flickerstreak@75: local bar = ReAction:GetBar(config.anchor) flickerstreak@52: if bar then flickerstreak@75: anchor = bar:GetFrame() flickerstreak@52: else flickerstreak@75: anchor = _G[config.anchor] flickerstreak@52: end flickerstreak@25: end flickerstreak@75: f:SetPoint(point, anchor or f:GetParent(), config.relpoint, config.x or 0, config.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@75: return (c.point or "CENTER"), (c.anchor or self:GetFrame():GetParent():GetName()), (c.relpoint or c.point or "CENTER"), (c.x or 0), (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@25: self.config.width = w flickerstreak@25: self.config.height = h flickerstreak@75: local f = self:GetFrame() 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@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@25: function Bar:GetName() flickerstreak@25: return self.name flickerstreak@25: end flickerstreak@25: flickerstreak@75: -- only ReAction:RenameBar() should call this function flickerstreak@33: function Bar:SetName(name) flickerstreak@33: self.name = name flickerstreak@75: -- controlLabelString is defined in Overlay.lua flickerstreak@33: if self.controlLabelString then flickerstreak@33: self.controlLabelString:SetText(self.name) flickerstreak@33: end flickerstreak@33: end flickerstreak@33: flickerstreak@75: function Bar:AddButton(idx, button) flickerstreak@75: self.buttons[button] = idx flickerstreak@75: SecureStateHeader_Refresh(self:GetFrame()) flickerstreak@75: end flickerstreak@75: flickerstreak@75: function Bar:RemoveButton(button) flickerstreak@75: self.buttons[button] = nil flickerstreak@75: end flickerstreak@75: flickerstreak@75: function Bar:IterateButtons() -- iterator returns button, idx 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@75: if not idx then return end flickerstreak@25: local r, c, s = self:GetButtonGrid() flickerstreak@25: local bh, bw = self:GetButtonSize() flickerstreak@75: local row, col = floor((idx-1)/c), fmod((idx-1),c) -- zero-based flickerstreak@25: local x, y = col*bw + (col+0.5)*s, row*bh + (row+0.5)*s flickerstreak@25: local scale = bw/baseW flickerstreak@75: local f = button:GetFrame() flickerstreak@25: flickerstreak@25: f:ClearAllPoints() flickerstreak@25: f:SetPoint("TOPLEFT",x/scale,-y/scale) flickerstreak@25: f:SetScale(scale) flickerstreak@25: end flickerstreak@25: flickerstreak@75: -- Creates (or updates) a named binding which binds a key press to a call to SetAttribute() flickerstreak@75: -- pass a nil key to unbind flickerstreak@75: function Bar:SetAttributeBinding( name, key, attribute, value ) flickerstreak@75: if not name then flickerstreak@75: error("usage - Bar:SetAttributeBinding(name [, key, attribute, value]") flickerstreak@75: end flickerstreak@75: local f = self:GetFrame() flickerstreak@71: flickerstreak@75: -- clear the old binding, if any flickerstreak@75: if self.keybinds[name] then flickerstreak@75: SetOverrideBinding(f, false, self.keybinds[name], nil) flickerstreak@75: end flickerstreak@75: if key then flickerstreak@75: f:SetAttribute(format("attribute-name-%s",name), attribute) flickerstreak@75: f:SetAttribute(format("attribute-value-%s",name), value) flickerstreak@75: SetOverrideBindingClick(f, false, key, f:GetName(), name) -- binding name is the virtual mouse button flickerstreak@75: end flickerstreak@75: self.keybinds[name] = key flickerstreak@68: end flickerstreak@28: flickerstreak@75: -- Sets up a state driver 'name' for the bar, using the provided 'rule' flickerstreak@75: -- Also sets attributes 'statemap--'= for each entry in the passed map flickerstreak@75: -- if 'rule' is nil or an empty string, the driver is unregistered. flickerstreak@75: function Bar:SetStateDriver( name, rule, map ) flickerstreak@75: local f = self:GetFrame() flickerstreak@75: if rule and #rule > 0 then flickerstreak@75: if map then flickerstreak@75: for key, value in pairs(map) do flickerstreak@75: f:SetAttribute( format("statemap-%s-%s",name,key), value ) flickerstreak@72: end flickerstreak@72: end flickerstreak@75: RegisterStateDriver(f, name, rule) flickerstreak@75: self.statedrivers[name] = true flickerstreak@75: elseif self.statedrivers[name] then flickerstreak@75: UnregisterStateDriver(f, name) flickerstreak@75: self.statedrivers[name] = nil flickerstreak@72: end flickerstreak@72: end flickerstreak@72: flickerstreak@75: -- Set an attribute on the frame (or its buttons if 'doButtons' = true) flickerstreak@75: -- Either or both 'map' and 'default' can be passed: flickerstreak@75: -- - If 'map' is omitted, then 'default' is set to the attribute. flickerstreak@75: -- - If 'map' is provided, then it is interpreted as an unordered flickerstreak@75: -- table of the form { ["statename"] = ["value"] }, and will be flickerstreak@75: -- converted into a SecureStateHeaderTemplate style state-parsed flickerstreak@75: -- string, e.g. ":;:". If 'default' flickerstreak@75: -- is also provided, then its value will be converted to a string flickerstreak@75: -- and appended. flickerstreak@75: function Bar:SetStateAttribute( attribute, map, default, doButtons ) flickerstreak@75: local value = default flickerstreak@75: if map then flickerstreak@75: local tmp = { } flickerstreak@75: for state, value in pairs(map) do flickerstreak@75: table.insert(tmp, format("%s:%s",tostring(state),tostring(value))) flickerstreak@68: end flickerstreak@75: if default then flickerstreak@75: table.insert(tmp, tostring(default)) flickerstreak@75: end flickerstreak@75: value = table.concat(tmp,";") flickerstreak@68: end flickerstreak@75: if doButtons then flickerstreak@75: for b in pairs(self.buttons) do flickerstreak@75: local f = b.GetFrame and b:GetFrame() flickerstreak@75: if f then flickerstreak@75: f:SetAttribute(attribute, value) flickerstreak@71: end flickerstreak@71: end flickerstreak@75: else flickerstreak@75: self:GetFrame():SetAttribute(attribute, value) flickerstreak@71: end flickerstreak@75: SecureStateHeader_Refresh(self:GetFrame()) flickerstreak@71: end flickerstreak@71: flickerstreak@33: flickerstreak@28: ------ Export as a class-factory ------ flickerstreak@28: ReAction.Bar = { flickerstreak@73: prototype = Bar, flickerstreak@75: New = function(self, ...) flickerstreak@28: local x = { } flickerstreak@28: for k,v in pairs(Bar) do flickerstreak@28: x[k] = v flickerstreak@28: end flickerstreak@28: Constructor(x, ...) flickerstreak@28: return x flickerstreak@28: end flickerstreak@28: }