flickerstreak@25: --[[ flickerstreak@62: ReAction bar state driver interface flickerstreak@25: flickerstreak@25: --]] flickerstreak@25: flickerstreak@25: -- local imports flickerstreak@25: local ReAction = ReAction flickerstreak@25: local L = ReAction.L flickerstreak@25: local _G = _G flickerstreak@90: local format = string.format flickerstreak@25: local InCombatLockdown = InCombatLockdown flickerstreak@90: local RegisterStateDriver = RegisterStateDriver flickerstreak@25: flickerstreak@80: ReAction:UpdateRevision("$Revision$") flickerstreak@77: flickerstreak@25: -- module declaration flickerstreak@25: local moduleID = "State" flickerstreak@65: local module = ReAction:NewModule( moduleID, "AceEvent-3.0" ) flickerstreak@62: flickerstreak@64: -- Utility -- flickerstreak@62: flickerstreak@62: -- traverse a table tree by key list and fetch the result or first nil flickerstreak@62: local function tfetch(t, ...) flickerstreak@62: for i = 1, select('#', ...) do flickerstreak@62: t = t and t[select(i, ...)] flickerstreak@62: end flickerstreak@62: return t flickerstreak@62: end flickerstreak@62: flickerstreak@62: -- traverse a table tree by key list and build tree as necessary flickerstreak@62: local function tbuild(t, ...) flickerstreak@62: for i = 1, select('#', ...) do flickerstreak@62: local key = select(i, ...) flickerstreak@62: if not t[key] then t[key] = { } end flickerstreak@62: t = t[key] flickerstreak@62: end flickerstreak@62: return t flickerstreak@62: end flickerstreak@62: flickerstreak@75: -- return a new array of keys of table 't', sorted by comparing flickerstreak@75: -- sub-fields (obtained via tfetch) of the table values flickerstreak@75: local function fieldsort( t, ... ) flickerstreak@75: local r = { } flickerstreak@75: for k in pairs(t) do flickerstreak@75: table.insert(r,k) flickerstreak@75: end flickerstreak@75: local path = { ... } flickerstreak@75: table.sort(r, function(lhs, rhs) flickerstreak@75: local olhs = tfetch(t[lhs], unpack(path)) or 0 flickerstreak@75: local orhs = tfetch(t[rhs], unpack(path)) or 0 flickerstreak@75: return olhs < orhs flickerstreak@75: end) flickerstreak@75: return r flickerstreak@75: end flickerstreak@75: flickerstreak@68: flickerstreak@90: local InitRules, ApplyStates, CleanupStates, SetProperty, GetProperty, RegisterProperty, ShowAll flickerstreak@75: flickerstreak@75: -- PRIVATE -- flickerstreak@64: do flickerstreak@90: flickerstreak@90: -- the field names must match the field names of the options table, below flickerstreak@90: -- the field values are secure snippets flickerstreak@90: local properties = { flickerstreak@90: hide = flickerstreak@90: [[ flickerstreak@90: local h = hide and hide[state] and not showAll flickerstreak@90: if h ~= hidden then flickerstreak@90: if h then flickerstreak@90: self:Hide() flickerstreak@90: else flickerstreak@90: self:Show() flickerstreak@90: end flickerstreak@90: hidden = h flickerstreak@90: end flickerstreak@90: ]], flickerstreak@90: flickerstreak@90: --keybindState TODO: broken flickerstreak@90: flickerstreak@90: -- the anchoring is handled in a special handler flickerstreak@90: anchorEnable = true, flickerstreak@90: --anchorFrame = true, TODO: broken flickerstreak@90: anchorPoint = true, flickerstreak@90: anchorRelPoint = true, flickerstreak@90: anchorX = true, flickerstreak@90: anchorY = true, flickerstreak@90: enableScale = true, flickerstreak@90: scale = true, flickerstreak@90: } flickerstreak@90: flickerstreak@90: flickerstreak@90: -- flickerstreak@90: -- Secure Handler Snippets flickerstreak@90: -- flickerstreak@90: local SetHandlerData, SetStateDriver, SetStateKeybind, RefreshState flickerstreak@90: do flickerstreak@90: local stateHandler_propInit = flickerstreak@90: [[ flickerstreak@90: propfuncs = table.new() flickerstreak@90: local proplist = self:GetAttribute("prop-func-list") flickerstreak@90: for s in string.gmatch(proplist, "(%w+)") do flickerstreak@90: table.insert(propfuncs, s) flickerstreak@90: end flickerstreak@90: ]] flickerstreak@90: flickerstreak@90: local onStateHandler = flickerstreak@90: -- function _onstate-reaction( self, stateid, newstate ) flickerstreak@90: [[ flickerstreak@90: print("received state",newstate,"on bar",self:GetName()) flickerstreak@90: set_state = newstate or set_state flickerstreak@90: flickerstreak@90: local oldState = state flickerstreak@90: state = state_override or set_state or state flickerstreak@90: flickerstreak@90: for i = 1, #propfuncs do flickerstreak@90: print("running state func",propfuncs[i]) flickerstreak@90: control:RunAttribute("func-"..propfuncs[i]) flickerstreak@90: end flickerstreak@90: flickerstreak@90: if anchorEnable and anchorEnable[state] ~= anchorstate then flickerstreak@90: anchorstate = anchorEnable[state] flickerstreak@90: control:RunAttribute("func-doanchor") flickerstreak@90: end flickerstreak@90: flickerstreak@90: control:ChildUpdate() flickerstreak@90: ]] flickerstreak@90: flickerstreak@90: local anchorHandler = flickerstreak@90: -- function func-doanchor( self ) flickerstreak@90: [[ flickerstreak@90: -- TODO flickerstreak@90: if anchorstate then flickerstreak@90: -- TODO: get anchor data from state tables flickerstreak@90: else flickerstreak@90: -- TODO: get anchor data from defaults flickerstreak@90: end flickerstreak@90: ]] flickerstreak@90: flickerstreak@90: local onClickHandler = flickerstreak@90: -- function OnClick( self, button, down ) flickerstreak@90: [[ flickerstreak@90: if state_override == button then flickerstreak@90: state_override = nil -- toggle flickerstreak@90: else flickerstreak@90: state_override = button flickerstreak@90: end flickerstreak@90: ]] .. onStateHandler flickerstreak@90: flickerstreak@90: local weak = { __mode = "k" } flickerstreak@90: local statedrivers = setmetatable( { }, weak ) flickerstreak@90: local keybinds = setmetatable( { }, weak ) flickerstreak@90: flickerstreak@90: -- Construct a lua assignment as a code string and execute it within the header flickerstreak@90: -- frame's sandbox. 'value' must be a string, boolean, number, or nil. If called flickerstreak@90: -- with four arguments, then it treats 'varname' as an existing global table and flickerstreak@90: -- sets a key-value pair. For a slight efficiency boost, pass the values in as flickerstreak@90: -- attributes and fetch them as attributes from the snippet code, to leverage snippet flickerstreak@90: -- caching. flickerstreak@90: function SetHandlerData( bar, varname, value, key ) flickerstreak@90: local f = bar:GetFrame() flickerstreak@90: f:SetAttribute("data-varname",varname) flickerstreak@90: f:SetAttribute("data-value", value) flickerstreak@90: f:SetAttribute("data-key", key) flickerstreak@90: f:Execute( flickerstreak@90: [[ flickerstreak@90: local name = self:GetAttribute("data-varname") flickerstreak@90: local value = self:GetAttribute("data-value") flickerstreak@90: local key = self:GetAttribute("data-key") flickerstreak@90: if name then flickerstreak@90: if key then flickerstreak@90: if not _G[name] then flickerstreak@90: _G[name] = table.new() flickerstreak@90: end flickerstreak@90: _G[name][key] = value flickerstreak@90: else flickerstreak@90: _G[name] = value flickerstreak@90: end flickerstreak@90: end flickerstreak@90: ]]) flickerstreak@90: end flickerstreak@90: flickerstreak@90: function SetDefaultAnchor( bar ) flickerstreak@90: local point, frame, relPoint, x, y = bar:GetAnchor() flickerstreak@90: SetHandlerData(bar, "defaultAnchor", point, "point") flickerstreak@90: SetHandlerData(bar, "defaultAnchor", relPoint, "relPoint") flickerstreak@90: SetHandlerData(bar, "defaultAnchor", x, "x") flickerstreak@90: SetHandlerData(bar, "defaultAnchor", y, "y") flickerstreak@90: flickerstreak@90: if frame then flickerstreak@90: local f = bar:GetFrame() flickerstreak@90: f:SetFrameRef("defaultAnchor", f) flickerstreak@90: f:Execute( flickerstreak@90: [[ flickerstreak@90: defaultAnchor.frame = self:GetAttribute("frameref-defaultAnchor") flickerstreak@90: ]]) flickerstreak@90: end flickerstreak@90: end flickerstreak@90: flickerstreak@90: function RefreshState( bar ) flickerstreak@90: SetDefaultAnchor(bar) flickerstreak@90: bar:GetFrame():Execute([[control:RunAttribute("reaction-refresh")]]) flickerstreak@90: end flickerstreak@90: flickerstreak@90: function SetStateDriver( bar, rule ) flickerstreak@90: local f = bar:GetFrame() flickerstreak@90: flickerstreak@90: local props = { } flickerstreak@90: for p, h in pairs(properties) do flickerstreak@90: if type(h) == "string" then flickerstreak@90: table.insert(props,p) flickerstreak@90: f:SetAttribute("func-"..p, h) flickerstreak@90: end flickerstreak@90: end flickerstreak@90: f:SetAttribute("prop-func-list", table.concat(props," ")) flickerstreak@90: f:Execute(stateHandler_propInit) flickerstreak@90: f:SetAttribute("reaction-refresh", onStateHandler) flickerstreak@90: f:SetAttribute("func-doanchor", anchorHandler) flickerstreak@90: flickerstreak@90: if rule and #rule > 0 then flickerstreak@90: f:SetAttribute( "_onstate-reaction", onStateHandler ) flickerstreak@90: RegisterStateDriver(f, "reaction", rule) flickerstreak@90: statedrivers[bar] = rule flickerstreak@90: elseif statedrivers[bar] then flickerstreak@90: UnregisterStateDriver(f, "reaction") flickerstreak@90: f:SetAttribute( "_onstate-reaction", nil ) flickerstreak@90: statedrivers[bar] = nil flickerstreak@90: end flickerstreak@90: end flickerstreak@90: flickerstreak@90: function SetStateKeybind( bar, key, state ) flickerstreak@90: local f = bar:GetFrame() flickerstreak@90: flickerstreak@90: local kb = keybinds[bar] flickerstreak@90: if kb == nil then flickerstreak@90: if key == nil then flickerstreak@90: -- nothing to do flickerstreak@90: return flickerstreak@90: end flickerstreak@90: kb = { } flickerstreak@90: keybinds[bar] = kb flickerstreak@90: end flickerstreak@90: flickerstreak@90: -- clear the old binding, if any flickerstreak@90: if kb[state] then flickerstreak@90: SetOverrideBinding(f, false, kb[state], nil) flickerstreak@90: end flickerstreak@90: kb[state] = key flickerstreak@90: flickerstreak@90: if key then flickerstreak@90: f:SetAttribute("_onclick", onClickHandler) flickerstreak@90: SetOverrideBindingClick(f, false, key, state, nil) -- state name is the virtual mouse button flickerstreak@90: end flickerstreak@90: end flickerstreak@90: end flickerstreak@90: flickerstreak@67: -- As far as I can tell the macro clauses are NOT locale-specific. flickerstreak@67: local ruleformats = { flickerstreak@67: stealth = "stealth", flickerstreak@67: nostealth = "nostealth", flickerstreak@67: shadowform = "form:1", flickerstreak@67: noshadowform = "noform", flickerstreak@67: pet = "pet", flickerstreak@67: nopet = "nopet", flickerstreak@67: harm = "target=target,harm", flickerstreak@67: help = "target=target,help", flickerstreak@67: notarget = "target=target,noexists", flickerstreak@67: focusharm = "target=focus,harm", flickerstreak@67: focushelp = "target=focus,help", flickerstreak@67: nofocus = "target=focus,noexists", flickerstreak@67: raid = "group:raid", flickerstreak@67: party = "group:party", flickerstreak@67: solo = "nogroup", flickerstreak@67: combat = "combat", flickerstreak@67: nocombat = "nocombat", flickerstreak@75: possess = "bonusbar:5", flickerstreak@67: } flickerstreak@65: flickerstreak@67: -- Have to do these shenanigans instead of hardcoding the stances/forms because flickerstreak@67: -- the ordering varies if the character is missing a form. For warriors flickerstreak@67: -- this is rarely a problem (c'mon, who actually skips the level 10 def stance quest?) flickerstreak@67: -- but for druids it can be. Some people never bother to do the aquatic form quest flickerstreak@75: -- until well past when they get cat form, and stance 5/6 can be flight, tree, or moonkin flickerstreak@67: -- depending on talents. flickerstreak@65: function InitRules() flickerstreak@65: local forms = { } flickerstreak@67: -- sort by icon since it's locale-independent flickerstreak@65: for i = 1, GetNumShapeshiftForms() do flickerstreak@65: local icon = GetShapeshiftFormInfo(i) flickerstreak@65: forms[icon] = i; flickerstreak@65: end flickerstreak@65: -- use 9 if not found since 9 is never a valid stance/form flickerstreak@65: local defensive = forms["Interface\\Icons\\Ability_Warrior_DefensiveStance"] or 9 flickerstreak@65: local berserker = forms["Interface\\Icons\\Ability_Racial_Avatar"] or 9 flickerstreak@65: local bear = forms["Interface\\Icons\\Ability_Racial_BearForm"] or 9 -- bear and dire bear share the same icon flickerstreak@65: local aquatic = forms["Interface\\Icons\\Ability_Druid_AquaticForm"] or 9 flickerstreak@65: local cat = forms["Interface\\Icons\\Ability_Druid_CatForm"] or 9 flickerstreak@65: local travel = forms["Interface\\Icons\\Ability_Druid_TravelForm"] or 9 flickerstreak@75: local tree = forms["Interface\\Icons\\Ability_Druid_TreeofLife"] or 9 flickerstreak@75: local moonkin = forms["Interface\\Icons\\Spell_Nature_ForceOfNature"] or 9 flickerstreak@67: local flight = forms["Interface\\Icons\\Ability_Druid_FlightForm"] or 9 -- flight and swift flight share the same icon flickerstreak@65: flickerstreak@75: ruleformats.battle = "stance:1" flickerstreak@75: ruleformats.defensive = format("stance:%d",defensive) flickerstreak@75: ruleformats.berserker = format("stance:%d",berserker) flickerstreak@75: ruleformats.caster = format("form:0/%d/%d/%d",aquatic, travel, flight) flickerstreak@75: ruleformats.bear = format("form:%d",bear) flickerstreak@75: ruleformats.cat = format("form:%d",cat) flickerstreak@75: ruleformats.tree = format("form:%d",tree) flickerstreak@75: ruleformats.moonkin = format("form:%d",moonkin) flickerstreak@64: end flickerstreak@62: flickerstreak@90: local function BuildRule(states) flickerstreak@75: local rules = { } flickerstreak@75: local default flickerstreak@75: flickerstreak@75: for idx, state in ipairs(fieldsort(states, "rule", "order")) do flickerstreak@75: local c = states[state].rule flickerstreak@75: local type = c.type flickerstreak@75: if type == "default" then flickerstreak@75: default = default or state flickerstreak@75: elseif type == "custom" then flickerstreak@75: if c.custom then flickerstreak@75: -- strip out all spaces from the custom rule flickerstreak@75: table.insert(rules, format("%s %s", c.custom:gsub("%s",""), state)) flickerstreak@75: end flickerstreak@75: elseif type == "any" then flickerstreak@75: if c.values then flickerstreak@75: local clauses = { } flickerstreak@75: for key, value in pairs(c.values) do flickerstreak@75: table.insert(clauses, format("[%s]", ruleformats[key])) flickerstreak@75: end flickerstreak@75: if #clauses > 0 then flickerstreak@75: table.insert(rules, format("%s %s", table.concat(clauses), state)) flickerstreak@75: end flickerstreak@75: end flickerstreak@75: elseif type == "all" then flickerstreak@75: if c.values then flickerstreak@75: local clauses = { } flickerstreak@75: for key, value in pairs(c.values) do flickerstreak@75: table.insert(clauses, ruleformats[key]) flickerstreak@75: end flickerstreak@75: if #clauses > 0 then flickerstreak@75: table.insert(rules, format("%s %s", format("[%s]", table.concat(clauses, ",")), state)) flickerstreak@75: end flickerstreak@75: end flickerstreak@75: end flickerstreak@75: end flickerstreak@75: -- make sure that the default, if any, is last flickerstreak@75: if default then flickerstreak@75: table.insert(rules, default) flickerstreak@75: end flickerstreak@90: return table.concat(rules,";") flickerstreak@90: end flickerstreak@90: flickerstreak@90: local function BuildKeybinds( bar, states ) flickerstreak@90: for name, state in pairs(states) do flickerstreak@90: local type = tfetch(state, "rule", "type") flickerstreak@90: if type == "keybind" then flickerstreak@90: local key = tfetch(state, "rule", "keybind") flickerstreak@90: SetStateKeybind(bar, key, name) flickerstreak@90: else flickerstreak@90: SetStateKeybind(bar, nil, name) -- this clears an existing keybind flickerstreak@90: end flickerstreak@90: end flickerstreak@90: end flickerstreak@90: flickerstreak@90: function GetProperty( bar, state, propname ) flickerstreak@90: return tfetch(module.db.profile.bars, bar:GetName(), "states", state, propname) flickerstreak@90: end flickerstreak@90: flickerstreak@90: function SetProperty( bar, state, propname, value ) flickerstreak@90: local s = tbuild(module.db.profile.bars, bar:GetName(), "states", state) flickerstreak@90: s[propname] = value flickerstreak@90: SetHandlerData(bar, propname, value, state) flickerstreak@90: RefreshState(bar) flickerstreak@90: end flickerstreak@90: flickerstreak@90: function RegisterProperty( propname, snippet ) flickerstreak@90: properties[propname] = snippet or true flickerstreak@90: print("registered property",propname) flickerstreak@90: for _, bar in ReAction:IterateBars() do flickerstreak@90: local states = tfetch(module.db.profile.bars, bar:GetName(), "states") flickerstreak@90: if states then flickerstreak@90: for name, s in pairs(states) do flickerstreak@90: SetHandlerData(bar, propname, s[propname], name) flickerstreak@90: end flickerstreak@90: SetStateDriver(bar, BuildRule(states)) flickerstreak@90: RefreshState(bar) flickerstreak@90: end flickerstreak@90: end flickerstreak@90: end flickerstreak@90: flickerstreak@90: function UnregisterProperty( propname ) flickerstreak@90: properties[propname] = nil flickerstreak@90: for _, bar in ReAction:IterateBars() do flickerstreak@90: SetHandlerData(bar, propname, nil) flickerstreak@90: SetStateDriver(bar, BuildRule(states)) flickerstreak@90: RefreshState(bar) flickerstreak@90: end flickerstreak@75: end flickerstreak@75: flickerstreak@75: function ApplyStates( bar ) flickerstreak@75: local states = tfetch(module.db.profile.bars, bar:GetName(), "states") flickerstreak@75: if states then flickerstreak@90: for propname in pairs(properties) do flickerstreak@90: for name, s in pairs(states) do flickerstreak@90: SetHandlerData(bar, propname, s[propname], name) flickerstreak@90: end flickerstreak@75: end flickerstreak@90: BuildKeybinds(bar, states) flickerstreak@90: SetStateDriver(bar, BuildRule(states)) flickerstreak@90: RefreshState(bar) flickerstreak@68: end flickerstreak@68: end flickerstreak@68: flickerstreak@90: function CleanupStates( bar ) flickerstreak@90: SetStateDriver(bar, nil) flickerstreak@90: end flickerstreak@90: flickerstreak@90: function ShowAll( bar, show ) flickerstreak@90: SetHandlerData(bar, "showAll", show) flickerstreak@90: RefreshState(bar) flickerstreak@90: end flickerstreak@64: end flickerstreak@64: flickerstreak@64: flickerstreak@68: flickerstreak@68: -- module event handlers -- flickerstreak@68: flickerstreak@65: function module:OnInitialize() flickerstreak@65: self.db = ReAction.db:RegisterNamespace( moduleID, flickerstreak@65: { flickerstreak@65: profile = { flickerstreak@65: bars = { }, flickerstreak@65: } flickerstreak@65: } flickerstreak@65: ) flickerstreak@65: flickerstreak@65: InitRules() flickerstreak@65: self:RegisterEvent("PLAYER_AURAS_CHANGED") flickerstreak@65: flickerstreak@65: ReAction:RegisterBarOptionGenerator(self, "GetBarOptions") flickerstreak@65: flickerstreak@65: ReAction.RegisterCallback(self, "OnCreateBar","OnRefreshBar") flickerstreak@90: ReAction.RegisterCallback(self, "OnDestroyBar") flickerstreak@65: ReAction.RegisterCallback(self, "OnRefreshBar") flickerstreak@65: ReAction.RegisterCallback(self, "OnEraseBar") flickerstreak@65: ReAction.RegisterCallback(self, "OnRenameBar") flickerstreak@65: ReAction.RegisterCallback(self, "OnConfigModeChanged") flickerstreak@65: end flickerstreak@65: flickerstreak@65: function module:PLAYER_AURAS_CHANGED() flickerstreak@65: self:UnregisterEvent("PLAYER_AURAS_CHANGED") flickerstreak@66: -- on login the number of stances is 0 until this event fires during the init sequence. flickerstreak@90: -- however if you just reload the UI the number of stances is correct immediately flickerstreak@66: -- and this event won't fire until you gain/lose buffs/debuffs, at which point you might flickerstreak@66: -- be in combat. flickerstreak@66: if not InCombatLockdown() then flickerstreak@66: InitRules() flickerstreak@66: for name, bar in ReAction:IterateBars() do flickerstreak@67: self:OnRefreshBar(nil,bar,name) flickerstreak@66: end flickerstreak@66: end flickerstreak@65: end flickerstreak@65: flickerstreak@65: function module:OnRefreshBar(event, bar, name) flickerstreak@65: local c = self.db.profile.bars[name] flickerstreak@65: if c then flickerstreak@68: ApplyStates(bar) flickerstreak@65: end flickerstreak@65: end flickerstreak@65: flickerstreak@90: function module:OnDestroyBar(event, bar, name) flickerstreak@90: CleanupStates(bar) flickerstreak@90: end flickerstreak@90: flickerstreak@65: function module:OnEraseBar(event, bar, name) flickerstreak@65: self.db.profile.bars[name] = nil flickerstreak@65: end flickerstreak@65: flickerstreak@65: function module:OnRenameBar(event, bar, oldname, newname) flickerstreak@75: local bars = self.db.profile.bars flickerstreak@65: bars[newname], bars[oldname] = bars[oldname], nil flickerstreak@65: end flickerstreak@65: flickerstreak@65: function module:OnConfigModeChanged(event, mode) flickerstreak@90: for name, bar in ReAction:IterateBars() do flickerstreak@90: if self.db.profile.bars[name] then flickerstreak@90: ShowAll(bar, mode) flickerstreak@90: end flickerstreak@90: end flickerstreak@65: end flickerstreak@65: flickerstreak@64: flickerstreak@64: flickerstreak@64: -- Options -- flickerstreak@64: flickerstreak@79: local CreateBarOptions, RegisterPropertyOptions flickerstreak@62: do flickerstreak@79: local playerClass = select(2, UnitClass("player")) flickerstreak@62: local function ClassCheck(...) flickerstreak@62: for i = 1, select('#',...) do flickerstreak@79: if playerClass == select(i,...) then flickerstreak@62: return false flickerstreak@62: end flickerstreak@62: end flickerstreak@62: return true flickerstreak@62: end flickerstreak@62: flickerstreak@64: -- pre-sorted by the order they should appear in flickerstreak@64: local rules = { flickerstreak@64: -- rule hidden fields flickerstreak@64: { "stance", ClassCheck("WARRIOR"), { {battle = L["Battle Stance"]}, {defensive = L["Defensive Stance"]}, {berserker = L["Berserker Stance"]} } }, flickerstreak@75: { "form", ClassCheck("DRUID"), { {caster = L["Caster Form"]}, {bear = L["Bear Form"]}, {cat = L["Cat Form"]}, {tree = L["Tree of Life"]}, {moonkin = L["Moonkin Form"]} } }, flickerstreak@64: { "stealth", ClassCheck("ROGUE","DRUID"), { {stealth = L["Stealth"]}, {nostealth = L["No Stealth"]} } }, flickerstreak@64: { "shadow", ClassCheck("PRIEST"), { {shadowform = L["Shadowform"]}, {noshadowform = L["No Shadowform"]} } }, flickerstreak@64: { "pet", ClassCheck("HUNTER","WARLOCK"), { {pet = L["With Pet"]}, {nopet = L["Without Pet"]} } }, flickerstreak@64: { "target", false, { {harm = L["Hostile Target"]}, {help = L["Friendly Target"]}, {notarget = L["No Target"]} } }, flickerstreak@64: { "focus", false, { {focusharm = L["Hostile Focus"]}, {focushelp = L["Friendly Focus"]}, {nofocus = L["No Focus"]} } }, flickerstreak@75: { "possess", false, { {possess = L["Mind Control"]} } }, flickerstreak@64: { "group", false, { {raid = L["Raid"]}, {party = L["Party"]}, {solo = L["Solo"]} } }, flickerstreak@64: { "combat", false, { {combat = L["In Combat"]}, {nocombat = L["Out of Combat"]} } }, flickerstreak@62: } flickerstreak@62: flickerstreak@64: local ruleSelect = { } flickerstreak@64: local ruleMap = { } flickerstreak@64: local optionMap = setmetatable({},{__mode="k"}) flickerstreak@62: flickerstreak@68: local pointTable = { flickerstreak@68: NONE = " ", flickerstreak@68: CENTER = L["Center"], flickerstreak@68: LEFT = L["Left"], flickerstreak@68: RIGHT = L["Right"], flickerstreak@68: TOP = L["Top"], flickerstreak@68: BOTTOM = L["Bottom"], flickerstreak@68: TOPLEFT = L["Top Left"], flickerstreak@68: TOPRIGHT = L["Top Right"], flickerstreak@68: BOTTOMLEFT = L["Bottom Left"], flickerstreak@68: BOTTOMRIGHT = L["Bottom Right"], flickerstreak@68: } flickerstreak@68: flickerstreak@64: -- unpack rules table into ruleSelect and ruleMap flickerstreak@64: for _, c in ipairs(rules) do flickerstreak@64: local rule, hidden, fields = unpack(c) flickerstreak@64: if not hidden then flickerstreak@64: for _, field in ipairs(fields) do flickerstreak@64: local key, label = next(field) flickerstreak@64: table.insert(ruleSelect, label) flickerstreak@64: table.insert(ruleMap, key) flickerstreak@62: end flickerstreak@62: end flickerstreak@62: end flickerstreak@62: flickerstreak@79: local stateOptions = { flickerstreak@79: ordering = { flickerstreak@79: name = L["Info"], flickerstreak@79: order = 1, flickerstreak@79: type = "group", flickerstreak@79: args = { flickerstreak@79: delete = { flickerstreak@79: name = L["Delete this State"], flickerstreak@79: order = -1, flickerstreak@79: type = "execute", flickerstreak@79: func = "DeleteState", flickerstreak@79: }, flickerstreak@79: rename = { flickerstreak@79: name = L["Name"], flickerstreak@79: order = 1, flickerstreak@79: type = "input", flickerstreak@79: get = "GetName", flickerstreak@79: set = "SetStateName", flickerstreak@79: pattern = "^%w*$", flickerstreak@79: usage = L["State names must be alphanumeric without spaces"], flickerstreak@79: }, flickerstreak@79: ordering = { flickerstreak@79: name = L["Evaluation Order"], flickerstreak@79: desc = L["State transitions are evaluated in the order listed:\nMove a state up or down to change the order"], flickerstreak@79: order = 2, flickerstreak@79: type = "group", flickerstreak@79: inline = true, flickerstreak@79: args = { flickerstreak@79: up = { flickerstreak@79: name = L["Up"], flickerstreak@79: order = 1, flickerstreak@79: type = "execute", flickerstreak@79: width = "half", flickerstreak@79: func = "MoveStateUp", flickerstreak@79: }, flickerstreak@79: down = { flickerstreak@79: name = L["Down"], flickerstreak@79: order = 2, flickerstreak@79: type = "execute", flickerstreak@79: width = "half", flickerstreak@79: func = "MoveStateDown", flickerstreak@79: } flickerstreak@79: } flickerstreak@79: } flickerstreak@79: } flickerstreak@79: }, flickerstreak@79: properties = { flickerstreak@79: name = L["Properties"], flickerstreak@79: order = 2, flickerstreak@79: type = "group", flickerstreak@79: args = { flickerstreak@79: desc = { flickerstreak@79: name = L["Set the properties for the bar when in this state"], flickerstreak@79: order = 1, flickerstreak@79: type = "description" flickerstreak@79: }, flickerstreak@79: hide = { flickerstreak@79: name = L["Hide Bar"], flickerstreak@81: order = 90, flickerstreak@79: type = "toggle", flickerstreak@79: set = "SetProp", flickerstreak@79: get = "GetProp", flickerstreak@79: }, flickerstreak@90: --[[ BROKEN flickerstreak@90: keybindState = { flickerstreak@79: name = L["Override Keybinds"], flickerstreak@79: desc = L["Set this state to maintain its own set of keybinds which override the defaults when active"], flickerstreak@81: order = 91, flickerstreak@79: type = "toggle", flickerstreak@79: set = "SetProp", flickerstreak@79: get = "GetProp", flickerstreak@90: }, ]] flickerstreak@79: position = { flickerstreak@79: name = L["Position"], flickerstreak@81: order = 92, flickerstreak@79: type = "group", flickerstreak@79: inline = true, flickerstreak@79: args = { flickerstreak@90: anchorEnable = { flickerstreak@79: name = L["Set New Position"], flickerstreak@79: order = 1, flickerstreak@79: type = "toggle", flickerstreak@79: set = "SetProp", flickerstreak@79: get = "GetProp", flickerstreak@79: }, flickerstreak@90: --[[ TODO: broken flickerstreak@90: anchorFrame = { flickerstreak@90: name = L["Anchor Frame"], flickerstreak@90: order = 2, flickerstreak@90: type = "select", flickerstreak@90: values = "GetAnchorFrames", flickerstreak@90: set = ??? flickerstreak@90: get = ??? flickerstreak@90: }, ]] flickerstreak@79: anchorPoint = { flickerstreak@79: name = L["Point"], flickerstreak@90: order = 3, flickerstreak@79: type = "select", flickerstreak@79: values = pointTable, flickerstreak@79: set = "SetAnchorPointProp", flickerstreak@79: get = "GetAnchorPointProp", flickerstreak@79: disabled = "GetAnchorDisabled", flickerstreak@79: hidden = "GetAnchorDisabled", flickerstreak@79: }, flickerstreak@79: anchorRelPoint = { flickerstreak@79: name = L["Relative Point"], flickerstreak@90: order = 4, flickerstreak@79: type = "select", flickerstreak@79: values = pointTable, flickerstreak@79: set = "SetAnchorPointProp", flickerstreak@79: get = "GetAnchorPointProp", flickerstreak@79: disabled = "GetAnchorDisabled", flickerstreak@79: hidden = "GetAnchorDisabled", flickerstreak@79: }, flickerstreak@79: anchorX = { flickerstreak@79: name = L["X Offset"], flickerstreak@90: order = 5, flickerstreak@79: type = "range", flickerstreak@79: min = -100, flickerstreak@79: max = 100, flickerstreak@79: step = 1, flickerstreak@79: set = "SetProp", flickerstreak@79: get = "GetProp", flickerstreak@79: disabled = "GetAnchorDisabled", flickerstreak@79: hidden = "GetAnchorDisabled", flickerstreak@79: }, flickerstreak@79: anchorY = { flickerstreak@79: name = L["Y Offset"], flickerstreak@90: order = 6, flickerstreak@79: type = "range", flickerstreak@79: min = -100, flickerstreak@79: max = 100, flickerstreak@79: step = 1, flickerstreak@79: set = "SetProp", flickerstreak@79: get = "GetProp", flickerstreak@79: disabled = "GetAnchorDisabled", flickerstreak@79: hidden = "GetAnchorDisabled", flickerstreak@79: }, flickerstreak@79: }, flickerstreak@79: }, flickerstreak@79: scale = { flickerstreak@79: name = L["Scale"], flickerstreak@81: order = 93, flickerstreak@79: type = "group", flickerstreak@79: inline = true, flickerstreak@79: args = { flickerstreak@79: enableScale = { flickerstreak@79: name = L["Set New Scale"], flickerstreak@79: order = 1, flickerstreak@79: type = "toggle", flickerstreak@79: set = "SetProp", flickerstreak@79: get = "GetProp", flickerstreak@79: }, flickerstreak@79: scale = { flickerstreak@79: name = L["Scale"], flickerstreak@79: order = 2, flickerstreak@79: type = "range", flickerstreak@79: min = 0.1, flickerstreak@79: max = 2.5, flickerstreak@79: step = 0.05, flickerstreak@79: isPercent = true, flickerstreak@79: set = "SetProp", flickerstreak@79: get = "GetProp", flickerstreak@79: disabled = "GetScaleDisabled", flickerstreak@79: hidden = "GetScaleDisabled", flickerstreak@79: }, flickerstreak@79: }, flickerstreak@79: }, flickerstreak@79: }, flickerstreak@79: plugins = { } flickerstreak@79: }, flickerstreak@79: rules = { flickerstreak@79: name = L["Rule"], flickerstreak@79: order = 3, flickerstreak@79: type = "group", flickerstreak@79: args = { flickerstreak@79: mode = { flickerstreak@79: name = L["Select this state"], flickerstreak@79: order = 2, flickerstreak@79: type = "select", flickerstreak@79: style = "radio", flickerstreak@79: values = { flickerstreak@79: default = L["by default"], flickerstreak@79: any = L["when ANY of these"], flickerstreak@79: all = L["when ALL of these"], flickerstreak@79: custom = L["via custom rule"], flickerstreak@79: keybind = L["via keybinding"], flickerstreak@79: }, flickerstreak@79: set = "SetType", flickerstreak@79: get = "GetType", flickerstreak@79: }, flickerstreak@79: clear = { flickerstreak@79: name = L["Clear All"], flickerstreak@79: order = 3, flickerstreak@79: type = "execute", flickerstreak@79: hidden = "GetClearAllDisabled", flickerstreak@79: disabled = "GetClearAllDisabled", flickerstreak@79: func = "ClearAllConditions", flickerstreak@79: }, flickerstreak@79: inputs = { flickerstreak@79: name = L["Conditions"], flickerstreak@79: order = 4, flickerstreak@79: type = "multiselect", flickerstreak@79: hidden = "GetConditionsDisabled", flickerstreak@79: disabled = "GetConditionsDisabled", flickerstreak@79: values = ruleSelect, flickerstreak@79: set = "SetCondition", flickerstreak@79: get = "GetCondition", flickerstreak@79: }, flickerstreak@79: custom = { flickerstreak@79: name = L["Custom Rule"], flickerstreak@79: order = 5, flickerstreak@79: type = "input", flickerstreak@79: multiline = true, flickerstreak@79: hidden = "GetCustomDisabled", flickerstreak@79: disabled = "GetCustomDisabled", flickerstreak@79: desc = L["Syntax like macro rules: see preset rules for examples"], flickerstreak@79: set = "SetCustomRule", flickerstreak@79: get = "GetCustomRule", flickerstreak@79: validate = "ValidateCustomRule", flickerstreak@79: }, flickerstreak@79: keybind = { flickerstreak@79: name = L["Keybinding"], flickerstreak@79: order = 6, flickerstreak@79: inline = true, flickerstreak@79: hidden = "GetKeybindDisabled", flickerstreak@79: disabled = "GetKeybindDisabled", flickerstreak@79: type = "group", flickerstreak@79: args = { flickerstreak@79: desc = { flickerstreak@79: name = L["Invoking a state keybind toggles an override of all other transition rules."], flickerstreak@79: order = 1, flickerstreak@79: type = "description", flickerstreak@79: }, flickerstreak@79: keybind = { flickerstreak@79: name = L["State Hotkey"], flickerstreak@79: desc = L["Define an override toggle keybind"], flickerstreak@79: order = 2, flickerstreak@79: type = "keybinding", flickerstreak@79: set = "SetKeybind", flickerstreak@79: get = "GetKeybind", flickerstreak@79: }, flickerstreak@79: }, flickerstreak@79: }, flickerstreak@79: }, flickerstreak@79: }, flickerstreak@79: } flickerstreak@79: flickerstreak@90: local handlers = { } flickerstreak@90: local meta = { flickerstreak@90: __index = function(self, key) flickerstreak@90: for _, h in pairs(handlers) do flickerstreak@90: if h[key] then flickerstreak@90: return h[key] flickerstreak@90: end flickerstreak@90: end flickerstreak@90: end, flickerstreak@90: } flickerstreak@90: local StateHandler = setmetatable({ }, meta) flickerstreak@90: local proto = { __index = StateHandler } flickerstreak@90: flickerstreak@90: function RegisterPropertyOptions( field, options, handler ) flickerstreak@90: stateOptions.properties.plugins[field] = options flickerstreak@90: handlers[field] = handler flickerstreak@90: end flickerstreak@90: flickerstreak@90: function UnregisterPropertyOptions( field ) flickerstreak@90: stateOptions.properties.plugins[field] = nil flickerstreak@90: handlers[field] = nil flickerstreak@90: end flickerstreak@79: flickerstreak@79: function StateHandler:New( bar, opts ) flickerstreak@90: local self = setmetatable( flickerstreak@90: { flickerstreak@90: bar = bar flickerstreak@90: }, flickerstreak@90: proto ) flickerstreak@79: flickerstreak@79: function self:GetName() flickerstreak@79: return opts.name flickerstreak@79: end flickerstreak@79: flickerstreak@79: function self:SetName(name) flickerstreak@79: opts.name = name flickerstreak@79: end flickerstreak@79: flickerstreak@79: function self:GetOrder() flickerstreak@79: return opts.order flickerstreak@79: end flickerstreak@79: flickerstreak@79: -- get reference to states table: even if the bar flickerstreak@79: -- name changes the states table ref won't flickerstreak@79: self.states = tbuild(module.db.profile.bars, bar:GetName(), "states") flickerstreak@90: self.state = tbuild(self.states, opts.name) flickerstreak@79: flickerstreak@90: opts.order = self:GetRuleField("order") flickerstreak@79: if opts.order == nil then flickerstreak@79: -- add after the highest flickerstreak@79: opts.order = 100 flickerstreak@79: for _, state in pairs(self.states) do flickerstreak@79: local x = tonumber(tfetch(state, "rule", "order")) flickerstreak@79: if x and x >= opts.order then flickerstreak@79: opts.order = x + 1 flickerstreak@79: end flickerstreak@79: end flickerstreak@90: self:SetRuleField("order",opts.order) flickerstreak@79: end flickerstreak@79: flickerstreak@79: return self flickerstreak@79: end flickerstreak@79: flickerstreak@79: -- helper methods flickerstreak@79: flickerstreak@90: function StateHandler:SetRuleField( key, value, ... ) flickerstreak@90: tbuild(self.state, "rule", ...)[key] = value flickerstreak@79: end flickerstreak@79: flickerstreak@90: function StateHandler:GetRuleField( ... ) flickerstreak@90: return tfetch(self.state, "rule", ...) flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:FixAll( setkey ) flickerstreak@79: -- if multiple selections in the same group are chosen when 'all' is selected, flickerstreak@79: -- keep only one of them. If changing the mode, the first in the fields list will flickerstreak@79: -- be chosen arbitrarily. Otherwise, if selecting a new checkbox from the field-set, flickerstreak@79: -- it will be retained. flickerstreak@79: local notified = false flickerstreak@90: if self:GetRuleField("type") == "all" then flickerstreak@79: for _, c in ipairs(rules) do flickerstreak@79: local rule, hidden, fields = unpack(c) flickerstreak@79: local once = false flickerstreak@79: if setkey then flickerstreak@79: for idx, field in ipairs(fields) do flickerstreak@79: if next(field) == setkey then flickerstreak@79: once = true flickerstreak@79: end flickerstreak@79: end flickerstreak@79: end flickerstreak@79: for idx, field in ipairs(fields) do flickerstreak@79: local key = next(field) flickerstreak@90: if self:GetRuleField("values",key) then flickerstreak@79: if once and key ~= setkey then flickerstreak@90: self:SetRuleField(key,false,"values") flickerstreak@79: if not setkey and not notified then flickerstreak@79: ReAction:UserError(L["Warning: one or more incompatible rules were turned off"]) flickerstreak@79: notified = true flickerstreak@79: end flickerstreak@79: end flickerstreak@79: once = true flickerstreak@79: end flickerstreak@79: end flickerstreak@79: end flickerstreak@79: end flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:GetNeighbors() flickerstreak@79: local before, after flickerstreak@79: for k, v in pairs(self.states) do flickerstreak@79: local o = tonumber(tfetch(v, "rule", "order")) flickerstreak@79: if o and k ~= self:GetName() then flickerstreak@79: local obefore = tfetch(self.states,before,"rule","order") flickerstreak@79: local oafter = tfetch(self.states,after,"rule","order") flickerstreak@79: if o < self:GetOrder() and (not obefore or obefore < o) then flickerstreak@79: before = k flickerstreak@79: end flickerstreak@79: if o > self:GetOrder() and (not oafter or oafter > o) then flickerstreak@79: after = k flickerstreak@79: end flickerstreak@79: end flickerstreak@79: end flickerstreak@79: return before, after flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:SwapOrder( a, b ) flickerstreak@79: -- do options table flickerstreak@79: local args = optionMap[self.bar].args flickerstreak@79: args[a].order, args[b].order = args[b].order, args[a].order flickerstreak@79: -- do profile flickerstreak@79: a = tbuild(self.states, a, "rule") flickerstreak@79: b = tbuild(self.states, b, "rule") flickerstreak@79: a.order, b.order = b.order, a.order flickerstreak@79: end flickerstreak@79: flickerstreak@79: -- handler methods flickerstreak@79: flickerstreak@79: function StateHandler:GetProp( info ) flickerstreak@79: -- gets property of the same name as the options arg flickerstreak@79: return GetProperty(self.bar, self:GetName(), info[#info]) flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:SetProp( info, value ) flickerstreak@79: -- sets property of the same name as the options arg flickerstreak@79: SetProperty(self.bar, self:GetName(), info[#info], value) flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:DeleteState() flickerstreak@79: if self.states[self:GetName()] then flickerstreak@79: self.states[self:GetName()] = nil flickerstreak@79: ApplyStates(self.bar) flickerstreak@79: end flickerstreak@79: optionMap[self.bar].args[self:GetName()] = nil flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:SetStateName(info, value) flickerstreak@79: -- check for existing state name flickerstreak@79: if self.states[value] then flickerstreak@79: ReAction:UserError(format(L["State named '%s' already exists"],value)) flickerstreak@79: return flickerstreak@79: end flickerstreak@79: local args = optionMap[self.bar].args flickerstreak@79: local name = self:GetName() flickerstreak@79: self.states[value], args[value], self.states[name], args[name] = self.states[name], args[name], nil, nil flickerstreak@79: self:SetName(value) flickerstreak@79: ApplyStates(self.bar) flickerstreak@83: ReAction:ShowEditor(self.bar, moduleID, value) flickerstreak@83: end flickerstreak@79: flickerstreak@79: function StateHandler:MoveStateUp() flickerstreak@79: local before, after = self:GetNeighbors() flickerstreak@79: if before then flickerstreak@79: self:SwapOrder(before, self:GetName()) flickerstreak@79: ApplyStates(self.bar) flickerstreak@79: end flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:MoveStateDown() flickerstreak@79: local before, after = self:GetNeighbors() flickerstreak@79: if after then flickerstreak@79: self:SwapOrder(self:GetName(), after) flickerstreak@79: ApplyStates(self.bar) flickerstreak@79: end flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:GetAnchorDisabled() flickerstreak@79: return not GetProperty(self.bar, self:GetName(), "enableAnchor") flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:SetAnchorPointProp(info, value) flickerstreak@79: self:SetProp(info, value ~= "NONE" and value or nil) flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:GetAnchorPointProp(info) flickerstreak@79: return self:GetProp(info) or "NONE" flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:GetScaleDisabled() flickerstreak@79: return not GetProperty(self.bar, self:GetName(), "enableScale") flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:SetType(info, value) flickerstreak@90: self:SetRuleField("type", value) flickerstreak@79: self:FixAll() flickerstreak@79: ApplyStates(self.bar) flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:GetType() flickerstreak@90: return self:GetRuleField("type") flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:GetClearAllDisabled() flickerstreak@90: local t = self:GetRuleField("type") flickerstreak@79: return not( t == "any" or t == "all" or t == "custom") flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:ClearAllConditions() flickerstreak@90: local t = self:GetRuleField("type") flickerstreak@79: if t == "custom" then flickerstreak@90: self:SetRuleField("custom","") flickerstreak@79: elseif t == "any" or t == "all" then flickerstreak@90: self:SetRuleField("values", {}) flickerstreak@79: end flickerstreak@79: ApplyStates(self.bar) flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:GetConditionsDisabled() flickerstreak@90: local t = self:GetRuleField("type") flickerstreak@79: return not( t == "any" or t == "all") flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:SetCondition(info, key, value) flickerstreak@90: self:SetRuleField(ruleMap[key], value or nil, "values") flickerstreak@79: if value then flickerstreak@79: self:FixAll(ruleMap[key]) flickerstreak@79: end flickerstreak@79: ApplyStates(self.bar) flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:GetCondition(info, key) flickerstreak@90: return self:GetRuleField("values", ruleMap[key]) or false flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:GetCustomDisabled() flickerstreak@90: return self:GetRuleField("type") ~= "custom" flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:SetCustomRule(info, value) flickerstreak@90: self:SetRuleField("custom",value) flickerstreak@79: ApplyStates(self.bar) flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:GetCustomRule() flickerstreak@90: return self:GetRuleField("custom") or "" flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:ValidateCustomRule(info, value) flickerstreak@79: local s = value:gsub("%s","") -- remove all spaces flickerstreak@79: -- unfortunately %b and captures don't support the '+' notation, or this would be considerably simpler flickerstreak@79: repeat flickerstreak@79: if s == "" then flickerstreak@79: return true flickerstreak@79: end flickerstreak@79: local c, r = s:match("(%b[])(.*)") flickerstreak@79: if c == nil and s and #s > 0 then flickerstreak@79: return format(L["Invalid custom rule '%s': each clause must appear within [brackets]"],value or "") flickerstreak@79: end flickerstreak@79: s = r flickerstreak@79: until c == nil flickerstreak@79: return true flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:GetKeybindDisabled() flickerstreak@90: return self:GetRuleField("type") ~= "keybind" flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:GetKeybind() flickerstreak@90: return self:GetRuleField("keybind") flickerstreak@79: end flickerstreak@79: flickerstreak@79: function StateHandler:SetKeybind(info, value) flickerstreak@79: if value and #value == 0 then flickerstreak@79: value = nil flickerstreak@79: end flickerstreak@90: self:SetRuleField("keybind",value) flickerstreak@79: ApplyStates(self.bar) flickerstreak@79: end flickerstreak@79: flickerstreak@62: local function CreateStateOptions(bar, name) flickerstreak@62: local opts = { flickerstreak@62: type = "group", flickerstreak@62: name = name, flickerstreak@64: childGroups = "tab", flickerstreak@79: args = stateOptions flickerstreak@25: } flickerstreak@62: flickerstreak@79: opts.handler = StateHandler:New(bar,opts) flickerstreak@64: flickerstreak@62: return opts flickerstreak@25: end flickerstreak@62: flickerstreak@79: function module:GetBarOptions(bar) flickerstreak@62: local private = { } flickerstreak@75: local states = tbuild(module.db.profile.bars, bar:GetName(), "states") flickerstreak@62: local options = { flickerstreak@77: name = L["Dynamic State"], flickerstreak@62: type = "group", flickerstreak@77: order = -1, flickerstreak@64: childGroups = "tree", flickerstreak@62: disabled = InCombatLockdown, flickerstreak@62: args = { flickerstreak@64: __desc__ = { flickerstreak@68: name = L["States are evaluated in the order they are listed"], flickerstreak@68: order = 1, flickerstreak@64: type = "description", flickerstreak@64: }, flickerstreak@64: __new__ = { flickerstreak@64: name = L["New State..."], flickerstreak@64: order = 2, flickerstreak@68: type = "group", flickerstreak@62: args = { flickerstreak@64: name = { flickerstreak@64: name = L["State Name"], flickerstreak@64: desc = L["Set a name for the new state"], flickerstreak@68: order = 1, flickerstreak@68: type = "input", flickerstreak@64: get = function() return private.newstatename or "" end, flickerstreak@64: set = function(info,value) private.newstatename = value end, flickerstreak@64: pattern = "^%w*$", flickerstreak@64: usage = L["State names must be alphanumeric without spaces"], flickerstreak@64: }, flickerstreak@64: create = { flickerstreak@68: name = L["Create State"], flickerstreak@68: order = 2, flickerstreak@64: type = "execute", flickerstreak@64: func = function () flickerstreak@64: local name = private.newstatename flickerstreak@68: if states[name] then flickerstreak@75: ReAction:UserError(format(L["State named '%s' already exists"],name)) flickerstreak@68: else flickerstreak@68: -- TODO: select default state options and pass as final argument flickerstreak@68: states[name] = { } flickerstreak@68: optionMap[bar].args[name] = CreateStateOptions(bar,name) flickerstreak@81: ReAction:ShowEditor(bar, moduleID, name) flickerstreak@68: private.newstatename = "" flickerstreak@68: end flickerstreak@64: end, flickerstreak@64: disabled = function() flickerstreak@64: local name = private.newstatename or "" flickerstreak@64: return #name == 0 or name:find("%W") flickerstreak@64: end, flickerstreak@62: } flickerstreak@62: } flickerstreak@64: } flickerstreak@62: } flickerstreak@62: } flickerstreak@79: for name, config in pairs(states) do flickerstreak@79: options.args[name] = CreateStateOptions(bar,name) flickerstreak@62: end flickerstreak@64: optionMap[bar] = options flickerstreak@62: return options flickerstreak@62: end flickerstreak@25: end flickerstreak@25: flickerstreak@79: -- Module API -- flickerstreak@79: flickerstreak@90: -- Pass in a property field-name, an implementation secure snippet, a static options table, and an flickerstreak@79: -- optional options handler method-table flickerstreak@79: -- flickerstreak@79: -- The options table is static, i.e. not bar-specific and should only reference handler method flickerstreak@79: -- strings (either existing ones or those added via optHandler). The existing options are ordered flickerstreak@81: -- 90-99. Order #1 is reserved for the heading. flickerstreak@79: -- flickerstreak@90: -- The contents of optHandler, if provided, will be added to the existing StateHandler options metatable. flickerstreak@79: -- See above, for existing API. In particular see the properties set up in the New method: self.bar, flickerstreak@79: -- self.states, and self:GetName(), and the generic property handlers self:GetProp() and self:SetProp(). flickerstreak@79: -- flickerstreak@90: function module:RegisterStateProperty( field, snippetHandler, options, optHandler ) flickerstreak@90: RegisterProperty(field, snippetHandler) flickerstreak@79: RegisterPropertyOptions(field, options, optHandler) flickerstreak@25: end flickerstreak@79: flickerstreak@90: function module:UnregisterStateProperty( field ) flickerstreak@90: UnregisterProperty(field) flickerstreak@90: UnregisterPropertyOptions(field) flickerstreak@90: end