view Config.lua @ 13:9455693fc290

Init - recall XML display state on reload ObjectiveFrame - quest coloring by relative level - quest coloring by daily/weekly/complete status - remember starting scroll value between reload - limit anchor points to edges for regions affected by style attributes ObjectiveInfo - AutoQuest outline definitions - Pull Quest title and tag data in addition to WatchInfo ObjectiveStyle - ensure consistent style table - hardcode certain attributes for sanity XML - ensure consistent naming conventions for heading and content elements - ensure hardcore anchors are based on edges - expansion of file structure to deal with complexities of dynamic widgets and style caching ObjectiveUI - determine primary style by block handler when restoring original style - moved framescript to 'ObjectiveWidgets' lua
author Nenue
date Sat, 02 Apr 2016 17:46:52 -0400
parents 3dbcad2b387d
children 66b927b46776
line wrap: on
line source
--- All the control GUI stuff, including chat command functions
-- @file-author@
-- @project-revision@ @project-hash@
-- @file-revision@ @file-hash@
-- Created: 3/12/2016 12:49 AM
local B, _G = select(2,...).frame, _G
local M = B:RegisterModule("Options")
local tostring, tonumber, floor, format = tostring, tonumber, floor, string.format
local unpack, select, pairs, ipairs, type, wipe = unpack, select, pairs, ipairs, type, table.wipe
local CreateFrame, IsControlKeyDown = _G.CreateFrame, _G.IsControlKeyDown
local max = math.max
local OpacitySliderFrame, ColorPickerFrame = _G.OpacitySliderFrame, _G.ColorPickerFrame
local print = B.print('Cfgl')
local function round(number, decimals)
  if floor(number) == number then
    return ('%d'):format(number)
  end

  return (("%%.%df"):format(decimals)):format(number)
end

--- STATE VARIABLES
local configInit
--- Dummies for addon table upvalues
local configFrames = {} -- actual frame objects
local displays = B.displays     -- anchor objects dummy


--- Returns a value retreival function and the current value stored in config
-- @paramsig value, previousValue = configInteger(key)
-- @param key Name of the config field being represented.
local defaultGroup = 'BuffButton'
local configInteger = function(group, key)
  return function(self ,display)
    return floor(tonumber(self:GetValue()) + 0.5)
  end, (B.Conf[group ..key] or B.Conf[defaultGroup..key])
end
local configPercent = function(group, key)
  return function(self, display)
    local value = self:GetValue()
    if display then
      return tostring(floor(value*100+0.5))..' %'
    else
      return floor((value*100+0.5))/100
    end
  end, (B.Conf[group ..key] or B.Conf[defaultGroup..key])
end
local configColor = function(group, key)
  -- table for config, color value list for text
  return function(self, display)
    if display then
      return "|cFFFF4444" .. round(self.rgba[1], 1) .. "|r, |cFF44FF44" .. round(self.rgba[2], 1) .. "|r, |cFF4488FF" ..
          round(self.rgba[3], 1) .. "|r, " .. round(self.rgba[4], 1)
    else
      return self.rgba
    end
  end, (B.Conf[group ..key] or B.Conf[defaultGroup..key])
end
local configCheck = function(group, key)
  return function(self) return self:GetChecked() end, B.Conf[group ..key] or B.Conf[defaultGroup..key]
