· 5 years ago · Oct 15, 2020, 04:40 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")}
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)
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")
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
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@]===]
194end
195
196------------------------------------------------------------------------------
197-- Dirty layout handler
198------------------------------------------------------------------------------
199lib.layoutCleaner = lib.layoutCleaner or CreateFrame("Frame")
200
201local layoutCleaner = lib.layoutCleaner
202layoutCleaner.registry = layoutCleaner.registry or {}
203
204function layoutCleaner:RegisterForCleanup(tooltip)
205 self.registry[tooltip] = true
206 self:Show()
207end
208
209function layoutCleaner:CleanupLayouts()
210 self:Hide()
211
212 for tooltip in pairs(self.registry) do
213 FixCellSizes(tooltip)
214 end
215
216 wipe(self.registry)
217end
218
219layoutCleaner:SetScript("OnUpdate", layoutCleaner.CleanupLayouts)
220
221------------------------------------------------------------------------------
222-- CellProvider and Cell
223------------------------------------------------------------------------------
224function providerPrototype:AcquireCell()
225 local cell = tremove(self.heap)
226
227 if not cell then
228 cell = setmetatable(CreateFrame("Frame", nil, UIParent, "BackdropTemplate"), self.cellMetatable)
229
230 if type(cell.InitializeCell) == "function" then
231 cell:InitializeCell()
232 end
233 end
234
235 self.cells[cell] = true
236
237 return cell
238end
239
240function 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)
251end
252
253function providerPrototype:GetCellPrototype()
254 return self.cellPrototype, self.cellMetatable
255end
256
257function providerPrototype:IterateCells()
258 return pairs(self.cells)
259end
260
261function 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
279end
280
281------------------------------------------------------------------------------
282-- Basic label provider
283------------------------------------------------------------------------------
284if not lib.LabelProvider then
285 lib.LabelProvider, lib.LabelPrototype = lib:CreateCellProvider()
286end
287
288local labelProvider = lib.LabelProvider
289local labelPrototype = lib.LabelPrototype
290
291function labelPrototype:InitializeCell()
292 self.fontString = self:CreateFontString()
293 self.fontString:SetFontObject(_G.GameTooltipText)
294end
295
296function 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
341end
342
343function 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
351end
352
353function labelPrototype:GetPosition()
354 return self._line, self._column
355end
356
357------------------------------------------------------------------------------
358-- Tooltip cache
359------------------------------------------------------------------------------
360local tooltipHeap = lib.tooltipHeap
361
362-- Returns a tooltip
363function AcquireTooltip()
364 local tooltip = tremove(tooltipHeap)
365
366 if not tooltip then
367 tooltip = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
368
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
387end
388
389-- Cleans the tooltip and stores it in the cache
390function 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@]===]
451end
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
457function 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
465end
466
467-- Cleans the cell hands it to its provider for storing
468function 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
488end
489
490------------------------------------------------------------------------------
491-- Table cache
492------------------------------------------------------------------------------
493local tableHeap = lib.tableHeap
494
495-- Returns a table
496function AcquireTable()
497 local tbl = tremove(tableHeap) or {}
498 --[===[@debug@
499 usedTables = usedTables + 1
500 --@end-debug@]===]
501 return tbl
502end
503
504-- Cleans the table and stores it in the cache
505function ReleaseTable(tableInstance)
506 wipe(tableInstance)
507 tinsert(tableHeap, tableInstance)
508 --[===[@debug@
509 usedTables = usedTables - 1
510 --@end-debug@]===]
511end
512
513------------------------------------------------------------------------------
514-- Tooltip prototype
515------------------------------------------------------------------------------
516function 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)
558end
559
560function tipPrototype:SetDefaultProvider(myProvider)
561 if not myProvider then
562 return
563 end
564
565 self.labelProvider = myProvider
566end
567
568function tipPrototype:GetDefaultProvider()
569 return self.labelProvider
570end
571
572local 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
581end
582
583function 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
599end
600
601function 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
628end
629
630------------------------------------------------------------------------------
631-- Convenient methods
632------------------------------------------------------------------------------
633function tipPrototype:Release()
634 lib:Release(self)
635end
636
637function tipPrototype:IsAcquiredBy(key)
638 return key ~= nil and self.key == key
639end
640
641------------------------------------------------------------------------------
642-- Script hooks
643------------------------------------------------------------------------------
644local RawSetScript = lib.frameMetatable.__index.SetScript
645
646function 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
654end
655
656function 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
668end
669
670-- That might break some addons ; those addons were breaking other
671-- addons' tooltip though.
672function tipPrototype:HookScript()
673 geterrorhandler()(":HookScript is not allowed on LibQTip tooltips")
674end
675
676------------------------------------------------------------------------------
677-- Scrollbar data and functions
678------------------------------------------------------------------------------
679local 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
693local function slider_OnValueChanged(self)
694 self.scrollFrame:SetVerticalScroll(self:GetValue())
695end
696
697local 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
708end
709
710-- Set the step size for the scroll bar
711function tipPrototype:SetScrollStep(step)
712 self.step = step
713end
714
715-- will resize the tooltip to fit the screen and show a scrollbar if needed
716function 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
778end
779
780------------------------------------------------------------------------------
781-- Tooltip methods for changing its contents.
782------------------------------------------------------------------------------
783function 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)
812end
813
814function 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
824end
825
826function 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
836end
837
838function 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)
847end
848
849-- Add 2 pixels to height so dangling letters (g, y, p, j, etc) are not clipped.
850function 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)
854end
855
856local 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
863end
864
865local function ResizeLine(tooltip, line, height)
866 SetTooltipSize(tooltip, tooltip.width, tooltip.height + height - line.height)
867
868 line.height = height
869 line:SetHeight(height)
870end
871
872function 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-- ExecAssist Adjustment: EA needs the remaining width dumped into the last cell of the col-span for esthetic reasons
899-- As https://www.wowace.com/projects/libqtip-1-0/issues/35 has not yet been addressed or and the max-cell-size parameter
900-- not being obeyed (like it should) two adjustments needs to be made:
901-- REMed width = width / (right - left + 1)
902-- This forces maxNeedWidthPerCol=width rather a percentage
903-- Later, instead of spreading 100% of the needed width to all spanned cells (left, right) it's dumped into the right-most cell
904-- (right, right). It's done like to create the least impact on the existing lib code
905 if width > maxNeedWidthPerCol then -- NB, maxNeedWidthPerCol now = width
906 maxNeedCols = colRange
907 maxNeedWidthPerCol = width
908 end
909 end
910 end
911
912 -- resize all columns for that colspan
913 if maxNeedCols then
914 local left, right = maxNeedCols:match("^(%d+)%-(%d+)$")
915
916 for col = right, right do -- -- ExecAssist Adjustment: was left, right
917 EnlargeColumn(tooltip, columns[col], columns[col].width + maxNeedWidthPerCol)
918 end
919
920 colspans[maxNeedCols] = nil
921 end
922 end
923
924 --now that the cell width is set, recalculate the rows' height
925 for _, line in ipairs(lines) do
926 if #(line.cells) > 0 then
927 local lineheight = 0
928
929 for _, cell in pairs(line.cells) do
930 if cell then
931 lineheight = max(lineheight, cell:getContentHeight())
932 end
933 end
934
935 if lineheight > 0 then
936 ResizeLine(tooltip, line, lineheight)
937 end
938 end
939 end
940end
941
942local function _SetCell(tooltip, lineNum, colNum, value, font, justification, colSpan, provider, ...)
943 local line = tooltip.lines[lineNum]
944 local cells = line.cells
945
946 -- Unset: be quick
947 if value == nil then
948 local cell = cells[colNum]
949
950 if cell then
951 for i = colNum, colNum + cell._colSpan - 1 do
952 cells[i] = nil
953 end
954
955 ReleaseCell(cell)
956 end
957
958 return lineNum, colNum
959 end
960
961 font = font or (line.is_header and tooltip.headerFont or tooltip.regularFont)
962
963 -- Check previous cell
964 local cell
965 local prevCell = cells[colNum]
966
967 if prevCell then
968 -- There is a cell here
969 justification = justification or prevCell._justification
970 colSpan = colSpan or prevCell._colSpan
971
972 -- Clear the currently marked colspan
973 for i = colNum + 1, colNum + prevCell._colSpan - 1 do
974 cells[i] = nil
975 end
976
977 if provider == nil or prevCell._provider == provider then
978 -- Reuse existing cell
979 cell = prevCell
980 provider = cell._provider
981 else
982 -- A new cell is required
983 cells[colNum] = ReleaseCell(prevCell)
984 end
985 elseif prevCell == nil then
986 -- Creating a new cell, using meaningful defaults.
987 provider = provider or tooltip.labelProvider
988 justification = justification or tooltip.columns[colNum].justification or "LEFT"
989 colSpan = colSpan or 1
990 else
991 error("overlapping cells at column " .. colNum, 3)
992 end
993
994 local tooltipWidth = #tooltip.columns
995 local rightColNum
996
997 if colSpan > 0 then
998 rightColNum = colNum + colSpan - 1
999
1000 if rightColNum > tooltipWidth then
1001 error("ColSpan too big, cell extends beyond right-most column", 3)
1002 end
1003 else
1004 -- Zero or negative: count back from right-most columns
1005 rightColNum = max(colNum, tooltipWidth + colSpan)
1006 -- Update colspan to its effective value
1007 colSpan = 1 + rightColNum - colNum
1008 end
1009
1010 -- Cleanup colspans
1011 for i = colNum + 1, rightColNum do
1012 local columnCell = cells[i]
1013
1014 if columnCell then
1015 ReleaseCell(columnCell)
1016 elseif columnCell == false then
1017 error("overlapping cells at column " .. i, 3)
1018 end
1019
1020 cells[i] = false
1021 end
1022
1023 -- Create the cell
1024 if not cell then
1025 cell = AcquireCell(tooltip, provider)
1026 cells[colNum] = cell
1027 end
1028
1029 -- Anchor the cell
1030 cell:SetPoint("LEFT", tooltip.columns[colNum])
1031 cell:SetPoint("RIGHT", tooltip.columns[rightColNum])
1032 cell:SetPoint("TOP", line)
1033 cell:SetPoint("BOTTOM", line)
1034
1035 -- Store the cell settings directly into the cell
1036 -- That's a bit risky but is really cheap compared to other ways to do it
1037 cell._font, cell._justification, cell._colSpan, cell._line, cell._column = font, justification, colSpan, lineNum, colNum
1038
1039 -- Setup the cell content
1040 local width, height = cell:SetupCell(tooltip, value, justification, font, ...)
1041 cell:Show()
1042
1043 if colSpan > 1 then
1044 -- Postpone width changes until the tooltip is shown
1045 local colRange = colNum .. "-" .. rightColNum
1046
1047 tooltip.colspans[colRange] = max(tooltip.colspans[colRange] or 0, width)
1048 layoutCleaner:RegisterForCleanup(tooltip)
1049 else
1050 -- Enlarge the column and tooltip if need be
1051 EnlargeColumn(tooltip, tooltip.columns[colNum], width)
1052 end
1053
1054 -- Enlarge the line and tooltip if need be
1055 if height > line.height then
1056 SetTooltipSize(tooltip, tooltip.width, tooltip.height + height - line.height)
1057
1058 line.height = height
1059 line:SetHeight(height)
1060 end
1061
1062 if rightColNum < tooltipWidth then
1063 return lineNum, rightColNum + 1
1064 else
1065 return lineNum, nil
1066 end
1067end
1068
1069do
1070 local function CreateLine(tooltip, font, ...)
1071 if #tooltip.columns == 0 then
1072 error("column layout should be defined before adding line", 3)
1073 end
1074
1075 local lineNum = #tooltip.lines + 1
1076 local line = tooltip.lines[lineNum] or AcquireFrame(tooltip.scrollChild)
1077
1078 line:SetFrameLevel(tooltip.scrollChild:GetFrameLevel() + 2)
1079 line:SetPoint("LEFT", tooltip.scrollChild)
1080 line:SetPoint("RIGHT", tooltip.scrollChild)
1081
1082 if lineNum > 1 then
1083 local v_margin = tooltip.cell_margin_v or CELL_MARGIN_V
1084
1085 line:SetPoint("TOP", tooltip.lines[lineNum - 1], "BOTTOM", 0, -v_margin)
1086 SetTooltipSize(tooltip, tooltip.width, tooltip.height + v_margin)
1087 else
1088 line:SetPoint("TOP", tooltip.scrollChild)
1089 end
1090
1091 tooltip.lines[lineNum] = line
1092
1093 line.cells = line.cells or AcquireTable()
1094 line.height = 0
1095 line:SetHeight(1)
1096 line:Show()
1097
1098 local colNum = 1
1099
1100 for i = 1, #tooltip.columns do
1101 local value = select(i, ...)
1102
1103 if value ~= nil then
1104 lineNum, colNum = _SetCell(tooltip, lineNum, i, value, font, nil, 1, tooltip.labelProvider)
1105 end
1106 end
1107
1108 return lineNum, colNum
1109 end
1110
1111 function tipPrototype:AddLine(...)
1112 return CreateLine(self, self.regularFont, ...)
1113 end
1114
1115 function tipPrototype:AddHeader(...)
1116 local line, col = CreateLine(self, self.headerFont, ...)
1117
1118 self.lines[line].is_header = true
1119
1120 return line, col
1121 end
1122end -- do-block
1123
1124local GenericBackdrop = {
1125 bgFile = "Interface\\Tooltips\\UI-Tooltip-Background"
1126}
1127
1128function tipPrototype:AddSeparator(height, r, g, b, a)
1129 local lineNum, colNum = self:AddLine()
1130 local line = self.lines[lineNum]
1131 local color = _G.NORMAL_FONT_COLOR
1132
1133 height = height or 1
1134
1135 SetTooltipSize(self, self.width, self.height + height)
1136
1137 line.height = height
1138 line:SetHeight(height)
1139
1140 if not line.SetBackdrop then --**
1141 Mixin(line, BackdropTemplateMixin)
1142 end
1143
1144 line:SetBackdrop(GenericBackdrop)
1145 line:SetBackdropColor(r or color.r, g or color.g, b or color.b, a or 1)
1146
1147 return lineNum, colNum
1148end
1149
1150function tipPrototype:SetCellColor(lineNum, colNum, r, g, b, a)
1151 local cell = self.lines[lineNum].cells[colNum]
1152
1153 if not cell.SetBackdrop then --**
1154 Mixin(cell, BackdropTemplateMixin)
1155 end
1156
1157 if cell then
1158 local sr, sg, sb, sa = self:GetBackdropColor()
1159
1160 cell:SetBackdrop(GenericBackdrop)
1161 cell:SetBackdropColor(r or sr, g or sg, b or sb, a or sa)
1162 end
1163end
1164
1165function tipPrototype:SetColumnColor(colNum, r, g, b, a)
1166 local column = self.columns[colNum]
1167
1168 if not column.SetBackdrop then --**
1169 Mixin(column, BackdropTemplateMixin)
1170 end
1171
1172 if column then
1173 local sr, sg, sb, sa = self:GetBackdropColor()
1174 column:SetBackdrop(GenericBackdrop)
1175 column:SetBackdropColor(r or sr, g or sg, b or sb, a or sa)
1176 end
1177end
1178
1179function tipPrototype:SetLineColor(lineNum, r, g, b, a)
1180 local line = self.lines[lineNum]
1181
1182 if not line.SetBackdrop then
1183 Mixin(line, BackdropTemplateMixin)
1184 end
1185
1186 if line then
1187 local sr, sg, sb, sa = self:GetBackdropColor()
1188
1189 line:SetBackdrop(GenericBackdrop)
1190 line:SetBackdropColor(r or sr, g or sg, b or sb, a or sa)
1191 end
1192end
1193
1194function tipPrototype:SetCellTextColor(lineNum, colNum, r, g, b, a)
1195 local line = self.lines[lineNum]
1196 local column = self.columns[colNum]
1197
1198 if not line or not column then
1199 return
1200 end
1201
1202 local cell = self.lines[lineNum].cells[colNum]
1203
1204 if cell then
1205 if not cell.fontString then
1206 error("cell's label provider did not assign a fontString field", 2)
1207 end
1208
1209 if not cell.r then
1210 cell.r, cell.g, cell.b, cell.a = cell.fontString:GetTextColor()
1211 end
1212
1213 cell.fontString:SetTextColor(r or cell.r, g or cell.g, b or cell.b, a or cell.a)
1214 end
1215end
1216
1217function tipPrototype:SetColumnTextColor(colNum, r, g, b, a)
1218 if not self.columns[colNum] then
1219 return
1220 end
1221
1222 for lineIndex = 1, #self.lines do
1223 self:SetCellTextColor(lineIndex, colNum, r, g, b, a)
1224 end
1225end
1226
1227function tipPrototype:SetLineTextColor(lineNum, r, g, b, a)
1228 local line = self.lines[lineNum]
1229
1230 if not line then
1231 return
1232 end
1233
1234 for cellIndex = 1, #line.cells do
1235 self:SetCellTextColor(lineNum, line.cells[cellIndex]._column, r, g, b, a)
1236 end
1237end
1238
1239function tipPrototype:SetHighlightTexture(...)
1240 return highlightTexture:SetTexture(...)
1241end
1242
1243function tipPrototype:SetHighlightTexCoord(...)
1244 highlightTexture:SetTexCoord(...)
1245end
1246
1247do
1248 local function checkFont(font, level, silent)
1249 local bad = false
1250
1251 if not font then
1252 bad = true
1253 elseif type(font) == "string" then
1254 local ref = _G[font]
1255
1256 if not ref or type(ref) ~= "table" or type(ref.IsObjectType) ~= "function" or not ref:IsObjectType("Font") then
1257 bad = true
1258 end
1259 elseif type(font) ~= "table" or type(font.IsObjectType) ~= "function" or not font:IsObjectType("Font") then
1260 bad = true
1261 end
1262
1263 if bad then
1264 if silent then
1265 return false
1266 end
1267
1268 error("font must be a Font instance or a string matching the name of a global Font instance, not: " .. tostring(font), level + 1)
1269 end
1270 return true
1271 end
1272
1273 function tipPrototype:SetFont(font)
1274 local is_string = type(font) == "string"
1275
1276 checkFont(font, 2)
1277 self.regularFont = is_string and _G[font] or font
1278 end
1279
1280 function tipPrototype:SetHeaderFont(font)
1281 local is_string = type(font) == "string"
1282
1283 checkFont(font, 2)
1284 self.headerFont = is_string and _G[font] or font
1285 end
1286
1287 -- TODO: fixed argument positions / remove checks for performance?
1288 function tipPrototype:SetCell(lineNum, colNum, value, ...)
1289 -- Mandatory argument checking
1290 if type(lineNum) ~= "number" then
1291 error("line number must be a number, not: " .. tostring(lineNum), 2)
1292 elseif lineNum < 1 or lineNum > #self.lines then
1293 error("line number out of range: " .. tostring(lineNum), 2)
1294 elseif type(colNum) ~= "number" then
1295 error("column number must be a number, not: " .. tostring(colNum), 2)
1296 elseif colNum < 1 or colNum > #self.columns then
1297 error("column number out of range: " .. tostring(colNum), 2)
1298 end
1299
1300 -- Variable argument checking
1301 local font, justification, colSpan, provider
1302 local i, arg = 1, ...
1303
1304 if arg == nil or checkFont(arg, 2, true) then
1305 i, font, arg = 2, ...
1306 end
1307
1308 if arg == nil or checkJustification(arg, 2, true) then
1309 i, justification, arg = i + 1, select(i, ...)
1310 end
1311
1312 if arg == nil or type(arg) == "number" then
1313 i, colSpan, arg = i + 1, select(i, ...)
1314 end
1315
1316 if arg == nil or type(arg) == "table" and type(arg.AcquireCell) == "function" then
1317 i, provider = i + 1, arg
1318 end
1319
1320 return _SetCell(self, lineNum, colNum, value, font, justification, colSpan, provider, select(i, ...))
1321 end
1322end -- do-block
1323
1324function tipPrototype:GetFont()
1325 return self.regularFont
1326end
1327
1328function tipPrototype:GetHeaderFont()
1329 return self.headerFont
1330end
1331
1332function tipPrototype:GetLineCount()
1333 return #self.lines
1334end
1335
1336function tipPrototype:GetColumnCount()
1337 return #self.columns
1338end
1339
1340------------------------------------------------------------------------------
1341-- Frame Scripts
1342------------------------------------------------------------------------------
1343local scripts = {
1344 OnEnter = function(frame, ...)
1345 highlightFrame:SetParent(frame)
1346 highlightFrame:SetAllPoints(frame)
1347 highlightFrame:Show()
1348
1349 if frame._OnEnter_func then
1350 frame:_OnEnter_func(frame._OnEnter_arg, ...)
1351 end
1352 end,
1353 OnLeave = function(frame, ...)
1354 highlightFrame:Hide()
1355 highlightFrame:ClearAllPoints()
1356 highlightFrame:SetParent(nil)
1357
1358 if frame._OnLeave_func then
1359 frame:_OnLeave_func(frame._OnLeave_arg, ...)
1360 end
1361 end,
1362 OnMouseDown = function(frame, ...)
1363 frame:_OnMouseDown_func(frame._OnMouseDown_arg, ...)
1364 end,
1365 OnMouseUp = function(frame, ...)
1366 frame:_OnMouseUp_func(frame._OnMouseUp_arg, ...)
1367 end,
1368 OnReceiveDrag = function(frame, ...)
1369 frame:_OnReceiveDrag_func(frame._OnReceiveDrag_arg, ...)
1370 end
1371}
1372
1373function SetFrameScript(frame, script, func, arg)
1374 if not scripts[script] then
1375 return
1376 end
1377
1378 frame["_" .. script .. "_func"] = func
1379 frame["_" .. script .. "_arg"] = arg
1380
1381 if script == "OnMouseDown" or script == "OnMouseUp" or script == "OnReceiveDrag" then
1382 if func then
1383 frame:SetScript(script, scripts[script])
1384 else
1385 frame:SetScript(script, nil)
1386 end
1387 end
1388
1389 -- if at least one script is set, set the OnEnter/OnLeave scripts for the highlight
1390 if frame._OnEnter_func or frame._OnLeave_func or frame._OnMouseDown_func or frame._OnMouseUp_func or frame._OnReceiveDrag_func then
1391 frame:EnableMouse(true)
1392 frame:SetScript("OnEnter", scripts.OnEnter)
1393 frame:SetScript("OnLeave", scripts.OnLeave)
1394 else
1395 frame:EnableMouse(false)
1396 frame:SetScript("OnEnter", nil)
1397 frame:SetScript("OnLeave", nil)
1398 end
1399end
1400
1401function ClearFrameScripts(frame)
1402 if frame._OnEnter_func or frame._OnLeave_func or frame._OnMouseDown_func or frame._OnMouseUp_func or frame._OnReceiveDrag_func then
1403 frame:EnableMouse(false)
1404
1405 frame:SetScript("OnEnter", nil)
1406 frame._OnEnter_func = nil
1407 frame._OnEnter_arg = nil
1408
1409 frame:SetScript("OnLeave", nil)
1410 frame._OnLeave_func = nil
1411 frame._OnLeave_arg = nil
1412
1413 frame:SetScript("OnReceiveDrag", nil)
1414 frame._OnReceiveDrag_func = nil
1415 frame._OnReceiveDrag_arg = nil
1416
1417 frame:SetScript("OnMouseDown", nil)
1418 frame._OnMouseDown_func = nil
1419 frame._OnMouseDown_arg = nil
1420
1421 frame:SetScript("OnMouseUp", nil)
1422 frame._OnMouseUp_func = nil
1423 frame._OnMouseUp_arg = nil
1424 end
1425end
1426
1427function tipPrototype:SetLineScript(lineNum, script, func, arg)
1428 SetFrameScript(self.lines[lineNum], script, func, arg)
1429end
1430
1431function tipPrototype:SetColumnScript(colNum, script, func, arg)
1432 SetFrameScript(self.columns[colNum], script, func, arg)
1433end
1434
1435function tipPrototype:SetCellScript(lineNum, colNum, script, func, arg)
1436 local cell = self.lines[lineNum].cells[colNum]
1437
1438 if cell then
1439 SetFrameScript(cell, script, func, arg)
1440 end
1441end
1442
1443------------------------------------------------------------------------------
1444-- Auto-hiding feature
1445------------------------------------------------------------------------------
1446
1447-- Script of the auto-hiding child frame
1448local function AutoHideTimerFrame_OnUpdate(self, elapsed)
1449 self.checkElapsed = self.checkElapsed + elapsed
1450
1451 if self.checkElapsed > 0.1 then
1452 if self.parent:IsMouseOver() or (self.alternateFrame and self.alternateFrame:IsMouseOver()) then
1453 self.elapsed = 0
1454 else
1455 self.elapsed = self.elapsed + self.checkElapsed
1456
1457 if self.elapsed >= self.delay then
1458 lib:Release(self.parent)
1459 end
1460 end
1461
1462 self.checkElapsed = 0
1463 end
1464end
1465
1466-- Usage:
1467-- :SetAutoHideDelay(0.25) => hides after 0.25sec outside of the tooltip
1468-- :SetAutoHideDelay(0.25, someFrame) => hides after 0.25sec outside of both the tooltip and someFrame
1469-- :SetAutoHideDelay() => disable auto-hiding (default)
1470function tipPrototype:SetAutoHideDelay(delay, alternateFrame, releaseHandler)
1471 local timerFrame = self.autoHideTimerFrame
1472 delay = tonumber(delay) or 0
1473
1474 if releaseHandler then
1475 if type(releaseHandler) ~= "function" then
1476 error("releaseHandler must be a function", 2)
1477 end
1478
1479 lib.onReleaseHandlers[self] = releaseHandler
1480 end
1481
1482 if delay > 0 then
1483 if not timerFrame then
1484 timerFrame = AcquireFrame(self)
1485 timerFrame:SetScript("OnUpdate", AutoHideTimerFrame_OnUpdate)
1486
1487 self.autoHideTimerFrame = timerFrame
1488 end
1489
1490 timerFrame.parent = self
1491 timerFrame.checkElapsed = 0
1492 timerFrame.elapsed = 0
1493 timerFrame.delay = delay
1494 timerFrame.alternateFrame = alternateFrame
1495 timerFrame:Show()
1496 elseif timerFrame then
1497 self.autoHideTimerFrame = nil
1498
1499 timerFrame.alternateFrame = nil
1500 timerFrame:SetScript("OnUpdate", nil)
1501
1502 ReleaseFrame(timerFrame)
1503 end
1504end
1505
1506------------------------------------------------------------------------------
1507-- "Smart" Anchoring
1508------------------------------------------------------------------------------
1509local function GetTipAnchor(frame)
1510 local x, y = frame:GetCenter()
1511
1512 if not x or not y then
1513 return "TOPLEFT", "BOTTOMLEFT"
1514 end
1515
1516 local hhalf = (x > UIParent:GetWidth() * 2 / 3) and "RIGHT" or (x < UIParent:GetWidth() / 3) and "LEFT" or ""
1517 local vhalf = (y > UIParent:GetHeight() / 2) and "TOP" or "BOTTOM"
1518
1519 return vhalf .. hhalf, frame, (vhalf == "TOP" and "BOTTOM" or "TOP") .. hhalf
1520end
1521
1522function tipPrototype:SmartAnchorTo(frame)
1523 if not frame then
1524 error("Invalid frame provided.", 2)
1525 end
1526
1527 self:ClearAllPoints()
1528 self:SetClampedToScreen(true)
1529 self:SetPoint(GetTipAnchor(frame))
1530end
1531
1532------------------------------------------------------------------------------
1533-- Debug slashcmds
1534------------------------------------------------------------------------------
1535-- @debug @
1536local print = print
1537local function PrintStats()
1538 local tipCache = tostring(#tooltipHeap)
1539 local frameCache = tostring(#frameHeap)
1540 local tableCache = tostring(#tableHeap)
1541 local header = false
1542
1543 print("Tooltips used: " .. usedTooltips .. ", Cached: " .. tipCache .. ", Total: " .. tipCache + usedTooltips)
1544 print("Frames used: " .. usedFrames .. ", Cached: " .. frameCache .. ", Total: " .. frameCache + usedFrames)
1545 print("Tables used: " .. usedTables .. ", Cached: " .. tableCache .. ", Total: " .. tableCache + usedTables)
1546
1547 for k in pairs(activeTooltips) do
1548 if not header then
1549 print("Active tooltips:")
1550 header = true
1551 end
1552 print("- " .. k)
1553 end
1554end
1555
1556SLASH_LibQTip1 = "/qtip"
1557_G.SlashCmdList["LibQTip"] = PrintStats
1558--@end-debug@
1559