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 floor = math.floor flickerstreak@122: local fmod = math.fmod flickerstreak@122: local format = string.format Flick@276: local tfetch = ns.tfetch Flick@276: local tbuild = ns.tbuild Flick@276: local fieldsort = ns.fieldsort flickerstreak@122: flickerstreak@193: local LSG = LibStub("ReAction-LibShowActionGrid-1.0") flickerstreak@147: flickerstreak@155: ---- Secure snippets ---- flickerstreak@155: local _reaction_init = flickerstreak@155: [[ flickerstreak@155: anchorKeys = newtable("point","relPoint","x","y") flickerstreak@155: flickerstreak@155: state = nil flickerstreak@155: set_state = nil flickerstreak@155: state_override = nil flickerstreak@156: unit_exists = nil flickerstreak@155: flickerstreak@155: showAll = false flickerstreak@155: hidden = false flickerstreak@155: flickerstreak@155: defaultAlpha = 1.0 flickerstreak@155: defaultScale = 1.0 flickerstreak@155: defaultAnchor = newtable() flickerstreak@155: flickerstreak@155: activeStates = newtable() flickerstreak@155: settings = newtable() flickerstreak@155: extensions = newtable() flickerstreak@155: ]] flickerstreak@155: flickerstreak@155: local _reaction_refresh = flickerstreak@155: [[ flickerstreak@155: local oldState = state flickerstreak@155: state = state_override or set_state or state flickerstreak@155: flickerstreak@156: local hide = nil flickerstreak@155: if state then flickerstreak@155: local settings = settings[state] flickerstreak@155: if settings then flickerstreak@155: -- show/hide flickerstreak@156: hide = settings.hide flickerstreak@155: -- re-anchor flickerstreak@155: local old_anchor = activeStates.anchor flickerstreak@155: activeStates.anchor = settings.anchorEnable and state flickerstreak@155: if old_anchor ~= activeStates.anchor or not set_state then flickerstreak@155: if activeStates.anchor then flickerstreak@155: if settings.anchorPoint then flickerstreak@155: self:ClearAllPoints() flickerstreak@155: local f = self:GetAttribute("frameref-anchor-"..state) flickerstreak@155: if f then flickerstreak@155: self:SetPoint(settings.anchorPoint, f, settings.anchorRelPoint, settings.anchorX, settings.anchorY) flickerstreak@155: end flickerstreak@155: end flickerstreak@155: elseif defaultAnchor.point then flickerstreak@155: self:ClearAllPoints() flickerstreak@155: self:SetPoint(defaultAnchor.point, defaultAnchor.frame, flickerstreak@155: defaultAnchor.relPoint, defaultAnchor.x, defaultAnchor.y) flickerstreak@155: end flickerstreak@155: end flickerstreak@155: -- re-scale flickerstreak@155: local old_scale = activeStates.scale flickerstreak@155: activeStates.scale = settings.enableScale and state flickerstreak@155: if old_scale ~= activeStates.scale or not set_state then flickerstreak@155: self:SetScale(activeStates.scale and settings.scale or defaultScale) flickerstreak@155: end flickerstreak@155: -- alpha flickerstreak@155: local old_alpha = activeStates.alpha flickerstreak@155: activeStates.alpha = settings.enableAlpha and state flickerstreak@155: if old_alpha ~= activeStates.alpha or not set_state then flickerstreak@155: self:SetAlpha(activeStates.alpha and settings.alpha or defaultAlpha) flickerstreak@155: end flickerstreak@155: end flickerstreak@155: end flickerstreak@155: flickerstreak@156: -- hide if state or unit_exists says to flickerstreak@156: hide = not showAll and (hide or unithide) flickerstreak@156: if hide ~= hidden then flickerstreak@156: hidden = hide flickerstreak@156: if hide then flickerstreak@156: self:Hide() flickerstreak@156: else flickerstreak@156: self:Show() flickerstreak@156: end flickerstreak@156: end flickerstreak@156: flickerstreak@155: for _, attr in pairs(extensions) do flickerstreak@155: control:RunAttribute(attr) flickerstreak@155: end flickerstreak@155: flickerstreak@155: control:ChildUpdate() flickerstreak@155: flickerstreak@155: if showAll then flickerstreak@155: control:CallMethod("UpdateHiddenLabel", state and settings[state] and settings[state].hide) flickerstreak@155: end flickerstreak@155: flickerstreak@155: if oldState ~= state then flickerstreak@155: control:CallMethod("StateRefresh", state) flickerstreak@155: end flickerstreak@155: ]] flickerstreak@155: flickerstreak@155: local _onstate_reaction = -- function( self, stateid, newstate ) flickerstreak@155: [[ flickerstreak@155: set_state = newstate flickerstreak@155: ]] .. _reaction_refresh flickerstreak@155: flickerstreak@155: local _onstate_showgrid = -- function( self, stateid, newstate ) flickerstreak@155: [[ flickerstreak@155: control:ChildUpdate(stateid,newstate) flickerstreak@155: control:CallMethod("UpdateShowGrid") flickerstreak@155: ]] flickerstreak@155: flickerstreak@155: local _onstate_unitexists = -- function( self, stateid, newstate ) flickerstreak@155: [[ flickerstreak@159: unithide = not newstate or newstate == "hide" flickerstreak@155: ]] .. _reaction_refresh flickerstreak@155: flickerstreak@155: local _onclick = -- function( self, button, down ) flickerstreak@155: [[ flickerstreak@155: if state_override == button then flickerstreak@155: state_override = nil -- toggle flickerstreak@155: else flickerstreak@155: state_override = button flickerstreak@155: end flickerstreak@155: ]] .. _reaction_refresh flickerstreak@155: flickerstreak@157: -- For reference flickerstreak@157: -- the option field names must match the field names of the options table, below flickerstreak@157: local stateProperties = { flickerstreak@157: hide = true, flickerstreak@157: --keybindState = true, TODO: broken flickerstreak@157: anchorEnable = true, flickerstreak@157: anchorFrame = true, flickerstreak@157: anchorPoint = true, flickerstreak@157: anchorRelPoint = true, flickerstreak@157: anchorX = true, flickerstreak@157: anchorY = true, flickerstreak@157: enableScale = true, flickerstreak@157: scale = true, flickerstreak@157: enableAlpha = true, flickerstreak@157: alpha = true, flickerstreak@157: } flickerstreak@157: flickerstreak@157: flickerstreak@155: flickerstreak@147: ---- Bar class ---- flickerstreak@122: local Bar = { } flickerstreak@147: local frameList = { } flickerstreak@122: flickerstreak@122: ReAction.Bar = Bar -- export to ReAction flickerstreak@122: Flick@234: function Bar:New( name, config, buttonClass ) 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: { Flick@234: config = config, Flick@234: name = name, Flick@234: buttons = { }, Flick@234: buttonClass = buttonClass, Flick@234: width = config.width or 480, Flick@234: 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@171: LSG:AddFrame(f) flickerstreak@122: Flick@287: if ReAction.LBF then Flick@287: local g = ReAction.LBF:Group(L["ReAction"], self.name) Flick@287: self.config.ButtonFacade = self.config.ButtonFacade or { Flick@287: skinID = "Blizzard", Flick@287: backdrop = true, Flick@287: gloss = 0, Flick@287: colors = {}, Flick@287: } Flick@287: local c = self.config.ButtonFacade Flick@287: g:Skin(c.skinID, c.gloss, c.backdrop, c.colors) Flick@287: self.LBFGroup = g Flick@287: end Flick@287: flickerstreak@155: -- secure handlers flickerstreak@155: f:Execute(_reaction_init) flickerstreak@155: f:SetAttribute("_onstate-reaction", _onstate_reaction) flickerstreak@155: f:SetAttribute("_onstate-showgrid", _onstate_showgrid) flickerstreak@155: f:SetAttribute("_onstate-unitexists", _onstate_unitexists) flickerstreak@155: f:SetAttribute("_onclick", _onclick) flickerstreak@155: flickerstreak@155: -- secure handler CallMethod()s flickerstreak@155: f.UpdateShowGrid = function() self:UpdateShowGrid() end flickerstreak@155: f.StateRefresh = function() self:RefreshControls() end flickerstreak@155: f.UpdateHiddenLabel = function(f,hidden) self:SetLabelSubtext(hidden and L["Hidden"]) end 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() Flick@287: if ReAction:GetConfigMode() then Flick@287: self:SetConfigMode(true) flickerstreak@213: end flickerstreak@122: ReAction.RegisterCallback(self, "OnConfigModeChanged") Flick@287: if ReAction:GetKeybindMode() then Flick@287: self:SetKeybindMode(true) Flick@287: end flickerstreak@122: Flick@234: buttonClass:SetupBar(self) Flick@242: self:ApplyStates() Flick@234: flickerstreak@122: return self flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:Destroy() flickerstreak@122: local f = self:GetFrame() Flick@242: self:CleanupStates() Flick@234: for idx, b in self:IterateButtons() do Flick@234: b:Destroy() Flick@234: end flickerstreak@122: f:UnregisterAllEvents() flickerstreak@125: self:ShowControls(false) flickerstreak@122: ReAction.UnregisterAllCallbacks(self) flickerstreak@171: LKB.UnregisterAllCallbacks(self) flickerstreak@213: if self.LBFGroup then flickerstreak@213: self.LBFGroup:Delete(true) flickerstreak@213: end flickerstreak@171: LSG: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@153: -- flickerstreak@153: -- Accessors flickerstreak@153: -- flickerstreak@153: flickerstreak@153: function Bar:GetName() flickerstreak@153: return self.name flickerstreak@153: end flickerstreak@153: flickerstreak@153: -- only ReAction:RenameBar() should call this function. Calling from any other flickerstreak@153: -- context will desync the bar list in the ReAction class. flickerstreak@153: function Bar:SetName(name) flickerstreak@213: if self.LBFGroup then flickerstreak@213: -- LBF doesn't offer a method of renaming a group, so delete and remake the group. flickerstreak@213: local c = self.config.ButtonFacade flickerstreak@213: local g = ReAction.LBF:Group(L["ReAction"], name) Flick@234: for idx, b in self:IterateButtons() do flickerstreak@213: self.LBFGroup:RemoveButton(b:GetFrame(), true) flickerstreak@213: g:AddButton(b:GetFrame()) flickerstreak@213: end flickerstreak@213: self.LBFGroup:Delete(true) flickerstreak@213: self.LBFGroup = g flickerstreak@213: self.LBFGroup:Skin(c.skinID, c.gloss, c.backdrop, c.colors) flickerstreak@213: end flickerstreak@153: self.name = name flickerstreak@154: if self.overlay then flickerstreak@154: self.overlay:SetLabel(self.name) flickerstreak@154: end flickerstreak@153: end flickerstreak@153: flickerstreak@153: function Bar:GetFrame() flickerstreak@153: -- this method is included for documentation purposes. It is overridden flickerstreak@153: -- for each object in the :New() method. flickerstreak@153: error("Invalid Bar object: used without initialization") flickerstreak@153: end flickerstreak@153: Flick@234: function Bar:GetButton(idx) Flick@234: return self.buttons[idx] Flick@234: end Flick@234: Flick@237: function Bar:GetButtonClass() Flick@237: return self.buttonClass Flick@237: end Flick@237: flickerstreak@153: function Bar:GetConfig() flickerstreak@153: return self.config flickerstreak@153: end flickerstreak@153: flickerstreak@153: function Bar:GetAnchor() flickerstreak@122: local c = self.config flickerstreak@153: return (c.point or "CENTER"), flickerstreak@153: (c.anchor or self:GetFrame():GetParent():GetName()), flickerstreak@153: (c.relpoint or c.point or "CENTER"), flickerstreak@153: (c.x or 0), flickerstreak@153: (c.y or 0) 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: 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 Flick@279: for _, b in pairs(self.buttons) do Flick@279: b:Refresh() Flick@279: end flickerstreak@122: end flickerstreak@122: end flickerstreak@122: flickerstreak@153: function Bar:GetNumButtons() flickerstreak@153: local r,c = self:GetButtonGrid() flickerstreak@153: return r*c flickerstreak@153: end flickerstreak@153: 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:SetButtonGrid(r,c,s) Flick@283: local cfg = self.config Flick@283: r = r or cfg.btnRows Flick@283: c = c or cfg.btnColumns Flick@283: s = s or cfg.spacing flickerstreak@122: if r > 0 and c > 0 and s > 0 then flickerstreak@122: cfg.btnRows = r flickerstreak@122: cfg.btnColumns = c flickerstreak@122: cfg.spacing = s flickerstreak@122: end Flick@234: self.buttonClass:SetupBar(self) 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@155: self:UpdateDefaultStateAlpha() flickerstreak@122: end flickerstreak@122: flickerstreak@122: function Bar:IterateButtons() Flick@234: -- iterator returns idx, button, but does NOT iterate in index order flickerstreak@122: return pairs(self.buttons) flickerstreak@122: end flickerstreak@122: flickerstreak@153: -- flickerstreak@153: -- Methods flickerstreak@153: -- flickerstreak@153: flickerstreak@147: function Bar:SetConfigMode(mode) flickerstreak@155: self:SetSecureData("showAll",mode) flickerstreak@147: self:ShowControls(mode) Flick@234: for idx, b in self:IterateButtons() do flickerstreak@147: b:ShowGridTemp(mode) flickerstreak@147: b:UpdateActionIDLabel(mode) flickerstreak@147: end Flick@279: self.buttonClass:SetupBar(self) -- force a full refresh flickerstreak@147: end flickerstreak@147: flickerstreak@147: function Bar:SetKeybindMode(mode) flickerstreak@155: self:SetSecureData("showAll",mode) Flick@234: for idx, b in self:IterateButtons() do flickerstreak@147: b:SetKeybindMode(mode) flickerstreak@147: end flickerstreak@147: end flickerstreak@147: flickerstreak@153: function Bar:ApplyAnchor() flickerstreak@153: local f = self:GetFrame() flickerstreak@153: local c = self.config flickerstreak@153: local p = c.point flickerstreak@153: flickerstreak@153: f:SetWidth(c.width) flickerstreak@153: f:SetHeight(c.height) flickerstreak@153: f:ClearAllPoints() flickerstreak@153: flickerstreak@153: if p then flickerstreak@153: local a = f:GetParent() flickerstreak@153: if c.anchor then flickerstreak@153: local bar = ReAction:GetBar(c.anchor) flickerstreak@153: if bar then flickerstreak@153: a = bar:GetFrame() flickerstreak@153: else flickerstreak@153: a = _G[c.anchor] flickerstreak@153: end flickerstreak@153: end flickerstreak@153: local fr = a or f:GetParent() flickerstreak@153: f:SetPoint(p, a or f:GetParent(), c.relpoint, c.x or 0, c.y or 0) flickerstreak@153: else flickerstreak@153: f:SetPoint("CENTER") flickerstreak@153: end flickerstreak@155: flickerstreak@155: self:UpdateDefaultStateAnchor() flickerstreak@153: end flickerstreak@153: flickerstreak@153: function Bar:ClipNButtons( n ) flickerstreak@153: local cfg = self.config flickerstreak@153: local r = cfg.btnRows or 1 flickerstreak@153: local c = cfg.btnColumns or 1 flickerstreak@153: flickerstreak@153: cfg.btnRows = ceil(n/c) flickerstreak@153: cfg.btnColumns = min(n,c) flickerstreak@153: end flickerstreak@153: flickerstreak@153: function Bar:AddButton(idx, button) flickerstreak@153: local f = self:GetFrame() flickerstreak@153: Flick@234: self.buttons[idx] = button flickerstreak@153: flickerstreak@153: -- Store a properly wrapped reference to the child frame as an attribute flickerstreak@153: -- (accessible via "frameref-btn#") flickerstreak@153: f:SetFrameRef(format("btn%d",idx), button:GetFrame()) flickerstreak@213: flickerstreak@213: -- button constructors are responsible for calling SkinButton flickerstreak@153: end flickerstreak@153: flickerstreak@153: function Bar:RemoveButton(button) Flick@234: local idx = button:GetIndex() flickerstreak@153: if idx then flickerstreak@153: self:GetFrame():SetAttribute(format("frameref-btn%d",idx),nil) Flick@234: self.buttons[idx] = nil flickerstreak@153: end flickerstreak@213: if self.LBFGroup then flickerstreak@213: self.LBFGroup:RemoveButton(button:GetFrame(),true) flickerstreak@213: end flickerstreak@153: end flickerstreak@153: flickerstreak@122: function Bar:PlaceButton(button, baseW, baseH) Flick@234: local idx = button:GetIndex() 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@213: function Bar:SkinButton( button, data ) flickerstreak@213: if self.LBFGroup then flickerstreak@213: self.LBFGroup:AddButton(button:GetFrame(), data) flickerstreak@213: end flickerstreak@147: end flickerstreak@148: flickerstreak@155: function Bar:UpdateShowGrid() Flick@234: for idx, button in self:IterateButtons() do flickerstreak@155: button:UpdateShowGrid() flickerstreak@155: end flickerstreak@155: end flickerstreak@155: flickerstreak@154: function Bar:ShowControls(show) flickerstreak@154: if show then flickerstreak@155: if not self.overlay then flickerstreak@155: self.overlay = Bar.Overlay:New(self) -- see Overlay.lua flickerstreak@154: end flickerstreak@155: self.overlay:Show() flickerstreak@155: self:RefreshSecureState() flickerstreak@155: elseif self.overlay then flickerstreak@155: self.overlay:Hide() flickerstreak@154: end flickerstreak@154: end flickerstreak@154: flickerstreak@154: function Bar:RefreshControls() flickerstreak@154: if self.overlay and self.overlay:IsShown() then flickerstreak@154: self.overlay:RefreshControls() flickerstreak@154: end flickerstreak@154: end flickerstreak@154: flickerstreak@154: function Bar:SetLabelSubtext(text) flickerstreak@154: if self.overlay then flickerstreak@154: self.overlay:SetLabelSubtext(text) flickerstreak@154: end flickerstreak@154: end flickerstreak@154: flickerstreak@154: -- flickerstreak@154: -- Secure state functions flickerstreak@154: -- flickerstreak@154: flickerstreak@155: function Bar:GetSecureState() flickerstreak@155: local env = GetManagedEnvironment(self:GetFrame()) flickerstreak@155: return env and env.state flickerstreak@155: end flickerstreak@155: flickerstreak@155: function Bar:GetStateProperty(state, propname) Flick@242: return tfetch(self:GetConfig(), "states", state, propname) flickerstreak@155: end flickerstreak@155: flickerstreak@155: function Bar:SetStateProperty(state, propname, value) Flick@242: local s = tbuild(self:GetConfig(), "states", state) Flick@242: s[propname] = value Flick@242: self:SetSecureStateData(state, propname, value) Flick@242: end Flick@242: Flick@242: function Bar:ApplyStates() Flick@242: local states = tfetch(self:GetConfig(), "states") Flick@242: if states then Flick@242: self:SetStateDriver(states) Flick@242: end Flick@242: end Flick@242: Flick@242: function Bar:CleanupStates() Flick@242: self:SetStateDriver(nil) flickerstreak@155: end flickerstreak@155: flickerstreak@155: function Bar:RefreshSecureState() flickerstreak@155: self:GetFrame():Execute(_reaction_refresh) flickerstreak@155: end flickerstreak@155: flickerstreak@155: -- usage: SetSecureData(globalname, [tblkey1, tblkey2, ...], value) flickerstreak@155: function Bar:SetSecureData( ... ) flickerstreak@155: local n = select('#',...) flickerstreak@155: if n < 2 then flickerstreak@155: error("ReAction.Bar:SetSecureData() requires at least 2 arguments") flickerstreak@155: end flickerstreak@155: local f = self:GetFrame() flickerstreak@155: f:SetAttribute("data-depth",n-1) flickerstreak@155: f:SetAttribute("data-value",select(n,...)) flickerstreak@155: for i = 1, n-1 do flickerstreak@155: local key = select(i,...) flickerstreak@155: if key == nil then flickerstreak@155: error("ReAction.Bar:SetSecureData() - nil table key in argument list (#"..i..")") flickerstreak@155: end flickerstreak@155: f:SetAttribute("data-key-"..i, key) flickerstreak@155: end flickerstreak@155: f:Execute( flickerstreak@155: [[ flickerstreak@155: local n = self:GetAttribute("data-depth") flickerstreak@155: if n > 0 then flickerstreak@155: local value = self:GetAttribute("data-value") flickerstreak@155: local t = _G flickerstreak@155: for i = 1, n do flickerstreak@155: local key = self:GetAttribute("data-key-"..i) flickerstreak@155: if not key then return end flickerstreak@155: if not t[key] then flickerstreak@155: t[key] = newtable() flickerstreak@155: end flickerstreak@155: if i == n then flickerstreak@155: t[key] = value flickerstreak@155: else flickerstreak@155: t = t[key] flickerstreak@155: end flickerstreak@155: end flickerstreak@155: end flickerstreak@155: ]]) flickerstreak@155: self:RefreshSecureState() flickerstreak@155: end flickerstreak@155: flickerstreak@155: function Bar:SetSecureStateData( state, key, value ) flickerstreak@155: self:SetSecureData("settings",state,key,value) flickerstreak@155: end flickerstreak@155: flickerstreak@155: -- sets a snippet to be run as an extension to _onstate-reaction flickerstreak@155: function Bar:SetSecureStateExtension( id, snippet ) flickerstreak@155: if id == nil then flickerstreak@155: error("ReAction.Bar:SetSecureStateExtension() requires an id") flickerstreak@155: end flickerstreak@155: local f = self:GetFrame() flickerstreak@155: f:SetAttribute("input-secure-ext-id",id) flickerstreak@155: f:SetAttribute("secure-ext-"..id,snippet) flickerstreak@155: f:Execute( flickerstreak@155: [[ flickerstreak@155: local id = self:GetAttribute("input-secure-ext-id") flickerstreak@155: if id then flickerstreak@155: extensions[id] = self:GetAttribute("secure-ext-"..id) or nil flickerstreak@155: end flickerstreak@155: ]]) flickerstreak@155: self:RefreshSecureState() flickerstreak@155: end flickerstreak@155: flickerstreak@155: function Bar:SetFrameRef( name, refFrame ) flickerstreak@155: if refFrame then flickerstreak@155: local _, explicit = refFrame:IsProtected() flickerstreak@155: if not explicit then flickerstreak@155: refFrame = nil flickerstreak@155: end flickerstreak@155: end flickerstreak@155: if refFrame then flickerstreak@155: self:GetFrame():SetFrameRef(name,refFrame) flickerstreak@155: else flickerstreak@155: self:GetFrame():SetAttribute("frameref-"..name,nil) flickerstreak@155: end flickerstreak@155: end flickerstreak@155: flickerstreak@157: function Bar:SetStateDriver( states ) flickerstreak@157: if states then flickerstreak@157: for state, props in pairs(states) do flickerstreak@157: self:SetSecureStateData(state, "active_", true) -- make sure there's a 'settings' field for this state flickerstreak@157: for propname, value in pairs(props) do flickerstreak@157: if propname == "anchorFrame" then flickerstreak@157: self:SetFrameRef("anchor-"..state, _G[value]) flickerstreak@157: elseif propname == "rule" then flickerstreak@157: -- do nothing flickerstreak@157: else flickerstreak@157: self:SetSecureStateData(state, propname, value) flickerstreak@157: end flickerstreak@157: end flickerstreak@157: end flickerstreak@157: end flickerstreak@157: local rule = states and self:BuildStateRule(states) flickerstreak@155: if rule then flickerstreak@155: RegisterStateDriver(self:GetFrame(),"reaction",rule) flickerstreak@155: elseif self.statedriver then flickerstreak@155: UnregisterStateDriver(self:GetFrame(),"reaction") flickerstreak@155: end flickerstreak@155: self.statedriver = rule flickerstreak@157: self:BuildStateKeybinds(states) flickerstreak@155: self:RefreshSecureState() flickerstreak@155: end flickerstreak@155: 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@156: if enable then Flick@234: if not self.unitwatch then Flick@279: RegisterUnitWatch(f,true) Flick@234: end flickerstreak@156: elseif self.unitwatch then Flick@279: UnregisterUnitWatch(f) flickerstreak@156: end flickerstreak@148: self.unitwatch = enable flickerstreak@156: self:RefreshSecureState() flickerstreak@148: end flickerstreak@148: flickerstreak@155: function Bar:SetStateKeybind( key, state ) flickerstreak@155: local f = self:GetFrame() flickerstreak@155: local binds = self.statebinds flickerstreak@155: if not binds then flickerstreak@155: binds = { } flickerstreak@155: self.statebinds = binds flickerstreak@155: end flickerstreak@155: flickerstreak@155: -- clear the old binding, if any flickerstreak@155: if binds[state] then flickerstreak@155: SetOverrideBinding(f, false, binds[state], nil) flickerstreak@155: end flickerstreak@155: flickerstreak@155: if key then flickerstreak@157: SetOverrideBindingClick(f, false, key, f:GetName(), state) -- state name is virtual mouse button flickerstreak@155: end flickerstreak@155: binds[state] = key flickerstreak@155: end flickerstreak@155: flickerstreak@155: function Bar:GetStateKeybind( state ) flickerstreak@155: if self.statebinds and state then flickerstreak@155: return self.statebinds[state] flickerstreak@155: end flickerstreak@155: end flickerstreak@155: flickerstreak@155: function Bar:UpdateDefaultStateAnchor() flickerstreak@155: local point, frame, relPoint, x, y = self:GetAnchor() flickerstreak@155: local f = self:GetFrame() flickerstreak@155: f:SetAttribute("defaultAnchor-point",point) flickerstreak@155: f:SetAttribute("defaultAnchor-relPoint",relPoint) flickerstreak@155: f:SetAttribute("defaultAnchor-x",x) flickerstreak@155: f:SetAttribute("defaultAnchor-y",y) flickerstreak@155: self:SetFrameRef("defaultAnchor",_G[frame or "UIParent"]) flickerstreak@155: f:Execute([[ flickerstreak@155: for _, k in pairs(anchorKeys) do flickerstreak@155: defaultAnchor[k] = self:GetAttribute("defaultAnchor-"..k) flickerstreak@155: end flickerstreak@155: defaultAnchor.frame = self:GetAttribute("frameref-defaultAnchor") flickerstreak@155: ]]) flickerstreak@155: end flickerstreak@155: flickerstreak@155: function Bar:UpdateDefaultStateAlpha() flickerstreak@155: local f = self:GetFrame() flickerstreak@155: f:SetAttribute("defaultAlpha",self:GetAlpha()) flickerstreak@155: f:Execute([[ flickerstreak@155: defaultAlpha = self:GetAttribute("defaultAlpha") flickerstreak@155: ]]) flickerstreak@155: end flickerstreak@157: flickerstreak@157: ---- secure state driver rules ---- flickerstreak@157: flickerstreak@157: local playerClass = select(2, UnitClass("player")) flickerstreak@157: local function ClassFilter(...) flickerstreak@157: for i = 1, select('#',...) do flickerstreak@157: if playerClass == select(i,...) then flickerstreak@157: return false flickerstreak@157: end flickerstreak@157: end flickerstreak@157: return true flickerstreak@157: end flickerstreak@157: flickerstreak@157: local ruleformats = { flickerstreak@157: stealth = { format = "stealth", filter = ClassFilter("ROGUE","DRUID") }, flickerstreak@157: nostealth = { format = "nostealth", filter = ClassFilter("ROGUE","DRUID") }, Flick@295: shadowdance = { format = "stance:2", filter = ClassFilter("ROGUE") }, flickerstreak@157: shadowform = { format = "form:1", filter = ClassFilter("PRIEST") }, flickerstreak@157: noshadowform = { format = "noform", filter = ClassFilter("PRIEST") }, flickerstreak@157: battle = { format = "stance:1", filter = ClassFilter("WARRIOR") }, flickerstreak@157: defensive = { format = "stance:2", filter = ClassFilter("WARRIOR") }, flickerstreak@157: berserker = { format = "stance:3", filter = ClassFilter("WARRIOR") }, flickerstreak@157: caster = { format = "form:0/2/4/5/6", filter = ClassFilter("DRUID") }, flickerstreak@157: bear = { format = "form:1", filter = ClassFilter("DRUID") }, flickerstreak@157: cat = { format = "form:3", filter = ClassFilter("DRUID") }, flickerstreak@157: tree = { format = "form:5", filter = ClassFilter("DRUID") }, flickerstreak@157: moonkin = { format = "form:5", filter = ClassFilter("DRUID") }, Flick@295: demon = { format = "form:1", filter = ClassFilter("WARLOCK") }, flickerstreak@178: nodemon = { format = "noform", filter = ClassFilter("WARLOCK") }, flickerstreak@157: pet = { format = "pet" }, flickerstreak@157: nopet = { format = "nopet" }, flickerstreak@176: harm = { format = "@target,harm" }, flickerstreak@176: help = { format = "@target,help" }, flickerstreak@176: notarget = { format = "@target,noexists" }, flickerstreak@176: focusharm = { format = "@focus,harm" }, flickerstreak@176: focushelp = { format = "@focus,help" }, flickerstreak@176: nofocus = { format = "@focus,noexists" }, flickerstreak@157: raid = { format = "group:raid" }, flickerstreak@157: party = { format = "group:party" }, flickerstreak@157: solo = { format = "nogroup" }, flickerstreak@157: combat = { format = "combat" }, flickerstreak@157: nocombat = { format = "nocombat" }, Flick@295: possess = { format = "overridebar" }, Flick@301: extrabar = { format = "extrabar" }, Flick@295: vehicle = { format = "vehicleui" }, flickerstreak@157: } flickerstreak@157: flickerstreak@157: function Bar.InitRuleFormats() flickerstreak@157: local forms = { } flickerstreak@157: for i = 1, GetNumShapeshiftForms() do flickerstreak@157: local _, name = GetShapeshiftFormInfo(i) flickerstreak@157: forms[name] = i; flickerstreak@157: end flickerstreak@157: -- use 9 if not found since 9 is never a valid stance/form flickerstreak@157: local defensive = forms[GetSpellInfo(71)] or 9 flickerstreak@157: local berserker = forms[GetSpellInfo(2458)] or 9 flickerstreak@176: local bear = forms[GetSpellInfo(5487)] or 9 flickerstreak@157: local aquatic = forms[GetSpellInfo(1066)] or 9 flickerstreak@157: local cat = forms[GetSpellInfo(768)] or 9 flickerstreak@157: local travel = forms[GetSpellInfo(783)] or 9 flickerstreak@157: local tree = forms[GetSpellInfo(33891)] or 9 flickerstreak@157: local moonkin = forms[GetSpellInfo(24858)] or 9 flickerstreak@157: local flight = forms[GetSpellInfo(40120)] or forms[GetSpellInfo(33943)] or 9 flickerstreak@157: flickerstreak@157: ruleformats.defensive.format = "stance:"..defensive flickerstreak@157: ruleformats.berserker.format = "stance:"..berserker flickerstreak@157: ruleformats.caster.format = format("form:0/%d/%d/%d", aquatic, travel, flight) flickerstreak@157: ruleformats.bear.format = "form:"..bear flickerstreak@157: ruleformats.cat.format = "form:"..cat flickerstreak@157: ruleformats.tree.format = "form:"..tree flickerstreak@157: ruleformats.moonkin.format = "form:"..moonkin flickerstreak@157: end flickerstreak@157: flickerstreak@157: function Bar:BuildStateRule(states) flickerstreak@157: -- states is a table : flickerstreak@157: -- states[statename].rule = { flickerstreak@157: -- order = #, flickerstreak@157: -- type = "default"/"custom"/"any"/"all", flickerstreak@157: -- values = { ... }, -- keys of ruleformats[] flickerstreak@157: -- custom = "...", flickerstreak@157: -- } flickerstreak@157: local rules = { } flickerstreak@157: local default flickerstreak@157: flickerstreak@157: for idx, state in ipairs(fieldsort(states, "rule", "order")) do flickerstreak@157: local c = states[state].rule flickerstreak@157: local type = c.type flickerstreak@157: if type == "default" then flickerstreak@157: default = default or state flickerstreak@157: elseif type == "custom" then flickerstreak@157: if c.custom then flickerstreak@157: -- strip out all spaces from the custom rule flickerstreak@157: table.insert(rules, format("%s %s", c.custom:gsub("%s",""), state)) flickerstreak@157: end flickerstreak@157: elseif type == "any" or type == "all" then flickerstreak@157: if c.values then flickerstreak@157: local clauses = { } flickerstreak@157: for key, value in pairs(c.values) do flickerstreak@157: if ruleformats[key] and not ruleformats[key].filter then flickerstreak@157: table.insert(clauses, ruleformats[key].format) flickerstreak@157: end flickerstreak@157: end flickerstreak@157: if #clauses > 0 then flickerstreak@157: local sep = (type == "any") and "][" or "," flickerstreak@157: table.insert(rules, format("[%s] %s", table.concat(clauses,sep), state)) flickerstreak@157: end flickerstreak@157: end flickerstreak@157: end flickerstreak@157: end flickerstreak@157: -- make sure that the default, if any, is last flickerstreak@157: if default then flickerstreak@157: table.insert(rules, default) flickerstreak@157: end flickerstreak@157: return table.concat(rules,";") flickerstreak@157: end flickerstreak@157: flickerstreak@157: function Bar:BuildStateKeybinds( states ) flickerstreak@157: if states then flickerstreak@157: for name, state in pairs(states) do flickerstreak@157: local rule = tfetch(state, "rule") flickerstreak@157: if rule and rule.type == "keybind" then flickerstreak@157: self:SetStateKeybind(rule.keybind, name) flickerstreak@157: else flickerstreak@157: self:SetStateKeybind(nil, name) -- this clears an existing keybind flickerstreak@157: end flickerstreak@157: end flickerstreak@157: end flickerstreak@157: end flickerstreak@157: