local modulo = math.modulo local getn = table.getn EVENT_TRACE_EVENT_HEIGHT = 16 EVENT_TRACE_MAX_ENTRIES = 1000 DEBUGLOCALS_LEVEL = 4 local _normalFontColor = {1, .82, 0, 1} EVENT_TRACE_SYSTEM_TIMES = {} EVENT_TRACE_SYSTEM_TIMES["System"] = true EVENT_TRACE_SYSTEM_TIMES["Elapsed"] = true EVENT_TRACE_EVENT_COLORS = {} EVENT_TRACE_EVENT_COLORS["System"] = _normalFontColor EVENT_TRACE_EVENT_COLORS["Elapsed"] = {.6, .6, .6, 1} local _EventTraceFrame local _framesSinceLast = 0 local _timeSinceLast = 0 local _timer = CreateFrame("FRAME") _timer:SetScript("OnUpdate", function () _framesSinceLast = _framesSinceLast + 1 _timeSinceLast = _timeSinceLast + arg1 end) function EventTraceFrame_OnLoad(self) local frameName = self:GetName() _G[frameName.."Scroll"].thumb = _G[frameName.."ScrollThumb"] _G[frameName.."DialogBG"]:SetVertexColor(0, 0, 0) _G[frameName.."DialogBG"]:SetAlpha(0.75) self.buttons = {} self.events = {} self.times = {} self.rawtimes = {} self.eventids = {} self.eventtimes = {} self.numhandlers = {} self.slowesthandlers = {} self.slowesthandlertimes = {} self.timeSinceLast = {} self.framesSinceLast = {} self.args = {{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}} self.ignoredEvents = {} self.lastIndex = 0 self.visibleButtons = 0 _EventTraceFrame = self self:SetScript("OnSizeChanged", function() EventTraceFrame_OnSizeChanged(this, this:GetWidth(), this:GetHeight()) end) EventTraceFrame_OnSizeChanged(self, self:GetWidth(), self:GetHeight()) self:EnableMouse(true) self:EnableMouseWheel(true) self:SetScript("OnMouseWheel", function() EventTraceFrame_OnMouseWheel(this, arg1) end) end local _workTable = {} function EventTraceFrame_OnEvent(self, event, ...) if not self.ignoredEvents[event] then if _framesSinceLast ~= 0 and event ~= "On Update" then EventTraceFrame_OnEvent(self, "On Update") end local nextIndex = self.lastIndex + 1 if nextIndex > EVENT_TRACE_MAX_ENTRIES then local staleIndex = nextIndex - EVENT_TRACE_MAX_ENTRIES self.events[staleIndex] = nil self.times[staleIndex] = nil self.rawtimes[staleIndex] = nil self.timeSinceLast[staleIndex] = nil self.framesSinceLast[staleIndex] = nil self.eventids[staleIndex] = nil self.eventtimes[staleIndex] = nil self.numhandlers[staleIndex] = nil self.slowesthandlers[staleIndex] = nil self.slowesthandlertimes[staleIndex] = nil for k, v in next, self.args do self.args[k][staleIndex] = nil end end if event == "Begin Capture" or event == "End Capture" then self.times[nextIndex] = "System" if self.eventsToCapture then self.events[nextIndex] = string.format("%s (%s events)", event, tostring(self.eventsToCapture)) else self.events[nextIndex] = event end self.timeSinceLast[nextIndex] = 0 self.framesSinceLast[nextIndex] = 0 elseif event == "On Update" then self.times[nextIndex] = "Elapsed" self.events[nextIndex] = string.format("%.3f sec - %d frame(s)", _timeSinceLast, _framesSinceLast) self.timeSinceLast[nextIndex] = _timeSinceLast self.framesSinceLast[nextIndex] = _framesSinceLast _timeSinceLast = 0 _framesSinceLast = 0 else self.events[nextIndex] = event local seconds = GetTime() local minutes = math.floor(math.floor(seconds) / 60) local hours = math.floor(minutes / 60) seconds = seconds - 60 * minutes minutes = minutes - 60 * hours hours = modulo(hours, 1000) self.times[nextIndex] = string.format("%.2d:%.2d:%06.3f", hours, minutes, seconds) self.timeSinceLast[nextIndex] = 0 self.framesSinceLast[nextIndex] = 0 self.eventids[nextIndex] = GetCurrentEventID() local numArgs = getn(arg) for i = 1, numArgs do if not self.args[i] then self.args[i] = {} end self.args[i][nextIndex] = arg[i] end if self.eventsToCapture then self.eventsToCapture = self.eventsToCapture - 1 end end self.rawtimes[nextIndex] = GetTime() self.lastIndex = nextIndex if self.eventsToCapture and self.eventsToCapture <= 0 then self.eventsToCapture = nil EventTraceFrame_StopEventCapture() end end end function EventTraceFrame_OnShow(self) wipe(self.ignoredEvents) local scrollBar = _G["EventTraceFrameScroll"] local minValue, maxValue = scrollBar:GetMinMaxValues() scrollBar:SetValue(maxValue) end function EventTraceFrame_OnUpdate(self, elapsed) EventTraceFrame_Update() end function EventTraceFrame_OnSizeChanged(self, width, height) local numButtonsToDisplay = math.floor((height - 36)/EVENT_TRACE_EVENT_HEIGHT) local numButtonsCreated = getn(self.buttons) if numButtonsCreated < numButtonsToDisplay then for i = numButtonsCreated + 1, numButtonsToDisplay do local button = CreateFrame("BUTTON", "EventTraceFrameButton" .. i, self, "EventTraceEventTemplate") button:SetPoint("BOTTOMLEFT", 12, (16 * (i - 1)) + 12) button:SetPoint("RIGHT", -28, 0) tinsert(self.buttons, button) end for i = self.visibleButtons + 1, numButtonsToDisplay do self.buttons[i]:Show() end self.visibleButtons = numButtonsToDisplay EventTraceFrame_Update() elseif self.visibleButtons < numButtonsToDisplay then for i = self.visibleButtons + 1, numButtonsToDisplay do self.buttons[i]:Show() end self.visibleButtons = numButtonsToDisplay EventTraceFrame_Update() elseif numButtonsToDisplay < self.visibleButtons then for i = numButtonsToDisplay + 1, self.visibleButtons do self.buttons[i]:Hide() end self.visibleButtons = numButtonsToDisplay end end function EventTraceFrame_Update () local offset = 0 local scrollBar = _G["EventTraceFrameScroll"] local scrollBarValue = scrollBar:GetValue() local minValue, maxValue = scrollBar:GetMinMaxValues() local firstID = max(1, _EventTraceFrame.lastIndex - EVENT_TRACE_MAX_ENTRIES + 1) local lastID = _EventTraceFrame.lastIndex or 1 if firstID >= lastID then scrollBar:SetMinMaxValues(firstID-1, lastID) else scrollBar:SetMinMaxValues(firstID, lastID) end if scrollBarValue < firstID then scrollBar:SetValue(firstID) scrollBarValue = firstID end if scrollBarValue < 1 then scrollBarValue = 1 elseif not _EventTraceFrame.selectedEvent then if scrollBarValue == maxValue then scrollBar:SetValue(_EventTraceFrame.lastIndex) end end for i = 1, _EventTraceFrame.visibleButtons do local button = _EventTraceFrame.buttons[i] if button then local index = scrollBarValue - (i - 1) local event = _EventTraceFrame.events[index] if event then local timeString = _EventTraceFrame.times[index] button.index = index button.time:SetText(timeString) button.event:SetText(event) if _EventTraceFrame.eventids[index] and not _EventTraceFrame.eventtimes[index] then local eventTime, numHandlers, slowestHandler, slowestHandlerTime = GetEventTime(_EventTraceFrame.eventids[index]) _EventTraceFrame.eventtimes[index] = eventTime _EventTraceFrame.numhandlers[index] = numHandlers _EventTraceFrame.slowesthandlers[index] = slowestHandler _EventTraceFrame.slowesthandlertimes[index] = slowestHandlerTime end local color = EVENT_TRACE_EVENT_COLORS[event] or EVENT_TRACE_EVENT_COLORS[timeString] if color then button.time:SetTextColor(unpack(color)) button.event:SetTextColor(unpack(color)) else local eventTime = _EventTraceFrame.eventtimes[index] if eventTime and eventTime > 50.0 then button.time:SetTextColor(1, 0, 0, 1) button.event:SetTextColor(1, 0, 0, 1) elseif eventTime and eventTime > 20.0 then button.time:SetTextColor(1, .5, 0, 1) button.event:SetTextColor(1, .5, 0, 1) elseif eventTime and eventTime > 10.0 then button.time:SetTextColor(1, .8, 0, 1) button.event:SetTextColor(1, .8, 0, 1) elseif eventTime and eventTime > 5.0 then button.time:SetTextColor(1, 1, .6, 1) button.event:SetTextColor(1, 1, .6, 1) else button.time:SetTextColor(1, 1, 1, 1) button.event:SetTextColor(1, 1, 1, 1) end end button:Show() if _EventTraceFrame.selectedEvent then if index == _EventTraceFrame.selectedEvent then EventTraceFrameEvent_DisplayTooltip(button) button:GetHighlightTexture():SetVertexColor(.15, .25, 1, .35) button:LockHighlight(true) button.wasSelected = true elseif button.wasSelected then button.wasSelected = nil button:GetHighlightTexture():SetVertexColor(.8, .8, 1, .15) button:UnlockHighlight() end else if button.wasSelected then button.wasSelected = nil button:GetHighlightTexture():SetVertexColor(.8, .8, 1, .15) button:UnlockHighlight() end end if button == GetMouseFocus() then EventTraceFrameEvent_OnEnter(button) else button.HideButton:Hide() end else button.index = index button:Hide() end end end EventTraceFrame_UpdateKeyboardStatus() end function EventTraceFrame_StartEventCapture() if _EventTraceFrame.started then return end EventHandler_Enable() _EventTraceFrame.started = true _framesSinceLast = 0 _timeSinceLast = 0 _EventTraceFrame:RegisterAllEvents() EventTraceFrame_OnEvent(_EventTraceFrame, "Begin Capture") end function EventTraceFrame_StopEventCapture() if not _EventTraceFrame.started then return end EventHandler_Disable() _EventTraceFrame.started = false _framesSinceLast = 0 _timeSinceLast = 0 _EventTraceFrame:UnregisterAllEvents() EventTraceFrame_OnEvent(_EventTraceFrame, "End Capture") end function EventTraceFrame_HandleSlashCmd(msg) msg = strlower(msg) if msg == "start" then EventTraceFrame_StartEventCapture() elseif msg == "stop" then EventTraceFrame_StopEventCapture() elseif tonumber(msg) and tonumber(msg) > 0 then if not _EventTraceFrame.started then _EventTraceFrame.eventsToCapture = tonumber(msg) EventTraceFrame_StartEventCapture() end elseif msg == "" then if not _EventTraceFrame:IsShown() then _EventTraceFrame:Show() if _EventTraceFrame.started == nil then EventTraceFrame_StartEventCapture() end else _EventTraceFrame:Hide() end end end function EventTraceFrame_OnMouseWheel(self, delta) local scrollBar = _G["EventTraceFrameScroll"] local minVal, maxVal = scrollBar:GetMinMaxValues() local currentValue = scrollBar:GetValue() local newValue = currentValue - (delta * 3) newValue = max(newValue, minVal) newValue = min(newValue, maxVal) if newValue ~= currentValue then scrollBar:SetValue(newValue) end end function EventTraceFrame_UpdateKeyboardStatus() if _EventTraceFrame.selectedEvent then local focus = GetMouseFocus() if focus == _EventTraceFrame or (focus and focus:GetParent() == _EventTraceFrame) then _EventTraceFrame:EnableKeyboard(true) return end end _EventTraceFrame:EnableKeyboard(false) end function EventTraceFrame_OnKeyUp(self, key) if key == "ESCAPE" then self.selectedEvent = nil EventTraceTooltip:Hide() EventTraceFrame_Update() end end function EventTraceFrame_RemoveEvent(i) if i >= 1 and i <= EventTraceFrame.lastIndex then tremove(EventTraceFrame.events, i) tremove(EventTraceFrame.times, i) tremove(EventTraceFrame.rawtimes, i) tremove(EventTraceFrame.timeSinceLast, i) tremove(EventTraceFrame.framesSinceLast, i) tremove(EventTraceFrame.eventtimes, i) tremove(EventTraceFrame.eventids, i) tremove(EventTraceFrame.numhandlers, i) tremove(EventTraceFrame.slowesthandlers, i) tremove(EventTraceFrame.slowesthandlertimes, i) for k, v in next, EventTraceFrame.args do for j = i, EventTraceFrame.lastIndex do EventTraceFrame.args[k][j] = EventTraceFrame.args[k][j+1] end end EventTraceFrame.lastIndex = EventTraceFrame.lastIndex-1 end end local TIME_LABEL = "Time:" local DETAILS_LABEL = "Details:" local SLOWEST_LABEL = "Slowest:" local ARGUMENT_LABEL_FORMAT = "arg %d:" local NUM_HANDLERS_FORMAT = "(%d handlers)" local EVENT_TIME_FORMAT = "%.2fms" local function EventTrace_FormatArgValue(val) if type(val) == "string" then return string.format('"%s"', val) elseif type(val) == "number" then return tostring(val) elseif type(val) == "boolean" then return string.format('|cffaaaaff%s|r', tostring(val)) elseif type(val) == "table" or type(val) == "bool" then return string.format('|cffffaaaa%s|r', tostring(val)) end end function EventTraceFrameEvent_DisplayTooltip(eventButton) local index = eventButton.index if not index then return end local tooltip = _G["EventTraceTooltip"] tooltip:SetOwner(eventButton, "ANCHOR_NONE") tooltip:SetPoint("TOPLEFT", eventButton, "TOPRIGHT", 24, 2) local timeString = _EventTraceFrame.times[index] if EVENT_TRACE_SYSTEM_TIMES[timeString] then tooltip:AddLine(timeString, 1, 1, 1) tooltip:AddDoubleLine(TIME_LABEL, _EventTraceFrame.rawtimes[index], 1, .82, 0, 1, 1, 1) tooltip:AddDoubleLine(DETAILS_LABEL, _EventTraceFrame.events[index], 1, .82, 0, 1, 1, 1) else tooltip:AddLine(_EventTraceFrame.events[index], 1, 1, 1) local eventTime = _EventTraceFrame.eventtimes[index] if eventTime then if eventTime < 0 then eventTime = "?" else eventTime = format(EVENT_TIME_FORMAT, eventTime) end tooltip:AddDoubleLine(TIME_LABEL, eventTime .. " " .. format(NUM_HANDLERS_FORMAT, _EventTraceFrame.numhandlers[index] or 0), 1, .82, 0, 1, 1, 1) else tooltip:AddDoubleLine(TIME_LABEL, _EventTraceFrame.rawtimes[index], 1, .82, 0, 1, 1, 1) end if _EventTraceFrame.slowesthandlers[index] then tooltip:AddDoubleLine(SLOWEST_LABEL, format("%s (%.2fms)", _EventTraceFrame.slowesthandlers[index], _EventTraceFrame.slowesthandlertimes[index]), 1, .82, 0, 1, 1, 1) end for k, v in ipairs(EventTraceFrame.args) do if v[index] then tooltip:AddDoubleLine(format(ARGUMENT_LABEL_FORMAT, k), EventTrace_FormatArgValue(v[index]), 1, .82, 0, 1, 1, 1) end end end tooltip:Show() end function EventTraceFrameEvent_OnEnter(self) if not EVENT_TRACE_SYSTEM_TIMES[EventTraceFrame.times[self.index]] then self.HideButton:Show() else self.HideButton:Hide() end if _EventTraceFrame.selectedEvent then return else EventTraceFrameEvent_DisplayTooltip(self) end end function EventTraceFrameEvent_OnLeave(self) if not self.HideButton == GetMouseFocus() then self.HideButton:Hide() end if not _EventTraceFrame.selectedEvent then EventTraceTooltip:Hide() end end function EventTraceFrameEvent_OnClick(self) if _EventTraceFrame.selectedEvent == self.index then _EventTraceFrame.selectedEvent = nil else _EventTraceFrame.selectedEvent = self.index end EventTraceFrame_Update() end function EventTraceFrameEventHideButton_OnClick(button) local eventName = button:GetParent().event:GetText() EventTraceFrame.ignoredEvents[eventName] = 1 EventTraceFrame.selectedEvent = nil for i = EventTraceFrame.lastIndex, 1, -1 do if EventTraceFrame.events[i] == eventName then EventTraceFrame_RemoveEvent(i) end end local lastWasElapsed = false for i = EventTraceFrame.lastIndex, 1, -1 do if EventTraceFrame.times[i] == "Elapsed" then if lastWasElapsed then EventTraceFrame.timeSinceLast[i] = EventTraceFrame.timeSinceLast[i] + EventTraceFrame.timeSinceLast[i+1] EventTraceFrame.framesSinceLast[i] = EventTraceFrame.framesSinceLast[i] + EventTraceFrame.framesSinceLast[i+1] EventTraceFrame.events[i] = string.format(string.format("%.3f sec", EventTraceFrame.timeSinceLast[i]) .. " - %d frame(s)", EventTraceFrame.framesSinceLast[i]) EventTraceFrame_RemoveEvent(i+1) end lastWasElapsed = true else lastWasElapsed = false end end EventTraceFrame_Update() end local ERROR_FORMAT = [[|cffffd200Message:|r|cffffffff %s|r |cffffd200Time:|r|cffffffff %s|r |cffffd200Count:|r|cffffffff %s|r |cffffd200Stack:|r|cffffffff %s|r]] local WARNING_AS_ERROR_FORMAT = [[|cffffd200Message:|r|cffffffff %s|r |cffffd200Time:|r|cffffffff %s|r |cffffd200Count:|r|cffffffff %s|r]] local WARNING_FORMAT = "Lua Warning:\n"..WARNING_AS_ERROR_FORMAT local INDEX_ORDER_FORMAT = "%d / %d" local _ScriptErrorsFrame function ScriptErrorsFrame_OnLoad(self) local frameName = self:GetName() self.title = _G[frameName.."Title"] self.indexLabel = _G[frameName.."IndexLabel"] self.reload = _G[frameName.."Reload"] self.previous = _G[frameName.."Previous"] self.next = _G[frameName.."Next"] self.close = _G[frameName.."CloseButton"] self.title:SetText(LUA_ERROR) self:RegisterForDrag("LeftButton") self.seen = {} self.order = {} self.count = {} self.messages = {} self.times = {} _ScriptErrorsFrame = self end function ScriptErrorsFrame_OnShow(self) ScriptErrorsFrame_Update() end function ScriptErrorsFrame_OnError(message, keepHidden) local stack = debugstack(DEBUGLOCALS_LEVEL) local messageStack = message..stack if _ScriptErrorsFrame then local index = _ScriptErrorsFrame.seen[messageStack] if index then _ScriptErrorsFrame.count[index] = _ScriptErrorsFrame.count[index] + 1 _ScriptErrorsFrame.messages[index] = message _ScriptErrorsFrame.times[index] = date() else tinsert(_ScriptErrorsFrame.order, stack) index = getn(_ScriptErrorsFrame.order) _ScriptErrorsFrame.count[index] = 1 _ScriptErrorsFrame.messages[index] = message _ScriptErrorsFrame.times[index] = date() _ScriptErrorsFrame.seen[messageStack] = index end if not _ScriptErrorsFrame:IsShown() and not keepHidden then _ScriptErrorsFrame.index = index _ScriptErrorsFrame:Show() else ScriptErrorsFrame_Update() end end end function ScriptErrorsFrame_Update() local editBox = ScriptErrorsFrameScrollFrameText local index = _ScriptErrorsFrame.index if not index or not _ScriptErrorsFrame.order[index] then index = getn(_ScriptErrorsFrame.order) _ScriptErrorsFrame.index = index end if index == 0 then editBox:SetText("") ScriptErrorsFrame_UpdateButtons() return end local text = string.format( ERROR_FORMAT, _ScriptErrorsFrame.messages[index], _ScriptErrorsFrame.times[index], _ScriptErrorsFrame.count[index], _ScriptErrorsFrame.order[index] ) local parent = editBox:GetParent() local prevText = editBox.text editBox.text = text if prevText ~= text then editBox:SetText(text) editBox:HighlightText(0) else ScriptErrorsFrameScrollFrame:UpdateScrollChildRect() end parent:SetVerticalScroll(0) ScriptErrorsFrame_UpdateButtons() end function ScriptErrorsFrame_UpdateButtons() local index = _ScriptErrorsFrame.index local numErrors = getn(_ScriptErrorsFrame.order) if index == 0 then _ScriptErrorsFrame.next:Disable() _ScriptErrorsFrame.previous:Disable() else if numErrors == 1 then _ScriptErrorsFrame.next:Disable() _ScriptErrorsFrame.previous:Disable() elseif index == 1 then _ScriptErrorsFrame.next:Enable() _ScriptErrorsFrame.previous:Disable() elseif index == numErrors then _ScriptErrorsFrame.next:Disable() _ScriptErrorsFrame.previous:Enable() else _ScriptErrorsFrame.next:Enable() _ScriptErrorsFrame.previous:Enable() end end _ScriptErrorsFrame.indexLabel:SetText(string.format(INDEX_ORDER_FORMAT, index, numErrors)) end function ScriptErrorsFrame_DeleteError(index) if _ScriptErrorsFrame.order[index] then _ScriptErrorsFrame.seen[_ScriptErrorsFrame.messages[index] .. _ScriptErrorsFrame.order[index]] = nil tremove(_ScriptErrorsFrame.order, index) tremove(_ScriptErrorsFrame.messages, index) tremove(_ScriptErrorsFrame.times, index) tremove(_ScriptErrorsFrame.count, index) end end function ScriptErrorsFrameButton_OnClick(self) local id = self:GetID() if id == 1 then _ScriptErrorsFrame.index = _ScriptErrorsFrame.index + 1 else _ScriptErrorsFrame.index = _ScriptErrorsFrame.index - 1 end ScriptErrorsFrame_Update() end function DebugTooltip_OnLoad(self) self:SetFrameLevel(self:GetFrameLevel() + 2) self:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b) self:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b) self.statusBar2 = getglobal(self:GetName().."StatusBar2") self.statusBar2Text = getglobal(self:GetName().."StatusBar2Text") end function FrameStackTooltip_OnDisplaySizeChanged(self) local height = GetScreenHeight() if height > 768 then self:SetScale(768 / height) else self:SetScale(1) end end function FrameStackTooltip_OnLoad(self) DebugTooltip_OnLoad(self) FrameStackTooltip_OnDisplaySizeChanged(self) self:RegisterEvent("DISPLAY_SIZE_CHANGED") end function FrameStackTooltip_OnEvent(self, event, ...) if event == "DISPLAY_SIZE_CHANGED" then FrameStackTooltip_OnDisplaySizeChanged(self) end end function FrameStackTooltip_Toggle(showHidden) local tooltip = _G["FrameStackTooltip"] if tooltip:IsVisible() then tooltip:Hide() FrameStackHighlight:Hide() else tooltip:SetOwner(UIParent, "ANCHOR_NONE") tooltip:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", -CONTAINER_OFFSET_X - 13, CONTAINER_OFFSET_Y) tooltip.default = 1 tooltip.showHidden = showHidden UpdateFrameStack(tooltip, showHidden) end end FRAMESTACK_UPDATE_TIME = .1 local _timeSinceLast = 0 function FrameStackTooltip_OnUpdate(self, elapsed) _timeSinceLast = _timeSinceLast - elapsed if _timeSinceLast <= 0 then _timeSinceLast = FRAMESTACK_UPDATE_TIME local highlightFrame = UpdateFrameStack(self, self.showHidden) FrameStackHighlight:ClearAllPoints() if highlightFrame then FrameStackHighlight:SetPoint("BOTTOMLEFT", highlightFrame) FrameStackHighlight:SetPoint("TOPRIGHT", highlightFrame) FrameStackHighlight:Show() else FrameStackHighlight:Hide() end end end function FrameStackTooltip_OnShow(self) local parent = self:GetParent() or UIParent local ps = parent:GetEffectiveScale() local px, py = parent:GetCenter() px, py = px * ps, py * ps local x, y = GetCursorPosition() self:ClearAllPoints() if x > px then if y > py then self:SetPoint("BOTTOMLEFT", parent, "BOTTOMLEFT", 20, 20) else self:SetPoint("TOPLEFT", parent, "TOPLEFT", 20, -20) end else if y > py then self:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", -20, 20) else self:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -20, -20) end end end FrameStackTooltip_OnEnter = FrameStackTooltip_OnShow