You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

375 lines
12 KiB

local addonName, addon = ...
local function onSettingChanged(owner, setting, value)
if setting.owner ~= addonName then
-- avoid phantom updates from addons having settings with the same key name
return
end
if owner then
-- triggered by player changing settings in the panel
addon:SetOption(setting:GetVariable(), value)
else
-- triggered by addon.SetOption
setting:SetValue(value)
end
end
local function formatCustom(fmt, value)
return fmt:format(value)
end
-- they removed the new flag for no apparent reason
local settingMixin = {}
function settingMixin:SetNewTagShown(state)
self.newTagShown = state
end
function settingMixin:IsNewTagShown() -- override method
return self.newTagShown
end
local CreateColorPicker -- I wish Settings.CreateColorPicker was a thing
do
local colorPickerMixin = {}
function colorPickerMixin:OnSettingValueChanged(setting, value)
local r, g, b, a = addon:CreateColor(value):GetRGBA()
self.Swatch:SetColorRGB(r, g, b)
-- modify colorInfo for next run
self.colorInfo.r = r
self.colorInfo.g = g
self.colorInfo.b = b
self.colorInfo.a = a
end
local function onClick(self)
local parent = self:GetParent()
local info = parent.colorInfo
if info.hasOpacity then
parent.oldValue = CreateColor(info.r, info.g, info.b, info.a):GenerateHexColor()
else
parent.oldValue = CreateColor(info.r, info.g, info.b):GenerateHexColorNoAlpha()
end
ColorPickerFrame:SetupColorPickerAndShow(info)
end
local function onColorChanged(self, setting)
local r, g, b = ColorPickerFrame:GetColorRGB()
if self.colorInfo.hasOpacity then
local a = ColorPickerFrame:GetColorAlpha()
setting:SetValue(CreateColor(r, g, b, a):GenerateHexColor())
else
setting:SetValue(CreateColor(r, g, b):GenerateHexColorNoAlpha())
end
end
local function onColorCancel(self, setting)
setting:SetValue(self.oldValue)
end
local function initFrame(initializer, self)
SettingsListElementMixin.OnLoad(self)
SettingsListElementMixin.Init(self, initializer)
Mixin(self, colorPickerMixin)
self:SetSize(280, 26) -- templates have a size
-- creating widgets would be equal to :OnLoad()
self.Swatch = CreateFrame('Button', nil, self, 'ColorSwatchTemplate')
self.Swatch:SetSize(30, 30)
self.Swatch:SetPoint('LEFT', self, 'CENTER', -80, 0)
self.Swatch:SetScript('OnClick', onClick)
-- setting up state would be equal to :Init()
local setting = initializer:GetSetting()
local value = setting:GetValue()
local r, g, b, a = addon:CreateColor(value):GetRGBA()
self.colorInfo = {
swatchFunc = GenerateClosure(onColorChanged, self, setting),
opacityFunc = GenerateClosure(onColorChanged, self, setting),
cancelFunc = GenerateClosure(onColorCancel, self, setting),
r = r,
g = g,
b = b,
opacity = a,
hasOpacity = #value == 8
}
self.Swatch:SetColorRGB(r, g, b)
-- set up callbacks, see SettingsControlMixin.Init as an example
-- this is used to change common values, and is triggered by setting:SetValue(), thus also from defaults
self.cbrHandles:SetOnValueChangedCallback(setting:GetVariable(), self.OnSettingValueChanged, self)
end
function CreateColorPicker(category, setting, options, tooltip)
local data = Settings.CreateSettingInitializerData(setting, options, tooltip)
local init = Settings.CreateElementInitializer('SettingsListElementTemplate', data)
init.InitFrame = initFrame
init:AddSearchTags(setting:GetName())
SettingsPanel:GetLayout(category):AddInitializer(init)
end
end
local function registerSetting(category, info)
local setting = Settings.RegisterAddOnSetting(category, info.title, info.key, type(info.default), info.default)
setting.owner = addonName -- unique flag on the setting per-addon to avoid phantom updates
Mixin(setting, settingMixin)
if info.type == 'toggle' then
(Settings.CreateCheckBox or Settings.CreateCheckbox)(category, setting, info.tooltip) -- TODO: TWW cleanup
elseif info.type == 'slider' then
local sliderOptions = Settings.CreateSliderOptions(info.minValue, info.maxValue, info.valueStep or 1)
local valueFormat
if type(info.valueFormat) == 'string' then
valueFormat = GenerateClosure(formatCustom, info.valueFormat)
elseif type(info.valueFormat) == 'function' then
valueFormat = info.valueFormat
end
sliderOptions:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right, valueFormat)
Settings.CreateSlider(category, setting, sliderOptions, info.tooltip)
elseif info.type == 'menu' then
local getMenuOptions = function()
local container = Settings.CreateControlTextContainer()
for key, name in next, info.options do
container:Add(key, name)
end
return container:GetData()
end
(Settings.CreateDropDown or Settings.CreateDropdown)(category, setting, getMenuOptions, info.tooltip) -- TODO: TWW cleanup
elseif info.type == 'colorpicker' then
CreateColorPicker(category, setting, nil, info.tooltip)
end
if info.new then
setting:SetNewTagShown(true)
end
-- hook into both the Settings object and Dashi's option callback for value changes
Settings.SetOnValueChangedCallback(info.key, onSettingChanged)
addon:RegisterOptionCallback(info.key, GenerateClosure(onSettingChanged, nil, setting))
end
local canvasMixin = {}
function canvasMixin:SetDefaultsHandler(callback)
local DefaultsButton = self:GetParent().Header.DefaultsButton
DefaultsButton:Show()
DefaultsButton:SetScript('OnClick', callback)
end
local function createCanvas(name)
local frame = CreateFrame('Frame')
-- replicate header from SettingsListTemplate
local header = CreateFrame('Frame', nil, frame)
header:SetPoint('TOPLEFT')
header:SetPoint('TOPRIGHT')
header:SetHeight(50)
frame.Header = header
local title = header:CreateFontString(nil, 'ARTWORK', 'GameFontHighlightHuge')
title:SetPoint('TOPLEFT', 7, -22)
title:SetJustifyH('LEFT')
title:SetText(string.format('%s - %s', addonName, name))
header.Title = title
local defaults = CreateFrame('Button', nil, header, 'UIPanelButtonTemplate')
defaults:SetPoint('TOPRIGHT', -36, -16)
defaults:SetSize(96, 22)
defaults:SetText(_G.SETTINGS_DEFAULTS)
defaults:Hide()
header.DefaultsButton = defaults
local divider = header:CreateTexture(nil, 'ARTWORK')
divider:SetPoint('TOP', 0, -50)
divider:SetAtlas('Options_HorizontalDivider', true)
-- exposed container the addon can use
local canvas = Mixin(CreateFrame('Frame', nil, frame), canvasMixin)
canvas:SetPoint('BOTTOMLEFT', 0, 5)
canvas:SetPoint('BOTTOMRIGHT', -12, 5)
canvas:SetPoint('TOP', 0, -56)
return frame, canvas
end
local children = {}
local function internalRegisterSettings(savedvariable, settings)
-- create a vertical layout category, handing off all elements to Blizzard
local category = Settings.RegisterVerticalLayoutCategory(C_AddOns.GetAddOnMetadata(addonName, 'Title'))
-- iterate through the provided settings table and generate settings objects and defaults
local defaults = {}
for _, setting in next, settings do
registerSetting(category, setting)
defaults[setting.key] = setting.default
end
-- register category and load the savedvariables
Settings.RegisterAddOnCategory(category)
addon:LoadOptions(savedvariable, defaults)
-- deal with sub-categories
for _, info in next, children do
if info.kind == 'settings' then
local sub = Settings.RegisterVerticalLayoutSubcategory(category, info.name)
table.wipe(defaults)
for _, setting in next, info.settings do
registerSetting(sub, setting)
defaults[setting.key] = setting.default
end
Settings.RegisterAddOnCategory(sub)
addon:LoadExtraOptions(savedvariable, defaults)
elseif info.kind == 'canvas' then
local frame, canvas = createCanvas(info.name)
local sub = Settings.RegisterCanvasLayoutSubcategory(category, frame, info.name)
Settings.RegisterAddOnCategory(sub)
-- delay callback until settings are shown
local shown
SettingsPanel:HookScript('OnShow', function()
if not shown then
info.callback(canvas)
shown = true
end
end)
end
end
end
local isRegistered
--[[ namespace:RegisterSettings(_savedvariables_, _settings_)
Registers a set of `settings` with the interface options panel.
The values will be stored by the `settings`' objects' `key` in `savedvariables`.
Should be used with the options methods below.
Usage:
```lua
namespace:RegisterSettings('MyAddOnDB', {
{
key = 'myToggle',
type = 'toggle',
title = 'My Toggle',
tooltip = 'Longer description of the toggle in a tooltip',
default = false,
new = false,
}
{
key = 'mySlider',
type = 'slider',
title = 'My Slider',
tooltip = 'Longer description of the slider in a tooltip',
default = 0.5,
minValue = 0.1,
maxValue = 1.0,
valueStep = 0.01,
valueFormat = formatter, -- callback function or a string for string.format
new = true,
},
{
key = 'myMenu',
type = 'menu',
title = 'My Menu',
tooltip = 'Longer description of the menu in a tooltip',
default = 'key1',
options = {
key1 = 'First option',
key2 = 'Second option',
key3 = 'Third option',
},
new = false,
},
{
key = 'myColor',
type = 'colorpicker',
title = 'My Color',
tooltip = 'Longer description of the color in a tooltip',
default = 'ff00ff', -- either "RRGGBB" or "AARRGGBB" format
new = false,
}
})
```
--]]
function addon:RegisterSettings(savedvariable, settings)
assert(not isRegistered, "can't register settings more than once")
isRegistered = true
-- ensure we only add the panel after savedvariables are available to the client
local _, isReady = C_AddOns.IsAddOnLoaded(addonName)
if isReady then
internalRegisterSettings(savedvariable, settings)
else
-- don't abuse OnLoad internally
addon:RegisterEvent('ADDON_LOADED', function(_, name)
if name == addonName then
internalRegisterSettings(savedvariable, settings)
return true -- unregister
end
end)
end
end
--[[ namespace:RegisterSubSettings(_name_, _settings_)
Registers a set of `settings` as a sub-category. `name` must be unique.
The values will be stored by the `settings`' objects' `key` in the previously created savedvariables.
The `settings` are identical to that of `namespace:RegisterSettings`.
--]]
function addon:RegisterSubSettings(name, settings)
assert(isRegistered, "can't register sub-settings without main settings")
table.insert(children, {
kind = 'settings',
name = name,
settings = settings,
})
end
--[[ namespace:RegisterSubSettingsCanvas(_name_, _callback_)
Registers a canvas sub-category. This does not handle savedvariables.
`name` must be unique, and `callback` is called with a canvas `frame` as payload.
Canvas frame has a custom method `SetDefaultsHandler` which takes a callback as arg1.
This callback is triggered when the "Defaults" button is clicked.
--]]
function addon:RegisterSubCanvas(name, callback)
assert(isRegistered, "can't register sub-canvas without main settings")
table.insert(children, {
kind = 'canvas',
name = name,
callback = callback,
})
end
--[[ namespace:RegisterSettingsSlash(_..._)
Wrapper for `namespace:RegisterSlash(...)`, except the callback is provided and will open the interface options for this addon.
--]]
function addon:RegisterSettingsSlash(...)
-- gotta do this dumb shit because `..., callback` is not valid Lua
local data = {...}
table.insert(data, function()
-- iterate over all categories until we find ours, since OpenToCategory only takes ID
local categoryID
local settingsName = C_AddOns.GetAddOnMetadata(addonName, 'Title')
for _, category in next, SettingsPanel:GetAllCategories() do
if category.name == settingsName then
assert(not categoryID, 'found multiple instances of the same category')
categoryID = category:GetID()
end
end
Settings.OpenToCategory(categoryID)
end)
addon:RegisterSlash(unpack(data))
end