initial commit

This commit is contained in:
Gitea
2020-11-13 14:13:12 -05:00
commit 05df49ff60
368 changed files with 128754 additions and 0 deletions

View File

@@ -0,0 +1,270 @@
-- ------------------------------------------------------------------------------ --
-- TradeSkillMaster --
-- https://tradeskillmaster.com --
-- All Rights Reserved - Detailed license information included with addon. --
-- ------------------------------------------------------------------------------ --
local _, TSM = ...
local Builder = TSM.Init("Service.ItemTooltipClasses.Builder")
local L = TSM.Include("Locale").GetTable()
local TempTable = TSM.Include("Util.TempTable")
local Math = TSM.Include("Util.Math")
local Money = TSM.Include("Util.Money")
local ItemString = TSM.Include("Util.ItemString")
local Theme = TSM.Include("Util.Theme")
local ItemInfo = TSM.Include("Service.ItemInfo")
local LibTSMClass = TSM.Include("LibTSMClass")
local TooltipBuilder = LibTSMClass.DefineClass("TooltipBuilder")
local private = {}
local TOOLTIP_CACHE_TIME = 5
local LINE_PART_SEP = "\t"
local DISABLED_TINT = -30
-- ============================================================================
-- TooltipBuilder - Class Meta Methods
-- ============================================================================
function Builder.Create()
return TooltipBuilder()
end
-- ============================================================================
-- TooltipBuilder - Class Meta Methods
-- ============================================================================
function TooltipBuilder.__init(self)
self._lines = {}
self._lineColors = {}
self._level = 0
self._levelNumLines = {}
self._levelHasHeading = {}
self._lastUpdate = 0
self._modifier = nil
self._inCombat = false
self._itemString = nil
self._quantity = nil
self._disabled = nil
end
-- ============================================================================
-- TooltipBuilder - Public Methods
-- ============================================================================
function TooltipBuilder.ApplyValueColor(self, text)
local color = Theme.GetColor("TEXT")
if self._disabled then
color = color:GetTint(DISABLED_TINT)
end
return color:ColorText(text)
end
function TooltipBuilder.FormatMoney(self, value, color)
color = color or Theme.GetColor("TEXT")
if self._disabled then
color = color:GetTint(DISABLED_TINT)
end
if not value then
return self:ApplyValueColor("---")
end
return Money.ToString(value, color:GetTextColorPrefix(), TSM.db.global.tooltipOptions.tooltipPriceFormat == "icon" and "OPT_ICON" or nil, self._disabled and "OPT_DISABLE" or nil)
end
function TooltipBuilder.AddLine(self, lineLeft, lineRight, color)
if lineRight then
lineLeft = strjoin(LINE_PART_SEP, lineLeft, lineRight)
end
local line = strrep(" ", self._level)..lineLeft
color = color or Theme.GetColor("INDICATOR_ALT")
if self._disabled then
color = color:GetTint(DISABLED_TINT)
end
assert(not self._lineColors[line] or self._lineColors[line] == color)
self._lineColors[line] = color
tinsert(self._lines, line)
self._levelNumLines[self._level] = self._levelNumLines[self._level] + 1
end
function TooltipBuilder.AddTextLine(self, label, text, ...)
if select("#", ...) == 0 then
self:AddLine(label, self:ApplyValueColor(text))
else
self:AddLine(label, self:ApplyValueColor(text).." ("..self:_TextsToStr(...)..")")
end
end
function TooltipBuilder.AddValueLine(self, label, ...)
self:AddLine(label, self:_ValuesToStr(...))
end
function TooltipBuilder.AddItemValueLine(self, label, value)
if self._quantity > 1 then
label = label.." x"..self._quantity
value = value * self._quantity
end
self:AddLine(label, self:FormatMoney(value))
end
function TooltipBuilder.AddQuantityValueLine(self, label, quantity, ...)
self:AddLine(label, self:ApplyValueColor(quantity).." ("..self:_ValuesToStr(...)..")")
end
function TooltipBuilder.AddSubItemValueLine(self, itemString, value, multiplier, matRate, minAmount, maxAmount)
local name = ItemInfo.GetName(itemString)
local color = ItemInfo.GetQualityColor(itemString)
if not name or not color then
return
end
multiplier = Math.Round(multiplier * self._quantity, 0.001)
matRate = matRate and matRate * 100
matRate = matRate and matRate.."% " or ""
local range = (minAmount and maxAmount) and (minAmount ~= maxAmount and "|cffffff00 ["..minAmount.."-"..maxAmount.."]|r" or "|cffffff00 ["..minAmount.."]|r") or ""
value = value and (value * multiplier) or nil
self:AddLine(color..matRate..name.." x"..multiplier.."|r"..range, self:FormatMoney(value))
end
function TooltipBuilder.StartSection(self, headingTextLeft, headingTextRight)
if self._level == 0 then
headingTextLeft = Theme.GetColor("INDICATOR"):ColorText(headingTextLeft)
end
if headingTextRight then
self:AddLine(headingTextLeft, headingTextRight)
elseif headingTextLeft then
self:AddLine(headingTextLeft)
end
self._level = self._level + 1
self._levelHasHeading[self._level] = headingTextLeft and true or false
self._levelNumLines[self._level] = 0
end
function TooltipBuilder.EndSection(self)
if self._levelNumLines[self._level] == 0 and self._levelHasHeading[self._level] then
-- remove the previous heading line
local headingLine = tremove(self._lines)
assert(headingLine)
self._lineColors[headingLine] = nil
self._levelNumLines[self._level - 1] = self._levelNumLines[self._level - 1] - 1
end
self._level = self._level - 1
assert(self._level >= 0)
end
function TooltipBuilder.SetDisabled(self, disabled)
self._disabled = disabled
end
function TooltipBuilder.GetNumLines(self)
return #self._lines
end
function TooltipBuilder.GetLine(self, index)
local line = self._lines[index]
local left, right = strsplit(LINE_PART_SEP, line)
return left, right, self._lineColors[line]
end
-- ============================================================================
-- TooltipBuilder - Private Methods
-- ============================================================================
function TooltipBuilder._GetModifierHash(self)
return (IsShiftKeyDown() and 4 or 0) + (IsAltKeyDown() and 2 or 0) + (IsControlKeyDown() and 1 or 0)
end
function TooltipBuilder._IsCached(self, itemString, quantity)
if itemString == ItemString.GetPlaceholder() then
return false
end
if self:_GetModifierHash() ~= self._modifier then
return false
end
if self._itemString ~= itemString or self._quantity ~= quantity then
return false
end
if GetTime() - self._lastUpdate >= TOOLTIP_CACHE_TIME then
return false
end
if InCombatLockdown() ~= self._inCombat then
return false
end
return true
end
function TooltipBuilder._Prepare(self, itemString, quantity)
if self:_IsCached(itemString, quantity) then
-- have the lines cached already
return true
end
wipe(self._lines)
wipe(self._lineColors)
wipe(self._levelNumLines)
wipe(self._levelHasHeading)
self._level = 0
self._levelNumLines[self._level] = 0
self._lastUpdate = GetTime()
self._modifier = self:_GetModifierHash()
self._inCombat = InCombatLockdown()
self._itemString = itemString
self._quantity = quantity
self._disabled = false
if InCombatLockdown() and itemString ~= ItemString.GetPlaceholder() then
self:AddLine(L["Can't load TSM tooltip while in combat"])
return true
else
return false
end
end
function TooltipBuilder._IsEmpty(self)
assert(self._level == 0)
return #self._lines == 0
end
function TooltipBuilder._LineIterator(self)
return private.LineIteratorHelper, self, 0
end
function TooltipBuilder._TextsToStr(self, ...)
local valueParts = TempTable.Acquire(...)
for i = 1, #valueParts do
valueParts[i] = self:ApplyValueColor(valueParts[i])
end
local result = table.concat(valueParts, " / ")
TempTable.Release(valueParts)
return result
end
function TooltipBuilder._ValuesToStr(self, ...)
-- can't just build the table with the vararg as there may be nils
local valueParts = TempTable.Acquire()
for i = 1, select("#", ...) do
local value = select(i, ...)
valueParts[i] = self:FormatMoney(value)
end
local result = table.concat(valueParts, " / ")
TempTable.Release(valueParts)
return result
end
-- ============================================================================
-- Private Helper Functions
-- ============================================================================
function private.LineIteratorHelper(self, index)
index = index + 1
if index > #self._lines then
return
end
return index, self:GetLine(index)
end

