view modules/ReAction_ConfigUI/ReAction_ConfigUI.lua @ 51:c964fb84560c

added anchor controls
author Flick <flickerstreak@gmail.com>
date Tue, 22 Apr 2008 21:33:37 +0000
parents c3c64e2def50
children c9df7866ff31
line wrap: on
line source
--[[
  ReAction Configuration UI module

  Hooks into Blizzard Interface Options AddOns panel
--]]

-- local imports
local ReAction = ReAction
local L = ReAction.L
local _G = _G
local AceConfigReg = LibStub("AceConfigRegistry-3.0")
local AceConfigDialog = LibStub("AceConfigDialog-3.0")

-- some constants
local pointTable = {
  CENTER      = L["Center"], 
  LEFT        = L["Left"],
  RIGHT       = L["Right"],
  TOP         = L["Top"],
  BOTTOM      = L["Bottom"],
  TOPLEFT     = L["Top Left"],
  TOPRIGHT    = L["Top Right"],
  BOTTOMLEFT  = L["Bottom Left"],
  BOTTOMRIGHT = L["Bottom Right"],
}

-- module declaration
local moduleID = "ConfigUI"
local module = ReAction:NewModule( moduleID,
  "AceEvent-3.0"
)

-- module methods
function module:OnInitialize()
  self.db = ReAction.db:RegisterNamespace( moduleID,
    { 
      profile = {
        closeOnLaunch = true,
        editorCloseOnLaunch = true,
      }
    }
  )

  ReAction.RegisterCallback(self,"OnOptionsRegistered")
  ReAction.RegisterCallback(self,"OnOptionsRefreshed")
  ReAction.RegisterCallback(self,"OnCreateBar")
  ReAction.RegisterCallback(self,"OnEraseBar")
  ReAction.RegisterCallback(self,"OnRenameBar")
  self:InitializeOptions()
  self:RegisterEvent("PLAYER_REGEN_DISABLED")
end

function module:OnOptionsRegistered(evt, context, module, opts)
  local c = self.configOptions.args[context]
  if c then
    for k, v in pairs(opts) do
      c.args[k] = v
    end
  elseif c == "bar" then
    
  end
end

function module:OnOptionsRefreshed(evt)
  AceConfigReg:NotifyChange("ReAction")
  AceConfigReg:NotifyChange("ReAction-Layout")
end

function module:OnCreateBar(evt, bar)
  local name = bar:GetName()
  -- AceConfig doesn't allow spaces, etc, in arg key names, and they must be
  -- unique strings. So generate a unique key (it can be whatever) for the bar
  local key
  local i = 1
  repeat
    key = ("bar%s"):format(i)
    i = i+1
  until self.layoutOpts.args[key] == nil
  self.barOptMap[name] = key
  self.layoutOpts.args[key] = {
    type = "group",
    name = name,
    childGroups = "tab",
    args = {
      general = {
        type = "group",
        name = L["General"],
        args = {
          name = {
            type = "input",
            name = L["Rename Bar"],
            get  = function() return bar:GetName() end,
            set  = function(info, value) return ReAction:RenameBar(bar, value) end,
            order = 1,
          },
          delete = {
            type = "execute",
            name = L["Delete Bar"],
            desc = function() return bar:GetName() end,
            confirm = true,
            func = function() ReAction:EraseBar(bar) end,
            order = 2
          },
          anchor = {
            type = "group",
            name = L["Anchor"],
            inline = true,
            args = {
              frame = {
                type = "input",
                name = L["Frame"],
                desc = L["The frame that the bar is anchored to"],
                get  = function() local _, f = bar:GetAnchor(); return f end,
                set  = function(info, val) bar:SetAnchor(nil,val) end,
                validate = function(info, name) 
                    if name then
                      local f = ReAction:GetBar(name)
                      if f then
                        return true
                      else
                        f = _G[name]
                        if f and type(f) == "table" and f.IsObjectType and f:IsObjectType("Frame") then
                          return true
                        end
                      end
                    end
                    return false
                  end,
                width = "double",
                order = 1
              },
              point = {
                type = "select",
                name = L["Point"],
                desc = L["Anchor point on the bar frame"],
                style = "dropdown",
                get  = function() return bar:GetAnchor() end,
                set  = function(info, val) bar:SetAnchor(val) end,
                values = pointTable,
                order = 2,
              },
              relativePoint = {
                type = "select",
                name = L["Relative Point"],
                desc = L["Anchor point on the target frame"],
                style = "dropdown",
                get  = function() local p,f,r = bar:GetAnchor(); return r end,
                set  = function(info, val) bar:SetAnchor(nil,nil,val) end,
                values = pointTable,
                order = 3,
              },
              x = {
                type = "input",
                pattern = "\-?%d+",
                name = L["X offset"],
                get = function() local p,f,r,x = bar:GetAnchor(); return x end,
                set = function(info,val) bar:SetAnchor(nil,nil,nil,val) end,
                order = 4
              },
              y = {
                type = "input",
                pattern = "\-?%d+",
                name = L["Y offset"],
                get = function() local p,f,r,x,y = bar:GetAnchor(); return y end,
                set = function(info,val) bar:SetAnchor(nil,nil,nil,nil,val) end,
                order = 5
              },
            },
            order = 3
          },
        },
      },
    },

  }
