Files
pfUI/modules/tooltip.lua
T
Brues 7efc3334ae Add an aura caster line to tooltips
Hook GameTooltip:SetUnitAura to append the caster's name; every pfUI aura
tooltip (player buffs, buffwatch, unitframes) routes through it, so one hook
covers them all. Resolve the name from the live sourceUnit token, falling
back to the sourceGUID name cache (players only) when the token is gone.

Gated behind a new tooltip.aurasource option (off by default) with a matching
"Show Aura Caster" checkbox. The toggle reloads the UI, so the hook is
installed conditionally at load rather than re-checking the flag per tooltip.
2026-08-06 02:51:08 -05:00

284 lines
10 KiB
Lua

pfUI:RegisterModule("tooltip", function ()
local rawborder, default_border = GetBorderSize()
pfUI.tooltip = CreateFrame('Frame', "pfTooltip", GameTooltip)
pfUI.tooltip.anchorframe = CreateFrame('Frame', "pfTooltipAnchor", UIParent)
pfUI.tooltip.anchorframe:SetSize(128, 72)
pfUI.tooltip.anchorframe:SetPoint("TOP", UIParent, "TOP", 0, -50)
pfUI.tooltip.anchorframe:Hide()
UpdateMovable(pfUI.tooltip.anchorframe)
function pfUI.tooltip:GetAnchorPoint()
local prefix = "TOP"
local suffix = "RIGHT"
local px, py = UIParent:GetCenter()
local tx, ty = this.anchorframe:GetCenter()
if (tx < px) then
suffix = "LEFT"
end
if (ty < py) then
prefix = "BOTTOM"
end
return prefix..suffix
end
if C.tooltip.position == "cursor" then
-- Cursor mode makes the tooltip follow the mouse. The client has no
-- mouse-move event, so following means polling GetCursorPosition() via an
-- invisible follower frame that the tooltip anchors to. The follower is
-- only shown while a tooltip is visible -- an OnUpdate fires only while its
-- frame is shown, so the poll stops the moment the tooltip hides instead
-- of running forever.
local follower, Reposition
if C.tooltip.cursoralign ~= "native" then
local size = tonumber(C.tooltip.cursoroffset) * 2
follower = CreateFrame("Frame", nil, UIParent)
follower:SetSize(size, size)
follower:Hide()
Reposition = function()
local scale = UIParent:GetScale()
local x, y = GetCursorPosition()
follower:SetPoint("CENTER", UIParent, "BOTTOMLEFT", x/scale, y/scale)
if C.tooltip.cursoralign == "top" then
follower:SetWidth(GameTooltip:GetWidth())
end
end
follower:SetScript("OnUpdate", function()
-- throttle - cursor following doesn't need to be every frame
if (this.tick or 0) > GetTime() then return end
this.tick = GetTime() + (pfUI.throttle and pfUI.throttle:Get("tooltip_cursor") or 0.1)
Reposition()
end)
-- stop polling as soon as the tooltip is gone
pfUI.tooltip:SetScript("OnHide", function() follower:Hide() end)
end
function _G.GameTooltip_SetDefaultAnchor(tooltip, parent)
tooltip:SetOwner(parent, "ANCHOR_CURSOR")
if not follower then return end
-- position the follower right away so the tooltip doesn't flash at a
-- stale spot before the first OnUpdate tick
follower:Show()
Reposition()
if C.tooltip.cursoralign == "top" then
tooltip:SetPoint("BOTTOMLEFT", follower, "TOPLEFT", 0, 0)
elseif C.tooltip.cursoralign == "left" then
tooltip:SetPoint("BOTTOMRIGHT", follower, "LEFT", 0, 0)
elseif C.tooltip.cursoralign == "right" then
tooltip:SetPoint("BOTTOMLEFT", follower, "RIGHT", 0, 0)
end
end
end
function pfUI.tooltip:GetUnit()
pfUI.tooltip.unit = "none"
if GameTooltip:HasUnit() then
local _, guid = GameTooltip:GetUnitGUID()
local token = guid and UnitTokenFromGUID(guid)
if token then pfUI.tooltip.unit = token end
end
return pfUI.tooltip.unit
end
pfUI.tooltip.dodge = {
"pfPanelRight", "pfChatRight"
}
if C.appearance.bags.movable == "0" then
table.insert(pfUI.tooltip.dodge, "pfBag")
end
pfUI.tooltip:SetAllPoints()
pfUI.tooltip:SetScript("OnShow", function()
pfUI.tooltip:Update()
if GameTooltip:GetAnchorType() == "ANCHOR_NONE" then
GameTooltip:ClearAllPoints()
if C.tooltip.position == "bottom" then
if pfUI.panel and pfUI.panel.right:IsShown() then
GameTooltip:SetPoint("BOTTOMRIGHT", pfUI.panel.right, "TOPRIGHT", 0, default_border*2)
else
GameTooltip:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", -5, 5)
end
elseif C.tooltip.position == "chat" then
local anchor = nil
for _, frame in pairs(pfUI.tooltip.dodge) do
if _G[frame] and _G[frame]:IsShown() then
anchor = _G[frame]
end
end
if anchor then
GameTooltip:SetPoint("BOTTOMRIGHT", anchor, "TOPRIGHT", 0, default_border*3)
else
GameTooltip:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", -5, 5)
end
elseif C.tooltip.position == "free" then
local point = this:GetAnchorPoint()
if point then
GameTooltip:SetPoint(point, this.anchorframe, point, 0, 0)
end
end
end
end)
pfUI.tooltipStatusBar = CreateFrame('Frame', nil, GameTooltipStatusBar)
pfUI.tooltipStatusBar:SetPoint("TOPLEFT", 0, 8)
pfUI.tooltipStatusBar:SetPoint("TOPRIGHT", 0, 8)
pfUI.tooltipStatusBar:SetHeight(12)
pfUI.tooltipStatusBar:RegisterEvent("UPDATE_MOUSEOVER_UNIT")
pfUI.tooltipStatusBar:SetScript("OnEvent", function()
this.name = UnitName("mouseover")
this.level = UnitLevel("mouseover")
end)
pfUI.tooltipStatusBar.text = CreateFrame("Frame", nil, pfUI.tooltipStatusBar)
pfUI.tooltipStatusBar.text:SetFrameLevel(16)
pfUI.tooltipStatusBar.text:SetAllPoints()
pfUI.tooltipStatusBar.HP = pfUI.tooltipStatusBar.text:CreateFontString("Status", "OVERLAY", "GameFontNormal")
pfUI.tooltipStatusBar.HP:SetAllPoints()
pfUI.tooltipStatusBar.HP:SetNonSpaceWrap(false)
pfUI.tooltipStatusBar.HP:SetFontObject(GameFontWhite)
pfUI.tooltipStatusBar.HP:SetFont(C.tooltip.font_tooltip, C.tooltip.font_tooltip_size + 2, "OUTLINE")
if GameTooltip.SetClampRectInsets then
GameTooltip:SetClampRectInsets(0, 0, 16, 0)
else
GameTooltipStatusBar:SetClampedToScreen(true)
pfUI.tooltipStatusBar:SetClampedToScreen(true)
end
pfUI.tooltipStatusBar:SetScript("OnUpdate", function()
local hp = GameTooltipStatusBar:GetValue()
local _, hpmax = GameTooltipStatusBar:GetMinMaxValues()
local rhp, rhpmax, estimated
if hpmax > 100 or (round(hpmax/100*hp) ~= hp) then
rhp, rhpmax = hp, hpmax
elseif pfUI.libhealth and pfUI.libhealth.enabled then
rhp, rhpmax, estimated = pfUI.libhealth:GetUnitHealthByName(this.name, this.level, tonumber(hp), tonumber(hpmax))
end
if C.tooltip.alwaysperc == "0" and ( estimated or hpmax > 100 or round(hpmax/100*hp) ~= hp ) then
pfUI.tooltipStatusBar.HP:SetText(string.format("%s / %s", Abbreviate(rhp), Abbreviate(rhpmax)))
elseif hpmax > 0 then
pfUI.tooltipStatusBar.HP:SetText(string.format("%s%%", ceil(hp/hpmax*100)))
else
pfUI.tooltipStatusBar.HP:SetText("")
end
end)
GameTooltipStatusBar:SetHeight(8)
GameTooltipStatusBar:ClearAllPoints()
GameTooltipStatusBar:SetPoint("BOTTOMLEFT", GameTooltip, "TOPLEFT", 0, default_border)
GameTooltipStatusBar:SetPoint("BOTTOMRIGHT", GameTooltip, "TOPRIGHT", 0, default_border)
GameTooltipStatusBar:SetStatusBarTexture(pfUI.media[C.tooltip.statusbar.texture])
CreateBackdrop(GameTooltipStatusBar, nil, nil, tonumber(C.tooltip.alpha))
CreateBackdropShadow(GameTooltipStatusBar)
GameTooltipStatusBar.SetStatusBarColor_orig = GameTooltipStatusBar.SetStatusBarColor
GameTooltipStatusBar.SetStatusBarColor = function() return end
function pfUI.tooltip:Update()
local unit = pfUI.tooltip:GetUnit()
if unit == "none" then
-- process item tooltips
if C.tooltip.itemid == "1" and GameTooltip:HasItem() then
local _, _, itemID = GameTooltip:GetItem()
GameTooltip:AddLine(T["ItemID"] .. ": " .. itemID, .25,.5,1)
GameTooltip:Show()
end
return
end
local pvpname = UnitPVPName(unit)
local name = UnitName(unit)
local validTarget, target = pcall(UnitName, unit .. 'target')
local class = UnitClassBase(unit)
local guild, rankstr, rankid = GetGuildInfo(unit)
local reaction = UnitReaction(unit, "player")
local pvptitle = gsub(gsub(pvpname or name, name, "", 1), "^%s*(.-)%s*$", "%1")
local hp = UnitHealth(unit)
local hpm = UnitHealthMax(unit)
if name then
if UnitIsPlayer(unit) and class then
local color = PFUI_CLASS_COLORS[class]
GameTooltipStatusBar:SetStatusBarColor_orig(color:GetRGB())
GameTooltip:SetBackdropBorderColor(color:GetRGB())
GameTooltipTextLeft1:SetText("|c" .. color.colorStr .. name)
elseif reaction then
local color = UnitReactionColor[reaction]
GameTooltipStatusBar:SetStatusBarColor_orig(color.r, color.g, color.b)
GameTooltip:SetBackdropBorderColor(color.r, color.g, color.b)
end
if pvptitle ~= name and pvptitle ~= "" then
GameTooltip:AppendText(" |cff666666["..pvptitle.."]|r")
end
end
if guild then
local rank = ""
local lead = ""
if C.tooltip.extguild == "1" then
if rankstr then rank = " |cffaaaaaa(" .. rankstr .. ")" end
if rankid and rankid == 0 then lead = "|cffffcc00*|r" end
end
GameTooltip:AddLine("<" .. guild .. ">" .. lead .. rank, 0.3, 1, 0.5)
end
if validTarget and target then
local targetClass = UnitClassBase(unit .. "target")
local targetReaction = UnitReaction("player",unit .. "target")
if UnitIsPlayer(unit .. "target") and targetClass then
local color = PFUI_CLASS_COLORS[targetClass]
GameTooltip:AddLine(target, color.r, color.g, color.b)
elseif targetReaction then
local color = UnitReactionColor[targetReaction]
if color then
GameTooltip:AddLine(target, color.r, color.g, color.b)
else
GameTooltip:AddLine(target, .5, .5, .5)
end
end
end
if C.tooltip.movespeed == "1" then
local currentSpeed = GetUnitSpeed(unit)
if currentSpeed and currentSpeed > 0 then
local pct = floor(currentSpeed / 7 * 100 + 0.5)
GameTooltip:AddLine(T["Speed"] .. ": " .. pct .. "%", 0.7, 0.7, 1)
end
end
if hp and hpm then
if hp >= 1000 then hp = round(hp / 1000, 1) .. "k" end
if hpm >= 1000 then hpm = round(hpm / 1000, 1) .. "k" end
pfUI.tooltipStatusBar.HP:SetText(hp .. " / " .. hpm)
end
GameTooltip:Show()
end
if C.tooltip.aurasource == "1" then
hooksecurefunc(GameTooltip, "SetUnitAura", function(self, ...)
local aura = C_UnitAuras.GetAuraDataByIndex(unpack(arg))
if not aura then return end
local caster = aura.sourceUnit and UnitName(aura.sourceUnit)
if not caster and aura.sourceGUID then
caster = UnitNameFromGUID(aura.sourceGUID)
end
if not caster or caster == "" then return end
self:AddLine(T["Cast by"] .. ": " .. caster, .7, .7, 1)
self:Show()
end)
end
end)