mirror of
https://github.com/brues-code/pfUI.git
synced 2026-11-01 16:36:56 +00:00
Every pfUI cooldown is now made by CreateCooldown(parent, kind) in the new api/cooldown.lua, which replaces the cooldown module. A kind names what the cooldown times: actionbar, bags, blizzard (spellbook and character), unitframe, indicator, nameplate, buff, tooltip, totem or lossofcontrol. The pfCooldownType, pfCooldownStyleText, pfCooldownStyleAnimation and pfCooldownSize fields are gone. The new file owns everything the cooldowns share: - the numbers' font and size - the formatter - each kind's numbers, sweep and sweep direction - the GCD threshold for spell kinds - the CooldownFrame_SetTimer hook for other addons' Cooldown frames UpdateCooldowns applies changed settings to every cooldown at once. GetAuraTimer moves here from the unit frames. All settings live in one C.cooldown table, on one page: Settings -> Cooldown. It has the shared font, size, threshold, tenths and colors, then "Show Numbers On", "Show Sweep On" and "Reverse Sweep On" lists with a checkbox per kind. The per-module options are removed: - each unit frame's timer text, animation and reverse, and the indicators' "Show Time Left" - the nameplate debuff timer options - the Blizzard and foreign font sizes and toggles - Action Bars' cooldown text size - the reverse options of bars, bags, buffs and totems The old appearance.cd settings are not migrated. The player buff frame and the tooltip aura icons now show the native countdown too, which replaces their own text and OnUpdate ticking. The buff frame keeps its inside or below-the-icon placement. Weapon enchants report only their time left, so they start through SetCooldownTimeLeft: numbers, no sweep. A slim poll remains for their charges and renewals, since no event reports those. Buff indicators now take the aura's real duration, which gives them a correct sweep. The spellbook and character slots now hide GCD numbers like the other spell cooldowns.
416 lines
16 KiB
Lua
416 lines
16 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()
|
|
if C.tooltip.itemid == "1" and GameTooltip:HasItem() then
|
|
local _, _, itemID = GameTooltip:GetItem()
|
|
GameTooltip:AddLine(T["ItemID"] .. ": " .. itemID, .25,.5,1)
|
|
GameTooltip:Show()
|
|
elseif C.tooltip.spellid == "1" and GameTooltip:HasSpell() then
|
|
local _, _, spellID = GameTooltip:GetSpell()
|
|
GameTooltip:AddLine(T["SpellID"] .. ": " .. spellID, .25,.5,1)
|
|
GameTooltip:Show()
|
|
end
|
|
|
|
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:SetFormattedText("%s / %s", Abbreviate(rhp), Abbreviate(rhpmax))
|
|
elseif hpmax > 0 then
|
|
pfUI.tooltipStatusBar.HP:SetFormattedText("%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
|
|
|
|
-- A row of buff icons anchored above the tooltip for mouseover units.
|
|
-- Icons come from a ClassicAPI object pool: ReleaseAll hides the whole
|
|
-- set each refresh, then we Acquire and re-anchor left-to-right.
|
|
local BUFF_SIZE, BUFF_SPACING, BUFF_MAX = 20, 2, 32
|
|
local auraSlots = {} -- reusable GetAuraSlots buffer for UpdateBuffs
|
|
pfUI.tooltip.buffs = CreateFrame("Frame", "pfTooltipBuffs", GameTooltip)
|
|
pfUI.tooltip.buffs:SetPoint("BOTTOMLEFT", GameTooltipStatusBar, "TOPLEFT", 0, default_border + 2)
|
|
pfUI.tooltip.buffs:SetHeight(BUFF_SIZE)
|
|
|
|
local function CreateBuffIcon()
|
|
local icon = CreateFrame("Frame", nil, pfUI.tooltip.buffs)
|
|
icon:SetSize(BUFF_SIZE, BUFF_SIZE)
|
|
icon.texture = icon:CreateTexture(nil, "BACKGROUND")
|
|
icon.texture:SetTexCoord(.07, .93, .07, .93)
|
|
icon.texture:SetAllPoints(icon)
|
|
CreateBackdrop(icon)
|
|
|
|
icon.stacks = icon:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
|
icon.stacks:SetPoint("BOTTOMRIGHT", icon, "BOTTOMRIGHT", 1, 0)
|
|
icon.stacks:SetFont(C.tooltip.font_tooltip, C.tooltip.font_tooltip_size, "OUTLINE")
|
|
icon.stacks:SetTextColor(1, 1, 1, 1)
|
|
|
|
icon.cd = CreateCooldown(icon, "tooltip")
|
|
return icon
|
|
end
|
|
|
|
pfUI.tooltip.buffpool = CreateObjectPool(CreateBuffIcon, function(_, icon)
|
|
icon:Hide()
|
|
icon:ClearAllPoints()
|
|
end)
|
|
|
|
function pfUI.tooltip:UpdateBuffs(unit)
|
|
pfUI.tooltip.buffpool:ReleaseAll()
|
|
|
|
if C.tooltip.showbuffs ~= "1" or not unit or unit == "none" then
|
|
pfUI.tooltip.buffs:Hide()
|
|
return
|
|
end
|
|
|
|
local prev, count = nil, 0
|
|
-- one GetAuraSlots enumeration + positional by-slot reads: no per-aura
|
|
-- table and no per-index re-walk of the aura array
|
|
local n = ScanAuraSlots(unit, "HELPFUL", auraSlots, BUFF_MAX)
|
|
for i = 1, n do
|
|
local name, texture, applications, _, duration, expirationTime = C_UnitAuras.UnitAuraBySlot(unit, auraSlots[i])
|
|
if not name then break end
|
|
|
|
count = count + 1
|
|
local icon = pfUI.tooltip.buffpool:Acquire()
|
|
icon.texture:SetTexture(texture)
|
|
icon.stacks:SetText(applications and applications > 1 and applications or "")
|
|
|
|
local start, dur = GetAuraTimer(duration, expirationTime)
|
|
CooldownFrame_SetTimer(icon.cd, start, dur, dur > 0 and 1 or 0)
|
|
|
|
if prev then
|
|
icon:SetPoint("LEFT", prev, "RIGHT", BUFF_SPACING, 0)
|
|
else
|
|
icon:SetPoint("BOTTOMLEFT", pfUI.tooltip.buffs, "BOTTOMLEFT", 0, 0)
|
|
end
|
|
icon:Show()
|
|
prev = icon
|
|
end
|
|
|
|
if count > 0 then
|
|
pfUI.tooltip.buffs:SetWidth(count * BUFF_SIZE + (count - 1) * BUFF_SPACING)
|
|
pfUI.tooltip.buffs:Show()
|
|
else
|
|
pfUI.tooltip.buffs:Hide()
|
|
end
|
|
end
|
|
|
|
-- A unit tooltip names the unit's target, so it rebuilds when that changes:
|
|
-- UNIT_TARGET by the unit's GUID, which unit events fire with whatever token
|
|
-- names the unit. SetUnit re-runs OnTooltipSetUnit and with it UpdateUnit.
|
|
-- It goes through the token the tooltip was built from -- "mouseover" for a
|
|
-- world unit stops naming it once the cursor leaves, so a fading tooltip is
|
|
-- left to fade -- and waits a frame: the rebuild re-points this frame's
|
|
-- registration, which the dispatch is still walking.
|
|
local function Retarget()
|
|
local _, unit = GameTooltip:GetUnit()
|
|
local _, guid = GameTooltip:GetUnitGUID()
|
|
if unit and GameTooltip:IsShown() and UnitGUID(unit) == guid then
|
|
GameTooltip:SetUnit(unit)
|
|
end
|
|
end
|
|
|
|
pfUI.tooltip.retarget = CreateFrame("Frame")
|
|
pfUI.tooltip.retarget:SetScript("OnEvent", function()
|
|
RunNextFrame(Retarget)
|
|
end)
|
|
|
|
-- pfUI's unit lines go on whenever a unit is set, not only when the tooltip
|
|
-- shows, so a tooltip rebuilt while it is up keeps them
|
|
GameTooltip:HookScript("OnTooltipSetUnit", function()
|
|
pfUI.tooltip:UpdateUnit()
|
|
end)
|
|
|
|
-- the buff row belongs to the unit it was built for
|
|
GameTooltip:HookScript("OnTooltipCleared", function()
|
|
pfUI.tooltip:UpdateBuffs("none")
|
|
end)
|
|
|
|
GameTooltip:HookScript("OnHide", function()
|
|
pfUI.tooltip:UpdateBuffs("none")
|
|
pfUI.tooltip.retarget:UnregisterEvent("UNIT_TARGET")
|
|
end)
|
|
|
|
function pfUI.tooltip:UpdateUnit()
|
|
local unit = pfUI.tooltip:GetUnit()
|
|
pfUI.tooltip:UpdateBuffs(unit)
|
|
if unit == "none" then return end
|
|
pfUI.tooltip.retarget:RegisterUnitEvent("UNIT_TARGET", UnitGUID(unit))
|
|
|
|
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 C.tooltip.unitid == "1" and not UnitIsPlayer(unit) then
|
|
local npcID = C_CreatureInfo.GetCreatureID(UnitGUID(unit))
|
|
if npcID then
|
|
GameTooltip:AddLine(T["UnitID"] .. ": " .. npcID, .25,.5,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" or C.tooltip.spellid == "1" then
|
|
hooksecurefunc(GameTooltip, "SetUnitAura", function(self, ...)
|
|
local aura = C_UnitAuras.GetAuraDataByIndex(unpack(arg))
|
|
if not aura then return end
|
|
if C.tooltip.aurasource == "1" then
|
|
local caster = aura.sourceUnit and UnitName(aura.sourceUnit)
|
|
if not caster and aura.sourceGUID then
|
|
caster = UnitNameFromGUID(aura.sourceGUID)
|
|
end
|
|
if caster and caster ~= "" then
|
|
local classToken
|
|
if aura.sourceUnit and UnitIsPlayer(aura.sourceUnit) then
|
|
classToken = UnitClassBase(aura.sourceUnit)
|
|
elseif aura.sourceGUID then
|
|
classToken = select(2, GetPlayerInfoByGUID(aura.sourceGUID))
|
|
end
|
|
if classToken then
|
|
caster = PFUI_CLASS_COLORS[classToken]:WrapTextInColorCode(caster)
|
|
end
|
|
self:AddLine(T["Cast by"] .. ": " .. caster, .25,.5,1)
|
|
end
|
|
end
|
|
if C.tooltip.spellid == "1" then
|
|
self:AddLine(T["SpellID"] .. ": " .. aura.spellId, .25,.5,1)
|
|
end
|
|
self:Show()
|
|
end)
|
|
end
|
|
end)
|