You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

660 lines
20 KiB

local _, addon = ...
local L = Narci.L;
local Round = addon.Math.Round;
local GetItemQualityColor = NarciAPI.GetItemQualityColor;
local UIParent = UIParent;
local TooltipFrame = CreateFrame("Frame");
addon.UIFrameTooltip = TooltipFrame;
TooltipFrame:Hide();
TooltipFrame:SetSize(16, 16);
TooltipFrame:SetIgnoreParentScale(true);
TooltipFrame:SetIgnoreParentAlpha(true);
TooltipFrame:SetFrameStrata("TOOLTIP");
TooltipFrame:SetFixedFrameStrata(true);
TooltipFrame:SetClampedToScreen(true);
TooltipFrame:SetClampRectInsets(-4, 4, 4, -4);
NarciFadeUI.CreateFadeObject(TooltipFrame);
TooltipFrame.ShowFrame = TooltipFrame.Show;
TooltipFrame.HideFrame = TooltipFrame.Hide;
local LINE_TYPE_PRICE = Enum.TooltipDataLineType.SellPrice or 11;
local FONT_LARGE = "GameTooltipHeaderText";
local FONT_MEDIUM = "GameTooltipText";
local FONT_SMALL = "GameTooltipTextSmall";
local FONT_HEIGHT_MEDIUM = 12;
local FONTSTRING_MIN_GAP = 24; --Betweeb the left and the right text of the same line
local SELL_PRICE_TEXT = (SELL_PRICE or "Sell Price").." ";
do
local _;
_, FONT_HEIGHT_MEDIUM = _G[FONT_MEDIUM]:GetFont();
FONT_HEIGHT_MEDIUM = Round(FONT_HEIGHT_MEDIUM);
end
local SPACING_NEW_LINE = 4; --Between paragraphs
local SPACING_INTERNAL = 2; --Within the same paragraph
local TOOLTIP_PADDING = 8;
local TOOLTIP_MAX_WIDTH = 320;
local FONTSTRING_MAX_WIDTH = TOOLTIP_MAX_WIDTH - 2*TOOLTIP_PADDING;
local C_TooltipInfo = C_TooltipInfo;
local unpack = unpack;
local pairs = pairs;
local floor = math.floor;
local function DisableSharpening(texture)
texture:SetTexelSnappingBias(0);
texture:SetSnapToPixelGrid(false);
end
local function EvaluateWidth(fontString, lastMax)
local width = fontString:GetWrappedWidth();
return ((width > lastMax) and width) or lastMax
end
function TooltipFrame:Init()
if not self.Background then
local texture = self:CreateTexture(nil, "BACKGROUND", nil, -1);
self.Background = texture;
texture:SetTexture("Interface/AddOns/Narcissus/Art/Tooltip/TooltipDefaultDarkLayout");
DisableSharpening(texture);
texture:SetTextureSliceMode(0); --Enum.UITextureSliceMode, 0 Stretched(Default) 1 Tiled
texture:SetTextureSliceMargins(16, 16, 16, 16);
texture:SetAllPoints(true);
end
if not self.Content then
self.Content = CreateFrame("Frame", nil, self);
self.Content:SetPoint("TOPLEFT", self, "TOPLEFT", TOOLTIP_PADDING, -TOOLTIP_PADDING);
self.Content:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", -TOOLTIP_PADDING, TOOLTIP_PADDING);
end
if not self.fontStrings then
self.fontStrings = {};
end
--[[
if not self.HotkeyIcon then
self.HotkeyIcon = addon.CreateHotkeyIcon(self);
local responsive = true;
self.HotkeyIcon:SetKey("Alt", responsive);
self.HotkeyIcon:SetPoint("BOTTOMLEFT", self, "TOPLEFT", 0, 2);
self.HotkeyIcon:Hide();
end
--]]
self.Init = nil;
end
function TooltipFrame:UpdatePixel(scale)
scale = scale or self:GetEffectiveScale();
if scale ~= self.effectiveScale then
self.effectiveScale = scale;
local _, screenHeight = GetPhysicalScreenSize(); --Assume players don't change screen resolution (triggers DISPLAY_SIZE_CHANGED)
local px = (768/screenHeight)/scale;
local offset = 4*px;
self.Background:ClearAllPoints();
self.Background:SetPoint("TOPLEFT", self, "TOPLEFT", -offset, offset);
self.Background:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", offset, -offset);
end
end
function TooltipFrame:ClearLines()
if self.numLines == 0 then return end;
self.numLines = 0;
self.numCols = 1;
self.numFontStrings = 0;
self.dataInstanceID = nil;
self.fontChanged = false;
self.grid = {};
self.useGridLayout = false;
if self.fontStrings then
for _, fontString in pairs(self.fontStrings) do
fontString:Hide();
fontString:SetText(nil);
end
end
self:SetAlternateTooltipFunc(nil);
end
function TooltipFrame:SetOwner(owner, anchor, offsetX, offsetY)
if self.Init then
self:Init();
end
self.owner = owner;
anchor = anchor or "ANCHOR_BOTTOM";
offsetX = offsetX or 0;
offsetY = offsetY or 0;
self:ClearAllPoints();
self:SetPoint("BOTTOMLEFT", owner, "TOPRIGHT", offsetX, offsetY);
end
function TooltipFrame:SetLineFont(fontString, sizeIndex)
if fontString.sizeIndex ~= sizeIndex then
fontString.sizeIndex = sizeIndex;
if sizeIndex == 1 then
fontString:SetFontObject(FONT_LARGE);
elseif sizeIndex == 2 then
fontString:SetFontObject(FONT_MEDIUM);
elseif sizeIndex == 3 then
fontString:SetFontObject(FONT_SMALL);
end
return true
end
end
function TooltipFrame:SetLineAlignment(fontString, alignIndex)
if fontString.alignIndex ~= alignIndex then
fontString.alignIndex = alignIndex;
if alignIndex == 1 then
fontString:SetJustifyH("LEFT");
elseif alignIndex == 2 then
fontString:SetJustifyH("CENTER");
elseif alignIndex == 3 then
fontString:SetJustifyH("RIGHT");
end
return true
end
end
function TooltipFrame:AcquireFontString()
local n = self.numFontStrings + 1;
self.numFontStrings = n;
if not self.fontStrings[n] then
self.fontStrings[n] = self.Content:CreateFontString(nil, "ARTWORK", FONT_MEDIUM);
self.fontStrings[n]:SetSpacing(SPACING_INTERNAL);
self.fontStrings[n].sizeIndex = 2;
self.fontStrings[n].alignIndex = 1;
self.fontStrings[n]:SetJustifyV("MIDDLE");
end
return self.fontStrings[n]
end
function TooltipFrame:AddText(text, r, g, b, wrapText, offsetY, sizeIndex, alignIndex)
r = r or 1;
g = g or 1;
b = b or 1;
wrapText = (wrapText == true) or false;
offsetY = offsetY or -SPACING_NEW_LINE;
sizeIndex = sizeIndex or 2;
alignIndex = alignIndex or 1;
local fs = self:AcquireFontString();
local fontChanged = self:SetLineFont(fs, sizeIndex);
if fontChanged then
self.fontChanged = true;
end
self:SetLineAlignment(fs, alignIndex);
fs:ClearAllPoints();
fs:SetPoint("TOPLEFT", self.Content, "TOPLEFT", 0, 0);
fs:SetWidth(FONTSTRING_MAX_WIDTH);
fs:SetText(text);
fs:SetTextColor(r, g, b, 1);
fs:Show();
fs.inGrid = nil;
return fs
end
function TooltipFrame:SetGridLine(row, col, text, r, g, b, sizeIndex, alignIndex)
if not text then return end
if self.grid[row] and self.grid[row][col] then
self.grid[row][col]:SetText("(Occupied)")
return
end
local fs = self:AddText(text, r, g, b, nil, nil, sizeIndex, alignIndex);
fs.inGrid = true;
if not self.grid[row] then
self.grid[row] = {};
end
self.grid[row][col] = fs;
if row > self.numLines then
self.numLines = row;
end
if col > self.numCols then
self.numCols = col;
end
self.useGridLayout = true;
end
function TooltipFrame:AddLeftLine(text, r, g, b, wrapText, offsetY, sizeIndex)
--This will start a new line
if not text then return end
local n = self.numLines + 1;
self.numLines = n;
local alignIndex = 1;
local fs = self:AddText(text, r, g, b, wrapText, offsetY, sizeIndex, alignIndex);
if not self.grid[n] then
self.grid[n] = {};
end
self.grid[n][1] = fs;
end
function TooltipFrame:AddCenterLine(text, r, g, b, wrapText, offsetY, sizeIndex)
--This will also start a new line
--Align to the center
if not text then return end
local n = self.numLines + 1;
self.numLines = n;
local alignIndex = 2;
local fs = self:AddText(text, r, g, b, wrapText, offsetY, sizeIndex, alignIndex);
if not self.grid[n] then
self.grid[n] = {};
end
self.grid[n][1] = fs;
end
function TooltipFrame:AddRightLine(text, r, g, b, wrapText, offsetY, sizeIndex)
--Right line must come in pairs with a LeftLine
--This will NOT start a new line
if not text then return end
local alignIndex = 3;
local fs = self:AddText(text, r, g, b, wrapText, offsetY, sizeIndex, alignIndex);
local n = self.numLines;
if not self.grid[n] then
self.grid[n] = {};
end
self.grid[n][2] = fs;
self.numCols = 2;
end
function TooltipFrame:AddBlankLine()
local n = self.numLines + 1;
self.numLines = n;
self.grid[n] = {
gap = SPACING_NEW_LINE,
};
end
function TooltipFrame:ProcessInfo(info)
self.lastInfo = info;
if not info then
return false
end
local tooltipData;
if info.getterArgs then
tooltipData = C_TooltipInfo[info.getterName](unpack(info.getterArgs));
else
tooltipData = C_TooltipInfo[info.getterName]();
end
self:ClearLines();
self:SetScript("OnUpdate", nil);
local success = self:ProcessTooltipData(tooltipData);
if success then
self:Show();
else
self:Hide();
end
end
local OVERRIDE_COLORS = {
["ffa335ee"] = 4,
["ff0070dd"] = 3,
};
local function GenerateMoneyText(rawCopper)
--Price
local text;
local gold = floor(rawCopper / 10000);
local silver = floor((rawCopper - gold * 10000) / 100);
local copper = floor(rawCopper - gold * 10000 - silver * 100);
local iconOffsetX = 2;
local goldText, silverText, copperText;
if copper > 0 then
copperText = string.format("%s|TInterface/AddOns/Narcissus/Art/Tooltip/Coins:0:0:%s:-%s:128:32:0:32:0:32|t", copper, iconOffsetX, SPACING_INTERNAL);
text = copperText;
end
if gold ~= 0 or silver ~= 0 then
silverText = string.format("%s|TInterface/AddOns/Narcissus/Art/Tooltip/Coins:0:0:%s:-%s:128:32:32:64:0:32|t", silver, iconOffsetX, SPACING_INTERNAL);
if gold > 0 then
goldText = string.format("%s|TInterface/AddOns/Narcissus/Art/Tooltip/Coins:0:0:%s:-%s:128:32:64:96:0:32|t", gold, iconOffsetX, SPACING_INTERNAL);
if text then
text = goldText.." "..silverText.." "..text;
elseif silver == 0 then
text = goldText;
else
text = goldText.." "..silverText;
end
else
if text then
text = silverText.." "..text;
else
text = silverText;
end
end
end
return text
end
function TooltipFrame:ProcessTooltipData(tooltipData)
if not (tooltipData and tooltipData.lines) then
return false
end
self.dataInstanceID = tooltipData.dataInstanceID;
self:RegisterEvent("TOOLTIP_DATA_UPDATE");
local leftText, leftColor, wrapText, rightText, leftOffset;
local r, g, b;
for i, lineData in ipairs(tooltipData.lines) do
leftText = lineData.leftText;
leftColor = lineData.leftColor or NORMAL_FONT_COLOR;
rightText = lineData.rightText;
wrapText = lineData.wrapText or false;
leftOffset = lineData.leftOffset;
if leftText then
if leftText == " "then
--A whole blank line is too tall, so we change its height
self:AddBlankLine();
else
if i == 1 then
local hex = leftColor:GenerateHexColor();
if OVERRIDE_COLORS[hex] then
leftColor = GetItemQualityColor( OVERRIDE_COLORS[hex] );
end
end
if lineData.type ~= LINE_TYPE_PRICE then
r, g, b = leftColor:GetRGB();
self:AddLeftLine(leftText, r, g, b, wrapText, nil, (i == 1 and 1) or 2);
elseif lineData.price and lineData.price > 0 then
local cointText = GenerateMoneyText(lineData.price);
if cointText then
leftText = SELL_PRICE_TEXT .. cointText;
--self:AddBlankLine();
self:AddLeftLine(leftText, 1, 1, 1, false, nil, 2);
end
end
end
end
end
return true
end
function TooltipFrame:Layout()
local textWidth, textHeight, lineWidth;
local totalHeight = 0;
local maxLineWidth = 0;
local maxLineHeight = 0;
local grid = self.grid;
local fs;
local ref = self.Content;
local colMaxWidths, rowOffsetYs;
local numCols = self.numCols;
local useGridLayout = self.useGridLayout;
if useGridLayout then
colMaxWidths = {};
rowOffsetYs = {};
for i = 1, numCols do
colMaxWidths[i] = 0;
end
end
for row = 1, self.numLines do
if grid[row] then
lineWidth = 0;
maxLineHeight = 0;
if grid[row].gap then
--Positive value increase the distance between lines
totalHeight = totalHeight + grid[row].gap;
end
for col = 1, numCols do
fs = grid[row][col];
if fs then
textWidth = fs:GetWrappedWidth();
textHeight = fs:GetHeight();
if col == 1 then
lineWidth = textWidth;
else
lineWidth = lineWidth + FONTSTRING_MIN_GAP + textWidth;
end
if lineWidth > maxLineWidth then
maxLineWidth = lineWidth;
end
if textHeight > maxLineHeight then
maxLineHeight = textHeight;
end
if useGridLayout and textWidth > colMaxWidths[col] and fs.inGrid then
colMaxWidths[col] = textWidth;
end
end
end
if row ~= 1 then
totalHeight = totalHeight + SPACING_NEW_LINE;
end
totalHeight = Round(totalHeight);
if useGridLayout then
rowOffsetYs[row] = -totalHeight;
else
for col = 1, numCols do
fs = grid[row][col];
if fs then
fs:ClearAllPoints();
if fs.alignIndex == 2 then
fs:SetPoint("TOP", ref, "TOP", 0, -totalHeight);
elseif fs.alignIndex == 3 then
fs:SetPoint("TOPRIGHT", ref, "TOPRIGHT", 0, -totalHeight);
else
fs:SetPoint("TOPLEFT", ref, "TOPLEFT", 0, -totalHeight);
end
end
end
end
totalHeight = totalHeight + maxLineHeight;
totalHeight = Round(totalHeight);
end
end
if useGridLayout then
local offsetX, offsetY;
for row = 1, self.numLines do
offsetX = 0;
offsetY = rowOffsetYs[row];
for col = 1, numCols do
fs = grid[row][col];
textWidth = colMaxWidths[col] + 1;
if fs then
fs:ClearAllPoints();
if fs.alignIndex == 2 then
if fs.inGrid then
fs:SetPoint("TOPLEFT", ref, "TOPLEFT", offsetX, offsetY);
fs:SetWidth(textWidth);
else
fs:SetPoint("TOP", ref, "TOP", offsetX, offsetY);
end
elseif fs.alignIndex == 3 then
if col == numCols then
fs:SetPoint("TOPRIGHT", ref, "TOPRIGHT", 0, offsetY);
else
fs:SetPoint("TOPLEFT", ref, "TOPLEFT", offsetX, offsetY);
fs:SetWidth(textWidth);
end
else
fs:SetPoint("TOPLEFT", ref, "TOPLEFT", offsetX, offsetY);
end
end
offsetX = offsetX + colMaxWidths[col] + FONTSTRING_MIN_GAP;
end
end
maxLineWidth = 0;
for col = 1, numCols do
maxLineWidth = maxLineWidth + colMaxWidths[col];
if col > 1 then
maxLineWidth = maxLineWidth + FONTSTRING_MIN_GAP;
end
end
end
local fullWidth = Round(maxLineWidth) + 2*TOOLTIP_PADDING;
local fullHeight = Round(totalHeight) + 2*TOOLTIP_PADDING;
self:SetSize(fullWidth, fullHeight);
end
local function SharedTooltip_OnUpdate_Layout(self, elapsed)
self:SetScript("OnUpdate", nil);
self:Layout();
end
function TooltipFrame:LayoutNextUpdate()
self:SetScript("OnUpdate", SharedTooltip_OnUpdate_Layout);
end
function TooltipFrame:Show()
if self.fontChanged or self.useGridLayout then
--fontString width will take one frame to change
self:LayoutNextUpdate();
else
self:Layout();
end
self:ShowFrame();
local scale = UIParent:GetEffectiveScale();
self:SetScale(scale);
self:UpdatePixel(scale);
end
function TooltipFrame:Hide()
self:HideFrame();
self:ClearAllPoints();
self:SetScript("OnUpdate", nil);
self:ClearLines();
end
local function SharedTooltip_OnModifierStateChanged(key, down)
--Show extra/alternate info when ALT pressed
if key == "LALT" or key == "RALT" then
if down == 1 then
local newMode = not TooltipFrame.alternateMode;
TooltipFrame.alternateMode = newMode;
if TooltipFrame.alternateTooltipFunc then
TooltipFrame.alternateTooltipFunc(TooltipFrame, newMode);
end
end
end
end
function TooltipFrame:OnEvent(event, ...)
if event == "TOOLTIP_DATA_UPDATE" then
local dataInstanceID = ...
if dataInstanceID and dataInstanceID == self.dataInstanceID then
self:ProcessInfo(self.lastInfo);
end
elseif event == "MODIFIER_STATE_CHANGED" then
SharedTooltip_OnModifierStateChanged(...)
end
end
TooltipFrame:SetScript("OnEvent", TooltipFrame.OnEvent);
TooltipFrame:SetScript("OnHide", function(self)
self:UnregisterEvent("TOOLTIP_DATA_UPDATE");
self:UnregisterEvent("MODIFIER_STATE_CHANGED");
self.lastInfo = nil;
end);
function TooltipFrame:SetAlternateTooltipFunc(setupFunc)
TooltipFrame.alternateTooltipFunc = setupFunc;
if setupFunc then
self:RegisterEvent("MODIFIER_STATE_CHANGED");
else
self:UnregisterEvent("MODIFIER_STATE_CHANGED");
end
end
do
--Emulate the default GameTooltip
--Code from Interface/SharedXML/Tooltip/TooltipDataHandler.lua
local function AddTooltipDataAccessor(handler, accessor, getterName)
handler[accessor] = function(self, ...)
local tooltipInfo = {
getterName = getterName,
getterArgs = { ... };
};
return self:ProcessInfo(tooltipInfo);
end
end
local accessors = {
SetItemByID = "GetItemByID",
SetCurrencyByID = "GetCurrencyByID",
};
local handler = TooltipFrame;
for accessor, getterName in pairs(accessors) do
AddTooltipDataAccessor(handler, accessor, getterName);
end
function TooltipFrame:SetText(text, r, g, b, wrapText)
self:AddLeftLine(text, r, g, b, wrapText);
end
function TooltipFrame:AddLine(text, r, g, b, wrapText)
self:AddLeftLine(text, r, g, b, wrapText);
end
function TooltipFrame:AddDoubleLine(leftText, rightText, leftR, leftG, leftB, rightR, rightG, rightB)
self:AddLeftLine(leftText, leftR, leftG, leftB, false);
self:AddRightLine(rightText, rightR, rightG, rightB, false);
end
end