local ADDON_NAME = "HailMaryGuildTools" local HMGT = _G[ADDON_NAME] if not HMGT then return end local L = HMGT.L or LibStub("AceLocale-3.0"):GetLocale(ADDON_NAME) local AceGUI = LibStub("AceGUI-3.0", true) local function GetOrderedDebugLevels() return { "error", "info", "verbose" } end local function GetOrderedDebugScopes() local values = HMGT:GetDebugScopeOptions() or {} local names = { "ALL" } for scope in pairs(values) do if scope ~= "ALL" then names[#names + 1] = scope end end table.sort(names, function(a, b) if a == "ALL" then return true end if b == "ALL" then return false end return tostring(values[a] or a) < tostring(values[b] or b) end) return names, values end local function SetFilterButtonText(buttonWidget, prefix, valueLabel) if not buttonWidget then return end buttonWidget:SetText(string.format("%s: %s", tostring(prefix or ""), tostring(valueLabel or ""))) end local function AdvanceDebugLevel(step) local levels = GetOrderedDebugLevels() local current = HMGT:GetConfiguredDebugLevel() local nextIndex = 1 for index, value in ipairs(levels) do if value == current then nextIndex = index + (step or 1) break end end if nextIndex < 1 then nextIndex = #levels elseif nextIndex > #levels then nextIndex = 1 end HMGT.db.profile.debugLevel = levels[nextIndex] HMGT:RefreshDebugWindow() end local function AdvanceDebugScope(step) local scopes, labels = GetOrderedDebugScopes() local current = (HMGT.db and HMGT.db.profile and HMGT.db.profile.debugScope) or "ALL" local nextIndex = 1 for index, value in ipairs(scopes) do if value == current then nextIndex = index + (step or 1) break end end if nextIndex < 1 then nextIndex = #scopes elseif nextIndex > #scopes then nextIndex = 1 end HMGT.db.profile.debugScope = scopes[nextIndex] HMGT:RefreshDebugWindow() end function HMGT:SetDebugWindowMinimized(minimized) local frame = self.debugWindow if not frame then return end minimized = minimized and true or false self.debugWindowStatus = self.debugWindowStatus or { width = 860, height = 340, } self.debugWindowStatus.minimized = minimized local collapsedHeight = 64 if minimized then self.debugWindowStatus.restoreHeight = self.debugWindowStatus.height or frame:GetHeight() or 340 end local targetHeight = minimized and collapsedHeight or (self.debugWindowStatus.restoreHeight or self.debugWindowStatus.height or 340) if frame.aceWidget then frame.aceWidget:EnableResize(not minimized) frame.aceWidget:SetHeight(targetHeight) else frame:SetHeight(targetHeight) end if frame.minimizeButton then frame.minimizeButton:SetText(minimized and "+" or "-") end if frame.clearButton then local buttonFrame = frame.clearButton.frame or frame.clearButton buttonFrame:SetShown(not minimized) end if frame.selectButton then local buttonFrame = frame.selectButton.frame or frame.selectButton buttonFrame:SetShown(not minimized) end if frame.levelFilter then local filterFrame = frame.levelFilter.frame or frame.levelFilter filterFrame:SetShown(not minimized) end if frame.scopeFilter then local filterFrame = frame.scopeFilter.frame or frame.scopeFilter filterFrame:SetShown(not minimized) end if frame.logWidget then frame.logWidget.frame:SetShown(not minimized) end if frame.scrollBG then frame.scrollBG:SetShown(not minimized) end if not minimized then self:RefreshDebugWindow() end end function HMGT:ToggleDebugWindowMinimized() self:SetDebugWindowMinimized(not (self.debugWindowStatus and self.debugWindowStatus.minimized)) end function HMGT:EnsureDebugWindow() if self.debugWindow then return self.debugWindow end local frameWidget if AceGUI then frameWidget = AceGUI:Create("Frame") self.debugWindowStatus = self.debugWindowStatus or { width = 860, height = 340, } frameWidget:SetTitle(L["DEBUG_WINDOW_TITLE"] or "HMGT Debug Console") frameWidget:SetStatusText(L["DEBUG_WINDOW_HINT"] or "Mouse wheel scrolls, Ctrl+A selects all, Ctrl+C copies selected text") frameWidget:SetStatusTable(self.debugWindowStatus) frameWidget:SetWidth(self.debugWindowStatus.width or 860) frameWidget:SetHeight(self.debugWindowStatus.height or 340) frameWidget:EnableResize(true) frameWidget.frame:SetClampedToScreen(true) frameWidget.frame:SetToplevel(true) frameWidget.frame:SetFrameStrata("FULLSCREEN_DIALOG") frameWidget:Hide() end local frame = frameWidget and frameWidget.frame or CreateFrame("Frame", "HMGT_DebugWindow", UIParent, "BackdropTemplate") if not frameWidget then frame:SetSize(860, 340) frame:SetPoint("CENTER", UIParent, "CENTER", 0, 0) frame:SetFrameStrata("DIALOG") frame:SetClampedToScreen(true) frame:SetMovable(true) frame:EnableMouse(true) frame:RegisterForDrag("LeftButton") frame:SetScript("OnDragStart", function(selfFrame) selfFrame:StartMoving() end) frame:SetScript("OnDragStop", function(selfFrame) selfFrame:StopMovingOrSizing() end) frame:SetBackdrop({ bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background-Dark", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", edgeSize = 12, insets = { left = 3, right = 3, top = 3, bottom = 3 }, }) frame:SetBackdropColor(0.05, 0.05, 0.06, 0.95) frame:SetBackdropBorderColor(0.35, 0.55, 0.85, 1) frame:Hide() local title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") title:SetPoint("TOPLEFT", frame, "TOPLEFT", 14, -12) title:SetText(L["DEBUG_WINDOW_TITLE"] or "HMGT Debug Console") frame.title = title local closeButton = CreateFrame("Button", nil, frame, "UIPanelCloseButton") closeButton:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -5, -5) frame.closeButton = closeButton local minimizeButton = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate") minimizeButton:SetSize(22, 20) minimizeButton:SetPoint("TOPRIGHT", closeButton, "TOPLEFT", -2, 0) minimizeButton:SetText((self.debugWindowStatus and self.debugWindowStatus.minimized) and "+" or "-") minimizeButton:SetScript("OnClick", function() HMGT:ToggleDebugWindowMinimized() end) frame.minimizeButton = minimizeButton end frame.aceWidget = frameWidget if frameWidget and AceGUI then local content = frameWidget.content local minimizeButton = AceGUI:Create("Button") minimizeButton:SetText((self.debugWindowStatus and self.debugWindowStatus.minimized) and "+" or "-") minimizeButton:SetWidth(24) minimizeButton:SetCallback("OnClick", function() HMGT:ToggleDebugWindowMinimized() end) minimizeButton.frame:SetParent(frame) minimizeButton.frame:ClearAllPoints() minimizeButton.frame:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -34, -4) minimizeButton.frame:SetHeight(20) minimizeButton.frame:Show() frame.minimizeButton = minimizeButton local clearButton = AceGUI:Create("Button") clearButton:SetText(L["OPT_DEBUG_CLEAR"] or "Clear log") clearButton:SetWidth(120) clearButton:SetCallback("OnClick", function() HMGT:ClearDebugLog() end) clearButton.frame:SetParent(content) clearButton.frame:ClearAllPoints() clearButton.frame:SetPoint("TOPRIGHT", content, "TOPRIGHT", 0, -2) clearButton.frame:Show() frame.clearButton = clearButton local selectButton = AceGUI:Create("Button") selectButton:SetText(L["OPT_DEBUG_SELECT_ALL"] or "Select all") selectButton:SetWidth(120) selectButton:SetCallback("OnClick", function() if frame.editBox then frame.editBox:SetFocus() frame.editBox:HighlightText(0) end end) selectButton.frame:SetParent(content) selectButton.frame:ClearAllPoints() selectButton.frame:SetPoint("TOPRIGHT", clearButton.frame, "TOPLEFT", -6, 0) selectButton.frame:Show() frame.selectButton = selectButton local levelFilter = AceGUI:Create("Button") levelFilter:SetWidth(150) levelFilter:SetCallback("OnClick", function() AdvanceDebugLevel(1) end) levelFilter.frame:SetParent(content) levelFilter.frame:ClearAllPoints() levelFilter.frame:SetPoint("TOPLEFT", content, "TOPLEFT", 0, 0) levelFilter.frame:Show() frame.levelFilter = levelFilter local scopeFilter = AceGUI:Create("Button") scopeFilter:SetWidth(180) scopeFilter:SetCallback("OnClick", function() AdvanceDebugScope(1) end) scopeFilter.frame:SetParent(content) scopeFilter.frame:ClearAllPoints() scopeFilter.frame:SetPoint("TOPLEFT", levelFilter.frame, "TOPRIGHT", 8, 0) scopeFilter.frame:Show() frame.scopeFilter = scopeFilter local logWidget = AceGUI:Create("MultiLineEditBox") logWidget:SetLabel("") logWidget:DisableButton(true) logWidget:SetNumLines(18) logWidget:SetText("") logWidget.frame:SetParent(content) logWidget.frame:ClearAllPoints() logWidget.frame:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -54) logWidget.frame:SetPoint("BOTTOMRIGHT", content, "BOTTOMRIGHT", 0, 0) logWidget.frame:Show() logWidget:SetCallback("OnTextChanged", function() HMGT:RefreshDebugWindow() end) logWidget.editBox:SetScript("OnKeyDown", function(selfBox, key) if IsControlKeyDown() and (key == "A" or key == "a") then selfBox:HighlightText(0) end end) frame.logWidget = logWidget frame.editBox = logWidget.editBox frame.scrollFrame = logWidget.scrollFrame self.debugWindow = frame self:SetDebugWindowMinimized(self.debugWindowStatus and self.debugWindowStatus.minimized) return frame end local clearButton = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate") clearButton:SetSize(90, 22) clearButton:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -30, -6) clearButton:SetText(L["OPT_DEBUG_CLEAR"] or "Clear log") clearButton:SetScript("OnClick", function() HMGT:ClearDebugLog() end) frame.clearButton = clearButton local selectButton = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate") selectButton:SetSize(90, 22) selectButton:SetPoint("TOPRIGHT", clearButton, "TOPLEFT", -6, 0) selectButton:SetText(L["OPT_DEBUG_SELECT_ALL"] or "Select all") selectButton:SetScript("OnClick", function() if frame.editBox then frame.editBox:SetFocus() frame.editBox:HighlightText(0) end end) frame.selectButton = selectButton local scopeFilter = CreateFrame("Frame", nil, frame) scopeFilter:SetSize(170, 22) scopeFilter:SetPoint("TOPLEFT", frame, "TOPLEFT", 16, -8) frame.scopeFilter = scopeFilter local scrollBG = CreateFrame("Frame", nil, frame, "BackdropTemplate") scrollBG:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", edgeSize = 16, insets = { left = 4, right = 3, top = 4, bottom = 3 }, }) scrollBG:SetBackdropColor(0, 0, 0, 0.95) scrollBG:SetBackdropBorderColor(0.4, 0.4, 0.4, 1) scrollBG:SetPoint("TOPLEFT", frame, "TOPLEFT", 14, -36) scrollBG:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -30, 14) frame.scrollBG = scrollBG local scrollFrame = CreateFrame("ScrollFrame", nil, scrollBG, "UIPanelScrollFrameTemplate") scrollFrame:SetPoint("TOPLEFT", scrollBG, "TOPLEFT", 6, -6) scrollFrame:SetPoint("BOTTOMRIGHT", scrollBG, "BOTTOMRIGHT", -27, 4) scrollFrame:EnableMouseWheel(true) scrollFrame:SetScript("OnMouseWheel", function(selfMsg, delta) if delta > 0 then selfMsg:SetVerticalScroll(math.max(0, selfMsg:GetVerticalScroll() - 42)) else selfMsg:SetVerticalScroll(selfMsg:GetVerticalScroll() + 42) end end) frame.scrollFrame = scrollFrame local editBox = CreateFrame("EditBox", nil, scrollFrame) editBox:SetMultiLine(true) editBox:SetAutoFocus(false) editBox:SetFontObject(ChatFontNormal) editBox:SetWidth(780) editBox:SetTextInsets(6, 6, 6, 6) editBox:EnableMouse(true) editBox:SetScript("OnEscapePressed", function(selfBox) selfBox:ClearFocus() end) editBox:SetScript("OnKeyDown", function(selfBox, key) if IsControlKeyDown() and (key == "A" or key == "a") then selfBox:HighlightText(0) end end) editBox:SetScript("OnTextChanged", function(selfBox, userInput) if userInput then HMGT:RefreshDebugWindow() else selfBox:SetCursorPosition(selfBox:GetNumLetters()) selfBox:SetHeight(math.max(scrollFrame:GetHeight(), HMGT:GetDebugWindowTextHeight(frame, selfBox:GetText()) + 16)) scrollFrame:UpdateScrollChildRect() end end) editBox:SetScript("OnMouseUp", function(selfBox) selfBox:SetFocus() end) scrollFrame:SetScrollChild(editBox) frame.editBox = editBox local measureText = frame:CreateFontString(nil, "ARTWORK", "ChatFontNormal") measureText:SetJustifyH("LEFT") measureText:SetJustifyV("TOP") if measureText.SetSpacing then measureText:SetSpacing(2) end measureText:SetWidth(768) frame.measureText = measureText self.debugWindow = frame self:SetDebugWindowMinimized(self.debugWindowStatus and self.debugWindowStatus.minimized) return frame end function HMGT:GetDebugWindowTextHeight(frame, text) if not frame or not frame.measureText then return 0 end local width = 768 if frame.editBox then width = math.max(1, (frame.editBox:GetWidth() or width) - 12) end frame.measureText:SetWidth(width) frame.measureText:SetText(text or "") return frame.measureText:GetStringHeight() end function HMGT:RefreshDebugWindow() local frame = self:EnsureDebugWindow() if not frame then return end local filtered = self:GetFilteredDebugBuffer() or self.debugBuffer or {} local text = table.concat(filtered, "\n") if frame.logWidget and frame.editBox then if frame.levelFilter then local levelOptions = self:GetDebugLevelOptions() SetFilterButtonText(frame.levelFilter, L["OPT_DEBUG_LEVEL"] or "Level", levelOptions[self:GetConfiguredDebugLevel()]) end if frame.scopeFilter then local scopeOptions = self:GetDebugScopeOptions() local currentScope = (self.db and self.db.profile and self.db.profile.debugScope) or "ALL" SetFilterButtonText(frame.scopeFilter, L["OPT_DEBUG_SCOPE"] or "Module", scopeOptions[currentScope] or currentScope) end frame.logWidget:SetText(text) frame.editBox:SetCursorPosition(frame.editBox:GetNumLetters()) return end if not frame.editBox then return end frame.editBox:SetText(text) frame.editBox:SetCursorPosition(#text) frame.editBox:SetHeight(math.max(frame.scrollFrame:GetHeight(), self:GetDebugWindowTextHeight(frame, text) + 16)) frame.scrollFrame:SetVerticalScroll(math.max(0, frame.editBox:GetHeight() - frame.scrollFrame:GetHeight())) end function HMGT:UpdateDebugWindowVisibility() if self.db and self.db.profile then self.db.profile.debug = false end local frame = self.debugWindow if not frame then return end local widget = frame.aceWidget if widget then widget:Hide() else frame:Hide() end end function HMGT:ClearDebugLog() wipe(self.debugBuffer) if self.debugWindow and self.debugWindow.logWidget then self.debugWindow.logWidget:SetText("") self.debugWindow.editBox:SetCursorPosition(0) self.debugWindow.scrollFrame:SetVerticalScroll(0) return end if self.debugWindow and self.debugWindow.editBox then self.debugWindow.editBox:SetText("") self.debugWindow.scrollFrame:SetVerticalScroll(0) end end function HMGT:ToggleDebugWindowShortcut() if self.db and self.db.profile then self.db.profile.debug = false end local frame = self.debugWindow if not frame then return end local widget = frame.aceWidget if widget then widget:Hide() else frame:Hide() end end function HMGT:DumpDebugLog(maxLines) return end