804 lines
26 KiB
Lua
804 lines
26 KiB
Lua
-- Modules/Tracker/TrackerManager.lua
|
|
-- Generic tracker manager for category-driven tracker frames.
|
|
|
|
local ADDON_NAME = "HailMaryGuildTools"
|
|
local HMGT = LibStub("AceAddon-3.0"):GetAddon(ADDON_NAME)
|
|
if not HMGT then return end
|
|
|
|
local Manager = {}
|
|
HMGT.TrackerManager = Manager
|
|
|
|
Manager.frames = Manager.frames or {}
|
|
Manager.perPlayerFrames = Manager.perPlayerFrames or {}
|
|
Manager.activeOrders = Manager.activeOrders or {}
|
|
Manager.unitByPlayer = Manager.unitByPlayer or {}
|
|
Manager.anchorLayoutSignatures = Manager.anchorLayoutSignatures or {}
|
|
Manager.nextAnchorRetryAt = Manager.nextAnchorRetryAt or {}
|
|
Manager.enabled = false
|
|
Manager.visualTicker = nil
|
|
Manager.lastEntryCount = 0
|
|
Manager._shared = Manager._shared or {}
|
|
Manager._trackerCache = Manager._trackerCache or nil
|
|
Manager._trackerCacheSignature = Manager._trackerCacheSignature or nil
|
|
Manager._displaySignatures = Manager._displaySignatures or {}
|
|
Manager._layoutDirty = Manager._layoutDirty == true
|
|
|
|
local function GetTrackerFrameKey(trackerId)
|
|
return "tracker:" .. tostring(tonumber(trackerId) or 0)
|
|
end
|
|
|
|
local function GetTrackerFrameName(trackerId)
|
|
return "GenericTracker_" .. tostring(tonumber(trackerId) or 0)
|
|
end
|
|
|
|
local function GetTrackerPlayerFrameName(trackerId, playerName)
|
|
local token = tostring(playerName or "Unknown"):gsub("[^%w_]", "_")
|
|
return string.format("%s_%s", GetTrackerFrameName(trackerId), token)
|
|
end
|
|
|
|
local function ShortName(name)
|
|
if not name then
|
|
return ""
|
|
end
|
|
local short = name:match("^[^-]+")
|
|
return short or name
|
|
end
|
|
|
|
local function IsUsableAnchorFrame(frame)
|
|
return frame
|
|
and frame.IsObjectType
|
|
and (frame:IsObjectType("Frame") or frame:IsObjectType("Button"))
|
|
end
|
|
|
|
local function GetFrameUnit(frame)
|
|
if not frame then
|
|
return nil
|
|
end
|
|
local unit = frame.unit
|
|
if not unit and frame.GetAttribute then
|
|
unit = frame:GetAttribute("unit")
|
|
end
|
|
return unit
|
|
end
|
|
|
|
local function FrameMatchesUnit(frame, unitId)
|
|
if not IsUsableAnchorFrame(frame) then
|
|
return false
|
|
end
|
|
if not unitId then
|
|
return true
|
|
end
|
|
return GetFrameUnit(frame) == unitId
|
|
end
|
|
|
|
local PLAYER_FRAME_CANDIDATES = {
|
|
"PlayerFrame",
|
|
"ElvUF_Player",
|
|
"NephUI_PlayerFrame",
|
|
"NephUIPlayerFrame",
|
|
"oUF_NephUI_Player",
|
|
"SUFUnitplayer",
|
|
}
|
|
|
|
local PARTY_FRAME_PATTERNS = {
|
|
"PartyMemberFrame%d",
|
|
"CompactPartyFrameMember%d",
|
|
"ElvUF_PartyGroup1UnitButton%d",
|
|
"ElvUF_PartyUnitButton%d",
|
|
"NephUI_PartyUnitButton%d",
|
|
"NephUI_PartyFrame%d",
|
|
"NephUIPartyFrame%d",
|
|
"oUF_NephUI_PartyUnitButton%d",
|
|
"SUFUnitparty%d",
|
|
}
|
|
|
|
local unitFrameCache = {}
|
|
|
|
local function EntryNeedsVisualTicker(entry)
|
|
if type(entry) ~= "table" then
|
|
return false
|
|
end
|
|
|
|
local remaining = tonumber(entry.remaining) or 0
|
|
if remaining > 0 then
|
|
return true
|
|
end
|
|
|
|
local maxCharges = tonumber(entry.maxCharges) or 0
|
|
local currentCharges = tonumber(entry.currentCharges)
|
|
if maxCharges > 0 and currentCharges ~= nil and currentCharges < maxCharges then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
local function BuildAnchorLayoutSignature(settings, ordered, unitByPlayer)
|
|
local parts = {
|
|
settings.attachToPartyFrame == true and "attach" or "stack",
|
|
tostring(settings.partyAttachSide or "RIGHT"),
|
|
tostring(tonumber(settings.partyAttachOffsetX) or 8),
|
|
tostring(tonumber(settings.partyAttachOffsetY) or 0),
|
|
tostring(settings.showBar and "bar" or "icon"),
|
|
tostring(settings.growDirection or "DOWN"),
|
|
tostring(settings.width or 250),
|
|
tostring(settings.barHeight or 20),
|
|
tostring(settings.iconSize or 32),
|
|
tostring(settings.iconCols or 6),
|
|
tostring(settings.barSpacing or 2),
|
|
tostring(settings.locked),
|
|
tostring(settings.anchorTo or "UIParent"),
|
|
tostring(settings.anchorPoint or "TOPLEFT"),
|
|
tostring(settings.anchorRelPoint or "TOPLEFT"),
|
|
tostring(settings.anchorX or settings.posX or 0),
|
|
tostring(settings.anchorY or settings.posY or 0),
|
|
}
|
|
|
|
for _, playerName in ipairs(ordered or {}) do
|
|
parts[#parts + 1] = tostring(playerName)
|
|
parts[#parts + 1] = tostring(unitByPlayer and unitByPlayer[playerName] or "")
|
|
end
|
|
|
|
return table.concat(parts, "|")
|
|
end
|
|
|
|
local function IsGroupTracker(tracker)
|
|
return type(tracker) == "table" and tracker.trackerType == "group"
|
|
end
|
|
|
|
local function ResolveNamedUnitFrame(unitId)
|
|
if unitId == "player" then
|
|
for _, frameName in ipairs(PLAYER_FRAME_CANDIDATES) do
|
|
local frame = _G[frameName]
|
|
if FrameMatchesUnit(frame, unitId) or (frameName == "PlayerFrame" and IsUsableAnchorFrame(frame)) then
|
|
return frame
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local idx = type(unitId) == "string" and unitId:match("^party(%d+)$")
|
|
if not idx then
|
|
return nil
|
|
end
|
|
|
|
idx = tonumber(idx)
|
|
for _, pattern in ipairs(PARTY_FRAME_PATTERNS) do
|
|
local frame = _G[pattern:format(idx)]
|
|
if FrameMatchesUnit(frame, unitId) then
|
|
return frame
|
|
end
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
local function ScanUnitFrame(unitId)
|
|
local frame = EnumerateFrames()
|
|
local scanned = 0
|
|
while frame and scanned < 8000 do
|
|
if IsUsableAnchorFrame(frame) and GetFrameUnit(frame) == unitId then
|
|
HMGT:Debug("verbose", "TrackerAttach scan unit=%s scanned=%d found=true", tostring(unitId), scanned)
|
|
return frame
|
|
end
|
|
scanned = scanned + 1
|
|
frame = EnumerateFrames(frame)
|
|
end
|
|
HMGT:Debug("verbose", "TrackerAttach scan unit=%s scanned=%d found=false", tostring(unitId), scanned)
|
|
return nil
|
|
end
|
|
|
|
local function ResolveUnitAnchorFrame(unitId)
|
|
if not unitId then
|
|
return nil
|
|
end
|
|
|
|
local now = GetTime()
|
|
local cached = unitFrameCache[unitId]
|
|
if cached and now < (cached.expires or 0) then
|
|
if cached.frame and cached.frame:IsShown() then
|
|
return cached.frame
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local frame = ResolveNamedUnitFrame(unitId)
|
|
if not frame then
|
|
frame = ScanUnitFrame(unitId)
|
|
end
|
|
|
|
local expiresIn = 1.0
|
|
if frame and frame:IsShown() then
|
|
expiresIn = 10.0
|
|
end
|
|
unitFrameCache[unitId] = {
|
|
frame = frame,
|
|
expires = now + expiresIn,
|
|
}
|
|
|
|
if frame and frame:IsShown() then
|
|
return frame
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function GetGroupPlayers(tracker)
|
|
local players = {}
|
|
|
|
local ownName = HMGT:NormalizePlayerName(UnitName("player"))
|
|
local ownClass = select(2, UnitClass("player"))
|
|
local includeOwnPlayer = true
|
|
if IsGroupTracker(tracker) then
|
|
includeOwnPlayer = tracker.includeSelfFrame == true
|
|
end
|
|
if includeOwnPlayer then
|
|
players[#players + 1] = {
|
|
name = ownName,
|
|
class = ownClass,
|
|
isOwn = true,
|
|
unitId = "player",
|
|
}
|
|
end
|
|
|
|
if IsInRaid() then
|
|
for i = 1, GetNumGroupMembers() do
|
|
local unitId = "raid" .. i
|
|
local name = HMGT:NormalizePlayerName(UnitName(unitId))
|
|
local class = select(2, UnitClass(unitId))
|
|
if name and name ~= ownName then
|
|
players[#players + 1] = {
|
|
name = name,
|
|
class = class,
|
|
unitId = unitId,
|
|
}
|
|
end
|
|
end
|
|
elseif IsInGroup() then
|
|
for i = 1, GetNumGroupMembers() - 1 do
|
|
local unitId = "party" .. i
|
|
local name = HMGT:NormalizePlayerName(UnitName(unitId))
|
|
local class = select(2, UnitClass(unitId))
|
|
if name and name ~= ownName then
|
|
players[#players + 1] = {
|
|
name = name,
|
|
class = class,
|
|
unitId = unitId,
|
|
}
|
|
end
|
|
end
|
|
end
|
|
|
|
return players
|
|
end
|
|
|
|
local function GetTrackerLabel(tracker)
|
|
if type(tracker) ~= "table" then
|
|
return "Tracker"
|
|
end
|
|
|
|
local name = tostring(tracker.name or ""):gsub("^%s+", ""):gsub("%s+$", "")
|
|
local trackerId = tonumber(tracker.id) or 0
|
|
if name ~= "" then
|
|
return name
|
|
end
|
|
if trackerId > 0 then
|
|
return string.format("Tracker %d", trackerId)
|
|
end
|
|
return "Tracker"
|
|
end
|
|
|
|
local function GetTrackerSpellPool(categories)
|
|
if HMGT_SpellData and type(HMGT_SpellData.GetSpellPoolForCategories) == "function" then
|
|
return HMGT_SpellData.GetSpellPoolForCategories(categories)
|
|
end
|
|
return {}
|
|
end
|
|
|
|
local function GetTrackerSpellsForPlayer(classToken, specIndex, categories)
|
|
if HMGT_SpellData and type(HMGT_SpellData.GetSpellsForCategories) == "function" then
|
|
return HMGT_SpellData.GetSpellsForCategories(classToken, specIndex, categories)
|
|
end
|
|
return {}
|
|
end
|
|
|
|
local function CollectEntriesForPlayer(tracker, playerInfo)
|
|
local entries = {}
|
|
if type(tracker) ~= "table" or type(playerInfo) ~= "table" then
|
|
return entries
|
|
end
|
|
|
|
local playerName = playerInfo.name
|
|
if not playerName then
|
|
return entries
|
|
end
|
|
|
|
local pData = HMGT.playerData[playerName]
|
|
local classToken = pData and pData.class or playerInfo.class
|
|
if not classToken then
|
|
return entries
|
|
end
|
|
|
|
local specIndex
|
|
if playerInfo.isOwn then
|
|
specIndex = GetSpecialization()
|
|
if not specIndex or specIndex == 0 then
|
|
return entries
|
|
end
|
|
else
|
|
specIndex = pData and pData.specIndex or nil
|
|
if not specIndex or tonumber(specIndex) <= 0 then
|
|
return entries
|
|
end
|
|
end
|
|
|
|
local talents = pData and pData.talents or {}
|
|
local spells = GetTrackerSpellsForPlayer(classToken, specIndex, tracker.categories)
|
|
for _, spellEntry in ipairs(spells) do
|
|
if tracker.enabledSpells[spellEntry.spellId] ~= false then
|
|
local remaining, total, currentCharges, maxCharges = HMGT:GetCooldownInfo(playerName, spellEntry.spellId)
|
|
local effectiveCd = HMGT_SpellData.GetEffectiveCooldown(spellEntry, talents)
|
|
local isAvailabilitySpell = HMGT:IsAvailabilitySpell(spellEntry)
|
|
local include = HMGT:ShouldDisplayEntry(tracker, remaining, currentCharges, maxCharges, spellEntry)
|
|
local spellKnown = HMGT:IsTrackedSpellKnownForPlayer(playerName, spellEntry.spellId)
|
|
local hasPartialCharges = (tonumber(maxCharges) or 0) > 0
|
|
and (tonumber(currentCharges) or tonumber(maxCharges) or 0) < (tonumber(maxCharges) or 0)
|
|
local hasActiveCd = ((remaining or 0) > 0) or hasPartialCharges
|
|
|
|
if not spellKnown and not hasActiveCd then
|
|
include = false
|
|
end
|
|
if isAvailabilitySpell and not spellKnown then
|
|
include = false
|
|
end
|
|
if not playerInfo.isOwn and isAvailabilitySpell and not HMGT:HasAvailabilityState(playerName, spellEntry.spellId) then
|
|
include = false
|
|
end
|
|
|
|
if include then
|
|
entries[#entries + 1] = {
|
|
playerName = playerName,
|
|
class = classToken,
|
|
spellEntry = spellEntry,
|
|
remaining = remaining,
|
|
total = total > 0 and total or effectiveCd,
|
|
currentCharges = currentCharges,
|
|
maxCharges = maxCharges,
|
|
}
|
|
end
|
|
end
|
|
end
|
|
|
|
return entries
|
|
end
|
|
|
|
local function CopyEntriesForPreview(entries, playerName)
|
|
local copies = {}
|
|
for _, entry in ipairs(entries or {}) do
|
|
local nextEntry = {}
|
|
for key, value in pairs(entry) do
|
|
nextEntry[key] = value
|
|
end
|
|
nextEntry.playerName = playerName
|
|
copies[#copies + 1] = nextEntry
|
|
end
|
|
return copies
|
|
end
|
|
|
|
local function GetAvailablePartyPreviewUnits()
|
|
local units = {}
|
|
for index = 1, 4 do
|
|
local unitId = "party" .. index
|
|
if ResolveUnitAnchorFrame(unitId) then
|
|
units[#units + 1] = {
|
|
playerName = string.format("Party %d", index),
|
|
unitId = unitId,
|
|
}
|
|
end
|
|
end
|
|
return units
|
|
end
|
|
|
|
local function BuildPartyPreviewEntries(entries)
|
|
local byPlayer = {}
|
|
local order = {}
|
|
local unitByPlayer = {}
|
|
local previewUnits = GetAvailablePartyPreviewUnits()
|
|
|
|
for _, previewUnit in ipairs(previewUnits) do
|
|
local playerName = previewUnit.playerName
|
|
local playerEntries = CopyEntriesForPreview(entries, playerName)
|
|
if #playerEntries > 0 then
|
|
byPlayer[playerName] = playerEntries
|
|
order[#order + 1] = playerName
|
|
unitByPlayer[playerName] = previewUnit.unitId
|
|
end
|
|
end
|
|
|
|
return byPlayer, order, unitByPlayer, #order > 0
|
|
end
|
|
|
|
local function SortTrackers(trackers)
|
|
table.sort(trackers, function(a, b)
|
|
local aId = tonumber(a and a.id) or 0
|
|
local bId = tonumber(b and b.id) or 0
|
|
if aId ~= bId then
|
|
return aId < bId
|
|
end
|
|
return tostring(a and a.name or "") < tostring(b and b.name or "")
|
|
end)
|
|
return trackers
|
|
end
|
|
|
|
local function BuildTrackerCacheSignature(trackers)
|
|
local parts = { tostring(#(trackers or {})) }
|
|
for index, tracker in ipairs(trackers or {}) do
|
|
parts[#parts + 1] = tostring(index)
|
|
parts[#parts + 1] = tostring(tonumber(tracker and tracker.id) or 0)
|
|
parts[#parts + 1] = tostring(tracker and tracker.trackerType or "normal")
|
|
parts[#parts + 1] = tostring(tracker and tracker.enabled)
|
|
parts[#parts + 1] = tostring(tracker and tracker.name or "")
|
|
end
|
|
return table.concat(parts, "|")
|
|
end
|
|
|
|
local function BuildNormalDisplaySignature(frameShown, entries)
|
|
local parts = { frameShown and "1" or "0", tostring(#(entries or {})) }
|
|
for index, entry in ipairs(entries or {}) do
|
|
parts[#parts + 1] = tostring(index)
|
|
parts[#parts + 1] = tostring(entry and entry.playerName or "")
|
|
parts[#parts + 1] = tostring(entry and entry.spellEntry and entry.spellEntry.spellId or 0)
|
|
end
|
|
return table.concat(parts, "|")
|
|
end
|
|
|
|
local function BuildGroupDisplaySignature(order, byPlayer)
|
|
local parts = { tostring(#(order or {})) }
|
|
for _, playerName in ipairs(order or {}) do
|
|
parts[#parts + 1] = tostring(playerName)
|
|
parts[#parts + 1] = tostring(#((byPlayer and byPlayer[playerName]) or {}))
|
|
end
|
|
return table.concat(parts, "|")
|
|
end
|
|
|
|
Manager._shared.GetTrackerFrameKey = GetTrackerFrameKey
|
|
Manager._shared.GetTrackerFrameName = GetTrackerFrameName
|
|
Manager._shared.GetTrackerPlayerFrameName = GetTrackerPlayerFrameName
|
|
Manager._shared.ShortName = ShortName
|
|
Manager._shared.BuildAnchorLayoutSignature = BuildAnchorLayoutSignature
|
|
Manager._shared.IsGroupTracker = IsGroupTracker
|
|
Manager._shared.ResolveUnitAnchorFrame = ResolveUnitAnchorFrame
|
|
Manager._shared.GetGroupPlayers = GetGroupPlayers
|
|
Manager._shared.GetTrackerLabel = GetTrackerLabel
|
|
Manager._shared.GetTrackerSpellPool = GetTrackerSpellPool
|
|
Manager._shared.GetTrackerSpellsForPlayer = GetTrackerSpellsForPlayer
|
|
Manager._shared.CollectEntriesForPlayer = CollectEntriesForPlayer
|
|
Manager._shared.BuildPartyPreviewEntries = BuildPartyPreviewEntries
|
|
Manager._shared.EntryNeedsVisualTicker = EntryNeedsVisualTicker
|
|
Manager._shared.BuildGroupDisplaySignature = BuildGroupDisplaySignature
|
|
|
|
function Manager:GetTrackers()
|
|
local profile = HMGT and HMGT.db and HMGT.db.profile
|
|
local trackers = profile and profile.trackers or {}
|
|
local signature = BuildTrackerCacheSignature(trackers)
|
|
if self._trackerCache and self._trackerCacheSignature == signature then
|
|
return self._trackerCache
|
|
end
|
|
local result = {}
|
|
for _, tracker in ipairs(trackers) do
|
|
result[#result + 1] = tracker
|
|
end
|
|
self._trackerCache = SortTrackers(result)
|
|
self._trackerCacheSignature = signature
|
|
return self._trackerCache
|
|
end
|
|
|
|
function Manager:MarkTrackersDirty()
|
|
self._trackerCache = nil
|
|
self._trackerCacheSignature = nil
|
|
self._layoutDirty = true
|
|
end
|
|
|
|
function Manager:MarkLayoutDirty()
|
|
self._layoutDirty = true
|
|
end
|
|
|
|
function Manager:EnsureFrame(tracker)
|
|
local frameKey = GetTrackerFrameKey(tracker.id)
|
|
local frame = self.frames[frameKey]
|
|
if not frame then
|
|
frame = HMGT.TrackerFrame:CreateTrackerFrame(GetTrackerFrameName(tracker.id), tracker)
|
|
frame._hmgtTrackerId = tonumber(tracker.id) or 0
|
|
self.frames[frameKey] = frame
|
|
end
|
|
|
|
frame._settings = tracker
|
|
HMGT.TrackerFrame:SetTitle(frame, GetTrackerLabel(tracker))
|
|
HMGT.TrackerFrame:SetLocked(frame, tracker.locked)
|
|
return frame
|
|
end
|
|
|
|
function Manager:GetAnchorFrame(tracker)
|
|
if type(tracker) ~= "table" then
|
|
return nil
|
|
end
|
|
|
|
if IsGroupTracker(tracker) then
|
|
local frameKey = GetTrackerFrameKey(tracker.id)
|
|
local order = self.activeOrders[frameKey] or {}
|
|
local frames = self.perPlayerFrames[frameKey] or {}
|
|
if order[1] and frames[order[1]] and frames[order[1]]:IsShown() then
|
|
return frames[order[1]]
|
|
end
|
|
end
|
|
|
|
return self:EnsureFrame(tracker)
|
|
end
|
|
|
|
function Manager:StopVisualTicker()
|
|
if self.visualTicker then
|
|
self.visualTicker:Cancel()
|
|
self.visualTicker = nil
|
|
end
|
|
end
|
|
|
|
function Manager:SetVisualTickerEnabled(enabled)
|
|
if enabled then
|
|
if not self.visualTicker then
|
|
self.visualTicker = C_Timer.NewTicker(0.1, function()
|
|
self:RefreshVisibleVisuals()
|
|
end)
|
|
end
|
|
else
|
|
self:StopVisualTicker()
|
|
end
|
|
end
|
|
|
|
function Manager:RefreshAnchors(force)
|
|
for _, tracker in ipairs(self:GetTrackers()) do
|
|
local frameKey = GetTrackerFrameKey(tracker.id)
|
|
if IsGroupTracker(tracker) then
|
|
local anchorFrame = self.frames[frameKey]
|
|
if anchorFrame and not anchorFrame._hmgtDragging then
|
|
HMGT.TrackerFrame:ApplyAnchor(anchorFrame)
|
|
end
|
|
self:RefreshPerGroupAnchors(tracker, force)
|
|
else
|
|
local frame = self.frames[frameKey]
|
|
if frame and (force or frame:IsShown()) then
|
|
if not frame._hmgtDragging then
|
|
HMGT.TrackerFrame:ApplyAnchor(frame)
|
|
end
|
|
frame:EnableMouse(not tracker.locked)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function Manager:InvalidateAnchorLayout()
|
|
self.anchorLayoutSignatures = {}
|
|
self.nextAnchorRetryAt = {}
|
|
self:MarkLayoutDirty()
|
|
self:RefreshAnchors(true)
|
|
end
|
|
|
|
function Manager:SetAllLocked(locked)
|
|
for _, frame in pairs(self.frames) do
|
|
HMGT.TrackerFrame:SetLocked(frame, locked)
|
|
end
|
|
for _, frameSet in pairs(self.perPlayerFrames) do
|
|
for _, frame in pairs(frameSet) do
|
|
HMGT.TrackerFrame:SetLocked(frame, locked)
|
|
end
|
|
end
|
|
end
|
|
|
|
function Manager:GetAnchorableFrames()
|
|
local frames = {}
|
|
for _, tracker in ipairs(self:GetTrackers()) do
|
|
local anchorKey = HMGT.GetTrackerAnchorKey and HMGT:GetTrackerAnchorKey(tracker.id) or nil
|
|
local frame = self:GetAnchorFrame(tracker)
|
|
if anchorKey and frame then
|
|
frames[anchorKey] = frame
|
|
end
|
|
end
|
|
return frames
|
|
end
|
|
|
|
function Manager:Enable()
|
|
self.enabled = true
|
|
self:MarkTrackersDirty()
|
|
self:UpdateDisplay()
|
|
end
|
|
|
|
function Manager:Disable()
|
|
self.enabled = false
|
|
self:StopVisualTicker()
|
|
self._layoutDirty = true
|
|
for _, frame in pairs(self.frames) do
|
|
frame:Hide()
|
|
end
|
|
for frameKey in pairs(self.perPlayerFrames) do
|
|
self:HidePlayerFrames(frameKey)
|
|
end
|
|
end
|
|
|
|
function Manager:RefreshVisibleVisuals()
|
|
if not self.enabled then
|
|
self:StopVisualTicker()
|
|
return
|
|
end
|
|
|
|
local shouldTick = false
|
|
local needsFullRefresh = false
|
|
local totalEntries = 0
|
|
|
|
for _, tracker in ipairs(self:GetTrackers()) do
|
|
local frameKey = GetTrackerFrameKey(tracker.id)
|
|
if IsGroupTracker(tracker) then
|
|
local currentOrder = self.activeOrders[frameKey] or {}
|
|
if #currentOrder > 0 then
|
|
local byPlayer, order, unitByPlayer, shouldShow = self:BuildEntriesByPlayerForTracker(tracker)
|
|
if not shouldShow or #order ~= #currentOrder then
|
|
needsFullRefresh = true
|
|
else
|
|
local byPlayerFiltered = {}
|
|
for index, playerName in ipairs(order) do
|
|
if playerName ~= currentOrder[index] then
|
|
needsFullRefresh = true
|
|
break
|
|
end
|
|
local entries = byPlayer[playerName] or {}
|
|
if HMGT.FilterDisplayEntries then
|
|
entries = HMGT:FilterDisplayEntries(tracker, entries) or entries
|
|
end
|
|
if HMGT.SortDisplayEntries then
|
|
HMGT:SortDisplayEntries(entries)
|
|
end
|
|
if #entries == 0 then
|
|
needsFullRefresh = true
|
|
break
|
|
end
|
|
local frame = self.perPlayerFrames[frameKey] and self.perPlayerFrames[frameKey][playerName]
|
|
if not frame or not frame:IsShown() then
|
|
needsFullRefresh = true
|
|
break
|
|
end
|
|
HMGT.TrackerFrame:UpdateFrame(frame, entries, true)
|
|
totalEntries = totalEntries + #entries
|
|
byPlayerFiltered[playerName] = entries
|
|
for _, entry in ipairs(entries) do
|
|
if EntryNeedsVisualTicker(entry) then
|
|
shouldTick = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
local newSignature = BuildGroupDisplaySignature(currentOrder, byPlayerFiltered)
|
|
if self._displaySignatures[frameKey] ~= newSignature then
|
|
needsFullRefresh = true
|
|
end
|
|
end
|
|
end
|
|
else
|
|
local frame = self.frames[frameKey]
|
|
if frame and frame:IsShown() then
|
|
local entries, shouldShow = self:BuildEntriesForTracker(tracker)
|
|
if not shouldShow then
|
|
needsFullRefresh = true
|
|
else
|
|
if HMGT.FilterDisplayEntries then
|
|
entries = HMGT:FilterDisplayEntries(tracker, entries) or entries
|
|
end
|
|
if HMGT.SortDisplayEntries then
|
|
HMGT:SortDisplayEntries(entries)
|
|
end
|
|
if #entries == 0 then
|
|
needsFullRefresh = true
|
|
else
|
|
HMGT.TrackerFrame:UpdateFrame(frame, entries, true)
|
|
totalEntries = totalEntries + #entries
|
|
local newSignature = BuildNormalDisplaySignature(true, entries)
|
|
if self._displaySignatures[frameKey] ~= newSignature then
|
|
needsFullRefresh = true
|
|
end
|
|
for _, entry in ipairs(entries) do
|
|
if EntryNeedsVisualTicker(entry) then
|
|
shouldTick = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
self.lastEntryCount = totalEntries
|
|
self:SetVisualTickerEnabled(shouldTick)
|
|
if needsFullRefresh then
|
|
HMGT:TriggerTrackerUpdate("layout")
|
|
end
|
|
end
|
|
|
|
function Manager:UpdateDisplay()
|
|
if not self.enabled then
|
|
self:StopVisualTicker()
|
|
return
|
|
end
|
|
|
|
local trackers = self:GetTrackers()
|
|
local activeFrames = {}
|
|
local shouldTick = false
|
|
local totalEntries = 0
|
|
local layoutDirty = self._layoutDirty == true
|
|
|
|
for _, tracker in ipairs(trackers) do
|
|
local frameKey = GetTrackerFrameKey(tracker.id)
|
|
local frame = self:EnsureFrame(tracker)
|
|
|
|
if IsGroupTracker(tracker) then
|
|
frame:Hide()
|
|
local shown, entryCount, trackerShouldTick = self:UpdatePerGroupMemberTracker(tracker)
|
|
totalEntries = totalEntries + (entryCount or 0)
|
|
if trackerShouldTick then
|
|
shouldTick = true
|
|
end
|
|
if not shown then
|
|
self:HidePlayerFrames(frameKey)
|
|
end
|
|
else
|
|
self:HidePlayerFrames(frameKey)
|
|
local entries, shouldShow = self:BuildEntriesForTracker(tracker)
|
|
|
|
if shouldShow then
|
|
if HMGT.FilterDisplayEntries then
|
|
entries = HMGT:FilterDisplayEntries(tracker, entries) or entries
|
|
end
|
|
if HMGT.SortDisplayEntries then
|
|
HMGT:SortDisplayEntries(entries)
|
|
end
|
|
|
|
HMGT.TrackerFrame:UpdateFrame(frame, entries, true)
|
|
frame:Show()
|
|
frame:EnableMouse(not tracker.locked)
|
|
activeFrames[frameKey] = true
|
|
totalEntries = totalEntries + #entries
|
|
local newSignature = BuildNormalDisplaySignature(true, entries)
|
|
if self._displaySignatures[frameKey] ~= newSignature then
|
|
self._displaySignatures[frameKey] = newSignature
|
|
layoutDirty = true
|
|
end
|
|
|
|
for _, entry in ipairs(entries) do
|
|
if EntryNeedsVisualTicker(entry) then
|
|
shouldTick = true
|
|
break
|
|
end
|
|
end
|
|
else
|
|
frame:Hide()
|
|
if self._displaySignatures[frameKey] ~= "0" then
|
|
self._displaySignatures[frameKey] = "0"
|
|
layoutDirty = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
for frameKey, frame in pairs(self.frames) do
|
|
if not activeFrames[frameKey] then
|
|
if frame:IsShown() then
|
|
layoutDirty = true
|
|
end
|
|
frame:Hide()
|
|
end
|
|
end
|
|
|
|
self.lastEntryCount = totalEntries
|
|
self:SetVisualTickerEnabled(shouldTick)
|
|
if layoutDirty then
|
|
self._layoutDirty = false
|
|
self:RefreshAnchors()
|
|
end
|
|
end
|