annotate Config/Config.lua @ 56:159855c2e9ae

Added tag v1.0-release for changeset d7655c4e6e06
author Nenue
date Fri, 10 Jun 2016 20:53:22 -0400
parents 16465f3fd919
children 07ef62fe201f
rev   line source
Nenue@0 1 --- All the control GUI stuff, including chat command functions
Nenue@0 2 -- @file-author@
Nenue@0 3 -- @project-revision@ @project-hash@
Nenue@0 4 -- @file-revision@ @file-hash@
Nenue@0 5 -- Created: 3/12/2016 12:49 AM
Nenue@49 6 local vn, _G = select(2,...).frame, _G
Nenue@49 7 local M = vn:RegisterModule("Options")
Nenue@49 8 local tostring, tonumber, floor, max, assert = tostring, tonumber, floor, math.max, assert
Nenue@49 9 local unpack, setmetatable, pairs, ipairs, type, wipe = unpack, setmetatable, pairs, ipairs, type, table.wipe
Nenue@0 10 local CreateFrame, IsControlKeyDown = _G.CreateFrame, _G.IsControlKeyDown
Nenue@0 11 local OpacitySliderFrame, ColorPickerFrame = _G.OpacitySliderFrame, _G.ColorPickerFrame
Nenue@49 12 local print = vn.print('Cfgl')
Nenue@0 13 local function round(number, decimals)
Nenue@0 14 if floor(number) == number then
Nenue@0 15 return ('%d'):format(number)
Nenue@0 16 end
Nenue@0 17
Nenue@0 18 return (("%%.%df"):format(decimals)):format(number)
Nenue@0 19 end
Nenue@0 20
Nenue@49 21 --- Set up this way to ensure that all the necessary data exists before things domino into something inscrutable
Nenue@49 22 M.prototypes = {
Nenue@49 23 value = setmetatable({}, {__call = function(self, frame)
Nenue@49 24 assert(frame, 'Expected table (received '..type(frame.OptInfo)..')')
Nenue@49 25 assert(frame.OptRoot, 'Invalid config table for frame '.. frame:GetName().. '')
Nenue@49 26 return self[frame.ValueType](frame.OptTab, frame.OptKey, frame.OptRoot)
Nenue@49 27 end}),
Nenue@49 28 reset = setmetatable({}, {__call = function(self, frame)
Nenue@49 29 assert(frame.GetName, 'Invalid frame reference (received '..type(frame.OptInfo)..')')
Nenue@49 30 assert(frame.OptInfo, 'Expecting a table (received '..type(frame.OptInfo)..')')
Nenue@49 31 return self[frame.OptType](frame, frame.OptInfo)
Nenue@49 32 end})
Nenue@49 33 }
Nenue@49 34 M.config = {}
Nenue@24 35 M.defaults = {
Nenue@24 36 enable = true
Nenue@24 37 }
Nenue@49 38 local GetValue = M.prototypes.value
Nenue@49 39 local ResetField = M.prototypes.reset
Nenue@0 40 --- STATE VARIABLES
Nenue@0 41 local configInit
Nenue@0 42 --- Dummies for addon table upvalues
Nenue@0 43 local configFrames = {} -- actual frame objects
Nenue@49 44 local displays = vn.displays -- anchor objects dummy
Nenue@0 45
Nenue@0 46
Nenue@0 47 --- Returns a value retreival function and the current value stored in config
Nenue@0 48 -- @paramsig value, previousValue = configInteger(key)
Nenue@0 49 -- @param key Name of the config field being represented.
Nenue@0 50 local defaultGroup = 'BuffButton'
Nenue@49 51
Nenue@49 52 GetValue.Integer = function(group, key, parent)
Nenue@49 53 return function(self)
Nenue@0 54 return floor(tonumber(self:GetValue()) + 0.5)
Nenue@49 55 end, (parent[group ..key] or parent[defaultGroup..key])
Nenue@0 56 end
Nenue@49 57 GetValue.Percent = function(group, key, parent)
Nenue@0 58 return function(self, display)
Nenue@0 59 local value = self:GetValue()
Nenue@0 60 if display then
Nenue@0 61 return tostring(floor(value*100+0.5))..' %'
Nenue@0 62 else
Nenue@0 63 return floor((value*100+0.5))/100
Nenue@0 64 end
Nenue@49 65 end, (parent[group ..key] or parent[defaultGroup..key])
Nenue@0 66 end
Nenue@49 67 GetValue.Color = function(group, key, parent)
Nenue@0 68 -- table for config, color value list for text
Nenue@0 69 return function(self, display)
Nenue@0 70 if display then
Nenue@0 71 return "|cFFFF4444" .. round(self.rgba[1], 1) .. "|r, |cFF44FF44" .. round(self.rgba[2], 1) .. "|r, |cFF4488FF" ..
Nenue@0 72 round(self.rgba[3], 1) .. "|r, " .. round(self.rgba[4], 1)
Nenue@0 73 else
Nenue@0 74 return self.rgba
Nenue@0 75 end
Nenue@49 76 end, (parent[group ..key] or parent[defaultGroup..key])
Nenue@0 77 end
Nenue@49 78 GetValue.Check = function(group, key, parent)
Nenue@49 79 return function(self) return self:GetChecked() end, parent[group ..key] or vn.Conf[defaultGroup..key]
Nenue@0 80 end
Nenue@0 81 -- initializes the corresponding type of config field
Nenue@0 82 local frameTypeConv = {
Nenue@0 83 Color = 'Button',
Nenue@0 84 Font = 'Frame',
Nenue@0 85 }
Nenue@49 86
Nenue@49 87
Nenue@49 88 ResetField.Slider = function(frame, optionInfo)
Nenue@0 89 frame:SetMinMaxValues(optionInfo[5], optionInfo[6])
Nenue@0 90 frame:SetValueStep(optionInfo[7])
Nenue@0 91 frame:SetStepsPerPage(optionInfo[8])
Nenue@0 92 print(frame.OptName, '\n {', optionInfo[5], optionInfo[6], optionInfo[7], optionInfo[8], '}')
Nenue@49 93 end
Nenue@49 94 ResetField.CheckButton = function(frame, optionInfo)
Nenue@0 95 frame.SetValue = function(self, ...)
Nenue@0 96 self:SetChecked(...)
Nenue@49 97 self.OptRoot[self.OptName] = self:GetChecked()
Nenue@0 98 print(self.OptTab)
Nenue@49 99 vn.UpdateAll()
Nenue@0 100 end
Nenue@0 101 frame:SetScript("OnClick",function(self)
Nenue@49 102 self.OptRoot[self.OptName] = self:GetChecked()
Nenue@49 103 print(self.OptRoot[self.OptName], self:GetChecked())
Nenue@49 104 vn.UpdateAll()
Nenue@0 105 end)
Nenue@49 106 end
Nenue@49 107 ResetField.Color = function(frame, optionInfo)
Nenue@49 108 frame.rgba = { frame.current:GetVertexColor() }
Nenue@49 109 local colorPickerCallback = function(restore)
Nenue@49 110 local newR, newG, newB, newA
Nenue@49 111 if restore then
Nenue@49 112 newR, newG, newB, newA = unpack(restore)
Nenue@49 113 else
Nenue@49 114 newA, newR, newG, newB = OpacitySliderFrame:GetValue(), ColorPickerFrame:GetColorRGB()
Nenue@49 115 print('not cancel', newA, newR, newB, newG)
Nenue@0 116 end
Nenue@49 117 frame:SetValue({newR, newG, newB, newA})
Nenue@49 118 vn.UpdateBuffs(frame.OptTab)
Nenue@0 119 end
Nenue@49 120 frame:SetScript("OnClick", function(self)
Nenue@49 121 print('got a click')
Nenue@49 122 local r, g, b, a = frame.current:GetVertexColor()
Nenue@49 123 ColorPickerFrame:SetColorRGB(r, g, b)
Nenue@49 124 ColorPickerFrame.hasOpacity = (a ~= nil)
Nenue@49 125 ColorPickerFrame.opacity = a
Nenue@49 126 ColorPickerFrame.previousValues = {r,g,b,a}
Nenue@49 127 ColorPickerFrame.func, ColorPickerFrame.opacityFunc, ColorPickerFrame.cancelFunc =
Nenue@49 128 colorPickerCallback, colorPickerCallback,colorPickerCallback
Nenue@49 129 ColorPickerFrame:Hide()
Nenue@49 130 ColorPickerFrame:Show()
Nenue@49 131 end)
Nenue@49 132 frame.SetValue = function(self, rgba)
Nenue@49 133 print(rgba)
Nenue@49 134 frame.rgba = rgba
Nenue@49 135 self.OptRoot[self.OptName] = rgba
Nenue@49 136 frame.current:SetVertexColor(unpack(rgba))
Nenue@49 137 frame.fieldvalue:SetText(frame.OptValue(frame, true))
Nenue@49 138 end
Nenue@49 139 end
Nenue@49 140
Nenue@0 141 --- configDialog
Nenue@0 142 -- @usage tinsert(configDialog, {prefix, row, [...] })
Nenue@0 143 -- Each top level member defines a group of config value handlers, structured as an iterative table where the
Nenue@0 144 -- first member is a key prefix, the second member is an integer row value, and all following members are treated
Nenue@0 145 -- as a widget resource, defined initially as a complete sub-table, which can be re-used further down by passing
Nenue@0 146 -- the string literal widget suffix.
Nenue@0 147 -- widget table: ... {'suffix', 'description', valueCallback, 'template', [widget parameters]}
Nenue@0 148 -- widget copy: ... 'suffix', ...
Nenue@49 149 M.config.BuffFrame = {
Nenue@0 150 {'BuffButton', 1,
Nenue@0 151
Nenue@49 152 {'Max', 'Max', 'Integer', 'Slider',
Nenue@0 153 1, _G.BUFF_MAX_DISPLAY, 1, 1}, -- valueMin, valueMax, valueStep, stepsPerPage
Nenue@49 154 {'PerRow', 'Per Row', 'Integer', 'Slider',
Nenue@0 155 1, _G.BUFF_MAX_DISPLAY, 1, 1}, -- valueMin, valueMax, valueStep, stepsPerPage,
Nenue@49 156 {'Size', 'Icon Size', 'Integer', 'Slider',
Nenue@0 157 1, 256, 1, 1},
Nenue@49 158 {'Spacing', 'Icon Spacing', 'Integer', 'Slider',
Nenue@0 159 1, 50, 1, 1},
Nenue@49 160 {'DurationSize', 'Duration Text Height', 'Integer', 'Slider',
Nenue@0 161 1, 72, 1, 1},
Nenue@49 162 {'Zoom', 'Icon Zoom', 'Integer', 'Slider',
Nenue@0 163 0, 100, 1, 1},
Nenue@49 164 {'Border', 'Border', 'Integer', 'Slider',
Nenue@0 165 1, 16, 1, 1},
Nenue@49 166 {'Color', 'Default Border', 'Color', 'Color'},
Nenue@49 167 {'RaidColor', 'RaidBuff Border', 'Color', 'Color'},
Nenue@49 168 {'PlayerColor', 'Player Buffs', 'Color', 'Color'},
Nenue@49 169 {'BossColor', 'Encounter Buffs', 'Color', 'Color'},
Nenue@49 170 {'ShowSelfCast', 'Show name for self-casts', 'Check', 'CheckButton'}
Nenue@0 171 },
Nenue@0 172 { 'DebuffButton', 1,
Nenue@49 173 {'Max', 'Max', 'Integer', 'Slider',
Nenue@0 174 1, _G.DEBUFF_MAX_DISPLAY, 1, 1 }
Nenue@0 175 ,
Nenue@49 176 {'PerRow', 'Per Row', 'Integer', 'Slider',
Nenue@0 177 1, _G.DEBUFF_MAX_DISPLAY, 1, 1 },
Nenue@0 178 'Size', 'Spacing', 'DurationSize', 'Zoom', 'Border',
Nenue@0 179 'Color', 'RaidColor', 'PlayerColor', 'BossColor',
Nenue@0 180 },
Nenue@0 181 { 'TempEnchant', 1,
Nenue@49 182 {'Max', 'Max', 'Integer', 'Slider',
Nenue@0 183 1, _G.NUM_TEMP_ENCHANT_FRAMES, 1, 1 },
Nenue@49 184 {'PerRow', 'Per Row', 'Integer', 'Slider',
Nenue@0 185 1, _G.NUM_TEMP_ENCHANT_FRAMES, 1, 1},
Nenue@0 186 'Size', 'Spacing', 'DurationSize', 'Zoom', 'Border',
Nenue@0 187 'Color', 'RaidColor', 'PlayerColor', 'BossColor',
Nenue@0 188 },
Nenue@0 189 { 'ConsolidatedBuff', 2,
Nenue@49 190 {'Position', 'Slot Position', 'Integer', 'Slider',
Nenue@0 191 1, _G.BUFF_MAX_DISPLAY, 1, 1 }
Nenue@0 192
Nenue@0 193 },
Nenue@0 194 { 'ConsolidatedBuff', 2,
Nenue@0 195 'Size'
Nenue@0 196 },
Nenue@0 197 { 'Raid', 3,
Nenue@49 198 {'ShowMissing', 'Verbose missing raid buffs', 'Check', 'CheckButton'}
Nenue@0 199 }
Nenue@0 200 }
Nenue@0 201
Nenue@0 202
Nenue@0 203
Nenue@0 204
Nenue@0 205 local configFrame
Nenue@0 206 local optionTemplates = {}
Nenue@0 207 local configPadding, configSpacing = 3, 3
Nenue@0 208
Nenue@0 209 --- Walks the structure table to generate a pretty config panel
Nenue@0 210 local InitConfig = function()
Nenue@0 211 configInit = true
Nenue@49 212 local configWidth = vn:GetWidth()
Nenue@0 213 local optionWidth = (configWidth - configPadding) / 3 - configSpacing
Nenue@0 214 local configHeight = 0
Nenue@0 215 local bottom_extent = 0
Nenue@0 216 local clusterHeight = 0
Nenue@0 217 local clusterOffset = 0
Nenue@0 218 local lastCluster
Nenue@0 219 local cluster = 1
Nenue@0 220 local col = 0
Nenue@49 221 for moduleName, moduleOpts in pairs(M.config) do
Nenue@49 222 for t, taboptions in ipairs(moduleOpts) do
Nenue@0 223 local group = taboptions[1]
Nenue@0 224 cluster = taboptions[2]
Nenue@0 225 col = col + 1
Nenue@0 226
Nenue@0 227
Nenue@0 228 if not configFrames[t] then
Nenue@0 229 configFrames[t] = {}
Nenue@0 230 end
Nenue@0 231
Nenue@0 232
Nenue@0 233 if cluster ~= lastCluster then
Nenue@0 234 configHeight = configHeight + clusterHeight
Nenue@0 235 print('|cFFFF8800## new cluster|r, advancing offset from', clusterOffset, 'to', clusterOffset + clusterHeight)
Nenue@0 236 clusterOffset = clusterOffset + clusterHeight
Nenue@0 237 col = 1
Nenue@0 238 clusterHeight = 0
Nenue@0 239 lastCluster = cluster
Nenue@0 240 end
Nenue@0 241
Nenue@0 242 print('processing tab', group)
Nenue@0 243 local row = 0
Nenue@0 244 for i = 3, #taboptions do
Nenue@0 245 row = row + 1
Nenue@0 246 local optionInfo = taboptions[i]
Nenue@0 247 if type(optionInfo) == 'string' then
Nenue@0 248 optionInfo = optionTemplates[optionInfo]
Nenue@0 249 end
Nenue@49 250 local key, fieldname, valueType, configType = unpack(optionInfo)
Nenue@49 251 assert(GetValue[valueType], 'Invalid valueType \''..tostring(valueType)..'\' ('..type(valueType)..')')
Nenue@49 252 assert(ResetField[configType], 'Invalid fieldType \''..tostring(configType)..'\' ('..type(configType)..')')
Nenue@0 253
Nenue@0 254 if not optionTemplates[key] then
Nenue@0 255 optionTemplates[key] = optionInfo
Nenue@0 256 end
Nenue@0 257
Nenue@0 258 local fullkey = group .. key
Nenue@0 259 print(fullkey, fieldname)
Nenue@0 260
Nenue@0 261 if not configFrames[t][row] then
Nenue@0 262 print('building frame', t, group, row)
Nenue@0 263 local frameTemplate = 'VeneerConfig'..configType
Nenue@0 264 local frameType = frameTypeConv[configType] or configType
Nenue@49 265 configFrames[t][row] = CreateFrame(frameType, 'Vn_'.. moduleName ..'_'.. fullkey, vn, frameTemplate)
Nenue@0 266 local f = configFrames[t][row]
Nenue@49 267 f.ValueType = valueType
Nenue@49 268 f.OptType = configType
Nenue@49 269 f.FrameType = frameType
Nenue@0 270 f.OptKey = key
Nenue@49 271 f.OptRoot = vn.Conf[moduleName]
Nenue@0 272 f.OptTab = group
Nenue@0 273 f.OptName = fullkey
Nenue@49 274 f.OptInfo = optionInfo
Nenue@49 275 local valueFunc, initialValue = GetValue(f)
Nenue@49 276 print(' value getter', '|cFFFFFF00'..moduleName..'|r.|cFF00FFFF'.. tostring(fullkey),'|r->', valueFunc,initialValue)
Nenue@49 277 ResetField(f)
Nenue@0 278 f.OptValue = valueFunc
Nenue@0 279
Nenue@0 280 --- Enclosing these to
Nenue@0 281 -- a) make the panel easy to bring up externally
Nenue@0 282 -- b) limit gameplay risk from config frame errors
Nenue@0 283 -- c) milk the iterator scope for all its worth
Nenue@0 284 f.OnChange = function(self)
Nenue@0 285
Nenue@0 286 -- holding control; mirror this setting in other categories
Nenue@0 287 if IsControlKeyDown() and not (configInit) then
Nenue@0 288 configInit = true
Nenue@0 289 for optTab, opts in pairs(configFrames) do
Nenue@0 290 for _, opt in ipairs(opts) do
Nenue@0 291 if opt.OptKey == key then
Nenue@0 292 if optTab ~= group then
Nenue@0 293 print('mapping to', optTab, opt.OptKey)
Nenue@0 294 opt:SetValue(self:GetValue())
Nenue@0 295 end
Nenue@0 296
Nenue@0 297 end
Nenue@0 298 end
Nenue@0 299 end
Nenue@0 300 configInit = nil
Nenue@0 301 end
Nenue@0 302 local newValue = valueFunc(self)
Nenue@49 303 if newValue ~= self.OptRoot[fullkey] then
Nenue@0 304 print(newValue, fullkey)
Nenue@0 305 f.fieldvalue:SetText(valueFunc(self, true))
Nenue@49 306 self.OptRoot[fullkey] = valueFunc(self)
Nenue@0 307 -- prepare to update
Nenue@49 308 vn[moduleName]:OnUpdate()
Nenue@49 309 vn.UpdateConfigLayers()
Nenue@0 310 end
Nenue@0 311
Nenue@0 312 end
Nenue@0 313
Nenue@0 314 f:SetValue(initialValue)
Nenue@0 315 local yBuffer = configPadding
Nenue@0 316 if f.fieldname then
Nenue@0 317 f.fieldname:SetText(fieldname)
Nenue@0 318 yBuffer = yBuffer + f.fieldname:GetHeight()
Nenue@0 319 end
Nenue@0 320 if f.fieldvalue then
Nenue@0 321 f.fieldvalue:SetText(f:OptValue(true))
Nenue@0 322 end
Nenue@0 323
Nenue@0 324 local point, relative, x, y = 'TOPLEFT', 'BOTTOMLEFT', 0, -3
Nenue@0 325
Nenue@0 326 local base
Nenue@0 327 if (row == 1) then
Nenue@0 328 bottom_extent = 0
Nenue@49 329 base = vn.header
Nenue@0 330 x = (col-1) * (optionWidth+configSpacing)
Nenue@0 331 y = -configPadding
Nenue@0 332 else
Nenue@0 333 base = configFrames[t][row-1]
Nenue@0 334 end
Nenue@0 335
Nenue@0 336 print('|cFFFF0088'..cluster..'|r |cFF00FF00'.. row..'|r', col, base:GetName(), x, y - clusterOffset)
Nenue@0 337
Nenue@0 338 if frameType ~= 'CheckButton' then
Nenue@0 339 f:SetWidth(optionWidth)
Nenue@0 340 end
Nenue@0 341
Nenue@0 342 f:SetPoint(point, base, relative, x, y-yBuffer-clusterOffset)
Nenue@0 343 --print('creating', frameType, fieldname)
Nenue@0 344 f:Show()
Nenue@0 345
Nenue@0 346 bottom_extent = bottom_extent + f:GetHeight() + yBuffer + configSpacing
Nenue@0 347
Nenue@0 348
Nenue@0 349
Nenue@0 350 clusterHeight = max(clusterHeight, bottom_extent)
Nenue@0 351 --print('y', floor(yBuffer+0.5), 'f:H', floor(f:GetHeight()+0.5), 'hTally', floor(bottom_extent+0.5), 'hMax', floor(configHeight+0.5))
Nenue@0 352 end
Nenue@0 353 end
Nenue@49 354 end
Nenue@0 355 end
Nenue@0 356
Nenue@49 357
Nenue@0 358 -- grab the last cluster
Nenue@0 359 if lastCluster == cluster then
Nenue@0 360 print('|cFF00FF00##scooping up last cluster info')
Nenue@0 361 configHeight = configHeight + clusterHeight
Nenue@0 362 end
Nenue@0 363
Nenue@49 364 if not vn.configFramesCreated then
Nenue@49 365 vn.configFramesCreated = true
Nenue@49 366 vn:SetHeight(vn.header:GetStringHeight() + configSpacing*3 + configHeight)
Nenue@0 367 end
Nenue@0 368 if configInit then configInit = nil end
Nenue@0 369 end
Nenue@0 370
Nenue@0 371 M.Command = function(enable, editbox)
Nenue@49 372 displays = vn.displays
Nenue@0 373 if type(enable) == 'boolean' then
Nenue@49 374 vn.Conf.ConfigMode = enable
Nenue@0 375 else
Nenue@49 376 vn.Conf.ConfigMode = (vn.Conf.ConfigMode == false) and true or false
Nenue@0 377 end
Nenue@0 378
Nenue@49 379 print('/BUFF', vn.Conf.ConfigMode, type(vn.Conf.ConfigMode))
Nenue@49 380 if vn.Conf.ConfigMode then
Nenue@49 381 if not vn.configFramesCreated then
Nenue@0 382 InitConfig()
Nenue@0 383 end
Nenue@0 384 print('Veneer config')
Nenue@49 385 vn:Show()
Nenue@0 386 else
Nenue@49 387 vn:Hide()
Nenue@0 388 end
Nenue@49 389 vn.UpdateAll()
Nenue@49 390 vn.UpdateConfigLayers()
Nenue@0 391 end
Nenue@0 392
Nenue@49 393 vn.Close = function ()
Nenue@0 394 M.Command()
Nenue@0 395 end
Nenue@0 396
Nenue@49 397 vn.ToggleGuides = function(_, self)
Nenue@49 398 vn.Conf.GuidesMode = (not vn.Conf.GuidesMode)
Nenue@49 399 if vn.Conf.GuidesMode then
Nenue@0 400 self:GetNormalTexture():SetTexture(0.94, 0.21, 0.21, 1)
Nenue@0 401 else
Nenue@0 402 self:GetNormalTexture():SetTexture(0, 0, 0, 1)
Nenue@0 403 end
Nenue@0 404
Nenue@49 405 vn.UpdateConfigLayers()
Nenue@0 406 end
Nenue@0 407
Nenue@0 408 M.OnEnable = function()
Nenue@49 409 print('|cFFFF0088config module', vn.Conf.ConfigMode)
Nenue@49 410 M.Command(vn.Conf.ConfigMode)
Nenue@0 411 end
Nenue@0 412
Nenue@0 413 M.OnInitialize = function()
Nenue@49 414 DEFAULT_CHAT_FRAME:AddMessage("|cFF22D822Veneer|r confogulator loaded. Type |cFF00FFFF/vn|r to begin.")
Nenue@49 415 SLASH_VENEER1, SLASH_VENEER2 = "/veneer", "/vn"
Nenue@49 416 SlashCmdList.VENEER = M.Command
Nenue@0 417
Nenue@0 418 end