· 5 years ago · Oct 17, 2020, 05:20 PM
1local MAJOR = "LibQTip-1.0.7.3.5.3ExecAssistMod"
2local MINOR = 46 -- Should be manually increased
3local LibStub = _G.LibStub
4
5assert(LibStub, MAJOR .. " requires LibStub")
6
7local lib, oldMinor = LibStub:NewLibrary(MAJOR, MINOR)
8
9if not lib then
10 return
11end -- No upgrade needed
12
13------------------------------------------------------------------------------
14-- Upvalued globals
15------------------------------------------------------------------------------
16local table = _G.table
17local tinsert = table.insert
18local tremove = table.remove
19local wipe = table.wipe
20
21local error = error
22local math = math
23local min, max = math.min, math.max
24local next = next
25local pairs, ipairs = pairs, ipairs
26local select = select
27local setmetatable = setmetatable
28local tonumber, tostring = tonumber, tostring
29local type = type
30
31local CreateFrame = _G.CreateFrame
32local GameTooltip = _G.GameTooltip
33local UIParent = _G.UIParent
34
35local geterrorhandler = _G.geterrorhandler
36
37------------------------------------------------------------------------------
38-- Tables and locals
39------------------------------------------------------------------------------
40lib.frameMetatable = lib.frameMetatable or {__index = CreateFrame("Frame", nil, nil, "BackdropTemplate")}
41
42lib.tipPrototype = lib.tipPrototype or setmetatable({}, lib.frameMetatable)
43lib.tipMetatable = lib.tipMetatable or {__index = lib.tipPrototype}
44
45lib.providerPrototype = lib.providerPrototype or {}
46lib.providerMetatable = lib.providerMetatable or {__index = lib.providerPrototype}
47
48lib.cellPrototype = lib.cellPrototype or setmetatable({}, lib.frameMetatable)
49lib.cellMetatable = lib.cellMetatable or {__index = lib.cellPrototype}
50
51lib.activeTooltips = lib.activeTooltips or {}
52
53lib.tooltipHeap = lib.tooltipHeap or {}
54lib.frameHeap = lib.frameHeap or {}
55lib.tableHeap = lib.tableHeap or {}
56
57lib.onReleaseHandlers = lib.onReleaseHandlers or {}
58
59local tipPrototype = lib.tipPrototype
60local tipMetatable = lib.tipMetatable
61
62local providerPrototype = lib.providerPrototype
63local providerMetatable = lib.providerMetatable
64
65local cellPrototype = lib.cellPrototype
66local cellMetatable = lib.cellMetatable
67
68local activeTooltips = lib.activeTooltips
69
70local highlightFrame = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
71highlightFrame:SetFrameStrata("TOOLTIP")
72highlightFrame:Hide()
73
74local DEFAULT_HIGHLIGHT_TEXTURE_PATH = [[Interface\QuestFrame\UI-QuestTitleHighlight]]
75
76local highlightTexture = highlightFrame:CreateTexture(nil, "OVERLAY")
77highlightTexture:SetTexture(DEFAULT_HIGHLIGHT_TEXTURE_PATH)
78highlightTexture:SetBlendMode("ADD")
79highlightTexture:SetAllPoints(highlightFrame)
80
81------------------------------------------------------------------------------
82-- Private methods for Caches and Tooltip
83------------------------------------------------------------------------------
84local AcquireTooltip, ReleaseTooltip
85local AcquireCell, ReleaseCell
86local AcquireTable, ReleaseTable
87
88local InitializeTooltip, SetTooltipSize, ResetTooltipSize, FixCellSizes
89local ClearTooltipScripts
90local SetFrameScript, ClearFrameScripts
91
92------------------------------------------------------------------------------
93-- Cache debugging.
94------------------------------------------------------------------------------
95-- @debug @
96local usedTables, usedFrames, usedTooltips = 0, 0, 0
97--@end-debug@
98
99------------------------------------------------------------------------------
100-- Internal constants to tweak the layout
101------------------------------------------------------------------------------
102local TOOLTIP_PADDING = 10
103local CELL_MARGIN_H = 6
104local 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>
117function 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
140end
141
142function 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
151end
152
153function 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])
159end
160
161function lib:IterateTooltips()
162 return pairs(activeTooltips)
163end
164
165------------------------------------------------------------------------------
166-- Frame cache
167------------------------------------------------------------------------------
168local frameHeap = lib.frameHeap
169
170local 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
177end
178
179local 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@]===]
191end
192
193------------------------------------------------------------------------------
194-- Dirty layout handler
195------------------------------------------------------------------------------
196lib.layoutCleaner = lib.layoutCleaner or CreateFrame("Frame", nil, nil, "BackdropTemplate")
197
198local layoutCleaner = lib.layoutCleaner
199layoutCleaner.registry = layoutCleaner.registry or {}
200
201function layoutCleaner:RegisterForCleanup(tooltip)
202 self.registry[tooltip] = true
203 self:Show()
204end
205
206function layoutCleaner:CleanupLayouts()
207 self:Hide()
208
209 for tooltip in pairs(self.registry) do
210 FixCellSizes(tooltip)
211 end
212
213 wipe(self.registry)
214end
215
216layoutCleaner:SetScript("OnUpdate", layoutCleaner.CleanupLayouts)
217
218------------------------------------------------------------------------------
219-- CellProvider and Cell
220------------------------------------------------------------------------------
221function 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
235end
236
237function 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)
248end
249
250function providerPrototype:GetCellPrototype()
251 return self.cellPrototype, self.cellMetatable
252end
253
254function providerPrototype:IterateCells()
255 return pairs(self.cells)
256end
257
258function 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
276end
277
278------------------------------------------------------------------------------
279-- Basic label provider
280------------------------------------------------------------------------------
281if not lib.LabelProvider then
282 lib.LabelProvider, lib.LabelPrototype = lib:CreateCellProvider()
283end
284
285local labelProvider = lib.LabelProvider
286local labelPrototype = lib.LabelPrototype
287
288function labelPrototype:InitializeCell()
289 self.fontString = self:CreateFontString()
290 self.fontString:SetFontObject(_G.GameTooltipText)
291end
292
293function 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
338end
339
340function 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
348end
349
350function labelPrototype:GetPosition()
351 return self._line, self._column
352end
353
354------------------------------------------------------------------------------
355-- Tooltip cache
356------------------------------------------------------------------------------
357local tooltipHeap = lib.tooltipHeap
358
359-- Returns a tooltip
360function 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
384end
385
386-- Cleans the tooltip and stores it in the cache
387function 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@]===]
448end
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
454function 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
462end
463
464-- Cleans the cell hands it to its provider for storing
465function 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
485end
486
487------------------------------------------------------------------------------
488-- Table cache
489------------------------------------------------------------------------------
490local tableHeap = lib.tableHeap
491
492-- Returns a table
493function AcquireTable()
494 local tbl = tremove(tableHeap) or {}
495 --[===[@debug@
496 usedTables = usedTables + 1
497 --@end-debug@]===]
498 return tbl
499end
500
501-- Cleans the table and stores it in the cache
502function ReleaseTable(tableInstance)
503 wipe(tableInstance)
504 tinsert(tableHeap, tableInstance)
505 --[===[@debug@
506 usedTables = usedTables - 1
507 --@end-debug@]===]
508end
509
510------------------------------------------------------------------------------
511-- Tooltip prototype
512------------------------------------------------------------------------------
513function 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)
551end
552
553function tipPrototype:SetDefaultProvider(myProvider)
554 if not myProvider then
555 return
556 end
557
558 self.labelProvider = myProvider
559end
560
561function tipPrototype:GetDefaultProvider()
562 return self.labelProvider
563end
564
565local 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
574end
575
576function 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
592end
593
594function 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
621end
622
623------------------------------------------------------------------------------
624-- Convenient methods
625------------------------------------------------------------------------------
626function tipPrototype:Release()
627 lib:Release(self)
628end
629
630function tipPrototype:IsAcquiredBy(key)
631 return key ~= nil and self.key == key
632end
633
634------------------------------------------------------------------------------
635-- Script hooks
636------------------------------------------------------------------------------
637local RawSetScript = lib.frameMetatable.__index.SetScript
638
639function 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
647end
648
649function 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
661end
662
663-- That might break some addons ; those addons were breaking other
664-- addons' tooltip though.
665function tipPrototype:HookScript()
666 geterrorhandler()(":HookScript is not allowed on LibQTip tooltips")
667end
668
669------------------------------------------------------------------------------
670-- Scrollbar data and functions
671------------------------------------------------------------------------------
672local 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
686local function slider_OnValueChanged(self)
687 self.scrollFrame:SetVerticalScroll(self:GetValue())
688end
689
690local 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
701end
702
703-- Set the step size for the scroll bar
704function tipPrototype:SetScrollStep(step)
705 self.step = step
706end
707
708-- will resize the tooltip to fit the screen and show a scrollbar if needed
709function 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
771end
772
773------------------------------------------------------------------------------
774-- Tooltip methods for changing its contents.
775------------------------------------------------------------------------------
776function 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)
805end
806
807function 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
817end
818
819function 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
829end
830
831function 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)
840end
841
842-- Add 2 pixels to height so dangling letters (g, y, p, j, etc) are not clipped.
843function 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)
847end
848
849local 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
856end
857
858local function ResizeLine(tooltip, line, height)
859 SetTooltipSize(tooltip, tooltip.width, tooltip.height + height - line.height)
860
861 line.height = height
862 line:SetHeight(height)
863end
864
865function 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
933end
934
935local 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
1060end
1061
1062do
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
1115end -- do-block
1116
1117local GenericBackdrop = {
1118 bgFile = "Interface\\Tooltips\\UI-Tooltip-Background"
1119}
1120
1121function 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
1136end
1137
1138function 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
1147end
1148
1149function 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
1157end
1158
1159function 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
1168end
1169
1170function 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
1191end
1192
1193function 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
1201end
1202
1203function 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
1213end
1214
1215function tipPrototype:SetHighlightTexture(...)
1216 return highlightTexture:SetTexture(...)
1217end
1218
1219function tipPrototype:SetHighlightTexCoord(...)
1220 highlightTexture:SetTexCoord(...)
1221end
1222
1223do
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
1298end -- do-block
1299
1300function tipPrototype:GetFont()
1301 return self.regularFont
1302end
1303
1304function tipPrototype:GetHeaderFont()
1305 return self.headerFont
1306end
1307
1308function tipPrototype:GetLineCount()
1309 return #self.lines
1310end
1311
1312function tipPrototype:GetColumnCount()
1313 return #self.columns
1314end
1315
1316------------------------------------------------------------------------------
1317-- Frame Scripts
1318------------------------------------------------------------------------------
1319local 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
1349function 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
1375end
1376
1377function 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
1401end
1402
1403function tipPrototype:SetLineScript(lineNum, script, func, arg)
1404 SetFrameScript(self.lines[lineNum], script, func, arg)
1405end
1406
1407function tipPrototype:SetColumnScript(colNum, script, func, arg)
1408 SetFrameScript(self.columns[colNum], script, func, arg)
1409end
1410
1411function 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
1417end
1418
1419------------------------------------------------------------------------------
1420-- Auto-hiding feature
1421------------------------------------------------------------------------------
1422
1423-- Script of the auto-hiding child frame
1424local 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
1440end
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)
1446function 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
1480end
1481
1482------------------------------------------------------------------------------
1483-- "Smart" Anchoring
1484------------------------------------------------------------------------------
1485local 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
1496end
1497
1498function 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))
1506end
1507
1508------------------------------------------------------------------------------
1509-- Debug slashcmds
1510------------------------------------------------------------------------------
1511-- @debug @
1512local print = print
1513local 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
1530end
1531
1532SLASH_LibQTip1 = "/qtip"
1533_G.SlashCmdList["LibQTip"] = PrintStats
1534--@end-debug@
1535