Last active
October 14, 2020 17:08
-
-
Save rherzfeld/e26c093ba2574f9057a76e3f73a90efd to your computer and use it in GitHub Desktop.
LibQTip-1.0
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local MAJOR = "LibQTip-1.0" | |
local MINOR = 46 -- Should be manually increased | |
local LibStub = _G.LibStub | |
assert(LibStub, MAJOR .. " requires LibStub") | |
local lib, oldMinor = LibStub:NewLibrary(MAJOR, MINOR) | |
if not lib then | |
return | |
end -- No upgrade needed | |
------------------------------------------------------------------------------ | |
-- Upvalued globals | |
------------------------------------------------------------------------------ | |
local table = _G.table | |
local tinsert = table.insert | |
local tremove = table.remove | |
local wipe = table.wipe | |
local error = error | |
local math = math | |
local min, max = math.min, math.max | |
local next = next | |
local pairs, ipairs = pairs, ipairs | |
local select = select | |
local setmetatable = setmetatable | |
local tonumber, tostring = tonumber, tostring | |
local type = type | |
local CreateFrame = _G.CreateFrame | |
local GameTooltip = _G.GameTooltip | |
local UIParent = _G.UIParent | |
local geterrorhandler = _G.geterrorhandler | |
------------------------------------------------------------------------------ | |
-- Tables and locals | |
------------------------------------------------------------------------------ | |
lib.frameMetatable = lib.frameMetatable or {__index = CreateFrame("Frame")} | |
lib.tipPrototype = lib.tipPrototype or setmetatable({}, lib.frameMetatable) | |
lib.tipMetatable = lib.tipMetatable or {__index = lib.tipPrototype} | |
lib.providerPrototype = lib.providerPrototype or {} | |
lib.providerMetatable = lib.providerMetatable or {__index = lib.providerPrototype} | |
lib.cellPrototype = lib.cellPrototype or setmetatable({}, lib.frameMetatable) | |
lib.cellMetatable = lib.cellMetatable or {__index = lib.cellPrototype} | |
lib.activeTooltips = lib.activeTooltips or {} | |
lib.tooltipHeap = lib.tooltipHeap or {} | |
lib.frameHeap = lib.frameHeap or {} | |
lib.tableHeap = lib.tableHeap or {} | |
lib.onReleaseHandlers = lib.onReleaseHandlers or {} | |
local tipPrototype = lib.tipPrototype | |
local tipMetatable = lib.tipMetatable | |
local providerPrototype = lib.providerPrototype | |
local providerMetatable = lib.providerMetatable | |
local cellPrototype = lib.cellPrototype | |
local cellMetatable = lib.cellMetatable | |
local activeTooltips = lib.activeTooltips | |
local highlightFrame = CreateFrame("Frame", nil, UIParent) | |
highlightFrame:SetFrameStrata("TOOLTIP") | |
highlightFrame:Hide() | |
local DEFAULT_HIGHLIGHT_TEXTURE_PATH = [[Interface\QuestFrame\UI-QuestTitleHighlight]] | |
local highlightTexture = highlightFrame:CreateTexture(nil, "OVERLAY") | |
highlightTexture:SetTexture(DEFAULT_HIGHLIGHT_TEXTURE_PATH) | |
highlightTexture:SetBlendMode("ADD") | |
highlightTexture:SetAllPoints(highlightFrame) | |
------------------------------------------------------------------------------ | |
-- Private methods for Caches and Tooltip | |
------------------------------------------------------------------------------ | |
local AcquireTooltip, ReleaseTooltip | |
local AcquireCell, ReleaseCell | |
local AcquireTable, ReleaseTable | |
local InitializeTooltip, SetTooltipSize, ResetTooltipSize, FixCellSizes | |
local ClearTooltipScripts | |
local SetFrameScript, ClearFrameScripts | |
------------------------------------------------------------------------------ | |
-- Cache debugging. | |
------------------------------------------------------------------------------ | |
-- @debug @ | |
local usedTables, usedFrames, usedTooltips = 0, 0, 0 | |
--@end-debug@]===] | |
------------------------------------------------------------------------------ | |
-- Internal constants to tweak the layout | |
------------------------------------------------------------------------------ | |
local TOOLTIP_PADDING = 10 | |
local CELL_MARGIN_H = 6 | |
local CELL_MARGIN_V = 3 | |
------------------------------------------------------------------------------ | |
-- Public library API | |
------------------------------------------------------------------------------ | |
--- Create or retrieve the tooltip with the given key. | |
-- If additional arguments are passed, they are passed to :SetColumnLayout for the acquired tooltip. | |
-- @name LibQTip:Acquire(key[, numColumns, column1Justification, column2justification, ...]) | |
-- @param key string or table - the tooltip key. Any value that can be used as a table key is accepted though you should try to provide unique keys to avoid conflicts. | |
-- Numbers and booleans should be avoided and strings should be carefully chosen to avoid namespace clashes - no "MyTooltip" - you have been warned! | |
-- @return tooltip Frame object - the acquired tooltip. | |
-- @usage Acquire a tooltip with at least 5 columns, justification : left, center, left, left, left | |
-- <pre>local tip = LibStub('LibQTip-1.0'):Acquire('MyFooBarTooltip', 5, "LEFT", "CENTER")</pre> | |
function lib:Acquire(key, ...) | |
if key == nil then | |
error("attempt to use a nil key", 2) | |
end | |
local tooltip = activeTooltips[key] | |
if not tooltip then | |
tooltip = AcquireTooltip() | |
InitializeTooltip(tooltip, key) | |
activeTooltips[key] = tooltip | |
end | |
if select("#", ...) > 0 then | |
-- Here we catch any error to properly report it for the calling code | |
local ok, msg = pcall(tooltip.SetColumnLayout, tooltip, ...) | |
if not ok then | |
error(msg, 2) | |
end | |
end | |
return tooltip | |
end | |
function lib:Release(tooltip) | |
local key = tooltip and tooltip.key | |
if not key or activeTooltips[key] ~= tooltip then | |
return | |
end | |
ReleaseTooltip(tooltip) | |
activeTooltips[key] = nil | |
end | |
function lib:IsAcquired(key) | |
if key == nil then | |
error("attempt to use a nil key", 2) | |
end | |
return not (not activeTooltips[key]) | |
end | |
function lib:IterateTooltips() | |
return pairs(activeTooltips) | |
end | |
------------------------------------------------------------------------------ | |
-- Frame cache | |
------------------------------------------------------------------------------ | |
local frameHeap = lib.frameHeap | |
local function AcquireFrame(parent) | |
local frame = tremove(frameHeap) or CreateFrame("Frame", nil, nil, "BackdropTemplate") | |
frame:SetParent(parent) | |
--[===[@debug@ | |
usedFrames = usedFrames + 1 | |
--@end-debug@]===] | |
return frame | |
end | |
local function ReleaseFrame(frame) | |
frame:Hide() | |
frame:SetParent(nil) | |
frame:ClearAllPoints() | |
if frame.ApplyBackdrop then | |
frame.backdropInfo = {} | |
frame:ApplyBackdrop() | |
end | |
ClearFrameScripts(frame) | |
tinsert(frameHeap, frame) | |
--[===[@debug@ | |
usedFrames = usedFrames - 1 | |
--@end-debug@]===] | |
end | |
------------------------------------------------------------------------------ | |
-- Dirty layout handler | |
------------------------------------------------------------------------------ | |
lib.layoutCleaner = lib.layoutCleaner or CreateFrame("Frame") | |
local layoutCleaner = lib.layoutCleaner | |
layoutCleaner.registry = layoutCleaner.registry or {} | |
function layoutCleaner:RegisterForCleanup(tooltip) | |
self.registry[tooltip] = true | |
self:Show() | |
end | |
function layoutCleaner:CleanupLayouts() | |
self:Hide() | |
for tooltip in pairs(self.registry) do | |
FixCellSizes(tooltip) | |
end | |
wipe(self.registry) | |
end | |
layoutCleaner:SetScript("OnUpdate", layoutCleaner.CleanupLayouts) | |
------------------------------------------------------------------------------ | |
-- CellProvider and Cell | |
------------------------------------------------------------------------------ | |
function providerPrototype:AcquireCell() | |
local cell = tremove(self.heap) | |
if not cell then | |
cell = setmetatable(CreateFrame("Frame", nil, UIParent), self.cellMetatable) | |
if type(cell.InitializeCell) == "function" then | |
cell:InitializeCell() | |
end | |
end | |
self.cells[cell] = true | |
return cell | |
end | |
function providerPrototype:ReleaseCell(cell) | |
if not self.cells[cell] then | |
return | |
end | |
if type(cell.ReleaseCell) == "function" then | |
cell:ReleaseCell() | |
end | |
self.cells[cell] = nil | |
tinsert(self.heap, cell) | |
end | |
function providerPrototype:GetCellPrototype() | |
return self.cellPrototype, self.cellMetatable | |
end | |
function providerPrototype:IterateCells() | |
return pairs(self.cells) | |
end | |
function lib:CreateCellProvider(baseProvider) | |
local cellBaseMetatable, cellBasePrototype | |
if baseProvider and baseProvider.GetCellPrototype then | |
cellBasePrototype, cellBaseMetatable = baseProvider:GetCellPrototype() | |
else | |
cellBaseMetatable = cellMetatable | |
end | |
local newCellPrototype = setmetatable({}, cellBaseMetatable) | |
local newCellProvider = setmetatable({}, providerMetatable) | |
newCellProvider.heap = {} | |
newCellProvider.cells = {} | |
newCellProvider.cellPrototype = newCellPrototype | |
newCellProvider.cellMetatable = {__index = newCellPrototype} | |
return newCellProvider, newCellPrototype, cellBasePrototype | |
end | |
------------------------------------------------------------------------------ | |
-- Basic label provider | |
------------------------------------------------------------------------------ | |
if not lib.LabelProvider then | |
lib.LabelProvider, lib.LabelPrototype = lib:CreateCellProvider() | |
end | |
local labelProvider = lib.LabelProvider | |
local labelPrototype = lib.LabelPrototype | |
function labelPrototype:InitializeCell() | |
self.fontString = self:CreateFontString() | |
self.fontString:SetFontObject(_G.GameTooltipText) | |
end | |
function labelPrototype:SetupCell(tooltip, value, justification, font, leftPadding, rightPadding, maxWidth, minWidth, ...) | |
local fontString = self.fontString | |
local line = tooltip.lines[self._line] | |
-- detatch fs from cell for size calculations | |
fontString:ClearAllPoints() | |
fontString:SetFontObject(font or (line.is_header and tooltip:GetHeaderFont() or tooltip:GetFont())) | |
fontString:SetJustifyH(justification) | |
fontString:SetText(tostring(value)) | |
leftPadding = leftPadding or 0 | |
rightPadding = rightPadding or 0 | |
local width = fontString:GetStringWidth() + leftPadding + rightPadding | |
if maxWidth and minWidth and (maxWidth < minWidth) then | |
error("maximum width cannot be lower than minimum width: " .. tostring(maxWidth) .. " < " .. tostring(minWidth), 2) | |
end | |
if maxWidth and (maxWidth < (leftPadding + rightPadding)) then | |
error("maximum width cannot be lower than the sum of paddings: " .. tostring(maxWidth) .. " < " .. tostring(leftPadding) .. " + " .. tostring(rightPadding), 2) | |
end | |
if minWidth and width < minWidth then | |
width = minWidth | |
end | |
if maxWidth and maxWidth < width then | |
width = maxWidth | |
end | |
fontString:SetWidth(width - (leftPadding + rightPadding)) | |
-- Use GetHeight() instead of GetStringHeight() so lines which are longer than width will wrap. | |
local height = fontString:GetHeight() | |
-- reanchor fs to cell | |
fontString:SetWidth(0) | |
fontString:SetPoint("TOPLEFT", self, "TOPLEFT", leftPadding, 0) | |
fontString:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", -rightPadding, 0) | |
--~ fs:SetPoint("TOPRIGHT", self, "TOPRIGHT", -r_pad, 0) | |
self._paddingL = leftPadding | |
self._paddingR = rightPadding | |
return width, height | |
end | |
function labelPrototype:getContentHeight() | |
local fontString = self.fontString | |
fontString:SetWidth(self:GetWidth() - (self._paddingL + self._paddingR)) | |
local height = self.fontString:GetHeight() | |
fontString:SetWidth(0) | |
return height | |
end | |
function labelPrototype:GetPosition() | |
return self._line, self._column | |
end | |
------------------------------------------------------------------------------ | |
-- Tooltip cache | |
------------------------------------------------------------------------------ | |
local tooltipHeap = lib.tooltipHeap | |
-- Returns a tooltip | |
function AcquireTooltip() | |
local tooltip = tremove(tooltipHeap) | |
if not tooltip then | |
tooltip = CreateFrame("Frame", nil, UIParent, "BackdropTemplate") | |
local scrollFrame = CreateFrame("ScrollFrame", nil, tooltip) | |
scrollFrame:SetPoint("TOP", tooltip, "TOP", 0, -TOOLTIP_PADDING) | |
scrollFrame:SetPoint("BOTTOM", tooltip, "BOTTOM", 0, TOOLTIP_PADDING) | |
scrollFrame:SetPoint("LEFT", tooltip, "LEFT", TOOLTIP_PADDING, 0) | |
scrollFrame:SetPoint("RIGHT", tooltip, "RIGHT", -TOOLTIP_PADDING, 0) | |
tooltip.scrollFrame = scrollFrame | |
local scrollChild = CreateFrame("Frame", nil, tooltip.scrollFrame, "BackdropTemplate") | |
scrollFrame:SetScrollChild(scrollChild) | |
tooltip.scrollChild = scrollChild | |
setmetatable(tooltip, tipMetatable) | |
end | |
--[===[@debug@ | |
usedTooltips = usedTooltips + 1 | |
--@end-debug@]===] | |
return tooltip | |
end | |
-- Cleans the tooltip and stores it in the cache | |
function ReleaseTooltip(tooltip) | |
if tooltip.releasing then | |
return | |
end | |
tooltip.releasing = true | |
tooltip:Hide() | |
local releaseHandler = lib.onReleaseHandlers[tooltip] | |
if releaseHandler then | |
lib.onReleaseHandlers[tooltip] = nil | |
local success, errorMessage = pcall(releaseHandler, tooltip) | |
if not success then | |
geterrorhandler()(errorMessage) | |
end | |
elseif tooltip.OnRelease then | |
local success, errorMessage = pcall(tooltip.OnRelease, tooltip) | |
if not success then | |
geterrorhandler()(errorMessage) | |
end | |
tooltip.OnRelease = nil | |
end | |
tooltip.releasing = nil | |
tooltip.key = nil | |
tooltip.step = nil | |
ClearTooltipScripts(tooltip) | |
tooltip:SetAutoHideDelay(nil) | |
tooltip:ClearAllPoints() | |
tooltip:Clear() | |
if tooltip.slider then | |
tooltip.slider:SetValue(0) | |
tooltip.slider:Hide() | |
tooltip.scrollFrame:SetPoint("RIGHT", tooltip, "RIGHT", -TOOLTIP_PADDING, 0) | |
tooltip:EnableMouseWheel(false) | |
end | |
for i, column in ipairs(tooltip.columns) do | |
tooltip.columns[i] = ReleaseFrame(column) | |
end | |
tooltip.columns = ReleaseTable(tooltip.columns) | |
tooltip.lines = ReleaseTable(tooltip.lines) | |
tooltip.colspans = ReleaseTable(tooltip.colspans) | |
layoutCleaner.registry[tooltip] = nil | |
tinsert(tooltipHeap, tooltip) | |
highlightTexture:SetTexture(DEFAULT_HIGHLIGHT_TEXTURE_PATH) | |
highlightTexture:SetTexCoord(0, 1, 0, 1) | |
--[===[@debug@ | |
usedTooltips = usedTooltips - 1 | |
--@end-debug@]===] | |
end | |
------------------------------------------------------------------------------ | |
-- Cell 'cache' (just a wrapper to the provider's cache) | |
------------------------------------------------------------------------------ | |
-- Returns a cell for the given tooltip from the given provider | |
function AcquireCell(tooltip, provider) | |
local cell = provider:AcquireCell(tooltip) | |
cell:SetParent(tooltip.scrollChild) | |
cell:SetFrameLevel(tooltip.scrollChild:GetFrameLevel() + 3) | |
cell._provider = provider | |
return cell | |
end | |
-- Cleans the cell hands it to its provider for storing | |
function ReleaseCell(cell) | |
if cell.fontString and cell.r then | |
cell.fontString:SetTextColor(cell.r, cell.g, cell.b, cell.a) | |
end | |
cell._font = nil | |
cell._justification = nil | |
cell._colSpan = nil | |
cell._line = nil | |
cell._column = nil | |
cell:Hide() | |
cell:ClearAllPoints() | |
cell:SetParent(nil) | |
if cell.ApplyBackdrop then | |
cell.backdropInfo = {} | |
cell:ApplyBackdrop() | |
end | |
ClearFrameScripts(cell) | |
cell._provider:ReleaseCell(cell) | |
cell._provider = nil | |
end | |
------------------------------------------------------------------------------ | |
-- Table cache | |
------------------------------------------------------------------------------ | |
local tableHeap = lib.tableHeap | |
-- Returns a table | |
function AcquireTable() | |
local tbl = tremove(tableHeap) or {} | |
--[===[@debug@ | |
usedTables = usedTables + 1 | |
--@end-debug@]===] | |
return tbl | |
end | |
-- Cleans the table and stores it in the cache | |
function ReleaseTable(tableInstance) | |
wipe(tableInstance) | |
tinsert(tableHeap, tableInstance) | |
--[===[@debug@ | |
usedTables = usedTables - 1 | |
--@end-debug@]===] | |
end | |
------------------------------------------------------------------------------ | |
-- Tooltip prototype | |
------------------------------------------------------------------------------ | |
function InitializeTooltip(tooltip, key) | |
---------------------------------------------------------------------- | |
-- (Re)set frame settings | |
---------------------------------------------------------------------- | |
local backdrop = GameTooltip:GetBackdrop() | |
tooltip.backdropInfo = backdrop | |
tooltip:ApplyBackdrop() | |
if backdrop then | |
tooltip:SetBackdropColor(GameTooltip:GetBackdropColor()) | |
tooltip:SetBackdropBorderColor(GameTooltip:GetBackdropBorderColor()) | |
end | |
tooltip:SetScale(GameTooltip:GetScale()) | |
tooltip:SetAlpha(1) | |
tooltip:SetFrameStrata("TOOLTIP") | |
tooltip:SetClampedToScreen(false) | |
---------------------------------------------------------------------- | |
-- Internal data. Since it's possible to Acquire twice without calling | |
-- release, check for pre-existence. | |
---------------------------------------------------------------------- | |
tooltip.key = key | |
tooltip.columns = tooltip.columns or AcquireTable() | |
tooltip.lines = tooltip.lines or AcquireTable() | |
tooltip.colspans = tooltip.colspans or AcquireTable() | |
tooltip.regularFont = _G.GameTooltipText | |
tooltip.headerFont = _G.GameTooltipHeaderText | |
tooltip.labelProvider = labelProvider | |
tooltip.cell_margin_h = tooltip.cell_margin_h or CELL_MARGIN_H | |
tooltip.cell_margin_v = tooltip.cell_margin_v or CELL_MARGIN_V | |
---------------------------------------------------------------------- | |
-- Finishing procedures | |
---------------------------------------------------------------------- | |
tooltip:SetAutoHideDelay(nil) | |
tooltip:Hide() | |
ResetTooltipSize(tooltip) | |
end | |
function tipPrototype:SetDefaultProvider(myProvider) | |
if not myProvider then | |
return | |
end | |
self.labelProvider = myProvider | |
end | |
function tipPrototype:GetDefaultProvider() | |
return self.labelProvider | |
end | |
local function checkJustification(justification, level, silent) | |
if justification ~= "LEFT" and justification ~= "CENTER" and justification ~= "RIGHT" then | |
if silent then | |
return false | |
end | |
error("invalid justification, must one of LEFT, CENTER or RIGHT, not: " .. tostring(justification), level + 1) | |
end | |
return true | |
end | |
function tipPrototype:SetColumnLayout(numColumns, ...) | |
if type(numColumns) ~= "number" or numColumns < 1 then | |
error("number of columns must be a positive number, not: " .. tostring(numColumns), 2) | |
end | |
for i = 1, numColumns do | |
local justification = select(i, ...) or "LEFT" | |
checkJustification(justification, 2) | |
if self.columns[i] then | |
self.columns[i].justification = justification | |
else | |
self:AddColumn(justification) | |
end | |
end | |
end | |
function tipPrototype:AddColumn(justification) | |
justification = justification or "LEFT" | |
checkJustification(justification, 2) | |
local colNum = #self.columns + 1 | |
local column = self.columns[colNum] or AcquireFrame(self.scrollChild) | |
column:SetFrameLevel(self.scrollChild:GetFrameLevel() + 1) | |
column.justification = justification | |
column.width = 0 | |
column:SetWidth(1) | |
column:SetPoint("TOP", self.scrollChild) | |
column:SetPoint("BOTTOM", self.scrollChild) | |
if colNum > 1 then | |
local h_margin = self.cell_margin_h or CELL_MARGIN_H | |
column:SetPoint("LEFT", self.columns[colNum - 1], "RIGHT", h_margin, 0) | |
SetTooltipSize(self, self.width + h_margin, self.height) | |
else | |
column:SetPoint("LEFT", self.scrollChild) | |
end | |
column:Show() | |
self.columns[colNum] = column | |
return colNum | |
end | |
------------------------------------------------------------------------------ | |
-- Convenient methods | |
------------------------------------------------------------------------------ | |
function tipPrototype:Release() | |
lib:Release(self) | |
end | |
function tipPrototype:IsAcquiredBy(key) | |
return key ~= nil and self.key == key | |
end | |
------------------------------------------------------------------------------ | |
-- Script hooks | |
------------------------------------------------------------------------------ | |
local RawSetScript = lib.frameMetatable.__index.SetScript | |
function ClearTooltipScripts(tooltip) | |
if tooltip.scripts then | |
for scriptType in pairs(tooltip.scripts) do | |
RawSetScript(tooltip, scriptType, nil) | |
end | |
tooltip.scripts = ReleaseTable(tooltip.scripts) | |
end | |
end | |
function tipPrototype:SetScript(scriptType, handler) | |
RawSetScript(self, scriptType, handler) | |
if handler then | |
if not self.scripts then | |
self.scripts = AcquireTable() | |
end | |
self.scripts[scriptType] = true | |
elseif self.scripts then | |
self.scripts[scriptType] = nil | |
end | |
end | |
-- That might break some addons ; those addons were breaking other | |
-- addons' tooltip though. | |
function tipPrototype:HookScript() | |
geterrorhandler()(":HookScript is not allowed on LibQTip tooltips") | |
end | |
------------------------------------------------------------------------------ | |
-- Scrollbar data and functions | |
------------------------------------------------------------------------------ | |
local sliderBackdrop = { | |
bgFile = [[Interface\Buttons\UI-SliderBar-Background]], | |
edgeFile = [[Interface\Buttons\UI-SliderBar-Border]], | |
tile = true, | |
edgeSize = 8, | |
tileSize = 8, | |
insets = { | |
left = 3, | |
right = 3, | |
top = 3, | |
bottom = 3 | |
} | |
} | |
local function slider_OnValueChanged(self) | |
self.scrollFrame:SetVerticalScroll(self:GetValue()) | |
end | |
local function tooltip_OnMouseWheel(self, delta) | |
local slider = self.slider | |
local currentValue = slider:GetValue() | |
local minValue, maxValue = slider:GetMinMaxValues() | |
local stepValue = self.step or 10 | |
if delta < 0 and currentValue < maxValue then | |
slider:SetValue(min(maxValue, currentValue + stepValue)) | |
elseif delta > 0 and currentValue > minValue then | |
slider:SetValue(max(minValue, currentValue - stepValue)) | |
end | |
end | |
-- Set the step size for the scroll bar | |
function tipPrototype:SetScrollStep(step) | |
self.step = step | |
end | |
-- will resize the tooltip to fit the screen and show a scrollbar if needed | |
function tipPrototype:UpdateScrolling(maxheight) | |
self:SetClampedToScreen(false) | |
-- all data is in the tooltip; fix colspan width and prevent the layout cleaner from messing up the tooltip later | |
FixCellSizes(self) | |
layoutCleaner.registry[self] = nil | |
local scale = self:GetScale() | |
local topside = self:GetTop() | |
local bottomside = self:GetBottom() | |
local screensize = UIParent:GetHeight() / scale | |
local tipsize = (topside - bottomside) | |
-- if the tooltip would be too high, limit its height and show the slider | |
if bottomside < 0 or topside > screensize or (maxheight and tipsize > maxheight) then | |
local shrink = (bottomside < 0 and (5 - bottomside) or 0) + (topside > screensize and (topside - screensize + 5) or 0) | |
if maxheight and tipsize - shrink > maxheight then | |
shrink = tipsize - maxheight | |
end | |
self:SetHeight(2 * TOOLTIP_PADDING + self.height - shrink) | |
self:SetWidth(2 * TOOLTIP_PADDING + self.width + 20) | |
self.scrollFrame:SetPoint("RIGHT", self, "RIGHT", -(TOOLTIP_PADDING + 20), 0) | |
if not self.slider then | |
local slider = CreateFrame("Slider", nil, self) | |
slider.scrollFrame = self.scrollFrame | |
slider:SetOrientation("VERTICAL") | |
slider:SetPoint("TOPRIGHT", self, "TOPRIGHT", -TOOLTIP_PADDING, -TOOLTIP_PADDING) | |
slider:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", -TOOLTIP_PADDING, TOOLTIP_PADDING) | |
slider.backdropInfo = sliderBackdrop | |
slider:ApplyBackdrop() | |
slider:SetThumbTexture([[Interface\Buttons\UI-SliderBar-Button-Vertical]]) | |
slider:SetMinMaxValues(0, 1) | |
slider:SetValueStep(1) | |
slider:SetWidth(12) | |
slider:SetScript("OnValueChanged", slider_OnValueChanged) | |
slider:SetValue(0) | |
self.slider = slider | |
end | |
self.slider:SetMinMaxValues(0, shrink) | |
self.slider:Show() | |
self:EnableMouseWheel(true) | |
self:SetScript("OnMouseWheel", tooltip_OnMouseWheel) | |
else | |
self:SetHeight(2 * TOOLTIP_PADDING + self.height) | |
self:SetWidth(2 * TOOLTIP_PADDING + self.width) | |
self.scrollFrame:SetPoint("RIGHT", self, "RIGHT", -TOOLTIP_PADDING, 0) | |
if self.slider then | |
self.slider:SetValue(0) | |
self.slider:Hide() | |
self:EnableMouseWheel(false) | |
self:SetScript("OnMouseWheel", nil) | |
end | |
end | |
end | |
------------------------------------------------------------------------------ | |
-- Tooltip methods for changing its contents. | |
------------------------------------------------------------------------------ | |
function tipPrototype:Clear() | |
for i, line in ipairs(self.lines) do | |
for _, cell in pairs(line.cells) do | |
if cell then | |
ReleaseCell(cell) | |
end | |
end | |
ReleaseTable(line.cells) | |
line.cells = nil | |
line.is_header = nil | |
ReleaseFrame(line) | |
self.lines[i] = nil | |
end | |
for _, column in ipairs(self.columns) do | |
column.width = 0 | |
column:SetWidth(1) | |
end | |
wipe(self.colspans) | |
self.cell_margin_h = nil | |
self.cell_margin_v = nil | |
ResetTooltipSize(self) | |
end | |
function tipPrototype:SetCellMarginH(size) | |
if #self.lines > 0 then | |
error("Unable to set horizontal margin while the tooltip has lines.", 2) | |
end | |
if not size or type(size) ~= "number" or size < 0 then | |
error("Margin size must be a positive number or zero.", 2) | |
end | |
self.cell_margin_h = size | |
end | |
function tipPrototype:SetCellMarginV(size) | |
if #self.lines > 0 then | |
error("Unable to set vertical margin while the tooltip has lines.", 2) | |
end | |
if not size or type(size) ~= "number" or size < 0 then | |
error("Margin size must be a positive number or zero.", 2) | |
end | |
self.cell_margin_v = size | |
end | |
function SetTooltipSize(tooltip, width, height) | |
tooltip.height = height | |
tooltip.width = width | |
tooltip:SetHeight(2 * TOOLTIP_PADDING + height) | |
tooltip:SetWidth(2 * TOOLTIP_PADDING + width) | |
tooltip.scrollChild:SetHeight(height) | |
tooltip.scrollChild:SetWidth(width) | |
end | |
-- Add 2 pixels to height so dangling letters (g, y, p, j, etc) are not clipped. | |
function ResetTooltipSize(tooltip) | |
local h_margin = tooltip.cell_margin_h or CELL_MARGIN_H | |
SetTooltipSize(tooltip, max(0, (h_margin * (#tooltip.columns - 1)) + (h_margin / 2)), 2) | |
end | |
local function EnlargeColumn(tooltip, column, width) | |
if width > column.width then | |
SetTooltipSize(tooltip, tooltip.width + width - column.width, tooltip.height) | |
column.width = width | |
column:SetWidth(width) | |
end | |
end | |
local function ResizeLine(tooltip, line, height) | |
SetTooltipSize(tooltip, tooltip.width, tooltip.height + height - line.height) | |
line.height = height | |
line:SetHeight(height) | |
end | |
function FixCellSizes(tooltip) | |
local columns = tooltip.columns | |
local colspans = tooltip.colspans | |
local lines = tooltip.lines | |
local h_margin = tooltip.cell_margin_h or CELL_MARGIN_H | |
-- resize columns to make room for the colspans | |
while next(colspans) do | |
local maxNeedCols | |
local maxNeedWidthPerCol = 0 | |
-- calculate the colspan with the highest additional width need per column | |
for colRange, width in pairs(colspans) do | |
local left, right = colRange:match("^(%d+)%-(%d+)$") | |
left, right = tonumber(left), tonumber(right) | |
for col = left, right - 1 do | |
width = width - columns[col].width - h_margin | |
end | |
width = width - columns[right].width | |
if width <= 0 then | |
colspans[colRange] = nil | |
else | |
width = width / (right - left + 1) | |
if width > maxNeedWidthPerCol then | |
maxNeedCols = colRange | |
maxNeedWidthPerCol = width | |
end | |
end | |
end | |
-- resize all columns for that colspan | |
if maxNeedCols then | |
local left, right = maxNeedCols:match("^(%d+)%-(%d+)$") | |
for col = left, right do | |
EnlargeColumn(tooltip, columns[col], columns[col].width + maxNeedWidthPerCol) | |
end | |
colspans[maxNeedCols] = nil | |
end | |
end | |
--now that the cell width is set, recalculate the rows' height | |
for _, line in ipairs(lines) do | |
if #(line.cells) > 0 then | |
local lineheight = 0 | |
for _, cell in pairs(line.cells) do | |
if cell then | |
lineheight = max(lineheight, cell:getContentHeight()) | |
end | |
end | |
if lineheight > 0 then | |
ResizeLine(tooltip, line, lineheight) | |
end | |
end | |
end | |
end | |
local function _SetCell(tooltip, lineNum, colNum, value, font, justification, colSpan, provider, ...) | |
local line = tooltip.lines[lineNum] | |
local cells = line.cells | |
-- Unset: be quick | |
if value == nil then | |
local cell = cells[colNum] | |
if cell then | |
for i = colNum, colNum + cell._colSpan - 1 do | |
cells[i] = nil | |
end | |
ReleaseCell(cell) | |
end | |
return lineNum, colNum | |
end | |
font = font or (line.is_header and tooltip.headerFont or tooltip.regularFont) | |
-- Check previous cell | |
local cell | |
local prevCell = cells[colNum] | |
if prevCell then | |
-- There is a cell here | |
justification = justification or prevCell._justification | |
colSpan = colSpan or prevCell._colSpan | |
-- Clear the currently marked colspan | |
for i = colNum + 1, colNum + prevCell._colSpan - 1 do | |
cells[i] = nil | |
end | |
if provider == nil or prevCell._provider == provider then | |
-- Reuse existing cell | |
cell = prevCell | |
provider = cell._provider | |
else | |
-- A new cell is required | |
cells[colNum] = ReleaseCell(prevCell) | |
end | |
elseif prevCell == nil then | |
-- Creating a new cell, using meaningful defaults. | |
provider = provider or tooltip.labelProvider | |
justification = justification or tooltip.columns[colNum].justification or "LEFT" | |
colSpan = colSpan or 1 | |
else | |
error("overlapping cells at column " .. colNum, 3) | |
end | |
local tooltipWidth = #tooltip.columns | |
local rightColNum | |
if colSpan > 0 then | |
rightColNum = colNum + colSpan - 1 | |
if rightColNum > tooltipWidth then | |
error("ColSpan too big, cell extends beyond right-most column", 3) | |
end | |
else | |
-- Zero or negative: count back from right-most columns | |
rightColNum = max(colNum, tooltipWidth + colSpan) | |
-- Update colspan to its effective value | |
colSpan = 1 + rightColNum - colNum | |
end | |
-- Cleanup colspans | |
for i = colNum + 1, rightColNum do | |
local columnCell = cells[i] | |
if columnCell then | |
ReleaseCell(columnCell) | |
elseif columnCell == false then | |
error("overlapping cells at column " .. i, 3) | |
end | |
cells[i] = false | |
end | |
-- Create the cell | |
if not cell then | |
cell = AcquireCell(tooltip, provider) | |
cells[colNum] = cell | |
end | |
-- Anchor the cell | |
cell:SetPoint("LEFT", tooltip.columns[colNum]) | |
cell:SetPoint("RIGHT", tooltip.columns[rightColNum]) | |
cell:SetPoint("TOP", line) | |
cell:SetPoint("BOTTOM", line) | |
-- Store the cell settings directly into the cell | |
-- That's a bit risky but is really cheap compared to other ways to do it | |
cell._font, cell._justification, cell._colSpan, cell._line, cell._column = font, justification, colSpan, lineNum, colNum | |
-- Setup the cell content | |
local width, height = cell:SetupCell(tooltip, value, justification, font, ...) | |
cell:Show() | |
if colSpan > 1 then | |
-- Postpone width changes until the tooltip is shown | |
local colRange = colNum .. "-" .. rightColNum | |
tooltip.colspans[colRange] = max(tooltip.colspans[colRange] or 0, width) | |
layoutCleaner:RegisterForCleanup(tooltip) | |
else | |
-- Enlarge the column and tooltip if need be | |
EnlargeColumn(tooltip, tooltip.columns[colNum], width) | |
end | |
-- Enlarge the line and tooltip if need be | |
if height > line.height then | |
SetTooltipSize(tooltip, tooltip.width, tooltip.height + height - line.height) | |
line.height = height | |
line:SetHeight(height) | |
end | |
if rightColNum < tooltipWidth then | |
return lineNum, rightColNum + 1 | |
else | |
return lineNum, nil | |
end | |
end | |
do | |
local function CreateLine(tooltip, font, ...) | |
if #tooltip.columns == 0 then | |
error("column layout should be defined before adding line", 3) | |
end | |
local lineNum = #tooltip.lines + 1 | |
local line = tooltip.lines[lineNum] or AcquireFrame(tooltip.scrollChild) | |
line:SetFrameLevel(tooltip.scrollChild:GetFrameLevel() + 2) | |
line:SetPoint("LEFT", tooltip.scrollChild) | |
line:SetPoint("RIGHT", tooltip.scrollChild) | |
if lineNum > 1 then | |
local v_margin = tooltip.cell_margin_v or CELL_MARGIN_V | |
line:SetPoint("TOP", tooltip.lines[lineNum - 1], "BOTTOM", 0, -v_margin) | |
SetTooltipSize(tooltip, tooltip.width, tooltip.height + v_margin) | |
else | |
line:SetPoint("TOP", tooltip.scrollChild) | |
end | |
tooltip.lines[lineNum] = line | |
line.cells = line.cells or AcquireTable() | |
line.height = 0 | |
line:SetHeight(1) | |
line:Show() | |
local colNum = 1 | |
for i = 1, #tooltip.columns do | |
local value = select(i, ...) | |
if value ~= nil then | |
lineNum, colNum = _SetCell(tooltip, lineNum, i, value, font, nil, 1, tooltip.labelProvider) | |
end | |
end | |
return lineNum, colNum | |
end | |
function tipPrototype:AddLine(...) | |
return CreateLine(self, self.regularFont, ...) | |
end | |
function tipPrototype:AddHeader(...) | |
local line, col = CreateLine(self, self.headerFont, ...) | |
self.lines[line].is_header = true | |
return line, col | |
end | |
end -- do-block | |
local GenericBackdrop = { | |
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background" | |
} | |
function tipPrototype:AddSeparator(height, r, g, b, a) | |
local lineNum, colNum = self:AddLine() | |
local line = self.lines[lineNum] | |
local color = _G.NORMAL_FONT_COLOR | |
height = height or 1 | |
SetTooltipSize(self, self.width, self.height + height) | |
line.height = height | |
line:SetHeight(height) | |
line.backdropInfo = GenericBackdrop | |
line:ApplyBackdrop() | |
line:SetBackdropColor(r or color.r, g or color.g, b or color.b, a or 1) | |
return lineNum, colNum | |
end | |
function tipPrototype:SetCellColor(lineNum, colNum, r, g, b, a) | |
local cell = self.lines[lineNum].cells[colNum] | |
if cell then | |
local sr, sg, sb, sa = self:GetBackdropColor() | |
line.backdropInfo = GenericBackdrop | |
line:ApplyBackdrop() | |
cell:SetBackdropColor(r or sr, g or sg, b or sb, a or sa) | |
end | |
end | |
function tipPrototype:SetColumnColor(colNum, r, g, b, a) | |
local column = self.columns[colNum] | |
if column then | |
local sr, sg, sb, sa = self:GetBackdropColor() | |
line.backdropInfo = GenericBackdrop | |
line:ApplyBackdrop() | |
column:SetBackdropColor(r or sr, g or sg, b or sb, a or sa) | |
end | |
end | |
function tipPrototype:SetLineColor(lineNum, r, g, b, a) | |
local line = self.lines[lineNum] | |
if line then | |
local sr, sg, sb, sa = self:GetBackdropColor() | |
line.backdropInfo = GenericBackdrop | |
line:ApplyBackdrop() | |
line:SetBackdropColor(r or sr, g or sg, b or sb, a or sa) | |
end | |
end | |
function tipPrototype:SetCellTextColor(lineNum, colNum, r, g, b, a) | |
local line = self.lines[lineNum] | |
local column = self.columns[colNum] | |
if not line or not column then | |
return | |
end | |
local cell = self.lines[lineNum].cells[colNum] | |
if cell then | |
if not cell.fontString then | |
error("cell's label provider did not assign a fontString field", 2) | |
end | |
if not cell.r then | |
cell.r, cell.g, cell.b, cell.a = cell.fontString:GetTextColor() | |
end | |
cell.fontString:SetTextColor(r or cell.r, g or cell.g, b or cell.b, a or cell.a) | |
end | |
end | |
function tipPrototype:SetColumnTextColor(colNum, r, g, b, a) | |
if not self.columns[colNum] then | |
return | |
end | |
for lineIndex = 1, #self.lines do | |
self:SetCellTextColor(lineIndex, colNum, r, g, b, a) | |
end | |
end | |
function tipPrototype:SetLineTextColor(lineNum, r, g, b, a) | |
local line = self.lines[lineNum] | |
if not line then | |
return | |
end | |
for cellIndex = 1, #line.cells do | |
self:SetCellTextColor(lineNum, line.cells[cellIndex]._column, r, g, b, a) | |
end | |
end | |
function tipPrototype:SetHighlightTexture(...) | |
return highlightTexture:SetTexture(...) | |
end | |
function tipPrototype:SetHighlightTexCoord(...) | |
highlightTexture:SetTexCoord(...) | |
end | |
do | |
local function checkFont(font, level, silent) | |
local bad = false | |
if not font then | |
bad = true | |
elseif type(font) == "string" then | |
local ref = _G[font] | |
if not ref or type(ref) ~= "table" or type(ref.IsObjectType) ~= "function" or not ref:IsObjectType("Font") then | |
bad = true | |
end | |
elseif type(font) ~= "table" or type(font.IsObjectType) ~= "function" or not font:IsObjectType("Font") then | |
bad = true | |
end | |
if bad then | |
if silent then | |
return false | |
end | |
error("font must be a Font instance or a string matching the name of a global Font instance, not: " .. tostring(font), level + 1) | |
end | |
return true | |
end | |
function tipPrototype:SetFont(font) | |
local is_string = type(font) == "string" | |
checkFont(font, 2) | |
self.regularFont = is_string and _G[font] or font | |
end | |
function tipPrototype:SetHeaderFont(font) | |
local is_string = type(font) == "string" | |
checkFont(font, 2) | |
self.headerFont = is_string and _G[font] or font | |
end | |
-- TODO: fixed argument positions / remove checks for performance? | |
function tipPrototype:SetCell(lineNum, colNum, value, ...) | |
-- Mandatory argument checking | |
if type(lineNum) ~= "number" then | |
error("line number must be a number, not: " .. tostring(lineNum), 2) | |
elseif lineNum < 1 or lineNum > #self.lines then | |
error("line number out of range: " .. tostring(lineNum), 2) | |
elseif type(colNum) ~= "number" then | |
error("column number must be a number, not: " .. tostring(colNum), 2) | |
elseif colNum < 1 or colNum > #self.columns then | |
error("column number out of range: " .. tostring(colNum), 2) | |
end | |
-- Variable argument checking | |
local font, justification, colSpan, provider | |
local i, arg = 1, ... | |
if arg == nil or checkFont(arg, 2, true) then | |
i, font, arg = 2, ... | |
end | |
if arg == nil or checkJustification(arg, 2, true) then | |
i, justification, arg = i + 1, select(i, ...) | |
end | |
if arg == nil or type(arg) == "number" then | |
i, colSpan, arg = i + 1, select(i, ...) | |
end | |
if arg == nil or type(arg) == "table" and type(arg.AcquireCell) == "function" then | |
i, provider = i + 1, arg | |
end | |
return _SetCell(self, lineNum, colNum, value, font, justification, colSpan, provider, select(i, ...)) | |
end | |
end -- do-block | |
function tipPrototype:GetFont() | |
return self.regularFont | |
end | |
function tipPrototype:GetHeaderFont() | |
return self.headerFont | |
end | |
function tipPrototype:GetLineCount() | |
return #self.lines | |
end | |
function tipPrototype:GetColumnCount() | |
return #self.columns | |
end | |
------------------------------------------------------------------------------ | |
-- Frame Scripts | |
------------------------------------------------------------------------------ | |
local scripts = { | |
OnEnter = function(frame, ...) | |
highlightFrame:SetParent(frame) | |
highlightFrame:SetAllPoints(frame) | |
highlightFrame:Show() | |
if frame._OnEnter_func then | |
frame:_OnEnter_func(frame._OnEnter_arg, ...) | |
end | |
end, | |
OnLeave = function(frame, ...) | |
highlightFrame:Hide() | |
highlightFrame:ClearAllPoints() | |
highlightFrame:SetParent(nil) | |
if frame._OnLeave_func then | |
frame:_OnLeave_func(frame._OnLeave_arg, ...) | |
end | |
end, | |
OnMouseDown = function(frame, ...) | |
frame:_OnMouseDown_func(frame._OnMouseDown_arg, ...) | |
end, | |
OnMouseUp = function(frame, ...) | |
frame:_OnMouseUp_func(frame._OnMouseUp_arg, ...) | |
end, | |
OnReceiveDrag = function(frame, ...) | |
frame:_OnReceiveDrag_func(frame._OnReceiveDrag_arg, ...) | |
end | |
} | |
function SetFrameScript(frame, script, func, arg) | |
if not scripts[script] then | |
return | |
end | |
frame["_" .. script .. "_func"] = func | |
frame["_" .. script .. "_arg"] = arg | |
if script == "OnMouseDown" or script == "OnMouseUp" or script == "OnReceiveDrag" then | |
if func then | |
frame:SetScript(script, scripts[script]) | |
else | |
frame:SetScript(script, nil) | |
end | |
end | |
-- if at least one script is set, set the OnEnter/OnLeave scripts for the highlight | |
if frame._OnEnter_func or frame._OnLeave_func or frame._OnMouseDown_func or frame._OnMouseUp_func or frame._OnReceiveDrag_func then | |
frame:EnableMouse(true) | |
frame:SetScript("OnEnter", scripts.OnEnter) | |
frame:SetScript("OnLeave", scripts.OnLeave) | |
else | |
frame:EnableMouse(false) | |
frame:SetScript("OnEnter", nil) | |
frame:SetScript("OnLeave", nil) | |
end | |
end | |
function ClearFrameScripts(frame) | |
if frame._OnEnter_func or frame._OnLeave_func or frame._OnMouseDown_func or frame._OnMouseUp_func or frame._OnReceiveDrag_func then | |
frame:EnableMouse(false) | |
frame:SetScript("OnEnter", nil) | |
frame._OnEnter_func = nil | |
frame._OnEnter_arg = nil | |
frame:SetScript("OnLeave", nil) | |
frame._OnLeave_func = nil | |
frame._OnLeave_arg = nil | |
frame:SetScript("OnReceiveDrag", nil) | |
frame._OnReceiveDrag_func = nil | |
frame._OnReceiveDrag_arg = nil | |
frame:SetScript("OnMouseDown", nil) | |
frame._OnMouseDown_func = nil | |
frame._OnMouseDown_arg = nil | |
frame:SetScript("OnMouseUp", nil) | |
frame._OnMouseUp_func = nil | |
frame._OnMouseUp_arg = nil | |
end | |
end | |
function tipPrototype:SetLineScript(lineNum, script, func, arg) | |
SetFrameScript(self.lines[lineNum], script, func, arg) | |
end | |
function tipPrototype:SetColumnScript(colNum, script, func, arg) | |
SetFrameScript(self.columns[colNum], script, func, arg) | |
end | |
function tipPrototype:SetCellScript(lineNum, colNum, script, func, arg) | |
local cell = self.lines[lineNum].cells[colNum] | |
if cell then | |
SetFrameScript(cell, script, func, arg) | |
end | |
end | |
------------------------------------------------------------------------------ | |
-- Auto-hiding feature | |
------------------------------------------------------------------------------ | |
-- Script of the auto-hiding child frame | |
local function AutoHideTimerFrame_OnUpdate(self, elapsed) | |
self.checkElapsed = self.checkElapsed + elapsed | |
if self.checkElapsed > 0.1 then | |
if self.parent:IsMouseOver() or (self.alternateFrame and self.alternateFrame:IsMouseOver()) then | |
self.elapsed = 0 | |
else | |
self.elapsed = self.elapsed + self.checkElapsed | |
if self.elapsed >= self.delay then | |
lib:Release(self.parent) | |
end | |
end | |
self.checkElapsed = 0 | |
end | |
end | |
-- Usage: | |
-- :SetAutoHideDelay(0.25) => hides after 0.25sec outside of the tooltip | |
-- :SetAutoHideDelay(0.25, someFrame) => hides after 0.25sec outside of both the tooltip and someFrame | |
-- :SetAutoHideDelay() => disable auto-hiding (default) | |
function tipPrototype:SetAutoHideDelay(delay, alternateFrame, releaseHandler) | |
local timerFrame = self.autoHideTimerFrame | |
delay = tonumber(delay) or 0 | |
if releaseHandler then | |
if type(releaseHandler) ~= "function" then | |
error("releaseHandler must be a function", 2) | |
end | |
lib.onReleaseHandlers[self] = releaseHandler | |
end | |
if delay > 0 then | |
if not timerFrame then | |
timerFrame = AcquireFrame(self) | |
timerFrame:SetScript("OnUpdate", AutoHideTimerFrame_OnUpdate) | |
self.autoHideTimerFrame = timerFrame | |
end | |
timerFrame.parent = self | |
timerFrame.checkElapsed = 0 | |
timerFrame.elapsed = 0 | |
timerFrame.delay = delay | |
timerFrame.alternateFrame = alternateFrame | |
timerFrame:Show() | |
elseif timerFrame then | |
self.autoHideTimerFrame = nil | |
timerFrame.alternateFrame = nil | |
timerFrame:SetScript("OnUpdate", nil) | |
ReleaseFrame(timerFrame) | |
end | |
end | |
------------------------------------------------------------------------------ | |
-- "Smart" Anchoring | |
------------------------------------------------------------------------------ | |
local function GetTipAnchor(frame) | |
local x, y = frame:GetCenter() | |
if not x or not y then | |
return "TOPLEFT", "BOTTOMLEFT" | |
end | |
local hhalf = (x > UIParent:GetWidth() * 2 / 3) and "RIGHT" or (x < UIParent:GetWidth() / 3) and "LEFT" or "" | |
local vhalf = (y > UIParent:GetHeight() / 2) and "TOP" or "BOTTOM" | |
return vhalf .. hhalf, frame, (vhalf == "TOP" and "BOTTOM" or "TOP") .. hhalf | |
end | |
function tipPrototype:SmartAnchorTo(frame) | |
if not frame then | |
error("Invalid frame provided.", 2) | |
end | |
self:ClearAllPoints() | |
self:SetClampedToScreen(true) | |
self:SetPoint(GetTipAnchor(frame)) | |
end | |
------------------------------------------------------------------------------ | |
-- Debug slashcmds | |
------------------------------------------------------------------------------ | |
-- @debug @ | |
local print = print | |
local function PrintStats() | |
local tipCache = tostring(#tooltipHeap) | |
local frameCache = tostring(#frameHeap) | |
local tableCache = tostring(#tableHeap) | |
local header = false | |
print("Tooltips used: " .. usedTooltips .. ", Cached: " .. tipCache .. ", Total: " .. tipCache + usedTooltips) | |
print("Frames used: " .. usedFrames .. ", Cached: " .. frameCache .. ", Total: " .. frameCache + usedFrames) | |
print("Tables used: " .. usedTables .. ", Cached: " .. tableCache .. ", Total: " .. tableCache + usedTables) | |
for k in pairs(activeTooltips) do | |
if not header then | |
print("Active tooltips:") | |
header = true | |
end | |
print("- " .. k) | |
end | |
end | |
SLASH_LibQTip1 = "/qtip" | |
_G.SlashCmdList["LibQTip"] = PrintStats | |
--@end-debug@]===] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment