annotate State.lua @ 71:3d2cef5dc459

Implemented state anchoring and scaling
author Flick <flickerstreak@gmail.com>
date Wed, 04 Jun 2008 21:46:51 +0000
parents 2c12e2b1752e
children aa88aed52124
rev   line source
flickerstreak@25 1 --[[
flickerstreak@62 2 ReAction bar state driver interface
flickerstreak@25 3
flickerstreak@25 4 --]]
flickerstreak@25 5
flickerstreak@25 6 -- local imports
flickerstreak@25 7 local ReAction = ReAction
flickerstreak@25 8 local L = ReAction.L
flickerstreak@25 9 local _G = _G
flickerstreak@25 10 local InCombatLockdown = InCombatLockdown
flickerstreak@25 11
flickerstreak@25 12 -- module declaration
flickerstreak@25 13 local moduleID = "State"
flickerstreak@65 14 local module = ReAction:NewModule( moduleID, "AceEvent-3.0" )
flickerstreak@62 15
flickerstreak@64 16 -- Utility --
flickerstreak@62 17
flickerstreak@62 18 -- traverse a table tree by key list and fetch the result or first nil
flickerstreak@62 19 local function tfetch(t, ...)
flickerstreak@62 20 for i = 1, select('#', ...) do
flickerstreak@62 21 t = t and t[select(i, ...)]
flickerstreak@62 22 end
flickerstreak@62 23 return t
flickerstreak@62 24 end
flickerstreak@62 25
flickerstreak@62 26 -- traverse a table tree by key list and build tree as necessary
flickerstreak@62 27 local function tbuild(t, ...)
flickerstreak@62 28 for i = 1, select('#', ...) do
flickerstreak@62 29 local key = select(i, ...)
flickerstreak@62 30 if not t[key] then t[key] = { } end
flickerstreak@62 31 t = t[key]
flickerstreak@62 32 end
flickerstreak@62 33 return t
flickerstreak@62 34 end
flickerstreak@62 35
flickerstreak@64 36 -- PRIVATE --
flickerstreak@68 37
flickerstreak@68 38 local InitRules, ApplyStates, SetProperty, GetProperty
flickerstreak@64 39 do
flickerstreak@67 40 -- As far as I can tell the macro clauses are NOT locale-specific.
flickerstreak@67 41 local ruleformats = {
flickerstreak@67 42 stealth = "stealth",
flickerstreak@67 43 nostealth = "nostealth",
flickerstreak@67 44 shadowform = "form:1",
flickerstreak@67 45 noshadowform = "noform",
flickerstreak@67 46 pet = "pet",
flickerstreak@67 47 nopet = "nopet",
flickerstreak@67 48 harm = "target=target,harm",
flickerstreak@67 49 help = "target=target,help",
flickerstreak@67 50 notarget = "target=target,noexists",
flickerstreak@67 51 focusharm = "target=focus,harm",
flickerstreak@67 52 focushelp = "target=focus,help",
flickerstreak@67 53 nofocus = "target=focus,noexists",
flickerstreak@67 54 raid = "group:raid",
flickerstreak@67 55 party = "group:party",
flickerstreak@67 56 solo = "nogroup",
flickerstreak@67 57 combat = "combat",
flickerstreak@67 58 nocombat = "nocombat",
flickerstreak@67 59 }
flickerstreak@65 60
flickerstreak@67 61 -- Have to do these shenanigans instead of hardcoding the stances/forms because
flickerstreak@67 62 -- the ordering varies if the character is missing a form. For warriors
flickerstreak@67 63 -- this is rarely a problem (c'mon, who actually skips the level 10 def stance quest?)
flickerstreak@67 64 -- but for druids it can be. Some people never bother to do the aquatic form quest
flickerstreak@67 65 -- until well past when they get cat form, and stance 5 can be flight, tree, or moonkin
flickerstreak@67 66 -- depending on talents.
flickerstreak@65 67 function InitRules()
flickerstreak@65 68 local forms = { }
flickerstreak@67 69 -- sort by icon since it's locale-independent
flickerstreak@65 70 for i = 1, GetNumShapeshiftForms() do
flickerstreak@65 71 local icon = GetShapeshiftFormInfo(i)
flickerstreak@65 72 forms[icon] = i;
flickerstreak@65 73 end
flickerstreak@65 74 -- use 9 if not found since 9 is never a valid stance/form
flickerstreak@65 75 local defensive = forms["Interface\\Icons\\Ability_Warrior_DefensiveStance"] or 9
flickerstreak@65 76 local berserker = forms["Interface\\Icons\\Ability_Racial_Avatar"] or 9
flickerstreak@65 77 local bear = forms["Interface\\Icons\\Ability_Racial_BearForm"] or 9 -- bear and dire bear share the same icon
flickerstreak@65 78 local aquatic = forms["Interface\\Icons\\Ability_Druid_AquaticForm"] or 9
flickerstreak@65 79 local cat = forms["Interface\\Icons\\Ability_Druid_CatForm"] or 9
flickerstreak@65 80 local travel = forms["Interface\\Icons\\Ability_Druid_TravelForm"] or 9
flickerstreak@65 81 local treekin = forms["Interface\\Icons\\Ability_Druid_TreeofLife"] or forms["Interface\\Icons\\Spell_Nature_ForceOfNature"] or 9
flickerstreak@67 82 local flight = forms["Interface\\Icons\\Ability_Druid_FlightForm"] or 9 -- flight and swift flight share the same icon
flickerstreak@65 83
flickerstreak@65 84 ruleformats.battle = "stance:1"
flickerstreak@65 85 ruleformats.defensive = ("stance:%d"):format(defensive)
flickerstreak@65 86 ruleformats.berserker = ("stance:%d"):format(berserker)
flickerstreak@65 87 ruleformats.caster = ("form:0/%d/%d/%d"):format(aquatic, travel, flight)
flickerstreak@65 88 ruleformats.bear = ("form:%d"):format(bear)
flickerstreak@65 89 ruleformats.cat = ("form:%d"):format(cat)
flickerstreak@65 90 ruleformats.treeOrMoonkin = ("form:%d"):format(treekin)
flickerstreak@64 91 end
flickerstreak@62 92
flickerstreak@68 93 -- return a new array of keys of table 't', sorted by comparing
flickerstreak@68 94 -- sub-fields (obtained via tfetch) of the table values
flickerstreak@68 95 local function fieldsort( t, ... )
flickerstreak@68 96 local r = { }
flickerstreak@68 97 for k in pairs(t) do
flickerstreak@68 98 table.insert(r,k)
flickerstreak@68 99 end
flickerstreak@68 100 local dotdotdot = { ... }
flickerstreak@68 101 table.sort(r, function(lhs, rhs)
flickerstreak@68 102 local olhs = tfetch(t[lhs], unpack(dotdotdot)) or 0
flickerstreak@68 103 local orhs = tfetch(t[rhs], unpack(dotdotdot)) or 0
flickerstreak@68 104 return olhs < orhs
flickerstreak@68 105 end)
flickerstreak@68 106 return r
flickerstreak@68 107 end
flickerstreak@68 108
flickerstreak@65 109 local function BuildRuleString(states)
flickerstreak@64 110 local s = ""
flickerstreak@64 111 local default
flickerstreak@68 112 local sorted = fieldsort(states, "rule", "order")
flickerstreak@64 113 for idx, name in ipairs(sorted) do
flickerstreak@64 114 local state = states[name]
flickerstreak@64 115 local semi = #s > 0 and "; " or ""
flickerstreak@64 116 local mode = tfetch(state,"rule","type")
flickerstreak@64 117 if mode == "default" then
flickerstreak@64 118 default = name
flickerstreak@64 119 elseif mode == "custom" then
flickerstreak@64 120 if state.rule.custom then
flickerstreak@64 121 -- strip out all spaces from the custom rule
flickerstreak@64 122 s = ("%s%s%s %s"):format(s, semi, state.rule.custom:gsub("%s",""), name)
flickerstreak@64 123 end
flickerstreak@64 124 elseif mode == "any" then
flickerstreak@64 125 if state.rule.values then
flickerstreak@64 126 local clause = ""
flickerstreak@64 127 for key, value in pairs(state.rule.values) do
flickerstreak@64 128 clause = ("%s[%s]"):format(clause,ruleformats[key])
flickerstreak@64 129 end
flickerstreak@64 130 if #clause > 0 then
flickerstreak@64 131 s = ("%s%s%s %s"):format(s, semi, clause, name)
flickerstreak@64 132 end
flickerstreak@64 133 end
flickerstreak@64 134 elseif mode == "all" then
flickerstreak@64 135 if state.rule.values then
flickerstreak@64 136 local clause = ""
flickerstreak@64 137 for key, value in pairs(state.rule.values) do
flickerstreak@64 138 clause = ("%s%s%s"):format(clause,#clause > 0 and "," or "", ruleformats[key])
flickerstreak@64 139 end
flickerstreak@64 140 if #clause > 0 then
flickerstreak@64 141 s = ("%s%s[%s] %s"):format(s, semi, clause, name)
flickerstreak@64 142 end
flickerstreak@64 143 end
flickerstreak@64 144 end
flickerstreak@64 145 end
flickerstreak@64 146 if default then
flickerstreak@64 147 s = ("%s%s%s"):format(s, #s > 0 and "; " or "", default)
flickerstreak@64 148 end
flickerstreak@68 149 return s, default
flickerstreak@64 150 end
flickerstreak@64 151
flickerstreak@64 152 local drivers = setmetatable({},{__mode="k"})
flickerstreak@68 153 local propertyFuncs = { }
flickerstreak@64 154
flickerstreak@64 155 function ApplyStates( bar )
flickerstreak@64 156 local states = tfetch(module.db.profile.bars, bar:GetName(), "states")
flickerstreak@64 157 if states then
flickerstreak@64 158 local frame = bar:GetFrame()
flickerstreak@68 159 local string, default = BuildRuleString(states)
flickerstreak@64 160 if string and #string > 0 then
flickerstreak@68 161 drivers[bar] = true
flickerstreak@68 162 -- register a map for each "statemap-reaction-XXX" to set 'state' to 'XXX'
flickerstreak@68 163 -- UNLESS we're in a keybound state AND there's a default state, in which case
flickerstreak@68 164 -- all keybound states go back to themselves.
flickerstreak@68 165 local keybindprefix
flickerstreak@68 166 if default then
flickerstreak@68 167 local tmp = { }
flickerstreak@68 168 for state, config in pairs(states) do
flickerstreak@68 169 if tfetch(config, "rule", "type") == "keybind" then
flickerstreak@68 170 bar:SetStateKeybind(tfetch(config,"rule","keybind"), state, tfetch(config,"rule","keybindreturn") or default or 0)
flickerstreak@68 171 table.insert(tmp, ("%s:%s"):format(state,state))
flickerstreak@68 172 end
flickerstreak@68 173 end
flickerstreak@68 174 if #tmp > 0 then
flickerstreak@68 175 table.insert(tmp,"") -- to get a final ';'
flickerstreak@68 176 end
flickerstreak@68 177 keybindprefix = table.concat(tmp,";")
flickerstreak@68 178 end
flickerstreak@68 179 for state in pairs(states) do
flickerstreak@68 180 frame:SetAttribute(("statemap-reaction-%s"):format(state), ("%s%s"):format(keybindprefix or "",state))
flickerstreak@68 181 end
flickerstreak@64 182 -- register a handler to set the value of attribute "state-reaction"
flickerstreak@64 183 -- in response to events as per the rule string
flickerstreak@64 184 RegisterStateDriver(frame, "reaction", string)
flickerstreak@68 185 SecureStateHeader_Refresh(frame)
flickerstreak@64 186 elseif drivers[bar] then
flickerstreak@64 187 UnregisterStateDriver(frame, "reaction")
flickerstreak@64 188 drivers[bar] = nil
flickerstreak@64 189 end
flickerstreak@68 190 for k, f in pairs(propertyFuncs) do
flickerstreak@68 191 f(bar, states)
flickerstreak@68 192 end
flickerstreak@64 193 end
flickerstreak@64 194 end
flickerstreak@68 195
flickerstreak@68 196 function GetProperty( bar, state, propname )
flickerstreak@68 197 return tfetch(module.db.profile.bars, bar:GetName(), "states", state, propname)
flickerstreak@68 198 end
flickerstreak@68 199
flickerstreak@68 200 function SetProperty( bar, state, propname, value )
flickerstreak@68 201 local states = tbuild(module.db.profile.bars, bar:GetName(), "states")
flickerstreak@68 202 tbuild(states, state)[propname] = value
flickerstreak@68 203 local f = propertyFuncs[propname]
flickerstreak@68 204 if f then
flickerstreak@68 205 f(bar, states)
flickerstreak@68 206 end
flickerstreak@68 207 end
flickerstreak@68 208
flickerstreak@68 209 -- state property functions
flickerstreak@68 210 function propertyFuncs.hide( bar, states )
flickerstreak@68 211 local tmp = { }
flickerstreak@68 212 for state, config in pairs(states) do
flickerstreak@68 213 if config.hide then
flickerstreak@68 214 table.insert(tmp, state)
flickerstreak@68 215 end
flickerstreak@68 216 end
flickerstreak@68 217 local s = table.concat(tmp,",")
flickerstreak@68 218 bar:SetHideStates(s)
flickerstreak@68 219 end
flickerstreak@68 220
flickerstreak@68 221 function propertyFuncs.page( bar, states )
flickerstreak@68 222 local map = { }
flickerstreak@68 223 for state, config in pairs(states) do
flickerstreak@68 224 map[state] = config.page
flickerstreak@68 225 end
flickerstreak@68 226 bar:SetStatePageMap(state, map)
flickerstreak@68 227 end
flickerstreak@68 228
flickerstreak@68 229 function propertyFuncs.keybindstate( bar, states )
flickerstreak@68 230 local map = { }
flickerstreak@68 231 for state, config in pairs(states) do
flickerstreak@68 232 if config.keybindstate then
flickerstreak@68 233 table.insert(map,state)
flickerstreak@68 234 end
flickerstreak@68 235 end
flickerstreak@68 236 bar:SetStateKeybindOverrideMap(map)
flickerstreak@68 237 end
flickerstreak@68 238
flickerstreak@71 239 local function updateAnchor(bar, states)
flickerstreak@71 240 local map = { }
flickerstreak@71 241 for state, c in pairs(states) do
flickerstreak@71 242 if c.enableAnchor then
flickerstreak@71 243 map[state] = { point = c.anchorPoint, relpoint = c.anchorRelPoint, x = c.anchorX, y = c.anchorY }
flickerstreak@71 244 end
flickerstreak@71 245 end
flickerstreak@71 246 bar:SetStateAnchorMap(map)
flickerstreak@68 247 end
flickerstreak@68 248
flickerstreak@71 249 propertyFuncs.enableAnchor = updateAnchor
flickerstreak@71 250 propertyFuncs.anchorPoint = updateAnchor
flickerstreak@71 251 propertyFuncs.anchorRelPoint = updateAnchor
flickerstreak@71 252 propertyFuncs.anchorX = updateAnchor
flickerstreak@71 253 propertyFuncs.anchorY = updateAnchor
flickerstreak@68 254
flickerstreak@71 255 local function updateScale( bar, states )
flickerstreak@71 256 local map = { }
flickerstreak@71 257 for state, c in pairs(states) do
flickerstreak@71 258 if c.enablescale then
flickerstreak@71 259 map[state] = c.scale
flickerstreak@71 260 end
flickerstreak@71 261 end
flickerstreak@71 262 bar:SetStateScaleMap(map)
flickerstreak@68 263 end
flickerstreak@68 264
flickerstreak@71 265 propertyFuncs.enablescale = updateScale
flickerstreak@71 266 propertyFuncs.scale = updateScale
flickerstreak@68 267
flickerstreak@64 268 end
flickerstreak@64 269
flickerstreak@64 270
flickerstreak@68 271
flickerstreak@68 272 -- module event handlers --
flickerstreak@68 273
flickerstreak@65 274 function module:OnInitialize()
flickerstreak@65 275 self.db = ReAction.db:RegisterNamespace( moduleID,
flickerstreak@65 276 {
flickerstreak@65 277 profile = {
flickerstreak@65 278 bars = { },
flickerstreak@65 279 }
flickerstreak@65 280 }
flickerstreak@65 281 )
flickerstreak@65 282
flickerstreak@65 283 InitRules()
flickerstreak@65 284 self:RegisterEvent("PLAYER_AURAS_CHANGED")
flickerstreak@65 285
flickerstreak@65 286 ReAction:RegisterBarOptionGenerator(self, "GetBarOptions")
flickerstreak@65 287
flickerstreak@65 288 ReAction.RegisterCallback(self, "OnCreateBar","OnRefreshBar")
flickerstreak@65 289 ReAction.RegisterCallback(self, "OnRefreshBar")
flickerstreak@65 290 ReAction.RegisterCallback(self, "OnEraseBar")
flickerstreak@65 291 ReAction.RegisterCallback(self, "OnRenameBar")
flickerstreak@65 292 ReAction.RegisterCallback(self, "OnConfigModeChanged")
flickerstreak@65 293 end
flickerstreak@65 294
flickerstreak@65 295 function module:PLAYER_AURAS_CHANGED()
flickerstreak@65 296 self:UnregisterEvent("PLAYER_AURAS_CHANGED")
flickerstreak@66 297 -- on login the number of stances is 0 until this event fires during the init sequence.
flickerstreak@66 298 -- however if you reload just the UI the number of stances is correct immediately
flickerstreak@66 299 -- and this event won't fire until you gain/lose buffs/debuffs, at which point you might
flickerstreak@66 300 -- be in combat.
flickerstreak@66 301 if not InCombatLockdown() then
flickerstreak@66 302 InitRules()
flickerstreak@66 303 for name, bar in ReAction:IterateBars() do
flickerstreak@67 304 self:OnRefreshBar(nil,bar,name)
flickerstreak@66 305 end
flickerstreak@66 306 end
flickerstreak@65 307 end
flickerstreak@65 308
flickerstreak@65 309 function module:OnRefreshBar(event, bar, name)
flickerstreak@65 310 local c = self.db.profile.bars[name]
flickerstreak@65 311 if c then
flickerstreak@68 312 ApplyStates(bar)
flickerstreak@65 313 end
flickerstreak@65 314 end
flickerstreak@65 315
flickerstreak@65 316 function module:OnEraseBar(event, bar, name)
flickerstreak@65 317 self.db.profile.bars[name] = nil
flickerstreak@65 318 end
flickerstreak@65 319
flickerstreak@65 320 function module:OnRenameBar(event, bar, oldname, newname)
flickerstreak@65 321 local b = self.db.profile.bars
flickerstreak@65 322 bars[newname], bars[oldname] = bars[oldname], nil
flickerstreak@65 323 end
flickerstreak@65 324
flickerstreak@65 325 function module:OnConfigModeChanged(event, mode)
flickerstreak@65 326 -- TODO: unregister all state drivers (temporarily) and hidestates
flickerstreak@65 327 end
flickerstreak@65 328
flickerstreak@64 329
flickerstreak@64 330
flickerstreak@64 331 -- Options --
flickerstreak@64 332
flickerstreak@64 333 local CreateBarOptions
flickerstreak@62 334 do
flickerstreak@62 335 local function ClassCheck(...)
flickerstreak@62 336 for i = 1, select('#',...) do
flickerstreak@62 337 local _, c = UnitClass("player")
flickerstreak@62 338 if c == select(i,...) then
flickerstreak@62 339 return false
flickerstreak@62 340 end
flickerstreak@62 341 end
flickerstreak@62 342 return true
flickerstreak@62 343 end
flickerstreak@62 344
flickerstreak@64 345 -- pre-sorted by the order they should appear in
flickerstreak@64 346 local rules = {
flickerstreak@64 347 -- rule hidden fields
flickerstreak@64 348 { "stance", ClassCheck("WARRIOR"), { {battle = L["Battle Stance"]}, {defensive = L["Defensive Stance"]}, {berserker = L["Berserker Stance"]} } },
flickerstreak@64 349 { "form", ClassCheck("DRUID"), { {caster = L["Caster Form"]}, {bear = L["Bear Form"]}, {cat = L["Cat Form"]}, {treeOrMoonkin = L["Tree/Moonkin"]} } },
flickerstreak@64 350 { "stealth", ClassCheck("ROGUE","DRUID"), { {stealth = L["Stealth"]}, {nostealth = L["No Stealth"]} } },
flickerstreak@64 351 { "shadow", ClassCheck("PRIEST"), { {shadowform = L["Shadowform"]}, {noshadowform = L["No Shadowform"]} } },
flickerstreak@64 352 { "pet", ClassCheck("HUNTER","WARLOCK"), { {pet = L["With Pet"]}, {nopet = L["Without Pet"]} } },
flickerstreak@64 353 { "target", false, { {harm = L["Hostile Target"]}, {help = L["Friendly Target"]}, {notarget = L["No Target"]} } },
flickerstreak@64 354 { "focus", false, { {focusharm = L["Hostile Focus"]}, {focushelp = L["Friendly Focus"]}, {nofocus = L["No Focus"]} } },
flickerstreak@64 355 { "group", false, { {raid = L["Raid"]}, {party = L["Party"]}, {solo = L["Solo"]} } },
flickerstreak@64 356 { "combat", false, { {combat = L["In Combat"]}, {nocombat = L["Out of Combat"]} } },
flickerstreak@62 357 }
flickerstreak@62 358
flickerstreak@64 359 local ruleSelect = { }
flickerstreak@64 360 local ruleMap = { }
flickerstreak@64 361 local optionMap = setmetatable({},{__mode="k"})
flickerstreak@62 362
flickerstreak@68 363 local pointTable = {
flickerstreak@68 364 NONE = " ",
flickerstreak@68 365 CENTER = L["Center"],
flickerstreak@68 366 LEFT = L["Left"],
flickerstreak@68 367 RIGHT = L["Right"],
flickerstreak@68 368 TOP = L["Top"],
flickerstreak@68 369 BOTTOM = L["Bottom"],
flickerstreak@68 370 TOPLEFT = L["Top Left"],
flickerstreak@68 371 TOPRIGHT = L["Top Right"],
flickerstreak@68 372 BOTTOMLEFT = L["Bottom Left"],
flickerstreak@68 373 BOTTOMRIGHT = L["Bottom Right"],
flickerstreak@68 374 }
flickerstreak@68 375
flickerstreak@64 376 -- unpack rules table into ruleSelect and ruleMap
flickerstreak@64 377 for _, c in ipairs(rules) do
flickerstreak@64 378 local rule, hidden, fields = unpack(c)
flickerstreak@64 379 if not hidden then
flickerstreak@64 380 for _, field in ipairs(fields) do
flickerstreak@64 381 local key, label = next(field)
flickerstreak@64 382 table.insert(ruleSelect, label)
flickerstreak@64 383 table.insert(ruleMap, key)
flickerstreak@62 384 end
flickerstreak@62 385 end
flickerstreak@62 386 end
flickerstreak@62 387
flickerstreak@62 388 local function CreateStateOptions(bar, name)
flickerstreak@62 389 local opts = {
flickerstreak@62 390 type = "group",
flickerstreak@62 391 name = name,
flickerstreak@64 392 childGroups = "tab",
flickerstreak@25 393 }
flickerstreak@62 394
flickerstreak@64 395 local states = tbuild(module.db.profile.bars, bar:GetName(), "states")
flickerstreak@64 396
flickerstreak@68 397 local function update()
flickerstreak@68 398 ApplyStates(bar)
flickerstreak@68 399 end
flickerstreak@68 400
flickerstreak@68 401 local function setrule( key, value, ... )
flickerstreak@64 402 tbuild(states, opts.name, "rule", ...)[key] = value
flickerstreak@64 403 end
flickerstreak@64 404
flickerstreak@68 405 local function getrule( ... )
flickerstreak@64 406 return tfetch(states, opts.name, "rule", ...)
flickerstreak@64 407 end
flickerstreak@64 408
flickerstreak@68 409 local function setprop(info, value)
flickerstreak@68 410 SetProperty(bar, opts.name, info[#info], value)
flickerstreak@68 411 end
flickerstreak@68 412
flickerstreak@68 413 local function getprop(info)
flickerstreak@68 414 return GetProperty(bar, opts.name, info[#info])
flickerstreak@68 415 end
flickerstreak@68 416
flickerstreak@64 417 local function fixall(setkey)
flickerstreak@64 418 -- if multiple selections in the same group are chosen when 'all' is selected,
flickerstreak@64 419 -- keep only one of them. If changing the mode, the first in the fields list will
flickerstreak@64 420 -- be chosen arbitrarily. Otherwise, if selecting a new checkbox from the field-set,
flickerstreak@64 421 -- it will be retained.
flickerstreak@64 422 local notified = false
flickerstreak@64 423 for _, c in ipairs(rules) do
flickerstreak@64 424 local rule, hidden, fields = unpack(c)
flickerstreak@64 425 local found = false
flickerstreak@64 426 for key in ipairs(fields) do
flickerstreak@68 427 if getrule("values",key) then
flickerstreak@64 428 if (found or setkey) and key ~= setkey then
flickerstreak@68 429 setrule(key,false,"values")
flickerstreak@64 430 if not setkey and not notified then
flickerstreak@64 431 ReAction:UserError(L["Warning: one or more incompatible rules were turned off"])
flickerstreak@64 432 notified = true
flickerstreak@64 433 end
flickerstreak@64 434 end
flickerstreak@64 435 found = true
flickerstreak@64 436 end
flickerstreak@62 437 end
flickerstreak@62 438 end
flickerstreak@62 439 end
flickerstreak@62 440
flickerstreak@64 441 local function getNeighbors()
flickerstreak@64 442 local before, after
flickerstreak@64 443 for k, v in pairs(states) do
flickerstreak@64 444 local o = tonumber(tfetch(v, "rule", "order"))
flickerstreak@64 445 if o and k ~= opts.name then
flickerstreak@64 446 local obefore = tfetch(states,before,"rule","order")
flickerstreak@64 447 local oafter = tfetch(states,after,"rule","order")
flickerstreak@64 448 if o < opts.order and (not obefore or obefore < o) then
flickerstreak@64 449 before = k
flickerstreak@64 450 end
flickerstreak@64 451 if o > opts.order and (not oafter or oafter > o) then
flickerstreak@64 452 after = k
flickerstreak@64 453 end
flickerstreak@64 454 end
flickerstreak@64 455 end
flickerstreak@64 456 return before, after
flickerstreak@64 457 end
flickerstreak@64 458
flickerstreak@64 459 local function swapOrder( a, b )
flickerstreak@64 460 -- do options table
flickerstreak@64 461 local args = optionMap[bar].args
flickerstreak@64 462 args[a].order, args[b].order = args[b].order, args[a].order
flickerstreak@64 463 -- do profile
flickerstreak@64 464 a = tbuild(states, a, "rule")
flickerstreak@64 465 b = tbuild(states, b, "rule")
flickerstreak@64 466 a.order, b.order = b.order, a.order
flickerstreak@64 467 end
flickerstreak@64 468
flickerstreak@68 469 local function anchordisable()
flickerstreak@71 470 return not GetProperty(bar, opts.name, "enableAnchor")
flickerstreak@64 471 end
flickerstreak@64 472
flickerstreak@68 473 tbuild(states, name)
flickerstreak@64 474
flickerstreak@68 475 opts.order = getrule("order")
flickerstreak@64 476 if opts.order == nil then
flickerstreak@64 477 -- add after the highest
flickerstreak@64 478 opts.order = 100
flickerstreak@64 479 for _, state in pairs(states) do
flickerstreak@64 480 local x = tonumber(tfetch(state, "rule", "order"))
flickerstreak@64 481 if x and x >= opts.order then
flickerstreak@64 482 opts.order = x + 1
flickerstreak@64 483 end
flickerstreak@64 484 end
flickerstreak@68 485 setrule("order",opts.order)
flickerstreak@64 486 end
flickerstreak@64 487
flickerstreak@64 488 opts.args = {
flickerstreak@68 489 ordering = {
flickerstreak@68 490 name = L["Info"],
flickerstreak@68 491 order = 1,
flickerstreak@64 492 type = "group",
flickerstreak@64 493 args = {
flickerstreak@64 494 delete = {
flickerstreak@68 495 name = L["Delete this State"],
flickerstreak@68 496 order = -1,
flickerstreak@64 497 type = "execute",
flickerstreak@64 498 func = function(info)
flickerstreak@68 499 if states[opts.name] then
flickerstreak@68 500 states[opts.name] = nil
flickerstreak@68 501 ApplyStates(bar)
flickerstreak@68 502 end
flickerstreak@64 503 optionMap[bar].args[opts.name] = nil
flickerstreak@64 504 end,
flickerstreak@64 505 },
flickerstreak@64 506 rename = {
flickerstreak@64 507 name = L["Name"],
flickerstreak@64 508 order = 1,
flickerstreak@68 509 type = "input",
flickerstreak@64 510 get = function() return opts.name end,
flickerstreak@64 511 set = function(info, value)
flickerstreak@64 512 -- check for existing state name
flickerstreak@64 513 if states[value] then
flickerstreak@64 514 L["State named '%s' already exists"]:format(value)
flickerstreak@64 515 end
flickerstreak@64 516 local args = optionMap[bar].args
flickerstreak@64 517 states[value], args[value], states[opts.name], args[opts.name] = states[opts.name], args[opts.name], nil, nil
flickerstreak@64 518 opts.name = value
flickerstreak@64 519 update()
flickerstreak@64 520 end,
flickerstreak@64 521 pattern = "^%w*$",
flickerstreak@64 522 usage = L["State names must be alphanumeric without spaces"],
flickerstreak@64 523 },
flickerstreak@64 524 ordering = {
flickerstreak@64 525 name = L["Evaluation Order"],
flickerstreak@64 526 desc = L["State transitions are evaluated in the order listed:\nMove a state up or down to change the order"],
flickerstreak@64 527 order = 2,
flickerstreak@68 528 type = "group",
flickerstreak@68 529 inline = true,
flickerstreak@64 530 args = {
flickerstreak@64 531 up = {
flickerstreak@68 532 name = L["Up"],
flickerstreak@68 533 order = 1,
flickerstreak@64 534 type = "execute",
flickerstreak@64 535 width = "half",
flickerstreak@64 536 func = function()
flickerstreak@64 537 local before, after = getNeighbors()
flickerstreak@64 538 if before then
flickerstreak@64 539 swapOrder(before, opts.name)
flickerstreak@64 540 update()
flickerstreak@64 541 end
flickerstreak@64 542 end,
flickerstreak@64 543 },
flickerstreak@64 544 down = {
flickerstreak@68 545 name = L["Down"],
flickerstreak@68 546 order = 2,
flickerstreak@64 547 type = "execute",
flickerstreak@64 548 width = "half",
flickerstreak@64 549 func = function()
flickerstreak@64 550 local before, after = getNeighbors()
flickerstreak@64 551 if after then
flickerstreak@64 552 swapOrder(opts.name, after)
flickerstreak@64 553 update()
flickerstreak@64 554 end
flickerstreak@64 555 end,
flickerstreak@64 556 }
flickerstreak@64 557 }
flickerstreak@68 558 }
flickerstreak@64 559 }
flickerstreak@64 560 },
flickerstreak@68 561 properties = {
flickerstreak@68 562 name = L["Properties"],
flickerstreak@68 563 order = 2,
flickerstreak@68 564 type = "group",
flickerstreak@68 565 args = {
flickerstreak@68 566 desc = {
flickerstreak@68 567 name = L["Set the properties for the bar when in this state"],
flickerstreak@68 568 order = 1,
flickerstreak@68 569 type = "description"
flickerstreak@68 570 },
flickerstreak@68 571 hide = {
flickerstreak@68 572 name = L["Hide Bar"],
flickerstreak@68 573 order = 2,
flickerstreak@68 574 type = "toggle",
flickerstreak@68 575 set = setprop,
flickerstreak@68 576 get = getprop,
flickerstreak@68 577 },
flickerstreak@68 578 page = {
flickerstreak@68 579 name = L["Show Page #"],
flickerstreak@68 580 order = 3,
flickerstreak@68 581 type = "select",
flickerstreak@68 582 disabled = function()
flickerstreak@68 583 return bar:GetNumPages() < 2
flickerstreak@68 584 end,
flickerstreak@68 585 hidden = function()
flickerstreak@68 586 return bar:GetNumPages() < 2
flickerstreak@68 587 end,
flickerstreak@68 588 values = function()
flickerstreak@68 589 local pages = { none = " " }
flickerstreak@68 590 for i = 1, bar:GetNumPages() do
flickerstreak@68 591 pages[i] = i
flickerstreak@68 592 end
flickerstreak@68 593 return pages
flickerstreak@68 594 end,
flickerstreak@68 595 set = function(info, value)
flickerstreak@68 596 if value == "none" then
flickerstreak@68 597 setprop(info, nil)
flickerstreak@68 598 else
flickerstreak@68 599 setprop(info, value)
flickerstreak@68 600 end
flickerstreak@68 601 end,
flickerstreak@68 602 get = function(info)
flickerstreak@68 603 return getprop(info) or "none"
flickerstreak@68 604 end,
flickerstreak@68 605 },
flickerstreak@68 606 keybindstate = {
flickerstreak@68 607 name = L["Override Keybinds"],
flickerstreak@68 608 desc = L["Set this state to maintain its own set of keybinds which override the defaults when active"],
flickerstreak@68 609 order = 4,
flickerstreak@68 610 type = "toggle",
flickerstreak@68 611 set = setprop,
flickerstreak@68 612 get = getprop,
flickerstreak@68 613 },
flickerstreak@68 614 position = {
flickerstreak@68 615 name = L["Position"],
flickerstreak@68 616 order = 5,
flickerstreak@68 617 type = "group",
flickerstreak@68 618 inline = true,
flickerstreak@68 619 args = {
flickerstreak@71 620 enableAnchor = {
flickerstreak@68 621 name = L["Set New Position"],
flickerstreak@68 622 order = 1,
flickerstreak@68 623 type = "toggle",
flickerstreak@68 624 set = setprop,
flickerstreak@68 625 get = getprop,
flickerstreak@68 626 },
flickerstreak@68 627 anchorPoint = {
flickerstreak@68 628 name = L["Point"],
flickerstreak@68 629 order = 2,
flickerstreak@68 630 type = "select",
flickerstreak@68 631 values = pointTable,
flickerstreak@68 632 set = function(info, value) setprop(info, value ~= "NONE" and value or nil) end,
flickerstreak@68 633 get = function(info) return getprop(info) or "NONE" end,
flickerstreak@68 634 disabled = anchordisable,
flickerstreak@68 635 hidden = anchordisable,
flickerstreak@68 636 },
flickerstreak@68 637 anchorRelPoint = {
flickerstreak@68 638 name = L["Relative Point"],
flickerstreak@68 639 order = 3,
flickerstreak@68 640 type = "select",
flickerstreak@68 641 values = pointTable,
flickerstreak@68 642 set = function(info, value) setprop(info, value ~= "NONE" and value or nil) end,
flickerstreak@68 643 get = function(info) return getprop(info) or "NONE" end,
flickerstreak@68 644 disabled = anchordisable,
flickerstreak@68 645 hidden = anchordisable,
flickerstreak@68 646 },
flickerstreak@68 647 anchorX = {
flickerstreak@68 648 name = L["X Offset"],
flickerstreak@68 649 order = 4,
flickerstreak@68 650 type = "range",
flickerstreak@68 651 min = -100,
flickerstreak@68 652 max = 100,
flickerstreak@68 653 step = 1,
flickerstreak@68 654 set = setprop,
flickerstreak@68 655 get = getprop,
flickerstreak@68 656 disabled = anchordisable,
flickerstreak@68 657 hidden = anchordisable,
flickerstreak@68 658 },
flickerstreak@68 659 anchorY = {
flickerstreak@68 660 name = L["Y Offset"],
flickerstreak@68 661 order = 5,
flickerstreak@68 662 type = "range",
flickerstreak@68 663 min = -100,
flickerstreak@68 664 max = 100,
flickerstreak@68 665 step = 1,
flickerstreak@68 666 set = setprop,
flickerstreak@68 667 get = getprop,
flickerstreak@68 668 disabled = anchordisable,
flickerstreak@68 669 hidden = anchordisable,
flickerstreak@68 670 },
flickerstreak@68 671 },
flickerstreak@68 672 },
flickerstreak@68 673 scale = {
flickerstreak@68 674 name = L["Scale"],
flickerstreak@68 675 order = 6,
flickerstreak@68 676 type = "group",
flickerstreak@68 677 inline = true,
flickerstreak@68 678 args = {
flickerstreak@68 679 enablescale = {
flickerstreak@68 680 name = L["Set New Scale"],
flickerstreak@68 681 order = 1,
flickerstreak@68 682 type = "toggle",
flickerstreak@68 683 set = setprop,
flickerstreak@68 684 get = getprop,
flickerstreak@68 685 },
flickerstreak@68 686 scale = {
flickerstreak@68 687 name = L["Scale"],
flickerstreak@68 688 order = 2,
flickerstreak@68 689 type = "range",
flickerstreak@68 690 min = 0.1,
flickerstreak@68 691 max = 2.5,
flickerstreak@68 692 step = 0.05,
flickerstreak@68 693 isPercent = true,
flickerstreak@68 694 set = setprop,
flickerstreak@68 695 get = function(info) return getprop(info) or 1 end,
flickerstreak@68 696 disabled = function() return not GetProperty(bar, opts.name, "enablescale") end,
flickerstreak@68 697 hidden = function() return not GetProperty(bar, opts.name, "enablescale") end,
flickerstreak@68 698 },
flickerstreak@68 699 },
flickerstreak@68 700 },
flickerstreak@68 701 },
flickerstreak@68 702 },
flickerstreak@64 703 rules = {
flickerstreak@70 704 name = L["Rule"],
flickerstreak@68 705 order = 3,
flickerstreak@64 706 type = "group",
flickerstreak@64 707 args = {
flickerstreak@64 708 mode = {
flickerstreak@68 709 name = L["Select this state"],
flickerstreak@68 710 order = 2,
flickerstreak@64 711 type = "select",
flickerstreak@64 712 style = "radio",
flickerstreak@64 713 values = {
flickerstreak@64 714 default = L["by default"],
flickerstreak@64 715 any = L["when ANY of these"],
flickerstreak@64 716 all = L["when ALL of these"],
flickerstreak@68 717 custom = L["via custom rule"],
flickerstreak@68 718 keybind = L["via keybinding"],
flickerstreak@64 719 },
flickerstreak@64 720 set = function( info, value )
flickerstreak@68 721 setrule("type", value)
flickerstreak@64 722 fixall()
flickerstreak@64 723 update()
flickerstreak@64 724 end,
flickerstreak@64 725 get = function( info )
flickerstreak@68 726 return getrule("type")
flickerstreak@64 727 end,
flickerstreak@64 728 },
flickerstreak@64 729 clear = {
flickerstreak@68 730 name = L["Clear All"],
flickerstreak@68 731 order = 3,
flickerstreak@64 732 type = "execute",
flickerstreak@68 733 hidden = function()
flickerstreak@68 734 local t = getrule("type")
flickerstreak@68 735 return t ~= "any" and t ~= "all"
flickerstreak@68 736 end,
flickerstreak@68 737 disabled = function()
flickerstreak@68 738 local t = getrule("type")
flickerstreak@68 739 return t ~= "any" and t ~= "all"
flickerstreak@68 740 end,
flickerstreak@64 741 func = function()
flickerstreak@68 742 local type = getrule("type")
flickerstreak@64 743 if type == "custom" then
flickerstreak@68 744 setrule("custom","")
flickerstreak@64 745 elseif type == "any" or type == "all" then
flickerstreak@68 746 setrule("values", {})
flickerstreak@64 747 end
flickerstreak@64 748 update()
flickerstreak@64 749 end,
flickerstreak@64 750 },
flickerstreak@64 751 inputs = {
flickerstreak@68 752 name = L["Conditions"],
flickerstreak@68 753 order = 4,
flickerstreak@64 754 type = "multiselect",
flickerstreak@64 755 hidden = function()
flickerstreak@68 756 local t = getrule("type")
flickerstreak@68 757 return t ~= "any" and t ~= "all"
flickerstreak@64 758 end,
flickerstreak@64 759 disabled = function()
flickerstreak@68 760 local t = getrule("type")
flickerstreak@68 761 return t ~= "any" and t ~= "all"
flickerstreak@64 762 end,
flickerstreak@64 763 values = ruleSelect,
flickerstreak@64 764 set = function(info, key, value )
flickerstreak@68 765 setrule(ruleMap[key], value or nil, "values")
flickerstreak@64 766 if value then
flickerstreak@64 767 fixall(ruleMap[key])
flickerstreak@64 768 end
flickerstreak@64 769 update()
flickerstreak@64 770 end,
flickerstreak@64 771 get = function(info, key)
flickerstreak@68 772 return getrule("values", ruleMap[key]) or false
flickerstreak@64 773 end,
flickerstreak@64 774 },
flickerstreak@64 775 custom = {
flickerstreak@68 776 name = L["Custom Rule"],
flickerstreak@68 777 order = 5,
flickerstreak@64 778 type = "input",
flickerstreak@64 779 multiline = true,
flickerstreak@64 780 hidden = function()
flickerstreak@68 781 return getrule("type") ~= "custom"
flickerstreak@64 782 end,
flickerstreak@64 783 disabled = function()
flickerstreak@68 784 return getrule("type") ~= "custom"
flickerstreak@64 785 end,
flickerstreak@64 786 desc = L["Syntax like macro rules: see preset rules for examples"],
flickerstreak@64 787 set = function(info, value)
flickerstreak@68 788 setrule("custom",value)
flickerstreak@64 789 update()
flickerstreak@64 790 end,
flickerstreak@64 791 get = function(info)
flickerstreak@68 792 return getrule("custom") or ""
flickerstreak@64 793 end,
flickerstreak@64 794 validate = function (info, rule)
flickerstreak@64 795 local s = rule:gsub("%s","") -- remove all spaces
flickerstreak@64 796 -- unfortunately %b and captures don't support the '+' notation, or this would be considerably simpler
flickerstreak@64 797 repeat
flickerstreak@64 798 if s == "" then
flickerstreak@64 799 return true
flickerstreak@64 800 end
flickerstreak@64 801 local c, r = s:match("(%b[])(.*)")
flickerstreak@64 802 if c == nil and s and #s > 0 then
flickerstreak@64 803 return L["Invalid custom rule '%s': each clause must appear within [brackets]"]:format(rule)
flickerstreak@64 804 end
flickerstreak@64 805 s = r
flickerstreak@64 806 until c == nil
flickerstreak@64 807 return true
flickerstreak@64 808 end,
flickerstreak@68 809 },
flickerstreak@68 810 keybind = {
flickerstreak@68 811 name = L["Keybinding"],
flickerstreak@68 812 order = 6,
flickerstreak@68 813 inline = true,
flickerstreak@68 814 hidden = function() return getrule("type") ~= "keybind" end,
flickerstreak@68 815 disabled = function() return getrule("type") ~= "keybind" end,
flickerstreak@68 816 type = "group",
flickerstreak@68 817 args = {
flickerstreak@68 818 desc = {
flickerstreak@68 819 name = L["Invoking a state keybind overrides all other transition rules. Toggle the keybind again to remove the override and return to the specified toggle-off state."],
flickerstreak@68 820 order = 1,
flickerstreak@68 821 type = "description",
flickerstreak@68 822 },
flickerstreak@68 823 keybind = {
flickerstreak@68 824 name = L["State Hotkey"],
flickerstreak@68 825 desc = L["Define an override toggle keybind"],
flickerstreak@68 826 order = 2,
flickerstreak@68 827 type = "keybinding",
flickerstreak@68 828 set = function(info, value)
flickerstreak@70 829 if value and #value == 0 then
flickerstreak@70 830 value = nil
flickerstreak@70 831 end
flickerstreak@68 832 setrule("keybind",value)
flickerstreak@68 833 update()
flickerstreak@68 834 end,
flickerstreak@68 835 get = function() return getrule("keybind") end,
flickerstreak@68 836 },
flickerstreak@68 837 default = {
flickerstreak@68 838 name = L["Toggle Off State"],
flickerstreak@68 839 desc = L["Select a state to return to when the keybind override is toggled off"],
flickerstreak@68 840 order = 3,
flickerstreak@68 841 type = "select",
flickerstreak@68 842 values = function()
flickerstreak@68 843 local t = { }
flickerstreak@68 844 for k in pairs(states) do
flickerstreak@68 845 if k ~= opts.name then
flickerstreak@68 846 t[k] = k
flickerstreak@68 847 end
flickerstreak@68 848 end
flickerstreak@68 849 return t
flickerstreak@68 850 end,
flickerstreak@68 851 set = function(info, value)
flickerstreak@68 852 setrule("keybindreturn",value)
flickerstreak@68 853 update()
flickerstreak@68 854 end,
flickerstreak@68 855 get = function() return getrule("keybindreturn") end,
flickerstreak@68 856 },
flickerstreak@68 857 },
flickerstreak@68 858 },
flickerstreak@68 859 },
flickerstreak@68 860 },
flickerstreak@62 861 }
flickerstreak@62 862 return opts
flickerstreak@25 863 end
flickerstreak@62 864
flickerstreak@64 865
flickerstreak@62 866 CreateBarOptions = function(bar)
flickerstreak@62 867 local private = { }
flickerstreak@62 868 local options = {
flickerstreak@62 869 type = "group",
flickerstreak@62 870 name = L["Dynamic State"],
flickerstreak@64 871 childGroups = "tree",
flickerstreak@62 872 disabled = InCombatLockdown,
flickerstreak@62 873 args = {
flickerstreak@64 874 __desc__ = {
flickerstreak@68 875 name = L["States are evaluated in the order they are listed"],
flickerstreak@68 876 order = 1,
flickerstreak@64 877 type = "description",
flickerstreak@64 878 },
flickerstreak@64 879 __new__ = {
flickerstreak@64 880 name = L["New State..."],
flickerstreak@64 881 order = 2,
flickerstreak@68 882 type = "group",
flickerstreak@62 883 args = {
flickerstreak@64 884 name = {
flickerstreak@64 885 name = L["State Name"],
flickerstreak@64 886 desc = L["Set a name for the new state"],
flickerstreak@68 887 order = 1,
flickerstreak@68 888 type = "input",
flickerstreak@64 889 get = function() return private.newstatename or "" end,
flickerstreak@64 890 set = function(info,value) private.newstatename = value end,
flickerstreak@64 891 pattern = "^%w*$",
flickerstreak@64 892 usage = L["State names must be alphanumeric without spaces"],
flickerstreak@64 893 },
flickerstreak@64 894 create = {
flickerstreak@68 895 name = L["Create State"],
flickerstreak@68 896 order = 2,
flickerstreak@64 897 type = "execute",
flickerstreak@64 898 func = function ()
flickerstreak@64 899 local name = private.newstatename
flickerstreak@68 900 if states[name] then
flickerstreak@68 901 ReAction:UserError(L["State named '%s' already exists"]:format(name))
flickerstreak@68 902 else
flickerstreak@68 903 -- TODO: select default state options and pass as final argument
flickerstreak@68 904 states[name] = { }
flickerstreak@68 905 optionMap[bar].args[name] = CreateStateOptions(bar,name)
flickerstreak@68 906 private.newstatename = ""
flickerstreak@68 907 end
flickerstreak@64 908 end,
flickerstreak@64 909 disabled = function()
flickerstreak@64 910 local name = private.newstatename or ""
flickerstreak@64 911 return #name == 0 or name:find("%W")
flickerstreak@64 912 end,
flickerstreak@62 913 }
flickerstreak@62 914 }
flickerstreak@64 915 }
flickerstreak@62 916 }
flickerstreak@62 917 }
flickerstreak@62 918 local states = tfetch(module.db.profile.bars, bar:GetName(), "states")
flickerstreak@62 919 if states then
flickerstreak@62 920 for name, config in pairs(states) do
flickerstreak@64 921 options.args[name] = CreateStateOptions(bar,name)
flickerstreak@62 922 end
flickerstreak@62 923 end
flickerstreak@64 924 optionMap[bar] = options
flickerstreak@62 925 return options
flickerstreak@62 926 end
flickerstreak@25 927 end
flickerstreak@25 928
flickerstreak@62 929 function module:GetBarOptions(bar)
flickerstreak@64 930 return CreateBarOptions(bar)
flickerstreak@25 931 end