end
-- initializes the corresponding type of config field
local frameTypeConv = {
  Color = 'Button',
  Font = 'Frame',
}
local configTypeParams = {
  Slider = function(frame, optionInfo)
    frame:SetMinMaxValues(optionInfo[5], optionInfo[6])
    frame:SetValueStep(optionInfo[7])
    frame:SetStepsPerPage(optionInfo[8])
    print(frame.OptName, '\n  {', optionInfo[5], optionInfo[6], optionInfo[7], optionInfo[8], '}')
  end,
  CheckButton = function(frame, optionInfo)
    frame.SetValue = function(self, ...)
      self:SetChecked(...)
      B.Conf[self.OptName] = self:GetChecked()
      print(self.OptTab)
      B.UpdateAll()
    end
    frame:SetScript("OnClick",function(self)
      B.Conf[self.OptName] = self:GetChecked()
      print(B.Conf[self.OptName], self:GetChecked())
      B.UpdateAll()
    end)
  end,
  Color = function(frame, optionInfo)
    frame.rgba = { frame.current:GetVertexColor() }
    local colorPickerCallback = function(restore)
      local newR, newG, newB, newA
      if restore then
        newR, newG, newB, newA = unpack(restore)
      else
        newA, newR, newG, newB = OpacitySliderFrame:GetValue(), ColorPickerFrame:GetColorRGB()
        print('not cancel', newA, newR, newB, newG)
      end
      frame:SetValue({newR, newG, newB, newA})
      B.UpdateBuffs(frame.OptTab)
    end
    frame:SetScript("OnClick", function(self)
      print('got a click')
      local r, g, b, a = frame.current:GetVertexColor()
      ColorPickerFrame:SetColorRGB(r, g, b)
      ColorPickerFrame.hasOpacity =  (a ~= nil)
      ColorPickerFrame.opacity = a
      ColorPickerFrame.previousValues = {r,g,b,a}
      ColorPickerFrame.func, ColorPickerFrame.opacityFunc, ColorPickerFrame.cancelFunc =
      colorPickerCallback, colorPickerCallback,colorPickerCallback
      ColorPickerFrame:Hide()
      ColorPickerFrame:Show()
    end)
    frame.SetValue = function(self, rgba)
      print(rgba)
      frame.rgba = rgba
      B.Conf[self.OptName] = rgba
      frame.current:SetVertexColor(unpack(rgba))
      frame.fieldvalue:SetText(frame.OptValue(frame, true))
    end
  end
}
--- configDialog
-- @usage tinsert(configDialog, {prefix, row, [...] })
-- Each top level member defines a group of config value handlers, structured as an iterative table where the
-- first member is a key prefix, the second member is an integer row value, and all following members are treated
-- as a widget resource, defined initially as a complete sub-table, which can be re-used further down by passing
-- the string literal widget suffix.
-- widget table: ... {'suffix', 'description', valueCallback, 'template', [widget parameters]}
-- widget copy: ... 'suffix', ...
local configDialog = {
  {'BuffButton', 1,

    {'Max', 'Max', configInteger, 'Slider',
      1, _G.BUFF_MAX_DISPLAY, 1, 1}, -- valueMin, valueMax, valueStep, stepsPerPage
    {'PerRow', 'Per Row', configInteger, 'Slider',
      1, _G.BUFF_MAX_DISPLAY, 1, 1}, -- valueMin, valueMax, valueStep, stepsPerPage,
    {'Size', 'Icon Size', configInteger, 'Slider',
      1, 256, 1, 1},
    {'Spacing', 'Icon Spacing', configInteger, 'Slider',
      1, 50, 1, 1},
    {'DurationSize', 'Duration Text Height', configInteger, 'Slider',
      1, 72, 1, 1},
    {'Zoom', 'Icon Zoom', configInteger, 'Slider',
      0, 100, 1, 1},
    {'Border', 'Border', configInteger, 'Slider',
      1, 16, 1, 1},
    {'Color', 'Default Border', configColor, 'Color'},
    {'RaidColor', 'RaidBuff Border', configColor, 'Color'},
    {'PlayerColor', 'Player Buffs', configColor, 'Color'},
    {'BossColor', 'Encounter Buffs', configColor, 'Color'},
    {'ShowSelfCast', 'Show name for self-casts', configCheck, 'CheckButton'}
  },
  { 'DebuffButton', 1,
    {'Max', 'Max', configInteger, 'Slider',
      1, _G.DEBUFF_MAX_DISPLAY, 1, 1 }
    ,
    {'PerRow', 'Per Row', configInteger, 'Slider',
      1, _G.DEBUFF_MAX_DISPLAY, 1, 1 },
    'Size', 'Spacing', 'DurationSize', 'Zoom', 'Border',
    'Color', 'RaidColor', 'PlayerColor', 'BossColor',
  },
  { 'TempEnchant', 1,
    {'Max', 'Max', configInteger, 'Slider',
      1, _G.NUM_TEMP_ENCHANT_FRAMES, 1, 1 },
    {'PerRow', 'Per Row', configInteger, 'Slider',
      1, _G.NUM_TEMP_ENCHANT_FRAMES, 1, 1},
    'Size', 'Spacing', 'DurationSize', 'Zoom', 'Border',
    'Color', 'RaidColor', 'PlayerColor', 'BossColor',
  },
  { 'ConsolidatedBuff', 2,
    {'Position', 'Slot Position', configInteger, 'Slider',
    1, _G.BUFF_MAX_DISPLAY, 1, 1 }

  },
  { 'ConsolidatedBuff', 2,
    'Size'
  },
  { 'Raid', 3,
    {'ShowMissing', 'Verbose missing raid buffs', configCheck, 'CheckButton'}
  }
}




local configFrame
local optionTemplates = {}
local configPadding, configSpacing = 3, 3

