flold

Untitled

Oct 17th, 2020
308
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 43.99 KB | None | 0 0
  1. local MAJOR = "LibQTip-1.0.7.3.5.3ExecAssistMod"
  2. local MINOR = 46 -- Should be manually increased
  3. local LibStub = _G.LibStub
  4.  
  5. assert(LibStub, MAJOR .. " requires LibStub")
  6.  
  7. local lib, oldMinor = LibStub:NewLibrary(MAJOR, MINOR)
  8.  
  9. if not lib then
  10.     return
  11. end -- No upgrade needed
  12.  
  13. ------------------------------------------------------------------------------
  14. -- Upvalued globals
  15. ------------------------------------------------------------------------------
  16. local table = _G.table
  17. local tinsert = table.insert
  18. local tremove = table.remove
  19. local wipe = table.wipe
  20.  
  21. local error = error
  22. local math = math
  23. local min, max = math.min, math.max
  24. local next = next
  25. local pairs, ipairs = pairs, ipairs
  26. local select = select
  27. local setmetatable = setmetatable
  28. local tonumber, tostring = tonumber, tostring
  29. local type = type
  30.  
  31. local CreateFrame = _G.CreateFrame
  32. local GameTooltip = _G.GameTooltip
  33. local UIParent = _G.UIParent
  34.  
  35. local geterrorhandler = _G.geterrorhandler
  36.  
  37. ------------------------------------------------------------------------------
  38. -- Tables and locals
  39. ------------------------------------------------------------------------------
  40. lib.frameMetatable = lib.frameMetatable or {__index = CreateFrame("Frame", nil, nil, "BackdropTemplate")}
  41.  
  42. lib.tipPrototype = lib.tipPrototype or setmetatable({}, lib.frameMetatable)
  43. lib.tipMetatable = lib.tipMetatable or {__index = lib.tipPrototype}
  44.  
  45. lib.providerPrototype = lib.providerPrototype or {}
  46. lib.providerMetatable = lib.providerMetatable or {__index = lib.providerPrototype}
  47.  
  48. lib.cellPrototype = lib.cellPrototype or setmetatable({}, lib.frameMetatable)
  49. lib.cellMetatable = lib.cellMetatable or {__index = lib.cellPrototype}
  50.  
  51. lib.activeTooltips = lib.activeTooltips or {}
  52.  
  53. lib.tooltipHeap = lib.tooltipHeap or {}
  54. lib.frameHeap = lib.frameHeap or {}
  55. lib.tableHeap = lib.tableHeap or {}
  56.  
  57. lib.onReleaseHandlers = lib.onReleaseHandlers or {}
  58.  
  59. local tipPrototype = lib.tipPrototype
  60. local tipMetatable = lib.tipMetatable
  61.  
  62. local providerPrototype = lib.providerPrototype
  63. local providerMetatable = lib.providerMetatable
  64.  
  65. local cellPrototype = lib.cellPrototype
  66. local cellMetatable = lib.cellMetatable
  67.  
  68. local activeTooltips = lib.activeTooltips
  69.  
  70. local highlightFrame = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
  71. highlightFrame:SetFrameStrata("TOOLTIP")
  72. highlightFrame:Hide()
  73.  
  74. local DEFAULT_HIGHLIGHT_TEXTURE_PATH = [[Interface\QuestFrame\UI-QuestTitleHighlight]]
  75.  
  76. local highlightTexture = highlightFrame:CreateTexture(nil, "OVERLAY")
  77. highlightTexture:SetTexture(DEFAULT_HIGHLIGHT_TEXTURE_PATH)
  78. highlightTexture:SetBlendMode("ADD")
  79. highlightTexture:SetAllPoints(highlightFrame)
  80.  
  81. ------------------------------------------------------------------------------
  82. -- Private methods for Caches and Tooltip
  83. ------------------------------------------------------------------------------
  84. local AcquireTooltip, ReleaseTooltip
  85. local AcquireCell, ReleaseCell
  86. local AcquireTable, ReleaseTable
  87.  
  88. local InitializeTooltip, SetTooltipSize, ResetTooltipSize, FixCellSizes
  89. local ClearTooltipScripts
  90. local SetFrameScript, ClearFrameScripts
  91.  
  92. ------------------------------------------------------------------------------
  93. -- Cache debugging.
  94. ------------------------------------------------------------------------------
  95. -- @debug @
  96. local usedTables, usedFrames, usedTooltips = 0, 0, 0
  97. --@end-debug@
  98.  
  99. ------------------------------------------------------------------------------
  100. -- Internal constants to tweak the layout
  101. ------------------------------------------------------------------------------
  102. local TOOLTIP_PADDING = 10
  103. local CELL_MARGIN_H = 6
  104. local CELL_MARGIN_V = 3
  105.  
  106. ------------------------------------------------------------------------------
  107. -- Public library API
  108. ------------------------------------------------------------------------------
  109. --- Create or retrieve the tooltip with the given key.
  110. -- If additional arguments are passed, they are passed to :SetColumnLayout for the acquired tooltip.
  111. -- @name LibQTip:Acquire(key[, numColumns, column1Justification, column2justification, ...])
  112. -- @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.
  113. -- Numbers and booleans should be avoided and strings should be carefully chosen to avoid namespace clashes - no "MyTooltip" - you have been warned!
  114. -- @return tooltip Frame object - the acquired tooltip.
  115. -- @usage Acquire a tooltip with at least 5 columns, justification : left, center, left, left, left
  116. -- <pre>local tip = LibStub('LibQTip-1.0'):Acquire('MyFooBarTooltip', 5, "LEFT", "CENTER")</pre>
  117. function lib:Acquire(key, ...)
  118.     if key == nil then
  119.         error("attempt to use a nil key", 2)
  120.     end
  121.  
  122.     local tooltip = activeTooltips[key]
  123.  
  124.     if not tooltip then
  125.         tooltip = AcquireTooltip()
  126.         InitializeTooltip(tooltip, key)
  127.         activeTooltips[key] = tooltip
  128.     end
  129.  
  130.     if select("#", ...) > 0 then
  131.         -- Here we catch any error to properly report it for the calling code
  132.         local ok, msg = pcall(tooltip.SetColumnLayout, tooltip, ...)
  133.  
  134.         if not ok then
  135.             error(msg, 2)
  136.         end
  137.     end
  138.  
  139.     return tooltip
  140. end
  141.  
  142. function lib:Release(tooltip)
  143.     local key = tooltip and tooltip.key
  144.  
  145.     if not key or activeTooltips[key] ~= tooltip then
  146.         return
  147.     end
  148.  
  149.     ReleaseTooltip(tooltip)
  150.     activeTooltips[key] = nil
  151. end
  152.  
  153. function lib:IsAcquired(key)
  154.     if key == nil then
  155.         error("attempt to use a nil key", 2)
  156.     end
  157.  
  158.     return not (not activeTooltips[key])
  159. end
  160.  
  161. function lib:IterateTooltips()
  162.     return pairs(activeTooltips)
  163. end
  164.  
  165. ------------------------------------------------------------------------------
  166. -- Frame cache
  167. ------------------------------------------------------------------------------
  168. local frameHeap = lib.frameHeap
  169.  
  170. local function AcquireFrame(parent)
  171.     local frame = tremove(frameHeap) or CreateFrame("Frame", nil, nil, "BackdropTemplate")
  172.     frame:SetParent(parent)
  173.     --[===[@debug@
  174.     usedFrames = usedFrames + 1
  175.     --@end-debug@]===]
  176.     return frame
  177. end
  178.  
  179. local function ReleaseFrame(frame)
  180.     frame:Hide()
  181.     frame:SetParent(nil)
  182.     frame:ClearAllPoints()
  183.     frame:SetBackdrop(nil)
  184.  
  185.     ClearFrameScripts(frame)
  186.  
  187.     tinsert(frameHeap, frame)
  188.     --[===[@debug@
  189.     usedFrames = usedFrames - 1
  190.     --@end-debug@]===]
  191. end
  192.  
  193. ------------------------------------------------------------------------------
  194. -- Dirty layout handler
  195. ------------------------------------------------------------------------------
  196. lib.layoutCleaner = lib.layoutCleaner or CreateFrame("Frame", nil, nil, "BackdropTemplate")
  197.  
  198. local layoutCleaner = lib.layoutCleaner
  199. layoutCleaner.registry = layoutCleaner.registry or {}
  200.  
  201. function layoutCleaner:RegisterForCleanup(tooltip)
  202.     self.registry[tooltip] = true
  203.     self:Show()
  204. end
  205.  
  206. function layoutCleaner:CleanupLayouts()
  207.     self:Hide()
  208.  
  209.     for tooltip in pairs(self.registry) do
  210.         FixCellSizes(tooltip)
  211.     end
  212.  
  213.     wipe(self.registry)
  214. end
  215.  
  216. layoutCleaner:SetScript("OnUpdate", layoutCleaner.CleanupLayouts)
  217.  
  218. ------------------------------------------------------------------------------
  219. -- CellProvider and Cell
  220. ------------------------------------------------------------------------------
  221. function providerPrototype:AcquireCell()
  222.     local cell = tremove(self.heap)
  223.  
  224.     if not cell then
  225.         cell = setmetatable(CreateFrame("Frame", nil, UIParent, "BackdropTemplate"), self.cellMetatable)
  226.  
  227.         if type(cell.InitializeCell) == "function" then
  228.             cell:InitializeCell()
  229.         end
  230.     end
  231.  
  232.     self.cells[cell] = true
  233.  
  234.     return cell
  235. end
  236.  
  237. function providerPrototype:ReleaseCell(cell)
  238.     if not self.cells[cell] then
  239.         return
  240.     end
  241.  
  242.     if type(cell.ReleaseCell) == "function" then
  243.         cell:ReleaseCell()
  244.     end
  245.  
  246.     self.cells[cell] = nil
  247.     tinsert(self.heap, cell)
  248. end
  249.  
  250. function providerPrototype:GetCellPrototype()
  251.     return self.cellPrototype, self.cellMetatable
  252. end
  253.  
  254. function providerPrototype:IterateCells()
  255.     return pairs(self.cells)
  256. end
  257.  
  258. function lib:CreateCellProvider(baseProvider)
  259.     local cellBaseMetatable, cellBasePrototype
  260.  
  261.     if baseProvider and baseProvider.GetCellPrototype then
  262.         cellBasePrototype, cellBaseMetatable = baseProvider:GetCellPrototype()
  263.     else
  264.         cellBaseMetatable = cellMetatable
  265.     end
  266.  
  267.     local newCellPrototype = setmetatable({}, cellBaseMetatable)
  268.     local newCellProvider = setmetatable({}, providerMetatable)
  269.  
  270.     newCellProvider.heap = {}
  271.     newCellProvider.cells = {}
  272.     newCellProvider.cellPrototype = newCellPrototype
  273.     newCellProvider.cellMetatable = {__index = newCellPrototype}
  274.  
  275.     return newCellProvider, newCellPrototype, cellBasePrototype
  276. end
  277.  
  278. ------------------------------------------------------------------------------
  279. -- Basic label provider
  280. ------------------------------------------------------------------------------
  281. if not lib.LabelProvider then
  282.     lib.LabelProvider, lib.LabelPrototype = lib:CreateCellProvider()
  283. end
  284.  
  285. local labelProvider = lib.LabelProvider
  286. local labelPrototype = lib.LabelPrototype
  287.  
  288. function labelPrototype:InitializeCell()
  289.     self.fontString = self:CreateFontString()
  290.     self.fontString:SetFontObject(_G.GameTooltipText)
  291. end
  292.  
  293. function labelPrototype:SetupCell(tooltip, value, justification, font, leftPadding, rightPadding, maxWidth, minWidth, ...)
  294.     local fontString = self.fontString
  295.     local line = tooltip.lines[self._line]
  296.  
  297.     -- detatch fs from cell for size calculations
  298.     fontString:ClearAllPoints()
  299.     fontString:SetFontObject(font or (line.is_header and tooltip:GetHeaderFont() or tooltip:GetFont()))
  300.     fontString:SetJustifyH(justification)
  301.     fontString:SetText(tostring(value))
  302.  
  303.     leftPadding = leftPadding or 0
  304.     rightPadding = rightPadding or 0
  305.  
  306.     local width = fontString:GetStringWidth() + leftPadding + rightPadding
  307.  
  308.     if maxWidth and minWidth and (maxWidth < minWidth) then
  309.         error("maximum width cannot be lower than minimum width: " .. tostring(maxWidth) .. " < " .. tostring(minWidth), 2)
  310.     end
  311.  
  312.     if maxWidth and (maxWidth < (leftPadding + rightPadding)) then
  313.         error("maximum width cannot be lower than the sum of paddings: " .. tostring(maxWidth) .. " < " .. tostring(leftPadding) .. " + " .. tostring(rightPadding), 2)
  314.     end
  315.  
  316.     if minWidth and width < minWidth then
  317.         width = minWidth
  318.     end
  319.  
  320.     if maxWidth and maxWidth < width then
  321.         width = maxWidth
  322.     end
  323.  
  324.     fontString:SetWidth(width - (leftPadding + rightPadding))
  325.     -- Use GetHeight() instead of GetStringHeight() so lines which are longer than width will wrap.
  326.     local height = fontString:GetHeight()
  327.  
  328.     -- reanchor fs to cell
  329.     fontString:SetWidth(0)
  330.     fontString:SetPoint("TOPLEFT", self, "TOPLEFT", leftPadding, 0)
  331.     fontString:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", -rightPadding, 0)
  332.     --~     fs:SetPoint("TOPRIGHT", self, "TOPRIGHT", -r_pad, 0)
  333.  
  334.     self._paddingL = leftPadding
  335.     self._paddingR = rightPadding
  336.  
  337.     return width, height
  338. end
  339.  
  340. function labelPrototype:getContentHeight()
  341.     local fontString = self.fontString
  342.     fontString:SetWidth(self:GetWidth() - (self._paddingL + self._paddingR))
  343.  
  344.     local height = self.fontString:GetHeight()
  345.     fontString:SetWidth(0)
  346.  
  347.     return height
  348. end
  349.  
  350. function labelPrototype:GetPosition()
  351.     return self._line, self._column
  352. end
  353.  
  354. ------------------------------------------------------------------------------
  355. -- Tooltip cache
  356. ------------------------------------------------------------------------------
  357. local tooltipHeap = lib.tooltipHeap
  358.  
  359. -- Returns a tooltip
  360. function AcquireTooltip()
  361.     local tooltip = tremove(tooltipHeap)
  362.  
  363.     if not tooltip then
  364.         tooltip = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
  365.  
  366.         local scrollFrame = CreateFrame("ScrollFrame", nil, tooltip, "BackdropTemplate")
  367.         scrollFrame:SetPoint("TOP", tooltip, "TOP", 0, -TOOLTIP_PADDING)
  368.         scrollFrame:SetPoint("BOTTOM", tooltip, "BOTTOM", 0, TOOLTIP_PADDING)
  369.         scrollFrame:SetPoint("LEFT", tooltip, "LEFT", TOOLTIP_PADDING, 0)
  370.         scrollFrame:SetPoint("RIGHT", tooltip, "RIGHT", -TOOLTIP_PADDING, 0)
  371.         tooltip.scrollFrame = scrollFrame
  372.  
  373.         local scrollChild = CreateFrame("Frame", nil, tooltip.scrollFrame, "BackdropTemplate")
  374.         scrollFrame:SetScrollChild(scrollChild)
  375.         tooltip.scrollChild = scrollChild
  376.  
  377.         setmetatable(tooltip, tipMetatable)
  378.     end
  379.  
  380.     --[===[@debug@
  381.     usedTooltips = usedTooltips + 1
  382.     --@end-debug@]===]
  383.     return tooltip
  384. end
  385.  
  386. -- Cleans the tooltip and stores it in the cache
  387. function ReleaseTooltip(tooltip)
  388.     if tooltip.releasing then
  389.         return
  390.     end
  391.  
  392.     tooltip.releasing = true
  393.     tooltip:Hide()
  394.  
  395.     local releaseHandler = lib.onReleaseHandlers[tooltip]
  396.  
  397.     if releaseHandler then
  398.         lib.onReleaseHandlers[tooltip] = nil
  399.  
  400.         local success, errorMessage = pcall(releaseHandler, tooltip)
  401.  
  402.         if not success then
  403.             geterrorhandler()(errorMessage)
  404.         end
  405.     elseif tooltip.OnRelease then
  406.         local success, errorMessage = pcall(tooltip.OnRelease, tooltip)
  407.         if not success then
  408.             geterrorhandler()(errorMessage)
  409.         end
  410.  
  411.         tooltip.OnRelease = nil
  412.     end
  413.  
  414.     tooltip.releasing = nil
  415.     tooltip.key = nil
  416.     tooltip.step = nil
  417.  
  418.     ClearTooltipScripts(tooltip)
  419.  
  420.     tooltip:SetAutoHideDelay(nil)
  421.     tooltip:ClearAllPoints()
  422.     tooltip:Clear()
  423.  
  424.     if tooltip.slider then
  425.         tooltip.slider:SetValue(0)
  426.         tooltip.slider:Hide()
  427.         tooltip.scrollFrame:SetPoint("RIGHT", tooltip, "RIGHT", -TOOLTIP_PADDING, 0)
  428.         tooltip:EnableMouseWheel(false)
  429.     end
  430.  
  431.     for i, column in ipairs(tooltip.columns) do
  432.         tooltip.columns[i] = ReleaseFrame(column)
  433.     end
  434.  
  435.     tooltip.columns = ReleaseTable(tooltip.columns)
  436.     tooltip.lines = ReleaseTable(tooltip.lines)
  437.     tooltip.colspans = ReleaseTable(tooltip.colspans)
  438.  
  439.     layoutCleaner.registry[tooltip] = nil
  440.     tinsert(tooltipHeap, tooltip)
  441.  
  442.     highlightTexture:SetTexture(DEFAULT_HIGHLIGHT_TEXTURE_PATH)
  443.     highlightTexture:SetTexCoord(0, 1, 0, 1)
  444.  
  445.     --[===[@debug@
  446.     usedTooltips = usedTooltips - 1
  447.     --@end-debug@]===]
  448. end
  449.  
  450. ------------------------------------------------------------------------------
  451. -- Cell 'cache' (just a wrapper to the provider's cache)
  452. ------------------------------------------------------------------------------
  453. -- Returns a cell for the given tooltip from the given provider
  454. function AcquireCell(tooltip, provider)
  455.     local cell = provider:AcquireCell(tooltip)
  456.  
  457.     cell:SetParent(tooltip.scrollChild)
  458.     cell:SetFrameLevel(tooltip.scrollChild:GetFrameLevel() + 3)
  459.     cell._provider = provider
  460.  
  461.     return cell
  462. end
  463.  
  464. -- Cleans the cell hands it to its provider for storing
  465. function ReleaseCell(cell)
  466.     if cell.fontString and cell.r then
  467.         cell.fontString:SetTextColor(cell.r, cell.g, cell.b, cell.a)
  468.     end
  469.  
  470.     cell._font = nil
  471.     cell._justification = nil
  472.     cell._colSpan = nil
  473.     cell._line = nil
  474.     cell._column = nil
  475.  
  476.     cell:Hide()
  477.     cell:ClearAllPoints()
  478.     cell:SetParent(nil)
  479.     cell:SetBackdrop(nil)
  480.  
  481.     ClearFrameScripts(cell)
  482.  
  483.     cell._provider:ReleaseCell(cell)
  484.     cell._provider = nil
  485. end
  486.  
  487. ------------------------------------------------------------------------------
  488. -- Table cache
  489. ------------------------------------------------------------------------------
  490. local tableHeap = lib.tableHeap
  491.  
  492. -- Returns a table
  493. function AcquireTable()
  494.     local tbl = tremove(tableHeap) or {}
  495.     --[===[@debug@
  496.     usedTables = usedTables + 1
  497.     --@end-debug@]===]
  498.     return tbl
  499. end
  500.  
  501. -- Cleans the table and stores it in the cache
  502. function ReleaseTable(tableInstance)
  503.     wipe(tableInstance)
  504.     tinsert(tableHeap, tableInstance)
  505.     --[===[@debug@
  506.     usedTables = usedTables - 1
  507.     --@end-debug@]===]
  508. end
  509.  
  510. ------------------------------------------------------------------------------
  511. -- Tooltip prototype
  512. ------------------------------------------------------------------------------
  513. function InitializeTooltip(tooltip, key)
  514.     ----------------------------------------------------------------------
  515.     -- (Re)set frame settings
  516.     ----------------------------------------------------------------------
  517.     local backdrop = GameTooltip:GetBackdrop()
  518.  
  519.     tooltip:SetBackdrop(backdrop)
  520.  
  521.     if backdrop then
  522.         tooltip:SetBackdropColor(GameTooltip:GetBackdropColor())
  523.         tooltip:SetBackdropBorderColor(GameTooltip:GetBackdropBorderColor())
  524.     end
  525.  
  526.     tooltip:SetScale(GameTooltip:GetScale())
  527.     tooltip:SetAlpha(1)
  528.     tooltip:SetFrameStrata("TOOLTIP")
  529.     tooltip:SetClampedToScreen(false)
  530.  
  531.     ----------------------------------------------------------------------
  532.     -- Internal data. Since it's possible to Acquire twice without calling
  533.     -- release, check for pre-existence.
  534.     ----------------------------------------------------------------------
  535.     tooltip.key = key
  536.     tooltip.columns = tooltip.columns or AcquireTable()
  537.     tooltip.lines = tooltip.lines or AcquireTable()
  538.     tooltip.colspans = tooltip.colspans or AcquireTable()
  539.     tooltip.regularFont = _G.GameTooltipText
  540.     tooltip.headerFont = _G.GameTooltipHeaderText
  541.     tooltip.labelProvider = labelProvider
  542.     tooltip.cell_margin_h = tooltip.cell_margin_h or CELL_MARGIN_H
  543.     tooltip.cell_margin_v = tooltip.cell_margin_v or CELL_MARGIN_V
  544.  
  545.     ----------------------------------------------------------------------
  546.     -- Finishing procedures
  547.     ----------------------------------------------------------------------
  548.     tooltip:SetAutoHideDelay(nil)
  549.     tooltip:Hide()
  550.     ResetTooltipSize(tooltip)
  551. end
  552.  
  553. function tipPrototype:SetDefaultProvider(myProvider)
  554.     if not myProvider then
  555.         return
  556.     end
  557.  
  558.     self.labelProvider = myProvider
  559. end
  560.  
  561. function tipPrototype:GetDefaultProvider()
  562.     return self.labelProvider
  563. end
  564.  
  565. local function checkJustification(justification, level, silent)
  566.     if justification ~= "LEFT" and justification ~= "CENTER" and justification ~= "RIGHT" then
  567.         if silent then
  568.             return false
  569.         end
  570.         error("invalid justification, must one of LEFT, CENTER or RIGHT, not: " .. tostring(justification), level + 1)
  571.     end
  572.  
  573.     return true
  574. end
  575.  
  576. function tipPrototype:SetColumnLayout(numColumns, ...)
  577.     if type(numColumns) ~= "number" or numColumns < 1 then
  578.         error("number of columns must be a positive number, not: " .. tostring(numColumns), 2)
  579.     end
  580.  
  581.     for i = 1, numColumns do
  582.         local justification = select(i, ...) or "LEFT"
  583.  
  584.         checkJustification(justification, 2)
  585.  
  586.         if self.columns[i] then
  587.             self.columns[i].justification = justification
  588.         else
  589.             self:AddColumn(justification)
  590.         end
  591.     end
  592. end
  593.  
  594. function tipPrototype:AddColumn(justification)
  595.     justification = justification or "LEFT"
  596.     checkJustification(justification, 2)
  597.  
  598.     local colNum = #self.columns + 1
  599.     local column = self.columns[colNum] or AcquireFrame(self.scrollChild)
  600.  
  601.     column:SetFrameLevel(self.scrollChild:GetFrameLevel() + 1)
  602.     column.justification = justification
  603.     column.width = 0
  604.     column:SetWidth(1)
  605.     column:SetPoint("TOP", self.scrollChild)
  606.     column:SetPoint("BOTTOM", self.scrollChild)
  607.  
  608.     if colNum > 1 then
  609.         local h_margin = self.cell_margin_h or CELL_MARGIN_H
  610.  
  611.         column:SetPoint("LEFT", self.columns[colNum - 1], "RIGHT", h_margin, 0)
  612.         SetTooltipSize(self, self.width + h_margin, self.height)
  613.     else
  614.         column:SetPoint("LEFT", self.scrollChild)
  615.     end
  616.  
  617.     column:Show()
  618.     self.columns[colNum] = column
  619.  
  620.     return colNum
  621. end
  622.  
  623. ------------------------------------------------------------------------------
  624. -- Convenient methods
  625. ------------------------------------------------------------------------------
  626. function tipPrototype:Release()
  627.     lib:Release(self)
  628. end
  629.  
  630. function tipPrototype:IsAcquiredBy(key)
  631.     return key ~= nil and self.key == key
  632. end
  633.  
  634. ------------------------------------------------------------------------------
  635. -- Script hooks
  636. ------------------------------------------------------------------------------
  637. local RawSetScript = lib.frameMetatable.__index.SetScript
  638.  
  639. function ClearTooltipScripts(tooltip)
  640.     if tooltip.scripts then
  641.         for scriptType in pairs(tooltip.scripts) do
  642.             RawSetScript(tooltip, scriptType, nil)
  643.         end
  644.  
  645.         tooltip.scripts = ReleaseTable(tooltip.scripts)
  646.     end
  647. end
  648.  
  649. function tipPrototype:SetScript(scriptType, handler)
  650.     RawSetScript(self, scriptType, handler)
  651.  
  652.     if handler then
  653.         if not self.scripts then
  654.             self.scripts = AcquireTable()
  655.         end
  656.  
  657.         self.scripts[scriptType] = true
  658.     elseif self.scripts then
  659.         self.scripts[scriptType] = nil
  660.     end
  661. end
  662.  
  663. -- That might break some addons ; those addons were breaking other
  664. -- addons' tooltip though.
  665. function tipPrototype:HookScript()
  666.     geterrorhandler()(":HookScript is not allowed on LibQTip tooltips")
  667. end
  668.  
  669. ------------------------------------------------------------------------------
  670. -- Scrollbar data and functions
  671. ------------------------------------------------------------------------------
  672. local sliderBackdrop = {
  673.     bgFile = [[Interface\Buttons\UI-SliderBar-Background]],
  674.     edgeFile = [[Interface\Buttons\UI-SliderBar-Border]],
  675.     tile = true,
  676.     edgeSize = 8,
  677.     tileSize = 8,
  678.     insets = {
  679.         left = 3,
  680.         right = 3,
  681.         top = 3,
  682.         bottom = 3
  683.     }
  684. }
  685.  
  686. local function slider_OnValueChanged(self)
  687.     self.scrollFrame:SetVerticalScroll(self:GetValue())
  688. end
  689.  
  690. local function tooltip_OnMouseWheel(self, delta)
  691.     local slider = self.slider
  692.     local currentValue = slider:GetValue()
  693.     local minValue, maxValue = slider:GetMinMaxValues()
  694.     local stepValue = self.step or 10
  695.  
  696.     if delta < 0 and currentValue < maxValue then
  697.         slider:SetValue(min(maxValue, currentValue + stepValue))
  698.     elseif delta > 0 and currentValue > minValue then
  699.         slider:SetValue(max(minValue, currentValue - stepValue))
  700.     end
  701. end
  702.  
  703. -- Set the step size for the scroll bar
  704. function tipPrototype:SetScrollStep(step)
  705.     self.step = step
  706. end
  707.  
  708. -- will resize the tooltip to fit the screen and show a scrollbar if needed
  709. function tipPrototype:UpdateScrolling(maxheight)
  710.     self:SetClampedToScreen(false)
  711.  
  712.     -- all data is in the tooltip; fix colspan width and prevent the layout cleaner from messing up the tooltip later
  713.     FixCellSizes(self)
  714.     layoutCleaner.registry[self] = nil
  715.  
  716.     local scale = self:GetScale()
  717.     local topside = self:GetTop()
  718.     local bottomside = self:GetBottom()
  719.     local screensize = UIParent:GetHeight() / scale
  720.     local tipsize = (topside - bottomside)
  721.  
  722.     -- if the tooltip would be too high, limit its height and show the slider
  723.     if bottomside < 0 or topside > screensize or (maxheight and tipsize > maxheight) then
  724.         local shrink = (bottomside < 0 and (5 - bottomside) or 0) + (topside > screensize and (topside - screensize + 5) or 0)
  725.  
  726.         if maxheight and tipsize - shrink > maxheight then
  727.             shrink = tipsize - maxheight
  728.         end
  729.  
  730.         self:SetHeight(2 * TOOLTIP_PADDING + self.height - shrink)
  731.         self:SetWidth(2 * TOOLTIP_PADDING + self.width + 20)
  732.         self.scrollFrame:SetPoint("RIGHT", self, "RIGHT", -(TOOLTIP_PADDING + 20), 0)
  733.  
  734.         if not self.slider then
  735.             local slider = CreateFrame("Slider", nil, self, "BackdropTemplate")
  736.             slider.scrollFrame = self.scrollFrame
  737.  
  738.             slider:SetOrientation("VERTICAL")
  739.             slider:SetPoint("TOPRIGHT", self, "TOPRIGHT", -TOOLTIP_PADDING, -TOOLTIP_PADDING)
  740.             slider:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", -TOOLTIP_PADDING, TOOLTIP_PADDING)
  741.             slider:SetBackdrop(sliderBackdrop)
  742.             slider:SetThumbTexture([[Interface\Buttons\UI-SliderBar-Button-Vertical]])
  743.             slider:SetMinMaxValues(0, 1)
  744.             slider:SetValueStep(1)
  745.             slider:SetWidth(12)
  746.             slider:SetScript("OnValueChanged", slider_OnValueChanged)
  747.             slider:SetValue(0)
  748.  
  749.             self.slider = slider
  750.         end
  751.  
  752.         self.slider:SetMinMaxValues(0, shrink)
  753.         self.slider:Show()
  754.  
  755.         self:EnableMouseWheel(true)
  756.         self:SetScript("OnMouseWheel", tooltip_OnMouseWheel)
  757.     else
  758.         self:SetHeight(2 * TOOLTIP_PADDING + self.height)
  759.         self:SetWidth(2 * TOOLTIP_PADDING + self.width)
  760.  
  761.         self.scrollFrame:SetPoint("RIGHT", self, "RIGHT", -TOOLTIP_PADDING, 0)
  762.  
  763.         if self.slider then
  764.             self.slider:SetValue(0)
  765.             self.slider:Hide()
  766.  
  767.             self:EnableMouseWheel(false)
  768.             self:SetScript("OnMouseWheel", nil)
  769.         end
  770.     end
  771. end
  772.  
  773. ------------------------------------------------------------------------------
  774. -- Tooltip methods for changing its contents.
  775. ------------------------------------------------------------------------------
  776. function tipPrototype:Clear()
  777.     for i, line in ipairs(self.lines) do
  778.         for _, cell in pairs(line.cells) do
  779.             if cell then
  780.                 ReleaseCell(cell)
  781.             end
  782.         end
  783.  
  784.         ReleaseTable(line.cells)
  785.  
  786.         line.cells = nil
  787.         line.is_header = nil
  788.  
  789.         ReleaseFrame(line)
  790.  
  791.         self.lines[i] = nil
  792.     end
  793.  
  794.     for _, column in ipairs(self.columns) do
  795.         column.width = 0
  796.         column:SetWidth(1)
  797.     end
  798.  
  799.     wipe(self.colspans)
  800.  
  801.     self.cell_margin_h = nil
  802.     self.cell_margin_v = nil
  803.  
  804.     ResetTooltipSize(self)
  805. end
  806.  
  807. function tipPrototype:SetCellMarginH(size)
  808.     if #self.lines > 0 then
  809.         error("Unable to set horizontal margin while the tooltip has lines.", 2)
  810.     end
  811.  
  812.     if not size or type(size) ~= "number" or size < 0 then
  813.         error("Margin size must be a positive number or zero.", 2)
  814.     end
  815.  
  816.     self.cell_margin_h = size
  817. end
  818.  
  819. function tipPrototype:SetCellMarginV(size)
  820.     if #self.lines > 0 then
  821.         error("Unable to set vertical margin while the tooltip has lines.", 2)
  822.     end
  823.  
  824.     if not size or type(size) ~= "number" or size < 0 then
  825.         error("Margin size must be a positive number or zero.", 2)
  826.     end
  827.  
  828.     self.cell_margin_v = size
  829. end
  830.  
  831. function SetTooltipSize(tooltip, width, height)
  832.     tooltip.height = height
  833.     tooltip.width = width
  834.  
  835.     tooltip:SetHeight(2 * TOOLTIP_PADDING + height)
  836.     tooltip:SetWidth(2 * TOOLTIP_PADDING + width)
  837.  
  838.     tooltip.scrollChild:SetHeight(height)
  839.     tooltip.scrollChild:SetWidth(width)
  840. end
  841.  
  842. -- Add 2 pixels to height so dangling letters (g, y, p, j, etc) are not clipped.
  843. function ResetTooltipSize(tooltip)
  844.     local h_margin = tooltip.cell_margin_h or CELL_MARGIN_H
  845.  
  846.     SetTooltipSize(tooltip, max(0, (h_margin * (#tooltip.columns - 1)) + (h_margin / 2)), 2)
  847. end
  848.  
  849. local function EnlargeColumn(tooltip, column, width)
  850.     if width > column.width then
  851.         SetTooltipSize(tooltip, tooltip.width + width - column.width, tooltip.height)
  852.  
  853.         column.width = width
  854.         column:SetWidth(width)
  855.     end
  856. end
  857.  
  858. local function ResizeLine(tooltip, line, height)
  859.     SetTooltipSize(tooltip, tooltip.width, tooltip.height + height - line.height)
  860.  
  861.     line.height = height
  862.     line:SetHeight(height)
  863. end
  864.  
  865. function FixCellSizes(tooltip)
  866.     local columns = tooltip.columns
  867.     local colspans = tooltip.colspans
  868.     local lines = tooltip.lines
  869.     local h_margin = tooltip.cell_margin_h or CELL_MARGIN_H
  870.  
  871.     -- resize columns to make room for the colspans
  872.     while next(colspans) do
  873.         local maxNeedCols
  874.         local maxNeedWidthPerCol = 0
  875.  
  876.         -- calculate the colspan with the highest additional width need per column
  877.         for colRange, width in pairs(colspans) do
  878.             local left, right = colRange:match("^(%d+)%-(%d+)$")
  879.  
  880.             left, right = tonumber(left), tonumber(right)
  881.  
  882.             for col = left, right - 1 do
  883.                 width = width - columns[col].width - h_margin
  884.             end
  885.  
  886.             width = width - columns[right].width
  887.  
  888.             if width <= 0 then
  889.                 colspans[colRange] = nil
  890.             else
  891. -- ExecAssist Adjustment: EA needs the remaining width dumped into the last cell of the col-span for esthetic reasons
  892. --  As https://www.wowace.com/projects/libqtip-1-0/issues/35 has not yet been addressed or and the max-cell-size parameter
  893. --  not being obeyed (like it should) two adjustments needs to be made:
  894. --  REMed           width = width / (right - left + 1)
  895. --  This forces maxNeedWidthPerCol=width rather a percentage
  896. --  Later, instead of spreading 100% of the needed width to all spanned cells (left, right) it's dumped into the right-most cell
  897. -- (right, right). It's done like to create the least impact on the existing lib code
  898.                 if width > maxNeedWidthPerCol then  -- NB, maxNeedWidthPerCol now = width
  899.                     maxNeedCols = colRange
  900.                     maxNeedWidthPerCol = width
  901.                 end
  902.             end
  903.         end
  904.  
  905.         -- resize all columns for that colspan
  906.         if maxNeedCols then
  907.             local left, right = maxNeedCols:match("^(%d+)%-(%d+)$")
  908.  
  909.             for col = right, right do -- -- ExecAssist Adjustment: was left, right
  910.                 EnlargeColumn(tooltip, columns[col], columns[col].width + maxNeedWidthPerCol)
  911.             end
  912.  
  913.             colspans[maxNeedCols] = nil
  914.         end
  915.     end
  916.  
  917.     --now that the cell width is set, recalculate the rows' height
  918.     for _, line in ipairs(lines) do
  919.         if #(line.cells) > 0 then
  920.             local lineheight = 0
  921.  
  922.             for _, cell in pairs(line.cells) do
  923.                 if cell then
  924.                     lineheight = max(lineheight, cell:getContentHeight())
  925.                 end
  926.             end
  927.  
  928.             if lineheight > 0 then
  929.                 ResizeLine(tooltip, line, lineheight)
  930.             end
  931.         end
  932.     end
  933. end
  934.  
  935. local function _SetCell(tooltip, lineNum, colNum, value, font, justification, colSpan, provider, ...)
  936.     local line = tooltip.lines[lineNum]
  937.     local cells = line.cells
  938.  
  939.     -- Unset: be quick
  940.     if value == nil then
  941.         local cell = cells[colNum]
  942.  
  943.         if cell then
  944.             for i = colNum, colNum + cell._colSpan - 1 do
  945.                 cells[i] = nil
  946.             end
  947.  
  948.             ReleaseCell(cell)
  949.         end
  950.  
  951.         return lineNum, colNum
  952.     end
  953.  
  954.     font = font or (line.is_header and tooltip.headerFont or tooltip.regularFont)
  955.  
  956.     -- Check previous cell
  957.     local cell
  958.     local prevCell = cells[colNum]
  959.  
  960.     if prevCell then
  961.         -- There is a cell here
  962.         justification = justification or prevCell._justification
  963.         colSpan = colSpan or prevCell._colSpan
  964.  
  965.         -- Clear the currently marked colspan
  966.         for i = colNum + 1, colNum + prevCell._colSpan - 1 do
  967.             cells[i] = nil
  968.         end
  969.  
  970.         if provider == nil or prevCell._provider == provider then
  971.             -- Reuse existing cell
  972.             cell = prevCell
  973.             provider = cell._provider
  974.         else
  975.             -- A new cell is required
  976.             cells[colNum] = ReleaseCell(prevCell)
  977.         end
  978.     elseif prevCell == nil then
  979.         -- Creating a new cell, using meaningful defaults.
  980.         provider = provider or tooltip.labelProvider
  981.         justification = justification or tooltip.columns[colNum].justification or "LEFT"
  982.         colSpan = colSpan or 1
  983.     else
  984.         error("overlapping cells at column " .. colNum, 3)
  985.     end
  986.  
  987.     local tooltipWidth = #tooltip.columns
  988.     local rightColNum
  989.  
  990.     if colSpan > 0 then
  991.         rightColNum = colNum + colSpan - 1
  992.  
  993.         if rightColNum > tooltipWidth then
  994.             error("ColSpan too big, cell extends beyond right-most column", 3)
  995.         end
  996.     else
  997.         -- Zero or negative: count back from right-most columns
  998.         rightColNum = max(colNum, tooltipWidth + colSpan)
  999.         -- Update colspan to its effective value
  1000.         colSpan = 1 + rightColNum - colNum
  1001.     end
  1002.  
  1003.     -- Cleanup colspans
  1004.     for i = colNum + 1, rightColNum do
  1005.         local columnCell = cells[i]
  1006.  
  1007.         if columnCell then
  1008.             ReleaseCell(columnCell)
  1009.         elseif columnCell == false then
  1010.             error("overlapping cells at column " .. i, 3)
  1011.         end
  1012.  
  1013.         cells[i] = false
  1014.     end
  1015.  
  1016.     -- Create the cell
  1017.     if not cell then
  1018.         cell = AcquireCell(tooltip, provider)
  1019.         cells[colNum] = cell
  1020.     end
  1021.  
  1022.     -- Anchor the cell
  1023.     cell:SetPoint("LEFT", tooltip.columns[colNum])
  1024.     cell:SetPoint("RIGHT", tooltip.columns[rightColNum])
  1025.     cell:SetPoint("TOP", line)
  1026.     cell:SetPoint("BOTTOM", line)
  1027.  
  1028.     -- Store the cell settings directly into the cell
  1029.     -- That's a bit risky but is really cheap compared to other ways to do it
  1030.     cell._font, cell._justification, cell._colSpan, cell._line, cell._column = font, justification, colSpan, lineNum, colNum
  1031.  
  1032.     -- Setup the cell content
  1033.     local width, height = cell:SetupCell(tooltip, value, justification, font, ...)
  1034.     cell:Show()
  1035.  
  1036.     if colSpan > 1 then
  1037.         -- Postpone width changes until the tooltip is shown
  1038.         local colRange = colNum .. "-" .. rightColNum
  1039.  
  1040.         tooltip.colspans[colRange] = max(tooltip.colspans[colRange] or 0, width)
  1041.         layoutCleaner:RegisterForCleanup(tooltip)
  1042.     else
  1043.         -- Enlarge the column and tooltip if need be
  1044.         EnlargeColumn(tooltip, tooltip.columns[colNum], width)
  1045.     end
  1046.  
  1047.     -- Enlarge the line and tooltip if need be
  1048.     if height > line.height then
  1049.         SetTooltipSize(tooltip, tooltip.width, tooltip.height + height - line.height)
  1050.  
  1051.         line.height = height
  1052.         line:SetHeight(height)
  1053.     end
  1054.  
  1055.     if rightColNum < tooltipWidth then
  1056.         return lineNum, rightColNum + 1
  1057.     else
  1058.         return lineNum, nil
  1059.     end
  1060. end
  1061.  
  1062. do
  1063.     local function CreateLine(tooltip, font, ...)
  1064.         if #tooltip.columns == 0 then
  1065.             error("column layout should be defined before adding line", 3)
  1066.         end
  1067.  
  1068.         local lineNum = #tooltip.lines + 1
  1069.         local line = tooltip.lines[lineNum] or AcquireFrame(tooltip.scrollChild)
  1070.  
  1071.         line:SetFrameLevel(tooltip.scrollChild:GetFrameLevel() + 2)
  1072.         line:SetPoint("LEFT", tooltip.scrollChild)
  1073.         line:SetPoint("RIGHT", tooltip.scrollChild)
  1074.  
  1075.         if lineNum > 1 then
  1076.             local v_margin = tooltip.cell_margin_v or CELL_MARGIN_V
  1077.  
  1078.             line:SetPoint("TOP", tooltip.lines[lineNum - 1], "BOTTOM", 0, -v_margin)
  1079.             SetTooltipSize(tooltip, tooltip.width, tooltip.height + v_margin)
  1080.         else
  1081.             line:SetPoint("TOP", tooltip.scrollChild)
  1082.         end
  1083.  
  1084.         tooltip.lines[lineNum] = line
  1085.  
  1086.         line.cells = line.cells or AcquireTable()
  1087.         line.height = 0
  1088.         line:SetHeight(1)
  1089.         line:Show()
  1090.  
  1091.         local colNum = 1
  1092.  
  1093.         for i = 1, #tooltip.columns do
  1094.             local value = select(i, ...)
  1095.  
  1096.             if value ~= nil then
  1097.                 lineNum, colNum = _SetCell(tooltip, lineNum, i, value, font, nil, 1, tooltip.labelProvider)
  1098.             end
  1099.         end
  1100.  
  1101.         return lineNum, colNum
  1102.     end
  1103.  
  1104.     function tipPrototype:AddLine(...)
  1105.         return CreateLine(self, self.regularFont, ...)
  1106.     end
  1107.  
  1108.     function tipPrototype:AddHeader(...)
  1109.         local line, col = CreateLine(self, self.headerFont, ...)
  1110.  
  1111.         self.lines[line].is_header = true
  1112.  
  1113.         return line, col
  1114.     end
  1115. end -- do-block
  1116.  
  1117. local GenericBackdrop = {
  1118.     bgFile = "Interface\\Tooltips\\UI-Tooltip-Background"
  1119. }
  1120.  
  1121. function tipPrototype:AddSeparator(height, r, g, b, a)
  1122.     local lineNum, colNum = self:AddLine()
  1123.     local line = self.lines[lineNum]
  1124.     local color = _G.NORMAL_FONT_COLOR
  1125.  
  1126.     height = height or 1
  1127.  
  1128.     SetTooltipSize(self, self.width, self.height + height)
  1129.  
  1130.     line.height = height
  1131.     line:SetHeight(height)
  1132.     line:SetBackdrop(GenericBackdrop)
  1133.     line:SetBackdropColor(r or color.r, g or color.g, b or color.b, a or 1)
  1134.  
  1135.     return lineNum, colNum
  1136. end
  1137.  
  1138. function tipPrototype:SetCellColor(lineNum, colNum, r, g, b, a)
  1139.     local cell = self.lines[lineNum].cells[colNum]
  1140.  
  1141.     if cell then
  1142.         local sr, sg, sb, sa = self:GetBackdropColor()
  1143.  
  1144.         cell:SetBackdrop(GenericBackdrop)
  1145.         cell:SetBackdropColor(r or sr, g or sg, b or sb, a or sa)
  1146.     end
  1147. end
  1148.  
  1149. function tipPrototype:SetColumnColor(colNum, r, g, b, a)
  1150.     local column = self.columns[colNum]
  1151.  
  1152.     if column then
  1153.         local sr, sg, sb, sa = self:GetBackdropColor()
  1154.         column:SetBackdrop(GenericBackdrop)
  1155.         column:SetBackdropColor(r or sr, g or sg, b or sb, a or sa)
  1156.     end
  1157. end
  1158.  
  1159. function tipPrototype:SetLineColor(lineNum, r, g, b, a)
  1160.     local line = self.lines[lineNum]
  1161.  
  1162.     if line then
  1163.         local sr, sg, sb, sa = self:GetBackdropColor()
  1164.  
  1165.         line:SetBackdrop(GenericBackdrop)
  1166.         line:SetBackdropColor(r or sr, g or sg, b or sb, a or sa)
  1167.     end
  1168. end
  1169.  
  1170. function tipPrototype:SetCellTextColor(lineNum, colNum, r, g, b, a)
  1171.     local line = self.lines[lineNum]
  1172.     local column = self.columns[colNum]
  1173.  
  1174.     if not line or not column then
  1175.         return
  1176.     end
  1177.  
  1178.     local cell = self.lines[lineNum].cells[colNum]
  1179.  
  1180.     if cell then
  1181.         if not cell.fontString then
  1182.             error("cell's label provider did not assign a fontString field", 2)
  1183.         end
  1184.  
  1185.         if not cell.r then
  1186.             cell.r, cell.g, cell.b, cell.a = cell.fontString:GetTextColor()
  1187.         end
  1188.  
  1189.         cell.fontString:SetTextColor(r or cell.r, g or cell.g, b or cell.b, a or cell.a)
  1190.     end
  1191. end
  1192.  
  1193. function tipPrototype:SetColumnTextColor(colNum, r, g, b, a)
  1194.     if not self.columns[colNum] then
  1195.         return
  1196.     end
  1197.  
  1198.     for lineIndex = 1, #self.lines do
  1199.         self:SetCellTextColor(lineIndex, colNum, r, g, b, a)
  1200.     end
  1201. end
  1202.  
  1203. function tipPrototype:SetLineTextColor(lineNum, r, g, b, a)
  1204.     local line = self.lines[lineNum]
  1205.  
  1206.     if not line then
  1207.         return
  1208.     end
  1209.  
  1210.     for cellIndex = 1, #line.cells do
  1211.         self:SetCellTextColor(lineNum, line.cells[cellIndex]._column, r, g, b, a)
  1212.     end
  1213. end
  1214.  
  1215. function tipPrototype:SetHighlightTexture(...)
  1216.     return highlightTexture:SetTexture(...)
  1217. end
  1218.  
  1219. function tipPrototype:SetHighlightTexCoord(...)
  1220.     highlightTexture:SetTexCoord(...)
  1221. end
  1222.  
  1223. do
  1224.     local function checkFont(font, level, silent)
  1225.         local bad = false
  1226.  
  1227.         if not font then
  1228.             bad = true
  1229.         elseif type(font) == "string" then
  1230.             local ref = _G[font]
  1231.  
  1232.             if not ref or type(ref) ~= "table" or type(ref.IsObjectType) ~= "function" or not ref:IsObjectType("Font") then
  1233.                 bad = true
  1234.             end
  1235.         elseif type(font) ~= "table" or type(font.IsObjectType) ~= "function" or not font:IsObjectType("Font") then
  1236.             bad = true
  1237.         end
  1238.  
  1239.         if bad then
  1240.             if silent then
  1241.                 return false
  1242.             end
  1243.  
  1244.             error("font must be a Font instance or a string matching the name of a global Font instance, not: " .. tostring(font), level + 1)
  1245.         end
  1246.         return true
  1247.     end
  1248.  
  1249.     function tipPrototype:SetFont(font)
  1250.         local is_string = type(font) == "string"
  1251.  
  1252.         checkFont(font, 2)
  1253.         self.regularFont = is_string and _G[font] or font
  1254.     end
  1255.  
  1256.     function tipPrototype:SetHeaderFont(font)
  1257.         local is_string = type(font) == "string"
  1258.  
  1259.         checkFont(font, 2)
  1260.         self.headerFont = is_string and _G[font] or font
  1261.     end
  1262.  
  1263.     -- TODO: fixed argument positions / remove checks for performance?
  1264.     function tipPrototype:SetCell(lineNum, colNum, value, ...)
  1265.         -- Mandatory argument checking
  1266.         if type(lineNum) ~= "number" then
  1267.             error("line number must be a number, not: " .. tostring(lineNum), 2)
  1268.         elseif lineNum < 1 or lineNum > #self.lines then
  1269.             error("line number out of range: " .. tostring(lineNum), 2)
  1270.         elseif type(colNum) ~= "number" then
  1271.             error("column number must be a number, not: " .. tostring(colNum), 2)
  1272.         elseif colNum < 1 or colNum > #self.columns then
  1273.             error("column number out of range: " .. tostring(colNum), 2)
  1274.         end
  1275.  
  1276.         -- Variable argument checking
  1277.         local font, justification, colSpan, provider
  1278.         local i, arg = 1, ...
  1279.  
  1280.         if arg == nil or checkFont(arg, 2, true) then
  1281.             i, font, arg = 2, ...
  1282.         end
  1283.  
  1284.         if arg == nil or checkJustification(arg, 2, true) then
  1285.             i, justification, arg = i + 1, select(i, ...)
  1286.         end
  1287.  
  1288.         if arg == nil or type(arg) == "number" then
  1289.             i, colSpan, arg = i + 1, select(i, ...)
  1290.         end
  1291.  
  1292.         if arg == nil or type(arg) == "table" and type(arg.AcquireCell) == "function" then
  1293.             i, provider = i + 1, arg
  1294.         end
  1295.  
  1296.         return _SetCell(self, lineNum, colNum, value, font, justification, colSpan, provider, select(i, ...))
  1297.     end
  1298. end -- do-block
  1299.  
  1300. function tipPrototype:GetFont()
  1301.     return self.regularFont
  1302. end
  1303.  
  1304. function tipPrototype:GetHeaderFont()
  1305.     return self.headerFont
  1306. end
  1307.  
  1308. function tipPrototype:GetLineCount()
  1309.     return #self.lines
  1310. end
  1311.  
  1312. function tipPrototype:GetColumnCount()
  1313.     return #self.columns
  1314. end
  1315.  
  1316. ------------------------------------------------------------------------------
  1317. -- Frame Scripts
  1318. ------------------------------------------------------------------------------
  1319. local scripts = {
  1320.     OnEnter = function(frame, ...)
  1321.         highlightFrame:SetParent(frame)
  1322.         highlightFrame:SetAllPoints(frame)
  1323.         highlightFrame:Show()
  1324.  
  1325.         if frame._OnEnter_func then
  1326.             frame:_OnEnter_func(frame._OnEnter_arg, ...)
  1327.         end
  1328.     end,
  1329.     OnLeave = function(frame, ...)
  1330.         highlightFrame:Hide()
  1331.         highlightFrame:ClearAllPoints()
  1332.         highlightFrame:SetParent(nil)
  1333.  
  1334.         if frame._OnLeave_func then
  1335.             frame:_OnLeave_func(frame._OnLeave_arg, ...)
  1336.         end
  1337.     end,
  1338.     OnMouseDown = function(frame, ...)
  1339.         frame:_OnMouseDown_func(frame._OnMouseDown_arg, ...)
  1340.     end,
  1341.     OnMouseUp = function(frame, ...)
  1342.         frame:_OnMouseUp_func(frame._OnMouseUp_arg, ...)
  1343.     end,
  1344.     OnReceiveDrag = function(frame, ...)
  1345.         frame:_OnReceiveDrag_func(frame._OnReceiveDrag_arg, ...)
  1346.     end
  1347. }
  1348.  
  1349. function SetFrameScript(frame, script, func, arg)
  1350.     if not scripts[script] then
  1351.         return
  1352.     end
  1353.  
  1354.     frame["_" .. script .. "_func"] = func
  1355.     frame["_" .. script .. "_arg"] = arg
  1356.  
  1357.     if script == "OnMouseDown" or script == "OnMouseUp" or script == "OnReceiveDrag" then
  1358.         if func then
  1359.             frame:SetScript(script, scripts[script])
  1360.         else
  1361.             frame:SetScript(script, nil)
  1362.         end
  1363.     end
  1364.  
  1365.     -- if at least one script is set, set the OnEnter/OnLeave scripts for the highlight
  1366.     if frame._OnEnter_func or frame._OnLeave_func or frame._OnMouseDown_func or frame._OnMouseUp_func or frame._OnReceiveDrag_func then
  1367.         frame:EnableMouse(true)
  1368.         frame:SetScript("OnEnter", scripts.OnEnter)
  1369.         frame:SetScript("OnLeave", scripts.OnLeave)
  1370.     else
  1371.         frame:EnableMouse(false)
  1372.         frame:SetScript("OnEnter", nil)
  1373.         frame:SetScript("OnLeave", nil)
  1374.     end
  1375. end
  1376.  
  1377. function ClearFrameScripts(frame)
  1378.     if frame._OnEnter_func or frame._OnLeave_func or frame._OnMouseDown_func or frame._OnMouseUp_func or frame._OnReceiveDrag_func then
  1379.         frame:EnableMouse(false)
  1380.  
  1381.         frame:SetScript("OnEnter", nil)
  1382.         frame._OnEnter_func = nil
  1383.         frame._OnEnter_arg = nil
  1384.  
  1385.         frame:SetScript("OnLeave", nil)
  1386.         frame._OnLeave_func = nil
  1387.         frame._OnLeave_arg = nil
  1388.  
  1389.         frame:SetScript("OnReceiveDrag", nil)
  1390.         frame._OnReceiveDrag_func = nil
  1391.         frame._OnReceiveDrag_arg = nil
  1392.  
  1393.         frame:SetScript("OnMouseDown", nil)
  1394.         frame._OnMouseDown_func = nil
  1395.         frame._OnMouseDown_arg = nil
  1396.  
  1397.         frame:SetScript("OnMouseUp", nil)
  1398.         frame._OnMouseUp_func = nil
  1399.         frame._OnMouseUp_arg = nil
  1400.     end
  1401. end
  1402.  
  1403. function tipPrototype:SetLineScript(lineNum, script, func, arg)
  1404.     SetFrameScript(self.lines[lineNum], script, func, arg)
  1405. end
  1406.  
  1407. function tipPrototype:SetColumnScript(colNum, script, func, arg)
  1408.     SetFrameScript(self.columns[colNum], script, func, arg)
  1409. end
  1410.  
  1411. function tipPrototype:SetCellScript(lineNum, colNum, script, func, arg)
  1412.     local cell = self.lines[lineNum].cells[colNum]
  1413.  
  1414.     if cell then
  1415.         SetFrameScript(cell, script, func, arg)
  1416.     end
  1417. end
  1418.  
  1419. ------------------------------------------------------------------------------
  1420. -- Auto-hiding feature
  1421. ------------------------------------------------------------------------------
  1422.  
  1423. -- Script of the auto-hiding child frame
  1424. local function AutoHideTimerFrame_OnUpdate(self, elapsed)
  1425.     self.checkElapsed = self.checkElapsed + elapsed
  1426.  
  1427.     if self.checkElapsed > 0.1 then
  1428.         if self.parent:IsMouseOver() or (self.alternateFrame and self.alternateFrame:IsMouseOver()) then
  1429.             self.elapsed = 0
  1430.         else
  1431.             self.elapsed = self.elapsed + self.checkElapsed
  1432.  
  1433.             if self.elapsed >= self.delay then
  1434.                 lib:Release(self.parent)
  1435.             end
  1436.         end
  1437.  
  1438.         self.checkElapsed = 0
  1439.     end
  1440. end
  1441.  
  1442. -- Usage:
  1443. -- :SetAutoHideDelay(0.25) => hides after 0.25sec outside of the tooltip
  1444. -- :SetAutoHideDelay(0.25, someFrame) => hides after 0.25sec outside of both the tooltip and someFrame
  1445. -- :SetAutoHideDelay() => disable auto-hiding (default)
  1446. function tipPrototype:SetAutoHideDelay(delay, alternateFrame, releaseHandler)
  1447.     local timerFrame = self.autoHideTimerFrame
  1448.     delay = tonumber(delay) or 0
  1449.  
  1450.     if releaseHandler then
  1451.         if type(releaseHandler) ~= "function" then
  1452.             error("releaseHandler must be a function", 2)
  1453.         end
  1454.  
  1455.         lib.onReleaseHandlers[self] = releaseHandler
  1456.     end
  1457.  
  1458.     if delay > 0 then
  1459.         if not timerFrame then
  1460.             timerFrame = AcquireFrame(self)
  1461.             timerFrame:SetScript("OnUpdate", AutoHideTimerFrame_OnUpdate)
  1462.  
  1463.             self.autoHideTimerFrame = timerFrame
  1464.         end
  1465.  
  1466.         timerFrame.parent = self
  1467.         timerFrame.checkElapsed = 0
  1468.         timerFrame.elapsed = 0
  1469.         timerFrame.delay = delay
  1470.         timerFrame.alternateFrame = alternateFrame
  1471.         timerFrame:Show()
  1472.     elseif timerFrame then
  1473.         self.autoHideTimerFrame = nil
  1474.  
  1475.         timerFrame.alternateFrame = nil
  1476.         timerFrame:SetScript("OnUpdate", nil)
  1477.  
  1478.         ReleaseFrame(timerFrame)
  1479.     end
  1480. end
  1481.  
  1482. ------------------------------------------------------------------------------
  1483. -- "Smart" Anchoring
  1484. ------------------------------------------------------------------------------
  1485. local function GetTipAnchor(frame)
  1486.     local x, y = frame:GetCenter()
  1487.  
  1488.     if not x or not y then
  1489.         return "TOPLEFT", "BOTTOMLEFT"
  1490.     end
  1491.  
  1492.     local hhalf = (x > UIParent:GetWidth() * 2 / 3) and "RIGHT" or (x < UIParent:GetWidth() / 3) and "LEFT" or ""
  1493.     local vhalf = (y > UIParent:GetHeight() / 2) and "TOP" or "BOTTOM"
  1494.  
  1495.     return vhalf .. hhalf, frame, (vhalf == "TOP" and "BOTTOM" or "TOP") .. hhalf
  1496. end
  1497.  
  1498. function tipPrototype:SmartAnchorTo(frame)
  1499.     if not frame then
  1500.         error("Invalid frame provided.", 2)
  1501.     end
  1502.  
  1503.     self:ClearAllPoints()
  1504.     self:SetClampedToScreen(true)
  1505.     self:SetPoint(GetTipAnchor(frame))
  1506. end
  1507.  
  1508. ------------------------------------------------------------------------------
  1509. -- Debug slashcmds
  1510. ------------------------------------------------------------------------------
  1511. -- @debug @
  1512. local print = print
  1513. local function PrintStats()
  1514.     local tipCache = tostring(#tooltipHeap)
  1515.     local frameCache = tostring(#frameHeap)
  1516.     local tableCache = tostring(#tableHeap)
  1517.     local header = false
  1518.  
  1519.     print("Tooltips used: " .. usedTooltips .. ", Cached: " .. tipCache .. ", Total: " .. tipCache + usedTooltips)
  1520.     print("Frames used: " .. usedFrames .. ", Cached: " .. frameCache .. ", Total: " .. frameCache + usedFrames)
  1521.     print("Tables used: " .. usedTables .. ", Cached: " .. tableCache .. ", Total: " .. tableCache + usedTables)
  1522.  
  1523.     for k in pairs(activeTooltips) do
  1524.         if not header then
  1525.             print("Active tooltips:")
  1526.             header = true
  1527.         end
  1528.         print("- " .. k)
  1529.     end
  1530. end
  1531.  
  1532. SLASH_LibQTip1 = "/qtip"
  1533. _G.SlashCmdList["LibQTip"] = PrintStats
  1534. --@end-debug@
  1535.  
Advertisement
Add Comment
Please, Sign In to add comment