end

function module:OnEraseBar(evt, name)
  local key = self.barOptMap[name]
  self.barOptMap[name] = nil
  if key then
    self.layoutOpts.args[key] = nil
    self:RefreshLayoutEditor()
  end
end

function module:OnRenameBar(evt, oldname, newname)
  local key = self.barOptMap[oldname]
  self.barOptMap[oldname], self.barOptMap[newname] = nil, key
  if key then
    self.layoutOpts.args[key].name = newname
    self:RefreshLayoutEditor()
  end
end

function module:PLAYER_REGEN_DISABLED()
  if self.editor then
    self.editor:Hide()
  end
end

function module:UserError(msg)
  -- any user errors should be flashed to the UIErrorsFrame
  UIErrorsFrame:AddMessage(msg)
end

function module:OpenConfig()
  InterfaceOptionsFrame_OpenToFrame("ReAction")
end

function module:LaunchLayoutEditor(bar)
  if InCombatLockdown() then
    self:UserError(L["ReAction config mode disabled during combat."])
  else
    if not self.editor then
      -- use a local container to work around AceConfigDialog closing
      -- both the layout editor and the global options when interface options is closed
      local ed = LibStub("AceGUI-3.0"):Create("Frame")
      ed.frame:SetClampedToScreen(true)
      local old_OnUpdate = ed.frame:GetScript("OnUpdate")
      ed.frame:SetScript("OnUpdate", function(dt)
          if old_OnUpdate then
            old_OnUpdate(dt)
          end
          if ed.closePending then
            InterfaceOptionsFrame:Hide()
            ed.closePending = false
          end
          if ed.selfClosePending then
            ed:Hide()
            AceConfigReg:NotifyChange("ReAction")
            ed.selfClosePending = false
          end
        end )
      ed:SetCallback("OnClose", 
        function() 
          ReAction:SetConfigMode(false)
        end )
      self.editor = ed
      AceConfigDialog:SetDefaultSize("ReAction-Layout", 640, 420)
    end

    AceConfigDialog:Open("ReAction-Layout", self.editor)
    ReAction:SetConfigMode(true)
  end
end

function module:RefreshLayoutEditor()
  AceConfigReg:NotifyChange("ReAction-Layout")
  if self.editor and self.editor.frame:IsShown() then
    AceConfigDialog:Open("ReAction-Layout", self.editor)
  end
end

function module:GetBarTypes()
  local opts = self.optBarTypes or { }
  self.optBarTypes = { }
  for k,v in pairs(opts) do
    opts[k] = nil
  end
  for k in pairs(ReAction.defaultBarConfig) do
    opts[k] = k
  end
  return opts
end

function module:CreateBar()
  if self.tmpBarName and self.tmpBarName ~= "" then
    ReAction:CreateBar(self.tmpBarName, self.tmpBarType or ReAction.defaultBarConfigChoice, self.tmpBarRows, self.tmpBarCols, self.tmpBarSize, self.tmpBarSpacing)
    self.tmpBarName = nil
  end
end

