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@51: local _G = _G flickerstreak@47: local AceConfigReg = LibStub("AceConfigRegistry-3.0") flickerstreak@47: local AceConfigDialog = LibStub("AceConfigDialog-3.0") flickerstreak@25: flickerstreak@51: -- some constants flickerstreak@60: local configName = "ReAction" flickerstreak@51: 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@60: self:RegisterEvent("PLAYER_REGEN_DISABLED") flickerstreak@60: ReAction.RegisterCallback(self,"OnOptionsRegistered","OnOptionsRefreshed") flickerstreak@33: ReAction.RegisterCallback(self,"OnOptionsRefreshed") flickerstreak@46: self:InitializeOptions() flickerstreak@25: end flickerstreak@25: flickerstreak@33: function module:OnOptionsRefreshed(evt) flickerstreak@60: AceConfigReg:NotifyChange(configName) flickerstreak@60: if self.editor then self.editor:Refresh() 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:OpenConfig() flickerstreak@60: InterfaceOptionsFrame_OpenToFrame(configName) flickerstreak@47: end flickerstreak@47: flickerstreak@46: function module:InitializeOptions() flickerstreak@60: ReAction:RegisterOptions(self, { flickerstreak@58: _launchEditor = { flickerstreak@47: type = "execute", flickerstreak@47: handler = self, flickerstreak@58: name = L["Edit Bars..."], flickerstreak@58: desc = L["Show the ReAction Bar Editor dialogue"], flickerstreak@47: func = function() flickerstreak@58: self:LaunchBarEditor() 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@58: desc = L["Close the Interface Options window when launching the ReAction Bar 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@60: }, true) -- global flickerstreak@60: flickerstreak@60: AceConfigReg:RegisterOptionsTable(configName,ReAction.options) flickerstreak@60: self.frame = AceConfigDialog:AddToBlizOptions(configName, configName) flickerstreak@47: self.frame.obj:SetCallback("default", flickerstreak@47: function() flickerstreak@47: ReAction.db:ResetProfile() flickerstreak@63: ReAction:RefreshOptions() flickerstreak@47: end ) flickerstreak@60: end flickerstreak@46: flickerstreak@60: flickerstreak@60: flickerstreak@60: flickerstreak@60: -- Bar Editor -- flickerstreak@60: local function NewEditor() flickerstreak@60: -- private variables flickerstreak@60: local editorName = "ReAction-Editor" flickerstreak@63: local barOptMap = setmetatable({},{__mode="v"}) flickerstreak@60: local tmp = { } flickerstreak@60: local pointTable = { flickerstreak@60: CENTER = L["Center"], flickerstreak@60: LEFT = L["Left"], flickerstreak@60: RIGHT = L["Right"], flickerstreak@60: TOP = L["Top"], flickerstreak@60: BOTTOM = L["Bottom"], flickerstreak@60: TOPLEFT = L["Top Left"], flickerstreak@60: TOPRIGHT = L["Top Right"], flickerstreak@60: BOTTOMLEFT = L["Bottom Left"], flickerstreak@60: BOTTOMRIGHT = L["Bottom Right"], flickerstreak@60: } flickerstreak@60: flickerstreak@60: flickerstreak@60: -- use a local GUI container to work around AceConfigDialog closing flickerstreak@60: -- both the bar editor and the global options when interface options is closed flickerstreak@60: local editor = LibStub("AceGUI-3.0"):Create("Frame") flickerstreak@60: local frame = editor.frame flickerstreak@60: frame:SetClampedToScreen(true) flickerstreak@60: local old_OnUpdate = frame:GetScript("OnUpdate") flickerstreak@60: frame:SetScript("OnUpdate", function(dt) flickerstreak@60: if old_OnUpdate then flickerstreak@60: old_OnUpdate(dt) flickerstreak@46: end flickerstreak@60: if editor.closePending then flickerstreak@60: InterfaceOptionsFrame:Hide() flickerstreak@60: editor.closePending = false flickerstreak@60: end flickerstreak@60: if editor.selfClosePending then flickerstreak@63: editor:Hide() flickerstreak@60: AceConfigReg:NotifyChange(configName) flickerstreak@60: editor.selfClosePending = false flickerstreak@60: end flickerstreak@60: end ) flickerstreak@60: editor:SetCallback("OnClose", flickerstreak@60: function() flickerstreak@60: ReAction:SetConfigMode(false) flickerstreak@60: end ) flickerstreak@61: AceConfigDialog:SetDefaultSize(editorName, 700, 540) flickerstreak@60: flickerstreak@46: flickerstreak@76: local name = ("ReAction - %s"):format(L["Bar Editor"]) flickerstreak@76: editor:SetTitle(name) flickerstreak@60: local options = { flickerstreak@47: type = "group", flickerstreak@76: name = name, flickerstreak@60: handler = editor, 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@60: module: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@48: -- So, schedule a close on the next OnUpdate flickerstreak@60: if module.db.profile.editorCloseOnLaunch then flickerstreak@60: editor.selfClosePending = true flickerstreak@47: end flickerstreak@47: end, flickerstreak@47: order = 2 flickerstreak@47: }, flickerstreak@48: closeThis = { flickerstreak@47: type = "toggle", flickerstreak@47: name = L["Close on Launch"], flickerstreak@58: desc = L["Close the Bar Editor when opening the ReAction global Interface Options"], flickerstreak@60: get = function() return module.db.profile.editorCloseOnLaunch end, flickerstreak@60: set = function(info, val) module.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@60: get = function() return tmp.barName or "" end, flickerstreak@60: set = function(info, val) tmp.barName = val end, flickerstreak@47: order = 2, flickerstreak@47: }, flickerstreak@47: type = { flickerstreak@47: type = "select", flickerstreak@47: name = L["Button Type"], flickerstreak@63: get = function() return tmp.barType or ReAction:GetDefaultBarType() or "" end, flickerstreak@53: set = function(info, val) flickerstreak@63: local c = ReAction:GetBarTypeConfig(val) flickerstreak@60: tmp.barType = val flickerstreak@60: tmp.barSize = c.defaultButtonSize or tmp.barSize flickerstreak@60: tmp.barRows = c.defaultBarRows or tmp.barRows flickerstreak@60: tmp.barCols = c.defaultBarCols or tmp.barCols flickerstreak@60: tmp.barSpacing = c.defaultBarSpacing or tmp.barSpacing flickerstreak@53: 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@60: get = function() return tmp.barRows or 1 end, flickerstreak@60: set = function(info, val) tmp.barRows = 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@60: get = function() return tmp.barCols or 12 end, flickerstreak@60: set = function(info, val) tmp.barCols = 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@60: get = function() return tmp.barSize or 36 end, flickerstreak@60: set = function(info, val) tmp.barSize = 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@60: get = function() return tmp.barSpacing or 3 end, flickerstreak@60: set = function(info, val) tmp.barSpacing = 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@60: AceConfigReg:RegisterOptionsTable(editorName, options) flickerstreak@60: flickerstreak@76: function editor:Open(bar) flickerstreak@76: if bar then flickerstreak@76: AceConfigDialog:SelectGroup(editorName, barOptMap[bar:GetName()]) flickerstreak@76: end flickerstreak@60: AceConfigDialog:Open(editorName,self) flickerstreak@60: end flickerstreak@60: flickerstreak@60: function editor:Refresh() flickerstreak@60: AceConfigReg:NotifyChange(editorName) flickerstreak@60: if frame:IsShown() then flickerstreak@60: self:Open() -- do I need this? flickerstreak@60: end flickerstreak@60: end flickerstreak@60: flickerstreak@60: function editor:CreateBarTree(bar) flickerstreak@60: local name = bar:GetName() flickerstreak@60: -- AceConfig doesn't allow spaces, etc, in arg key names, and they must be flickerstreak@60: -- unique strings. So generate a unique key (it can be whatever) for the bar flickerstreak@60: local args = options.args flickerstreak@60: local key flickerstreak@60: local i = 1 flickerstreak@60: repeat flickerstreak@60: key = ("bar%s"):format(i) flickerstreak@60: i = i+1 flickerstreak@60: until args[key] == nil flickerstreak@60: barOptMap[name] = key flickerstreak@60: args[key] = { flickerstreak@60: type = "group", flickerstreak@60: name = name, flickerstreak@60: childGroups = "tab", flickerstreak@60: args = { flickerstreak@60: general = { flickerstreak@60: type = "group", flickerstreak@60: name = L["General"], flickerstreak@60: order = 1, flickerstreak@60: args = { flickerstreak@60: name = { flickerstreak@60: type = "input", flickerstreak@60: name = L["Rename Bar"], flickerstreak@60: get = function() return bar:GetName() end, flickerstreak@60: set = function(info, value) return ReAction:RenameBar(bar, value) end, flickerstreak@60: order = 1, flickerstreak@60: }, flickerstreak@60: delete = { flickerstreak@60: type = "execute", flickerstreak@60: name = L["Delete Bar"], flickerstreak@60: desc = function() return bar:GetName() end, flickerstreak@60: confirm = true, flickerstreak@60: func = function() ReAction:EraseBar(bar) end, flickerstreak@60: order = 2 flickerstreak@60: }, flickerstreak@60: anchor = { flickerstreak@60: type = "group", flickerstreak@60: name = L["Anchor"], flickerstreak@60: inline = true, flickerstreak@60: args = { flickerstreak@60: frame = { flickerstreak@60: type = "input", flickerstreak@60: name = L["Frame"], flickerstreak@60: desc = L["The frame that the bar is anchored to"], flickerstreak@60: get = function() local _, f = bar:GetAnchor(); return f end, flickerstreak@60: set = function(info, val) bar:SetAnchor(nil,val) end, flickerstreak@60: validate = function(info, name) flickerstreak@60: if name then flickerstreak@60: local f = ReAction:GetBar(name) flickerstreak@60: if f then flickerstreak@60: return true flickerstreak@60: else flickerstreak@60: f = _G[name] flickerstreak@60: if f and type(f) == "table" and f.IsObjectType and f:IsObjectType("Frame") then flickerstreak@60: return true flickerstreak@60: end flickerstreak@60: end flickerstreak@60: end flickerstreak@60: return false flickerstreak@60: end, flickerstreak@60: width = "double", flickerstreak@60: order = 1 flickerstreak@60: }, flickerstreak@60: point = { flickerstreak@60: type = "select", flickerstreak@60: name = L["Point"], flickerstreak@60: desc = L["Anchor point on the bar frame"], flickerstreak@60: style = "dropdown", flickerstreak@60: get = function() return bar:GetAnchor() end, flickerstreak@60: set = function(info, val) bar:SetAnchor(val) end, flickerstreak@60: values = pointTable, flickerstreak@60: order = 2, flickerstreak@60: }, flickerstreak@60: relativePoint = { flickerstreak@60: type = "select", flickerstreak@60: name = L["Relative Point"], flickerstreak@60: desc = L["Anchor point on the target frame"], flickerstreak@60: style = "dropdown", flickerstreak@60: get = function() local p,f,r = bar:GetAnchor(); return r end, flickerstreak@60: set = function(info, val) bar:SetAnchor(nil,nil,val) end, flickerstreak@60: values = pointTable, flickerstreak@60: order = 3, flickerstreak@60: }, flickerstreak@60: x = { flickerstreak@60: type = "input", flickerstreak@60: pattern = "\-?%d+", flickerstreak@60: name = L["X offset"], flickerstreak@60: get = function() local p,f,r,x = bar:GetAnchor(); return ("%d"):format(x) end, flickerstreak@60: set = function(info,val) bar:SetAnchor(nil,nil,nil,val) end, flickerstreak@60: order = 4 flickerstreak@60: }, flickerstreak@60: y = { flickerstreak@60: type = "input", flickerstreak@60: pattern = "\-?%d+", flickerstreak@60: name = L["Y offset"], flickerstreak@60: get = function() local p,f,r,x,y = bar:GetAnchor(); return ("%d"):format(y) end, flickerstreak@60: set = function(info,val) bar:SetAnchor(nil,nil,nil,nil,val) end, flickerstreak@60: order = 5 flickerstreak@60: }, flickerstreak@60: }, flickerstreak@60: order = 3 flickerstreak@60: }, flickerstreak@60: }, flickerstreak@60: }, flickerstreak@60: } flickerstreak@60: } flickerstreak@60: self:RefreshBarTree(bar) flickerstreak@60: end flickerstreak@60: flickerstreak@60: function editor:RefreshBarTree(bar) flickerstreak@63: local key = barOptMap[bar:GetName()] flickerstreak@63: if key and options.args[key] then flickerstreak@63: options.args[key].plugins = ReAction:GenerateBarOptionsTable(bar) flickerstreak@63: AceConfigReg:NotifyChange(editorName) flickerstreak@60: end flickerstreak@60: end flickerstreak@60: flickerstreak@60: function editor:OnCreateBar(evt, bar) flickerstreak@60: self:CreateBarTree(bar) flickerstreak@60: end flickerstreak@60: flickerstreak@63: function editor:OnDestroyBar(evt, bar, name) flickerstreak@63: local key = barOptMap[name] flickerstreak@63: if key then flickerstreak@63: options.args[key] = nil flickerstreak@63: end flickerstreak@63: self:Refresh() flickerstreak@63: end flickerstreak@63: flickerstreak@60: function editor:OnEraseBar(evt, name) flickerstreak@60: local key = barOptMap[name] flickerstreak@60: barOptMap[name] = nil flickerstreak@60: if key then flickerstreak@60: options.args[key] = nil flickerstreak@60: self:Refresh() flickerstreak@60: end flickerstreak@60: end flickerstreak@60: flickerstreak@60: function editor:OnRenameBar(evt, oldname, newname) flickerstreak@60: local key = barOptMap[oldname] flickerstreak@60: barOptMap[oldname], barOptMap[newname] = nil, key flickerstreak@60: if key then flickerstreak@60: options.args[key].name = newname flickerstreak@60: self:Refresh() flickerstreak@60: end flickerstreak@60: end flickerstreak@60: flickerstreak@63: function editor:OnBarOptionGeneratorRegistered(evt) flickerstreak@63: for name in pairs(barOptMap) do flickerstreak@63: local bar = ReAction:GetBar(name) flickerstreak@63: if bar then flickerstreak@63: self:RefreshBarTree(bar) flickerstreak@63: end flickerstreak@63: end flickerstreak@63: end flickerstreak@63: flickerstreak@60: local _scratch = { } flickerstreak@60: function editor:GetBarTypes() flickerstreak@60: for k,v in pairs(_scratch) do flickerstreak@60: _scratch[k] = nil flickerstreak@60: end flickerstreak@63: return ReAction:GetBarTypeOptions(_scratch) flickerstreak@60: end flickerstreak@60: flickerstreak@60: function editor:CreateBar() flickerstreak@60: if tmp.barName and tmp.barName ~= "" then flickerstreak@63: ReAction:CreateBar(tmp.barName, tmp.barType or ReAction:GetDefaultBarType(), tmp.barRows, tmp.barCols, tmp.barSize, tmp.barSpacing) flickerstreak@60: tmp.barName = nil flickerstreak@60: end flickerstreak@60: end flickerstreak@60: flickerstreak@60: ReAction.RegisterCallback(editor,"OnCreateBar") flickerstreak@63: ReAction.RegisterCallback(editor,"OnDestroyBar") flickerstreak@60: ReAction.RegisterCallback(editor,"OnEraseBar") flickerstreak@60: ReAction.RegisterCallback(editor,"OnRenameBar") flickerstreak@63: ReAction.RegisterCallback(editor,"OnBarOptionGeneratorRegistered") flickerstreak@60: flickerstreak@63: for name, bar in ReAction:IterateBars() do flickerstreak@60: editor:CreateBarTree(bar) flickerstreak@60: end flickerstreak@60: flickerstreak@60: return editor flickerstreak@44: end flickerstreak@44: flickerstreak@60: flickerstreak@60: function module:LaunchBarEditor(bar) flickerstreak@60: if InCombatLockdown() then flickerstreak@61: ReAction:UserError(L["ReAction config mode disabled during combat."]) flickerstreak@60: else flickerstreak@60: if not self.editor then flickerstreak@60: self.editor = NewEditor() flickerstreak@60: end flickerstreak@76: self.editor:Open(bar) flickerstreak@60: ReAction:SetConfigMode(true) flickerstreak@60: end flickerstreak@60: end flickerstreak@60: