/Tukui/modules/tooltip/tooltip.lua

http://github.com/Asphyxia/Tukui · Lua · 442 lines · 367 code · 55 blank · 20 comment · 153 complexity · 2049b1d97a60a672eb2bafe232c6125f MD5 · raw file

  1. local T, C, L = unpack(select(2, ...)) -- Import: T - functions, constants, variables; C - config; L - locales
  2. -- credits : Aezay (TipTac) and Caellian for some parts of code.
  3. if not C["tooltip"].enable then return end
  4. local TukuiTooltip = CreateFrame("Frame", "TukuiTooltip", UIParent)
  5. local _G = getfenv(0)
  6. local GameTooltip, GameTooltipStatusBar = _G["GameTooltip"], _G["GameTooltipStatusBar"]
  7. local gsub, find, format = string.gsub, string.find, string.format
  8. local Tooltips = {GameTooltip,ShoppingTooltip1,ShoppingTooltip2,ShoppingTooltip3,WorldMapTooltip,WorldMapCompareTooltip1,WorldMapCompareTooltip2,WorldMapCompareTooltip3}
  9. local ItemRefTooltip = ItemRefTooltip
  10. local linkTypes = {item = true, enchant = true, spell = true, quest = true, unit = true, talent = true, achievement = true, glyph = true}
  11. local classification = {
  12. worldboss = "|cffAF5050Boss|r",
  13. rareelite = "|cffAF5050+ Rare|r",
  14. elite = "|cffAF5050+|r",
  15. rare = "|cffAF5050Rare|r",
  16. }
  17. local NeedBackdropBorderRefresh = true
  18. local anchor = CreateFrame("Frame", "TukuiTooltipAnchor", UIParent)
  19. anchor:SetSize(200, TukuiInfoRight:GetHeight())
  20. anchor:SetFrameStrata("TOOLTIP")
  21. anchor:SetFrameLevel(20)
  22. anchor:SetClampedToScreen(true)
  23. anchor:SetAlpha(0)
  24. if C.chat.background and TukuiChatBackgroundRight then
  25. anchor:SetPoint("BOTTOMRIGHT", TukuiChatBackgroundRight, "TOPRIGHT", 0, 5)
  26. else
  27. anchor:SetPoint("BOTTOMRIGHT", TukuiInfoRight)
  28. end
  29. anchor:SetTemplate("Default")
  30. anchor:SetBackdropBorderColor(C.media.backdropcolor)
  31. anchor:SetMovable(true)
  32. anchor.text = T.SetFontString(anchor, C.media.pixelfont, C["datatext"].fontsize, "MONOCHROMEOUTLINE")
  33. anchor.text:SetPoint("CENTER")
  34. anchor.text:SetText(L.move_tooltip)
  35. -- Update Tukui Tooltip Position on some specifics Tooltip
  36. -- Also used because on Eyefinity, SetClampedToScreen doesn't work on left and right side of screen #1
  37. local function UpdateTooltip(self)
  38. local owner = self:GetOwner()
  39. if not owner then return end
  40. local name = owner:GetName()
  41. -- fix X-offset or Y-offset
  42. local x = T.Scale(5)
  43. -- mouseover
  44. if self:GetAnchorType() == "ANCHOR_CURSOR" then
  45. -- h4x for world object tooltip border showing last border color
  46. -- or showing background sometime ~blue :x
  47. if NeedBackdropBorderRefresh then
  48. self:ClearAllPoints()
  49. NeedBackdropBorderRefresh = false
  50. self:SetBackdropColor(unpack(C.media.backdropcolor))
  51. if not C["tooltip"].cursor then
  52. self:SetBackdropBorderColor(unpack(C.media.bordercolor))
  53. end
  54. end
  55. elseif self:GetAnchorType() == "ANCHOR_NONE" and InCombatLockdown() and C["tooltip"].hidecombat == true then
  56. self:Hide()
  57. end
  58. if name and (TukuiPlayerBuffs or TukuiPlayerDebuffs) then
  59. if (TukuiPlayerBuffs:GetPoint():match("LEFT") or TukuiPlayerDebuffs:GetPoint():match("LEFT")) and (name:match("TukuiPlayerBuffs") or name:match("TukuiPlayerDebuffs")) then
  60. self:SetAnchorType("ANCHOR_BOTTOMRIGHT", x, -x)
  61. end
  62. end
  63. if (owner == MiniMapBattlefieldFrame or owner == MiniMapMailFrame) and TukuiMinimap then
  64. if TukuiMinimap:GetPoint():match("LEFT") then
  65. self:SetAnchorType("ANCHOR_TOPRIGHT", x, -x)
  66. end
  67. end
  68. if self:GetAnchorType() == "ANCHOR_NONE" and TukuiTooltipAnchor then
  69. local point = TukuiTooltipAnchor:GetPoint()
  70. if point == "TOPLEFT" then
  71. self:ClearAllPoints()
  72. self:SetPoint("TOPLEFT", TukuiTooltipAnchor, "BOTTOMLEFT", 0, -x)
  73. elseif point == "TOP" then
  74. self:ClearAllPoints()
  75. self:SetPoint("TOP", TukuiTooltipAnchor, "BOTTOM", 0, -x)
  76. elseif point == "TOPRIGHT" then
  77. self:ClearAllPoints()
  78. self:SetPoint("TOPRIGHT", TukuiTooltipAnchor, "BOTTOMRIGHT", 0, -x)
  79. elseif point == "BOTTOMLEFT" or point == "LEFT" then
  80. self:ClearAllPoints()
  81. self:SetPoint("BOTTOMLEFT", TukuiTooltipAnchor, "TOPLEFT", 0, x)
  82. elseif point == "BOTTOMRIGHT" or point == "RIGHT" then
  83. if TukuiBags and TukuiBags:IsShown() then
  84. self:ClearAllPoints()
  85. self:SetPoint("BOTTOMRIGHT", TukuiBags, "TOPRIGHT", 0, x)
  86. else
  87. if not C["actionbar"].enable then
  88. if not C["chat"].background then
  89. self:ClearAllPoints()
  90. self:SetPoint("BOTTOMRIGHT", TukuiInfoRight, "TOPRIGHT", 0, x)
  91. else
  92. self:ClearAllPoints()
  93. self:SetPoint("BOTTOMRIGHT", TukuiChatBackgroundRight, "TOPRIGHT", 0, x)
  94. end
  95. else
  96. if not C["chat"].background then
  97. self:ClearAllPoints()
  98. self:SetPoint("BOTTOMRIGHT", TukuiInfoRight, "TOPRIGHT", 0, x)
  99. elseif HasPetUI() then
  100. if C["actionbar"].vertical_rightbars then
  101. self:ClearAllPoints()
  102. self:SetPoint("BOTTOMRIGHT", TukuiPetBar, "BOTTOMLEFT", -x, 0)
  103. else
  104. self:ClearAllPoints()
  105. self:SetPoint("BOTTOMRIGHT", TukuiPetBar, "TOPRIGHT", 0, x)
  106. end
  107. else
  108. if C["actionbar"].vertical_rightbars and TukuiSaved.rightbars > 0 then
  109. self:ClearAllPoints()
  110. self:SetPoint("BOTTOMRIGHT", TukuiRightBar, "BOTTOMLEFT", -x, 0)
  111. elseif not C["actionbar"].vertical_rightbars and TukuiSaved.rightbars > 0 then
  112. self:ClearAllPoints()
  113. self:SetPoint("BOTTOMRIGHT", TukuiRightBar, "TOPRIGHT", 0, x)
  114. else
  115. self:ClearAllPoints()
  116. self:SetPoint("BOTTOMRIGHT", TukuiChatBackgroundRight, "TOPRIGHT", 0, x)
  117. end
  118. end
  119. end
  120. end
  121. else
  122. self:ClearAllPoints()
  123. self:SetPoint("BOTTOM", TukuiTooltipAnchor, "TOP", 0, x)
  124. end
  125. end
  126. end
  127. hooksecurefunc("GameTooltip_SetDefaultAnchor", function(self, parent)
  128. if C["tooltip"].cursor == true then
  129. if IsAddOnLoaded("Tukui_Raid_Healing") and parent ~= UIParent then
  130. self:SetOwner(parent, "ANCHOR_NONE")
  131. else
  132. self:SetOwner(parent, "ANCHOR_CURSOR")
  133. end
  134. else
  135. self:SetOwner(parent, "ANCHOR_NONE")
  136. end
  137. self:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", -111111, -111111) -- hack to update GameStatusBar instantly.
  138. end)
  139. GameTooltip:HookScript("OnUpdate", function(self, ...) UpdateTooltip(self) end)
  140. local function Hex(color)
  141. return string.format('|cff%02x%02x%02x', color.r * 255, color.g * 255, color.b * 255)
  142. end
  143. local function GetColor(unit)
  144. if(UnitIsPlayer(unit) and not UnitHasVehicleUI(unit)) then
  145. local _, class = UnitClass(unit)
  146. local color = RAID_CLASS_COLORS[class]
  147. if not color then return end -- sometime unit too far away return nil for color :(
  148. local r,g,b = color.r, color.g, color.b
  149. return Hex(color), r, g, b
  150. else
  151. local color = FACTION_BAR_COLORS[UnitReaction(unit, "player")]
  152. if not color then return end -- sometime unit too far away return nil for color :(
  153. local r,g,b = color.r, color.g, color.b
  154. return Hex(color), r, g, b
  155. end
  156. end
  157. -- function to short-display HP value on StatusBar
  158. local function ShortValue(value)
  159. if value >= 1e7 then
  160. return ('%.1fm'):format(value / 1e6):gsub('%.?0+([km])$', '%1')
  161. elseif value >= 1e6 then
  162. return ('%.2fm'):format(value / 1e6):gsub('%.?0+([km])$', '%1')
  163. elseif value >= 1e5 then
  164. return ('%.0fk'):format(value / 1e3)
  165. elseif value >= 1e3 then
  166. return ('%.1fk'):format(value / 1e3):gsub('%.?0+([km])$', '%1')
  167. else
  168. return value
  169. end
  170. end
  171. -- update HP value on status bar
  172. GameTooltipStatusBar:SetScript("OnValueChanged", function(self, value)
  173. if not value then
  174. return
  175. end
  176. local min, max = self:GetMinMaxValues()
  177. if (value < min) or (value > max) then
  178. return
  179. end
  180. local _, unit = GameTooltip:GetUnit()
  181. -- fix target of target returning nil
  182. if (not unit) then
  183. local GMF = GetMouseFocus()
  184. unit = GMF and GMF:GetAttribute("unit")
  185. end
  186. if not self.text then
  187. self.text = self:CreateFontString(nil, "OVERLAY")
  188. local position = TukuiTooltipAnchor:GetPoint()
  189. if position:match("TOP") then
  190. self.text:Point("CENTER", GameTooltipStatusBar, 2, 2)
  191. else
  192. self.text:Point("CENTER", GameTooltipStatusBar, 2, 2)
  193. end
  194. self.text:SetFont(C.media.pixelfont, C["datatext"].fontsize, "MONOCHROMEOUTLINE")
  195. self.text:Show()
  196. if unit then
  197. min, max = UnitHealth(unit), UnitHealthMax(unit)
  198. local hp = ShortValue(min).." / "..ShortValue(max)
  199. if UnitIsGhost(unit) then
  200. self.text:SetText(L.unitframes_ouf_ghost)
  201. elseif min == 0 or UnitIsDead(unit) or UnitIsGhost(unit) then
  202. self.text:SetText(L.unitframes_ouf_dead)
  203. else
  204. self.text:SetText(hp)
  205. end
  206. end
  207. else
  208. if unit then
  209. min, max = UnitHealth(unit), UnitHealthMax(unit)
  210. self.text:Show()
  211. local hp = ShortValue(min).." / "..ShortValue(max)
  212. if UnitIsGhost(unit) then
  213. self.text:SetText(L.unitframes_ouf_ghost)
  214. elseif min == 0 or UnitIsDead(unit) or UnitIsGhost(unit) then
  215. self.text:SetText(L.unitframes_ouf_dead)
  216. else
  217. self.text:SetText(hp)
  218. end
  219. else
  220. self.text:Hide()
  221. end
  222. end
  223. end)
  224. local healthBar = GameTooltipStatusBar
  225. healthBar:ClearAllPoints()
  226. healthBar:Height(11)
  227. healthBar:Point("BOTTOMLEFT", healthBar:GetParent(), "TOPLEFT", 2, 5)
  228. healthBar:Point("BOTTOMRIGHT", healthBar:GetParent(), "TOPRIGHT", -2, 5)
  229. healthBar:SetStatusBarTexture(C.media.normTex)
  230. local healthBarBG = CreateFrame("Frame", "StatusBarBG", healthBar)
  231. healthBarBG:SetFrameLevel(healthBar:GetFrameLevel() - 1)
  232. healthBarBG:Point("TOPLEFT", -2, 2)
  233. healthBarBG:Point("BOTTOMRIGHT", 2, -2)
  234. healthBarBG:SetTemplate("Default")
  235. -- Add "Targeted By" line
  236. local targetedList = {}
  237. local ClassColors = {};
  238. local token
  239. for class, color in next, RAID_CLASS_COLORS do
  240. ClassColors[class] = ("|cff%.2x%.2x%.2x"):format(color.r*255,color.g*255,color.b*255);
  241. end
  242. GameTooltip:HookScript("OnTooltipSetUnit", function(self)
  243. local lines = self:NumLines()
  244. local GMF = GetMouseFocus()
  245. local unit = (select(2, self:GetUnit())) or (GMF and GMF:GetAttribute("unit"))
  246. -- A mage's mirror images sometimes doesn't return a unit, this would fix it
  247. if (not unit) and (UnitExists("mouseover")) then
  248. unit = "mouseover"
  249. end
  250. -- Sometimes when you move your mouse quicky over units in the worldframe, we can get here without a unit
  251. if not unit then self:Hide() return end
  252. -- for hiding tooltip on unitframes
  253. if (self:GetOwner() ~= UIParent and C["tooltip"].hideuf) then self:Hide() return end
  254. -- A "mouseover" unit is better to have as we can then safely say the tip should no longer show when it becomes invalid.
  255. if (UnitIsUnit(unit,"mouseover")) then
  256. unit = "mouseover"
  257. end
  258. local race = UnitRace(unit)
  259. local class = UnitClass(unit)
  260. local level = UnitLevel(unit)
  261. local guildName, guildRankName, guildRankIndex = GetGuildInfo(unit)
  262. local name, realm = UnitName(unit)
  263. local crtype = UnitCreatureType(unit)
  264. local classif = UnitClassification(unit)
  265. local title = UnitPVPName(unit)
  266. local r, g, b = GetQuestDifficultyColor(level).r, GetQuestDifficultyColor(level).g, GetQuestDifficultyColor(level).b
  267. local color = GetColor(unit)
  268. if not color then color = "|CFFFFFFFF" end -- just safe mode for when GetColor(unit) return nil for unit too far away
  269. _G["GameTooltipTextLeft1"]:SetFormattedText("%s%s%s", color, title or name, realm and realm ~= "" and " - "..realm.."|r" or "|r")
  270. if(UnitIsPlayer(unit)) then
  271. if UnitIsAFK(unit) then
  272. self:AppendText((" %s"):format(CHAT_FLAG_AFK))
  273. elseif UnitIsDND(unit) then
  274. self:AppendText((" %s"):format(CHAT_FLAG_DND))
  275. end
  276. local offset = 2
  277. if guildName then
  278. if UnitIsInMyGuild(unit) then
  279. _G["GameTooltipTextLeft2"]:SetText("<".."|cff0090ff"..guildName.."|r> [".."|cff00ff10"..guildRankName.."|r]") -- setting guildranks here
  280. else
  281. _G["GameTooltipTextLeft2"]:SetText("<|cff00ff10"..guildName.."|r> [|cff00ff10"..guildRankName.."|r]")
  282. end
  283. offset = offset + 1
  284. end
  285. for i= offset, lines do
  286. if(_G["GameTooltipTextLeft"..i]:GetText():find("^"..LEVEL)) then
  287. _G["GameTooltipTextLeft"..i]:SetFormattedText("|cff%02x%02x%02x%s|r %s %s%s", r*255, g*255, b*255, level > 0 and level or "??", race, color, class.."|r")
  288. break
  289. end
  290. end
  291. else
  292. for i = 2, lines do
  293. if((_G["GameTooltipTextLeft"..i]:GetText():find("^"..LEVEL)) or (crtype and _G["GameTooltipTextLeft"..i]:GetText():find("^"..crtype))) then
  294. if level == -1 and classif == "elite" then classif = "worldboss" end
  295. _G["GameTooltipTextLeft"..i]:SetFormattedText("|cff%02x%02x%02x%s|r%s %s", r*255, g*255, b*255, classif ~= "worldboss" and level ~= 0 and level or "", classification[classif] or "", crtype or "")
  296. break
  297. end
  298. end
  299. end
  300. local pvpLine
  301. for i = 1, lines do
  302. local text = _G["GameTooltipTextLeft"..i]:GetText()
  303. if text and text == PVP_ENABLED then
  304. pvpLine = _G["GameTooltipTextLeft"..i]
  305. pvpLine:SetText()
  306. break
  307. end
  308. end
  309. -- ToT line
  310. if UnitExists(unit.."target") and unit~="player" then
  311. local hex, r, g, b = GetColor(unit.."target")
  312. if not r and not g and not b then r, g, b = 1, 1, 1 end
  313. GameTooltip:AddLine(UnitName(unit.."target"), r, g, b)
  314. end
  315. -- Sometimes this wasn't getting reset, the fact a cleanup isn't performed at this point, now that it was moved to "OnTooltipCleared" is very bad, so this is a fix
  316. self.fadeOut = nil
  317. end)
  318. local BorderColor = function(self)
  319. local GMF = GetMouseFocus()
  320. local unit = (select(2, self:GetUnit())) or (GMF and GMF:GetAttribute("unit"))
  321. local reaction = unit and UnitReaction(unit, "player")
  322. local player = unit and UnitIsPlayer(unit)
  323. local tapped = unit and UnitIsTapped(unit)
  324. local tappedbyme = unit and UnitIsTappedByPlayer(unit)
  325. local connected = unit and UnitIsConnected(unit)
  326. local dead = unit and UnitIsDead(unit)
  327. if player then
  328. local class = select(2, UnitClass(unit))
  329. local c = T.oUF_colors.class[class]
  330. r, g, b = c[1], c[2], c[3]
  331. self:SetBackdropBorderColor(r, g, b)
  332. healthBarBG:SetBackdropBorderColor(r, g, b)
  333. healthBar:SetStatusBarColor(r, g, b)
  334. elseif reaction then
  335. local c = T.oUF_colors.reaction[reaction]
  336. r, g, b = c[1], c[2], c[3]
  337. self:SetBackdropBorderColor(r, g, b)
  338. healthBarBG:SetBackdropBorderColor(r, g, b)
  339. healthBar:SetStatusBarColor(r, g, b)
  340. else
  341. local _, link = self:GetItem()
  342. local quality = link and select(3, GetItemInfo(link))
  343. if quality and quality >= 2 then
  344. local r, g, b = GetItemQualityColor(quality)
  345. self:SetBackdropBorderColor(r, g, b)
  346. else
  347. self:SetBackdropBorderColor(unpack(C["media"].bordercolor))
  348. healthBarBG:SetBackdropBorderColor(unpack(C["media"].bordercolor))
  349. healthBar:SetStatusBarColor(unpack(C["media"].bordercolor))
  350. end
  351. end
  352. -- need this
  353. NeedBackdropBorderRefresh = true
  354. end
  355. local SetStyle = function(self)
  356. self:SetTemplate("Default", true)
  357. BorderColor(self)
  358. end
  359. TukuiTooltip:RegisterEvent("PLAYER_ENTERING_WORLD")
  360. TukuiTooltip:SetScript("OnEvent", function(self)
  361. for _, tt in pairs(Tooltips) do
  362. tt:HookScript("OnShow", SetStyle)
  363. end
  364. ItemRefTooltip:HookScript("OnTooltipSetItem", SetStyle)
  365. ItemRefTooltip:HookScript("OnShow", SetStyle)
  366. FriendsTooltip:SetTemplate("Default", true)
  367. self:UnregisterEvent("PLAYER_ENTERING_WORLD")
  368. self:SetScript("OnEvent", nil)
  369. -- move health status bar if anchor is found at top
  370. local position = TukuiTooltipAnchor:GetPoint()
  371. if position:match("TOP") then
  372. healthBar:ClearAllPoints()
  373. healthBar:Point("TOPLEFT", healthBar:GetParent(), "BOTTOMLEFT", 2, -5)
  374. healthBar:Point("TOPRIGHT", healthBar:GetParent(), "BOTTOMRIGHT", -2, -5)
  375. end
  376. -- Hide tooltips in combat for actions, pet actions and shapeshift
  377. if C["tooltip"].hidebuttons == true then
  378. local CombatHideActionButtonsTooltip = function(self)
  379. if not IsShiftKeyDown() then
  380. self:Hide()
  381. end
  382. end
  383. hooksecurefunc(GameTooltip, "SetAction", CombatHideActionButtonsTooltip)
  384. hooksecurefunc(GameTooltip, "SetPetAction", CombatHideActionButtonsTooltip)
  385. hooksecurefunc(GameTooltip, "SetShapeshift", CombatHideActionButtonsTooltip)
  386. end
  387. end)