Mercurial > wow > reaction
diff ReAction.lua @ 63:768be7eb22a0
Converted several ReAction APIs to event-driven model instead of 'call-method-on-all-modules' model. Cleaned up a number of other architectural issues.
author | Flick <flickerstreak@gmail.com> |
---|---|
date | Thu, 22 May 2008 22:02:08 +0000 |
parents | 2ee41dcd673f |
children | 06cd74bdc7da |
line wrap: on
line diff
--- a/ReAction.lua Tue May 13 16:42:52 2008 +0000 +++ b/ReAction.lua Thu May 22 22:02:08 2008 +0000 @@ -1,13 +1,37 @@ --- ReAction.lua --- See modules/ReAction_ModuleTemplate for Module API listing --- See Bar.lua for Bar object listing +--[[ + ReAction.lua + + The ReAction core manages 4 collections: + - modules (via AceAddon) + - bars + - options + - bar-type constructors + + and publishes events when those collections change. It also implements a single property, 'config mode', + and has a couple convenience methods which drill down to particular modules. + + Most of the "real work" of the addon happens in Bar.lua and the various modules. + + Events (with handler arguments): + -------------------------------- + "OnCreateBar" (bar, name) : after a bar object is created + "OnDestroyBar" (bar, name) : before a bar object is destroyed + "OnEraseBar" (bar, name) : before a bar config is removed from the profile db + "OnRenameBar" (bar, oldname, newname) : after a bar is renamed + "OnRefreshBar" (bar, name) : after a bar's state has been updated + "OnOptionsRefreshed" () : after the global options tree is refreshed + "OnConfigModeChanged" (mode) : after the config mode is changed + "OnBarOptionGeneratorRegistered" (module, function) : after an options generator function is registered + + ReAction is also an AceAddon-3.0 and contains an AceDB-3.0, which in turn publish more events. +]]-- +local version = GetAddOnMetadata("ReAction","Version") ------ CORE ------ local ReAction = LibStub("AceAddon-3.0"):NewAddon( "ReAction", "AceConsole-3.0", "AceEvent-3.0" ) -ReAction.version = GetAddOnMetadata("ReAction","Version") ReAction.revision = tonumber(("$Revision$"):match("%d+")) ------ GLOBALS ------ @@ -20,29 +44,78 @@ dbprint = function(msg) DEFAULT_CHAT_FRAME:AddMessage(msg) end - --seterrorhandler(dbprint) else dbprint = function() end end ReAction.dbprint = dbprint ------ LIBRARIES ------ +local callbacks = LibStub("CallbackHandler-1.0"):New(ReAction) local L = LibStub("AceLocale-3.0"):GetLocale("ReAction") ReAction.L = L ------ PRIVATE ------ -local SelectBar, DestroyBar, InitializeBars, TearDownBars, DeepCopy, SafeCall, CheckMethod, SlashHandler +local private = { } +local bars = {} +local defaultBarConfig = {} +local barOptionGenerators = { } +local options = { + type = "group", + name = "ReAction", + childGroups = "tab", + args = { + _desc = { + type = "description", + name = L["Customizable replacement for Blizzard's Action Bars"], + order = 1, + }, + global = { + type = "group", + name = L["Global Settings"], + desc = L["Global configuration settings"], + args = { + unlock = { + type = "toggle", + name = L["Unlock Bars"], + desc = L["Unlock bars for dragging and resizing with the mouse"], + handler = ReAction, + get = "GetConfigMode", + set = function(info, value) ReAction:SetConfigMode(value) end, + disabled = InCombatLockdown, + order = 1 + }, + }, + plugins = { }, + order = 2, + }, + module = { + type = "group", + childGroups = "select", + name = L["Module Settings"], + desc = L["Configuration settings for each module"], + args = { }, + plugins = { }, + order = 3, + }, + }, + plugins = { } +} +ReAction.options = options + +local SelectBar, DestroyBar, InitializeBars, TearDownBars, DeepCopy, CallModuleMethod, SlashHandler do local pcall = pcall local geterrorhandler = geterrorhandler + local self = ReAction + local inited = false - SelectBar = function(x) + function SelectBar(x) local bar, name if type(x) == "string" then name = x - bar = ReAction:GetBar(name) + bar = self:GetBar(name) else - for k,v in pairs(ReAction.bars) do + for k,v in pairs(bars) do if v == x then name = k bar = x @@ -52,37 +125,40 @@ return bar, name end - DestroyBar = function(x) + function DestroyBar(x) local bar, name = SelectBar(x) - if name and bar then - ReAction.bars[name] = nil - ReAction:CallMethodOnAllModules("RemoveFromBar", bar) + if bar and name then + bars[name] = nil + callbacks:Fire("OnDestroyBar", bar, name) bar:Destroy() end end - InitializeBars = function () - if not(ReAction.inited) then - for name, config in pairs(ReAction.db.profile.bars) do + function InitializeBars() + if not inited then + for name, config in pairs(self.db.profile.bars) do if config then - ReAction:CreateBar(name, config) + self:CreateBar(name, config) end end - ReAction:CallMethodOnAllBars("ApplyAnchor") -- re-anchor in the case of oddball ordering - ReAction.inited = true + -- re-anchor in case anchor order does not match init order + for name, bar in pairs(bars) do + bar:ApplyAnchor() + end + inited = true end end - TearDownBars = function() - for name, bar in pairs(ReAction.bars) do + function TearDownBars() + for name, bar in pairs(bars) do if bar then - ReAction.bars[name] = DestroyBar(bar) + bars[name] = DestroyBar(bar) end end - ReAction.inited = false + inited = false end - DeepCopy = function(x) + function DeepCopy(x) if type(x) ~= "table" then return x end @@ -93,39 +169,41 @@ return r end - SafeCall = function(f, ...) - if f then - local success, err = pcall(f,...) - if not success then - geterrorhandler()(err) + function CallModuleMethod(modulename, method, ...) + local m = self:GetModule(modulename,true) + if not m then + LoadAddOn(("ReAction_%s"):format(modulename)) + m = self:GetModule(modulename,true) + if m then + dbprint(("succesfully loaded LOD module: %s"):format(modulename)) end end + if m then + if type(m) == "table" and type(m[method]) == "function" then + m[method](m,...) + else + dbprint(("Bad call '%s' to %s module"):format(tostring(method),modulename)); + end + else + self:Print(("Module '%s' not found"):format(tostring(modulename))) + end end - CheckMethod = function(m) - if type(m) == "function" then - return m - end - if type(m) ~= "string" then - error("Invalid method") - end - end - - SlashHandler = function(option) + function SlashHandler(option) if option == "config" then - ReAction:ShowConfig() + self:ShowConfig() elseif option == "edit" then - ReAction:ShowEditor() + self:ShowEditor() elseif option == "unlock" then - ReAction:SetConfigMode(true) + self:SetConfigMode(true) elseif option == "lock" then - ReAction:SetConfigMode(false) + self:SetConfigMode(false) else - ReAction:Print(("%3.1f.%d"):format(ReAction.version,ReAction.revision)) - ReAction:Print("/reaction config") - ReAction:Print("/reaction edit") - ReAction:Print("/reaction lock") - ReAction:Print("/reaction unlock") + self:Print(("%3.1f.%d"):format(version,self.revision)) + self:Print("/rxn config") + self:Print("/rxn edit") + self:Print("/rxn lock") + self:Print("/rxn unlock") end end end @@ -144,57 +222,12 @@ ) self.db.RegisterCallback(self,"OnProfileChanged") self.db.RegisterCallback(self,"OnProfileReset","OnProfileChanged") - self.callbacks = LibStub("CallbackHandler-1.0"):New(self) + + options.args.profile = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db) + self:RegisterChatCommand("reaction", SlashHandler) self:RegisterChatCommand("rxn", SlashHandler) self:RegisterEvent("PLAYER_REGEN_DISABLED") - - self.bars = {} - self.defaultBarConfig = {} - - self.options = { - type = "group", - name = "ReAction", - childGroups = "tab", - args = { - _desc = { - type = "description", - name = L["Customizable replacement for Blizzard's Action Bars"], - order = 1, - }, - global = { - type = "group", - name = L["Global Settings"], - desc = L["Global configuration settings"], - args = { - unlock = { - type = "toggle", - handler = module, - name = L["Unlock Bars"], - desc = L["Unlock bars for dragging and resizing with the mouse"], - get = function() return self.configMode end, - set = function(info, value) self:SetConfigMode(value) end, - disabled = InCombatLockdown, - order = 1 - }, - }, - plugins = { }, - order = 2, - }, - module = { - type = "group", - childGroups = "select", - name = L["Module Settings"], - desc = L["Configuration settings for each module"], - args = { }, - plugins = { }, - order = 3, - }, - profile = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db) - }, - plugins = { } - } - end function ReAction:OnEnable() @@ -210,29 +243,9 @@ InitializeBars() end -function ReAction:OnModuleEnable(module) - if module.ApplyToBar then - for _, b in pairs(bars) do - if b then - module:ApplyToBar(b) - end - end - end -end - -function ReAction:OnModuleDisable(module) - if module.RemoveFromBar then - for _, b in pairs(bars) do - if b then - module:RemoveFromBar(b) - end - end - end -end - function ReAction:PLAYER_REGEN_DISABLED() - if self.configMode == true then - UIErrorsFrame:AddMessage(L["ReAction config mode disabled during combat."]) + if private.configMode == true then + self:UserError(L["ReAction config mode disabled during combat."]) self:SetConfigMode(false) end end @@ -245,45 +258,9 @@ UIErrorsFrame:AddMessage(msg) end -function ReAction:CallMethodOnAllModules(method, ...) - local m = CheckMethod(method) - for _, x in self:IterateModules() do - if x then - SafeCall(m or x[method], x, ...) - end - end -end - -function ReAction:CallMethodOnAllBars(method,...) - local m = CheckMethod(method) - for _, x in pairs(self.bars) do - if x then - SafeCall(m or x[method], x, ...) - end - end -end - -function ReAction:CallModuleMethod(modulename, method, ...) - local m = self:GetModule(modulename,true) - if not m then - LoadAddOn(("ReAction_%s"):format(modulename)) - m = self:GetModule(modulename,true) - if m then - dbprint(("succesfully loaded LOD module: %s"):format(modulename)) - end - end - if m then - if type(m) == "table" and type(m[method]) == "function" then - m[method](m,...) - else - dbprint(("Bad call '%s' to %s module"):format(tostring(method),modulename)); - end - else - self:Print(("Module '%s' not found"):format(tostring(modulename))) - end -end - - +-- usage: +-- (1) ReAction:CreateBar(name, cfgTable) +-- (2) ReAction:CreateBar(name, "barType", [nRows], [nCols], [btnSize], [btnSpacing]) function ReAction:CreateBar(name, ...) local config = select(1,...) if config and type(config) ~= "table" then @@ -291,7 +268,7 @@ if type(bartype) ~= "string" then error("ReAction:CreateBar() - first argument must be a config table or a default config type string") end - config = self.defaultBarConfig[bartype] + config = defaultBarConfig[bartype] if not config then error(("ReAction:CreateBar() - unknown bar type '%s'"):format(bartype)) end @@ -317,14 +294,13 @@ repeat name = prefix..i i = i + 1 - until self.bars[name] == nil + until bars[name] == nil end profile.bars[name] = profile.bars[name] or config local bar = self.Bar:new( name, profile.bars[name] ) -- ReAction.Bar defined in Bar.lua - self:CallMethodOnAllModules("ApplyToBar", bar) - self.bars[name] = bar - self.callbacks:Fire("OnCreateBar", bar) - if self.configMode then + bars[name] = bar + callbacks:Fire("OnCreateBar", bar, name) + if private.configMode then bar:ShowControls(true) end @@ -333,69 +309,150 @@ function ReAction:EraseBar(x) local bar, name = SelectBar(x) - if name and bar then + if bar and name then + callbacks:Fire("OnEraseBar", bar, name) DestroyBar(bar) self.db.profile.bars[name] = nil - self:CallMethodOnAllModules("EraseBarConfig", name) - self.callbacks:Fire("OnEraseBar",name) end end function ReAction:GetBar(name) - return self.bars[name] + return bars[name] +end + +function ReAction:IterateBars() + return pairs(bars) end function ReAction:RenameBar(x, newname) local bar, name = SelectBar(x) - if bar and name and newname then - if self.bars[newname] then - UIErrorsFrame:AddMessage(("%s ('%s')"):format(L["ReAction: name already in use"],newname)) + if type(newname) ~= "string" then + error("ReAction:RenameBar() - second argument must be a string") + end + if bar and name and #newname > 0 then + if bars[newname] then + self:UserError(("%s ('%s')"):format(L["ReAction: name already in use"],newname)) else - self.bars[newname] = self.bars[name] - self.bars[name] = nil + bars[newname], bars[name] = bars[name], nil bar:SetName(newname or "") local cfg = self.db.profile.bars cfg[newname], cfg[name] = cfg[name], nil - self:CallMethodOnAllModules("RenameBarConfig", name, newname) - self.callbacks:Fire("OnRenameBar", name, newname) + callbacks:Fire("OnRenameBar", bar, name, newname) end end end +function ReAction:RefreshBar(x) + local bar, name = SelectBar(x) + if bar and name then + callbacks:Fire("OnRefreshBar", bar, name) + end +end + function ReAction:RegisterBarType( name, config, isDefaultChoice ) - self.defaultBarConfig[name] = config + defaultBarConfig[name] = config if isDefaultChoice then - self.defaultBarConfigChoice = name + defaultBarConfigChoice = name end self:RefreshOptions() end function ReAction:UnregisterBarType( name ) - self.defaultBarConfig[name] = nil - if self.defaultBarConfigChoice == name then - self.defaultBarConfigChoice = nil + defaultBarConfig[name] = nil + if private.defaultBarConfigChoice == name then + private.defaultBarConfigChoice = nil end self:RefreshOptions() end -function ReAction:RegisterOptions(module, options, global) - self.options.args[global and "global" or "module"].plugins[module:GetName()] = options +function ReAction:IterateBarTypes() + return pairs(defaultBarConfig) +end + +function ReAction:GetBarTypeConfig(name) + if name then + return defaultBarConfig[name] + end +end + +function ReAction:GetBarTypeOptions( fill ) + fill = fill or { } + for k in self:IterateBarTypes() do + fill[k] = k + end + return fill +end + +function ReAction:GetDefaultBarType() + return private.defaultBarConfigChoice +end + +function ReAction:RegisterOptions(module, opts, global) + options.args[global and "global" or "module"].plugins[module:GetName()] = opts + self:RefreshOptions() end function ReAction:RefreshOptions() - self.callbacks:Fire("OnOptionsRefreshed") + callbacks:Fire("OnOptionsRefreshed") +end + +-- +-- In addition to global and general module options, options tables +-- must be generated dynamically for each bar. +-- +-- 'func' should be a function or a method string. +-- The function or method will be passed the bar as its parameter. +-- (methods will of course get the module as the first 'self' parameter) +-- +-- A generator can be unregistered by passing a nil func. +-- +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 + barOptionGenerators[module] = func + callbacks:Fire("OnBarOptionGeneratorRegistered", module, func) +end + +-- builds a table suitable for use as an AceConfig3 group 'plugins' sub-table +function ReAction:GenerateBarOptionsTable( bar ) + local opts = { } + for module, func in pairs(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 + opts[module:GetName()] = { [module:GetName()] = r } + else + geterrorhandler()(r) + end + end + return opts end function ReAction:SetConfigMode( mode ) - self:CallMethodOnAllBars("ShowControls",mode) - self:CallMethodOnAllModules("ApplyConfigMode",mode,self.bars) - self.configMode = mode + private.configMode = mode + callbacks:Fire("OnConfigModeChanged", mode) +end + +function ReAction:GetConfigMode() + return private.configMode end function ReAction:ShowConfig() - self:CallModuleMethod("ConfigUI","OpenConfig") + CallModuleMethod("ConfigUI","OpenConfig") end -function ReAction:ShowEditor() - self:CallModuleMethod("ConfigUI","LaunchBarEditor") +function ReAction:ShowEditor(bar) + CallModuleMethod("ConfigUI","LaunchBarEditor",bar) end