Nenue@0: --- All the control GUI stuff, including chat command functions Nenue@0: -- @file-author@ Nenue@0: -- @project-revision@ @project-hash@ Nenue@0: -- @file-revision@ @file-hash@ Nenue@0: -- Created: 3/12/2016 12:49 AM Nenue@0: local B, _G = select(2,...).frame, _G Nenue@0: local M = B:RegisterModule("Options") Nenue@0: local tostring, tonumber, floor, format = tostring, tonumber, floor, string.format Nenue@0: local unpack, select, pairs, ipairs, type, wipe = unpack, select, pairs, ipairs, type, table.wipe Nenue@0: local CreateFrame, IsControlKeyDown = _G.CreateFrame, _G.IsControlKeyDown Nenue@0: local max = math.max Nenue@0: local OpacitySliderFrame, ColorPickerFrame = _G.OpacitySliderFrame, _G.ColorPickerFrame Nenue@0: local print = B.print('Cfgl') Nenue@0: local function round(number, decimals) Nenue@0: if floor(number) == number then Nenue@0: return ('%d'):format(number) Nenue@0: end Nenue@0: Nenue@0: return (("%%.%df"):format(decimals)):format(number) Nenue@0: end Nenue@0: Nenue@24: M.defaults = { Nenue@24: enable = true Nenue@24: } Nenue@24: Nenue@0: --- STATE VARIABLES Nenue@0: local configInit Nenue@0: --- Dummies for addon table upvalues Nenue@0: local configFrames = {} -- actual frame objects Nenue@0: local displays = B.displays -- anchor objects dummy Nenue@0: Nenue@0: Nenue@0: --- Returns a value retreival function and the current value stored in config Nenue@0: -- @paramsig value, previousValue = configInteger(key) Nenue@0: -- @param key Name of the config field being represented. Nenue@0: local defaultGroup = 'BuffButton' Nenue@0: local configInteger = function(group, key) Nenue@0: return function(self ,display) Nenue@0: return floor(tonumber(self:GetValue()) + 0.5) Nenue@0: end, (B.Conf[group ..key] or B.Conf[defaultGroup..key]) Nenue@0: end Nenue@0: local configPercent = function(group, key) Nenue@0: return function(self, display) Nenue@0: local value = self:GetValue() Nenue@0: if display then Nenue@0: return tostring(floor(value*100+0.5))..' %' Nenue@0: else Nenue@0: return floor((value*100+0.5))/100 Nenue@0: end Nenue@0: end, (B.Conf[group ..key] or B.Conf[defaultGroup..key]) Nenue@0: end Nenue@0: local configColor = function(group, key) Nenue@0: -- table for config, color value list for text Nenue@0: return function(self, display) Nenue@0: if display then Nenue@0: return "|cFFFF4444" .. round(self.rgba[1], 1) .. "|r, |cFF44FF44" .. round(self.rgba[2], 1) .. "|r, |cFF4488FF" .. Nenue@0: round(self.rgba[3], 1) .. "|r, " .. round(self.rgba[4], 1) Nenue@0: else Nenue@0: return self.rgba Nenue@0: end Nenue@0: end, (B.Conf[group ..key] or B.Conf[defaultGroup..key]) Nenue@0: end Nenue@0: local configCheck = function(group, key) Nenue@0: return function(self) return self:GetChecked() end, B.Conf[group ..key] or B.Conf[defaultGroup..key] Nenue@0: end Nenue@0: -- initializes the corresponding type of config field Nenue@0: local frameTypeConv = { Nenue@0: Color = 'Button', Nenue@0: Font = 'Frame', Nenue@0: } Nenue@0: local configTypeParams = { Nenue@0: Slider = function(frame, optionInfo) Nenue@0: frame:SetMinMaxValues(optionInfo[5], optionInfo[6]) Nenue@0: frame:SetValueStep(optionInfo[7]) Nenue@0: frame:SetStepsPerPage(optionInfo[8]) Nenue@0: print(frame.OptName, '\n {', optionInfo[5], optionInfo[6], optionInfo[7], optionInfo[8], '}') Nenue@0: end, Nenue@0: CheckButton = function(frame, optionInfo) Nenue@0: frame.SetValue = function(self, ...) Nenue@0: self:SetChecked(...) Nenue@0: B.Conf[self.OptName] = self:GetChecked() Nenue@0: print(self.OptTab) Nenue@0: B.UpdateAll() Nenue@0: end Nenue@0: frame:SetScript("OnClick",function(self) Nenue@0: B.Conf[self.OptName] = self:GetChecked() Nenue@0: print(B.Conf[self.OptName], self:GetChecked()) Nenue@0: B.UpdateAll() Nenue@0: end) Nenue@0: end, Nenue@0: Color = function(frame, optionInfo) Nenue@0: frame.rgba = { frame.current:GetVertexColor() } Nenue@0: local colorPickerCallback = function(restore) Nenue@0: local newR, newG, newB, newA Nenue@0: if restore then Nenue@0: newR, newG, newB, newA = unpack(restore) Nenue@0: else Nenue@0: newA, newR, newG, newB = OpacitySliderFrame:GetValue(), ColorPickerFrame:GetColorRGB() Nenue@0: print('not cancel', newA, newR, newB, newG) Nenue@0: end Nenue@0: frame:SetValue({newR, newG, newB, newA}) Nenue@0: B.UpdateBuffs(frame.OptTab) Nenue@0: end Nenue@0: frame:SetScript("OnClick", function(self) Nenue@0: print('got a click') Nenue@0: local r, g, b, a = frame.current:GetVertexColor() Nenue@0: ColorPickerFrame:SetColorRGB(r, g, b) Nenue@0: ColorPickerFrame.hasOpacity = (a ~= nil) Nenue@0: ColorPickerFrame.opacity = a Nenue@0: ColorPickerFrame.previousValues = {r,g,b,a} Nenue@0: ColorPickerFrame.func, ColorPickerFrame.opacityFunc, ColorPickerFrame.cancelFunc = Nenue@0: colorPickerCallback, colorPickerCallback,colorPickerCallback Nenue@0: ColorPickerFrame:Hide() Nenue@0: ColorPickerFrame:Show() Nenue@0: end) Nenue@0: frame.SetValue = function(self, rgba) Nenue@0: print(rgba) Nenue@0: frame.rgba = rgba Nenue@0: B.Conf[self.OptName] = rgba Nenue@0: frame.current:SetVertexColor(unpack(rgba)) Nenue@0: frame.fieldvalue:SetText(frame.OptValue(frame, true)) Nenue@0: end Nenue@0: end Nenue@0: } Nenue@0: --- configDialog Nenue@0: -- @usage tinsert(configDialog, {prefix, row, [...] }) Nenue@0: -- Each top level member defines a group of config value handlers, structured as an iterative table where the Nenue@0: -- first member is a key prefix, the second member is an integer row value, and all following members are treated Nenue@0: -- as a widget resource, defined initially as a complete sub-table, which can be re-used further down by passing Nenue@0: -- the string literal widget suffix. Nenue@0: -- widget table: ... {'suffix', 'description', valueCallback, 'template', [widget parameters]} Nenue@0: -- widget copy: ... 'suffix', ... Nenue@0: local configDialog = { Nenue@0: {'BuffButton', 1, Nenue@0: Nenue@0: {'Max', 'Max', configInteger, 'Slider', Nenue@0: 1, _G.BUFF_MAX_DISPLAY, 1, 1}, -- valueMin, valueMax, valueStep, stepsPerPage Nenue@0: {'PerRow', 'Per Row', configInteger, 'Slider', Nenue@0: 1, _G.BUFF_MAX_DISPLAY, 1, 1}, -- valueMin, valueMax, valueStep, stepsPerPage, Nenue@0: {'Size', 'Icon Size', configInteger, 'Slider', Nenue@0: 1, 256, 1, 1}, Nenue@0: {'Spacing', 'Icon Spacing', configInteger, 'Slider', Nenue@0: 1, 50, 1, 1}, Nenue@0: {'DurationSize', 'Duration Text Height', configInteger, 'Slider', Nenue@0: 1, 72, 1, 1}, Nenue@0: {'Zoom', 'Icon Zoom', configInteger, 'Slider', Nenue@0: 0, 100, 1, 1}, Nenue@0: {'Border', 'Border', configInteger, 'Slider', Nenue@0: 1, 16, 1, 1}, Nenue@0: {'Color', 'Default Border', configColor, 'Color'}, Nenue@0: {'RaidColor', 'RaidBuff Border', configColor, 'Color'}, Nenue@0: {'PlayerColor', 'Player Buffs', configColor, 'Color'}, Nenue@0: {'BossColor', 'Encounter Buffs', configColor, 'Color'}, Nenue@0: {'ShowSelfCast', 'Show name for self-casts', configCheck, 'CheckButton'} Nenue@0: }, Nenue@0: { 'DebuffButton', 1, Nenue@0: {'Max', 'Max', configInteger, 'Slider', Nenue@0: 1, _G.DEBUFF_MAX_DISPLAY, 1, 1 } Nenue@0: , Nenue@0: {'PerRow', 'Per Row', configInteger, 'Slider', Nenue@0: 1, _G.DEBUFF_MAX_DISPLAY, 1, 1 }, Nenue@0: 'Size', 'Spacing', 'DurationSize', 'Zoom', 'Border', Nenue@0: 'Color', 'RaidColor', 'PlayerColor', 'BossColor', Nenue@0: }, Nenue@0: { 'TempEnchant', 1, Nenue@0: {'Max', 'Max', configInteger, 'Slider', Nenue@0: 1, _G.NUM_TEMP_ENCHANT_FRAMES, 1, 1 }, Nenue@0: {'PerRow', 'Per Row', configInteger, 'Slider', Nenue@0: 1, _G.NUM_TEMP_ENCHANT_FRAMES, 1, 1}, Nenue@0: 'Size', 'Spacing', 'DurationSize', 'Zoom', 'Border', Nenue@0: 'Color', 'RaidColor', 'PlayerColor', 'BossColor', Nenue@0: }, Nenue@0: { 'ConsolidatedBuff', 2, Nenue@0: {'Position', 'Slot Position', configInteger, 'Slider', Nenue@0: 1, _G.BUFF_MAX_DISPLAY, 1, 1 } Nenue@0: Nenue@0: }, Nenue@0: { 'ConsolidatedBuff', 2, Nenue@0: 'Size' Nenue@0: }, Nenue@0: { 'Raid', 3, Nenue@0: {'ShowMissing', 'Verbose missing raid buffs', configCheck, 'CheckButton'} Nenue@0: } Nenue@0: } Nenue@0: Nenue@0: Nenue@0: Nenue@0: Nenue@0: local configFrame Nenue@0: local optionTemplates = {} Nenue@0: local configPadding, configSpacing = 3, 3 Nenue@0: Nenue@0: --- Walks the structure table to generate a pretty config panel Nenue@0: local InitConfig = function() Nenue@0: configInit = true Nenue@0: local configWidth = B:GetWidth() Nenue@0: local optionWidth = (configWidth - configPadding) / 3 - configSpacing Nenue@0: local configHeight = 0 Nenue@0: local bottom_extent = 0 Nenue@0: local clusterHeight = 0 Nenue@0: local clusterOffset = 0 Nenue@0: local lastCluster Nenue@0: local cluster = 1 Nenue@0: local col = 0 Nenue@0: for t, taboptions in ipairs(configDialog) do Nenue@0: local group = taboptions[1] Nenue@0: cluster = taboptions[2] Nenue@0: col = col + 1 Nenue@0: Nenue@0: Nenue@0: if not configFrames[t] then Nenue@0: configFrames[t] = {} Nenue@0: end Nenue@0: Nenue@0: Nenue@0: if cluster ~= lastCluster then Nenue@0: configHeight = configHeight + clusterHeight Nenue@0: print('|cFFFF8800## new cluster|r, advancing offset from', clusterOffset, 'to', clusterOffset + clusterHeight) Nenue@0: clusterOffset = clusterOffset + clusterHeight Nenue@0: col = 1 Nenue@0: clusterHeight = 0 Nenue@0: lastCluster = cluster Nenue@0: end Nenue@0: Nenue@0: print('processing tab', group) Nenue@0: local row = 0 Nenue@0: for i = 3, #taboptions do Nenue@0: row = row + 1 Nenue@0: local optionInfo = taboptions[i] Nenue@0: if type(optionInfo) == 'string' then Nenue@0: optionInfo = optionTemplates[optionInfo] Nenue@0: end Nenue@0: local key, fieldname, valueFuncGenerator, configType = unpack(optionInfo) Nenue@0: Nenue@0: if not optionTemplates[key] then Nenue@0: optionTemplates[key] = optionInfo Nenue@0: end Nenue@0: Nenue@0: local fullkey = group .. key Nenue@0: print(fullkey, fieldname) Nenue@0: Nenue@0: if not configFrames[t][row] then Nenue@0: print('building frame', t, group, row) Nenue@0: local frameTemplate = 'VeneerConfig'..configType Nenue@0: local frameType = frameTypeConv[configType] or configType Nenue@0: configFrames[t][row] = CreateFrame(frameType, fullkey, B, frameTemplate) Nenue@0: local f = configFrames[t][row] Nenue@0: f.OptKey = key Nenue@0: f.OptTab = group Nenue@0: f.OptName = fullkey Nenue@0: local valueFunc, initialValue = valueFuncGenerator(group, key) Nenue@0: print(' value getter', fullkey,'->', valueFunc,initialValue) Nenue@0: configTypeParams[configType](f, optionInfo) Nenue@0: f.OptValue = valueFunc Nenue@0: Nenue@0: --- Enclosing these to Nenue@0: -- a) make the panel easy to bring up externally Nenue@0: -- b) limit gameplay risk from config frame errors Nenue@0: -- c) milk the iterator scope for all its worth Nenue@0: f.OnChange = function(self) Nenue@0: Nenue@0: -- holding control; mirror this setting in other categories Nenue@0: if IsControlKeyDown() and not (configInit) then Nenue@0: configInit = true Nenue@0: for optTab, opts in pairs(configFrames) do Nenue@0: for _, opt in ipairs(opts) do Nenue@0: if opt.OptKey == key then Nenue@0: if optTab ~= group then Nenue@0: print('mapping to', optTab, opt.OptKey) Nenue@0: opt:SetValue(self:GetValue()) Nenue@0: end Nenue@0: Nenue@0: end Nenue@0: end Nenue@0: end Nenue@0: configInit = nil Nenue@0: end Nenue@0: local newValue = valueFunc(self) Nenue@0: if newValue ~= B.Conf[fullkey] then Nenue@0: print(newValue, fullkey) Nenue@0: f.fieldvalue:SetText(valueFunc(self, true)) Nenue@0: B.Conf[fullkey] = valueFunc(self) Nenue@0: -- prepare to update Nenue@0: wipe(B.drawn[f.OptTab]) Nenue@0: B.UpdateBuffs(self.OptTab) Nenue@0: B.UpdateConfigLayers() Nenue@0: end Nenue@0: Nenue@0: end Nenue@0: Nenue@0: f:SetValue(initialValue) Nenue@0: local yBuffer = configPadding Nenue@0: if f.fieldname then Nenue@0: f.fieldname:SetText(fieldname) Nenue@0: yBuffer = yBuffer + f.fieldname:GetHeight() Nenue@0: end Nenue@0: if f.fieldvalue then Nenue@0: f.fieldvalue:SetText(f:OptValue(true)) Nenue@0: end Nenue@0: Nenue@0: local point, relative, x, y = 'TOPLEFT', 'BOTTOMLEFT', 0, -3 Nenue@0: Nenue@0: local base Nenue@0: if (row == 1) then Nenue@0: bottom_extent = 0 Nenue@0: base = B.header Nenue@0: x = (col-1) * (optionWidth+configSpacing) Nenue@0: y = -configPadding Nenue@0: else Nenue@0: base = configFrames[t][row-1] Nenue@0: end Nenue@0: Nenue@0: print('|cFFFF0088'..cluster..'|r |cFF00FF00'.. row..'|r', col, base:GetName(), x, y - clusterOffset) Nenue@0: Nenue@0: if frameType ~= 'CheckButton' then Nenue@0: f:SetWidth(optionWidth) Nenue@0: end Nenue@0: Nenue@0: f:SetPoint(point, base, relative, x, y-yBuffer-clusterOffset) Nenue@0: --print('creating', frameType, fieldname) Nenue@0: f:Show() Nenue@0: Nenue@0: bottom_extent = bottom_extent + f:GetHeight() + yBuffer + configSpacing Nenue@0: Nenue@0: Nenue@0: Nenue@0: clusterHeight = max(clusterHeight, bottom_extent) Nenue@0: --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: end Nenue@0: end Nenue@0: end Nenue@0: Nenue@0: -- grab the last cluster Nenue@0: if lastCluster == cluster then Nenue@0: print('|cFF00FF00##scooping up last cluster info') Nenue@0: configHeight = configHeight + clusterHeight Nenue@0: end Nenue@0: Nenue@0: if not B.configFramesCreated then Nenue@0: B.configFramesCreated = true Nenue@0: B:SetHeight(B.header:GetStringHeight() + configSpacing*3 + configHeight) Nenue@0: end Nenue@0: if configInit then configInit = nil end Nenue@0: end Nenue@0: Nenue@0: M.Command = function(enable, editbox) Nenue@0: displays = B.displays Nenue@0: if type(enable) == 'boolean' then Nenue@0: B.Conf.ConfigMode = enable Nenue@0: else Nenue@0: B.Conf.ConfigMode = (B.Conf.ConfigMode == false) and true or false Nenue@0: end Nenue@0: Nenue@0: print('/BUFF', B.Conf.ConfigMode, type(B.Conf.ConfigMode)) Nenue@0: if B.Conf.ConfigMode then Nenue@0: if not B.configFramesCreated then Nenue@0: InitConfig() Nenue@0: end Nenue@0: print('Veneer config') Nenue@0: B:Show() Nenue@0: else Nenue@0: B:Hide() Nenue@0: end Nenue@0: B.UpdateAll() Nenue@0: B.UpdateConfigLayers() Nenue@0: end Nenue@0: Nenue@0: B.Close = function () Nenue@0: M.Command() Nenue@0: end Nenue@0: Nenue@0: B.ToggleGuides = function(_, self) Nenue@0: B.Conf.GuidesMode = (not B.Conf.GuidesMode) Nenue@0: if B.Conf.GuidesMode then Nenue@0: self:GetNormalTexture():SetTexture(0.94, 0.21, 0.21, 1) Nenue@0: else Nenue@0: self:GetNormalTexture():SetTexture(0, 0, 0, 1) Nenue@0: end Nenue@0: Nenue@0: B.UpdateConfigLayers() Nenue@0: end Nenue@0: Nenue@0: M.OnEnable = function() Nenue@24: print('|cFFFF0088config module', B.Conf.ConfigMode) Nenue@0: M.Command(B.Conf.ConfigMode) Nenue@0: end Nenue@0: Nenue@0: M.OnInitialize = function() Nenue@0: DEFAULT_CHAT_FRAME:AddMessage("|cFF22D822Veneer|r") Nenue@0: SLASH_BUFFALO1, SLASH_BUFFALO2 = "/buffalo", "/buff" Nenue@0: SlashCmdList.BUFFALO = M.Command Nenue@0: Nenue@0: end