--[[ CritTracker - Configuration & Options UI Supports Modern WoW 12.x / Retail Settings API and Standalone UI ]] local addonName, addonTable = ... local CritTracker = addonTable.CritTracker local panel = CreateFrame("Frame", "CritTrackerOptionsPanel", UIParent) CritTracker.optionsPanel = panel panel.name = "CritTracker" -- Standalone Window Styling (Close Button & Dragging) panel:SetSize(660, 560) panel:SetMovable(true) panel:EnableMouse(true) panel:RegisterForDrag("LeftButton") panel:SetScript("OnDragStart", panel.StartMoving) panel:SetScript("OnDragStop", panel.StopMovingOrSizing) -- Close Button for Standalone Mode local closeBtn = CreateFrame("Button", nil, panel, "UIPanelCloseButton") closeBtn:SetPoint("TOPRIGHT", panel, "TOPRIGHT", -6, -6) closeBtn:SetScript("OnClick", function() panel:Hide() end) -- UI Element References for Language Updating local uiLabels = {} local function RegisterLabel(fontString, locKey) table.insert(uiLabels, { element = fontString, key = locKey, isText = true }) fontString:SetText(CritTracker.L[locKey] or locKey) end -- Controls State Update Helper local function UpdateSegmentedButtons(buttons, selectedKey) for key, btn in pairs(buttons) do if key == selectedKey then btn:LockHighlight() btn:SetText("|cffffd100" .. (btn.displayLabel or key) .. "|r") else btn:UnlockHighlight() btn:SetText(btn.displayLabel or key) end end end -- Refresh Panel Displays function CritTracker:UpdateOptionsDisplay() if not self.db then return end -- Checkboxes if self.ui_cbEnable then self.ui_cbEnable:SetChecked(self.db.enabled) end if self.ui_cbChat then self.ui_cbChat:SetChecked(self.db.enableChat) end if self.ui_cbSound then self.ui_cbSound:SetChecked(self.db.enableSound) end if self.ui_cbLow then self.ui_cbLow:SetChecked(self.db.enableLowCrit) end -- Sliders if self.ui_sliderCooldown then self.ui_sliderCooldown:SetValue(self.db.cooldown or 5) self.ui_lblCooldown:SetText(string.format(self.L["OPT_COOLDOWN_TITLE"], self.db.cooldown or 5)) end if self.ui_sliderThreshold then self.ui_sliderThreshold:SetValue(self.db.lowThreshold or 30) self.ui_lblThreshold:SetText(string.format(self.L["OPT_THRESHOLD_TITLE"], self.db.lowThreshold or 30)) end -- Audio Buttons if self.ui_damageButtons then UpdateSegmentedButtons(self.ui_damageButtons, self.db.damageSound or "crit1") end if self.ui_healButtons then UpdateSegmentedButtons(self.ui_healButtons, self.db.healSound or "heal1") end if self.ui_lowButtons then UpdateSegmentedButtons(self.ui_lowButtons, self.db.lowSound or "low") end if self.ui_channelButtons then UpdateSegmentedButtons(self.ui_channelButtons, self.db.soundChannel or "Master") end if self.ui_langButtons then UpdateSegmentedButtons(self.ui_langButtons, self.db.language or "auto") end -- Character Stats local charData = self:GetCharData() local dmgNum = self:FormatNumber(charData.maxDamage) local dmgSpell = (charData.maxDamageSpell ~= "") and charData.maxDamageSpell or self.L["OPT_STATS_NONE"] local healNum = self:FormatNumber(charData.maxHeal) local healSpell = (charData.maxHealSpell ~= "") and charData.maxHealSpell or self.L["OPT_STATS_NONE"] if self.ui_lblDamageRecord then self.ui_lblDamageRecord:SetText(string.format(self.L["OPT_STATS_DAMAGE"], "|cffff4444" .. dmgNum .. "|r", dmgSpell)) end if self.ui_lblHealRecord then self.ui_lblHealRecord:SetText(string.format(self.L["OPT_STATS_HEAL"], "|cff44ff44" .. healNum .. "|r", healSpell)) end -- Dynamic Language Labels for _, item in ipairs(uiLabels) do if item.isText and item.element and item.key then item.element:SetText(self.L[item.key] or item.key) end end end -- Initialize UI Components function CritTracker:InitConfig() -- Title & Header local title = panel:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge") title:SetPoint("TOPLEFT", panel, "TOPLEFT", 20, -18) title:SetText("|cff00e5ffCritTracker|r") local subtitle = panel:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") subtitle:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -4) subtitle:SetText(self.L["ADDON_SUBTITLE"] .. " |cff888888v1.0.0 (TOC 16001)|r") ------------------------------------------------------------------------- -- LEFT COLUMN: General Options, Cooldown/Threshold Sliders, Character Stats ------------------------------------------------------------------------- local colLeftX = 20 local curY = -70 -- Section: General local secGeneral = panel:CreateFontString(nil, "ARTWORK", "GameFontNormal") secGeneral:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX, curY) RegisterLabel(secGeneral, "OPT_SECTION_GENERAL") curY = curY - 24 -- Checkbox: Enable Addon local cbEnable = CreateFrame("CheckButton", "CritTrackerCB_Enable", panel, "UICheckButtonTemplate") cbEnable:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX, curY) cbEnable.Text:SetText(self.L["OPT_ENABLE"]) RegisterLabel(cbEnable.Text, "OPT_ENABLE") cbEnable:SetScript("OnClick", function(selfBtn) CritTracker.db.enabled = selfBtn:GetChecked() end) self.ui_cbEnable = cbEnable curY = curY - 28 -- Checkbox: Enable Chat local cbChat = CreateFrame("CheckButton", "CritTrackerCB_Chat", panel, "UICheckButtonTemplate") cbChat:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX, curY) cbChat.Text:SetText(self.L["OPT_CHAT"]) RegisterLabel(cbChat.Text, "OPT_CHAT") cbChat:SetScript("OnClick", function(selfBtn) CritTracker.db.enableChat = selfBtn:GetChecked() end) self.ui_cbChat = cbChat curY = curY - 28 -- Checkbox: Enable Sound local cbSound = CreateFrame("CheckButton", "CritTrackerCB_Sound", panel, "UICheckButtonTemplate") cbSound:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX, curY) cbSound.Text:SetText(self.L["OPT_SOUND"]) RegisterLabel(cbSound.Text, "OPT_SOUND") cbSound:SetScript("OnClick", function(selfBtn) CritTracker.db.enableSound = selfBtn:GetChecked() end) self.ui_cbSound = cbSound curY = curY - 28 -- Checkbox: Enable Low Crit local cbLow = CreateFrame("CheckButton", "CritTrackerCB_Low", panel, "UICheckButtonTemplate") cbLow:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX, curY) cbLow.Text:SetText(self.L["OPT_LOW"]) RegisterLabel(cbLow.Text, "OPT_LOW") cbLow:SetScript("OnClick", function(selfBtn) CritTracker.db.enableLowCrit = selfBtn:GetChecked() end) self.ui_cbLow = cbLow curY = curY - 36 -- Section: Cooldown & Threshold local secCooldown = panel:CreateFontString(nil, "ARTWORK", "GameFontNormal") secCooldown:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX, curY) RegisterLabel(secCooldown, "OPT_SECTION_COOLDOWN_THRESHOLD") curY = curY - 22 -- Slider: Cooldown (1 - 30 seconds) local lblCooldown = panel:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") lblCooldown:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX + 4, curY) self.ui_lblCooldown = lblCooldown curY = curY - 16 local sliderCooldown = CreateFrame("Slider", "CritTrackerSlider_Cooldown", panel, "OptionsSliderTemplate") sliderCooldown:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX + 6, curY) sliderCooldown:SetWidth(270) sliderCooldown:SetMinMaxValues(1, 30) sliderCooldown:SetValueStep(1) sliderCooldown:SetObeyStepOnDrag(true) _G[sliderCooldown:GetName() .. "Low"]:SetText("1s") _G[sliderCooldown:GetName() .. "High"]:SetText("30s") _G[sliderCooldown:GetName() .. "Text"]:SetText("") sliderCooldown:SetScript("OnValueChanged", function(selfSlider, val) val = math.floor(val + 0.5) CritTracker.db.cooldown = val lblCooldown:SetText(string.format(CritTracker.L["OPT_COOLDOWN_TITLE"], val)) end) self.ui_sliderCooldown = sliderCooldown curY = curY - 42 -- Slider: Low Threshold (5% - 100%, default 30%) local lblThreshold = panel:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") lblThreshold:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX + 4, curY) self.ui_lblThreshold = lblThreshold curY = curY - 16 local sliderThreshold = CreateFrame("Slider", "CritTrackerSlider_Threshold", panel, "OptionsSliderTemplate") sliderThreshold:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX + 6, curY) sliderThreshold:SetWidth(270) sliderThreshold:SetMinMaxValues(5, 100) sliderThreshold:SetValueStep(5) sliderThreshold:SetObeyStepOnDrag(true) _G[sliderThreshold:GetName() .. "Low"]:SetText("5%") _G[sliderThreshold:GetName() .. "High"]:SetText("100%") _G[sliderThreshold:GetName() .. "Text"]:SetText("") sliderThreshold:SetScript("OnValueChanged", function(selfSlider, val) val = math.floor(val + 0.5) CritTracker.db.lowThreshold = val lblThreshold:SetText(string.format(CritTracker.L["OPT_THRESHOLD_TITLE"], val)) end) self.ui_sliderThreshold = sliderThreshold curY = curY - 44 -- Section: Character Records local secRecords = panel:CreateFontString(nil, "ARTWORK", "GameFontNormal") secRecords:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX, curY) RegisterLabel(secRecords, "OPT_SECTION_RECORDS") curY = curY - 20 -- Box for Records local recordBox = CreateFrame("Frame", nil, panel) recordBox:SetPoint("TOPLEFT", panel, "TOPLEFT", colLeftX, curY) recordBox:SetSize(280, 80) local bgTex = recordBox:CreateTexture(nil, "BACKGROUND") bgTex:SetAllPoints() bgTex:SetColorTexture(0, 0, 0, 0.35) local lblCharName = recordBox:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") lblCharName:SetPoint("TOPLEFT", recordBox, "TOPLEFT", 8, -8) lblCharName:SetText("|cff00e5ff" .. CritTracker:GetCharKey() .. "|r") local lblDmg = recordBox:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") lblDmg:SetPoint("TOPLEFT", lblCharName, "BOTTOMLEFT", 0, -6) self.ui_lblDamageRecord = lblDmg local lblHeal = recordBox:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") lblHeal:SetPoint("TOPLEFT", lblDmg, "BOTTOMLEFT", 0, -4) self.ui_lblHealRecord = lblHeal -- Reset Button local resetBtn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate") resetBtn:SetPoint("TOPLEFT", recordBox, "BOTTOMLEFT", 0, -8) resetBtn:SetSize(160, 24) resetBtn:SetText(self.L["OPT_RESET_BUTTON"]) RegisterLabel(resetBtn:GetFontString(), "OPT_RESET_BUTTON") resetBtn:SetScript("OnClick", function() CritTracker:ResetRecords() end) ------------------------------------------------------------------------- -- RIGHT COLUMN: Audio Selection, Test Buttons, Language ------------------------------------------------------------------------- local colRightX = 330 local curRightY = -70 -- Section: Audio local secAudio = panel:CreateFontString(nil, "ARTWORK", "GameFontNormal") secAudio:SetPoint("TOPLEFT", panel, "TOPLEFT", colRightX, curRightY) RegisterLabel(secAudio, "OPT_SECTION_AUDIO") curRightY = curRightY - 24 -- Damage Sound Row local lblDmgSound = panel:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") lblDmgSound:SetPoint("TOPLEFT", panel, "TOPLEFT", colRightX, curRightY) RegisterLabel(lblDmgSound, "OPT_DAMAGE_SOUND") curRightY = curRightY - 18 self.ui_damageButtons = {} local dmgOptions = { "crit1", "crit2", "crit3", "crit4", "none" } local btnX = colRightX for _, opt in ipairs(dmgOptions) do local btn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate") btn:SetPoint("TOPLEFT", panel, "TOPLEFT", btnX, curRightY) btn:SetSize((opt == "none") and 42 or 46, 22) btn.displayLabel = (opt == "none") and (self.L["OPT_SOUND_NONE"]) or opt btn:SetText(btn.displayLabel) btn:SetScript("OnClick", function() CritTracker.db.damageSound = opt UpdateSegmentedButtons(CritTracker.ui_damageButtons, opt) end) self.ui_damageButtons[opt] = btn btnX = btnX + ((opt == "none") and 44 or 48) end local testDmgBtn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate") testDmgBtn:SetPoint("TOPLEFT", panel, "TOPLEFT", btnX + 4, curRightY) testDmgBtn:SetSize(52, 22) testDmgBtn:SetText("►") testDmgBtn:SetScript("OnClick", function() CritTracker:PlaySound(CritTracker.db.damageSound or "crit1") end) curRightY = curRightY - 34 -- Heal Sound Row local lblHealSound = panel:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") lblHealSound:SetPoint("TOPLEFT", panel, "TOPLEFT", colRightX, curRightY) RegisterLabel(lblHealSound, "OPT_HEAL_SOUND") curRightY = curRightY - 18 self.ui_healButtons = {} local healOptions = { "heal1", "heal2", "heal3", "heal4", "none" } btnX = colRightX for _, opt in ipairs(healOptions) do local btn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate") btn:SetPoint("TOPLEFT", panel, "TOPLEFT", btnX, curRightY) btn:SetSize((opt == "none") and 42 or 46, 22) btn.displayLabel = (opt == "none") and (self.L["OPT_SOUND_NONE"]) or opt btn:SetText(btn.displayLabel) btn:SetScript("OnClick", function() CritTracker.db.healSound = opt UpdateSegmentedButtons(CritTracker.ui_healButtons, opt) end) self.ui_healButtons[opt] = btn btnX = btnX + ((opt == "none") and 44 or 48) end local testHealBtn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate") testHealBtn:SetPoint("TOPLEFT", panel, "TOPLEFT", btnX + 4, curRightY) testHealBtn:SetSize(52, 22) testHealBtn:SetText("►") testHealBtn:SetScript("OnClick", function() CritTracker:PlaySound(CritTracker.db.healSound or "heal1") end) curRightY = curRightY - 34 -- Low Sound Row local lblLowSound = panel:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") lblLowSound:SetPoint("TOPLEFT", panel, "TOPLEFT", colRightX, curRightY) RegisterLabel(lblLowSound, "OPT_LOW_SOUND") curRightY = curRightY - 18 self.ui_lowButtons = {} local lowOptions = { "low", "none" } btnX = colRightX for _, opt in ipairs(lowOptions) do local btn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate") btn:SetPoint("TOPLEFT", panel, "TOPLEFT", btnX, curRightY) btn:SetSize((opt == "none") and 42 or 52, 22) btn.displayLabel = (opt == "none") and (self.L["OPT_SOUND_NONE"]) or opt btn:SetText(btn.displayLabel) btn:SetScript("OnClick", function() CritTracker.db.lowSound = opt UpdateSegmentedButtons(CritTracker.ui_lowButtons, opt) end) self.ui_lowButtons[opt] = btn btnX = btnX + ((opt == "none") and 44 or 54) end local testLowBtn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate") testLowBtn:SetPoint("TOPLEFT", panel, "TOPLEFT", btnX + 4, curRightY) testLowBtn:SetSize(52, 22) testLowBtn:SetText("►") testLowBtn:SetScript("OnClick", function() CritTracker:PlaySound(CritTracker.db.lowSound or "low") end) curRightY = curRightY - 36 -- Sound Channel Row local lblChannel = panel:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") lblChannel:SetPoint("TOPLEFT", panel, "TOPLEFT", colRightX, curRightY) RegisterLabel(lblChannel, "OPT_SOUND_CHANNEL") curRightY = curRightY - 18 self.ui_channelButtons = {} local chanOptions = { "Master", "Dialog", "SFX" } btnX = colRightX for _, opt in ipairs(chanOptions) do local btn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate") btn:SetPoint("TOPLEFT", panel, "TOPLEFT", btnX, curRightY) btn:SetSize(62, 22) btn.displayLabel = opt btn:SetText(opt) btn:SetScript("OnClick", function() CritTracker.db.soundChannel = opt UpdateSegmentedButtons(CritTracker.ui_channelButtons, opt) end) self.ui_channelButtons[opt] = btn btnX = btnX + 66 end curRightY = curRightY - 38 -- Section: Language local secLang = panel:CreateFontString(nil, "ARTWORK", "GameFontNormal") secLang:SetPoint("TOPLEFT", panel, "TOPLEFT", colRightX, curRightY) RegisterLabel(secLang, "OPT_SECTION_LANGUAGE") curRightY = curRightY - 22 local lblLang = panel:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") lblLang:SetPoint("TOPLEFT", panel, "TOPLEFT", colRightX, curRightY) RegisterLabel(lblLang, "OPT_LANGUAGE") curRightY = curRightY - 18 self.ui_langButtons = {} local langOptions = { { id = "auto", label = "OPT_LANG_AUTO", width = 85 }, { id = "German", label = "OPT_LANG_DE", width = 85 }, { id = "English", label = "OPT_LANG_EN", width = 85 }, } btnX = colRightX for _, item in ipairs(langOptions) do local btn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate") btn:SetPoint("TOPLEFT", panel, "TOPLEFT", btnX, curRightY) btn:SetSize(item.width, 22) btn.displayLabel = self.L[item.label] or item.id btn:SetText(btn.displayLabel) btn:SetScript("OnClick", function() CritTracker.db.language = item.id CritTracker:UpdateOptionsDisplay() end) self.ui_langButtons[item.id] = btn btnX = btnX + item.width + 4 end -- Initial display refresh panel:SetScript("OnShow", function() CritTracker:UpdateOptionsDisplay() end) -- Register with Modern Settings API if available if Settings and Settings.RegisterCanvasLayoutCategory then local category = Settings.RegisterCanvasLayoutCategory(panel, "CritTracker") Settings.RegisterAddOnCategory(category) CritTracker.settingsCategory = category elseif InterfaceOptions_AddCategory then InterfaceOptions_AddCategory(panel) end end -- Open Options Method function CritTracker:OpenOptions() if InCombatLockdown and InCombatLockdown() then self:Print("|cffff4444[CritTracker]|r Einstellungen können während des Kampfes nicht geöffnet werden.") return end local opened = false if Settings and Settings.OpenToCategory and self.settingsCategory then local ok = pcall(function() Settings.OpenToCategory(self.settingsCategory:GetID()) end) if ok then opened = true end end if not opened and InterfaceOptionsFrame_OpenToCategory and panel then local ok = pcall(function() InterfaceOptionsFrame_OpenToCategory(panel) end) if ok then opened = true end end -- Fallback to Standalone Movable Dialog Window if not opened and panel then if panel:GetParent() ~= UIParent then panel:SetParent(UIParent) end if not panel.hasBackdrop and BackdropTemplateMixin then Mixin(panel, BackdropTemplateMixin) panel:SetBackdrop({ bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background-Dark", edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", tile = true, tileSize = 32, edgeSize = 32, insets = { left = 8, right = 8, top = 8, bottom = 8 }, }) panel.hasBackdrop = true end panel:ClearAllPoints() panel:SetPoint("CENTER", UIParent, "CENTER", 0, 0) panel:Show() self:UpdateOptionsDisplay() end end