Files
MetaHunt/api/debug-frame.lua
DuvelCorp b2060e0aab V1.3.0
2026-03-19 16:27:21 +01:00

379 lines
10 KiB
Lua

------------------------------------------------------
-- MetaHunt: Debug Frame
-- Displays errors and debug messages in a scrollable frame
------------------------------------------------------
MTH_DebugFrame = {
frame = nil,
scrollFrame = nil,
scrollText = nil,
errors = {},
maxErrors = 500,
isVisible = false,
initialized = false,
captureCount = 0,
infoCount = 0
}
local function MTH_DF_L(key, default)
if MTH and MTH.GetLocalization then
return MTH:GetLocalization(key, default)
end
return default or key
end
function MTH_DebugFrame:Initialize()
if self.initialized then
return
end
self.initialized = true
-- Create main frame
self.frame = CreateFrame("Frame", "MTH_DebugWindow", UIParent)
self.frame:SetWidth(800)
self.frame:SetHeight(400)
self.frame:SetPoint("CENTER", UIParent, "CENTER")
self.frame:SetFrameStrata("DIALOG")
self.frame:SetBackdrop({
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
tile = true,
tileSize = 32,
edgeSize = 32,
insets = { left = 11, right = 12, top = 12, bottom = 11 }
})
self.frame:SetBackdropColor(0, 0, 0, 0.8)
self.frame:SetMovable(true)
self.frame:EnableMouse(true)
self.frame:RegisterForDrag("LeftButton")
self.frame:SetScript("OnDragStart", function() self.frame:StartMoving() end)
self.frame:SetScript("OnDragStop", function() self.frame:StopMovingOrSizing() end)
if UISpecialFrames then
local already = false
for i = 1, table.getn(UISpecialFrames) do
if UISpecialFrames[i] == "MTH_DebugWindow" then
already = true
break
end
end
if not already then
table.insert(UISpecialFrames, "MTH_DebugWindow")
end
end
-- Title
local title = self.frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
title:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 20, -15)
title:SetText(MTH_DF_L("DEBUG_TITLE", "MetaHunt Debug - Errors & Messages"))
-- Close button
local closeButton = CreateFrame("Button", nil, self.frame, "UIPanelButtonTemplate")
closeButton:SetPoint("TOPRIGHT", self.frame, "TOPRIGHT", -8, -8)
closeButton:SetWidth(22)
closeButton:SetHeight(22)
closeButton:SetText(MTH_DF_L("COMMON_CLOSE_SHORT", "X"))
closeButton:SetScript("OnClick", function() MTH_DebugFrame:Hide() end)
-- Clear button
local clearButton = CreateFrame("Button", nil, self.frame, "GameMenuButtonTemplate")
clearButton:SetPoint("BOTTOMLEFT", self.frame, "BOTTOMLEFT", 10, 10)
clearButton:SetWidth(100)
clearButton:SetHeight(25)
clearButton:SetText(MTH_DF_L("COMMON_CLEAR", "Clear"))
clearButton:SetScript("OnClick", function() MTH_DebugFrame:Clear() end)
-- Select-all button
local copyButton = CreateFrame("Button", nil, self.frame, "GameMenuButtonTemplate")
copyButton:SetPoint("LEFT", clearButton, "RIGHT", 10, 0)
copyButton:SetWidth(100)
copyButton:SetHeight(25)
copyButton:SetText(MTH_DF_L("COMMON_SELECT_ALL", "Select All"))
copyButton:SetScript("OnClick", function() MTH_DebugFrame:SelectAllText() end)
-- Scroll frame + text content with proper vertical scrollbar
self.scrollFrame = CreateFrame("ScrollFrame", "MTH_DebugScroll", self.frame, "UIPanelScrollFrameTemplate")
self.scrollFrame:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 15, -50)
self.scrollFrame:SetPoint("BOTTOMRIGHT", self.frame, "BOTTOMRIGHT", -35, 45)
self.scrollText = CreateFrame("EditBox", "MTH_DebugScrollText", self.scrollFrame)
self.scrollText:SetMultiLine(true)
self.scrollText:SetMaxLetters(999999)
self.scrollText:SetFont("Fonts\\FRIZQT__.TTF", 11, "")
self.scrollText:SetTextColor(0.8, 0.8, 0.8, 1)
self.scrollText:SetWidth(730)
self.scrollText:SetHeight(1)
self.scrollText:EnableMouse(true)
self.scrollText:SetScript("OnCursorChanged", function()
if not MTH_DebugFrame.scrollFrame then
return
end
local width = MTH_DebugFrame.scrollFrame:GetWidth()
if width and width > 20 then
MTH_DebugFrame.scrollText:SetWidth(width - 20)
end
end)
self.scrollText:SetScript("OnEscapePressed", function() self.scrollText:ClearFocus() end)
self.scrollText:SetAutoFocus(false)
self.scrollText:EnableMouseWheel(true)
self.scrollText:SetScript("OnMouseWheel", function()
if not MTH_DebugFrame.scrollFrame then
return
end
local curr = MTH_DebugFrame.scrollFrame:GetVerticalScroll()
local step = 24
if arg1 and arg1 > 0 then
MTH_DebugFrame.scrollFrame:SetVerticalScroll(curr - step)
else
MTH_DebugFrame.scrollFrame:SetVerticalScroll(curr + step)
end
end)
self.scrollText:SetScript("OnTextChanged", nil)
self.scrollFrame:SetScrollChild(self.scrollText)
-- Hide by default
self.frame:Hide()
self.isVisible = false
end
function MTH_DebugFrame:AddError(message)
if not message then return end
self.captureCount = self.captureCount + 1
if string.len(message) > 1000 then
message = string.sub(message, 1, 1000) .. "..."
end
table.insert(self.errors, 1, {
time = date("%H:%M:%S"),
message = message,
level = "error"
})
-- Limit stored errors
if table.getn(self.errors) > self.maxErrors then
table.remove(self.errors, self.maxErrors + 1)
end
self:UpdateDisplay()
end
function MTH_DebugFrame:AddInfo(message)
if not message then return end
self.infoCount = self.infoCount + 1
if string.len(message) > 1000 then
message = string.sub(message, 1, 1000) .. "..."
end
table.insert(self.errors, 1, {
time = date("%H:%M:%S"),
message = message,
level = "info"
})
if table.getn(self.errors) > self.maxErrors then
table.remove(self.errors, self.maxErrors + 1)
end
self:UpdateDisplay()
end
function MTH_DebugFrame:UpdateDisplay()
if not self.scrollText or not self.initialized then
return
end
local text = string.format(MTH_DF_L("DEBUG_SUMMARY_ERRORS", "=== Errors Captured: %s ==="), tostring(self.captureCount)) .. "\n"
text = text .. string.format(MTH_DF_L("DEBUG_SUMMARY_INFO", "=== Info Captured: %s ==="), tostring(self.infoCount or 0)) .. "\n"
text = text .. string.format(MTH_DF_L("DEBUG_SUMMARY_QUEUE", "=== Total in queue: %s ==="), tostring(table.getn(self.errors))) .. "\n\n"
for i = 1, table.getn(self.errors) do
local err = self.errors[i]
text = text .. "[" .. err.time .. "] " .. err.message .. "\n"
end
local _, lineCount = string.gsub(text, "\n", "\n")
local lineHeight = 13
local contentHeight = (lineCount + 1) * lineHeight + 8
if contentHeight < 1 then
contentHeight = 1
end
self.scrollText:SetHeight(contentHeight)
self.scrollText:SetText(text)
if self.scrollFrame then
self.scrollFrame:SetVerticalScroll(0)
end
end
function MTH_DebugFrame:Toggle()
if self.isVisible then
self.frame:Hide()
self.isVisible = false
else
if not self.initialized then
self:Initialize()
end
self.frame:Show()
self.isVisible = true
self:UpdateDisplay()
end
end
function MTH_DebugFrame:Show()
if not self.initialized then
self:Initialize()
end
self.frame:Show()
self.isVisible = true
self:UpdateDisplay()
end
function MTH_DebugFrame:Hide()
if self.frame then
self.frame:Hide()
end
self.isVisible = false
end
function MTH_DebugFrame:Clear()
self.errors = {}
self.captureCount = 0
self.infoCount = 0
self:UpdateDisplay()
if self.initialized and self.isVisible then
if MTH and MTH.Print then
MTH:Print(MTH_DF_L("DEBUG_CLEARED", "Debug frame cleared"))
end
end
end
function MTH_DebugFrame:SelectAllText()
if not self.scrollText or not self.initialized then
return
end
-- Select all text
self.scrollText:HighlightText(0, -1)
self.scrollText:SetFocus()
if MTH and MTH.Print then
MTH:Print(MTH_DF_L("DEBUG_ALL_SELECTED", "All text selected - press Ctrl+C to copy"))
end
end
------------------------------------------------------
-- Error Capture Hooks
------------------------------------------------------
local function MTH_DF_AddMessageCapture(msg)
if msg then
local msgStr = tostring(msg)
if string.find(msgStr, "%.lua:%d+:") then
MTH_DebugFrame:AddError(msgStr)
end
end
end
local function MTH_DF_IsGlobalCaptureEnabled()
if MTH and MTH.GetConfig then
return MTH:GetConfig("debug", "globalErrorCapture", false) and true or false
end
return false
end
local function MTH_DF_SetGlobalCaptureEnabled(enabled)
if MTH and MTH.SetConfig then
MTH:SetConfig("debug", "globalErrorCapture", enabled and true or false)
end
end
local function MTH_DF_InstallHooks()
if MTH_DebugFrame.hooksInstalled then
return
end
if not MTH_DF_IsGlobalCaptureEnabled() then
return
end
if DEFAULT_CHAT_FRAME and type(DEFAULT_CHAT_FRAME.AddMessage) == "function" then
MTH_DebugFrame.originalAddMessage = DEFAULT_CHAT_FRAME.AddMessage
MTH_DebugFrame.addMessageHook = function(self, msg, arg1, arg2, arg3, arg4, arg5)
MTH_DF_AddMessageCapture(msg)
if MTH_DebugFrame.originalAddMessage then
return MTH_DebugFrame.originalAddMessage(self, msg, arg1, arg2, arg3, arg4, arg5)
end
end
DEFAULT_CHAT_FRAME.AddMessage = MTH_DebugFrame.addMessageHook
end
if _G and type(_G["ScriptErrors"]) == "function" then
MTH_DebugFrame.originalScriptErrors = _G["ScriptErrors"]
MTH_DebugFrame.scriptErrorsHook = function(msg)
if msg then
MTH_DebugFrame:AddError(tostring(msg))
end
if MTH_DebugFrame.originalScriptErrors then
return MTH_DebugFrame.originalScriptErrors(msg)
end
end
_G["ScriptErrors"] = MTH_DebugFrame.scriptErrorsHook
end
MTH_DebugFrame.hooksInstalled = true
end
local function MTH_DF_UninstallHooks()
if not MTH_DebugFrame.hooksInstalled then
return
end
if DEFAULT_CHAT_FRAME
and MTH_DebugFrame.addMessageHook
and DEFAULT_CHAT_FRAME.AddMessage == MTH_DebugFrame.addMessageHook
and MTH_DebugFrame.originalAddMessage
then
DEFAULT_CHAT_FRAME.AddMessage = MTH_DebugFrame.originalAddMessage
end
if _G
and MTH_DebugFrame.scriptErrorsHook
and _G["ScriptErrors"] == MTH_DebugFrame.scriptErrorsHook
and MTH_DebugFrame.originalScriptErrors
then
_G["ScriptErrors"] = MTH_DebugFrame.originalScriptErrors
end
MTH_DebugFrame.hooksInstalled = false
end
function MTH_DebugFrame:IsGlobalCaptureEnabled()
return MTH_DF_IsGlobalCaptureEnabled()
end
function MTH_DebugFrame:SetGlobalCaptureEnabled(enabled)
local want = enabled and true or false
MTH_DF_SetGlobalCaptureEnabled(want)
if want then
MTH_DF_InstallHooks()
else
MTH_DF_UninstallHooks()
end
end
MTH_DebugFrame.InstallHooks = MTH_DF_InstallHooks
MTH_DebugFrame.UninstallHooks = MTH_DF_UninstallHooks
-- Event listener for addon load
local eventFrame = CreateFrame("Frame")
eventFrame:RegisterEvent("ADDON_LOADED")
eventFrame:SetScript("OnEvent", function()
if event == "ADDON_LOADED" and arg1 == "MetaHunt" then
if MTH and MTH.GetConfig then
MTH_DF_SetGlobalCaptureEnabled(MTH:GetConfig("debug", "globalErrorCapture", false))
end
MTH_DF_InstallHooks()
MTH_DebugFrame:Initialize()
end
end)