function module:InitializeOptions()
  -- general config options
  local opts = {
    type = "group",
    name = "ReAction",
    childGroups = "tab",
    args = {
      _desc = {
        type = "description",
        name = L["Customizable replacement for Blizzard's Action Bars"],
        order = 1,
      },
      _launchLayout = {
        type = "execute",
        handler = self,
        name = L["Layout Editor..."],
        desc = L["Show the ReAction Layout Editor to edit your bars"],
        func = function()
            self:LaunchLayoutEditor()
            -- you can't close a dialog in response to an options click, because the end of the 
            -- handler for all the button events calls lib:Open()
            -- So, schedule a close on the next OnUpdate
            if self.db.profile.closeOnLaunch then
              self.editor.closePending = true
            end
          end,
        order = 2,
      },
      _closeThis = {
        type = "toggle",
        name = L["Close on Launch"],
        desc = L["Close the Interface Options window when launching the ReAction Layout Editor"],
        get  = function() return self.db.profile.closeOnLaunch end,
        set  = function(info, val) self.db.profile.closeOnLaunch = val end,
        order = 3,
      },
      global = {
        type = "group",
        name = L["Global Settings"],
        desc = L["Global configuration settings"],
        args = { },
        order = 3,
      },
      _profile = LibStub("AceDBOptions-3.0"):GetOptionsTable(ReAction.db),
      module = {
        type = "group",
        childGroups = "select",
        name = L["Module Settings"],
        desc = L["Configuration settings for each module"],
        args = { },
        order = -1,
      },
    },
    plugins = { }
  }
  self.configOptions = opts
  opts.args._profile.order = -2
  AceConfigReg:RegisterOptionsTable("ReAction",opts)
  self.frame = AceConfigDialog:AddToBlizOptions("ReAction", "ReAction")
  self.frame.obj:SetCallback("default", 
    function() 
      ReAction.db:ResetProfile()
      module:OpenConfig()
    end )

  -- import options from registered modules
  for c, tbl in pairs(opts.args) do
    for _, m in pairs(ReAction:GetOptions(c)) do
      for k, v in pairs(m) do
        tbl.args[k] = v
      end
    end
  end

  ReAction:RegisterOptions("module",self, {
    configUI = {
      type = "group",
      name = "Config UI",
      desc = "description",
      args = {
        foo = {
          type = "toggle",
          handler = self,
          name = "foo",
          desc = "description",
          get = function() return true end,
          set = function() end,
        }
      }
    },
  })

  -- layout editor options
  local layoutOpts = {
    type = "group",
    name = ("ReAction - %s"):format(L["Layout"]),
    handler = self,
    childGroups = "tree",
    args = {
      desc = {
        type = "description",
        name = L["Use the mouse to arrange and resize the bars on screen. Tooltips on bars indicate additional functionality."],
        order = 1
      },
      launchConfig = {
        type = "execute",
        name = L["Global Config"],
        desc = L["Opens ReAction global configuration settings panel"],
        func = function()
            self:OpenConfig()
            -- you can't close a dialog in response to an options click, because the end of the 
            -- handler for all the button events calls lib:Open()
            -- So, schedule a close on the next OnUpdate
            if self.db.profile.editorCloseOnLaunch then
              self.editor.selfClosePending = true
            end
          end,
        order = 2
      },
      closeThis = {
        type = "toggle",
        name = L["Close on Launch"],
        desc = L["Close the Layout Editor when opening the ReAction global Interface Options"],
        get  = function() return self.db.profile.editorCloseOnLaunch end,
        set  = function(info, val) self.db.profile.editorCloseOnLaunch = val end,
        order = 3,
      },
      new = {
        type = "group",
        name = L["New Bar..."],
        order = 4,
        args = { 
          desc = {
            type = "description",
            name = L["Choose a name, type, and initial grid for your new action bar:"],
            order = 1,
          },
          name = {
            type = "input",
            name = L["Bar Name"],
            desc = L["Enter a name for your new action bar"],
            get  = function() return self.tmpBarName or "" end,
            set  = function(info, val) self.tmpBarName = val end,
            order = 2,
          },
          type = {
            type = "select",
            name = L["Button Type"],
            get  = function() return self.tmpBarType or ReAction.defaultBarConfigChoice or "" end,
            set  = function(info, val) self.tmpBarType = val end,
            values = "GetBarTypes",
            order = 3,
          },
          grid = {
            type = "group",
            name = L["Button Grid"],
            inline = true,
            args = {
              hdr = {
                type = "header",
                name = L["Button Grid"],
                order = 1,
              },
              rows = {
                type = "range",
                name = L["Rows"],
                get  = function() return self.tmpBarRows or 1 end,
                set  = function(info, val) self.tmpBarRows = val end,
                width = "half",
                min = 1,
                max = 32,
                step = 1,
                order = 2,
              },
              cols = {
                type = "range",
                name = L["Columns"],
                get  = function() return self.tmpBarCols or 12 end,
                set  = function(info, val) self.tmpBarCols = val end,
                width = "half",
                min = 1, 
                max = 32,
                step = 1,
                order = 3,
              },
              sz = {
                type = "range",
                name = L["Size"],
                get  = function() return self.tmpBarSize or 36 end,
                set  = function(info, val) self.tmpBarSize = val end,
                width = "half",
                min = 10,
                max = 72,
                step = 1,
                order = 4,
              },
              spacing = {
                type = "range",
                name = L["Spacing"],
                get  = function() return self.tmpBarSpacing or 3 end,
                set  = function(info, val) self.tmpBarSpacing = val end,
                width = "half",
                min = 0,
                max = 24,
                step = 1,
                order = 5,
              }
            },
            order = 4
          },
          spacer = {
            type = "header",
            name = "",
            width = "full",
            order = -2
          },
          go = {
            type = "execute",
            name = L["Create Bar"],
            func = "CreateBar",
            order = -1,
          }
        }
      }
    }
  }
  self.layoutOpts = layoutOpts
  self.barOptMap = { }
  AceConfigReg:RegisterOptionsTable("ReAction-Layout",layoutOpts)
end