From d5d8007f8407874f2871a928e5561a3b9aee5529 Mon Sep 17 00:00:00 2001 From: Lasse Nielsen Date: Wed, 29 Apr 2026 10:20:16 +0200 Subject: [PATCH] feat: add AceConfig options UI for overlay positioning (#40) Replaces the text-argument /phd config handler with an AceConfig-3.0 panel registered via AceConfigDialog:AddToBlizOptions. Settings bind to db.profile.overlay and re-render via ns.ActionBar.ApplySettings on every change. Includes a Reset to Defaults button driven by ns.DB_DEFAULTS. Closes #40 --- .luacheckrc | 2 + Core/Init.lua | 116 +++++-------------------------- Core/Options.lua | 173 +++++++++++++++++++++++++++++++++++++++++++++++ Libs/embeds.xml | 2 + PhDamage.toc | 1 + 5 files changed, 194 insertions(+), 100 deletions(-) create mode 100644 Core/Options.lua diff --git a/.luacheckrc b/.luacheckrc index 4b4ee73..be9d2e5 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -87,6 +87,8 @@ read_globals = { "GameTooltipText", "UIParent", "C_Timer", + "Settings", + "InterfaceOptionsFrame_OpenToCategory", -- Third-party addons (optional at runtime) "ElvUI", diff --git a/Core/Init.lua b/Core/Init.lua index 8d8df3c..567025b 100644 --- a/Core/Init.lua +++ b/Core/Init.lua @@ -35,6 +35,7 @@ local DB_DEFAULTS = { }, }, } +ns.DB_DEFAULTS = DB_DEFAULTS ------------------------------------------------------------------------------- -- Lifecycle: OnInitialize (runs once, before PLAYER_LOGIN) @@ -46,6 +47,11 @@ function PhDamage:OnInitialize() self:RegisterChatCommand("phd", "OnSlashCommand") self:RegisterChatCommand("phdamage", "OnSlashCommand") + -- Register the AceConfig options panel + if ns.Options and ns.Options.Register then + ns.Options:Register() + end + self:Print("PhDamage loaded. Type /phd for diagnostics.") end @@ -77,95 +83,6 @@ end -- Slash Command Router ------------------------------------------------------------------------------- --- Valid anchor point identifiers accepted by the config command -local VALID_ANCHORS = { - BOTTOM = true, TOP = true, CENTER = true, - LEFT = true, RIGHT = true, - BOTTOMLEFT = true, BOTTOMRIGHT = true, - TOPLEFT = true, TOPRIGHT = true, -} - -local function HandleConfigCommand(addon, args) - local cfg = addon.db.profile.overlay - local setting = args[2] and strlower(args[2]) or "" - local value = args[3] - - if setting == "" then - addon:Print("Overlay settings:") - addon:Print(string.format(" anchor: %s", cfg.anchor)) - addon:Print(string.format(" offsetX: %d", cfg.offsetX)) - addon:Print(string.format(" offsetY: %d", cfg.offsetY)) - addon:Print(string.format(" fontSize: %d", cfg.fontSize)) - addon:Print(string.format(" abbreviate: %s", cfg.abbreviateNumbers and "on" or "off")) - return - end - - if setting == "anchor" then - local v = value and strupper(value) or "" - if VALID_ANCHORS[v] then - cfg.anchor = v - addon:Print("Overlay anchor set to: " .. v) - else - addon:Print("Invalid anchor. Valid values: BOTTOM, TOP, CENTER, LEFT, RIGHT, " - .. "BOTTOMLEFT, BOTTOMRIGHT, TOPLEFT, TOPRIGHT") - return - end - - elseif setting == "offsetx" then - local n = tonumber(value) - if n then - cfg.offsetX = n - addon:Print("Overlay offsetX set to: " .. n) - else - addon:Print("Invalid value for offsetX. Expected a number.") - return - end - - elseif setting == "offsety" then - local n = tonumber(value) - if n then - cfg.offsetY = n - addon:Print("Overlay offsetY set to: " .. n) - else - addon:Print("Invalid value for offsetY. Expected a number.") - return - end - - elseif setting == "fontsize" then - local n = tonumber(value) - if n and n >= 6 and n <= 24 then - cfg.fontSize = n - addon:Print("Overlay font size set to: " .. n) - else - addon:Print("Invalid font size. Expected a number between 6 and 24.") - return - end - - elseif setting == "abbreviate" then - local v = value and strlower(value) or "" - if v == "on" or v == "true" or v == "1" then - cfg.abbreviateNumbers = true - addon:Print("Number abbreviation enabled.") - elseif v == "off" or v == "false" or v == "0" then - cfg.abbreviateNumbers = false - addon:Print("Number abbreviation disabled.") - else - addon:Print("Invalid value for abbreviate. Use 'on' or 'off'.") - return - end - - else - addon:Print("Unknown config setting '" .. setting - .. "'. Valid settings: anchor, offsetX, offsetY, fontSize, abbreviate") - return - end - - -- Apply the updated settings to all existing overlays - if ns.ActionBar and ns.ActionBar.ApplySettings then - ns.ActionBar.ApplySettings() - end -end - function PhDamage:OnSlashCommand(input) if not ns.Diagnostics then self:Print("Diagnostics module not loaded.") @@ -182,19 +99,18 @@ function PhDamage:OnSlashCommand(input) local linkName = spellInput:match("|Hspell:%d+.-|h%[(.-)%]|h") ns.Diagnostics.PrintSpell(linkName or spellInput) elseif cmd == "config" then - HandleConfigCommand(self, args) + if ns.Options and ns.Options.Open then + ns.Options:Open() + else + self:Print("Options module not loaded.") + end elseif cmd == "help" then self:Print("Usage:") - self:Print(" /phd — Show all spell computations") - self:Print(" /phd state — Show current player state snapshot") - self:Print(" /phd spell — Detailed breakdown for one spell") - self:Print(" /phd config — Show overlay display settings") - self:Print(" /phd config anchor — Set text anchor (BOTTOM, TOP, CENTER, ...)") - self:Print(" /phd config offsetX — Set horizontal offset (default 0)") - self:Print(" /phd config offsetY — Set vertical offset (default 2)") - self:Print(" /phd config fontSize — Set font size 6-24 (default 10)") - self:Print(" /phd config abbreviate on|off — Toggle k/M number shortening (default on)") - self:Print(" /phd help — Show this help") + self:Print(" /phd - Show all spell computations") + self:Print(" /phd state - Show current player state snapshot") + self:Print(" /phd spell - Detailed breakdown for one spell") + self:Print(" /phd config - Open the overlay options panel") + self:Print(" /phd help - Show this help") else ns.Diagnostics.PrintAll() end diff --git a/Core/Options.lua b/Core/Options.lua new file mode 100644 index 0000000..88e6e56 --- /dev/null +++ b/Core/Options.lua @@ -0,0 +1,173 @@ +------------------------------------------------------------------------------- +-- Options.lua +-- AceConfig-3.0 options panel bound to db.profile.overlay +-- +-- Supported versions: TBC Anniversary +------------------------------------------------------------------------------- + +local ADDON_NAME, ns = ... + +------------------------------------------------------------------------------- +-- Cached globals +------------------------------------------------------------------------------- +local LibStub = LibStub +local Settings = Settings +local InterfaceOptionsFrame_OpenToCategory = InterfaceOptionsFrame_OpenToCategory +local pairs = pairs + +------------------------------------------------------------------------------- +-- Module +------------------------------------------------------------------------------- +local Options = {} +ns.Options = Options + +------------------------------------------------------------------------------- +-- Anchor choice list (also drives the dropdown values) +------------------------------------------------------------------------------- +local ANCHOR_VALUES = { + TOPLEFT = "TOPLEFT", + TOP = "TOP", + TOPRIGHT = "TOPRIGHT", + LEFT = "LEFT", + CENTER = "CENTER", + RIGHT = "RIGHT", + BOTTOMLEFT = "BOTTOMLEFT", + BOTTOM = "BOTTOM", + BOTTOMRIGHT = "BOTTOMRIGHT", +} + +------------------------------------------------------------------------------- +-- Internal helpers +------------------------------------------------------------------------------- +local function GetOverlay() + return ns.Addon.db.profile.overlay +end + +local function ApplyOverlay() + if ns.ActionBar and ns.ActionBar.ApplySettings then + ns.ActionBar.ApplySettings() + end +end + +-- get/set handlers for AceConfig - info[#info] is the leaf option key +local function GetSetting(info) + return GetOverlay()[info[#info]] +end + +local function SetSetting(info, value) + GetOverlay()[info[#info]] = value + ApplyOverlay() +end + +local function ResetToDefaults() + local defaults = ns.DB_DEFAULTS and ns.DB_DEFAULTS.profile and ns.DB_DEFAULTS.profile.overlay + if not defaults then return end + + local overlay = GetOverlay() + for key, value in pairs(defaults) do + overlay[key] = value + end + ApplyOverlay() +end + +------------------------------------------------------------------------------- +-- BuildOptionsTable() - returns the AceConfig schema +------------------------------------------------------------------------------- +function Options:BuildOptionsTable() + return { + type = "group", + name = "PhDamage", + get = GetSetting, + set = SetSetting, + args = { + overlayHeader = { + type = "header", + name = "Action Bar Overlay", + order = 1, + }, + anchor = { + type = "select", + name = "Anchor", + desc = "Anchor point on the action button where the damage text is placed.", + values = ANCHOR_VALUES, + order = 10, + }, + offsetX = { + type = "range", + name = "Horizontal Offset", + desc = "Horizontal pixel offset from the anchor point.", + min = -50, + max = 50, + step = 1, + order = 20, + }, + offsetY = { + type = "range", + name = "Vertical Offset", + desc = "Vertical pixel offset from the anchor point.", + min = -50, + max = 50, + step = 1, + order = 30, + }, + fontSize = { + type = "range", + name = "Font Size", + desc = "Font size of the overlay damage text.", + min = 6, + max = 32, + step = 1, + order = 40, + }, + abbreviateNumbers = { + type = "toggle", + name = "Abbreviate Numbers", + desc = "Display large numbers with k/M suffixes (e.g. 1.2k instead of 1234).", + order = 50, + }, + spacer = { + type = "description", + name = " ", + order = 60, + }, + reset = { + type = "execute", + name = "Reset to Defaults", + desc = "Restore all overlay settings to their default values.", + func = ResetToDefaults, + order = 70, + }, + }, + } +end + +------------------------------------------------------------------------------- +-- Register() - wires the options table into AceConfig and Blizzard panels +------------------------------------------------------------------------------- +function Options:Register() + local AceConfig = LibStub("AceConfig-3.0", true) + local AceConfigDialog = LibStub("AceConfigDialog-3.0", true) + if not AceConfig or not AceConfigDialog then return end + + AceConfig:RegisterOptionsTable(ADDON_NAME, self:BuildOptionsTable()) + self.blizFrame = AceConfigDialog:AddToBlizOptions(ADDON_NAME, "PhDamage") +end + +------------------------------------------------------------------------------- +-- Open() - opens the Blizzard interface options to the PhDamage panel +------------------------------------------------------------------------------- +function Options:Open() + local frame = self.blizFrame + if not frame then return end + + if Settings and Settings.OpenToCategory then + Settings.OpenToCategory(frame.name or frame) + return + end + + if InterfaceOptionsFrame_OpenToCategory then + -- TBC 2.5.x quirk: first call only opens the panel; second call selects it. + InterfaceOptionsFrame_OpenToCategory(frame) + InterfaceOptionsFrame_OpenToCategory(frame) + end +end diff --git a/Libs/embeds.xml b/Libs/embeds.xml index 52331ee..8ee16fe 100644 --- a/Libs/embeds.xml +++ b/Libs/embeds.xml @@ -7,5 +7,7 @@ + + diff --git a/PhDamage.toc b/PhDamage.toc index 42d2668..e3da8a3 100644 --- a/PhDamage.toc +++ b/PhDamage.toc @@ -16,6 +16,7 @@ Libs\embeds.xml # Core Core\Constants.lua Core\Init.lua +Core\Options.lua Core\Events.lua Core\StateCollector.lua