Guest User

LibQTip-1.0.lua for SL (17.08.2020)

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