Mercurial > wow > reaction
comparison State.lua @ 90:7cabc8ac6c16
Updates for wow 3.0
 - TOC update
 - updated changed APIs/frame names
 - rewrote state code per new SecureHandlers API
 - cleaned up Bar, ActionButton code
 - removed AceLibrary/Dewdrop, menu from bar right-click
 - fixed various small bugs
Updated WowAce external locations
Updated README.html
| author | Flick <flickerstreak@gmail.com> | 
|---|---|
| date | Wed, 15 Oct 2008 16:29:41 +0000 | 
| parents | 1ad208c25618 | 
| children | c2504a8b996c | 
   comparison
  equal
  deleted
  inserted
  replaced
| 89:491a6ffe7260 | 90:7cabc8ac6c16 | 
|---|---|
| 5 | 5 | 
| 6 -- local imports | 6 -- local imports | 
| 7 local ReAction = ReAction | 7 local ReAction = ReAction | 
| 8 local L = ReAction.L | 8 local L = ReAction.L | 
| 9 local _G = _G | 9 local _G = _G | 
| 10 local format = string.format | |
| 10 local InCombatLockdown = InCombatLockdown | 11 local InCombatLockdown = InCombatLockdown | 
| 11 local format = string.format | 12 local RegisterStateDriver = RegisterStateDriver | 
| 12 | 13 | 
| 13 ReAction:UpdateRevision("$Revision$") | 14 ReAction:UpdateRevision("$Revision$") | 
| 14 | 15 | 
| 15 -- module declaration | 16 -- module declaration | 
| 16 local moduleID = "State" | 17 local moduleID = "State" | 
| 51 end) | 52 end) | 
| 52 return r | 53 return r | 
| 53 end | 54 end | 
| 54 | 55 | 
| 55 | 56 | 
| 56 local InitRules, ApplyStates, SetProperty, GetProperty, RegisterProperty | 57 local InitRules, ApplyStates, CleanupStates, SetProperty, GetProperty, RegisterProperty, ShowAll | 
| 57 | 58 | 
| 58 -- PRIVATE -- | 59 -- PRIVATE -- | 
| 59 do | 60 do | 
| 61 | |
| 62 -- the field names must match the field names of the options table, below | |
| 63 -- the field values are secure snippets | |
| 64 local properties = { | |
| 65 hide = | |
| 66 [[ | |
| 67 local h = hide and hide[state] and not showAll | |
| 68 if h ~= hidden then | |
| 69 if h then | |
| 70 self:Hide() | |
| 71 else | |
| 72 self:Show() | |
| 73 end | |
| 74 hidden = h | |
| 75 end | |
| 76 ]], | |
| 77 | |
| 78 --keybindState TODO: broken | |
| 79 | |
| 80 -- the anchoring is handled in a special handler | |
| 81 anchorEnable = true, | |
| 82 --anchorFrame = true, TODO: broken | |
| 83 anchorPoint = true, | |
| 84 anchorRelPoint = true, | |
| 85 anchorX = true, | |
| 86 anchorY = true, | |
| 87 enableScale = true, | |
| 88 scale = true, | |
| 89 } | |
| 90 | |
| 91 | |
| 92 -- | |
| 93 -- Secure Handler Snippets | |
| 94 -- | |
| 95 local SetHandlerData, SetStateDriver, SetStateKeybind, RefreshState | |
| 96 do | |
| 97 local stateHandler_propInit = | |
| 98 [[ | |
| 99 propfuncs = table.new() | |
| 100 local proplist = self:GetAttribute("prop-func-list") | |
| 101 for s in string.gmatch(proplist, "(%w+)") do | |
| 102 table.insert(propfuncs, s) | |
| 103 end | |
| 104 ]] | |
| 105 | |
| 106 local onStateHandler = | |
| 107 -- function _onstate-reaction( self, stateid, newstate ) | |
| 108 [[ | |
| 109 print("received state",newstate,"on bar",self:GetName()) | |
| 110 set_state = newstate or set_state | |
| 111 | |
| 112 local oldState = state | |
| 113 state = state_override or set_state or state | |
| 114 | |
| 115 for i = 1, #propfuncs do | |
| 116 print("running state func",propfuncs[i]) | |
| 117 control:RunAttribute("func-"..propfuncs[i]) | |
| 118 end | |
| 119 | |
| 120 if anchorEnable and anchorEnable[state] ~= anchorstate then | |
| 121 anchorstate = anchorEnable[state] | |
| 122 control:RunAttribute("func-doanchor") | |
| 123 end | |
| 124 | |
| 125 control:ChildUpdate() | |
| 126 ]] | |
| 127 | |
| 128 local anchorHandler = | |
| 129 -- function func-doanchor( self ) | |
| 130 [[ | |
| 131 -- TODO | |
| 132 if anchorstate then | |
| 133 -- TODO: get anchor data from state tables | |
| 134 else | |
| 135 -- TODO: get anchor data from defaults | |
| 136 end | |
| 137 ]] | |
| 138 | |
| 139 local onClickHandler = | |
| 140 -- function OnClick( self, button, down ) | |
| 141 [[ | |
| 142 if state_override == button then | |
| 143 state_override = nil -- toggle | |
| 144 else | |
| 145 state_override = button | |
| 146 end | |
| 147 ]] .. onStateHandler | |
| 148 | |
| 149 local weak = { __mode = "k" } | |
| 150 local statedrivers = setmetatable( { }, weak ) | |
| 151 local keybinds = setmetatable( { }, weak ) | |
| 152 | |
| 153 -- Construct a lua assignment as a code string and execute it within the header | |
| 154 -- frame's sandbox. 'value' must be a string, boolean, number, or nil. If called | |
| 155 -- with four arguments, then it treats 'varname' as an existing global table and | |
| 156 -- sets a key-value pair. For a slight efficiency boost, pass the values in as | |
| 157 -- attributes and fetch them as attributes from the snippet code, to leverage snippet | |
| 158 -- caching. | |
| 159 function SetHandlerData( bar, varname, value, key ) | |
| 160 local f = bar:GetFrame() | |
| 161 f:SetAttribute("data-varname",varname) | |
| 162 f:SetAttribute("data-value", value) | |
| 163 f:SetAttribute("data-key", key) | |
| 164 f:Execute( | |
| 165 [[ | |
| 166 local name = self:GetAttribute("data-varname") | |
| 167 local value = self:GetAttribute("data-value") | |
| 168 local key = self:GetAttribute("data-key") | |
| 169 if name then | |
| 170 if key then | |
| 171 if not _G[name] then | |
| 172 _G[name] = table.new() | |
| 173 end | |
| 174 _G[name][key] = value | |
| 175 else | |
| 176 _G[name] = value | |
| 177 end | |
| 178 end | |
| 179 ]]) | |
| 180 end | |
| 181 | |
| 182 function SetDefaultAnchor( bar ) | |
| 183 local point, frame, relPoint, x, y = bar:GetAnchor() | |
| 184 SetHandlerData(bar, "defaultAnchor", point, "point") | |
| 185 SetHandlerData(bar, "defaultAnchor", relPoint, "relPoint") | |
| 186 SetHandlerData(bar, "defaultAnchor", x, "x") | |
| 187 SetHandlerData(bar, "defaultAnchor", y, "y") | |
| 188 | |
| 189 if frame then | |
| 190 local f = bar:GetFrame() | |
| 191 f:SetFrameRef("defaultAnchor", f) | |
| 192 f:Execute( | |
| 193 [[ | |
| 194 defaultAnchor.frame = self:GetAttribute("frameref-defaultAnchor") | |
| 195 ]]) | |
| 196 end | |
| 197 end | |
| 198 | |
| 199 function RefreshState( bar ) | |
| 200 SetDefaultAnchor(bar) | |
| 201 bar:GetFrame():Execute([[control:RunAttribute("reaction-refresh")]]) | |
| 202 end | |
| 203 | |
| 204 function SetStateDriver( bar, rule ) | |
| 205 local f = bar:GetFrame() | |
| 206 | |
| 207 local props = { } | |
| 208 for p, h in pairs(properties) do | |
| 209 if type(h) == "string" then | |
| 210 table.insert(props,p) | |
| 211 f:SetAttribute("func-"..p, h) | |
| 212 end | |
| 213 end | |
| 214 f:SetAttribute("prop-func-list", table.concat(props," ")) | |
| 215 f:Execute(stateHandler_propInit) | |
| 216 f:SetAttribute("reaction-refresh", onStateHandler) | |
| 217 f:SetAttribute("func-doanchor", anchorHandler) | |
| 218 | |
| 219 if rule and #rule > 0 then | |
| 220 f:SetAttribute( "_onstate-reaction", onStateHandler ) | |
| 221 RegisterStateDriver(f, "reaction", rule) | |
| 222 statedrivers[bar] = rule | |
| 223 elseif statedrivers[bar] then | |
| 224 UnregisterStateDriver(f, "reaction") | |
| 225 f:SetAttribute( "_onstate-reaction", nil ) | |
| 226 statedrivers[bar] = nil | |
| 227 end | |
| 228 end | |
| 229 | |
| 230 function SetStateKeybind( bar, key, state ) | |
| 231 local f = bar:GetFrame() | |
| 232 | |
| 233 local kb = keybinds[bar] | |
| 234 if kb == nil then | |
| 235 if key == nil then | |
| 236 -- nothing to do | |
| 237 return | |
| 238 end | |
| 239 kb = { } | |
| 240 keybinds[bar] = kb | |
| 241 end | |
| 242 | |
| 243 -- clear the old binding, if any | |
| 244 if kb[state] then | |
| 245 SetOverrideBinding(f, false, kb[state], nil) | |
| 246 end | |
| 247 kb[state] = key | |
| 248 | |
| 249 if key then | |
| 250 f:SetAttribute("_onclick", onClickHandler) | |
| 251 SetOverrideBindingClick(f, false, key, state, nil) -- state name is the virtual mouse button | |
| 252 end | |
| 253 end | |
| 254 end | |
| 255 | |
| 60 -- As far as I can tell the macro clauses are NOT locale-specific. | 256 -- As far as I can tell the macro clauses are NOT locale-specific. | 
| 61 local ruleformats = { | 257 local ruleformats = { | 
| 62 stealth = "stealth", | 258 stealth = "stealth", | 
| 63 nostealth = "nostealth", | 259 nostealth = "nostealth", | 
| 64 shadowform = "form:1", | 260 shadowform = "form:1", | 
| 111 ruleformats.cat = format("form:%d",cat) | 307 ruleformats.cat = format("form:%d",cat) | 
| 112 ruleformats.tree = format("form:%d",tree) | 308 ruleformats.tree = format("form:%d",tree) | 
| 113 ruleformats.moonkin = format("form:%d",moonkin) | 309 ruleformats.moonkin = format("form:%d",moonkin) | 
| 114 end | 310 end | 
| 115 | 311 | 
| 116 | 312 local function BuildRule(states) | 
| 117 -- state property functions | |
| 118 local ofskeys = { | |
| 119 anchorPoint = "point", | |
| 120 anchorRelPoint = "relpoint", | |
| 121 anchorX = "x", | |
| 122 anchorY = "y" | |
| 123 } | |
| 124 | |
| 125 local barofsidx = { | |
| 126 anchorPoint = 1, | |
| 127 anchorRelPoint = 3, | |
| 128 anchorX = 4, | |
| 129 anchorY = 5 | |
| 130 } | |
| 131 | |
| 132 local function UpdatePartialAnchor(bar, states, ckey) | |
| 133 local map = { } | |
| 134 local bc = bar.config | |
| 135 for state, c in pairs(states) do | |
| 136 if c.enableAnchor then | |
| 137 map[state] = c[ckey] | |
| 138 end | |
| 139 end | |
| 140 local ofskey = ofskeys[ckey] | |
| 141 local default = select(barofsidx[ckey], bar:GetAnchor()) | |
| 142 bar:SetStateAttribute(format("headofs%s",ofskeys[ckey]), map, default) | |
| 143 end | |
| 144 | |
| 145 -- the table key name for each function maps to the name of the config element | |
| 146 local propertyFuncs = { | |
| 147 hide = function( bar, states ) | |
| 148 local hs = { } | |
| 149 for state, config in pairs(states) do | |
| 150 if config.hide then | |
| 151 table.insert(hs, state) | |
| 152 end | |
| 153 end | |
| 154 bar:GetButtonFrame():SetAttribute("hidestates", table.concat(hs,",")) | |
| 155 end, | |
| 156 | |
| 157 keybindstate = function( bar, states ) | |
| 158 local map = { } | |
| 159 for state, config in pairs(states) do | |
| 160 local kbset = config.keybindstate and state | |
| 161 map[state] = kbset | |
| 162 for button in bar:IterateButtons() do | |
| 163 -- TODO: inform children they should maintain multiple binding sets | |
| 164 -- ?? button:UpdateBindingSet(kbset) | |
| 165 end | |
| 166 end | |
| 167 bar:SetStateAttribute("statebindings", map, true) -- apply to button frame, bindings only work for direct children | |
| 168 end, | |
| 169 | |
| 170 enableAnchor = function( bar, states ) | |
| 171 for ckey in pairs(ofskeys) do | |
| 172 UpdatePartialAnchor(bar, states, ckey) | |
| 173 end | |
| 174 end, | |
| 175 | |
| 176 enableScale = function( bar, states ) | |
| 177 local map = { } | |
| 178 for state, c in pairs(states) do | |
| 179 if c.enableScale then | |
| 180 map[state] = c.scale | |
| 181 end | |
| 182 end | |
| 183 bar:SetStateAttribute("headscale", map, 1.0) | |
| 184 end, | |
| 185 } | |
| 186 | |
| 187 -- generate some table entries | |
| 188 propertyFuncs.scale = propertyFuncs.enableScale | |
| 189 for ckey in pairs(ofskeys) do | |
| 190 propertyFuncs[ckey] = function( bar, states ) | |
| 191 UpdatePartialAnchor(bar, states, ckey) | |
| 192 end | |
| 193 end | |
| 194 | |
| 195 | |
| 196 function GetProperty( bar, state, propname ) | |
| 197 return tfetch(module.db.profile.bars, bar:GetName(), "states", state, propname) | |
| 198 end | |
| 199 | |
| 200 function SetProperty( bar, state, propname, value ) | |
| 201 local states = tbuild(module.db.profile.bars, bar:GetName(), "states") | |
| 202 tbuild(states, state)[propname] = value | |
| 203 local f = propertyFuncs[propname] | |
| 204 if f then | |
| 205 f(bar, states) | |
| 206 end | |
| 207 end | |
| 208 | |
| 209 function RegisterProperty( propname, f ) | |
| 210 propertyFuncs[propname] = f | |
| 211 for bar in ReAction:IterateBars() do | |
| 212 local states = tfetch(module.db.profile.bars, bar:GetName(), "states") | |
| 213 if states then | |
| 214 f(bar,states) | |
| 215 end | |
| 216 end | |
| 217 end | |
| 218 | |
| 219 | |
| 220 | |
| 221 -- | |
| 222 -- Build a state-transition spec string and statemap to be passed to | |
| 223 -- Bar:SetStateDriver(). | |
| 224 -- | |
| 225 -- The statemap building is complex: keybound states override all | |
| 226 -- other transitions, so must remain in their current state, but must | |
| 227 -- also remember other transitions that happen while they're stuck there | |
| 228 -- so that when the binding is toggled off it can return to the proper state | |
| 229 -- | |
| 230 local function BuildStateMap(states) | |
| 231 local rules = { } | 313 local rules = { } | 
| 232 local statemap = { } | |
| 233 local keybinds = { } | |
| 234 local default | 314 local default | 
| 235 | 315 | 
| 236 -- first grab all the keybind override states | |
| 237 -- and construct an override template | |
| 238 local override | |
| 239 do | |
| 240 local overrides = { } | |
| 241 for name, state in pairs(states) do | |
| 242 local type = tfetch(state, "rule", "type") | |
| 243 if type == "keybind" then | |
| 244 -- use the state-stack to remember the current transition | |
| 245 -- use $s as a marker for a later call to gsub() | |
| 246 table.insert(overrides, format("%s:$s set() %s", name, name)) | |
| 247 end | |
| 248 end | |
| 249 if #overrides > 0 then | |
| 250 table.insert(overrides, "") -- for a trailing ';' | |
| 251 end | |
| 252 override = table.concat(overrides, ";") or "" | |
| 253 end | |
| 254 | |
| 255 -- now iterate the rules in order | |
| 256 for idx, state in ipairs(fieldsort(states, "rule", "order")) do | 316 for idx, state in ipairs(fieldsort(states, "rule", "order")) do | 
| 257 local c = states[state].rule | 317 local c = states[state].rule | 
| 258 local type = c.type | 318 local type = c.type | 
| 259 if type == "default" then | 319 if type == "default" then | 
| 260 default = default or state | 320 default = default or state | 
| 282 if #clauses > 0 then | 342 if #clauses > 0 then | 
| 283 table.insert(rules, format("%s %s", format("[%s]", table.concat(clauses, ",")), state)) | 343 table.insert(rules, format("%s %s", format("[%s]", table.concat(clauses, ",")), state)) | 
| 284 end | 344 end | 
| 285 end | 345 end | 
| 286 end | 346 end | 
| 287 | |
| 288 -- use a different virtual button for the actual keybind transition, | |
| 289 -- to implement a toggle. You have to clear it regardless of the type | |
| 290 -- (which is usually a no-op) to unbind state transitions when switching | |
| 291 -- transition types. | |
| 292 local bindbutton = format("%s_binding",state) | |
| 293 if type == "keybind" then | |
| 294 keybinds[bindbutton] = c.keybind or false | |
| 295 statemap[bindbutton] = format("%s:pop();*:set(%s)", state, state) | |
| 296 else | |
| 297 keybinds[bindbutton] = false | |
| 298 end | |
| 299 | |
| 300 -- construct the statemap. gsub() the state name into the override template. | |
| 301 statemap[state] = format("%s%s", override:gsub("%$s",state), state) | |
| 302 end | 347 end | 
| 303 -- make sure that the default, if any, is last | 348 -- make sure that the default, if any, is last | 
| 304 if default then | 349 if default then | 
| 305 table.insert(rules, default) | 350 table.insert(rules, default) | 
| 306 end | 351 end | 
| 307 return table.concat(rules,";"), statemap, keybinds | 352 return table.concat(rules,";") | 
| 353 end | |
| 354 | |
| 355 local function BuildKeybinds( bar, states ) | |
| 356 for name, state in pairs(states) do | |
| 357 local type = tfetch(state, "rule", "type") | |
| 358 if type == "keybind" then | |
| 359 local key = tfetch(state, "rule", "keybind") | |
| 360 SetStateKeybind(bar, key, name) | |
| 361 else | |
| 362 SetStateKeybind(bar, nil, name) -- this clears an existing keybind | |
| 363 end | |
| 364 end | |
| 365 end | |
| 366 | |
| 367 function GetProperty( bar, state, propname ) | |
| 368 return tfetch(module.db.profile.bars, bar:GetName(), "states", state, propname) | |
| 369 end | |
| 370 | |
| 371 function SetProperty( bar, state, propname, value ) | |
| 372 local s = tbuild(module.db.profile.bars, bar:GetName(), "states", state) | |
| 373 s[propname] = value | |
| 374 SetHandlerData(bar, propname, value, state) | |
| 375 RefreshState(bar) | |
| 376 end | |
| 377 | |
| 378 function RegisterProperty( propname, snippet ) | |
| 379 properties[propname] = snippet or true | |
| 380 print("registered property",propname) | |
| 381 for _, bar in ReAction:IterateBars() do | |
| 382 local states = tfetch(module.db.profile.bars, bar:GetName(), "states") | |
| 383 if states then | |
| 384 for name, s in pairs(states) do | |
| 385 SetHandlerData(bar, propname, s[propname], name) | |
| 386 end | |
| 387 SetStateDriver(bar, BuildRule(states)) | |
| 388 RefreshState(bar) | |
| 389 end | |
| 390 end | |
| 391 end | |
| 392 | |
| 393 function UnregisterProperty( propname ) | |
| 394 properties[propname] = nil | |
| 395 for _, bar in ReAction:IterateBars() do | |
| 396 SetHandlerData(bar, propname, nil) | |
| 397 SetStateDriver(bar, BuildRule(states)) | |
| 398 RefreshState(bar) | |
| 399 end | |
| 308 end | 400 end | 
| 309 | 401 | 
| 310 function ApplyStates( bar ) | 402 function ApplyStates( bar ) | 
| 311 local states = tfetch(module.db.profile.bars, bar:GetName(), "states") | 403 local states = tfetch(module.db.profile.bars, bar:GetName(), "states") | 
| 312 if states then | 404 if states then | 
| 313 local rule, statemap, keybinds = BuildStateMap(states) | 405 for propname in pairs(properties) do | 
| 314 bar:SetStateDriver("reaction", rule, statemap) | 406 for name, s in pairs(states) do | 
| 315 for state, key in pairs(keybinds) do | 407 SetHandlerData(bar, propname, s[propname], name) | 
| 316 bar:SetAttributeBinding(state, key, "state-reaction", state) | 408 end | 
| 317 end | 409 end | 
| 318 for k, f in pairs(propertyFuncs) do | 410 BuildKeybinds(bar, states) | 
| 319 f(bar, states) | 411 SetStateDriver(bar, BuildRule(states)) | 
| 320 end | 412 RefreshState(bar) | 
| 321 end | 413 end | 
| 322 end | 414 end | 
| 323 | 415 | 
| 416 function CleanupStates( bar ) | |
| 417 SetStateDriver(bar, nil) | |
| 418 end | |
| 419 | |
| 420 function ShowAll( bar, show ) | |
| 421 SetHandlerData(bar, "showAll", show) | |
| 422 RefreshState(bar) | |
| 423 end | |
| 324 end | 424 end | 
| 325 | 425 | 
| 326 | 426 | 
| 327 | 427 | 
| 328 -- module event handlers -- | 428 -- module event handlers -- | 
| 340 self:RegisterEvent("PLAYER_AURAS_CHANGED") | 440 self:RegisterEvent("PLAYER_AURAS_CHANGED") | 
| 341 | 441 | 
| 342 ReAction:RegisterBarOptionGenerator(self, "GetBarOptions") | 442 ReAction:RegisterBarOptionGenerator(self, "GetBarOptions") | 
| 343 | 443 | 
| 344 ReAction.RegisterCallback(self, "OnCreateBar","OnRefreshBar") | 444 ReAction.RegisterCallback(self, "OnCreateBar","OnRefreshBar") | 
| 445 ReAction.RegisterCallback(self, "OnDestroyBar") | |
| 345 ReAction.RegisterCallback(self, "OnRefreshBar") | 446 ReAction.RegisterCallback(self, "OnRefreshBar") | 
| 346 ReAction.RegisterCallback(self, "OnEraseBar") | 447 ReAction.RegisterCallback(self, "OnEraseBar") | 
| 347 ReAction.RegisterCallback(self, "OnRenameBar") | 448 ReAction.RegisterCallback(self, "OnRenameBar") | 
| 348 ReAction.RegisterCallback(self, "OnConfigModeChanged") | 449 ReAction.RegisterCallback(self, "OnConfigModeChanged") | 
| 349 end | 450 end | 
| 350 | 451 | 
| 351 function module:PLAYER_AURAS_CHANGED() | 452 function module:PLAYER_AURAS_CHANGED() | 
| 352 self:UnregisterEvent("PLAYER_AURAS_CHANGED") | 453 self:UnregisterEvent("PLAYER_AURAS_CHANGED") | 
| 353 -- on login the number of stances is 0 until this event fires during the init sequence. | 454 -- on login the number of stances is 0 until this event fires during the init sequence. | 
| 354 -- however if you reload just the UI the number of stances is correct immediately | 455 -- however if you just reload the UI the number of stances is correct immediately | 
| 355 -- and this event won't fire until you gain/lose buffs/debuffs, at which point you might | 456 -- and this event won't fire until you gain/lose buffs/debuffs, at which point you might | 
| 356 -- be in combat. | 457 -- be in combat. | 
| 357 if not InCombatLockdown() then | 458 if not InCombatLockdown() then | 
| 358 InitRules() | 459 InitRules() | 
| 359 for name, bar in ReAction:IterateBars() do | 460 for name, bar in ReAction:IterateBars() do | 
| 367 if c then | 468 if c then | 
| 368 ApplyStates(bar) | 469 ApplyStates(bar) | 
| 369 end | 470 end | 
| 370 end | 471 end | 
| 371 | 472 | 
| 473 function module:OnDestroyBar(event, bar, name) | |
| 474 CleanupStates(bar) | |
| 475 end | |
| 476 | |
| 372 function module:OnEraseBar(event, bar, name) | 477 function module:OnEraseBar(event, bar, name) | 
| 373 self.db.profile.bars[name] = nil | 478 self.db.profile.bars[name] = nil | 
| 374 end | 479 end | 
| 375 | 480 | 
| 376 function module:OnRenameBar(event, bar, oldname, newname) | 481 function module:OnRenameBar(event, bar, oldname, newname) | 
| 377 local bars = self.db.profile.bars | 482 local bars = self.db.profile.bars | 
| 378 bars[newname], bars[oldname] = bars[oldname], nil | 483 bars[newname], bars[oldname] = bars[oldname], nil | 
| 379 end | 484 end | 
| 380 | 485 | 
| 381 function module:OnConfigModeChanged(event, mode) | 486 function module:OnConfigModeChanged(event, mode) | 
| 382 -- nothing to do (yet) | 487 for name, bar in ReAction:IterateBars() do | 
| 488 if self.db.profile.bars[name] then | |
| 489 ShowAll(bar, mode) | |
| 490 end | |
| 491 end | |
| 383 end | 492 end | 
| 384 | 493 | 
| 385 | 494 | 
| 386 | 495 | 
| 387 -- Options -- | 496 -- Options -- | 
| 503 order = 90, | 612 order = 90, | 
| 504 type = "toggle", | 613 type = "toggle", | 
| 505 set = "SetProp", | 614 set = "SetProp", | 
| 506 get = "GetProp", | 615 get = "GetProp", | 
| 507 }, | 616 }, | 
| 508 keybindstate = { | 617 --[[ BROKEN | 
| 618 keybindState = { | |
| 509 name = L["Override Keybinds"], | 619 name = L["Override Keybinds"], | 
| 510 desc = L["Set this state to maintain its own set of keybinds which override the defaults when active"], | 620 desc = L["Set this state to maintain its own set of keybinds which override the defaults when active"], | 
| 511 order = 91, | 621 order = 91, | 
| 512 type = "toggle", | 622 type = "toggle", | 
| 513 set = "SetProp", | 623 set = "SetProp", | 
| 514 get = "GetProp", | 624 get = "GetProp", | 
| 515 }, | 625 }, ]] | 
| 516 position = { | 626 position = { | 
| 517 name = L["Position"], | 627 name = L["Position"], | 
| 518 order = 92, | 628 order = 92, | 
| 519 type = "group", | 629 type = "group", | 
| 520 inline = true, | 630 inline = true, | 
| 521 args = { | 631 args = { | 
| 522 enableAnchor = { | 632 anchorEnable = { | 
| 523 name = L["Set New Position"], | 633 name = L["Set New Position"], | 
| 524 order = 1, | 634 order = 1, | 
| 525 type = "toggle", | 635 type = "toggle", | 
| 526 set = "SetProp", | 636 set = "SetProp", | 
| 527 get = "GetProp", | 637 get = "GetProp", | 
| 528 }, | 638 }, | 
| 639 --[[ TODO: broken | |
| 640 anchorFrame = { | |
| 641 name = L["Anchor Frame"], | |
| 642 order = 2, | |
| 643 type = "select", | |
| 644 values = "GetAnchorFrames", | |
| 645 set = ??? | |
| 646 get = ??? | |
| 647 }, ]] | |
| 529 anchorPoint = { | 648 anchorPoint = { | 
| 530 name = L["Point"], | 649 name = L["Point"], | 
| 531 order = 2, | 650 order = 3, | 
| 532 type = "select", | 651 type = "select", | 
| 533 values = pointTable, | 652 values = pointTable, | 
| 534 set = "SetAnchorPointProp", | 653 set = "SetAnchorPointProp", | 
| 535 get = "GetAnchorPointProp", | 654 get = "GetAnchorPointProp", | 
| 536 disabled = "GetAnchorDisabled", | 655 disabled = "GetAnchorDisabled", | 
| 537 hidden = "GetAnchorDisabled", | 656 hidden = "GetAnchorDisabled", | 
| 538 }, | 657 }, | 
| 539 anchorRelPoint = { | 658 anchorRelPoint = { | 
| 540 name = L["Relative Point"], | 659 name = L["Relative Point"], | 
| 541 order = 3, | 660 order = 4, | 
| 542 type = "select", | 661 type = "select", | 
| 543 values = pointTable, | 662 values = pointTable, | 
| 544 set = "SetAnchorPointProp", | 663 set = "SetAnchorPointProp", | 
| 545 get = "GetAnchorPointProp", | 664 get = "GetAnchorPointProp", | 
| 546 disabled = "GetAnchorDisabled", | 665 disabled = "GetAnchorDisabled", | 
| 547 hidden = "GetAnchorDisabled", | 666 hidden = "GetAnchorDisabled", | 
| 548 }, | 667 }, | 
| 549 anchorX = { | 668 anchorX = { | 
| 550 name = L["X Offset"], | 669 name = L["X Offset"], | 
| 551 order = 4, | 670 order = 5, | 
| 552 type = "range", | 671 type = "range", | 
| 553 min = -100, | 672 min = -100, | 
| 554 max = 100, | 673 max = 100, | 
| 555 step = 1, | 674 step = 1, | 
| 556 set = "SetProp", | 675 set = "SetProp", | 
| 558 disabled = "GetAnchorDisabled", | 677 disabled = "GetAnchorDisabled", | 
| 559 hidden = "GetAnchorDisabled", | 678 hidden = "GetAnchorDisabled", | 
| 560 }, | 679 }, | 
| 561 anchorY = { | 680 anchorY = { | 
| 562 name = L["Y Offset"], | 681 name = L["Y Offset"], | 
| 563 order = 5, | 682 order = 6, | 
| 564 type = "range", | 683 type = "range", | 
| 565 min = -100, | 684 min = -100, | 
| 566 max = 100, | 685 max = 100, | 
| 567 step = 1, | 686 step = 1, | 
| 568 set = "SetProp", | 687 set = "SetProp", | 
| 678 }, | 797 }, | 
| 679 }, | 798 }, | 
| 680 }, | 799 }, | 
| 681 } | 800 } | 
| 682 | 801 | 
| 683 local StateHandler = { } | 802 local handlers = { } | 
| 803 local meta = { | |
| 804 __index = function(self, key) | |
| 805 for _, h in pairs(handlers) do | |
| 806 if h[key] then | |
| 807 return h[key] | |
| 808 end | |
| 809 end | |
| 810 end, | |
| 811 } | |
| 812 local StateHandler = setmetatable({ }, meta) | |
| 813 local proto = { __index = StateHandler } | |
| 814 | |
| 815 function RegisterPropertyOptions( field, options, handler ) | |
| 816 stateOptions.properties.plugins[field] = options | |
| 817 handlers[field] = handler | |
| 818 end | |
| 819 | |
| 820 function UnregisterPropertyOptions( field ) | |
| 821 stateOptions.properties.plugins[field] = nil | |
| 822 handlers[field] = nil | |
| 823 end | |
| 684 | 824 | 
| 685 function StateHandler:New( bar, opts ) | 825 function StateHandler:New( bar, opts ) | 
| 686 local self = setmetatable({ bar = bar }, { __index = StateHandler }) | 826 local self = setmetatable( | 
| 827 { | |
| 828 bar = bar | |
| 829 }, | |
| 830 proto ) | |
| 687 | 831 | 
| 688 function self:GetName() | 832 function self:GetName() | 
| 689 return opts.name | 833 return opts.name | 
| 690 end | 834 end | 
| 691 | 835 | 
| 698 end | 842 end | 
| 699 | 843 | 
| 700 -- get reference to states table: even if the bar | 844 -- get reference to states table: even if the bar | 
| 701 -- name changes the states table ref won't | 845 -- name changes the states table ref won't | 
| 702 self.states = tbuild(module.db.profile.bars, bar:GetName(), "states") | 846 self.states = tbuild(module.db.profile.bars, bar:GetName(), "states") | 
| 703 | 847 self.state = tbuild(self.states, opts.name) | 
| 704 tbuild(self.states, opts.name) | 848 | 
| 705 | 849 opts.order = self:GetRuleField("order") | 
| 706 opts.order = self:GetRule("order") | |
| 707 if opts.order == nil then | 850 if opts.order == nil then | 
| 708 -- add after the highest | 851 -- add after the highest | 
| 709 opts.order = 100 | 852 opts.order = 100 | 
| 710 for _, state in pairs(self.states) do | 853 for _, state in pairs(self.states) do | 
| 711 local x = tonumber(tfetch(state, "rule", "order")) | 854 local x = tonumber(tfetch(state, "rule", "order")) | 
| 712 if x and x >= opts.order then | 855 if x and x >= opts.order then | 
| 713 opts.order = x + 1 | 856 opts.order = x + 1 | 
| 714 end | 857 end | 
| 715 end | 858 end | 
| 716 self:SetRule("order",opts.order) | 859 self:SetRuleField("order",opts.order) | 
| 717 end | 860 end | 
| 718 | 861 | 
| 719 return self | 862 return self | 
| 720 end | 863 end | 
| 721 | 864 | 
| 722 -- helper methods | 865 -- helper methods | 
| 723 | 866 | 
| 724 function StateHandler:SetRule( key, value, ... ) | 867 function StateHandler:SetRuleField( key, value, ... ) | 
| 725 tbuild(self.states, self:GetName(), "rule", ...)[key] = value | 868 tbuild(self.state, "rule", ...)[key] = value | 
| 726 end | 869 end | 
| 727 | 870 | 
| 728 function StateHandler:GetRule( ... ) | 871 function StateHandler:GetRuleField( ... ) | 
| 729 return tfetch(self.states, self:GetName(), "rule", ...) | 872 return tfetch(self.state, "rule", ...) | 
| 730 end | 873 end | 
| 731 | 874 | 
| 732 function StateHandler:FixAll( setkey ) | 875 function StateHandler:FixAll( setkey ) | 
| 733 -- if multiple selections in the same group are chosen when 'all' is selected, | 876 -- if multiple selections in the same group are chosen when 'all' is selected, | 
| 734 -- keep only one of them. If changing the mode, the first in the fields list will | 877 -- keep only one of them. If changing the mode, the first in the fields list will | 
| 735 -- be chosen arbitrarily. Otherwise, if selecting a new checkbox from the field-set, | 878 -- be chosen arbitrarily. Otherwise, if selecting a new checkbox from the field-set, | 
| 736 -- it will be retained. | 879 -- it will be retained. | 
| 737 local notified = false | 880 local notified = false | 
| 738 if self:GetRule("type") == "all" then | 881 if self:GetRuleField("type") == "all" then | 
| 739 for _, c in ipairs(rules) do | 882 for _, c in ipairs(rules) do | 
| 740 local rule, hidden, fields = unpack(c) | 883 local rule, hidden, fields = unpack(c) | 
| 741 local once = false | 884 local once = false | 
| 742 if setkey then | 885 if setkey then | 
| 743 for idx, field in ipairs(fields) do | 886 for idx, field in ipairs(fields) do | 
| 746 end | 889 end | 
| 747 end | 890 end | 
| 748 end | 891 end | 
| 749 for idx, field in ipairs(fields) do | 892 for idx, field in ipairs(fields) do | 
| 750 local key = next(field) | 893 local key = next(field) | 
| 751 if self:GetRule("values",key) then | 894 if self:GetRuleField("values",key) then | 
| 752 if once and key ~= setkey then | 895 if once and key ~= setkey then | 
| 753 self:SetRule(key,false,"values") | 896 self:SetRuleField(key,false,"values") | 
| 754 if not setkey and not notified then | 897 if not setkey and not notified then | 
| 755 ReAction:UserError(L["Warning: one or more incompatible rules were turned off"]) | 898 ReAction:UserError(L["Warning: one or more incompatible rules were turned off"]) | 
| 756 notified = true | 899 notified = true | 
| 757 end | 900 end | 
| 758 end | 901 end | 
| 856 function StateHandler:GetScaleDisabled() | 999 function StateHandler:GetScaleDisabled() | 
| 857 return not GetProperty(self.bar, self:GetName(), "enableScale") | 1000 return not GetProperty(self.bar, self:GetName(), "enableScale") | 
| 858 end | 1001 end | 
| 859 | 1002 | 
| 860 function StateHandler:SetType(info, value) | 1003 function StateHandler:SetType(info, value) | 
| 861 self:SetRule("type", value) | 1004 self:SetRuleField("type", value) | 
| 862 self:FixAll() | 1005 self:FixAll() | 
| 863 ApplyStates(self.bar) | 1006 ApplyStates(self.bar) | 
| 864 end | 1007 end | 
| 865 | 1008 | 
| 866 function StateHandler:GetType() | 1009 function StateHandler:GetType() | 
| 867 return self:GetRule("type") | 1010 return self:GetRuleField("type") | 
| 868 end | 1011 end | 
| 869 | 1012 | 
| 870 function StateHandler:GetClearAllDisabled() | 1013 function StateHandler:GetClearAllDisabled() | 
| 871 local t = self:GetRule("type") | 1014 local t = self:GetRuleField("type") | 
| 872 return not( t == "any" or t == "all" or t == "custom") | 1015 return not( t == "any" or t == "all" or t == "custom") | 
| 873 end | 1016 end | 
| 874 | 1017 | 
| 875 function StateHandler:ClearAllConditions() | 1018 function StateHandler:ClearAllConditions() | 
| 876 local t = self:GetRule("type") | 1019 local t = self:GetRuleField("type") | 
| 877 if t == "custom" then | 1020 if t == "custom" then | 
| 878 self:SetRule("custom","") | 1021 self:SetRuleField("custom","") | 
| 879 elseif t == "any" or t == "all" then | 1022 elseif t == "any" or t == "all" then | 
| 880 self:SetRule("values", {}) | 1023 self:SetRuleField("values", {}) | 
| 881 end | 1024 end | 
| 882 ApplyStates(self.bar) | 1025 ApplyStates(self.bar) | 
| 883 end | 1026 end | 
| 884 | 1027 | 
| 885 function StateHandler:GetConditionsDisabled() | 1028 function StateHandler:GetConditionsDisabled() | 
| 886 local t = self:GetRule("type") | 1029 local t = self:GetRuleField("type") | 
| 887 return not( t == "any" or t == "all") | 1030 return not( t == "any" or t == "all") | 
| 888 end | 1031 end | 
| 889 | 1032 | 
| 890 function StateHandler:SetCondition(info, key, value) | 1033 function StateHandler:SetCondition(info, key, value) | 
| 891 self:SetRule(ruleMap[key], value or nil, "values") | 1034 self:SetRuleField(ruleMap[key], value or nil, "values") | 
| 892 if value then | 1035 if value then | 
| 893 self:FixAll(ruleMap[key]) | 1036 self:FixAll(ruleMap[key]) | 
| 894 end | 1037 end | 
| 895 ApplyStates(self.bar) | 1038 ApplyStates(self.bar) | 
| 896 end | 1039 end | 
| 897 | 1040 | 
| 898 function StateHandler:GetCondition(info, key) | 1041 function StateHandler:GetCondition(info, key) | 
| 899 return self:GetRule("values", ruleMap[key]) or false | 1042 return self:GetRuleField("values", ruleMap[key]) or false | 
| 900 end | 1043 end | 
| 901 | 1044 | 
| 902 function StateHandler:GetCustomDisabled() | 1045 function StateHandler:GetCustomDisabled() | 
| 903 return self:GetRule("type") ~= "custom" | 1046 return self:GetRuleField("type") ~= "custom" | 
| 904 end | 1047 end | 
| 905 | 1048 | 
| 906 function StateHandler:SetCustomRule(info, value) | 1049 function StateHandler:SetCustomRule(info, value) | 
| 907 self:SetRule("custom",value) | 1050 self:SetRuleField("custom",value) | 
| 908 ApplyStates(self.bar) | 1051 ApplyStates(self.bar) | 
| 909 end | 1052 end | 
| 910 | 1053 | 
| 911 function StateHandler:GetCustomRule() | 1054 function StateHandler:GetCustomRule() | 
| 912 return self:GetRule("custom") or "" | 1055 return self:GetRuleField("custom") or "" | 
| 913 end | 1056 end | 
| 914 | 1057 | 
| 915 function StateHandler:ValidateCustomRule(info, value) | 1058 function StateHandler:ValidateCustomRule(info, value) | 
| 916 local s = value:gsub("%s","") -- remove all spaces | 1059 local s = value:gsub("%s","") -- remove all spaces | 
| 917 -- unfortunately %b and captures don't support the '+' notation, or this would be considerably simpler | 1060 -- unfortunately %b and captures don't support the '+' notation, or this would be considerably simpler | 
| 927 until c == nil | 1070 until c == nil | 
| 928 return true | 1071 return true | 
| 929 end | 1072 end | 
| 930 | 1073 | 
| 931 function StateHandler:GetKeybindDisabled() | 1074 function StateHandler:GetKeybindDisabled() | 
| 932 return self:GetRule("type") ~= "keybind" | 1075 return self:GetRuleField("type") ~= "keybind" | 
| 933 end | 1076 end | 
| 934 | 1077 | 
| 935 function StateHandler:GetKeybind() | 1078 function StateHandler:GetKeybind() | 
| 936 return self:GetRule("keybind") | 1079 return self:GetRuleField("keybind") | 
| 937 end | 1080 end | 
| 938 | 1081 | 
| 939 function StateHandler:SetKeybind(info, value) | 1082 function StateHandler:SetKeybind(info, value) | 
| 940 if value and #value == 0 then | 1083 if value and #value == 0 then | 
| 941 value = nil | 1084 value = nil | 
| 942 end | 1085 end | 
| 943 self:SetRule("keybind",value) | 1086 self:SetRuleField("keybind",value) | 
| 944 ApplyStates(self.bar) | 1087 ApplyStates(self.bar) | 
| 945 end | 1088 end | 
| 946 | 1089 | 
| 947 local function CreateStateOptions(bar, name) | 1090 local function CreateStateOptions(bar, name) | 
| 948 local opts = { | 1091 local opts = { | 
| 954 | 1097 | 
| 955 opts.handler = StateHandler:New(bar,opts) | 1098 opts.handler = StateHandler:New(bar,opts) | 
| 956 | 1099 | 
| 957 return opts | 1100 return opts | 
| 958 end | 1101 end | 
| 959 | |
| 960 | |
| 961 function RegisterPropertyOptions( field, options, handler ) | |
| 962 stateOptions.properties.plugins[field] = options | |
| 963 if handler then | |
| 964 for k,v in pairs(handler) do | |
| 965 StateHandler[k] = v | |
| 966 end | |
| 967 end | |
| 968 end | |
| 969 | |
| 970 | 1102 | 
| 971 function module:GetBarOptions(bar) | 1103 function module:GetBarOptions(bar) | 
| 972 local private = { } | 1104 local private = { } | 
| 973 local states = tbuild(module.db.profile.bars, bar:GetName(), "states") | 1105 local states = tbuild(module.db.profile.bars, bar:GetName(), "states") | 
| 974 local options = { | 1106 local options = { | 
| 1031 end | 1163 end | 
| 1032 end | 1164 end | 
| 1033 | 1165 | 
| 1034 -- Module API -- | 1166 -- Module API -- | 
| 1035 | 1167 | 
| 1036 -- Pass in a property field-name, an implementation function, a static options table, and an | 1168 -- Pass in a property field-name, an implementation secure snippet, a static options table, and an | 
| 1037 -- optional options handler method-table | 1169 -- optional options handler method-table | 
| 1038 -- | |
| 1039 -- propertyImplFunc prototype: | |
| 1040 -- propertyImplFunc( bar, stateTable ) | |
| 1041 -- where stateTable is a { ["statename"] = { state config } } table. | |
| 1042 -- | 1170 -- | 
| 1043 -- The options table is static, i.e. not bar-specific and should only reference handler method | 1171 -- The options table is static, i.e. not bar-specific and should only reference handler method | 
| 1044 -- strings (either existing ones or those added via optHandler). The existing options are ordered | 1172 -- strings (either existing ones or those added via optHandler). The existing options are ordered | 
| 1045 -- 90-99. Order #1 is reserved for the heading. | 1173 -- 90-99. Order #1 is reserved for the heading. | 
| 1046 -- | 1174 -- | 
| 1047 -- The contents of optHandler, if provided, will be added to the existing StateHandler metatable. | 1175 -- The contents of optHandler, if provided, will be added to the existing StateHandler options metatable. | 
| 1048 -- See above, for existing API. In particular see the properties set up in the New method: self.bar, | 1176 -- See above, for existing API. In particular see the properties set up in the New method: self.bar, | 
| 1049 -- self.states, and self:GetName(), and the generic property handlers self:GetProp() and self:SetProp(). | 1177 -- self.states, and self:GetName(), and the generic property handlers self:GetProp() and self:SetProp(). | 
| 1050 -- | 1178 -- | 
| 1051 function module:RegisterStateProperty( field, propertyImplFunc, options, optHandler ) | 1179 function module:RegisterStateProperty( field, snippetHandler, options, optHandler ) | 
| 1052 RegisterProperty(field, propertyImplFunc) | 1180 RegisterProperty(field, snippetHandler) | 
| 1053 RegisterPropertyOptions(field, options, optHandler) | 1181 RegisterPropertyOptions(field, options, optHandler) | 
| 1054 end | 1182 end | 
| 1055 | 1183 | 
| 1184 function module:UnregisterStateProperty( field ) | |
| 1185 UnregisterProperty(field) | |
| 1186 UnregisterPropertyOptions(field) | |
| 1187 end | 
