flickerstreak@25: --[[ flickerstreak@25: ReAction Configuration UI module flickerstreak@25: flickerstreak@33: Hooks into Blizzard Interface Options AddOns panel flickerstreak@25: --]] flickerstreak@25: flickerstreak@25: -- local imports flickerstreak@25: local ReAction = ReAction flickerstreak@25: local L = ReAction.L flickerstreak@47: local AceConfigReg = LibStub("AceConfigRegistry-3.0") flickerstreak@47: local AceConfigDialog = LibStub("AceConfigDialog-3.0") flickerstreak@25: flickerstreak@25: -- module declaration flickerstreak@25: local moduleID = "ConfigUI" flickerstreak@47: local module = ReAction:NewModule( moduleID, flickerstreak@47: "AceEvent-3.0" flickerstreak@47: ) flickerstreak@25: flickerstreak@25: -- module methods flickerstreak@25: function module:OnInitialize() flickerstreak@47: self.db = ReAction.db:RegisterNamespace( moduleID, flickerstreak@47: { flickerstreak@47: profile = { flickerstreak@47: closeOnLaunch = true, flickerstreak@47: editorCloseOnLaunch = true, flickerstreak@47: } flickerstreak@47: } flickerstreak@47: ) flickerstreak@47: flickerstreak@30: ReAction.RegisterCallback(self,"OnOptionsRegistered") flickerstreak@33: ReAction.RegisterCallback(self,"OnOptionsRefreshed") flickerstreak@47: ReAction.RegisterCallback(self,"OnCreateBar") flickerstreak@47: ReAction.RegisterCallback(self,"OnEraseBar") flickerstreak@47: ReAction.RegisterCallback(self,"OnRenameBar") flickerstreak@46: self:InitializeOptions() flickerstreak@47: self:RegisterEvent("PLAYER_REGEN_DISABLED") flickerstreak@25: end flickerstreak@25: flickerstreak@30: function module:OnOptionsRegistered(evt, context, module, opts) flickerstreak@46: local c = self.configOptions.args[context] flickerstreak@46: if c then flickerstreak@30: for k, v in pairs(opts) do flickerstreak@46: c.args[k] = v flickerstreak@30: end flickerstreak@47: elseif c == "bar" then flickerstreak@47: flickerstreak@30: end flickerstreak@25: end flickerstreak@25: flickerstreak@33: function module:OnOptionsRefreshed(evt) flickerstreak@33: -- TODO: refresh options frame (just OpenConfig again?) flickerstreak@25: end flickerstreak@25: flickerstreak@47: function module:OnCreateBar(evt, bar) flickerstreak@47: local name = bar:GetName() flickerstreak@47: -- AceConfig doesn't allow spaces, etc, in arg key names, and they must be flickerstreak@47: -- unique strings. So generate a unique key (it can be whatever) for the bar flickerstreak@47: local key flickerstreak@47: local i = 1 flickerstreak@47: repeat flickerstreak@47: key = ("bar%s"):format(i) flickerstreak@47: i = i+1 flickerstreak@47: until self.layoutOpts.args[key] == nil flickerstreak@47: self.barOptMap[name] = key flickerstreak@47: self.layoutOpts.args[key] = { flickerstreak@47: type = "group", flickerstreak@47: name = name, flickerstreak@47: childGroups = "tab", flickerstreak@47: args = { flickerstreak@47: general = { flickerstreak@47: type = "group", flickerstreak@47: name = L["General"], flickerstreak@47: args = { flickerstreak@47: name = { flickerstreak@47: type = "input", flickerstreak@47: name = L["Rename Bar"], flickerstreak@47: get = function() return bar:GetName() end, flickerstreak@47: set = function(info, value) return ReAction:RenameBar(bar, value) end, flickerstreak@47: order = 1, flickerstreak@47: }, flickerstreak@47: delete = { flickerstreak@47: type = "execute", flickerstreak@47: name = L["Delete Bar"], flickerstreak@47: desc = function() return bar:GetName() end, flickerstreak@47: confirm = true, flickerstreak@47: func = function() ReAction:EraseBar(bar) end, flickerstreak@47: order = -1 flickerstreak@47: }, flickerstreak@47: flickerstreak@47: } flickerstreak@47: }, flickerstreak@47: }, flickerstreak@47: flickerstreak@47: } flickerstreak@47: end flickerstreak@47: flickerstreak@47: function module:OnEraseBar(evt, name) flickerstreak@47: local key = self.barOptMap[name] flickerstreak@47: self.barOptMap[name] = nil flickerstreak@47: if key then flickerstreak@47: self.layoutOpts.args[key] = nil flickerstreak@47: self:RefreshLayoutEditor() flickerstreak@25: end flickerstreak@25: end flickerstreak@44: flickerstreak@47: function module:OnRenameBar(evt, oldname, newname) flickerstreak@47: local key = self.barOptMap[oldname] flickerstreak@47: self.barOptMap[oldname], self.barOptMap[newname] = nil, key flickerstreak@47: if key then flickerstreak@47: self.layoutOpts.args[key].name = newname flickerstreak@47: self:RefreshLayoutEditor() flickerstreak@47: end flickerstreak@47: end flickerstreak@47: flickerstreak@47: function module:PLAYER_REGEN_DISABLED() flickerstreak@47: if self.editor then flickerstreak@47: self.editor:Hide() flickerstreak@47: end flickerstreak@47: end flickerstreak@47: flickerstreak@47: function module:UserError(msg) flickerstreak@47: -- any user errors should be flashed to the UIErrorsFrame flickerstreak@47: UIErrorsFrame:AddMessage(msg) flickerstreak@47: end flickerstreak@47: flickerstreak@47: function module:OpenConfig() flickerstreak@47: InterfaceOptionsFrame_OpenToFrame("ReAction") flickerstreak@47: end flickerstreak@47: flickerstreak@47: function module:LaunchLayoutEditor(bar) flickerstreak@47: if InCombatLockdown() then flickerstreak@47: self:UserError(L["ReAction config mode disabled during combat."]) flickerstreak@47: else flickerstreak@47: if not self.editor then flickerstreak@47: -- use a local container to work around AceConfigDialog closing flickerstreak@47: -- both the layout editor and the global options when interface options is closed flickerstreak@47: local ed = LibStub("AceGUI-3.0"):Create("Frame") flickerstreak@47: ed.frame:SetClampedToScreen(true) flickerstreak@47: local old_OnUpdate = ed.frame:GetScript("OnUpdate") flickerstreak@47: ed.frame:SetScript("OnUpdate", function(dt) flickerstreak@47: if old_OnUpdate then flickerstreak@47: old_OnUpdate(dt) flickerstreak@47: end flickerstreak@47: if ed.closePending then flickerstreak@47: InterfaceOptionsFrame:Hide() flickerstreak@47: ed.closePending = false flickerstreak@47: end flickerstreak@47: if ed.selfClosePending then flickerstreak@47: ed:Hide() flickerstreak@47: AceConfigReg:NotifyChange("ReAction") flickerstreak@47: ed.selfClosePending = false flickerstreak@47: end flickerstreak@47: end ) flickerstreak@47: ed:SetCallback("OnClose", flickerstreak@47: function() flickerstreak@47: ReAction:SetConfigMode(false) flickerstreak@47: end ) flickerstreak@47: self.editor = ed flickerstreak@47: AceConfigDialog:SetDefaultSize("ReAction-Layout", 600, 450) flickerstreak@47: end flickerstreak@47: flickerstreak@47: AceConfigDialog:Open("ReAction-Layout", self.editor) flickerstreak@47: ReAction:SetConfigMode(true) flickerstreak@47: end flickerstreak@47: end flickerstreak@47: flickerstreak@47: function module:RefreshLayoutEditor() flickerstreak@47: AceConfigReg:NotifyChange("ReAction-Layout") flickerstreak@47: if self.editor and self.editor.frame:IsShown() then flickerstreak@47: AceConfigDialog:Open("ReAction-Layout", self.editor) flickerstreak@47: end flickerstreak@47: end flickerstreak@47: flickerstreak@47: function module:GetBarTypes() flickerstreak@47: local opts = self.optBarTypes or { } flickerstreak@47: self.optBarTypes = opts flickerstreak@47: flickerstreak@47: -- TODO: get these from module registration somehow flickerstreak@47: opts[1] = "foo" flickerstreak@47: flickerstreak@47: return opts flickerstreak@47: end flickerstreak@47: flickerstreak@47: function module:CreateBar() flickerstreak@47: local name = self.tmpBarName flickerstreak@47: local type = self.tmpBarType flickerstreak@47: flickerstreak@47: self.tmpBarName = nil flickerstreak@47: self.tmpBarType = nil flickerstreak@47: flickerstreak@47: -- TODO: get from module registration flickerstreak@47: -- TODO: use rows/cols info flickerstreak@47: ReAction:CreateBar(name) flickerstreak@47: end flickerstreak@47: flickerstreak@46: function module:InitializeOptions() flickerstreak@47: -- general config options flickerstreak@47: local opts = { flickerstreak@46: type = "group", flickerstreak@47: name = "ReAction", flickerstreak@46: childGroups = "tab", flickerstreak@46: args = { flickerstreak@47: _desc = { flickerstreak@47: type = "description", flickerstreak@47: name = L["Customizable replacement for Blizzard's Action Bars"], flickerstreak@47: order = 1, flickerstreak@47: }, flickerstreak@47: _launchLayout = { flickerstreak@47: type = "execute", flickerstreak@47: handler = self, flickerstreak@47: name = L["Layout Editor..."], flickerstreak@47: desc = L["Show the ReAction Layout Editor to edit your bars"], flickerstreak@47: func = function() flickerstreak@47: self:LaunchLayoutEditor() flickerstreak@47: -- you can't close a dialog in response to an options click, because the end of the flickerstreak@47: -- handler for all the button events calls lib:Open() flickerstreak@47: -- So, schedule a close on the next OnUpdate flickerstreak@47: if self.db.profile.closeOnLaunch then flickerstreak@47: self.editor.closePending = true flickerstreak@47: end flickerstreak@47: end, flickerstreak@47: order = 2, flickerstreak@47: }, flickerstreak@47: _closeThis = { flickerstreak@47: type = "toggle", flickerstreak@47: name = L["Close on Launch"], flickerstreak@47: desc = L["Close the Interface Options window when launching the ReAction Layout Editor"], flickerstreak@47: get = function() return self.db.profile.closeOnLaunch end, flickerstreak@47: set = function(info, val) self.db.profile.closeOnLaunch = val end, flickerstreak@47: order = 3, flickerstreak@47: }, flickerstreak@46: global = { flickerstreak@46: type = "group", flickerstreak@46: name = L["Global Settings"], flickerstreak@46: desc = L["Global configuration settings"], flickerstreak@46: args = { }, flickerstreak@47: order = 3, flickerstreak@46: }, flickerstreak@47: _profile = LibStub("AceDBOptions-3.0"):GetOptionsTable(ReAction.db), flickerstreak@46: module = { flickerstreak@46: type = "group", flickerstreak@46: childGroups = "select", flickerstreak@46: name = L["Module Settings"], flickerstreak@46: desc = L["Configuration settings for each module"], flickerstreak@46: args = { }, flickerstreak@46: order = -1, flickerstreak@46: }, flickerstreak@46: }, flickerstreak@46: plugins = { } flickerstreak@46: } flickerstreak@47: self.configOptions = opts flickerstreak@47: opts.args._profile.order = -2 flickerstreak@47: AceConfigReg:RegisterOptionsTable("ReAction",opts) flickerstreak@47: self.frame = AceConfigDialog:AddToBlizOptions("ReAction", "ReAction") flickerstreak@47: self.frame.obj:SetCallback("default", flickerstreak@47: function() flickerstreak@47: ReAction.db:ResetProfile() flickerstreak@47: module:OpenConfig() flickerstreak@47: end ) flickerstreak@46: flickerstreak@47: -- import options from registered modules flickerstreak@47: for c, tbl in pairs(opts.args) do flickerstreak@46: for _, m in pairs(ReAction:GetOptions(c)) do flickerstreak@46: for k, v in pairs(m) do flickerstreak@46: tbl.args[k] = v flickerstreak@46: end flickerstreak@46: end flickerstreak@46: end flickerstreak@46: flickerstreak@46: ReAction:RegisterOptions("module",self, { flickerstreak@46: configUI = { flickerstreak@46: type = "group", flickerstreak@46: name = "Config UI", flickerstreak@46: desc = "description", flickerstreak@46: args = { flickerstreak@46: foo = { flickerstreak@46: type = "toggle", flickerstreak@46: handler = self, flickerstreak@46: name = "foo", flickerstreak@46: desc = "description", flickerstreak@46: get = function() return true end, flickerstreak@46: set = function() end, flickerstreak@46: } flickerstreak@46: } flickerstreak@46: }, flickerstreak@46: }) flickerstreak@46: flickerstreak@47: -- layout editor options flickerstreak@47: local layoutOpts = { flickerstreak@47: type = "group", flickerstreak@47: name = ("ReAction - %s"):format(L["Layout"]), flickerstreak@47: handler = self, flickerstreak@47: childGroups = "tree", flickerstreak@47: args = { flickerstreak@47: desc = { flickerstreak@47: type = "description", flickerstreak@47: name = L["Use the mouse to arrange and resize the bars on screen. Tooltips on bars indicate additional functionality."], flickerstreak@47: order = 1 flickerstreak@47: }, flickerstreak@47: launchConfig = { flickerstreak@47: type = "execute", flickerstreak@47: name = L["Global Config"], flickerstreak@47: desc = L["Opens ReAction global configuration settings panel"], flickerstreak@47: func = function() flickerstreak@47: self:OpenConfig() flickerstreak@47: -- you can't close a dialog in response to an options click, because the end of the flickerstreak@47: -- handler for all the button events calls lib:Open() flickerstreak@47: -- So, schedule a close on the next appropriate event flickerstreak@47: if self.db.profile.editorCloseOnLaunch then flickerstreak@47: self.editor.selfClosePending = true flickerstreak@47: end flickerstreak@47: end, flickerstreak@47: order = 2 flickerstreak@47: }, flickerstreak@47: closThis = { flickerstreak@47: type = "toggle", flickerstreak@47: name = L["Close on Launch"], flickerstreak@47: desc = L["Close the Layout Editor when opening the ReAction global Interface Options"], flickerstreak@47: get = function() return self.db.profile.editorCloseOnLaunch end, flickerstreak@47: set = function(info, val) self.db.profile.editorCloseOnLaunch = val end, flickerstreak@47: order = 3, flickerstreak@47: }, flickerstreak@47: new = { flickerstreak@47: type = "group", flickerstreak@47: name = L["New Bar..."], flickerstreak@47: order = 4, flickerstreak@47: args = { flickerstreak@47: desc = { flickerstreak@47: type = "description", flickerstreak@47: name = L["Choose a name, type, and initial grid for your new action bar:"], flickerstreak@47: order = 1, flickerstreak@47: }, flickerstreak@47: name = { flickerstreak@47: type = "input", flickerstreak@47: name = L["Bar Name"], flickerstreak@47: desc = L["Enter a name for your new action bar"], flickerstreak@47: get = function() return self.tmpBarName or "" end, flickerstreak@47: set = function(info, val) self.tmpBarName = val end, flickerstreak@47: order = 2, flickerstreak@47: }, flickerstreak@47: type = { flickerstreak@47: type = "select", flickerstreak@47: name = L["Button Type"], flickerstreak@47: get = function() return self.tmpBarType or "" end, flickerstreak@47: set = function(info, val) self.tmpBarType = val end, flickerstreak@47: values = "GetBarTypes", flickerstreak@47: order = 3, flickerstreak@47: }, flickerstreak@47: grid = { flickerstreak@47: type = "group", flickerstreak@47: name = L["Button Grid"], flickerstreak@47: inline = true, flickerstreak@47: args = { flickerstreak@47: hdr = { flickerstreak@47: type = "header", flickerstreak@47: name = L["Button Grid"], flickerstreak@47: order = 1, flickerstreak@47: }, flickerstreak@47: rows = { flickerstreak@47: type = "range", flickerstreak@47: name = L["Rows"], flickerstreak@47: get = function() return self.tmpBarRows or 1 end, flickerstreak@47: set = function(info, val) self.tmpBarRows = val end, flickerstreak@47: width = "half", flickerstreak@47: min = 1, flickerstreak@47: max = 32, flickerstreak@47: step = 1, flickerstreak@47: order = 2, flickerstreak@47: }, flickerstreak@47: cols = { flickerstreak@47: type = "range", flickerstreak@47: name = L["Columns"], flickerstreak@47: get = function() return self.tmpBarCols or 12 end, flickerstreak@47: set = function(info, val) self.tmpBarCols = val end, flickerstreak@47: width = "half", flickerstreak@47: min = 1, flickerstreak@47: max = 32, flickerstreak@47: step = 1, flickerstreak@47: order = 3, flickerstreak@47: }, flickerstreak@47: sz = { flickerstreak@47: type = "range", flickerstreak@47: name = L["Size"], flickerstreak@47: get = function() return self.tmpBarSize or 36 end, flickerstreak@47: set = function(info, val) self.tmpBarSize = val end, flickerstreak@47: width = "half", flickerstreak@47: min = 10, flickerstreak@47: max = 72, flickerstreak@47: step = 1, flickerstreak@47: order = 4, flickerstreak@47: }, flickerstreak@47: spacing = { flickerstreak@47: type = "range", flickerstreak@47: name = L["Spacing"], flickerstreak@47: get = function() return self.tmpBarSpacing or 3 end, flickerstreak@47: set = function(info, val) self.tmpBarSpacing = val end, flickerstreak@47: width = "half", flickerstreak@47: min = 0, flickerstreak@47: max = 24, flickerstreak@47: step = 1, flickerstreak@47: order = 5, flickerstreak@47: } flickerstreak@47: }, flickerstreak@47: order = 4 flickerstreak@47: }, flickerstreak@47: spacer = { flickerstreak@47: type = "header", flickerstreak@47: name = "", flickerstreak@47: width = "full", flickerstreak@47: order = -2 flickerstreak@47: }, flickerstreak@47: go = { flickerstreak@47: type = "execute", flickerstreak@47: name = L["Create Bar"], flickerstreak@47: func = "CreateBar", flickerstreak@47: order = -1, flickerstreak@47: } flickerstreak@47: } flickerstreak@47: } flickerstreak@47: } flickerstreak@47: } flickerstreak@47: self.layoutOpts = layoutOpts flickerstreak@47: self.barOptMap = { } flickerstreak@47: AceConfigReg:RegisterOptionsTable("ReAction-Layout",layoutOpts) flickerstreak@44: end flickerstreak@44: