Files
HailMaryGuildTools/Modules/Tracker/TrackerOptions.lua
Torsten Brendgen fc5a8aa361 initial commit
2026-04-10 21:30:31 +02:00

2176 lines
86 KiB
Lua

-- Modules/Tracker/TrackerOptions.lua
local ADDON_NAME = "HailMaryGuildTools"
local HMGT = LibStub("AceAddon-3.0"):GetAddon(ADDON_NAME)
if not HMGT then return end
if not HMGT_Config or not HMGT_Config.RegisterOptionsProvider then return end
local L = LibStub("AceLocale-3.0"):GetLocale(ADDON_NAME)
local LSM = LibStub("LibSharedMedia-3.0", true)
local AceConfigRegistry = LibStub("AceConfigRegistry-3.0", true)
local trackerOptionsGroup
local trackedSpellsOptionsGroup
local RefreshTrackerOptionArgs
local RefreshTrackedSpellsOptionArgs
local ANCHOR_POINT_VALUES = {
TOPLEFT = "TOPLEFT", TOP = "TOP", TOPRIGHT = "TOPRIGHT",
LEFT = "LEFT", CENTER = "CENTER", RIGHT = "RIGHT",
BOTTOMLEFT = "BOTTOMLEFT", BOTTOM = "BOTTOM", BOTTOMRIGHT = "BOTTOMRIGHT",
}
local FONT_OUTLINE_VALUES = {
[""] = L["OPT_OUTLINE_NONE"] or "None",
OUTLINE = L["OPT_OUTLINE_NORMAL"] or "Outline",
THICKOUTLINE = L["OPT_OUTLINE_THICK"] or "Thick Outline",
MONOCHROME = L["OPT_OUTLINE_MONO"] or "Monochrome",
}
local ROLE_FILTER_VALUES = {
ALL = L["OPT_ROLE_FILTER_ALL"] or "All",
TANK = L["OPT_ROLE_FILTER_TANK"] or "Tank",
HEALER = L["OPT_ROLE_FILTER_HEALER"] or "Healer",
DAMAGER = L["OPT_ROLE_FILTER_DAMAGER"] or "Damage",
}
local function NotifyOptionsChanged()
if AceConfigRegistry and type(AceConfigRegistry.NotifyChange) == "function" then
AceConfigRegistry:NotifyChange(ADDON_NAME)
end
end
local function TriggerTrackerUpdate(rebuildArgs)
if rebuildArgs then
if RefreshTrackerOptionArgs then
RefreshTrackerOptionArgs()
end
if RefreshTrackedSpellsOptionArgs then
RefreshTrackedSpellsOptionArgs()
end
end
HMGT:TriggerTrackerUpdate()
NotifyOptionsChanged()
end
local function RefreshAnchors(rebuildArgs)
if rebuildArgs and RefreshTrackerOptionArgs then
RefreshTrackerOptionArgs()
end
HMGT:RefreshFrameAnchors(true)
NotifyOptionsChanged()
end
local function CopyGroup(group, overrides)
if type(group) ~= "table" then
return nil
end
local out = {}
for key, value in pairs(group) do
out[key] = value
end
for key, value in pairs(overrides or {}) do
out[key] = value
end
return out
end
local function GetSortedTrackers()
local trackers = {}
for _, tracker in ipairs(HMGT:GetTrackerConfigs()) do
trackers[#trackers + 1] = tracker
end
table.sort(trackers, function(a, b)
local aId = tonumber(a and a.id) or 0
local bId = tonumber(b and b.id) or 0
if aId ~= bId then
return aId < bId
end
return tostring(a and a.name or "") < tostring(b and b.name or "")
end)
return trackers
end
local function GetTrackerSettings(trackerId)
return HMGT:GetTrackerConfigById(trackerId)
end
local function GetTrackerLabel(tracker)
if type(tracker) ~= "table" then
return L["OPT_TRACKER"] or "Tracker"
end
local name = tostring(tracker.name or ""):gsub("^%s+", ""):gsub("%s+$", "")
if name ~= "" then
return name
end
return string.format("%s %d", L["OPT_TRACKER"] or "Tracker", tonumber(tracker.id) or 0)
end
local function GetTrackerCategoryValues()
if HMGT_SpellData and type(HMGT_SpellData.GetTrackerCategoryValues) == "function" then
return HMGT_SpellData.GetTrackerCategoryValues()
end
return {
interrupt = L["CAT_interrupt"] or "Interrupts",
defensive = L["CAT_defensive"] or "Defensive Cooldowns",
offensive = L["CAT_offensive"] or "Offensive Cooldowns",
cc = L["CAT_cc"] or "Crowd Control",
raid = L["CAT_raid"] or "Raid Cooldowns",
}
end
local function GetCategoryLabel(category)
return GetTrackerCategoryValues()[tostring(category or "")] or tostring(category or "utility")
end
local function GetTrackerCategoriesSummary(tracker)
local labels = {}
for _, category in ipairs((tracker and tracker.categories) or {}) do
labels[#labels + 1] = GetCategoryLabel(category)
end
if #labels == 0 then
return "No categories selected."
end
return table.concat(labels, ", ")
end
local function IsPartyAttachMode(tracker)
return type(tracker) == "table"
and tracker.trackerType == "group"
and tracker.attachToPartyFrame == true
end
local function IsGroupTracker(tracker)
return type(tracker) == "table" and tracker.trackerType == "group"
end
local TRACKER_TYPE_VALUES = {
normal = L["OPT_TRACKER_TYPE_NORMAL"] or "Normal tracker",
group = L["OPT_TRACKER_TYPE_GROUP"] or "Group-based tracker",
}
local function GetTrackerVisibilitySummary(tracker)
local parts = {}
if tracker.showInSolo ~= false then parts[#parts + 1] = L["OPT_SHOW_SOLO"] or "Solo" end
if tracker.showInGroup ~= false then parts[#parts + 1] = L["OPT_SHOW_GROUP"] or "Group" end
if tracker.showInRaid ~= false then parts[#parts + 1] = L["OPT_SHOW_RAID"] or "Raid" end
if #parts == 0 then
return L["OPT_VISIBILITY_NONE"] or "Hidden everywhere"
end
return table.concat(parts, ", ")
end
local function GetTrackerSummaryText(tracker)
if type(tracker) ~= "table" then
return L["OPT_TRACKERS_EMPTY"] or "No tracker selected."
end
local modeLabel
if tracker.testMode then
modeLabel = L["OPT_TEST_MODE"] or "Test mode"
elseif tracker.demoMode then
modeLabel = L["OPT_DEMO_MODE"] or "Demo mode"
elseif tracker.enabled ~= false then
modeLabel = L["OPT_ENABLED"] or "Enabled"
else
modeLabel = L["OPT_DISABLED"] or "Disabled"
end
local display = tracker.showBar and (L["OPT_DISPLAY_BAR"] or "Progress bars") or (L["OPT_DISPLAY_ICON"] or "Icons")
return table.concat({
string.format("|cffffd100%s|r: %s", L["OPT_TRACKER_TYPE"] or "Tracker type", TRACKER_TYPE_VALUES[tracker.trackerType or "normal"] or (L["OPT_TRACKER_TYPE_NORMAL"] or "Normal tracker")),
string.format("|cffffd100%s|r: %s", L["OPT_TRACKER_CATEGORIES"] or "Categories", GetTrackerCategoriesSummary(tracker)),
string.format("|cffffd100%s|r: %s", L["OPT_STATUS_MODE"] or "Mode", modeLabel),
string.format("|cffffd100%s|r: %s", L["OPT_STATUS_DISPLAY"] or "Display", display),
string.format("|cffffd100%s|r: %s", L["OPT_STATUS_VISIBILITY"] or "Visibility", GetTrackerVisibilitySummary(tracker)),
}, "\n")
end
local function NormalizeSearchText(value)
local text = tostring(value or ""):lower()
text = text:gsub("^%s+", "")
text = text:gsub("%s+$", "")
return text
end
local function LocalizedClassName(classToken)
return (LOCALIZED_CLASS_NAMES_MALE and LOCALIZED_CLASS_NAMES_MALE[classToken])
or (LOCALIZED_CLASS_NAMES_FEMALE and LOCALIZED_CLASS_NAMES_FEMALE[classToken])
or classToken
end
local function GetSpellNameById(spellId)
local sid = tonumber(spellId)
if not sid or sid <= 0 then return nil end
if C_Spell and type(C_Spell.GetSpellName) == "function" then
local ok, name = pcall(C_Spell.GetSpellName, sid)
if ok and type(name) == "string" and name ~= "" then
return name
end
end
if type(GetSpellInfo) == "function" then
local name = GetSpellInfo(sid)
if type(name) == "string" and name ~= "" then
return name
end
end
return nil
end
local function GetSpellIconPath(spellId)
local icon = HMGT_SpellData and HMGT_SpellData.GetSpellIcon and HMGT_SpellData.GetSpellIcon(spellId)
if not icon or icon == "" then
icon = "Interface\\Icons\\INV_Misc_QuestionMark"
end
return icon
end
local function SpellLabel(entry)
local spellId = tonumber(entry and entry.spellId) or 0
return tostring(entry and entry.name or GetSpellNameById(spellId) or ("Spell " .. spellId))
end
local function SpellDesc(entry)
local cooldown = tonumber(HMGT_SpellData and HMGT_SpellData.GetBaseCooldown and HMGT_SpellData.GetBaseCooldown(entry)) or tonumber(entry and entry.cooldown) or 0
local specs = {}
if type(entry and entry.specs) == "table" then
for _, spec in ipairs(entry.specs) do
specs[#specs + 1] = tostring(spec)
end
end
return table.concat({
string.format("SpellID: %d", tonumber(entry and entry.spellId) or 0),
string.format("%s: %s", L["OPT_TRACKER_CATEGORIES"] or "Categories", GetCategoryLabel(entry and entry.category or "utility")),
string.format("CD: %ss", cooldown),
#specs > 0 and ("Specs: " .. table.concat(specs, ", ")) or "Specs: All",
}, " | ")
end
local function GetOrderedValues(preferredOrder, availableMap)
local ordered = {}
local seen = {}
for _, key in ipairs(preferredOrder or {}) do
if availableMap[key] then
ordered[#ordered + 1] = key
seen[key] = true
end
end
local extras = {}
for key in pairs(availableMap or {}) do
if not seen[key] then
extras[#extras + 1] = key
end
end
table.sort(extras)
for _, key in ipairs(extras) do
ordered[#ordered + 1] = key
end
return ordered
end
local function GetSpellFilterState(filterKey)
HMGT._genericTrackerSpellFilters = HMGT._genericTrackerSpellFilters or {}
local key = tostring(filterKey or "default")
HMGT._genericTrackerSpellFilters[key] = HMGT._genericTrackerSpellFilters[key] or {
search = "",
enabledOnly = false,
}
return HMGT._genericTrackerSpellFilters[key]
end
local function BuildSpellBrowserArgs(config)
config = type(config) == "table" and config or {}
local filterKey = tostring(config.filterKey or "default")
local emptyText = tostring(config.emptyText or (L["OPT_TRACKERS_EMPTY"] or "No tracked spells available."))
local spellDescription = config.spellDescription or SpellDesc
local applyChanges = config.applyChanges or function()
TriggerTrackerUpdate(false)
end
local pool = {}
if type(config.poolProvider) == "function" then
pool = config.poolProvider() or {}
elseif type(config.pool) == "table" then
pool = config.pool
end
if type(config.isSpellEnabled) ~= "function" or type(config.setSpellEnabled) ~= "function" then
return {
missing = {
type = "description",
order = 1,
width = "full",
name = emptyText,
},
}
end
local browserClassSpells = {}
local entryBySpellId = {}
for _, entry in ipairs(pool) do
for _, classToken in ipairs(entry.classes or {}) do
browserClassSpells[classToken] = browserClassSpells[classToken] or {}
local category = tostring(entry.category or "utility")
browserClassSpells[classToken][category] = browserClassSpells[classToken][category] or {}
local duplicate = false
for _, existing in ipairs(browserClassSpells[classToken][category]) do
if existing.spellId == entry.spellId then
duplicate = true
break
end
end
if not duplicate then
browserClassSpells[classToken][category][#browserClassSpells[classToken][category] + 1] = entry
end
end
local spellId = tonumber(entry and entry.spellId)
if spellId and spellId > 0 then
entryBySpellId[spellId] = entry
end
end
local classOrder = GetOrderedValues(HMGT_SpellData and HMGT_SpellData.ClassOrder or {}, browserClassSpells)
if #classOrder == 0 then
return {
missing = {
type = "description",
order = 1,
width = "full",
name = emptyText,
},
}
end
local function IsEntryVisible(entry, classToken, category)
local spellId = tonumber(entry and entry.spellId)
if not spellId or spellId <= 0 then
return false
end
local filterState = GetSpellFilterState(filterKey)
if filterState.enabledOnly and not config.isSpellEnabled(spellId, entry) then
return false
end
local search = NormalizeSearchText(filterState.search)
if search == "" then
return true
end
local haystack = table.concat({
tostring(entry.name or GetSpellNameById(spellId) or ""),
tostring(spellId),
tostring(LocalizedClassName(classToken)),
tostring(GetCategoryLabel(category)),
}, " "):lower()
return haystack:find(search, 1, true) ~= nil
end
local function CollectVisibleSpellIds(classToken, category)
local ids = {}
local categories = {}
if category then
categories[1] = category
else
categories = GetOrderedValues(HMGT_SpellData and HMGT_SpellData.CategoryOrder or {}, browserClassSpells[classToken] or {})
end
for _, currentCategory in ipairs(categories) do
for _, entry in ipairs((browserClassSpells[classToken] and browserClassSpells[classToken][currentCategory]) or {}) do
if IsEntryVisible(entry, classToken, currentCategory) then
ids[#ids + 1] = tonumber(entry.spellId)
end
end
end
return ids
end
local function CountVisible(classToken, category)
local total = 0
local enabled = 0
for _, spellId in ipairs(CollectVisibleSpellIds(classToken, category)) do
total = total + 1
if config.isSpellEnabled(spellId, entryBySpellId[spellId]) then
enabled = enabled + 1
end
end
return total, enabled
end
local function CountAllVisible()
local total = 0
local enabled = 0
for _, classToken in ipairs(classOrder) do
local classTotal, classEnabled = CountVisible(classToken)
total = total + classTotal
enabled = enabled + classEnabled
end
return total, enabled
end
local function SetSpellIdsEnabled(spellIds, value)
for _, spellId in ipairs(spellIds) do
config.setSpellEnabled(spellId, value, entryBySpellId[spellId])
end
applyChanges()
end
local browserArgs = {
header = {
type = "header",
order = 1,
name = L["OPT_SPELL_BROWSER"] or "Spell Browser",
},
info = {
type = "description",
order = 2,
width = "full",
name = function()
local total, enabled = CountAllVisible()
if type(config.infoText) == "function" then
return config.infoText(total, enabled)
end
return string.format(
"%s\n\n|cffffd100%s|r: %d\n|cffffd100%s|r: %d",
L["OPT_SPELL_BROWSER_DESC"] or "Filter tracked spells by name or Spell ID and apply quick actions to the visible results.",
L["OPT_SPELLS_VISIBLE"] or "Visible spells",
total,
L["OPT_SPELLS_ENABLED_COUNT"] or "Enabled",
enabled
)
end,
},
search = {
type = "input",
order = 3,
width = "full",
name = L["OPT_FILTER_SEARCH"] or "Search",
desc = L["OPT_FILTER_SEARCH_DESC"] or "Search by spell name or Spell ID",
get = function()
return GetSpellFilterState(filterKey).search or ""
end,
set = function(_, val)
GetSpellFilterState(filterKey).search = val or ""
NotifyOptionsChanged()
end,
},
enabledOnly = {
type = "toggle",
order = 4,
width = "full",
name = L["OPT_FILTER_ENABLED_ONLY"] or "Show enabled spells only",
get = function()
return GetSpellFilterState(filterKey).enabledOnly == true
end,
set = function(_, val)
GetSpellFilterState(filterKey).enabledOnly = val and true or false
NotifyOptionsChanged()
end,
},
selectVisible = {
type = "execute",
order = 5,
width = "full",
name = L["OPT_SELECT_VISIBLE"] or "Select visible",
func = function()
local spellIds = {}
for _, classToken in ipairs(classOrder) do
for _, spellId in ipairs(CollectVisibleSpellIds(classToken)) do
spellIds[#spellIds + 1] = spellId
end
end
SetSpellIdsEnabled(spellIds, true)
end,
},
deselectVisible = {
type = "execute",
order = 6,
width = "full",
name = L["OPT_DESELECT_VISIBLE"] or "Deselect visible",
func = function()
local spellIds = {}
for _, classToken in ipairs(classOrder) do
for _, spellId in ipairs(CollectVisibleSpellIds(classToken)) do
spellIds[#spellIds + 1] = spellId
end
end
SetSpellIdsEnabled(spellIds, false)
end,
},
resetFilters = {
type = "execute",
order = 7,
width = "full",
name = L["OPT_FILTER_RESET"] or "Reset filters",
func = function()
local filterState = GetSpellFilterState(filterKey)
filterState.search = ""
filterState.enabledOnly = false
NotifyOptionsChanged()
end,
},
}
local function BuildClassArgs(classToken)
local categories = browserClassSpells[classToken] or {}
local categoryOrder = GetOrderedValues(HMGT_SpellData and HMGT_SpellData.CategoryOrder or {}, categories)
local classArgs = {
browserHeader = {
type = "header",
order = 1,
name = L["OPT_SPELL_BROWSER"] or "Spell Browser",
},
search = {
type = "input",
order = 2,
width = "full",
name = L["OPT_FILTER_SEARCH"] or "Search",
desc = L["OPT_FILTER_SEARCH_DESC"] or "Search by spell name or Spell ID",
get = function()
return GetSpellFilterState(filterKey).search or ""
end,
set = function(_, val)
GetSpellFilterState(filterKey).search = val or ""
NotifyOptionsChanged()
end,
},
enabledOnly = {
type = "toggle",
order = 3,
width = "full",
name = L["OPT_FILTER_ENABLED_ONLY"] or "Show enabled spells only",
get = function()
return GetSpellFilterState(filterKey).enabledOnly == true
end,
set = function(_, val)
GetSpellFilterState(filterKey).enabledOnly = val and true or false
NotifyOptionsChanged()
end,
},
resetFilters = {
type = "execute",
order = 4,
width = "full",
name = L["OPT_FILTER_RESET"] or "Reset filters",
func = function()
local filterState = GetSpellFilterState(filterKey)
filterState.search = ""
filterState.enabledOnly = false
NotifyOptionsChanged()
end,
},
selectionHeader = {
type = "header",
order = 5,
name = L["OPT_SELECTION"] or "Selection",
},
summary = {
type = "description",
order = 6,
width = "full",
name = function()
local total, enabled = CountVisible(classToken)
return string.format(
"|cffffd100%s|r: %d |cffffd100%s|r: %d",
L["OPT_SPELLS_VISIBLE"] or "Visible spells",
total,
L["OPT_SPELLS_ENABLED_COUNT"] or "Enabled",
enabled
)
end,
},
selectVisible = {
type = "execute",
order = 7,
width = "full",
name = L["OPT_SELECT_VISIBLE"] or "Select visible",
func = function()
SetSpellIdsEnabled(CollectVisibleSpellIds(classToken), true)
end,
},
deselectVisible = {
type = "execute",
order = 8,
width = "full",
name = L["OPT_DESELECT_VISIBLE"] or "Deselect visible",
func = function()
SetSpellIdsEnabled(CollectVisibleSpellIds(classToken), false)
end,
},
}
local categoryOrderIndex = 10
for _, category in ipairs(categoryOrder) do
if categories[category] then
local categoryArgs = {
summary = {
type = "description",
order = 1,
width = "full",
name = function()
local total, enabled = CountVisible(classToken, category)
return string.format(
"|cffffd100%s|r: %d |cffffd100%s|r: %d",
L["OPT_SPELLS_VISIBLE"] or "Visible spells",
total,
L["OPT_SPELLS_ENABLED_COUNT"] or "Enabled",
enabled
)
end,
},
}
local spellOrder = 2
for _, entry in ipairs(categories[category]) do
local spellId = tonumber(entry.spellId)
local spellIcon = GetSpellIconPath(spellId)
categoryArgs["spell_" .. tostring(spellId)] = {
type = "toggle",
order = spellOrder,
name = SpellLabel(entry),
image = spellIcon,
imageCoords = { 0.07, 0.93, 0.07, 0.93 },
desc = spellDescription(entry),
hidden = function()
return not IsEntryVisible(entry, classToken, category)
end,
get = function()
return config.isSpellEnabled(spellId, entry)
end,
set = function(_, val)
config.setSpellEnabled(spellId, val, entry)
applyChanges()
end,
}
spellOrder = spellOrder + 1
end
classArgs["cat_" .. category] = {
type = "group",
order = categoryOrderIndex,
inline = true,
name = " ",
hidden = function()
local total = CountVisible(classToken, category)
return total == 0
end,
args = {
header = {
type = "header",
order = 1,
name = GetCategoryLabel(category),
},
},
}
for key, value in pairs(categoryArgs) do
classArgs["cat_" .. category].args[key] = value
end
categoryOrderIndex = categoryOrderIndex + 1
end
end
return classArgs
end
for index, classToken in ipairs(classOrder) do
browserArgs["class_" .. classToken] = {
type = "group",
order = 10 + index,
hidden = function()
return CountVisible(classToken) == 0
end,
name = function()
local total, enabled = CountVisible(classToken)
return string.format("%s (%d/%d)", LocalizedClassName(classToken), enabled, total)
end,
args = BuildClassArgs(classToken),
}
end
return browserArgs
end
local function GetAllTrackedSpellPool()
local categoryMap = {}
for _, tracker in ipairs(GetSortedTrackers()) do
for _, category in ipairs(tracker.categories or {}) do
categoryMap[category] = true
end
end
local orderedCategories = GetOrderedValues(HMGT_SpellData and HMGT_SpellData.CategoryOrder or {}, categoryMap)
if HMGT_SpellData and type(HMGT_SpellData.GetSpellPoolForCategories) == "function" then
return HMGT_SpellData.GetSpellPoolForCategories(orderedCategories)
end
return {}
end
local function EntryMatchesTracker(entry, tracker)
if not entry or not tracker then
return false
end
if HMGT_SpellData and type(HMGT_SpellData.EntryMatchesCategories) == "function" then
return HMGT_SpellData.EntryMatchesCategories(entry, tracker.categories)
end
local trackerCategories = {}
for _, category in ipairs(tracker.categories or {}) do
trackerCategories[tostring(category)] = true
end
if trackerCategories[tostring(entry.category or "utility")] then
return true
end
for _, tag in ipairs(entry.trackerTags or {}) do
if trackerCategories[tostring(tag)] then
return true
end
end
return false
end
local function GetApplicableTrackersForSpell(entry)
local trackers = {}
for _, tracker in ipairs(GetSortedTrackers()) do
if EntryMatchesTracker(entry, tracker) then
trackers[#trackers + 1] = tracker
end
end
return trackers
end
local function GetApplicableTrackerLabels(entry)
local labels = {}
for _, tracker in ipairs(GetApplicableTrackersForSpell(entry)) do
labels[#labels + 1] = GetTrackerLabel(tracker)
end
return labels
end
local function IsGlobalSpellEnabled(spellId, entry)
local trackers = GetApplicableTrackersForSpell(entry)
if #trackers == 0 then
return false
end
for _, tracker in ipairs(trackers) do
if tracker.enabledSpells[spellId] == false then
return false
end
end
return true
end
local function SetGlobalSpellEnabled(spellId, value, entry)
for _, tracker in ipairs(GetApplicableTrackersForSpell(entry)) do
tracker.enabledSpells[spellId] = value
end
end
local function BuildGlobalSpellDescription(entry)
local base = SpellDesc(entry)
local labels = GetApplicableTrackerLabels(entry)
if #labels == 0 then
return base
end
return string.format("%s | %s: %s", base, L["OPT_TRACKERS"] or "Tracker Bars", table.concat(labels, ", "))
end
local function BuildGlobalSpellBrowserArgs()
return BuildSpellBrowserArgs({
filterKey = "global-tracked-spells",
emptyText = L["OPT_TRACKED_SPELLS_EMPTY"] or "No spells are currently tracked by your tracker bars.",
poolProvider = GetAllTrackedSpellPool,
isSpellEnabled = IsGlobalSpellEnabled,
setSpellEnabled = SetGlobalSpellEnabled,
spellDescription = BuildGlobalSpellDescription,
infoText = function(total, enabled)
return string.format(
"%s\n\n%s\n\n|cffffd100%s|r: %d\n|cffffd100%s|r: %d",
L["OPT_SPELL_BROWSER_DESC"] or "Filter tracked spells by name or Spell ID and apply quick actions to the visible results.",
L["OPT_TRACKED_SPELLS_DESC"] or "Changes here apply to all tracker bars that use the spell's categories.",
L["OPT_SPELLS_VISIBLE"] or "Visible spells",
total,
L["OPT_SPELLS_ENABLED_COUNT"] or "Enabled",
enabled
)
end,
applyChanges = function()
TriggerTrackerUpdate(false)
end,
})
end
local function BuildTrackerOverviewArgs()
return {
description = {
type = "description",
order = 1,
width = "full",
name = function()
local trackers = GetSortedTrackers()
local names = {}
for _, tracker in ipairs(trackers) do
names[#names + 1] = GetTrackerLabel(tracker)
end
local body = L["OPT_TRACKERS_DESC"] or "Create tracker bars and bind them to one or more spell categories."
if #names == 0 then
return string.format("%s\n\n%s", body, L["OPT_TRACKERS_EMPTY"] or "No tracker bars configured yet.")
end
return string.format("%s\n\n%s (%d): %s", body, L["OPT_TRACKERS"] or "Tracker Bars", #trackers, table.concat(names, ", "))
end,
},
addTracker = {
type = "execute",
order = 2,
width = "full",
name = L["OPT_ADD_TRACKER"] or "Add tracker",
func = function()
local nextId = HMGT:GetNextTrackerId()
local tracker = HMGT:CreateTrackerConfig(nextId, {
name = string.format("%s %d", L["OPT_TRACKER"] or "Tracker", nextId),
})
HMGT.db.profile.trackers = HMGT.db.profile.trackers or {}
HMGT.db.profile.trackers[#HMGT.db.profile.trackers + 1] = tracker
TriggerTrackerUpdate(true)
end,
},
}
end
local function BuildTrackerGroup(trackerId, order)
local function s()
return GetTrackerSettings(trackerId)
end
local function SetCategoryEnabled(category, enabled)
local tracker = s()
if not tracker then
return
end
local selected = {}
for _, existing in ipairs(tracker.categories or {}) do
selected[existing] = true
end
if enabled then
selected[category] = true
else
local count = 0
for _ in pairs(selected) do
count = count + 1
end
if count <= 1 then
return
end
selected[category] = nil
end
local ordered = {}
local seen = {}
for _, key in ipairs(HMGT_SpellData and HMGT_SpellData.CategoryOrder or {}) do
if selected[key] then
ordered[#ordered + 1] = key
seen[key] = true
end
end
local extras = {}
for key in pairs(selected) do
if not seen[key] then
extras[#extras + 1] = key
end
end
table.sort(extras)
for _, key in ipairs(extras) do
ordered[#ordered + 1] = key
end
tracker.categories = ordered
TriggerTrackerUpdate(true)
end
local function RemoveTracker()
local profileTrackers = HMGT.db.profile.trackers or {}
local removedAnchorKey = HMGT:GetTrackerAnchorKey(trackerId)
for index = #profileTrackers, 1, -1 do
if tonumber(profileTrackers[index].id) == tonumber(trackerId) then
table.remove(profileTrackers, index)
break
end
end
for _, tracker in ipairs(profileTrackers) do
if tracker.anchorTo == removedAnchorKey then
tracker.anchorTo = "UIParent"
tracker.anchorCustom = ""
end
end
HMGT:RefreshFrameAnchors(true)
TriggerTrackerUpdate(true)
end
local function GetRemoveTrackerConfirmationText()
local tracker = s()
local label = tracker and GetTrackerLabel(tracker) or (L["OPT_TRACKER"] or "Tracker")
return string.format(
L["OPT_REMOVE_TRACKER_CONFIRM"] or 'Really remove tracker "%s"?',
tostring(label)
)
end
return {
type = "group",
order = order,
childGroups = "tab",
name = function()
local tracker = s()
return tracker and GetTrackerLabel(tracker) or (L["OPT_TRACKER"] or "Tracker")
end,
args = {
header = {
type = "header",
order = 1,
name = function()
local tracker = s()
return tracker and GetTrackerLabel(tracker) or (L["OPT_TRACKER"] or "Tracker")
end,
},
summaryGroup = {
type = "group",
order = 2,
inline = true,
name = " ",
args = {
summary = {
type = "description",
order = 1,
width = "full",
name = function()
local tracker = s()
return tracker and GetTrackerSummaryText(tracker) or (L["OPT_TRACKERS_EMPTY"] or "No tracker selected.")
end,
},
},
},
removeTracker = {
type = "execute",
order = 3,
width = "full",
name = L["OPT_REMOVE_TRACKER"] or "Remove tracker",
confirm = GetRemoveTrackerConfirmationText,
func = RemoveTracker,
},
mode = {
type = "group",
order = 10,
name = L["OPT_UI_GROUP_MODE"] or "Mode",
args = {
name = {
type = "input",
order = 1,
width = "full",
name = L["OPT_TRACKER_NAME"] or "Tracker name",
get = function()
local tracker = s()
return tracker and tracker.name or ""
end,
set = function(_, val)
local tracker = s()
if tracker then
local trimmed = tostring(val or ""):gsub("^%s+", ""):gsub("%s+$", "")
if trimmed == "" then
trimmed = string.format("%s %d", L["OPT_TRACKER"] or "Tracker", tonumber(tracker.id) or 0)
end
tracker.name = trimmed
NotifyOptionsChanged()
end
end,
},
trackerType = {
type = "select",
order = 2,
width = "full",
name = L["OPT_TRACKER_TYPE"] or "Tracker type",
desc = L["OPT_TRACKER_TYPE_DESC"] or "Choose whether this tracker uses one shared frame or separate frames per group member.",
values = TRACKER_TYPE_VALUES,
get = function()
local tracker = s()
return (tracker and tracker.trackerType) or "normal"
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.trackerType = (val == "group") and "group" or "normal"
tracker.perGroupMember = tracker.trackerType == "group"
if tracker.trackerType ~= "group" then
tracker.attachToPartyFrame = false
end
NotifyOptionsChanged()
RefreshAnchors(true)
TriggerTrackerUpdate(false)
end
end,
},
showBar = {
type = "select",
order = 2.5,
width = "full",
name = L["OPT_DISPLAY_MODE"] or "Display mode",
desc = L["OPT_DISPLAY_MODE_DESC"] or "Show as progress bars or icons",
values = {
bar = L["OPT_DISPLAY_BAR"] or "Progress bars",
icon = L["OPT_DISPLAY_ICON"] or "Icons",
},
get = function()
local tracker = s()
return (tracker and tracker.showBar) and "bar" or "icon"
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.showBar = (val == "bar")
if tracker.showBar and (tracker.growDirection == "LEFT" or tracker.growDirection == "RIGHT") then
tracker.growDirection = "DOWN"
end
TriggerTrackerUpdate(false)
end
end,
},
enabled = {
type = "toggle",
order = 3,
width = "half",
name = L["OPT_ENABLED"] or "Enabled",
desc = L["OPT_ENABLED_DESC"] or "Enable or disable this tracker",
get = function()
local tracker = s()
return tracker and tracker.enabled ~= false
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.enabled = val and true or false
TriggerTrackerUpdate(false)
end
end,
},
includeSelfFrame = {
type = "toggle",
order = 4,
width = "full",
name = L["OPT_INCLUDE_SELF_FRAME"] or "Create a frame for your own player too",
desc = L["OPT_INCLUDE_SELF_FRAME_DESC"] or "Also provision a per-member tracker frame for your own player.",
hidden = function()
local tracker = s()
return not tracker
or not IsGroupTracker(tracker)
end,
get = function()
local tracker = s()
return tracker and tracker.includeSelfFrame == true
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.includeSelfFrame = val and true or false
NotifyOptionsChanged()
RefreshAnchors(true)
TriggerTrackerUpdate(false)
end
end,
},
categories = {
type = "multiselect",
order = 5,
width = "full",
name = L["OPT_TRACKER_CATEGORIES"] or "Categories",
desc = L["OPT_TRACKER_CATEGORIES_DESC"] or "Select which spell categories this tracker should display.",
values = function()
return GetTrackerCategoryValues()
end,
get = function(_, key)
local tracker = s()
if not tracker then
return false
end
for _, category in ipairs(tracker.categories or {}) do
if category == key then
return true
end
end
return false
end,
set = function(_, key, val)
SetCategoryEnabled(key, val)
end,
},
},
},
visibility = {
type = "group",
order = 20,
name = L["OPT_UI_GROUP_VISIBILITY"] or "Visibility",
args = {
showInSolo = {
type = "toggle",
order = 1,
name = L["OPT_SHOW_SOLO"] or "Show when solo",
desc = L["OPT_SHOW_SOLO_DESC"] or "Show this tracker when not in a group",
get = function()
local tracker = s()
return tracker and tracker.showInSolo ~= false
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.showInSolo = val and true or false
TriggerTrackerUpdate(false)
end
end,
},
showInGroup = {
type = "toggle",
order = 2,
name = L["OPT_SHOW_GROUP"] or "Show in group",
desc = L["OPT_SHOW_GROUP_DESC"] or "Show this tracker in a party",
get = function()
local tracker = s()
return tracker and tracker.showInGroup ~= false
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.showInGroup = val and true or false
TriggerTrackerUpdate(false)
end
end,
},
showInRaid = {
type = "toggle",
order = 3,
name = L["OPT_SHOW_RAID"] or "Show in raid",
desc = L["OPT_SHOW_RAID_DESC"] or "Show this tracker in a raid group",
get = function()
local tracker = s()
return tracker and tracker.showInRaid ~= false
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.showInRaid = val and true or false
TriggerTrackerUpdate(false)
end
end,
},
showOnlyReady = {
type = "toggle",
order = 4,
width = "full",
name = L["OPT_SHOW_ONLY_READY"] or "Show only ready cooldowns",
desc = L["OPT_SHOW_ONLY_READY_DESC"] or "Only show entries that are currently ready",
get = function()
local tracker = s()
return tracker and tracker.showOnlyReady == true
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.showOnlyReady = val and true or false
TriggerTrackerUpdate(false)
end
end,
},
readySoonSec = {
type = "range",
order = 5,
min = 0,
max = 300,
step = 1,
width = "full",
name = L["OPT_READY_SOON_SEC"] or "Ready soon threshold (sec)",
desc = L["OPT_READY_SOON_SEC_DESC"] or "Show only cooldowns that are ready or below this remaining time (0 = disabled)",
get = function()
local tracker = s()
return tracker and tracker.readySoonSec or 0
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.readySoonSec = val
TriggerTrackerUpdate(false)
end
end,
},
roleFilter = {
type = "select",
order = 6,
name = L["OPT_ROLE_FILTER"] or "Role filter",
values = ROLE_FILTER_VALUES,
get = function()
local tracker = s()
return tracker and tracker.roleFilter or "ALL"
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.roleFilter = val
TriggerTrackerUpdate(false)
end
end,
},
rangeCheck = {
type = "toggle",
order = 7,
name = L["OPT_RANGE_CHECK"] or "Range check",
get = function()
local tracker = s()
return tracker and tracker.rangeCheck == true
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.rangeCheck = val and true or false
TriggerTrackerUpdate(false)
end
end,
},
hideOutOfRange = {
type = "toggle",
order = 8,
name = L["OPT_HIDE_OOR"] or "Hide out of range",
disabled = function()
local tracker = s()
return not tracker or tracker.rangeCheck ~= true
end,
get = function()
local tracker = s()
return tracker and tracker.hideOutOfRange == true
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.hideOutOfRange = val and true or false
TriggerTrackerUpdate(false)
end
end,
},
outOfRangeAlpha = {
type = "range",
order = 9,
min = 0.1,
max = 1,
step = 0.05,
name = L["OPT_OOR_ALPHA"] or "Out of range alpha",
disabled = function()
local tracker = s()
return not tracker or tracker.rangeCheck ~= true
end,
get = function()
local tracker = s()
return tracker and tracker.outOfRangeAlpha or 0.4
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.outOfRangeAlpha = val
TriggerTrackerUpdate(false)
end
end,
},
},
},
placement = {
type = "group",
order = 30,
name = L["OPT_UI_GROUP_PLACEMENT"] or "Placement",
args = {
locked = {
type = "toggle",
order = 1,
name = L["OPT_LOCKED"] or "Lock frame",
desc = L["OPT_LOCKED_DESC"] or "Prevent the frame from being moved",
get = function()
local tracker = s()
return tracker and tracker.locked == true
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.locked = val and true or false
TriggerTrackerUpdate(false)
end
end,
},
growDirection = {
type = "select",
order = 2,
name = L["OPT_GROW_DIR"] or "Grow direction",
values = function()
local tracker = s()
if tracker and tracker.showBar then
return {
DOWN = L["OPT_GROW_DOWN"] or "Downward",
UP = L["OPT_GROW_UP"] or "Upward",
}
end
return {
DOWN = L["OPT_GROW_DOWN"] or "Downward",
UP = L["OPT_GROW_UP"] or "Upward",
LEFT = L["OPT_GROW_LEFT"] or "Leftward",
RIGHT = L["OPT_GROW_RIGHT"] or "Rightward",
}
end,
get = function()
local tracker = s()
if not tracker then
return "DOWN"
end
local dir = tracker.growDirection or "DOWN"
if tracker.showBar and (dir == "LEFT" or dir == "RIGHT") then
return "DOWN"
end
return dir
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.growDirection = val
TriggerTrackerUpdate(false)
end
end,
},
attachToPartyFrame = {
type = "toggle",
order = 3,
width = "full",
name = L["OPT_ATTACH_PARTY_FRAME"] or "Attach to Party Frame",
desc = L["OPT_ATTACH_PARTY_FRAME_DESC"] or "Anchor each group cooldown frame to its corresponding party unit frame",
hidden = function()
local tracker = s()
return not tracker or not IsGroupTracker(tracker)
end,
get = function()
local tracker = s()
return tracker and tracker.attachToPartyFrame == true
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.attachToPartyFrame = val and true or false
NotifyOptionsChanged()
RefreshAnchors(true)
TriggerTrackerUpdate(false)
end
end,
},
partyAttachSide = {
type = "select",
order = 4,
name = L["OPT_ATTACH_PARTY_SIDE"] or "Attach side",
values = {
LEFT = L["OPT_ATTACH_LEFT"] or "Left",
RIGHT = L["OPT_ATTACH_RIGHT"] or "Right",
},
hidden = function()
local tracker = s()
return not tracker or not IsGroupTracker(tracker) or tracker.attachToPartyFrame ~= true
end,
get = function()
local tracker = s()
return tracker and tracker.partyAttachSide or "RIGHT"
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.partyAttachSide = val
RefreshAnchors(true)
end
end,
},
partyAttachOffsetX = {
type = "range",
order = 5,
min = -200,
max = 200,
step = 1,
name = function()
local tracker = s()
return string.format("%s: %s", L["OPT_ATTACH_PARTY_OFFSET_X"] or "Attach X offset", tostring(tracker and tracker.partyAttachOffsetX or 8))
end,
hidden = function()
local tracker = s()
return not tracker or not IsGroupTracker(tracker) or tracker.attachToPartyFrame ~= true
end,
get = function()
local tracker = s()
return tracker and tracker.partyAttachOffsetX or 8
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.partyAttachOffsetX = val
RefreshAnchors(true)
end
end,
},
partyAttachOffsetY = {
type = "range",
order = 6,
min = -200,
max = 200,
step = 1,
name = function()
local tracker = s()
return string.format("%s: %s", L["OPT_ATTACH_PARTY_OFFSET_Y"] or "Attach Y offset", tostring(tracker and tracker.partyAttachOffsetY or 0))
end,
hidden = function()
local tracker = s()
return not tracker or not IsGroupTracker(tracker) or tracker.attachToPartyFrame ~= true
end,
get = function()
local tracker = s()
return tracker and tracker.partyAttachOffsetY or 0
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.partyAttachOffsetY = val
RefreshAnchors(true)
end
end,
},
anchorTo = {
type = "select",
order = 7,
name = L["OPT_ANCHOR_TO"] or "Anchor to",
desc = L["OPT_ANCHOR_TO_DESC"] or "Attach this frame to another frame or to the screen",
hidden = function()
return IsPartyAttachMode(s())
end,
values = function()
local tracker = s()
return tracker and HMGT:GetAnchorTargetOptions(tracker.id, tracker.anchorTo) or {}
end,
get = function()
local tracker = s()
if not tracker then
return "UIParent"
end
local current = tracker.anchorTo or "UIParent"
local values = HMGT:GetAnchorTargetOptions(tracker.id, current)
if values[current] then
return current
end
return "CUSTOM"
end,
set = function(_, val)
local tracker = s()
if tracker then
local selfAnchor = HMGT:GetTrackerAnchorKey(tracker.id)
if val == selfAnchor then
val = "UIParent"
end
if val ~= "CUSTOM" then
tracker.anchorCustom = tracker.anchorCustom or ""
end
tracker.anchorTo = val
RefreshAnchors(false)
end
end,
},
anchorCustom = {
type = "input",
order = 8,
width = "full",
name = L["OPT_ANCHOR_CUSTOM_NAME"] or "Custom frame",
desc = L["OPT_ANCHOR_CUSTOM_NAME_DESC"] or "Global frame name, e.g. ElvUF_Player",
hidden = function()
local tracker = s()
return not tracker or IsPartyAttachMode(tracker) or (tracker.anchorTo or "UIParent") ~= "CUSTOM"
end,
get = function()
local tracker = s()
return tracker and tracker.anchorCustom or ""
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.anchorCustom = tostring(val or ""):gsub("^%s+", ""):gsub("%s+$", "")
RefreshAnchors(false)
end
end,
},
anchorPoint = {
type = "select",
order = 9,
name = L["OPT_ANCHOR_POINT"] or "Anchor point",
desc = L["OPT_ANCHOR_POINT_DESC"] or "Point on this frame to use for anchoring",
values = ANCHOR_POINT_VALUES,
hidden = function()
return IsPartyAttachMode(s())
end,
get = function()
local tracker = s()
return tracker and tracker.anchorPoint or "TOPLEFT"
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.anchorPoint = val
RefreshAnchors(false)
end
end,
},
anchorRelativePoint = {
type = "select",
order = 10,
name = L["OPT_ANCHOR_REL_POINT"] or "Relative point",
desc = L["OPT_ANCHOR_REL_POINT_DESC"] or "Point on the target frame to anchor to",
values = ANCHOR_POINT_VALUES,
hidden = function()
return IsPartyAttachMode(s())
end,
get = function()
local tracker = s()
return tracker and tracker.anchorRelPoint or "TOPLEFT"
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.anchorRelPoint = val
RefreshAnchors(false)
end
end,
},
anchorX = {
type = "range",
order = 11,
min = -2000,
max = 2000,
step = 1,
name = L["OPT_ANCHOR_X"] or "X offset",
hidden = function()
return IsPartyAttachMode(s())
end,
get = function()
local tracker = s()
if not tracker then
return 0
end
if tracker.anchorX ~= nil then
return tracker.anchorX
end
return tracker.posX or 0
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.anchorX = val
RefreshAnchors(false)
end
end,
},
anchorY = {
type = "range",
order = 12,
min = -2000,
max = 2000,
step = 1,
name = L["OPT_ANCHOR_Y"] or "Y offset",
hidden = function()
return IsPartyAttachMode(s())
end,
get = function()
local tracker = s()
if not tracker then
return 0
end
if tracker.anchorY ~= nil then
return tracker.anchorY
end
return tracker.posY or 0
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.anchorY = val
RefreshAnchors(false)
end
end,
},
},
},
layout = {
type = "group",
order = 40,
name = L["OPT_UI_GROUP_LAYOUT"] or "Layout",
args = {
width = {
type = "range",
order = 1,
min = 100,
max = 600,
step = 1,
name = L["OPT_WIDTH"] or "Width (bars)",
hidden = function()
local tracker = s()
return not tracker or tracker.showBar ~= true
end,
get = function()
local tracker = s()
return tracker and tracker.width or 250
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.width = val
TriggerTrackerUpdate(false)
end
end,
},
barHeight = {
type = "range",
order = 2,
min = 10,
max = 60,
step = 1,
name = L["OPT_BAR_HEIGHT"] or "Bar height",
hidden = function()
local tracker = s()
return not tracker or tracker.showBar ~= true
end,
get = function()
local tracker = s()
return tracker and tracker.barHeight or 20
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.barHeight = val
TriggerTrackerUpdate(false)
end
end,
},
barSpacing = {
type = "range",
order = 3,
min = 0,
max = 20,
step = 1,
name = L["OPT_BAR_SPACING"] or "Bar spacing",
hidden = function()
local tracker = s()
return not tracker or tracker.showBar ~= true
end,
get = function()
local tracker = s()
return tracker and tracker.barSpacing or 2
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.barSpacing = val
TriggerTrackerUpdate(false)
end
end,
},
barTexture = {
type = "select",
order = 4,
name = L["OPT_BAR_TEXTURE"] or "Texture",
desc = L["OPT_BAR_TEXTURE_DESC"] or "Texture of the progress bar",
dialogControl = "LSM30_Statusbar",
hidden = function()
local tracker = s()
return not tracker or tracker.showBar ~= true
end,
values = AceGUIWidgetLSMlists and AceGUIWidgetLSMlists.statusbar or (LSM and LSM:HashTable("statusbar")) or {},
get = function()
local tracker = s()
return tracker and tracker.barTexture or "Blizzard"
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.barTexture = val
TriggerTrackerUpdate(false)
end
end,
},
iconSize = {
type = "range",
order = 5,
min = 12,
max = 100,
step = 1,
name = L["OPT_ICON_SIZE"] or "Icon size",
hidden = function()
local tracker = s()
return not tracker or tracker.showBar == true
end,
get = function()
local tracker = s()
return tracker and tracker.iconSize or 32
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.iconSize = val
TriggerTrackerUpdate(false)
end
end,
},
iconSpacing = {
type = "range",
order = 6,
min = 0,
max = 20,
step = 1,
name = L["OPT_ICON_SPACING"] or "Icon spacing",
hidden = function()
local tracker = s()
return not tracker or tracker.showBar == true
end,
get = function()
local tracker = s()
return tracker and tracker.iconSpacing or 2
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.iconSpacing = val
TriggerTrackerUpdate(false)
end
end,
},
iconCols = {
type = "range",
order = 7,
min = 1,
max = 20,
step = 1,
name = L["OPT_ICON_COLS"] or "Icons per row",
desc = L["OPT_ICON_COLS_DESC"] or "Number of icons per row (DOWN/UP) or per column (LEFT/RIGHT)",
hidden = function()
local tracker = s()
return not tracker or tracker.showBar == true
end,
get = function()
local tracker = s()
return tracker and tracker.iconCols or 6
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.iconCols = val
TriggerTrackerUpdate(false)
end
end,
},
iconOverlay = {
type = "select",
order = 8,
name = L["OPT_ICON_OVERLAY"] or "Cooldown display",
desc = L["OPT_ICON_OVERLAY_DESC"] or "How to show remaining cooldown on icons",
hidden = function()
local tracker = s()
return not tracker or tracker.showBar == true
end,
values = {
sweep = L["OPT_ICON_OVERLAY_SWEEP"] or "Cooldown sweep",
timer = L["OPT_ICON_OVERLAY_TIMER"] or "Text timer (MM:SS)",
},
get = function()
local tracker = s()
return tracker and tracker.iconOverlay or "sweep"
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.iconOverlay = val
TriggerTrackerUpdate(false)
end
end,
},
textAnchor = {
type = "select",
order = 9,
name = L["OPT_TEXT_ANCHOR"] or "Text position",
desc = L["OPT_TEXT_ANCHOR_DESC"] or "Where to show name and timer relative to the icon",
hidden = function()
local tracker = s()
return not tracker or tracker.showBar == true
end,
values = {
onIcon = L["OPT_ANCHOR_ON_ICON"] or "On icon (overlay)",
above = L["OPT_ANCHOR_ABOVE"] or "Above icon",
below = L["OPT_ANCHOR_BELOW"] or "Below icon",
left = L["OPT_ANCHOR_LEFT"] or "Left of icon",
right = L["OPT_ANCHOR_RIGHT"] or "Right of icon",
},
get = function()
local tracker = s()
return tracker and tracker.textAnchor or "below"
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.textAnchor = val
TriggerTrackerUpdate(false)
end
end,
},
showReadyText = {
type = "toggle",
order = 10,
name = L["OPT_SHOW_READY_TEXT"] or "Show ready text",
hidden = function()
local tracker = s()
return not tracker or tracker.showBar == true
end,
get = function()
local tracker = s()
return tracker and tracker.showReadyText ~= false
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.showReadyText = val and true or false
TriggerTrackerUpdate(false)
end
end,
},
showChargesOnIcon = {
type = "toggle",
order = 11,
name = L["OPT_SHOW_CHARGES_ON_ICON"] or "Show charges on icon",
hidden = function()
local tracker = s()
return not tracker or tracker.showBar == true
end,
get = function()
local tracker = s()
return tracker and tracker.showChargesOnIcon == true
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.showChargesOnIcon = val and true or false
TriggerTrackerUpdate(false)
end
end,
},
showRemainingOnIcon = {
type = "toggle",
order = 12,
name = L["OPT_SHOW_REMAINING_ON_ICON"] or "Show remaining time on icon",
hidden = function()
local tracker = s()
return not tracker or tracker.showBar == true or (tracker.iconOverlay or "sweep") == "sweep"
end,
get = function()
local tracker = s()
return tracker and tracker.showRemainingOnIcon == true
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.showRemainingOnIcon = val and true or false
TriggerTrackerUpdate(false)
end
end,
},
},
},
appearance = {
type = "group",
order = 50,
name = L["OPT_UI_GROUP_APPEARANCE"] or "Appearance",
args = {
showPlayerName = {
type = "toggle",
order = 1,
name = L["OPT_SHOW_NAME"] or "Show player names",
get = function()
local tracker = s()
return tracker and tracker.showPlayerName ~= false
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.showPlayerName = val and true or false
TriggerTrackerUpdate(false)
end
end,
},
colorByClass = {
type = "toggle",
order = 2,
name = L["OPT_CLASS_COLOR"] or "Use class colours",
get = function()
local tracker = s()
return tracker and tracker.colorByClass ~= false
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.colorByClass = val and true or false
TriggerTrackerUpdate(false)
end
end,
},
font = {
type = "select",
order = 3,
name = L["OPT_FONT"] or "Typeface",
dialogControl = "LSM30_Font",
values = AceGUIWidgetLSMlists and AceGUIWidgetLSMlists.font or (LSM and LSM:HashTable("font")) or {},
get = function()
local tracker = s()
return tracker and tracker.font or "Friz Quadrata TT"
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.font = val
TriggerTrackerUpdate(false)
end
end,
},
fontSize = {
type = "range",
order = 4,
min = 6,
max = 24,
step = 1,
name = L["OPT_FONT_SIZE"] or "Font size",
get = function()
local tracker = s()
return tracker and tracker.fontSize or 12
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.fontSize = val
TriggerTrackerUpdate(false)
end
end,
},
fontOutline = {
type = "select",
order = 5,
name = L["OPT_FONT_OUTLINE"] or "Font outline",
values = FONT_OUTLINE_VALUES,
get = function()
local tracker = s()
return tracker and tracker.fontOutline or "OUTLINE"
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.fontOutline = val
TriggerTrackerUpdate(false)
end
end,
},
borderEnabled = {
type = "toggle",
order = 6,
name = L["OPT_BORDER_ENABLED"] or "Show border",
desc = L["OPT_BORDER_ENABLED_DESC"] or "Show a 1px border around progress bars and icons",
get = function()
local tracker = s()
return tracker and tracker.borderEnabled == true
end,
set = function(_, val)
local tracker = s()
if tracker then
tracker.borderEnabled = val and true or false
TriggerTrackerUpdate(false)
end
end,
},
borderColor = {
type = "color",
order = 7,
hasAlpha = true,
name = L["OPT_BORDER_COLOR"] or "Border color",
desc = L["OPT_BORDER_COLOR_DESC"] or "Color of the 1px border",
get = function()
local tracker = s()
local color = tracker and tracker.borderColor or {}
return color.r or color[1] or 1, color.g or color[2] or 1, color.b or color[3] or 1, color.a or color[4] or 1
end,
set = function(_, r, g, b, a)
local tracker = s()
if tracker then
tracker.borderColor = { r = r, g = g, b = b, a = a }
TriggerTrackerUpdate(false)
end
end,
},
},
},
},
}
end
RefreshTrackerOptionArgs = function()
if not trackerOptionsGroup then
return
end
trackerOptionsGroup.args = BuildTrackerOverviewArgs()
local trackers = GetSortedTrackers()
for index, tracker in ipairs(trackers) do
local trackerId = tonumber(tracker.id)
if trackerId then
trackerOptionsGroup.args["tracker_" .. trackerId] = BuildTrackerGroup(trackerId, 10 + index)
end
end
end
RefreshTrackedSpellsOptionArgs = function()
if not trackedSpellsOptionsGroup then
return
end
trackedSpellsOptionsGroup.args = BuildGlobalSpellBrowserArgs()
end
trackerOptionsGroup = {
type = "group",
name = L["OPT_TRACKERS"] or "Tracker Bars",
order = 10,
childGroups = "tree",
args = {},
}
trackedSpellsOptionsGroup = {
type = "group",
name = L["OPT_SECTION_SPELLS"] or "Tracked Spells",
order = 20,
childGroups = "tree",
args = {},
}
RefreshTrackerOptionArgs()
RefreshTrackedSpellsOptionArgs()
HMGT_Config:RegisterOptionsProvider("tracker.trackers", function()
if RefreshTrackerOptionArgs then
RefreshTrackerOptionArgs()
end
return {
path = "tracker.trackers",
order = 10,
group = trackerOptionsGroup,
}
end)
HMGT_Config:RegisterOptionsProvider("tracker.spells", function()
if RefreshTrackedSpellsOptionArgs then
RefreshTrackedSpellsOptionArgs()
end
return {
path = "tracker.spells",
order = 20,
group = trackedSpellsOptionsGroup,
}
end)
HMGT_Config:RegisterOptionsProvider("tracker", function()
if RefreshTrackerOptionArgs then
RefreshTrackerOptionArgs()
end
if RefreshTrackedSpellsOptionArgs then
RefreshTrackedSpellsOptionArgs()
end
local group = {
type = "group",
name = L["OPT_MODULE_TRACKER"] or "Tracker",
order = 10,
childGroups = "tree",
args = {
actionsHeader = {
type = "header",
name = L["OPT_TRACKER_ACTIONS"] or "Tracker actions",
order = 1,
},
lockAll = {
type = "execute",
order = 1.1,
width = "full",
name = L["OPT_LOCK_ALL"],
func = function()
if HMGT_Config and HMGT_Config.SetAllTrackerFramesLocked then
HMGT_Config.SetAllTrackerFramesLocked(true)
end
end,
},
unlockAll = {
type = "execute",
order = 1.2,
width = "full",
name = L["OPT_UNLOCK_ALL"],
func = function()
if HMGT_Config and HMGT_Config.SetAllTrackerFramesLocked then
HMGT_Config.SetAllTrackerFramesLocked(false)
end
end,
},
demoMode = {
type = "execute",
order = 1.3,
width = "full",
name = L["OPT_DEMO_MODE"],
desc = L["OPT_DEMO_MODE_DESC"],
func = function()
HMGT:DemoMode()
end,
},
testMode = {
type = "execute",
order = 1.4,
width = "full",
name = L["OPT_TEST_MODE"],
desc = L["OPT_TEST_MODE_DESC"],
func = function()
HMGT:TestMode()
end,
},
trackerBars = CopyGroup(trackerOptionsGroup, {
order = 10,
name = trackerOptionsGroup.name or (L["OPT_TRACKERS"] or "Tracker Bars"),
}),
trackedSpells = CopyGroup(trackedSpellsOptionsGroup, {
order = 20,
name = trackedSpellsOptionsGroup.name or (L["OPT_SECTION_SPELLS"] or "Tracked Spells"),
}),
},
}
return {
path = "tracker",
order = 10,
group = group,
}
end)