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