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.
2765 lines
97 KiB
2765 lines
97 KiB
--[[
|
|
writes to options field of aura data, which is then read to construct the user config panel
|
|
|
|
Values in aura data:
|
|
authorOptions -> array of options.
|
|
config -> key/value hash table.
|
|
key, and format of value are defined by author, and the precise value is defined by the user.
|
|
This table gets copied into the aura's script environment via aura_env.config.
|
|
authorMode -> bool, used to determine if author or user mode is displayed in Custom Options tab.
|
|
|
|
option -> table with fields:
|
|
type (required) -> string such as "toggle", "slider", "string", "number", "color", etc.
|
|
key (required) -> string which custom scripts can use to read the selected option
|
|
default (required) -> default value of the option
|
|
name (required) -> displayed name in the user config panel
|
|
width (required) -> number between 0.1 and 2 (softMin of 0.5). Determines the width of the option.
|
|
useDesc (optional) -> bool. If false, then the tooltip will not be used.
|
|
desc (optional) -> string to be displayed in the option tooltip
|
|
When options are merged together (i.e. when the user multiselects and then opens the custom options tab), there is one additional field:
|
|
references -> childID <=> optionID map, used to dereference to the proper option table in setters
|
|
Supported option types, and additional fields that each type supports/requires:
|
|
group -> represents a group of options.
|
|
useCollapse (optional) -> if true, then group will have a collapsible header in user mode.
|
|
collapse (optional) -> whether or not the collapsible header begins collapsed when the user begins a session.
|
|
subOptions (required) -> array of options
|
|
groupType (required) -> type of group:
|
|
simple -> group is for organizational purposes only.
|
|
config value is a sub config
|
|
array -> group represents an array of entries from the user, with similar information between them:
|
|
config value is arranged as an array of sub configs, one for each entry in the array.
|
|
limitType (required) -> Specifies if user can add or remove entries from the array freely
|
|
size (optional) -> required if the limitType is not "none".
|
|
description -> dummy option which can be used to display some text. Not interactive, and so key/default/name are not set or required.
|
|
text (required) -> text displayed on the panel
|
|
fontSize (optional) -> fontSize. Default is medium.
|
|
space -> dummy option which acts as a spacer. Not interactive, and so key/default/name are not set or required.
|
|
useWidth (required) -> bool. If false, then the space is given full width in AceConfig. Else, option.width is used.
|
|
useHeight (required) -> bool. If false, then the space covers only the line it renders on. Else, it covers the number of lines specified.
|
|
height (optional) -> number. Height of space, in lines.
|
|
separator -> AceConfig header widget.
|
|
useName (required) -> bool. If true, then option.text is used as the name.
|
|
test (optional) -> string. Text to be shown on the header widget.
|
|
input -> text field which the user can input a string.
|
|
length (optional) -> allowed length of the string. If set, then input longer than the allowed length will be trimmed
|
|
number -> text field which the user can type in a number. Input is converted to a number value.
|
|
max (optional) -> maximum allowed value. If set, then input greater than the maximum will be clamped
|
|
min (optional) -> minimum allowed value. If set, then input lesser than the minimum will be clamped
|
|
step (optional) -> stepsize
|
|
select -> dropdown menu with author-specified strings to select.
|
|
values (required) -> array of strings to select. config value will be the index corresponding to the string.
|
|
toggle -> checkbutton which can be in a state of true or false, corresponding to checked and unchecked
|
|
color -> color selector. Color is delivered as an {r, g, b, a} array.
|
|
range -> slider element which allows the user to select a number value.
|
|
max (optional) -> maximum allowed value. If set, then input greater than the maximum will be clamped
|
|
min (optional) -> minimum allowed value. If set, then input lesser than the minimum will be clamped
|
|
softmax (optional) -> Like max, but the manual entry will accept values up to the softmax.
|
|
softmin (optional) -> Like min, but the manual entry will accept values down to the softmin.
|
|
bigStep (optional) -> step size of the slider. Defaults to 0.05
|
|
step (optional) -> like bigStep, but applies to number input as well
|
|
]]
|
|
if not WeakAuras.IsLibsOK() then return end
|
|
---@type string
|
|
local AddonName = ...
|
|
---@class OptionsPrivate
|
|
local OptionsPrivate = select(2, ...)
|
|
|
|
---@class WeakAuras
|
|
local WeakAuras = WeakAuras
|
|
local L = WeakAuras.L
|
|
|
|
local tinsert, tremove, tconcat = table.insert, table.remove, table.concat
|
|
local conflictBlue = "|cFF4080FF"
|
|
local conflict = {} -- magic value
|
|
|
|
local function atLeastOneSet(references, key)
|
|
for _, optionData in pairs(references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
if childOption[key] ~= nil then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
local function neq(a, b)
|
|
if type(a) == "table" and type(b) == "table" then
|
|
for k, v in pairs(a) do
|
|
if neq(v, b[k]) then
|
|
return true
|
|
end
|
|
end
|
|
for k, v in pairs(b) do
|
|
if neq(v, a[k]) then
|
|
return true
|
|
end
|
|
end
|
|
else
|
|
return a ~= b
|
|
end
|
|
end
|
|
|
|
-- blues the name if there are conflicts between the references for this value
|
|
local function name(option, key, name, phrase)
|
|
local header = name or phrase
|
|
if option[key] ~= nil or not atLeastOneSet(option.references, key) then
|
|
return header
|
|
else
|
|
return conflictBlue .. header
|
|
end
|
|
end
|
|
|
|
-- blue if at least one member of the group does not have an option in the references
|
|
local function nameHead(data, option, phrase)
|
|
if not data.controlledChildren then
|
|
return phrase
|
|
else
|
|
for child in OptionsPrivate.Private.TraverseLeafs(data) do
|
|
if not option.references[child.id] then
|
|
return conflictBlue .. phrase
|
|
end
|
|
end
|
|
end
|
|
return phrase
|
|
end
|
|
|
|
local function nameUser(option)
|
|
local firstValue
|
|
for id, optionData in pairs(option.references) do
|
|
local childConfig = optionData.config
|
|
if not childConfig then
|
|
return option.name
|
|
elseif firstValue == nil then
|
|
firstValue = childConfig[option.key]
|
|
elseif neq(firstValue, childConfig[option.key]) then
|
|
return conflictBlue .. option.name
|
|
end
|
|
end
|
|
return option.name
|
|
end
|
|
|
|
local function nameUserDesc(option)
|
|
if option.text then
|
|
return option.text
|
|
else
|
|
local text = {}
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
if childOption.text and childOption.text ~= nil then
|
|
tinsert(text, childOption.text)
|
|
end
|
|
end
|
|
return conflictBlue .. tconcat(text, "\n")
|
|
end
|
|
end
|
|
|
|
local function nameArray(option, array, index, phrase)
|
|
local value
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
if not childOption[array] then
|
|
return conflictBlue .. phrase
|
|
elseif childOption[array][index] == nil then
|
|
return conflictBlue .. phrase
|
|
elseif value == nil then
|
|
value = childOption[array][index]
|
|
elseif value ~= childOption[array][index] then
|
|
return conflictBlue .. phrase
|
|
end
|
|
end
|
|
return phrase
|
|
end
|
|
|
|
-- provides a tooltip showing all the conflicting values if there are any
|
|
local function desc(option, key, phrase)
|
|
if option[key] or not atLeastOneSet(option.references, key) then
|
|
return phrase
|
|
else
|
|
local desc = {}
|
|
if phrase then
|
|
desc[1] = phrase
|
|
end
|
|
tinsert(desc, L["Values:"])
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
if childOption[key] ~= nil then
|
|
tinsert(
|
|
desc,
|
|
("%s #%i: %s"):format(childData.id, optionData.path[#optionData.path], tostring(childOption[key]))
|
|
)
|
|
end
|
|
end
|
|
return tconcat(desc, "\n")
|
|
end
|
|
end
|
|
|
|
local function descType(option)
|
|
local desc = {
|
|
L["This setting controls what widget is generated in user mode."],
|
|
L["Used in Auras:"]
|
|
}
|
|
for id, optionData in pairs(option.references) do
|
|
tinsert(desc, ("%s - Option %i"):format(id, optionData.path[#optionData.path]))
|
|
end
|
|
return tconcat(desc, "\n")
|
|
end
|
|
|
|
local function descSelect(option, key)
|
|
if option.values then
|
|
return ""
|
|
else
|
|
local desc = {L["Values:"]}
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
if childOption.values[key] ~= nil then
|
|
tinsert(
|
|
desc,
|
|
("%s %i: - %s"):format(id, optionData.path[#optionData.path], tostring(childOption.values[key]))
|
|
)
|
|
end
|
|
end
|
|
return tconcat(desc, "\n")
|
|
end
|
|
end
|
|
|
|
local function descColor(option, key)
|
|
if option[key] or not atLeastOneSet(option.references, key) then
|
|
return L["Values are in normalized rgba format."]
|
|
else
|
|
local desc = {
|
|
L["Values are in normalized rgba format."],
|
|
L["Values:"]
|
|
}
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
if childOption[key] ~= nil then
|
|
tinsert(
|
|
desc,
|
|
("%s #%i: %.2f %.2f %.2f %.2f"):format(
|
|
id,
|
|
childOption.path[#childOption.path],
|
|
unpack(childOption[key])
|
|
)
|
|
)
|
|
end
|
|
end
|
|
return tconcat(desc, "\n")
|
|
end
|
|
end
|
|
|
|
local function descUser(option)
|
|
if option.useDesc ~= nil and option.desc ~= nil then
|
|
return option.useDesc and option.desc or nil
|
|
else
|
|
local desc = {}
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
if childOption.useDesc and childOption.desc and childOption.desc ~= "" then
|
|
tinsert(desc, ("%s - %s"):format(id, childOption.desc))
|
|
end
|
|
end
|
|
return tconcat(desc, "\n")
|
|
end
|
|
end
|
|
|
|
local function descArray(option, array, index, phrase)
|
|
local desc, values, isConflict = {phrase}, {}, false
|
|
local initialValue = nil
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
values[id] = tostring(childOption[array][index])
|
|
if initialValue == nil then
|
|
initialValue = values[id]
|
|
elseif values[id] ~= initialValue then
|
|
isConflict = true
|
|
end
|
|
end
|
|
if isConflict then
|
|
for id, value in pairs(values) do
|
|
tinsert(desc, ("%s - %s"):format(id, value))
|
|
end
|
|
end
|
|
return tconcat(desc, "\n")
|
|
end
|
|
|
|
-- getters for AceConfig
|
|
local function get(option, key)
|
|
return function()
|
|
return option[key]
|
|
end
|
|
end
|
|
|
|
local function getUser(option)
|
|
return function()
|
|
local value
|
|
for _, optionData in pairs(option.references) do
|
|
if not optionData.config then
|
|
return
|
|
elseif value == nil then
|
|
value = optionData.config[option.key]
|
|
elseif neq(value, optionData.config[option.key]) then
|
|
return
|
|
end
|
|
end
|
|
return value
|
|
end
|
|
end
|
|
|
|
local function getStr(option, key)
|
|
return function()
|
|
local str = option[key] or ""
|
|
return str:gsub("|", "||")
|
|
end
|
|
end
|
|
|
|
local function getNumAsString(option, key)
|
|
return function()
|
|
if option[key] ~= nil then
|
|
return tostring(option[key])
|
|
end
|
|
end
|
|
end
|
|
|
|
local function getUserNumAsString(option)
|
|
return function()
|
|
local value
|
|
for id, optionData in pairs(option.references) do
|
|
if value == nil then
|
|
value = optionData.config[option.key]
|
|
elseif neq(value, optionData.config[option.key]) then
|
|
return ""
|
|
end
|
|
end
|
|
if value ~= nil then
|
|
return tostring(value)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function getValues(option)
|
|
local values = {}
|
|
local firstChild = true
|
|
for _, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childValues = childOption.values
|
|
local i = 1
|
|
while i <= #values or i <= #childValues do
|
|
if firstChild then
|
|
values[i] = childValues[i]
|
|
elseif values[i] ~= childValues[i] then
|
|
values[i] = conflict
|
|
end
|
|
i = i + 1
|
|
end
|
|
firstChild = false
|
|
end
|
|
return values
|
|
end
|
|
|
|
local function getUserValues(option)
|
|
local values = getValues(option)
|
|
for i, v in ipairs(values) do
|
|
if v == conflict then
|
|
values[i] = conflictBlue .. L["Value %i"]:format(i)
|
|
end
|
|
end
|
|
return values
|
|
end
|
|
|
|
local function getColor(option, key)
|
|
return function()
|
|
if option[key] then
|
|
return unpack(option[key])
|
|
end
|
|
end
|
|
end
|
|
|
|
local function getUserColor(option)
|
|
return function()
|
|
local firstValue
|
|
for id, optionData in pairs(option.references) do
|
|
local childConfig = optionData.config
|
|
if firstValue == nil then
|
|
firstValue = childConfig[option.key]
|
|
elseif neq(firstValue, childConfig[option.key]) then
|
|
return
|
|
end
|
|
if firstValue then
|
|
return unpack(firstValue)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function getArrayStr(option, array, index)
|
|
return function()
|
|
if option[array][index] then
|
|
return option[array][index]:gsub("|","||")
|
|
else
|
|
return ""
|
|
end
|
|
end
|
|
end
|
|
|
|
-- setters for AceConfig
|
|
local function set(data, option, key)
|
|
return function(_, value)
|
|
for _, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
childOption[key] = value
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
end
|
|
|
|
local function setUser(data, option)
|
|
return function(_, value)
|
|
for _, optionData in pairs(option.references) do
|
|
local childData = optionData.data
|
|
local childConfig = optionData.config
|
|
childConfig[option.key] = value
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
end
|
|
|
|
local function setStr(data, option, key)
|
|
return function(_, value)
|
|
value = value:gsub("||", "|")
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
childOption[key] = value
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
end
|
|
|
|
local function setNum(data, option, key, required)
|
|
return function(_, value)
|
|
if value ~= "" then
|
|
local num = tonumber(value)
|
|
if not num or math.abs(num) == math.huge or tostring(num) == "nan" then
|
|
return
|
|
end
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
childOption[key] = num
|
|
WeakAuras.Add(childData)
|
|
end
|
|
elseif not required then
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
childOption[key] = nil
|
|
WeakAuras.Add(childData)
|
|
end
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
end
|
|
|
|
local function setUserNum(data, option)
|
|
return function(_, value)
|
|
if value ~= "" then
|
|
local num = tonumber(value)
|
|
if not num or math.abs(num) == math.huge or tostring(num) == "nan" then return end
|
|
for _, optionData in pairs(option.references) do
|
|
local childData = optionData.data
|
|
local childConfig = optionData.config
|
|
childConfig[option.key] = num
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function setColor(data, option, key)
|
|
return function(_, r, g, b, a)
|
|
local color = {r, g, b, a}
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
childOption[key] = color
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
end
|
|
|
|
local function setUserColor(data, option)
|
|
return function(_, r, g, b, a)
|
|
local color = {r, g, b, a}
|
|
for id, optionData in pairs(option.references) do
|
|
local childData = optionData.data
|
|
local childConfig = optionData.config
|
|
childConfig[option.key] = color
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
end
|
|
|
|
local function setSelectDefault(data, option, key)
|
|
return function(_, value)
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
childOption.default = min(value, #childOption.values)
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
end
|
|
|
|
local function setArrayStr(data, option, array, index)
|
|
return function(_, value)
|
|
value = value:gsub("||","|")
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
childOption[array][index] = value
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
end
|
|
|
|
local function ensureUniqueKey(candidate, suffix, options, index)
|
|
index = index or 1
|
|
local goodKey = true
|
|
local key = candidate
|
|
local existingKeys = {}
|
|
for _, option in ipairs(options) do
|
|
if option.key then
|
|
if option.key == key then
|
|
goodKey = false
|
|
end
|
|
existingKeys[option.key] = true
|
|
end
|
|
end
|
|
if not goodKey then
|
|
local prefix = candidate .. suffix
|
|
while not goodKey do
|
|
key = prefix .. index
|
|
goodKey = not existingKeys[key]
|
|
index = index + 1
|
|
end
|
|
end
|
|
return key
|
|
end
|
|
|
|
local function generateKey(prefix, options, index)
|
|
return ensureUniqueKey(prefix, "", options, index)
|
|
end
|
|
|
|
local typeControlAdders, addAuthorModeOption
|
|
typeControlAdders = {
|
|
toggle = function(options, args, data, order, prefix, i)
|
|
local option = options[i]
|
|
args[prefix .. "default"] = {
|
|
type = "select",
|
|
width = WeakAuras.normalWidth,
|
|
name = name(option, "default", L["Default"]),
|
|
desc = desc(option, "default"),
|
|
order = order(),
|
|
values = OptionsPrivate.Private.bool_types,
|
|
get = function()
|
|
if option.default == nil then
|
|
return
|
|
end
|
|
return option.default and 1 or 0
|
|
end,
|
|
set = function(_, value)
|
|
local val = value == 1
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
childOption.default = val
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
}
|
|
end,
|
|
input = function(options, args, data, order, prefix, i)
|
|
local option = options[i]
|
|
args[prefix .. "default"] = {
|
|
type = "input",
|
|
width = WeakAuras.normalWidth,
|
|
name = name(option, "default", L["Default"]),
|
|
desc = desc(option, "default"),
|
|
order = order(),
|
|
get = get(option, "default"),
|
|
set = set(data, option, "default")
|
|
}
|
|
args[prefix .. "useLength"] = {
|
|
type = "toggle",
|
|
width = WeakAuras.normalWidth,
|
|
name = name(option, "useLength", L["Max Length"]),
|
|
desc = desc(option, "useLength"),
|
|
order = order(),
|
|
get = get(option, "useLength"),
|
|
set = set(data, option, "useLength")
|
|
}
|
|
args[prefix .. "length"] = {
|
|
type = "range",
|
|
control = "WeakAurasSpinBox",
|
|
width = WeakAuras.normalWidth,
|
|
name = name(option, "length", L["Length"]),
|
|
desc = desc(option, "length"),
|
|
order = order(),
|
|
min = 1,
|
|
step = 1,
|
|
softMax = 20,
|
|
get = get(option, "length"),
|
|
set = set(data, option, "length"),
|
|
disabled = function()
|
|
return not option.useLength
|
|
end
|
|
}
|
|
args[prefix .. "multiline"] = {
|
|
type = "toggle",
|
|
width = WeakAuras.doubleWidth,
|
|
name = name(option, "multiline", L["Large Input"]),
|
|
desc = desc(option, "multiline", L["If checked, then the user will see a multi line edit box. This is useful for inputting large amounts of text."]),
|
|
order = order(),
|
|
get = get(option, "multiline"),
|
|
set = set(data, option, "multiline"),
|
|
}
|
|
end,
|
|
number = function(options, args, data, order, prefix, i)
|
|
local option = options[i]
|
|
args[prefix .. "default"] = {
|
|
type = "input",
|
|
width = WeakAuras.normalWidth,
|
|
name = name(option, "default", L["Default"]),
|
|
desc = desc(option, "default"),
|
|
order = order(),
|
|
get = getNumAsString(option, "default"),
|
|
set = setNum(data, option, "default", true)
|
|
}
|
|
args[prefix .. "min"] = {
|
|
type = "input",
|
|
width = WeakAuras.normalWidth * 2 / 3,
|
|
name = name(option, "min", L["Min"]),
|
|
desc = desc(option, "min"),
|
|
order = order(),
|
|
get = getNumAsString(option, "min"),
|
|
set = setNum(data, option, "min")
|
|
}
|
|
args[prefix .. "max"] = {
|
|
type = "input",
|
|
width = WeakAuras.normalWidth * 2 / 3,
|
|
name = name(option, "max", L["Max"]),
|
|
desc = desc(option, "min"),
|
|
order = order(),
|
|
get = getNumAsString(option, "max"),
|
|
set = setNum(data, option, "max")
|
|
}
|
|
args[prefix .. "step"] = {
|
|
type = "input",
|
|
width = WeakAuras.normalWidth * 2 / 3,
|
|
name = name(option, "step", L["Step Size"]),
|
|
desc = desc(option, "step"),
|
|
order = order(),
|
|
get = getNumAsString(option, "step"),
|
|
set = setNum(data, option, "step")
|
|
}
|
|
end,
|
|
range = function(options, args, data, order, prefix, i)
|
|
local option = options[i]
|
|
local min, max, softMin, softMax, step, bigStep
|
|
softMax = option.softMax
|
|
softMin = option.softMin
|
|
bigStep = option.bigStep
|
|
min = option.min
|
|
max = option.max
|
|
local effectiveMin = softMin or min or 0
|
|
local effectiveMax = softMax or max or 100
|
|
if (effectiveMin > effectiveMax) then
|
|
-- This will cause a error inside the slider
|
|
-- Fix up either softMax or max, depending on which one is the effective one
|
|
if softMax then
|
|
softMax = effectiveMin
|
|
elseif max then
|
|
max = effectiveMin
|
|
else
|
|
softMax = effectiveMin
|
|
end
|
|
end
|
|
step = option.step
|
|
args[prefix .. "default"] = {
|
|
type = "range",
|
|
control = "WeakAurasSpinBox",
|
|
width = WeakAuras.normalWidth,
|
|
name = name(option, "default", L["Default"]),
|
|
desc = desc(option, "default"),
|
|
order = order(),
|
|
get = get(option, "default"),
|
|
set = set(data, option, "default"),
|
|
min = min,
|
|
max = max,
|
|
step = step,
|
|
softMin = softMin,
|
|
softMax = softMax,
|
|
bigStep = bigStep,
|
|
}
|
|
|
|
args[prefix .. "min"] = {
|
|
type = "input",
|
|
width = WeakAuras.normalWidth * 2 / 3,
|
|
name = name(option, "min", L["Min"]),
|
|
desc = desc(option, "min"),
|
|
order = order(),
|
|
get = getNumAsString(option, "min"),
|
|
set = setNum(data, option, "min")
|
|
}
|
|
|
|
args[prefix .. "max"] = {
|
|
type = "input",
|
|
width = WeakAuras.normalWidth * 2 / 3,
|
|
name = name(option, "max", L["Max"]),
|
|
desc = desc(option, "max"),
|
|
order = order(),
|
|
get = getNumAsString(option, "max"),
|
|
set = setNum(data, option, "max")
|
|
}
|
|
|
|
args[prefix .. "step"] = {
|
|
type = "input",
|
|
width = WeakAuras.normalWidth * 2 / 3,
|
|
name = name(option, "step", L["Step Size"]),
|
|
desc = desc(option, "step"),
|
|
order = order(),
|
|
get = getNumAsString(option, "step"),
|
|
set = setNum(data, option, "step")
|
|
}
|
|
|
|
args[prefix .. "softmin"] = {
|
|
type = "input",
|
|
width = WeakAuras.normalWidth * 2 / 3,
|
|
name = name(option, "softMin", L["Soft Min"]),
|
|
desc = desc(option, "softMin"),
|
|
order = order(),
|
|
get = getNumAsString(option, "softMin"),
|
|
set = setNum(data, option, "softMin")
|
|
}
|
|
|
|
args[prefix .. "softmax"] = {
|
|
type = "input",
|
|
width = WeakAuras.normalWidth * 2 / 3,
|
|
name = name(option, "softMax", L["Soft Max"]),
|
|
desc = desc(option, "softMax"),
|
|
order = order(),
|
|
get = getNumAsString(option, "softMax"),
|
|
set = setNum(data, option, "softMax")
|
|
}
|
|
|
|
args[prefix .. "bigstep"] = {
|
|
type = "input",
|
|
width = WeakAuras.normalWidth * 2 / 3,
|
|
name = name(option, "bigStep", L["Slider Step Size"]),
|
|
desc = desc(option, "bigStep"),
|
|
order = order(),
|
|
get = getNumAsString(option, "bigStep"),
|
|
set = setNum(data, option, "bigStep")
|
|
}
|
|
end,
|
|
description = function(options, args, data, order, prefix, i)
|
|
local option = options[i]
|
|
args[prefix .. "key"] = nil
|
|
args[prefix .. "name"] = nil
|
|
args[prefix .. "fontsize"] = {
|
|
type = "select",
|
|
width = WeakAuras.normalWidth,
|
|
name = name(option, "fontSize", L["Font Size"]),
|
|
desc = desc(option, "fontSize"),
|
|
order = order(),
|
|
values = OptionsPrivate.Private.font_sizes,
|
|
get = get(option, "fontSize"),
|
|
set = set(data, option, "fontSize")
|
|
}
|
|
args[prefix .. "descinput"] = {
|
|
type = "input",
|
|
width = WeakAuras.doubleWidth,
|
|
name = name(option, "text", L["Description Text"]),
|
|
desc = desc(option, "text"),
|
|
order = order(),
|
|
multiline = true,
|
|
get = getStr(option, "text"),
|
|
set = setStr(data, option, "text")
|
|
}
|
|
end,
|
|
color = function(options, args, data, order, prefix, i)
|
|
local option = options[i]
|
|
args[prefix .. "default"] = {
|
|
type = "color",
|
|
width = WeakAuras.normalWidth,
|
|
hasAlpha = true,
|
|
name = name(option, "default", L["Default"]),
|
|
desc = descColor(option, "default"),
|
|
order = order(),
|
|
get = getColor(option, "default"),
|
|
set = setColor(data, option, "default")
|
|
}
|
|
end,
|
|
select = function(options, args, data, order, prefix, i)
|
|
local option = options[i]
|
|
local values = getValues(option)
|
|
local defaultValues = {}
|
|
for i, v in ipairs(values) do
|
|
if v == conflict then
|
|
defaultValues[i] = conflictBlue .. L["Value %i"]:format(i)
|
|
else
|
|
defaultValues[i] = v
|
|
end
|
|
end
|
|
args[prefix .. "default"] = {
|
|
type = "select",
|
|
width = WeakAuras.normalWidth,
|
|
name = name(option, "default", L["Default"]),
|
|
desc = desc(option, "default"),
|
|
order = order(),
|
|
values = defaultValues,
|
|
get = get(option, "default"),
|
|
set = setSelectDefault(data, option)
|
|
}
|
|
for j, value in ipairs(values) do
|
|
args[prefix .. "space" .. j] = {
|
|
type = "toggle",
|
|
width = WeakAuras.normalWidth,
|
|
name = L["Value %i"]:format(j),
|
|
order = order(),
|
|
disabled = function()
|
|
return true
|
|
end,
|
|
get = function()
|
|
return true
|
|
end,
|
|
set = function()
|
|
end
|
|
}
|
|
args[prefix .. "value" .. j] = {
|
|
type = "input",
|
|
width = WeakAuras.normalWidth - 0.15,
|
|
name = (value == conflict and conflictBlue or "") .. L["Value %i"]:format(j),
|
|
desc = descSelect(option, j),
|
|
order = order(),
|
|
get = function()
|
|
if value ~= conflict then
|
|
return value:gsub("|", "||")
|
|
end
|
|
end,
|
|
set = function(_, value)
|
|
value = value:gsub("||", "|")
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
local insertPoint = math.min(j, #childOption.values + 1)
|
|
if value == "" then
|
|
tremove(childOption.values, insertPoint)
|
|
else
|
|
childOption.values[insertPoint] = value
|
|
end
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
}
|
|
args[prefix .. "valdelete" .. j] = {
|
|
type = "execute",
|
|
width = 0.15,
|
|
name = L["Delete"],
|
|
order = order(),
|
|
func = function()
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
tremove(childOption.values, j)
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end,
|
|
image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\delete",
|
|
imageWidth = 24,
|
|
imageHeight = 24,
|
|
control = "WeakAurasIcon"
|
|
}
|
|
end
|
|
args[prefix .. "newvaluespace"] = {
|
|
type = "toggle",
|
|
width = WeakAuras.normalWidth,
|
|
name = L["New Value"],
|
|
order = order(),
|
|
disabled = function()
|
|
return true
|
|
end,
|
|
get = function()
|
|
return true
|
|
end,
|
|
set = function()
|
|
end
|
|
}
|
|
args[prefix .. "newvalue"] = {
|
|
type = "input",
|
|
width = WeakAuras.normalWidth,
|
|
name = L["New Value"],
|
|
order = order(),
|
|
get = function()
|
|
return ""
|
|
end,
|
|
set = function(_, value)
|
|
value = value:gsub("||", "|")
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
childOption.values[#childOption.values + 1] = value
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
}
|
|
end,
|
|
space = function(options, args, data, order, prefix, i)
|
|
local option = options[i]
|
|
-- this option should be just useWidth but no need to do a migration in the data just for that.
|
|
args[prefix .. "variableWidth"] = {
|
|
type = "toggle",
|
|
width = WeakAuras.normalWidth,
|
|
order = order(),
|
|
name = name(option, "variableWidth", L["Width"]),
|
|
desc = desc(
|
|
option,
|
|
"variableWidth",
|
|
L["If unchecked, then this space will fill the entire line it is on in User Mode."]
|
|
),
|
|
get = get(option, "variableWidth"),
|
|
set = set(data, option, "variableWidth")
|
|
}
|
|
args[prefix .. "widthSpace"] = nil
|
|
|
|
local widthOption = args[prefix .. "width"]
|
|
widthOption.name = name(option, "width", L["Width"])
|
|
widthOption.disabled = function()
|
|
return not option.variableWidth
|
|
end
|
|
widthOption.order = order()
|
|
|
|
args[prefix .. "useHeight"] = {
|
|
type = "toggle",
|
|
width = WeakAuras.normalWidth,
|
|
order = order(),
|
|
name = name(option, "useHeight", L["Height"]),
|
|
desc = desc(option, "useHeight", L["If checked, then this space will span across multiple lines."]),
|
|
get = get(option, "useHeight"),
|
|
set = set(data, option, "useHeight")
|
|
}
|
|
|
|
args[prefix .. "height"] = {
|
|
type = "range",
|
|
control = "WeakAurasSpinBox",
|
|
width = WeakAuras.normalWidth,
|
|
order = order(),
|
|
name = name(option, "height", L["Height"]),
|
|
desc = desc(option, "height"),
|
|
get = get(option, "height"),
|
|
set = set(data, option, "height"),
|
|
disabled = function()
|
|
return not option.useHeight
|
|
end,
|
|
min = 1,
|
|
softMax = 10,
|
|
step = 1
|
|
}
|
|
end,
|
|
media = function(options, args, data, order, prefix, i)
|
|
local option = options[i]
|
|
args[prefix .. "mediaType"] = {
|
|
type = "select",
|
|
width = WeakAuras.normalWidth,
|
|
name = name(option, "mediaType", L["Media Type"]),
|
|
desc = desc(option, "mediaType"),
|
|
values = OptionsPrivate.Private.shared_media_types,
|
|
order = order(),
|
|
get = get(option, "mediaType"),
|
|
set = function(_, value)
|
|
for _, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
childOption.mediaType = value
|
|
childOption.default = OptionsPrivate.Private.author_option_media_defaults[value]
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
}
|
|
args[prefix .. "default"] = {
|
|
type = "select",
|
|
width = WeakAuras.doubleWidth,
|
|
name = name(option, "default", L["Default"]),
|
|
desc = desc(option, "default"),
|
|
values = function()
|
|
if option.mediaType == "sound" then
|
|
return OptionsPrivate.Private.sound_file_types
|
|
else
|
|
return AceGUIWidgetLSMlists[option.mediaType]
|
|
end
|
|
end,
|
|
sorting = function()
|
|
if option.mediaType == "sound" then
|
|
return OptionsPrivate.Private.SortOrderForValues(OptionsPrivate.Private.sound_file_types)
|
|
else
|
|
return nil
|
|
end
|
|
end,
|
|
dialogControl = OptionsPrivate.Private.author_option_media_controls[option.mediaType],
|
|
itemControl = OptionsPrivate.Private.author_option_media_itemControls[option.mediaType],
|
|
order = order(),
|
|
get = get(option, "default"),
|
|
set = function(_, value)
|
|
if option.mediaType == "sound" then
|
|
-- do this outside the deref loop, so we don't play the sound a million times
|
|
PlaySoundFile(value, "Master")
|
|
end
|
|
for _, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
childOption.default = value
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
}
|
|
end,
|
|
multiselect = function(options, args, data, order, prefix, i)
|
|
local option = options[i]
|
|
local values = getValues(option)
|
|
local defaultValues = {}
|
|
for i, v in ipairs(values) do
|
|
if v == conflict then
|
|
defaultValues[i] = conflictBlue .. L["Value %i"]:format(i)
|
|
else
|
|
defaultValues[i] = v
|
|
end
|
|
end
|
|
args[prefix .. "default"] = {
|
|
type = "multiselect",
|
|
width = WeakAuras.normalWidth * 0.9,
|
|
name = L["Default"],
|
|
order = order(),
|
|
values = defaultValues,
|
|
get = function(_, k)
|
|
return option.default and option.default[k]
|
|
end,
|
|
set = function(_, k, v)
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
childOption.default[k] = v
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
}
|
|
for j, value in ipairs(values) do
|
|
args[prefix .. "space" .. j] = {
|
|
type = "toggle",
|
|
width = WeakAuras.normalWidth,
|
|
name = L["Value %i"]:format(j),
|
|
order = order(),
|
|
disabled = function()
|
|
return true
|
|
end,
|
|
get = function()
|
|
return true
|
|
end,
|
|
set = function()
|
|
end
|
|
}
|
|
args[prefix .. "value" .. j] = {
|
|
type = "input",
|
|
width = WeakAuras.normalWidth - 0.15,
|
|
name = (value == conflict and conflictBlue or "") .. L["Value %i"]:format(j),
|
|
desc = descSelect(option, j),
|
|
order = order(),
|
|
get = function()
|
|
if value ~= conflict then
|
|
return value:gsub("|", "||")
|
|
end
|
|
end,
|
|
set = function(_, value)
|
|
value = value:gsub("||", "|")
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
local insertPoint = math.min(j, #childOption.values + 1)
|
|
if value == "" then
|
|
tremove(childOption.values, insertPoint)
|
|
tremove(childOption.default, insertPoint)
|
|
else
|
|
childOption.values[insertPoint] = value
|
|
end
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
}
|
|
args[prefix .. "valdelete" .. j] = {
|
|
type = "execute",
|
|
width = 0.15,
|
|
name = "",
|
|
order = order(),
|
|
func = function()
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
tremove(childOption.values, j)
|
|
tremove(childOption.default, j)
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end,
|
|
image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\delete",
|
|
imageWidth = 24,
|
|
imageHeight = 24
|
|
}
|
|
end
|
|
args[prefix .. "newvaluespace"] = {
|
|
type = "toggle",
|
|
width = WeakAuras.normalWidth,
|
|
name = L["New Value"],
|
|
order = order(),
|
|
disabled = function()
|
|
return true
|
|
end,
|
|
get = function()
|
|
return true
|
|
end,
|
|
set = function()
|
|
end
|
|
}
|
|
args[prefix .. "newvalue"] = {
|
|
type = "input",
|
|
width = WeakAuras.normalWidth,
|
|
name = L["New Value"],
|
|
order = order(),
|
|
get = function()
|
|
return ""
|
|
end,
|
|
set = function(_, value)
|
|
value = value:gsub("||", "|")
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
childOption.values[#childOption.values + 1] = value
|
|
childOption.default[#childOption.default + 1] = false
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
}
|
|
end,
|
|
header = function(options, args, data, order, prefix, i)
|
|
local option = options[i]
|
|
args[prefix .. "width"] = nil
|
|
args[prefix .. "useName"] = {
|
|
type = "toggle",
|
|
name = name(option, "useName", L["Separator text"]),
|
|
desc = desc(
|
|
option,
|
|
"useName",
|
|
L["If checked, then this separator will include text. Otherwise, it will be just a horizontal line."]
|
|
),
|
|
order = order(),
|
|
width = WeakAuras.normalWidth,
|
|
get = get(option, "useName"),
|
|
set = set(data, option, "useName")
|
|
}
|
|
args[prefix .. "text"] = {
|
|
type = "input",
|
|
name = name(option, "text", L["Separator Text"]),
|
|
desc = desc(option, "text"),
|
|
order = order(),
|
|
width = WeakAuras.normalWidth,
|
|
get = getStr(option, "text"),
|
|
set = setStr(data, option, "text"),
|
|
disabled = function()
|
|
return not option.useName
|
|
end
|
|
}
|
|
end,
|
|
group = function(options, args, data, order, prefix, i)
|
|
local option = options[i]
|
|
args[prefix .. "width"] = nil
|
|
args[prefix .. "groupType"] = {
|
|
type = "select",
|
|
name = name(option, "groupType", L["Group Type"]),
|
|
order = order(),
|
|
width = WeakAuras.doubleWidth,
|
|
values = OptionsPrivate.Private.group_option_types,
|
|
get = get(option, "groupType"),
|
|
set = function(_, value)
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
childOption.groupType = value
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
}
|
|
args[prefix .. "useCollapse"] = {
|
|
type = "toggle",
|
|
name = name(option, "useCollapse", L["Collapsible Group"]),
|
|
desc = desc(option, "useCollapse", L["If checked, then this option group can be temporarily collapsed by the user."]),
|
|
order = order(),
|
|
width = WeakAuras.normalWidth,
|
|
get = get(option, "useCollapse"),
|
|
set = set(data, option, "useCollapse"),
|
|
}
|
|
args[prefix .. "collapseDefault"] = {
|
|
type = "toggle",
|
|
name = name(option, "collapse", L["Start Collapsed"]),
|
|
desc = desc(option, "collapse", L["If checked, then this option group will start collapsed."]),
|
|
order = order(),
|
|
width = WeakAuras.normalWidth,
|
|
get = get(option, "collapse"),
|
|
set = function(_, value)
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
childOption.collapse = value
|
|
OptionsPrivate.SetCollapsed(id, "config", optionData.path, value)
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end,
|
|
disabled = function() return not option.useCollapse end
|
|
}
|
|
args[prefix .. "noMerge"] = {
|
|
type = "toggle",
|
|
name = WeakAuras.newFeatureString .. name(option, "noMerge", L["Prevent Merging"]),
|
|
desc = desc(option, "noMerge", L["If checked, then this group will not merge with other group when selecting multiple auras."]),
|
|
order = order(),
|
|
width = option.groupType =="simple" and WeakAuras.doubleWidth or WeakAuras.normalWidth,
|
|
get = get(option, "noMerge"),
|
|
set = set(data, option, "noMerge"),
|
|
}
|
|
if option.groupType ~="simple" then
|
|
args[prefix .. "sortAlphabetically"] = {
|
|
type = "toggle",
|
|
name = WeakAuras.newFeatureString .. name(option, "sortAlphabetically", L["Sort"]),
|
|
desc = desc(option, "sortAlphabetically", L["If checked, then the combo box in the User settings will be sorted."]),
|
|
order = order(),
|
|
width = WeakAuras.normalWidth,
|
|
get = get(option, "sortAlphabetically"),
|
|
set = set(data, option, "sortAlphabetically"),
|
|
}
|
|
end
|
|
if option.groupType ~="simple" then
|
|
args[prefix .. "limitType"] = {
|
|
type = "select",
|
|
name = name(option, "limitType", L["Number of Entries"]),
|
|
desc = desc(option, "limitType", L["Determines how many entries can be in the table."]),
|
|
order = order(),
|
|
width = WeakAuras.normalWidth,
|
|
values = OptionsPrivate.Private.group_limit_types,
|
|
get = get(option, "limitType"),
|
|
set = function(_, value)
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
if childOption.limitType == "fixed" and childOption.nameSource == -1 and value ~= "fixed" then
|
|
childOption.entryNames = nil
|
|
childOption.nameSource = 0
|
|
end
|
|
childOption.limitType = value
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end,
|
|
}
|
|
args[prefix .. "size"] = {
|
|
type = "range",
|
|
control = "WeakAurasSpinBox",
|
|
name = name(option, "limitType", option.limitType == "max" and L["Entry limit"] or L["Number of Entries"]),
|
|
desc = desc(option, "limitType"),
|
|
order = order(),
|
|
width = WeakAuras.normalWidth,
|
|
min = 1, -- no point in a table with no entries
|
|
softMax = 20, -- 20 people in a mythic raid group
|
|
step = 1,
|
|
get = get(option, "size"),
|
|
set = function(_, value)
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
if childOption.nameSource == -1 then
|
|
if value < childOption.size then
|
|
for i = value + 1, childOption.size do
|
|
childOption.entryNames[i] = nil
|
|
end
|
|
else
|
|
for i = childOption.size + 1, value do
|
|
childOption.entryNames[i] = L["Entry %i"]:format(i)
|
|
end
|
|
end
|
|
end
|
|
childOption.size = value
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end,
|
|
disabled = function() return option.limitType == "none" end,
|
|
}
|
|
args[prefix .. "hideReorder"] = {
|
|
type = "toggle",
|
|
name = name(option, "hideReorder", L["Disallow Entry Reordering"]),
|
|
desc = desc(option, "hideReorder"),
|
|
order = order(),
|
|
width = WeakAuras.normalWidth,
|
|
get = function()
|
|
return option.hideReorder or option.nameSource == -1
|
|
end,
|
|
set = set(data, option, "hideReorder"),
|
|
disabled = function()
|
|
return option.nameSource == -1
|
|
end,
|
|
}
|
|
local nameSources = CopyTable(OptionsPrivate.Private.array_entry_name_types)
|
|
local validNameSourceTypes = OptionsPrivate.Private.name_source_option_types
|
|
if option.limitType ~= "fixed" then
|
|
nameSources[-1] = nil
|
|
end
|
|
for subIndex, subOption in ipairs(option.subOptions) do
|
|
if validNameSourceTypes[subOption.type] then
|
|
local allShareThisOption = true
|
|
for id in pairs(option.references) do
|
|
if not subOption.references[id] then
|
|
allShareThisOption = false
|
|
break
|
|
end
|
|
end
|
|
if allShareThisOption then
|
|
nameSources[subIndex] = subOption.key
|
|
end
|
|
end
|
|
end
|
|
args[prefix .. "nameSource"] = {
|
|
type = "select",
|
|
name = name(option, "nameSource", L["Entry Name Source"]),
|
|
desc = desc(option, "nameSource"),
|
|
order = order(),
|
|
values = nameSources,
|
|
width = WeakAuras.doubleWidth,
|
|
get = function()
|
|
return option.nameSource or 0
|
|
end,
|
|
set = function(_, value)
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
if (value == -1) ~= (childOption.nameSource == -1) then
|
|
if value == -1 then
|
|
local entryNames = {}
|
|
for i = 1, childOption.size do
|
|
entryNames[i] = L["Entry %i"]:format(i)
|
|
end
|
|
childOption.entryNames = entryNames
|
|
else
|
|
childOption.entryNames = nil
|
|
end
|
|
end
|
|
if value > 0 then
|
|
childOption.nameSource = option.subOptions[value].references[id].index
|
|
else
|
|
childOption.nameSource = value
|
|
end
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end,
|
|
}
|
|
if option.nameSource == -1 then
|
|
for i = 1, option.size do
|
|
args[prefix .. "entry" .. i .. "name"] = {
|
|
type = "input",
|
|
name = nameArray(option, "entryNames", i, L["Entry %i"]:format(i)),
|
|
desc = descArray(option, "entryNames", i),
|
|
order = order(),
|
|
width = WeakAuras.doubleWidth,
|
|
get = getArrayStr(option, "entryNames", i),
|
|
set = setArrayStr(data, option, "entryNames", i),
|
|
}
|
|
end
|
|
end
|
|
end
|
|
args[prefix .. "groupStart"] = {
|
|
type = "header",
|
|
name = L["Start of %s"]:format(option.name),
|
|
order = order()
|
|
}
|
|
local subPrefix = prefix .. "option"
|
|
for subIndex, subOption in ipairs(option.subOptions) do
|
|
local addControlsForType = typeControlAdders[subOption.type]
|
|
if addControlsForType then
|
|
addAuthorModeOption(option.subOptions, args, data, order, subPrefix .. subIndex, subIndex)
|
|
end
|
|
end
|
|
args[prefix .. "addSubOption"] = {
|
|
type = "execute",
|
|
name = L["Add Sub Option"],
|
|
order = order(),
|
|
width = WeakAuras.normalWidth,
|
|
func = function()
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
local path = optionData.path
|
|
local j = #childOption.subOptions + 1
|
|
path[#path + 1] = j
|
|
childOption.subOptions[j] = {
|
|
type = "toggle",
|
|
key = generateKey("subOption", childOption.subOptions, j),
|
|
name = L["Sub Option %i"]:format(j),
|
|
default = false,
|
|
width = 1,
|
|
useDesc = false,
|
|
}
|
|
OptionsPrivate.SetCollapsed(id, "author", path, false)
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
}
|
|
args[prefix .. "groupEnd"] = {
|
|
type = "header",
|
|
name = L["End of %s"]:format(option.name),
|
|
order = order()
|
|
}
|
|
end
|
|
}
|
|
|
|
local function up(data, options, index)
|
|
local option = options[index]
|
|
return function()
|
|
for id, optionData in pairs(option.references) do
|
|
if optionData.path[#optionData.path] <= 1 then
|
|
return true
|
|
end
|
|
end
|
|
end, function()
|
|
for id, optionData in pairs(option.references) do
|
|
-- move the option up in the subOptions
|
|
local path = optionData.path
|
|
local optionID = optionData.index
|
|
local childData = optionData.data
|
|
local childOptions = optionData.options
|
|
local parent = optionData.parent
|
|
if parent and parent.groupType == "array" then
|
|
local dereferencedParent = parent.references[id].options[parent.references[id].index]
|
|
if dereferencedParent.nameSource == optionID then
|
|
dereferencedParent.nameSource = optionID - 1
|
|
elseif dereferencedParent.nameSource == optionID - 1 then
|
|
dereferencedParent.nameSource = optionID
|
|
end
|
|
end
|
|
OptionsPrivate.MoveCollapseDataUp(id, "author", path)
|
|
childOptions[optionID], childOptions[optionID - 1] = childOptions[optionID - 1], childOptions[optionID]
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
end
|
|
|
|
local function down(data, options, index)
|
|
local option = options[index]
|
|
return function()
|
|
for id, optionData in pairs(option.references) do
|
|
if optionData.path[#optionData.path] >= #optionData.options then
|
|
return true
|
|
end
|
|
end
|
|
end, function()
|
|
for id, optionData in pairs(option.references) do
|
|
-- move the option down in the subOptions
|
|
local path = optionData.path
|
|
local optionID = optionData.index
|
|
local childData = optionData.data
|
|
local parent = optionData.parent
|
|
if parent and parent.groupType == "array" then
|
|
local dereferencedParent = parent.references[id].options[parent.references[id].index]
|
|
if dereferencedParent.nameSource == optionID then
|
|
dereferencedParent.nameSource = optionID + 1
|
|
elseif dereferencedParent.nameSource == optionID + 1 then
|
|
dereferencedParent.nameSource = optionID
|
|
end
|
|
end
|
|
local childOptions = optionData.options
|
|
OptionsPrivate.MoveCollapseDataDown(id, "author", path)
|
|
childOptions[optionID], childOptions[optionID + 1] = childOptions[optionID + 1], childOptions[optionID]
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
end
|
|
|
|
local function duplicate(data, options, index)
|
|
local option = options[index]
|
|
return function()
|
|
for id, optionData in pairs(option.references) do
|
|
local optionID = optionData.index
|
|
local childOptions = optionData.options
|
|
local childData = optionData.data
|
|
local path = optionData.path
|
|
path[#path] = path[#path] + 1 -- this data is being regenerated very soon
|
|
OptionsPrivate.InsertCollapsed(id, "author", optionData.path, false)
|
|
local newOption = CopyTable(childOptions[optionID])
|
|
if newOption.key then
|
|
local existingKeys = {}
|
|
for _, option in ipairs(childOptions) do
|
|
if option.key then
|
|
existingKeys[option.key] = true
|
|
end
|
|
end
|
|
while existingKeys[newOption.key] do
|
|
newOption.key = generateKey(newOption.key .. "copy", childOptions, 1)
|
|
end
|
|
end
|
|
if newOption.name then
|
|
newOption.name = newOption.name .. " - " .. L["Copy"]
|
|
end
|
|
tinsert(childOptions, optionID + 1, newOption)
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
end
|
|
|
|
local function validateNonDuplicateKey(option)
|
|
-- note: this has some unintuitive behavior
|
|
-- e.g. if aura A has option keys "foo", "bar"
|
|
-- and aura B has option keys "foo", "baz",
|
|
-- then you still cannot change the merged option with key "foo" to "bar"
|
|
-- unless you unselect aura A, even though aura B would be fine with that.
|
|
return function(_, newKey)
|
|
for id, optionData in pairs(option.references) do
|
|
for index, otherOption in ipairs(optionData.options) do
|
|
if index ~= optionData.index and otherOption.key == newKey then
|
|
return L["%s - Option #%i has the key %s. Please choose a different option key."]:format(id, index, newKey)
|
|
end
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
end
|
|
|
|
function addAuthorModeOption(options, args, data, order, prefix, i)
|
|
-- add header controls
|
|
local option = options[i]
|
|
|
|
local collapsed = false
|
|
for id, optionData in pairs(option.references) do
|
|
if OptionsPrivate.IsCollapsed(id, "author", optionData.path, true) then
|
|
collapsed = true
|
|
break
|
|
end
|
|
end
|
|
|
|
local _, optionData = next(option.references)
|
|
local isInGroup = optionData.parent ~= nil
|
|
local buttonWidth = 0.6
|
|
if isInGroup then
|
|
buttonWidth = buttonWidth + 0.3
|
|
end
|
|
|
|
local optionBelow = options[i + 1]
|
|
local isAboveGroup = optionBelow and OptionsPrivate.Private.author_option_classes[optionBelow.type] == "group"
|
|
if isAboveGroup then
|
|
buttonWidth = buttonWidth + 0.15
|
|
end
|
|
|
|
local optionAbove = options[i - 1]
|
|
local isBelowGroup = optionAbove and OptionsPrivate.Private.author_option_classes[optionAbove.type] == "group"
|
|
if isBelowGroup then
|
|
buttonWidth = buttonWidth + 0.15
|
|
end
|
|
local optionClass = OptionsPrivate.Private.author_option_classes[option.type]
|
|
local optionName = optionClass == "noninteractive" and OptionsPrivate.Private.author_option_types[option.type]
|
|
or option.name
|
|
|
|
args[prefix .. "collapse"] = {
|
|
type = "execute",
|
|
name = nameHead(data, option, optionName),
|
|
order = order(),
|
|
width = WeakAuras.doubleWidth - buttonWidth,
|
|
func = function()
|
|
for id, optionData in pairs(option.references) do
|
|
OptionsPrivate.SetCollapsed(id, "author", optionData.path, not collapsed)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end,
|
|
image = collapsed and "Interface\\AddOns\\WeakAuras\\Media\\Textures\\expand" or
|
|
"Interface\\AddOns\\WeakAuras\\Media\\Textures\\collapse",
|
|
imageWidth = 18,
|
|
imageHeight = 18,
|
|
control = "WeakAurasExpand"
|
|
}
|
|
|
|
args[prefix .. "upAndIn"] = {
|
|
type = "execute",
|
|
width = 0.15,
|
|
name = L["Move Into Above Group"],
|
|
order = order(),
|
|
hidden = function() return not isBelowGroup end,
|
|
func = function()
|
|
for id, optionData in pairs(option.references) do
|
|
local groupData = optionAbove.references[id]
|
|
if groupData then
|
|
local childGroup = groupData.options[groupData.index]
|
|
local childCollapsed = OptionsPrivate.IsCollapsed(id, "author", optionData.path, true)
|
|
OptionsPrivate.RemoveCollapsed(id, "author", optionData.path)
|
|
local newPath = groupData.path
|
|
tinsert(newPath, #childGroup.subOptions + 1)
|
|
OptionsPrivate.InsertCollapsed(id, "author", newPath, childCollapsed)
|
|
local childOption = tremove(optionData.options, optionData.index)
|
|
childOption.key = ensureUniqueKey(childOption.key, "In", childGroup.subOptions)
|
|
local childData = optionData.data
|
|
tinsert(childGroup.subOptions, childOption)
|
|
WeakAuras.Add(childData)
|
|
end
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end,
|
|
image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\upright",
|
|
imageWidth = 24,
|
|
imageHeight = 24,
|
|
control = "WeakAurasIcon"
|
|
}
|
|
args[prefix .. "downAndIn"] = {
|
|
type = "execute",
|
|
width = 0.15,
|
|
name = L["Move Into Below Group"],
|
|
order = order(),
|
|
hidden = function() return not isAboveGroup end,
|
|
func = function()
|
|
for id, optionData in pairs(option.references) do
|
|
local groupData = optionBelow.references[id]
|
|
if groupData then
|
|
local childGroup = groupData.options[groupData.index]
|
|
local childCollapsed = OptionsPrivate.IsCollapsed(id, "author", optionData.path, true)
|
|
OptionsPrivate.RemoveCollapsed(id, "author", optionData.path)
|
|
local newPath = groupData.path
|
|
tinsert(newPath, 1)
|
|
OptionsPrivate.InsertCollapsed(id, "author", newPath, childCollapsed)
|
|
local childOption = tremove(optionData.options, optionData.index)
|
|
childOption.key = ensureUniqueKey(childOption.key, "In", childGroup.subOptions)
|
|
local childData = optionData.data
|
|
tinsert(childGroup.subOptions, 1, childOption)
|
|
WeakAuras.Add(childData)
|
|
end
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end,
|
|
image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\downright",
|
|
imageWidth = 24,
|
|
imageHeight = 24,
|
|
control = "WeakAurasIcon"
|
|
}
|
|
|
|
args[prefix .. "upAndOut"] = {
|
|
type = "execute",
|
|
width = 0.15,
|
|
name = L["Move Above Group"],
|
|
order = order(),
|
|
hidden = function() return not isInGroup end,
|
|
func = function()
|
|
for id, optionData in pairs(option.references) do
|
|
local path = optionData.path
|
|
local parent = optionData.parent
|
|
local parentOptions = parent and parent.references[id].options or optionData.data.authorOptions
|
|
local childOption = tremove(optionData.options, optionData.index)
|
|
if parent and parent.groupType == "array" then
|
|
local dereferencedParent = parent.references[id].options[parent.references[id].index]
|
|
if dereferencedParent.nameSource == optionData.index then
|
|
dereferencedParent.nameSource = 0
|
|
elseif dereferencedParent.nameSource > optionData.index then
|
|
dereferencedParent.nameSource = dereferencedParent.nameSource - 1
|
|
end
|
|
end
|
|
OptionsPrivate.RemoveCollapsed(id, "author", optionData.path)
|
|
childOption.key = ensureUniqueKey(childOption.key, "Out", parentOptions)
|
|
tinsert(parentOptions, path[#path - 1], childOption)
|
|
path[#path] = nil
|
|
OptionsPrivate.InsertCollapsed(id, "author", path)
|
|
WeakAuras.Add(optionData.data)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end,
|
|
image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\upleft",
|
|
imageWidth = 24,
|
|
imageHeight = 24,
|
|
control = "WeakAurasIcon"
|
|
}
|
|
args[prefix .. "downAndOut"] = {
|
|
type = "execute",
|
|
width = 0.15,
|
|
name = L["Move Below Group"],
|
|
order = order(),
|
|
hidden = function() return not isInGroup end,
|
|
func = function()
|
|
for id, optionData in pairs(option.references) do
|
|
local path = optionData.path
|
|
local parent = optionData.parent
|
|
local parentOptions = parent and parent.references[id].options or optionData.data.authorOptions
|
|
local childOption = tremove(optionData.options, optionData.index)
|
|
if parent and parent.groupType == "array" then
|
|
local dereferencedParent = parent.references[id].options[parent.references[id].index]
|
|
if dereferencedParent.nameSource == optionData.index then
|
|
dereferencedParent.nameSource = 0
|
|
elseif dereferencedParent.nameSource > optionData.index then
|
|
dereferencedParent.nameSource = dereferencedParent.nameSource - 1
|
|
end
|
|
end
|
|
OptionsPrivate.RemoveCollapsed(id, "author", optionData.path)
|
|
childOption.key = ensureUniqueKey(childOption.key, "Out", parentOptions)
|
|
tinsert(parentOptions, path[#path - 1] + 1, childOption)
|
|
path[#path] = nil
|
|
path[#path] = path[#path] + 1
|
|
OptionsPrivate.InsertCollapsed(id, "author", path)
|
|
WeakAuras.Add(optionData.data)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end,
|
|
image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\downleft",
|
|
imageWidth = 24,
|
|
imageHeight = 24,
|
|
control = "WeakAurasIcon"
|
|
}
|
|
local upDisable, upFunc = up(data, options, i)
|
|
args[prefix .. "up"] = {
|
|
type = "execute",
|
|
width = 0.15,
|
|
name = L["Move Up"],
|
|
order = order(),
|
|
disabled = upDisable,
|
|
func = upFunc,
|
|
image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\moveup",
|
|
imageWidth = 24,
|
|
imageHeight = 24,
|
|
control = "WeakAurasIcon"
|
|
}
|
|
|
|
local downDisable, downFunc = down(data, options, i)
|
|
args[prefix .. "down"] = {
|
|
type = "execute",
|
|
width = 0.15,
|
|
name = L["Move Down"],
|
|
order = order(),
|
|
disabled = downDisable,
|
|
func = downFunc,
|
|
image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\movedown",
|
|
imageWidth = 24,
|
|
imageHeight = 24,
|
|
control = "WeakAurasIcon"
|
|
}
|
|
|
|
args[prefix .. "duplicate"] = {
|
|
type = "execute",
|
|
width = 0.15,
|
|
name = L["Duplicate"],
|
|
order = order(),
|
|
func = duplicate(data, options, i),
|
|
image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\duplicate",
|
|
imageWidth = 24,
|
|
imageHeight = 24,
|
|
control = "WeakAurasIcon"
|
|
}
|
|
|
|
args[prefix .. "delete"] = {
|
|
type = "execute",
|
|
width = 0.15,
|
|
name = L["Delete"],
|
|
order = order(),
|
|
func = function()
|
|
for id, optionData in pairs(option.references) do
|
|
local childOptions = optionData.options
|
|
local optionIndex = optionData.index
|
|
local childData = optionData.data
|
|
local parent = optionData.parent
|
|
OptionsPrivate.RemoveCollapsed(id, "author", optionData.path)
|
|
tremove(childOptions, optionIndex)
|
|
if parent and parent.groupType == "array" then
|
|
local dereferencedParent = parent.references[id].options[parent.references[id].index]
|
|
if dereferencedParent.nameSource == optionData.index then
|
|
dereferencedParent.nameSource = 0
|
|
elseif dereferencedParent.nameSource > optionData.index then
|
|
dereferencedParent.nameSource = dereferencedParent.nameSource - 1
|
|
end
|
|
end
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end,
|
|
image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\delete",
|
|
imageWidth = 24,
|
|
imageHeight = 24,
|
|
control = "WeakAurasIcon"
|
|
}
|
|
|
|
if collapsed then return end
|
|
|
|
args[prefix .. "type"] = {
|
|
type = "select",
|
|
width = WeakAuras.doubleWidth,
|
|
name = L["Option Type"],
|
|
desc = descType(option),
|
|
order = order(),
|
|
values = OptionsPrivate.Private.author_option_types,
|
|
get = get(option, "type"),
|
|
set = function(_, value)
|
|
if value == option.type then
|
|
return
|
|
end
|
|
local author_option_fields = OptionsPrivate.Private.author_option_fields
|
|
local commonFields, newFields = author_option_fields.common, author_option_fields[value]
|
|
local newClass = OptionsPrivate.Private.author_option_classes[value]
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
local parentOption = optionData.parent
|
|
for k in pairs(childOption) do
|
|
if not commonFields[k] then
|
|
childOption[k] = nil
|
|
end
|
|
end
|
|
for k, v in pairs(newFields) do
|
|
if type(v) == "table" then
|
|
childOption[k] = CopyTable(v)
|
|
else
|
|
childOption[k] = v
|
|
end
|
|
end
|
|
childOption.type = value
|
|
if newClass == "noninteractive" then
|
|
childOption.name = nil
|
|
childOption.desc = nil
|
|
childOption.key = nil
|
|
childOption.useDesc = nil
|
|
childOption.default = nil
|
|
else
|
|
-- don't use the option index here if switching from a noninteractive type
|
|
-- mostly because it would have a very non-intuitive effect
|
|
-- the names and keys would likely not match anymore, and so
|
|
-- the merged display would basically explode into a bunch of separate options
|
|
childOption.name = childOption.name or (L["Option %i"]):format(i)
|
|
if not childOption.key then
|
|
local newKey = "option" .. i
|
|
local existingKeys = {}
|
|
for index, option in pairs(optionData.options) do
|
|
if index ~= optionData.index and option.key then
|
|
existingKeys[option.key] = true
|
|
end
|
|
end
|
|
while existingKeys[newKey] do
|
|
newKey = newKey .. "copy"
|
|
end
|
|
childOption.key = newKey
|
|
end
|
|
end
|
|
if parentOption and parentOption.groupType == "array" and not OptionsPrivate.Private.array_entry_name_types[value] then
|
|
local dereferencedParent = parentOption.references[id]
|
|
if dereferencedParent.nameSource == optionData.index then
|
|
dereferencedParent.nameSource = 0
|
|
end
|
|
end
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
}
|
|
|
|
if optionClass ~= "noninteractive" then
|
|
args[prefix .. "name"] = {
|
|
type = "input",
|
|
width = WeakAuras.normalWidth,
|
|
name = name(option, "name", L["Display Name"]),
|
|
desc = desc(option, "name"),
|
|
order = order(),
|
|
get = getStr(option, "name"),
|
|
set = setStr(data, option, "name")
|
|
}
|
|
|
|
args[prefix .. "key"] = {
|
|
type = "input",
|
|
width = WeakAuras.normalWidth,
|
|
name = name(option, "key", optionClass == "group" and L["Group key"] or L["Option key"]),
|
|
order = order(),
|
|
validate = validateNonDuplicateKey(option),
|
|
get = get(option, "key"),
|
|
set = set(data, option, "key")
|
|
}
|
|
end
|
|
|
|
if optionClass == "simple" then
|
|
args[prefix .. "tooltipSpace"] = {
|
|
type = "description",
|
|
width = WeakAuras.doubleWidth,
|
|
name = "",
|
|
order = order
|
|
}
|
|
args[prefix .. "usetooltip"] = {
|
|
type = "toggle",
|
|
name = name(option, "useDesc", L["Tooltip"]),
|
|
order = order(),
|
|
width = WeakAuras.halfWidth,
|
|
get = get(option, "useDesc"),
|
|
set = set(data, option, "useDesc")
|
|
}
|
|
args[prefix .. "tooltip"] = {
|
|
type = "input",
|
|
name = name(option, "desc", L["Tooltip Text"]),
|
|
desc = desc(option, "desc"),
|
|
order = order(),
|
|
width = WeakAuras.normalWidth * 1.5,
|
|
get = getStr(option, "desc"),
|
|
set = setStr(data, option, "desc"),
|
|
disabled = function()
|
|
return not option.useDesc
|
|
end
|
|
}
|
|
end
|
|
|
|
args[prefix .. "width"] = {
|
|
type = "range",
|
|
control = "WeakAurasSpinBox",
|
|
width = WeakAuras.normalWidth,
|
|
name = name(option, "width", L["Width"]),
|
|
desc = desc(option, "width"),
|
|
order = order(),
|
|
min = 0.1,
|
|
max = 2,
|
|
step = 0.05,
|
|
get = get(option, "width"),
|
|
set = set(data, option, "width")
|
|
}
|
|
|
|
local addControlsForType = typeControlAdders[option.type]
|
|
if addControlsForType then
|
|
addControlsForType(options, args, data, order, prefix, i)
|
|
end
|
|
end
|
|
|
|
local groupPages = {}
|
|
|
|
local function getPage(id, path, max)
|
|
max = max or math.huge
|
|
groupPages[id] = groupPages[id] or {}
|
|
local base = groupPages[id]
|
|
for _, index in ipairs(path) do
|
|
if not base[index] then
|
|
base[index] = {}
|
|
end
|
|
base = base[index]
|
|
end
|
|
if not base.page or (max and base.page > max) then
|
|
base.page = 1
|
|
end
|
|
return base.page
|
|
end
|
|
|
|
local function setPage(id, path, page)
|
|
groupPages[id] = groupPages[id] or {}
|
|
local base = groupPages[id]
|
|
for _, index in ipairs(path) do
|
|
if not base[index] then
|
|
base[index] = {}
|
|
end
|
|
base = base[index]
|
|
end
|
|
base.page = page
|
|
end
|
|
|
|
local function addUserModeOption(options, args, data, order, prefix, i)
|
|
local option = options[i]
|
|
local optionType = option.type
|
|
local optionClass = OptionsPrivate.Private.author_option_classes[optionType]
|
|
local userOption
|
|
|
|
if optionClass == "simple" then
|
|
userOption = {
|
|
type = optionType,
|
|
name = nameUser(option),
|
|
desc = descUser(option),
|
|
width = (option.width or 1) * WeakAuras.normalWidth,
|
|
order = order(),
|
|
get = getUser(option),
|
|
set = setUser(data, option)
|
|
}
|
|
elseif optionClass == "noninteractive" then
|
|
userOption = {
|
|
type = "description",
|
|
order = order(),
|
|
name = "",
|
|
width = (option.width or 1) * WeakAuras.normalWidth
|
|
}
|
|
elseif optionClass == "group" then
|
|
local collapsed = false
|
|
if option.useCollapse then
|
|
local defaultCollapsed = true
|
|
if option.collapse ~= nil then
|
|
defaultCollapsed = option.collapse
|
|
end
|
|
for id, optionData in pairs(option.references) do
|
|
if OptionsPrivate.IsCollapsed(id, "config", optionData.path, defaultCollapsed) then
|
|
collapsed = true
|
|
break
|
|
end
|
|
end
|
|
args[prefix .. "collapse"] = {
|
|
type = "execute",
|
|
name = option.name,
|
|
order = order(),
|
|
width = WeakAuras.doubleWidth,
|
|
func = function()
|
|
for id, optionData in pairs(option.references) do
|
|
OptionsPrivate.SetCollapsed(id, "config", optionData.path, not collapsed)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end,
|
|
image = collapsed and "Interface\\AddOns\\WeakAuras\\Media\\Textures\\expand" or
|
|
"Interface\\AddOns\\WeakAuras\\Media\\Textures\\collapse",
|
|
imageWidth = 18,
|
|
imageHeight = 18,
|
|
control = "WeakAurasExpand"
|
|
}
|
|
end
|
|
if not collapsed then
|
|
local skipSubOptions = false
|
|
if option.groupType == "array" then
|
|
local values, firstChild = {}, true
|
|
local nameSource = option.nameSource or 0
|
|
if nameSource > 0 then
|
|
nameSource = option.subOptions[option.nameSource].key
|
|
end
|
|
if nameSource == -1 then
|
|
for id, optionData in pairs(option.references) do
|
|
local i = 1
|
|
local childOption = optionData.options[optionData.index]
|
|
local entryNames = childOption.entryNames
|
|
while i <= #values or i <= childOption.size do
|
|
if firstChild then
|
|
values[i] = entryNames[i]
|
|
elseif values[i] ~= entryNames[i] then
|
|
values[i] = conflictBlue .. L["Entry %i"]:format(i)
|
|
end
|
|
i = i + 1
|
|
end
|
|
firstChild = false
|
|
end
|
|
elseif nameSource == 0 then
|
|
for id, optionData in pairs(option.references) do
|
|
local i = 1
|
|
local childOption = optionData.options[optionData.index]
|
|
local childValues = optionData.config[childOption.key]
|
|
while i <= #values or i <= #childValues do
|
|
if firstChild then
|
|
values[i] = L["Entry %i"]:format(i)
|
|
elseif values[i] == nil or childValues[i] == nil then
|
|
values[i] = conflictBlue .. L["Entry %i"]:format(i)
|
|
end
|
|
i = i + 1
|
|
end
|
|
firstChild = false
|
|
end
|
|
else
|
|
for id, optionData in pairs(option.references) do
|
|
local i = 1
|
|
local childOption = optionData.options[optionData.index]
|
|
local childValues = optionData.config[option.key]
|
|
while i <= #values or i <= #childValues do
|
|
if firstChild then
|
|
values[i] = childValues[i][nameSource] or conflictBlue .. L["Entry %i"]:format(i)
|
|
elseif not childValues[i] or childValues[i][nameSource] ~= values[i] then
|
|
values[i] = conflictBlue .. L["Entry %i"]:format(i)
|
|
end
|
|
i = i + 1
|
|
end
|
|
firstChild = false
|
|
end
|
|
end
|
|
skipSubOptions = #values == 0
|
|
local buttonWidth = 0.75
|
|
if option.limitType == "fixed" then
|
|
buttonWidth = buttonWidth - 0.30
|
|
end
|
|
if option.hideReorder or option.nameSource == -1 then
|
|
buttonWidth = buttonWidth - 0.30
|
|
end
|
|
args[prefix .. "entryChoice"] = {
|
|
type = "select",
|
|
name = nameUser(option),
|
|
order = order(),
|
|
width = WeakAuras.doubleWidth - buttonWidth,
|
|
values = values,
|
|
get = function()
|
|
if skipSubOptions then
|
|
return 1 -- show the "create" prompt, which is at index 1
|
|
end
|
|
local value
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childConfigList = optionData.config[childOption.key]
|
|
if value == nil then
|
|
value = getPage(id, optionData.path, #childConfigList)
|
|
elseif value ~= getPage(id, optionData.path, #childConfigList) then
|
|
return ""
|
|
end
|
|
end
|
|
return value
|
|
end,
|
|
set = function(_, value)
|
|
for id, optionData in pairs(option.references) do
|
|
setPage(id, optionData.path, value) -- XXX: mergeOptions will reset this to the maximum value if it's too big
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end,
|
|
sorting = option.sortAlphabetically and OptionsPrivate.Private.SortOrderForValues(values) or nil
|
|
}
|
|
args[prefix .. "resetEntry"] = {
|
|
type = "execute",
|
|
name = L["Reset Entry"],
|
|
order = order(),
|
|
func = function()
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childData = optionData.data
|
|
local childPage = getPage(id, optionData.path)
|
|
local childConfigList = optionData.config[childOption.key]
|
|
childConfigList[childPage] = {}
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end,
|
|
width = 0.15,
|
|
image = "Interface\\Addons\\WeakAuras\\Media\\Textures\\reset",
|
|
imageWidth = 18,
|
|
imageHeight = 18,
|
|
control = "WeakAurasIcon"
|
|
}
|
|
if option.limitType ~= "fixed" then
|
|
args[prefix .. "createEntry"] = {
|
|
type = "execute",
|
|
name = L["Add Entry"],
|
|
order = order(),
|
|
func = function()
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childConfigList = optionData.config[childOption.key]
|
|
local childData = optionData.data
|
|
if childOption.limitType == "none" or #childConfigList < childOption.size then
|
|
tinsert(childConfigList, {})
|
|
setPage(id, optionData.path, #childConfigList)
|
|
-- we do need to Add here, so that the new entry can get its default values
|
|
WeakAuras.Add(childData)
|
|
end
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end,
|
|
disabled = function()
|
|
if option.limitType == "none" then
|
|
return false
|
|
else
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childConfigList = optionData.config[childOption.key]
|
|
if #childConfigList >= childOption.size then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
end,
|
|
width = 0.15,
|
|
image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\add",
|
|
imageWidth = 18,
|
|
imageHeight = 18,
|
|
control = "WeakAurasIcon"
|
|
}
|
|
args[prefix .. "deleteEntry"] = {
|
|
type = "execute",
|
|
name = L["Delete Entry"],
|
|
order = order(),
|
|
confirm = true,
|
|
func = function()
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childConfigList = optionData.config[childOption.key]
|
|
local childData = optionData.data
|
|
local page = getPage(id, optionData.path)
|
|
if #childConfigList ~= 0 then
|
|
tremove(childConfigList, page)
|
|
setPage(id, optionData.path, min(#childConfigList, page))
|
|
WeakAuras.Add(childData)
|
|
end
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end,
|
|
disabled = function()
|
|
return skipSubOptions
|
|
end,
|
|
width = 0.15,
|
|
image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\delete",
|
|
imageWidth = 18,
|
|
imageHeight = 18,
|
|
control = "WeakAurasIcon"
|
|
}
|
|
end
|
|
if option.nameSource ~= -1 and not option.hideReorder then
|
|
args[prefix .. "moveEntryUp"] = {
|
|
type = "execute",
|
|
name = L["Move Entry Up"],
|
|
order = order(),
|
|
func = function()
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childConfigList = optionData.config[childOption.key]
|
|
local childData = optionData.data
|
|
local childPage = getPage(id, optionData.path, #childConfigList)
|
|
if childConfigList[childPage] then
|
|
childConfigList[childPage], childConfigList[childPage - 1] = childConfigList[childPage - 1], childConfigList[childPage]
|
|
setPage(id, optionData.path, childPage - 1)
|
|
WeakAuras.Add(childData)
|
|
end
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end,
|
|
disabled = function()
|
|
for id, optionData in pairs(option.references) do
|
|
if getPage(id, optionData.path) <= 1 then
|
|
return true
|
|
end
|
|
end
|
|
end,
|
|
width = 0.15,
|
|
image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\moveup",
|
|
imageWidth = 18,
|
|
imageHeight = 18,
|
|
control = "WeakAurasIcon"
|
|
}
|
|
args[prefix .. "moveEntryDown"] = {
|
|
type = "execute",
|
|
name = L["Move Entry Down"],
|
|
order = order(),
|
|
func = function()
|
|
for id, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
local childConfigList = optionData.config[childOption.key]
|
|
local childData = optionData.data
|
|
local childPage = getPage(id, optionData.path, #childConfigList)
|
|
if childConfigList[childPage] then
|
|
childConfigList[childPage], childConfigList[childPage + 1] = childConfigList[childPage + 1], childConfigList[childPage]
|
|
setPage(id, optionData.path, childPage + 1)
|
|
WeakAuras.Add(childData)
|
|
end
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end,
|
|
disabled = function()
|
|
for id, optionData in pairs(option.references) do
|
|
local childPage = getPage(id, optionData.path)
|
|
local childOption = optionData.options[optionData.index]
|
|
local childConfigList = optionData.config[childOption.key]
|
|
if childPage >= #childConfigList then
|
|
return true
|
|
end
|
|
end
|
|
end,
|
|
width = 0.15,
|
|
image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\movedown",
|
|
imageWidth = 18,
|
|
imageHeight = 18,
|
|
control = "WeakAurasIcon"
|
|
}
|
|
end
|
|
end
|
|
|
|
if not skipSubOptions then
|
|
local subPrefix = prefix .. "subOption"
|
|
for j = 1, #option.subOptions do
|
|
addUserModeOption(option.subOptions, args, data, order, subPrefix .. j, j)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
args[prefix] = userOption
|
|
|
|
-- convert from weakauras option type to ace option type
|
|
if optionClass == "simple" then
|
|
-- toggle and input don't need any extra love
|
|
if optionType == "input" then
|
|
userOption.multiline = option.multiline
|
|
elseif optionType == "number" then
|
|
userOption.type = "input"
|
|
userOption.get = getUserNumAsString(option)
|
|
userOption.set = setUserNum(data, option)
|
|
elseif optionType == "range" then
|
|
userOption.softMax = option.softMax
|
|
userOption.softMin = option.softMin
|
|
userOption.bigStep = option.bigStep
|
|
userOption.min = option.min
|
|
userOption.max = option.max
|
|
local effectiveMin = userOption.softMin or userOption.min or 0
|
|
local effectiveMax = userOption.softMax or userOption.max or 100
|
|
if (effectiveMin > effectiveMax) then
|
|
-- This will cause a error inside the slider
|
|
-- Fix up either softMax or max, depending on which one is the effective one
|
|
if userOption.softMax then
|
|
userOption.softMax = effectiveMin
|
|
elseif userOption.max then
|
|
userOption.max = effectiveMin
|
|
else
|
|
userOption.softMax = effectiveMin
|
|
end
|
|
end
|
|
userOption.step = option.step
|
|
elseif optionType == "color" then
|
|
userOption.hasAlpha = true
|
|
userOption.get = getUserColor(option)
|
|
userOption.set = setUserColor(data, option)
|
|
elseif optionType == "select" then
|
|
userOption.values = getUserValues(option)
|
|
elseif optionType == "multiselect" then
|
|
userOption.values = getUserValues(option)
|
|
userOption.get = function(_, k)
|
|
local value
|
|
for id, optionData in pairs(option.references) do
|
|
if value == nil then
|
|
value = optionData.config[option.key][k]
|
|
elseif value ~= optionData.config[option.key][k] then
|
|
return
|
|
end
|
|
end
|
|
return value
|
|
end
|
|
userOption.set = function(_, k, v)
|
|
for _, optionData in pairs(option.references) do
|
|
optionData.config[option.key][k] = v
|
|
WeakAuras.Add(optionData.data)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
elseif optionType == "media" then
|
|
userOption.type = "select"
|
|
userOption.dialogControl = OptionsPrivate.Private.author_option_media_controls[option.mediaType]
|
|
userOption.itemControl = OptionsPrivate.Private.author_option_media_itemControls[option.mediaType]
|
|
userOption.values = function()
|
|
if option.mediaType == "sound" then
|
|
return OptionsPrivate.Private.sound_file_types
|
|
else
|
|
return AceGUIWidgetLSMlists[option.mediaType]
|
|
end
|
|
end
|
|
|
|
userOption.sorting = function()
|
|
if option.mediaType == "sound" then
|
|
return OptionsPrivate.Private.SortOrderForValues(OptionsPrivate.Private.sound_file_types)
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
|
|
userOption.set = function(_, value)
|
|
if option.mediaType == "sound" then
|
|
PlaySoundFile(value, "Master")
|
|
end
|
|
for _, optionData in pairs(option.references) do
|
|
local childData = optionData.data
|
|
local childConfig = optionData.config
|
|
childConfig[option.key] = value
|
|
WeakAuras.Add(childData)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
end
|
|
elseif optionClass == "noninteractive" then
|
|
if optionType == "header" then
|
|
userOption.type = "header"
|
|
local name = {}
|
|
local firstName = nil
|
|
local conflict = false
|
|
for _, optionData in pairs(option.references) do
|
|
local childOption = optionData.options[optionData.index]
|
|
if childOption.useName and #childOption.text > 0 then
|
|
if firstName == nil then
|
|
firstName = childOption.text
|
|
tinsert(name, (childOption.text:gsub("||", "|")))
|
|
elseif childOption.text ~= firstName then
|
|
conflict = true
|
|
tinsert(name, (childOption.text:gsub("||", "|")))
|
|
end
|
|
end
|
|
end
|
|
userOption.name = (conflict and conflictBlue or "") .. tconcat(name, " / ")
|
|
elseif optionType == "description" then
|
|
userOption.name = nameUserDesc(option)
|
|
userOption.fontSize = option.fontSize
|
|
elseif optionType == "space" then
|
|
if not option.variableWidth then
|
|
userOption.width = "full"
|
|
end
|
|
if option.useHeight and (option.height or 1) > 1 then
|
|
userOption.name = string.rep("\n", option.height - 1)
|
|
else
|
|
userOption.name = " "
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function initReferences(mergedOption, data, options, index, config, path, parent)
|
|
mergedOption.references = {
|
|
[data.id] = {
|
|
data = data,
|
|
options = options,
|
|
index = index,
|
|
config = config,
|
|
path = path,
|
|
parent = parent,
|
|
}
|
|
}
|
|
if mergedOption.subOptions then
|
|
local subConfig
|
|
if config then
|
|
if mergedOption.groupType == "simple" then
|
|
subConfig = config[mergedOption.key]
|
|
else
|
|
local configList = config[mergedOption.key]
|
|
local page = getPage(data.id, path, #configList)
|
|
subConfig = configList[page]
|
|
end
|
|
end
|
|
local subOptions = options[index].subOptions
|
|
local subPath
|
|
local subParent = mergedOption
|
|
for i, submergedOption in ipairs(mergedOption.subOptions) do -- ha, submerged
|
|
subPath = CopyTable(path)
|
|
subPath[#subPath + 1] = i
|
|
initReferences(submergedOption, data, subOptions, i, subConfig, subPath, subParent)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- all of these fields must be identical for an option to be merged
|
|
-- sometimes this just means that they are both nil, e.g. descriptions have no key
|
|
local significantFieldsForMerge = {
|
|
type = true,
|
|
name = true,
|
|
key = true,
|
|
groupType = true,
|
|
limitType = true,
|
|
size = true,
|
|
mediaType = true,
|
|
}
|
|
|
|
-- these fields are special cases, generally reserved for when the UI displays something based on the merged options
|
|
-- e.g. array name source displays options in merged order, so the dereferenced source is not useful at that level.
|
|
local specialCasesForMerge = {
|
|
nameSource = true
|
|
}
|
|
|
|
local function mergeOptions(mergedOptions, data, options, config, prepath, parent)
|
|
local nextInsert = 1
|
|
for i = 1, #options do
|
|
local path = CopyTable(prepath)
|
|
path[#path + 1] = i
|
|
-- find the best place to start inserting the next option to merge
|
|
local nextToMerge = options[i]
|
|
local shouldMerge = false
|
|
if not nextToMerge.noMerge then
|
|
for j = nextInsert, #mergedOptions + 1 do
|
|
local mergedOption = mergedOptions[j]
|
|
if not mergedOption then
|
|
break
|
|
end -- no more options to check, so must insert
|
|
local validMerge = not mergedOption.noMerge
|
|
if validMerge then
|
|
for field in pairs(significantFieldsForMerge) do
|
|
if nextToMerge[field] ~= mergedOption[field] then
|
|
validMerge = false
|
|
break
|
|
end
|
|
end
|
|
end
|
|
if validMerge then
|
|
shouldMerge = true
|
|
nextInsert = j
|
|
break
|
|
end
|
|
end
|
|
end
|
|
-- now we know at what point to add nextToMerge
|
|
if shouldMerge then
|
|
local mergedOption = mergedOptions[nextInsert]
|
|
-- nil out all fields which aren't the same
|
|
mergedOption.references[data.id] = {
|
|
data = data,
|
|
options = options,
|
|
index = i,
|
|
config = config,
|
|
path = path,
|
|
parent = parent,
|
|
}
|
|
for k, v in pairs(nextToMerge) do
|
|
if k == "subOptions" then
|
|
local subConfig
|
|
if config then
|
|
if mergedOption.groupType == "simple" then
|
|
subConfig = config[mergedOption.key]
|
|
else
|
|
local configList = config[mergedOption.key]
|
|
local page = getPage(data.id, path, #configList)
|
|
subConfig = configList[page]
|
|
end
|
|
end
|
|
local subParent = mergedOption
|
|
mergeOptions(mergedOption.subOptions, data, v, subConfig, path, subParent)
|
|
if mergedOption.groupType == "array" and mergedOption.nameSource ~= nil then
|
|
-- special case merge of nameSource
|
|
-- nameSource can be an optionID of the array's subOptions.
|
|
-- Since the optionIDs are normally hidden away in references and options with different optionIDs can be merged together,
|
|
-- we can't simply use nilmerge like we do with most other fields.
|
|
-- Obviously, if the nameSource has already been set to nil then we do not need to do any more checks,
|
|
-- as newly merged options can never resolve conflicts. Otherwise, we need to examine the semantic value of the nameSource.
|
|
if nextToMerge.nameSource < 1 or mergedOption.nameSource < 1 then -- either the names are fixed, or they are auto-generated as "Entry #"
|
|
-- in this case, nilmerge is the appropriate strategy
|
|
if mergedOption.nameSource ~= nextToMerge.nameSource then
|
|
mergedOption.nameSource = nil
|
|
end
|
|
else -- entry names are sourced from config of a particular subOption
|
|
-- check if nextToMerge.nameSource was merged in the same spot as mergedOption.nameSource
|
|
local subMergedOption = mergedOption.subOptions[mergedOption.nameSource]
|
|
local optionData = subMergedOption.references[data.id]
|
|
if not optionData or optionData.index ~= nextToMerge.nameSource then
|
|
-- either an option was not merged at the name source's index, or the wrong option was.
|
|
-- in both cases, the name source is conflicted. Fallback to "Entry #" as entry names
|
|
mergedOption.nameSource = nil
|
|
end
|
|
end
|
|
end
|
|
elseif not specialCasesForMerge[k] and neq(mergedOption[k], v) then
|
|
mergedOption[k] = nil
|
|
end
|
|
end
|
|
else
|
|
nextInsert = #mergedOptions + 1
|
|
-- can't merge, should insert instead
|
|
local newOption = CopyTable(nextToMerge)
|
|
initReferences(newOption, data, options, i, config, path, parent)
|
|
tinsert(mergedOptions, nextInsert, newOption)
|
|
end
|
|
-- never merge 2 options from the same child
|
|
nextInsert = nextInsert + 1
|
|
end
|
|
end
|
|
|
|
local function valuesAreEqual(t1, t2)
|
|
if t1 == t2 then
|
|
return true
|
|
end
|
|
local ty1 = type(t1)
|
|
local ty2 = type(t2)
|
|
if ty1 ~= ty2 then
|
|
return false
|
|
end
|
|
if ty1 == "number" then
|
|
return abs(t1 - t2) < 1e-9
|
|
end
|
|
if ty1 ~= "table" then
|
|
return false
|
|
end
|
|
for k1, v1 in pairs(t1) do
|
|
local v2 = t2[k1]
|
|
if v2 == nil or not valuesAreEqual(v1, v2) then
|
|
return false
|
|
end
|
|
end
|
|
|
|
for k2, v2 in pairs(t2) do
|
|
local v1 = t1[k2]
|
|
if v1 == nil or not valuesAreEqual(v1, v2) then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function allChoicesAreDefault(option, config, id, path)
|
|
local optionClass = OptionsPrivate.Private.author_option_classes[option.type]
|
|
if optionClass == "simple" then
|
|
return valuesAreEqual(option.default, config[option.key])
|
|
elseif optionClass == "group" then
|
|
if option.groupType == "simple" then
|
|
local subConfig = config[option.key]
|
|
path[#path + 1] = 0
|
|
for i, subOption in ipairs(option.subOptions) do
|
|
path[#path] = i
|
|
if not allChoicesAreDefault(subOption, subConfig, id, path) then
|
|
return false
|
|
end
|
|
end
|
|
path[#path] = nil
|
|
elseif option.groupType == "array" then
|
|
path[#path + 1] = 0
|
|
for _, subConfig in ipairs(config[option.key]) do
|
|
for i, subOption in ipairs(option.subOptions) do
|
|
path[#path] = i
|
|
if not allChoicesAreDefault(subOption, subConfig, id, path) then
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
path[#path] = nil
|
|
end
|
|
if option.useCollapse then
|
|
local isCollapsed = OptionsPrivate.IsCollapsed(id, "config", path, option.collapse)
|
|
if isCollapsed ~= option.collapse then
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function createorder(startorder)
|
|
local order = startorder or 1
|
|
return function()
|
|
order = order + 1
|
|
return order
|
|
end
|
|
end
|
|
|
|
function OptionsPrivate.GetAuthorOptions(data)
|
|
-- initialize the process
|
|
local authorOptions = {
|
|
type = "group",
|
|
name = L["Custom Options"],
|
|
order = 100,
|
|
args = {}
|
|
}
|
|
local args = authorOptions.args
|
|
local isAuthorMode = true
|
|
local options = {}
|
|
local order = createorder(1)
|
|
|
|
for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do
|
|
mergeOptions(options, child, child.authorOptions, child.config, {})
|
|
isAuthorMode = isAuthorMode and child.authorMode
|
|
end
|
|
|
|
if isAuthorMode then
|
|
args["enterUserMode"] = {
|
|
type = "execute",
|
|
width = WeakAuras.normalWidth,
|
|
name = L["Enter User Mode"],
|
|
desc = L["Enter user mode."],
|
|
order = order(),
|
|
func = function()
|
|
for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do
|
|
child.authorMode = nil
|
|
-- no need to add, author mode is picked up by ClearAndUpdateOptions
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
}
|
|
args["enterUserModeSpacer"] = {
|
|
type = "description",
|
|
name = "",
|
|
order = order()
|
|
}
|
|
for i = 1, #options do
|
|
addAuthorModeOption(options, args, data, order, "option" .. i, i)
|
|
end
|
|
args["addOption"] = {
|
|
type = "execute",
|
|
width = WeakAuras.normalWidth,
|
|
name = L["Add Option"],
|
|
order = order(),
|
|
func = function()
|
|
for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do
|
|
local i = #child.authorOptions + 1
|
|
child.authorOptions[i] = {
|
|
type = "toggle",
|
|
key = generateKey("option", child.authorOptions, i),
|
|
name = L["Option %i"]:format(i),
|
|
default = false,
|
|
width = 1,
|
|
useDesc = false,
|
|
}
|
|
OptionsPrivate.SetCollapsed(child.id, "author", i, false)
|
|
WeakAuras.Add(child)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
}
|
|
else
|
|
for i = 1, #options do
|
|
addUserModeOption(options, args, data, order, "userOption" .. i, i)
|
|
end
|
|
args["userConfigFooter"] = {
|
|
type = "header",
|
|
name = "",
|
|
order = order()
|
|
}
|
|
args["resetToDefault"] = {
|
|
type = "execute",
|
|
width = WeakAuras.normalWidth,
|
|
name = L["Reset to Defaults"],
|
|
desc = L["Reset all options to their default values."],
|
|
order = order(),
|
|
func = function()
|
|
for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do
|
|
child.config = {} -- config validation in Add() will set all the needed keys to their defaults
|
|
OptionsPrivate.ResetCollapsed(child.id, "config")
|
|
WeakAuras.Add(child)
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end,
|
|
disabled = function()
|
|
local path = {}
|
|
for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do
|
|
local config = child.config
|
|
for i, option in ipairs(child.authorOptions) do
|
|
path[1] = i
|
|
local result = allChoicesAreDefault(option, config, child.id, path)
|
|
if result == false then
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
}
|
|
args["enterAuthorMode"] = {
|
|
type = "execute",
|
|
width = WeakAuras.normalWidth,
|
|
name = L["Enter Author Mode"],
|
|
desc = L["Configure what options appear on this panel."],
|
|
order = order(),
|
|
func = function()
|
|
for configData in OptionsPrivate.Private.TraverseLeafsOrAura(data) do
|
|
-- no need to add, author mode is picked up by ClearAndUpdateOptions
|
|
configData.authorMode = true
|
|
end
|
|
WeakAuras.ClearAndUpdateOptions(data.id, true)
|
|
end
|
|
}
|
|
end
|
|
return authorOptions
|
|
end
|
|
|