Merge pull request #9 from Dusk-92/test/tooltip-details-classicapi
Use ClassicAPI native Tooltip Details paths
This commit is contained in:
@@ -109,6 +109,14 @@ API.playerstate = type(_G.IsMounted) == "function"
|
||||
and type(_G.CancelShapeshiftForm) == "function"
|
||||
|
||||
API.unitguid = type(_G.UnitGUID) == "function"
|
||||
API.unittoken = type(_G.UnitTokenFromGUID) == "function"
|
||||
API.tooltipunit = _G.GameTooltip
|
||||
and type(_G.GameTooltip.GetUnitGUID) == "function"
|
||||
-- OnTooltipSetUnit shipped in ClassicAPI v1.8.2, but its per-tooltip
|
||||
-- handler cells became reload-safe in v1.12.2. Require the fixed generation
|
||||
-- instead of falling back to Vanilla OnShow/mouseover polling.
|
||||
API.tooltipsetunit = API.classicapi_version >= 11202
|
||||
and API.tooltipunit and API.unittoken
|
||||
API.unitrange = type(_G.UnitInRange) == "function"
|
||||
|
||||
API.macrospell = type(_G.GetMacroSpell) == "function"
|
||||
@@ -660,6 +668,19 @@ API.UnitGUID = function(unit)
|
||||
end
|
||||
end
|
||||
|
||||
API.UnitTokenFromGUID = function(guid)
|
||||
if API.unittoken and guid then
|
||||
return _G.UnitTokenFromGUID(guid)
|
||||
end
|
||||
end
|
||||
|
||||
API.GetTooltipUnitGUID = function(tooltip)
|
||||
if API.tooltipunit and tooltip then
|
||||
local _, guid = tooltip:GetUnitGUID()
|
||||
return guid
|
||||
end
|
||||
end
|
||||
|
||||
-- Prefer ClassicAPI's reach-aware 40-yard UnitInRange. The wrapper handles
|
||||
-- the player's own frame explicitly and preserves a Vanilla interaction check
|
||||
-- only as a centralized compatibility fallback.
|
||||
|
||||
+78
-226
@@ -5,45 +5,12 @@ local rgbhex = ShaguTweaks.rgbhex
|
||||
local Abbreviate = ShaguTweaks.Abbreviate
|
||||
local API = ShaguTweaks.API
|
||||
|
||||
local current_unit = "none"
|
||||
local current_guid = nil
|
||||
local statusbar = nil
|
||||
local tooltip_from_mouseover = false
|
||||
local tooltipSetStatusBarColor
|
||||
|
||||
local direct_units = { "mouseover", "target", "focus", "player", "pet" }
|
||||
|
||||
local function UnitMatchesGUID(unit, guid)
|
||||
if not guid or not UnitExists(unit) then return false end
|
||||
|
||||
if API and API.UnitGUID then
|
||||
return API.UnitGUID(unit) == guid
|
||||
elseif type(UnitGUID) == "function" then
|
||||
return UnitGUID(unit) == guid
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function GetTooltipGUID()
|
||||
if type(GameTooltip.GetUnitGUID) == "function" then
|
||||
local _, guid = GameTooltip:GetUnitGUID()
|
||||
return guid
|
||||
end
|
||||
end
|
||||
|
||||
local function TooltipMatchesMouseover()
|
||||
if not UnitExists("mouseover") then return false end
|
||||
|
||||
local guid = GetTooltipGUID()
|
||||
if guid then
|
||||
return UnitMatchesGUID("mouseover", guid)
|
||||
end
|
||||
|
||||
-- Compatibility fallback for clients without the ClassicAPI tooltip GUID.
|
||||
local tooltipName = GameTooltipTextLeft1:GetText()
|
||||
return tooltipName and
|
||||
(UnitName("mouseover") == tooltipName or UnitPVPName("mouseover") == tooltipName)
|
||||
local function GetUnit()
|
||||
local guid = API.GetTooltipUnitGUID(GameTooltip)
|
||||
if not guid then return end
|
||||
return API.UnitTokenFromGUID(guid)
|
||||
end
|
||||
|
||||
local function ClearTooltipBarColor()
|
||||
@@ -66,105 +33,37 @@ local function SetTooltipBarColor(r, g, b, a)
|
||||
end
|
||||
end
|
||||
|
||||
local function ResetTooltipIdentity()
|
||||
current_unit = "none"
|
||||
current_guid = nil
|
||||
tooltip_from_mouseover = false
|
||||
local function UpdateHealthText()
|
||||
local backdrop = GameTooltipStatusBar.backdrop
|
||||
if not backdrop or not backdrop.health then return end
|
||||
|
||||
local hp = GameTooltipStatusBar:GetValue()
|
||||
local _, hpmax = GameTooltipStatusBar:GetMinMaxValues()
|
||||
|
||||
local hasRealHealth = hpmax > 100
|
||||
or (hpmax > 0 and round(hpmax / 100 * hp) ~= hp)
|
||||
|
||||
if hasRealHealth then
|
||||
backdrop.health:SetText(string.format("%s / %s",
|
||||
Abbreviate(hp, true), Abbreviate(hpmax, true)))
|
||||
elseif hpmax > 0 then
|
||||
backdrop.health:SetText(string.format("%s%%", ceil(hp / hpmax * 100)))
|
||||
else
|
||||
backdrop.health:SetText("")
|
||||
end
|
||||
end
|
||||
|
||||
local function ResetTooltipState()
|
||||
ClearTooltipBarColor()
|
||||
|
||||
if statusbar then
|
||||
statusbar.name = nil
|
||||
statusbar.level = nil
|
||||
statusbar.lastHP = nil
|
||||
statusbar.lastHPMax = nil
|
||||
statusbar.lastName = nil
|
||||
statusbar.lastLevel = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function FindUnitByGUID(guid)
|
||||
if not guid then return end
|
||||
|
||||
-- The common case is mouseover/target, so check cheap tokens first.
|
||||
for _, unit in pairs(direct_units) do
|
||||
if UnitMatchesGUID(unit, guid) then
|
||||
return unit
|
||||
end
|
||||
if GameTooltipStatusBar.bg then
|
||||
GameTooltipStatusBar.bg:SetVertexColor(.1, .1, 0, .8)
|
||||
end
|
||||
|
||||
for i=1,4 do
|
||||
if UnitMatchesGUID("party" .. i, guid) then return "party" .. i end
|
||||
if UnitMatchesGUID("partypet" .. i, guid) then return "partypet" .. i end
|
||||
if GameTooltipStatusBar.backdrop
|
||||
and GameTooltipStatusBar.backdrop.health then
|
||||
GameTooltipStatusBar.backdrop.health:SetText("")
|
||||
end
|
||||
|
||||
for i=1,40 do
|
||||
if UnitMatchesGUID("raid" .. i, guid) then return "raid" .. i end
|
||||
if UnitMatchesGUID("raidpet" .. i, guid) then return "raidpet" .. i end
|
||||
end
|
||||
end
|
||||
|
||||
local function FindUnitByName()
|
||||
local tooltipName = GameTooltipTextLeft1:GetText()
|
||||
if not tooltipName then return end
|
||||
|
||||
for _, unit in pairs(direct_units) do
|
||||
if UnitExists(unit) and
|
||||
(UnitName(unit) == tooltipName or UnitPVPName(unit) == tooltipName) then
|
||||
return unit
|
||||
end
|
||||
end
|
||||
|
||||
for i=1,4 do
|
||||
local party = "party" .. i
|
||||
local pet = "partypet" .. i
|
||||
if UnitExists(party) and
|
||||
(UnitName(party) == tooltipName or UnitPVPName(party) == tooltipName) then
|
||||
return party
|
||||
end
|
||||
if UnitExists(pet) and
|
||||
(UnitName(pet) == tooltipName or UnitPVPName(pet) == tooltipName) then
|
||||
return pet
|
||||
end
|
||||
end
|
||||
|
||||
for i=1,40 do
|
||||
local raid = "raid" .. i
|
||||
local pet = "raidpet" .. i
|
||||
if UnitExists(raid) and
|
||||
(UnitName(raid) == tooltipName or UnitPVPName(raid) == tooltipName) then
|
||||
return raid
|
||||
end
|
||||
if UnitExists(pet) and
|
||||
(UnitName(pet) == tooltipName or UnitPVPName(pet) == tooltipName) then
|
||||
return pet
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function GetUnit()
|
||||
-- ClassicAPI stores the actual unit GUID on GameTooltip. Prefer it over the
|
||||
-- old name-comparison heuristic, which can misidentify units with matching
|
||||
-- display names or PvP titles.
|
||||
local guid = GetTooltipGUID()
|
||||
if guid then
|
||||
if current_guid == guid and current_unit ~= "none" and
|
||||
UnitMatchesGUID(current_unit, guid) then
|
||||
return current_unit
|
||||
end
|
||||
|
||||
local unit = FindUnitByGUID(guid)
|
||||
if unit then
|
||||
current_guid = guid
|
||||
current_unit = unit
|
||||
return current_unit
|
||||
end
|
||||
end
|
||||
|
||||
-- Compatibility fallback for clients without the ClassicAPI tooltip method,
|
||||
-- or for a unit that cannot currently be mapped back to a known unit token.
|
||||
current_guid = nil
|
||||
current_unit = FindUnitByName() or "none"
|
||||
return current_unit
|
||||
end
|
||||
|
||||
local updating = false
|
||||
@@ -173,23 +72,18 @@ local function UpdateTooltip()
|
||||
if updating then return end
|
||||
updating = true
|
||||
|
||||
-- ClassicAPI keeps the exact tooltip GUID and resolves it back to a live
|
||||
-- unit token natively. No party/raid scans or display-name fallback.
|
||||
local unit = GetUnit()
|
||||
if unit == "none" then
|
||||
if not unit then
|
||||
updating = false
|
||||
return
|
||||
end
|
||||
|
||||
-- A newly resolved unit chooses its own class/reaction color below. Clear the
|
||||
-- previous enforcement first so an unknown color cannot leak from the last
|
||||
-- tooltip while the same GameTooltip frame is being reused.
|
||||
-- previous enforcement first so a stale color cannot leak between tooltips.
|
||||
ClearTooltipBarColor()
|
||||
|
||||
-- Remember whether this particular unit tooltip came from the world
|
||||
-- mouseover. If the same GUID is also the selected target, GetUnit() can
|
||||
-- legitimately resolve it as "target" after mouseover ends; this flag keeps
|
||||
-- the tooltip lifetime tied to the cursor instead of the target token.
|
||||
tooltip_from_mouseover = TooltipMatchesMouseover()
|
||||
|
||||
local pvpname = UnitPVPName(unit)
|
||||
local name = UnitName(unit)
|
||||
local target = UnitName(unit .. "target")
|
||||
@@ -200,24 +94,15 @@ local function UpdateTooltip()
|
||||
local reaction = UnitReaction(unit, "player")
|
||||
local pvptitle = name and gsub(pvpname or name, " " .. name, "", 1) or nil
|
||||
|
||||
-- Keep the status display bound to the unit actually represented by the
|
||||
-- tooltip, not merely whichever unit happened to be mouseover last.
|
||||
if statusbar then
|
||||
statusbar.name = name
|
||||
statusbar.level = UnitLevel(unit)
|
||||
statusbar.lastHP = nil
|
||||
statusbar.lastHPMax = nil
|
||||
statusbar.lastName = nil
|
||||
statusbar.lastLevel = nil
|
||||
end
|
||||
|
||||
if name then
|
||||
if UnitIsPlayer(unit) and class then
|
||||
local color = RAID_CLASS_COLORS[class]
|
||||
if color then
|
||||
SetTooltipBarColor(color.r, color.g, color.b, 1)
|
||||
GameTooltipStatusBar.bg:SetVertexColor(color.r * 0.15, color.g * 0.15, color.b * 0.15, 0.8)
|
||||
GameTooltipTextLeft1:SetText(rgbhex(color.r, color.g, color.b, color.a) .. name)
|
||||
GameTooltipStatusBar.bg:SetVertexColor(
|
||||
color.r * 0.15, color.g * 0.15, color.b * 0.15, 0.8)
|
||||
GameTooltipTextLeft1:SetText(
|
||||
rgbhex(color.r, color.g, color.b, color.a) .. name)
|
||||
else
|
||||
GameTooltipTextLeft1:SetText("|cff999999" .. name)
|
||||
end
|
||||
@@ -225,7 +110,8 @@ local function UpdateTooltip()
|
||||
local color = UnitReactionColor[reaction]
|
||||
if color then
|
||||
SetTooltipBarColor(color.r, color.g, color.b, 1)
|
||||
GameTooltipStatusBar.bg:SetVertexColor(color.r * 0.15, color.g * 0.15, color.b * 0.15, 0.8)
|
||||
GameTooltipStatusBar.bg:SetVertexColor(
|
||||
color.r * 0.15, color.g * 0.15, color.b * 0.15, 0.8)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -279,6 +165,7 @@ local function UpdateTooltip()
|
||||
end
|
||||
end
|
||||
|
||||
UpdateHealthText()
|
||||
GameTooltip:Show()
|
||||
updating = false
|
||||
end
|
||||
@@ -299,6 +186,10 @@ local backdrop = {
|
||||
}
|
||||
|
||||
module.enable = function(self)
|
||||
-- Tooltip Details intentionally requires the native ClassicAPI tooltip
|
||||
-- identity + OnTooltipSetUnit path. Do not fall back to Vanilla name scans.
|
||||
if not API.tooltipsetunit then return end
|
||||
|
||||
GameTooltipStatusBar:SetHeight(10)
|
||||
GameTooltipStatusBar:ClearAllPoints()
|
||||
GameTooltipStatusBar:SetPoint("BOTTOMLEFT", GameTooltip, "TOPLEFT", 4, 2)
|
||||
@@ -309,90 +200,51 @@ module.enable = function(self)
|
||||
GameTooltipStatusBar.bg:SetVertexColor(.1, .1, 0, .8)
|
||||
GameTooltipStatusBar.bg:SetAllPoints(true)
|
||||
|
||||
GameTooltipStatusBar.backdrop = CreateFrame("Frame", "GameTooltipStatusBarBackdrop", GameTooltipStatusBar)
|
||||
GameTooltipStatusBar.backdrop:SetPoint("TOPLEFT", GameTooltipStatusBar, "TOPLEFT", -3, 3)
|
||||
GameTooltipStatusBar.backdrop:SetPoint("BOTTOMRIGHT", GameTooltipStatusBar, "BOTTOMRIGHT", 3, -3)
|
||||
GameTooltipStatusBar.backdrop =
|
||||
CreateFrame("Frame", "GameTooltipStatusBarBackdrop", GameTooltipStatusBar)
|
||||
GameTooltipStatusBar.backdrop:SetPoint(
|
||||
"TOPLEFT", GameTooltipStatusBar, "TOPLEFT", -3, 3)
|
||||
GameTooltipStatusBar.backdrop:SetPoint(
|
||||
"BOTTOMRIGHT", GameTooltipStatusBar, "BOTTOMRIGHT", 3, -3)
|
||||
GameTooltipStatusBar.backdrop:SetBackdrop(backdrop)
|
||||
GameTooltipStatusBar.backdrop:SetBackdropBorderColor(.8,.8,.8,1)
|
||||
|
||||
GameTooltipStatusBar.backdrop.health = GameTooltipStatusBar.backdrop:CreateFontString("Status", "DIALOG", "GameFontWhite")
|
||||
GameTooltipStatusBar.backdrop.health:SetFont(STANDARD_TEXT_FONT, 12, "OUTLINE")
|
||||
GameTooltipStatusBar.backdrop.health =
|
||||
GameTooltipStatusBar.backdrop:CreateFontString(
|
||||
"Status", "DIALOG", "GameFontWhite")
|
||||
GameTooltipStatusBar.backdrop.health:SetFont(
|
||||
STANDARD_TEXT_FONT, 12, "OUTLINE")
|
||||
GameTooltipStatusBar.backdrop.health:SetPoint("TOP", 0, 4)
|
||||
GameTooltipStatusBar.backdrop.health:SetNonSpaceWrap(false)
|
||||
|
||||
-- Preserve the real statusbar method for Blizzard and other addons. A
|
||||
-- post-hook reapplies ShaguTweaks' class/reaction color only while a unit
|
||||
-- tooltip has an active desired color.
|
||||
tooltipSetStatusBarColor = tooltipSetStatusBarColor or GameTooltipStatusBar.SetStatusBarColor
|
||||
tooltipSetStatusBarColor =
|
||||
tooltipSetStatusBarColor or GameTooltipStatusBar.SetStatusBarColor
|
||||
if not self.statusColorHooked then
|
||||
self.statusColorHooked = true
|
||||
ShaguTweaks.hooksecurefunc(GameTooltipStatusBar, "SetStatusBarColor", function()
|
||||
local r = GameTooltipStatusBar.ShaguTweaksColorR
|
||||
local g = GameTooltipStatusBar.ShaguTweaksColorG
|
||||
local b = GameTooltipStatusBar.ShaguTweaksColorB
|
||||
local a = GameTooltipStatusBar.ShaguTweaksColorA
|
||||
if r and g and b then
|
||||
tooltipSetStatusBarColor(GameTooltipStatusBar, r, g, b, a or 1)
|
||||
end
|
||||
end)
|
||||
ShaguTweaks.hooksecurefunc(
|
||||
GameTooltipStatusBar, "SetStatusBarColor", function()
|
||||
local r = GameTooltipStatusBar.ShaguTweaksColorR
|
||||
local g = GameTooltipStatusBar.ShaguTweaksColorG
|
||||
local b = GameTooltipStatusBar.ShaguTweaksColorB
|
||||
local a = GameTooltipStatusBar.ShaguTweaksColorA
|
||||
if r and g and b then
|
||||
tooltipSetStatusBarColor(
|
||||
GameTooltipStatusBar, r, g, b, a or 1)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- update tooltip whenever it gets shown
|
||||
local details = CreateFrame("Frame", nil, GameTooltip)
|
||||
details:SetScript("OnShow", UpdateTooltip)
|
||||
details:SetScript("OnHide", ResetTooltipIdentity)
|
||||
-- ClassicAPI fires this after the native unit-tooltip builder completes.
|
||||
-- This replaces OnShow + UPDATE_MOUSEOVER_UNIT identity refreshes.
|
||||
GameTooltip:HookScript("OnTooltipSetUnit", UpdateTooltip)
|
||||
-- OnTooltipCleared fires for every new Set* path, not only when the frame
|
||||
-- hides, so unit-only state cannot leak into a following item/spell tooltip.
|
||||
GameTooltip:HookScript("OnTooltipCleared", ResetTooltipState)
|
||||
|
||||
-- refresh the currently displayed unit identity
|
||||
statusbar = CreateFrame("Frame", nil, GameTooltipStatusBar)
|
||||
statusbar:RegisterEvent("UPDATE_MOUSEOVER_UNIT")
|
||||
statusbar:SetScript("OnEvent", function()
|
||||
-- Do not force Hide/FadeOut here. Turtle/Vanilla owns the tooltip lifetime
|
||||
-- and applies its native delay + fade when the cursor leaves a world unit.
|
||||
-- We only avoid re-showing the stale tooltip through the selected target.
|
||||
if not UnitExists("mouseover") and tooltip_from_mouseover then
|
||||
return
|
||||
end
|
||||
|
||||
local unit = GetUnit()
|
||||
if unit ~= "none" then
|
||||
this.name = UnitName(unit)
|
||||
this.level = UnitLevel(unit)
|
||||
else
|
||||
this.name = UnitName("mouseover")
|
||||
this.level = UnitLevel("mouseover")
|
||||
end
|
||||
|
||||
this.lastHP = nil
|
||||
this.lastHPMax = nil
|
||||
this.lastName = nil
|
||||
this.lastLevel = nil
|
||||
UpdateTooltip()
|
||||
end)
|
||||
|
||||
-- Keep the lightweight OnUpdate because the vanilla tooltip statusbar can
|
||||
-- change without a dedicated Lua event. Skip string work entirely while its
|
||||
-- inputs are unchanged.
|
||||
statusbar:SetScript("OnUpdate", function()
|
||||
local hp = GameTooltipStatusBar:GetValue()
|
||||
local _, hpmax = GameTooltipStatusBar:GetMinMaxValues()
|
||||
|
||||
if hp == this.lastHP and hpmax == this.lastHPMax and
|
||||
this.name == this.lastName and this.level == this.lastLevel then
|
||||
return
|
||||
end
|
||||
|
||||
this.lastHP = hp
|
||||
this.lastHPMax = hpmax
|
||||
this.lastName = this.name
|
||||
this.lastLevel = this.level
|
||||
|
||||
local hasRealHealth = hpmax > 100 or (round(hpmax / 100 * hp) ~= hp)
|
||||
if hasRealHealth then
|
||||
GameTooltipStatusBar.backdrop.health:SetText(string.format("%s / %s", Abbreviate(hp, true), Abbreviate(hpmax, true)))
|
||||
elseif hpmax > 0 then
|
||||
GameTooltipStatusBar.backdrop.health:SetText(string.format("%s%%", ceil(hp / hpmax * 100)))
|
||||
else
|
||||
GameTooltipStatusBar.backdrop.health:SetText("")
|
||||
end
|
||||
end)
|
||||
-- StatusBar already has a native value-change script, so health text can be
|
||||
-- event-driven too. No permanent OnUpdate is needed.
|
||||
GameTooltipStatusBar:HookScript("OnValueChanged", UpdateHealthText)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user