--- Walks the structure table to generate a pretty config panel
local InitConfig = function()
  configInit = true
  local configWidth = B:GetWidth()
  local optionWidth = (configWidth - configPadding) / 3 - configSpacing
  local configHeight = 0
  local bottom_extent = 0
  local clusterHeight = 0
  local clusterOffset = 0
  local lastCluster
  local cluster = 1
  local col = 0
  for t, taboptions in ipairs(configDialog) do
    local group = taboptions[1]
    cluster = taboptions[2]
    col = col + 1


    if not configFrames[t] then
      configFrames[t] = {}
    end


    if cluster ~= lastCluster then
      configHeight = configHeight + clusterHeight
      print('|cFFFF8800## new cluster|r, advancing offset from', clusterOffset, 'to', clusterOffset + clusterHeight)
      clusterOffset = clusterOffset + clusterHeight
      col = 1
      clusterHeight = 0
      lastCluster = cluster
    end

    print('processing tab', group)
    local row = 0
    for i = 3, #taboptions do
      row = row + 1
      local optionInfo = taboptions[i]
      if type(optionInfo) == 'string' then
        optionInfo = optionTemplates[optionInfo]
      end
      local key, fieldname, valueFuncGenerator, configType = unpack(optionInfo)

      if not optionTemplates[key] then
        optionTemplates[key] = optionInfo
      end

      local fullkey = group .. key
      print(fullkey, fieldname)

      if not configFrames[t][row] then
        print('building frame', t, group, row)
        local frameTemplate =  'VeneerConfig'..configType
        local frameType = frameTypeConv[configType] or configType
        configFrames[t][row] = CreateFrame(frameType, fullkey, B, frameTemplate)
        local f = configFrames[t][row]
        f.OptKey = key
        f.OptTab = group
        f.OptName = fullkey
        local valueFunc, initialValue = valueFuncGenerator(group, key)
        print('  value getter', fullkey,'->', valueFunc,initialValue)
        configTypeParams[configType](f, optionInfo)
        f.OptValue = valueFunc

        --- Enclosing these to
        -- a) make the panel easy to bring up externally
        -- b) limit gameplay risk from config frame errors
        -- c) milk the iterator scope for all its worth
        f.OnChange = function(self)

          -- holding control; mirror this setting in other categories
          if IsControlKeyDown() and not (configInit) then
            configInit = true
            for optTab, opts in pairs(configFrames) do
              for _, opt in ipairs(opts) do
                if opt.OptKey == key then
                  if optTab ~= group then
                    print('mapping to', optTab, opt.OptKey)
                    opt:SetValue(self:GetValue())
                  end

                end
              end
            end
            configInit = nil
          end
          local newValue = valueFunc(self)
          if newValue ~= B.Conf[fullkey] then
            print(newValue, fullkey)
            f.fieldvalue:SetText(valueFunc(self, true))
            B.Conf[fullkey] = valueFunc(self)
            -- prepare to update
            wipe(B.drawn[f.OptTab])
            B.UpdateBuffs(self.OptTab)
            B.UpdateConfigLayers()
          end

        end

        f:SetValue(initialValue)
        local yBuffer = configPadding
        if f.fieldname then
          f.fieldname:SetText(fieldname)
          yBuffer = yBuffer + f.fieldname:GetHeight()
        end
        if f.fieldvalue then
          f.fieldvalue:SetText(f:OptValue(true))
        end

        local point, relative, x, y = 'TOPLEFT', 'BOTTOMLEFT', 0, -3

        local base
        if (row == 1) then
          bottom_extent = 0
          base = B.header
          x = (col-1) * (optionWidth+configSpacing)
          y = -configPadding
        else
          base = configFrames[t][row-1]
        end

        print('|cFFFF0088'..cluster..'|r |cFF00FF00'.. row..'|r', col, base:GetName(), x, y - clusterOffset)

        if frameType ~= 'CheckButton' then
          f:SetWidth(optionWidth)
        end

        f:SetPoint(point, base, relative, x, y-yBuffer-clusterOffset)
        --print('creating', frameType, fieldname)
        f:Show()

        bottom_extent = bottom_extent + f:GetHeight() + yBuffer + configSpacing



        clusterHeight = max(clusterHeight, bottom_extent)
        --print('y', floor(yBuffer+0.5), 'f:H', floor(f:GetHeight()+0.5), 'hTally', floor(bottom_extent+0.5), 'hMax', floor(configHeight+0.5))
      end
    end
  end

  -- grab the last cluster
  if lastCluster == cluster then
    print('|cFF00FF00##scooping up last cluster info')
    configHeight = configHeight + clusterHeight
  end

  if not B.configFramesCreated then
    B.configFramesCreated = true
    B:SetHeight(B.header:GetStringHeight() + configSpacing*3 + configHeight)
  end
  if configInit then configInit = nil end
end

M.Command = function(enable, editbox)
  displays = B.displays
  if type(enable) == 'boolean' then
    B.Conf.ConfigMode = enable
  else
    B.Conf.ConfigMode = (B.Conf.ConfigMode == false) and true or false
  end

  print('/BUFF', B.Conf.ConfigMode, type(B.Conf.ConfigMode))
  if B.Conf.ConfigMode  then
    if not B.configFramesCreated then
      InitConfig()
    end
    print('Veneer config')
    B:Show()
  else
    B:Hide()
  end
  B.UpdateAll()
  B.UpdateConfigLayers()
end

B.Close = function ()
  M.Command()
end

B.ToggleGuides = function(_, self)
  B.Conf.GuidesMode = (not B.Conf.GuidesMode)
  if B.Conf.GuidesMode then
    self:GetNormalTexture():SetTexture(0.94, 0.21, 0.21, 1)
  else
    self:GetNormalTexture():SetTexture(0, 0, 0, 1)
  end

  B.UpdateConfigLayers()
end

M.OnEnable = function()
  M.Command(B.Conf.ConfigMode)
end

M.OnInitialize = function()
  DEFAULT_CHAT_FRAME:AddMessage("|cFF22D822Veneer|r")
  SLASH_BUFFALO1, SLASH_BUFFALO2 = "/buffalo", "/buff"
  SlashCmdList.BUFFALO = M.Command

end