View File

@@ -0,0 +1,114 @@
-- ------------------------------------------------------------------------------ --
-- TradeSkillMaster --
-- https://tradeskillmaster.com --
-- All Rights Reserved - Detailed license information included with addon. --
-- ------------------------------------------------------------------------------ --
local _, TSM = ...
local ExtraTip = TSM.Init("Service.ItemTooltipClasses.ExtraTip")
local private = {
numExtraTips = 0,
}
-- ============================================================================
-- Module Functions
-- ============================================================================
function ExtraTip.Create(tooltip)
return private.CreateExtraTip(tooltip)
end
-- ============================================================================
-- Extra Tip Methods
-- ============================================================================
local EXTRA_TIP_METHODS = {
Attach = function(self, tooltip)
self:SetOwner(tooltip, "ANCHOR_PRESERVE")
self.anchorFrame = nil
self:OnUpdate()
end,
Show = function(self)
self.anchorFrame = nil
GameTooltip.Show(self)
local bottom = self:GetBottom()
if bottom and self:GetParent():IsShown() then
self.isTop = bottom <= 0
end
self:OnUpdate()
local numLines = self:NumLines()
local changedLines = self.changedLines or 0
if changedLines >= numLines then return end
for i = changedLines + 1, numLines do
local left, right = self.Left[i], self.Right[i]
local font = i == 1 and GameTooltipHeader or GameFontNormal
local r, g, b, a = left:GetTextColor()
left:SetFontObject(font)
left:SetTextColor(r, g, b, a)
r, g, b, a = right:GetTextColor()
right:SetFontObject(font)
right:SetTextColor(r, g, b, a)
end
self.changedLines = numLines
GameTooltip.Show(self)
self:OnUpdate()
end,
OnUpdate = function(self)
local tooltip = self:GetParent()
local anchorFrame = private.GetExtraTipFrame(tooltip, tooltip:GetChildren()) or tooltip
if anchorFrame ~= self.anchorFrame then
self:ClearAllPoints()
if self.isTop then
self:SetPoint("BOTTOM", anchorFrame, "TOP")
else
self:SetPoint("TOP", anchorFrame, "BOTTOM")
end
self.anchorFrame = anchorFrame
end
end,
}
local LINE_METATABLE = {
__index = function(t, k)
local v = _G[t.name..k]
rawset(t, k, v)
return v
end,
}
-- ============================================================================
-- Private Helper Functions
-- ============================================================================
function private.CreateExtraTip(tooltip)
private.numExtraTips = private.numExtraTips + 1
local extraTip = CreateFrame("GameTooltip", "TSMExtraTip"..private.numExtraTips, tooltip, "GameTooltipTemplate")
extraTip:SetClampedToScreen(true)
for name, func in pairs(EXTRA_TIP_METHODS) do
extraTip[name] = func
end
extraTip.Left = setmetatable({name = extraTip:GetName().."TextLeft"}, LINE_METATABLE)
extraTip.Right = setmetatable({name = extraTip:GetName().."TextRight"}, LINE_METATABLE)
return extraTip
end
function private.GetExtraTipFrame(tooltip, ...)
for i = 1, select('#', ...) do
local frame = select(i, ...)
if frame.InitLines and frame:GetParent() == tooltip and frame:IsVisible() then
return frame
end
end
end

