Mercurial > wow > reaction
diff Editor.lua @ 244:f255cd69e890
fold state options into Editor.lua
author | Flick |
---|---|
date | Sat, 26 Mar 2011 12:26:55 -0700 |
parents | 704f4a05a1d7 |
children | d7a7ef367faf |
line wrap: on
line diff
--- a/Editor.lua Sat Mar 26 11:31:01 2011 -0700 +++ b/Editor.lua Sat Mar 26 12:26:55 2011 -0700 @@ -3,12 +3,17 @@ local L = ReAction.L local _G = _G local wipe = wipe +local format = string.format +local InCombatLockdown = InCombatLockdown +local tfetch = addonTable.tfetch +local tbuild = addonTable.tbuild local AceConfigReg = LibStub("AceConfigRegistry-3.0") local AceConfigDialog = LibStub("AceConfigDialog-3.0") local pointTable = { + NONE = " ", CENTER = L["Center"], LEFT = L["Left"], RIGHT = L["Right"], @@ -325,29 +330,12 @@ }, }, }, - buttonOpts = self:CreateButtonOptions(bar) - }, - plugins = { } + buttonOpts = self:CreateButtonOptions(bar), + stateOpts = self:CreateStateOptions(bar) + } } end - if ReAction.barOptionGenerators then - for module, func in pairs(ReAction.barOptionGenerators) do - local success, r - if type(func) == "string" then - success, r = pcall(module[func], module, bar) - else - success, r = pcall(func, bar) - end - if success then - if r then - args[key].plugins[module:GetName()] = { [module:GetName()] = r } - end - else - geterrorhandler()(r) - end - end - end end function Editor:CreateButtonOptions(bar) @@ -956,6 +944,749 @@ end +------------------------------ +--- Dynamic State options ---- +------------------------------ +do + local ApplyStates = ReAction.Bar.ApplyStates + local CleanupStates = ReAction.Bar.CleanupStates + local SetProperty = ReAction.Bar.SetStateProperty + local GetProperty = ReAction.Bar.GetStateProperty + + -- pre-sorted by the order they should appear in + local rules = { + -- rule fields + { "stance", { {battle = L["Battle Stance"]}, {defensive = L["Defensive Stance"]}, {berserker = L["Berserker Stance"]} } }, + { "form", { {caster = L["Caster Form"]}, {bear = L["Bear Form"]}, {cat = L["Cat Form"]}, {tree = L["Tree of Life"]}, {moonkin = L["Moonkin Form"]} } }, + { "stealth", { {stealth = L["Stealth"]}, {nostealth = L["No Stealth"]}, {shadowdance = L["Shadow Dance"]} } }, + { "shadow", { {shadowform = L["Shadowform"]}, {noshadowform = L["No Shadowform"]} } }, + { "demon", { {demon = L["Demon Form"]}, {nodemon = L["No Demon Form"]} } }, + { "pet", { {pet = L["With Pet"]}, {nopet = L["Without Pet"]} } }, + { "target", { {harm = L["Hostile Target"]}, {help = L["Friendly Target"]}, {notarget = L["No Target"]} } }, + { "focus", { {focusharm = L["Hostile Focus"]}, {focushelp = L["Friendly Focus"]}, {nofocus = L["No Focus"]} } }, + { "possess", { {possess = L["Mind Control"]} } }, + { "vehicle", { {vehicle = L["In a Vehicle"]} } }, + { "group", { {raid = L["Raid"]}, {party = L["Party"]}, {solo = L["Solo"]} } }, + { "combat", { {combat = L["In Combat"]}, {nocombat = L["Out of Combat"]} } }, + } + + local ruleSelect = { } + local ruleMap = { } + local optionMap = setmetatable({},{__mode="k"}) + + + -- unpack rules table into ruleSelect and ruleMap + for _, c in ipairs(rules) do + local rule, fields = unpack(c) + for _, field in ipairs(fields) do + local key, label = next(field) + table.insert(ruleSelect, label) + table.insert(ruleMap, key) + end + end + + local stateOptions = { + ordering = { + name = L["Info"], + order = 1, + type = "group", + args = { + delete = { + name = L["Delete this State"], + order = -1, + type = "execute", + func = "DeleteState", + }, + rename = { + name = L["Name"], + order = 1, + type = "input", + get = "GetName", + set = "SetStateName", + pattern = "^%w*$", + usage = L["State names must be alphanumeric without spaces"], + }, + ordering = { + name = L["Evaluation Order"], + desc = L["State transitions are evaluated in the order listed:\nMove a state up or down to change the order"], + order = 2, + type = "group", + inline = true, + args = { + up = { + name = L["Up"], + order = 1, + type = "execute", + width = "half", + func = "MoveStateUp", + }, + down = { + name = L["Down"], + order = 2, + type = "execute", + width = "half", + func = "MoveStateDown", + } + } + } + } + }, + properties = { + name = L["Properties"], + order = 2, + type = "group", + args = { + desc = { + name = L["Set the properties for the bar when in this state"], + order = 1, + type = "description" + }, + page = { + name = L["Show Page #"], + order = 11, + type = "select", + width = "half", + disabled = "IsPageDisabled", + hidden = "IsPageHidden", + values = "GetPageValues", + set = "SetProp", + get = "GetPage", + }, + hide = { + name = L["Hide Bar"], + order = 90, + type = "toggle", + set = "SetProp", + get = "GetProp", + }, + --[[ BROKEN + keybindState = { + name = L["Override Keybinds"], + desc = L["Set this state to maintain its own set of keybinds which override the defaults when active"], + order = 91, + type = "toggle", + set = "SetProp", + get = "GetProp", + }, ]] + position = { + name = L["Position"], + order = 92, + type = "group", + inline = true, + args = { + anchorEnable = { + name = L["Reposition"], + order = 1, + type = "toggle", + set = "SetProp", + get = "GetProp", + }, + anchorFrame = { + name = L["Anchor Frame"], + order = 2, + type = "select", + values = "GetAnchorFrames", + set = "SetAnchorFrame", + get = "GetAnchorFrame", + disabled = "GetAnchorDisabled", + hidden = "GetAnchorDisabled", + }, + anchorPoint = { + name = L["Point"], + order = 3, + type = "select", + values = pointTable, + set = "SetAnchorPointProp", + get = "GetAnchorPointProp", + disabled = "GetAnchorDisabled", + hidden = "GetAnchorDisabled", + }, + anchorRelPoint = { + name = L["Relative Point"], + order = 4, + type = "select", + values = pointTable, + set = "SetAnchorPointProp", + get = "GetAnchorPointProp", + disabled = "GetAnchorDisabled", + hidden = "GetAnchorDisabled", + }, + anchorX = { + name = L["X Offset"], + order = 5, + type = "range", + min = -100, + max = 100, + step = 1, + set = "SetProp", + get = "GetProp", + disabled = "GetAnchorDisabled", + hidden = "GetAnchorDisabled", + }, + anchorY = { + name = L["Y Offset"], + order = 6, + type = "range", + min = -100, + max = 100, + step = 1, + set = "SetProp", + get = "GetProp", + disabled = "GetAnchorDisabled", + hidden = "GetAnchorDisabled", + }, + }, + }, + scale = { + name = L["Scale"], + order = 93, + type = "group", + inline = true, + args = { + enableScale = { + name = L["Set New Scale"], + order = 1, + type = "toggle", + set = "SetProp", + get = "GetProp", + }, + scale = { + name = L["Scale"], + order = 2, + type = "range", + min = 0.25, + max = 2.5, + step = 0.05, + isPercent = true, + set = "SetProp", + get = "GetScale", + disabled = "GetScaleDisabled", + hidden = "GetScaleDisabled", + }, + }, + }, + alpha = { + name = L["Transparency"], + order = 94, + type = "group", + inline = true, + args = { + enableAlpha = { + name = L["Set Transparency"], + order = 1, + type = "toggle", + set = "SetProp", + get = "GetProp", + }, + alpha = { + name = L["Transparency"], + order = 2, + type = "range", + min = 0, + max = 1, + step = 0.01, + bigStep = 0.05, + isPercent = true, + set = "SetProp", + get = "GetAlpha", + disabled = "GetAlphaDisabled", + hidden = "GetAlphaDisabled", + }, + }, + }, + }, + plugins = { } + }, + rules = { + name = L["Rule"], + order = 3, + type = "group", + args = { + mode = { + name = L["Select this state"], + order = 2, + type = "select", + style = "radio", + values = { + default = L["by default"], + any = L["when ANY of these"], + all = L["when ALL of these"], + custom = L["via custom rule"], + keybind = L["via keybinding"], + }, + set = "SetType", + get = "GetType", + }, + clear = { + name = L["Clear All"], + order = 3, + type = "execute", + hidden = "GetClearAllDisabled", + disabled = "GetClearAllDisabled", + func = "ClearAllConditions", + }, + inputs = { + name = L["Conditions"], + order = 4, + type = "multiselect", + hidden = "GetConditionsDisabled", + disabled = "GetConditionsDisabled", + values = ruleSelect, + set = "SetCondition", + get = "GetCondition", + }, + custom = { + name = L["Custom Rule"], + order = 5, + type = "input", + multiline = true, + hidden = "GetCustomDisabled", + disabled = "GetCustomDisabled", + desc = L["Syntax like macro rules: see preset rules for examples"], + set = "SetCustomRule", + get = "GetCustomRule", + validate = "ValidateCustomRule", + }, + keybind = { + name = L["Keybinding"], + order = 6, + inline = true, + hidden = "GetKeybindDisabled", + disabled = "GetKeybindDisabled", + type = "group", + args = { + desc = { + name = L["Invoking a state keybind toggles an override of all other transition rules."], + order = 1, + type = "description", + }, + keybind = { + name = L["State Hotkey"], + desc = L["Define an override toggle keybind"], + order = 2, + type = "keybinding", + set = "SetKeybind", + get = "GetKeybind", + }, + }, + }, + }, + }, + } + + local StateHandler = { } + local meta = { __index = StateHandler } + + function StateHandler:New( bar, opts ) + local self = setmetatable( + { + bar = bar + }, + meta ) + + function self:GetName() + return opts.name + end + + function self:SetName(name) + opts.name = name + end + + function self:GetOrder() + return opts.order + end + + -- get reference to states table: even if the bar + -- name changes the states table ref won't + self.states = tbuild(bar:GetConfig(), "states") + self.state = tbuild(self.states, opts.name) + + opts.order = self:GetRuleField("order") + if opts.order == nil then + -- add after the highest + opts.order = 100 + for _, state in pairs(self.states) do + local x = tonumber(tfetch(state, "rule", "order")) + if x and x >= opts.order then + opts.order = x + 1 + end + end + self:SetRuleField("order",opts.order) + end + + return self + end + + -- helper methods + + function StateHandler:SetRuleField( key, value, ... ) + tbuild(self.state, "rule", ...)[key] = value + end + + function StateHandler:GetRuleField( ... ) + return tfetch(self.state, "rule", ...) + end + + function StateHandler:FixAll( setkey ) + -- if multiple selections in the same group are chosen when 'all' is selected, + -- keep only one of them. If changing the mode, the first in the fields list will + -- be chosen arbitrarily. Otherwise, if selecting a new checkbox from the field-set, + -- it will be retained. + local notified = false + if self:GetRuleField("type") == "all" then + for _, c in ipairs(rules) do + local rule, fields = unpack(c) + local once = false + if setkey then + for idx, field in ipairs(fields) do + if next(field) == setkey then + once = true + end + end + end + for idx, field in ipairs(fields) do + local key = next(field) + if self:GetRuleField("values",key) then + if once and key ~= setkey then + self:SetRuleField(key,false,"values") + if not setkey and not notified then + ReAction:UserError(L["Warning: one or more incompatible rules were turned off"]) + notified = true + end + end + once = true + end + end + end + end + end + + function StateHandler:GetNeighbors() + local before, after + for k, v in pairs(self.states) do + local o = tonumber(tfetch(v, "rule", "order")) + if o and k ~= self:GetName() then + local obefore = tfetch(self.states,before,"rule","order") + local oafter = tfetch(self.states,after,"rule","order") + if o < self:GetOrder() and (not obefore or obefore < o) then + before = k + end + if o > self:GetOrder() and (not oafter or oafter > o) then + after = k + end + end + end + return before, after + end + + function StateHandler:SwapOrder( a, b ) + -- do options table + local args = optionMap[self.bar].args + args[a].order, args[b].order = args[b].order, args[a].order + -- do profile + a = tbuild(self.states, a, "rule") + b = tbuild(self.states, b, "rule") + a.order, b.order = b.order, a.order + end + + -- handler methods + + function StateHandler:GetProp( info ) + -- gets property of the same name as the options arg + return GetProperty(self.bar, self:GetName(), info[#info]) + end + + function StateHandler:SetProp( info, value ) + -- sets property of the same name as the options arg + SetProperty(self.bar, self:GetName(), info[#info], value) + end + + function StateHandler:DeleteState() + if self.states[self:GetName()] then + self.states[self:GetName()] = nil + ApplyStates(self.bar) + end + optionMap[self.bar].args[self:GetName()] = nil + end + + function StateHandler:SetStateName(info, value) + -- check for existing state name + if self.states[value] then + ReAction:UserError(format(L["State named '%s' already exists"],value)) + return + end + local args = optionMap[self.bar].args + local name = self:GetName() + self.states[value], args[value], self.states[name], args[name] = self.states[name], args[name], nil, nil + self:SetName(value) + ApplyStates(self.bar) + ReAction:ShowEditor(self.bar, moduleID, value) + end + + function StateHandler:MoveStateUp() + local before, after = self:GetNeighbors() + if before then + self:SwapOrder(before, self:GetName()) + ApplyStates(self.bar) + end + end + + function StateHandler:MoveStateDown() + local before, after = self:GetNeighbors() + if after then + self:SwapOrder(self:GetName(), after) + ApplyStates(self.bar) + end + end + + function StateHandler:GetAnchorDisabled() + return not GetProperty(self.bar, self:GetName(), "anchorEnable") + end + + function StateHandler:IsPageDisabled() + local n = self.bar:GetConfig().nPages or 1 + return not (n > 1) + end + + function StateHandler:IsPageHidden() + return not self.bar:GetConfig().nPages + end + + function StateHandler:GetPageValues() + if not self._pagevalues then + self._pagevalues = { } + end + local n = self.bar:GetConfig().nPages + -- cache the results + if self._npages ~= n then + self._npages = n + wipe(self._pagevalues) + for i = 1, n do + self._pagevalues["page"..i] = i + end + end + return self._pagevalues + end + + function StateHandler:GetPage(info) + return self:GetProp(info) or 1 + end + + function StateHandler:GetAnchorFrames(info) + self._anchorframes = self._anchorframes or { } + table.wipe(self._anchorframes) + + table.insert(self._anchorframes, "UIParent") + for name, bar in ReAction:IterateBars() do + table.insert(self._anchorframes, bar:GetFrame():GetName()) + end + return self._anchorframes + end + + function StateHandler:GetAnchorFrame(info) + local value = self:GetProp(info) + for k,v in pairs(self._anchorframes) do + if v == value then + return k + end + end + end + + function StateHandler:SetAnchorFrame(info, value) + local f = _G[self._anchorframes[value]] + if f then + self.bar:SetFrameRef("anchor-"..self:GetName(), f) + self:SetProp(info, f:GetName()) + end + end + + function StateHandler:SetAnchorPointProp(info, value) + self:SetProp(info, value ~= "NONE" and value or nil) + end + + function StateHandler:GetAnchorPointProp(info) + return self:GetProp(info) or "NONE" + end + + function StateHandler:GetScale(info) + return self:GetProp(info) or 1.0 + end + + function StateHandler:GetScaleDisabled() + return not GetProperty(self.bar, self:GetName(), "enableScale") + end + + function StateHandler:GetAlpha(info) + return self:GetProp(info) or 1.0 + end + + function StateHandler:GetAlphaDisabled() + return not GetProperty(self.bar, self:GetName(), "enableAlpha") + end + + function StateHandler:SetType(info, value) + self:SetRuleField("type", value) + self:FixAll() + ApplyStates(self.bar) + end + + function StateHandler:GetType() + return self:GetRuleField("type") + end + + function StateHandler:GetClearAllDisabled() + local t = self:GetRuleField("type") + return not( t == "any" or t == "all" or t == "custom") + end + + function StateHandler:ClearAllConditions() + local t = self:GetRuleField("type") + if t == "custom" then + self:SetRuleField("custom","") + elseif t == "any" or t == "all" then + self:SetRuleField("values", {}) + end + ApplyStates(self.bar) + end + + function StateHandler:GetConditionsDisabled() + local t = self:GetRuleField("type") + return not( t == "any" or t == "all") + end + + function StateHandler:SetCondition(info, key, value) + self:SetRuleField(ruleMap[key], value or nil, "values") + if value then + self:FixAll(ruleMap[key]) + end + ApplyStates(self.bar) + end + + function StateHandler:GetCondition(info, key) + return self:GetRuleField("values", ruleMap[key]) or false + end + + function StateHandler:GetCustomDisabled() + return self:GetRuleField("type") ~= "custom" + end + + function StateHandler:SetCustomRule(info, value) + self:SetRuleField("custom",value) + ApplyStates(self.bar) + end + + function StateHandler:GetCustomRule() + return self:GetRuleField("custom") or "" + end + + function StateHandler:ValidateCustomRule(info, value) + local s = value:gsub("%s","") -- remove all spaces + -- unfortunately %b and captures don't support the '+' notation, or this would be considerably simpler + repeat + if s == "" then + return true + end + local c, r = s:match("(%b[])(.*)") + if c == nil and s and #s > 0 then + return format(L["Invalid custom rule '%s': each clause must appear within [brackets]"],value or "") + end + s = r + until c == nil + return true + end + + function StateHandler:GetKeybindDisabled() + return self:GetRuleField("type") ~= "keybind" + end + + function StateHandler:GetKeybind() + return self:GetRuleField("keybind") + end + + function StateHandler:SetKeybind(info, value) + if value and #value == 0 then + value = nil + end + self:SetRuleField("keybind",value) + ApplyStates(self.bar) + end + + local function CreateStateOptions(bar, name) + local opts = { + type = "group", + name = name, + childGroups = "tab", + args = stateOptions + } + + opts.handler = StateHandler:New(bar,opts) + + return opts + end + + function Editor:CreateStateOptions(bar) + local private = { } + local states = tbuild(bar:GetConfig(), "states") + local options = { + name = L["Dynamic State"], + type = "group", + order = -1, + childGroups = "tree", + disabled = InCombatLockdown, + args = { + __desc__ = { + name = L["States are evaluated in the order they are listed"], + order = 1, + type = "description", + }, + __new__ = { + name = L["New State..."], + order = 2, + type = "group", + args = { + name = { + name = L["State Name"], + desc = L["Set a name for the new state"], + order = 1, + type = "input", + get = function() return private.newstatename or "" end, + set = function(info,value) private.newstatename = value end, + pattern = "^%w*$", + usage = L["State names must be alphanumeric without spaces"], + }, + create = { + name = L["Create State"], + order = 2, + type = "execute", + func = function () + local name = private.newstatename + if states[name] then + ReAction:UserError(format(L["State named '%s' already exists"],name)) + else + -- TODO: select default state options and pass as final argument + states[name] = { } + optionMap[bar].args[name] = CreateStateOptions(bar,name) + ReAction:ShowEditor(bar, moduleID, name) + private.newstatename = "" + end + end, + disabled = function() + local name = private.newstatename or "" + return #name == 0 or name:find("%W") + end, + } + } + } + } + } + for name, config in pairs(states) do + options.args[name] = CreateStateOptions(bar,name) + end + optionMap[bar] = options + return options + end +end + ---- Export to ReAction ---- function ReAction:ShowEditor(bar, ...) @@ -980,22 +1711,3 @@ end end -function ReAction:RegisterBarOptionGenerator( module, func ) - if not module or type(module) ~= "table" then -- doesn't need to be a proper module, strictly - error("ReAction:RegisterBarOptionGenerator() : Invalid module") - end - if type(func) == "string" then - if not module[func] then - error(("ReAction:RegisterBarOptionGenerator() : Invalid method '%s'"):format(func)) - end - elseif func and type(func) ~= "function" then - error("ReAction:RegisterBarOptionGenerator() : Invalid function") - end - self.barOptionGenerators = self.barOptionGenerators or { } - self.barOptionGenerators[module] = func - - if self.editor then - self.editor:RefreshBarOptions() - end -end -