View File

@@ -0,0 +1,320 @@
-- ------------------------------------------------------------------------------ --
-- TradeSkillMaster --
-- https://tradeskillmaster.com --
-- All Rights Reserved - Detailed license information included with addon. --
-- ------------------------------------------------------------------------------ --
local _, TSM = ...
local Wrapper = TSM.Init("Service.ItemTooltipClasses.Wrapper")
local ExtraTip = TSM.Include("Service.ItemTooltipClasses.ExtraTip")
local Builder = TSM.Include("Service.ItemTooltipClasses.Builder")
local ItemInfo = TSM.Include("Service.ItemInfo")
local Settings = TSM.Include("Service.Settings")
local ItemString = TSM.Include("Util.ItemString")
local private = {
builder = nil,
settings = nil,
tooltipRegistry = {},
hookedBattlepetGlobal = nil,
tooltipMethodPrehooks = nil,
tooltipMethodPosthooks = {},
lastMailTooltipUpdate = nil,
lastMailTooltipIndex = nil,
populateFunc = nil,
}
-- ============================================================================
-- Module Loading
-- ============================================================================
Wrapper:OnSettingsLoad(function()
private.builder = Builder.Create()
private.settings = Settings.NewView()
:AddKey("global", "tooltipOptions", "embeddedTooltip")
:AddKey("global", "tooltipOptions", "enabled")
:AddKey("global", "tooltipOptions", "tooltipShowModifier")
private.RegisterTooltip(GameTooltip)
private.RegisterTooltip(ItemRefTooltip)
if not TSM.IsWowClassic() then
private.RegisterTooltip(BattlePetTooltip)
private.RegisterTooltip(FloatingBattlePetTooltip)
end
local orig = OpenMailAttachment_OnEnter
OpenMailAttachment_OnEnter = function(self, index)
private.lastMailTooltipUpdate = private.lastMailTooltipUpdate or 0
if private.lastMailTooltipIndex ~= index or private.lastMailTooltipUpdate + 0.1 < GetTime() then
private.lastMailTooltipUpdate = GetTime()
private.lastMailTooltipIndex = index
orig(self, index)
end
end
end)
-- ============================================================================
-- Module Functions
-- ============================================================================
function Wrapper.SetPopulateFunction(func)
assert(type(func) == "function" and not private.populateFunc)
private.populateFunc = func
end
-- ============================================================================
-- Private Helper Functions
-- ============================================================================
function private.RegisterTooltip(tooltip)
local reg = {}
reg.extraTip = ExtraTip.Create(tooltip)
private.tooltipRegistry[tooltip] = reg
if private.IsBattlePetTooltip(tooltip) then
if not private.hookedBattlepetGlobal then
private.hookedBattlepetGlobal = true
hooksecurefunc("BattlePetTooltipTemplate_SetBattlePet", private.OnTooltipSetBattlePet)
hooksecurefunc("BattlePetToolTip_Show", private.OnBattlePetTooltipShow)
end
tooltip:HookScript("OnHide", private.OnTooltipCleared)
else
local scriptHooks = {
OnTooltipSetItem = private.OnTooltipSetItem,
OnTooltipCleared = private.OnTooltipCleared
}
for script, prehook in pairs(scriptHooks) do
tooltip:HookScript(script, prehook)
end
for method, prehook in pairs(private.tooltipMethodPrehooks) do
local posthook = private.tooltipMethodPosthooks[method]
local orig = tooltip[method]
tooltip[method] = function(...)
prehook(...)
local a, b, c, d, e, f, g, h, i, j, k = orig(...)
posthook(...)
return a, b, c, d, e, f, g, h, i, j, k
end
end
end
end
function private.IsBattlePetTooltip(tooltip)
if TSM.IsWowClassic() then
return false
end
return tooltip == BattlePetTooltip or tooltip == FloatingBattlePetTooltip
end
function private.OnTooltipSetItem(tooltip)
local reg = private.tooltipRegistry[tooltip]
if reg.hasItem then
return
end
tooltip:Show()
local testName, item = tooltip:GetItem()
if not item then
item = reg.item
elseif testName == "" then
-- this is likely a case where :GetItem() is broken for recipes - detect and try to fix it
if strmatch(item, "item:([0-9]*):") == "" then
item = reg.item
end
end
if not item then
return
end
private.SetTooltipItem(tooltip, item)
end
function private.OnTooltipSetBattlePet(tooltip, data)
local reg = private.tooltipRegistry[tooltip]
if reg.hasItem then
private.OnTooltipCleared(tooltip)
end
local link = reg.item
if not link then
-- extract values from data
local speciesID = data.speciesID
local level = data.level
local maxHealth = data.maxHealth
local power = data.power
local speed = data.speed
local battlePetID = data.battlePetID or "0x0000000000000000"
local name = data.name
local customName = data.customName
local breedQuality = data.breedQuality
local colorCode = breedQuality == -1 and NORMAL_FONT_COLOR_CODE or (ITEM_QUALITY_COLORS[breedQuality] or ITEM_QUALITY_COLORS[0]).hex
link = format("%s|Hbattlepet:%d:%d:%d:%d:%d:%d:%s|h[%s]|h|r", colorCode, speciesID, level, breedQuality, maxHealth, power, speed, battlePetID, customName or name)
end
private.SetTooltipItem(tooltip, link)
end
function private.OnTooltipCleared(tooltip)
local reg = private.tooltipRegistry[tooltip]
if reg.ignoreOnCleared then return end
reg.extraTipUsed = nil
reg.minWidth = 0
reg.quantity = nil
reg.hasItem = nil
reg.item = nil
reg.extraTip:Hide()
reg.extraTip.minWidth = 0
reg.extraTip.isTop = nil
end
function private.OnBattlePetTooltipShow()
local reg = private.tooltipRegistry[BattlePetTooltip]
reg.extraTip:Show()
end
function private.SetTooltipItem(tooltip, link)
local itemString = ItemString.Get(link)
if not private.IsEnabled() or not itemString then
return
end
local reg = private.tooltipRegistry[tooltip]
local quantity = max(IsShiftKeyDown() and reg.quantity or 1, 1)
local isCached = private.builder:_Prepare(itemString, quantity)
if not isCached then
-- populate all the lines
private.populateFunc(private.builder, itemString)
end
if private.builder:_IsEmpty() then
return
end
reg.hasItem = true
local useExtraTip = private.IsBattlePetTooltip(tooltip) or not private.settings.embeddedTooltip
-- setup the extra tip if necessary
if useExtraTip then
reg.extraTip:Attach(tooltip)
local r, g, b = GetItemQualityColor(ItemInfo.GetQuality(link) or 0)
reg.extraTip:AddLine(ItemInfo.GetName(link), r, g, b)
end
-- add all the lines
local targetTip = useExtraTip and reg.extraTip or tooltip
targetTip:AddLine(" ")
for _, left, right, lineColor in private.builder:_LineIterator() do
local r, g, b = lineColor:GetFractionalRGBA()
if right then
targetTip:AddDoubleLine(left, right, r, g, b, r, g, b)
else
targetTip:AddLine(left, r, g, b)
end
end
-- show the tooltip / extra tip as necessary
if not private.IsBattlePetTooltip(tooltip) then
tooltip:Show()
end
if useExtraTip then
reg.extraTip:Show()
end
end
function private.IsEnabled()
if not private.settings.enabled then
return false
elseif private.settings.tooltipShowModifier == "alt" and not IsAltKeyDown() then
return false
elseif private.settings.tooltipShowModifier == "ctrl" and not IsControlKeyDown() then
return false
end
return true
end
-- ============================================================================
-- Hook Setup Code
-- ============================================================================
do
local function PreHookHelper(self, quantityFunc, quantityOffset, ...)
private.OnTooltipCleared(self)
local reg = private.tooltipRegistry[self]
reg.ignoreOnCleared = true
if type(quantityFunc) == "number" then
reg.quantity = quantityFunc
else
reg.quantity = select(quantityOffset, quantityFunc(...))
end
return reg
end
private.tooltipMethodPrehooks = {
SetQuestItem = function(self, ...) PreHookHelper(self, GetQuestItemInfo, 3, ...) end,
SetQuestLogItem = function(self, type, ...)
local quantityFunc = type == "choice" and GetQuestLogChoiceInfo or GetQuestLogRewardInfo
PreHookHelper(self, quantityFunc, 3, ...)
end,
SetRecipeReagentItem = function(self, ...)
local reg = PreHookHelper(self, C_TradeSkillUI.GetRecipeReagentInfo, 3, ...)
reg.item = C_TradeSkillUI.GetRecipeReagentItemLink(...)
end,
SetRecipeResultItem = function(self, ...)
private.OnTooltipCleared(self)
local reg = private.tooltipRegistry[self]
reg.ignoreOnCleared = true
local lNum, hNum = C_TradeSkillUI.GetRecipeNumItemsProduced(...)
-- the quantity can be a range, so use a quantity of 1 if so
reg.quantity = lNum == hNum and lNum or 1
end,
SetBagItem = function(self, ...) PreHookHelper(self, GetContainerItemInfo, 2, ...) end,
SetGuildBankItem = function(self, ...)
local reg = PreHookHelper(self, GetGuildBankItemInfo, 2, ...)
reg.item = GetGuildBankItemLink(...)
end,
SetVoidItem = function(self, ...) PreHookHelper(self, 1) end,
SetVoidDepositItem = function(self, ...) PreHookHelper(self, 1) end,
SetVoidWithdrawalItem = function(self, ...) PreHookHelper(self, 1) end,
SetInventoryItem = function(self, ...) PreHookHelper(self, GetInventoryItemCount, 1, ...) end,
SetMerchantItem = function(self, ...)
local reg = PreHookHelper(self, GetMerchantItemInfo, 4, ...)
reg.item = GetMerchantItemLink(...)
end,
SetMerchantCostItem = function(self, ...) PreHookHelper(self, GetMerchantItemCostItem, 2, ...) end,
SetBuybackItem = function(self, ...) PreHookHelper(self, GetBuybackItemInfo, 4, ...) end,
SetAuctionItem = function(self, ...)
local reg = PreHookHelper(self, GetAuctionItemInfo, 3, ...)
reg.item = GetAuctionItemLink(...)
end,
SetAuctionSellItem = function(self, ...) PreHookHelper(self, GetAuctionSellItemInfo, 3, ...) end,
SetInboxItem = function(self, index) PreHookHelper(self, GetInboxItem, 4, index, 1) end,
SetSendMailItem = function(self, ...) PreHookHelper(self, GetSendMailItem, 4, ...) end,
SetLootItem = function(self, ...) PreHookHelper(self, GetLootSlotInfo, 3, ...) end,
SetLootRollItem = function(self, ...) PreHookHelper(self, GetLootRollItemInfo, 3, ...) end,
SetTradePlayerItem = function(self, ...) PreHookHelper(self, GetTradePlayerItemInfo, 3, ...) end,
SetTradeTargetItem = function(self, ...) PreHookHelper(self, GetTradeTargetItemInfo, 3, ...) end,
SetHyperlink = function(self, link)
local reg = private.tooltipRegistry[self]
private.OnTooltipCleared(self)
reg.ignoreOnCleared = true
reg.item = link
end,
}
-- populate all the posthooks
local function TooltipMethodPostHook(self)
private.tooltipRegistry[self].ignoreOnCleared = nil
end
for funcName in pairs(private.tooltipMethodPrehooks) do
private.tooltipMethodPosthooks[funcName] = TooltipMethodPostHook
end
-- SetHyperlink is special
private.tooltipMethodPosthooks.SetHyperlink = function(self)
local reg = private.tooltipRegistry[self]
reg.ignoreOnCleared = nil
end
end