Files
ShaguTweaks-ClassicAPI/compat-dragonflightui.lua
T

372 lines
11 KiB
Lua

-- Compatibility bridge for DragonflightUI-Reforged.
--
-- DragonflightUI-Reforged currently keeps a hard-coded list of ShaguTweaks
-- modules and also references a couple of legacy module names directly.
-- This bridge keeps those legacy lookups harmless and exposes newer
-- ShaguTweaks / Extras ClassicAPI modules to DragonflightUI's ShaguTweaks tab
-- without modifying DragonflightUI itself.
-- Keep this file completely dormant when DragonflightUI-Reforged is not
-- installed/enabled. This avoids creating compatibility tables, metatables or
-- a polling frame for normal ShaguTweaks users.
local function DragonflightUIEnabled()
if DFRL then return true end
if not GetNumAddOns or not GetAddOnInfo then return false end
for i = 1, GetNumAddOns() do
local name, _, _, enabled = GetAddOnInfo(i)
if name == "DragonflightUI-Reforged" then
return enabled and true or false
end
end
return false
end
if not DragonflightUIEnabled() then return end
local T = ShaguTweaks.T
local mods = ShaguTweaks.mods
ShaguTweaks.DragonflightUICompat = ShaguTweaks.DragonflightUICompat or {}
local Compat = ShaguTweaks.DragonflightUICompat
Compat.active = false
Compat.coreInjected = Compat.coreInjected or 0
Compat.extrasInjected = Compat.extrasInjected or 0
Compat.extrasRecovered = Compat.extrasRecovered or 0
Compat.legacyHits = Compat.legacyHits or 0
-- DragonflightUI still indexes the old Extras energy-tick module name
-- directly. Keep a virtual entry so the legacy lookup stays harmless without
-- adding a fake module to ShaguTweaks' own list or SavedVariables.
local function LegacyStub()
return {
enabled = nil,
expansions = { ["vanilla"] = true },
enable = function() end,
disable = function() end,
dragonflightCompatStub = true,
}
end
local legacy = {}
legacy[T["Show Energy Ticks"]] = LegacyStub()
local mt = getmetatable(mods) or {}
if not mt.dragonflightUICompat then
local previousIndex = mt.__index
mt.__index = function(tab, key)
local value = legacy[key]
if value then
Compat.legacyHits = Compat.legacyHits + 1
return value
end
if type(previousIndex) == "function" then
return previousIndex(tab, key)
elseif type(previousIndex) == "table" then
return previousIndex[key]
end
end
mt.dragonflightUICompat = true
setmetatable(mods, mt)
end
-- Snapshot the modules belonging to ShaguTweaks itself. Extras loads later
-- because it depends on ShaguTweaks.
Compat.coreTitles = Compat.coreTitles or {}
for title in pairs(mods) do
Compat.coreTitles[title] = true
end
-- Modules DragonflightUI explicitly suppresses because it replaces the same
-- pieces of UI. Don't re-add them to its settings page.
local blocked = {}
local function Block(name)
blocked[name] = true
blocked[T[name]] = true
end
Block("Hide Errors")
Block("Darkened UI")
Block("Hide Gryphons")
Block("MiniMap Clock")
Block("MiniMap Tweaks")
Block("MiniMap Square")
Block("Movable Unit Frames")
-- DragonflightUI replaces the stock player/target bars and provides its own
-- health/power text. Keep the real ShaguTweaks modules disabled there even
-- though Real Health Numbers is once again a genuine core module.
Block("Real Health Numbers")
Block("Unit Frame Big Health")
Block("Reduced Actionbar Size")
Block("Unit Frame Class Colors")
Block("Unit Frame Health Colors")
Block("Unit Frame Class Portraits")
Block("Enemy Castbars")
Block("Show Bags")
Block("Show Micro Menu")
Block("Reagent Counter")
Block("Show Energy Ticks")
Block("Floating Actionbar")
Block("Dragonflight Gryphons")
Block("Center Vertical Actionbar")
-- New name for the old Extras energy-tick feature DragonflightUI suppresses.
Block("Unit Frame Energy & Mana Tick")
local function CleanCategory(category)
category = tostring(category or "Other")
category = string.gsub(category, "|c%x%x%x%x%x%x%x%x", "")
category = string.gsub(category, "|r", "")
category = string.gsub(category, "^Extras ClassicAPI:%s*", "")
category = string.gsub(category, "^Mods:%s*", "")
if category == "" then category = "Other" end
return category
end
local function CategoryIndexes(target)
local indexes = {}
for _, data in pairs(target or {}) do
local category = data[5] or "Other"
local index = tonumber(data[6]) or 0
if not indexes[category] or index > indexes[category] then
indexes[category] = index
end
end
return indexes
end
local function AddModules(target, predicate)
if not target then return 0 end
local titles = {}
for title, mod in pairs(mods) do
if type(mod) == "table"
and not target[title]
and not blocked[title]
and predicate(title, mod) then
table.insert(titles, title)
end
end
table.sort(titles)
local indexes = CategoryIndexes(target)
local added = 0
for _, title in pairs(titles) do
local mod = mods[title]
local category = CleanCategory(mod.category)
indexes[category] = (indexes[category] or 0) + 1
target[title] = {
true,
"checkbox",
nil,
nil,
category,
indexes[category],
mod.description or title,
nil,
nil,
}
added = added + 1
end
return added
end
local function IsExtrasModule(title, mod)
local category = tostring(mod.category or "")
return string.find(category, "Extras ClassicAPI:", 1, true) ~= nil
end
local function ExtrasLoaded()
return IsAddOnLoaded
and IsAddOnLoaded("ShaguTweaks-extras")
and true or false
end
-- DragonflightUI has two separate ADDON_LOADED listeners: one records that
-- ShaguTweaks-extras is loaded and another builds its Shagu metadata. On some
-- clients the metadata listener can run first, permanently leaving
-- shaguExtras/shaguExtrasData unset even though addon2 becomes true moments
-- later. Recover that state from WoW's authoritative addon-loaded flag before
-- DragonflightUI builds the settings page.
function Compat:RecoverExtrasMetadata(forceLoaded)
if not DFRL or not DFRL.gui then return false end
if not forceLoaded and not ExtrasLoaded() then return false end
DFRL.addon2 = true
if not DFRL.gui.shaguExtrasData then
DFRL.gui.shaguExtrasData = {}
self.extrasRecovered = self.extrasRecovered + 1
end
DFRL.gui.shaguExtras = true
return true
end
function Compat:InjectCore()
if not DFRL or not DFRL.gui or not DFRL.gui.shaguCoreData then return 0 end
local added = AddModules(DFRL.gui.shaguCoreData, function(title)
return self.coreTitles[title] == true
end)
self.coreInjected = self.coreInjected + added
return added
end
function Compat:InjectExtras()
if not DFRL or not DFRL.gui or not DFRL.gui.shaguExtrasData then return 0 end
local added = AddModules(DFRL.gui.shaguExtrasData, IsExtrasModule)
self.extrasInjected = self.extrasInjected + added
return added
end
function Compat:GrowDragonflightPanel()
if not DFRL or not DFRL.gui or not DFRL.gui.Base then return end
local base = DFRL.gui.Base
local panel = base.scrollChildren and base.scrollChildren[5]
if not panel then return end
local items = 0
local categories = 0
local function Count(data, prefix)
if not data then return end
local seen = {}
for _, value in pairs(data) do
items = items + 1
local category = tostring(value[5] or "Other")
local key = prefix .. category
if not seen[key] then
seen[key] = true
categories = categories + 1
end
end
end
Count(DFRL.gui.shaguCoreData, "core:")
Count(DFRL.gui.shaguExtrasData, "extras:")
-- Mirrors DragonflightUI's current spacing with some extra room at the end.
local required = 500 + (items * 45) + (categories * 90)
if panel:GetHeight() < required then
panel:SetHeight(required)
end
end
function Compat:InjectAll()
self:InjectCore()
self:InjectExtras()
self:GrowDragonflightPanel()
end
function Compat:InstallDragonflightHooks(forceExtrasLoaded)
if not DFRL or not DFRL.gui then return end
self.active = true
-- Repair DragonflightUI's Extras metadata race before any injection attempt.
-- During ADDON_LOADED itself, Vanilla can still report IsAddOnLoaded() as
-- false until the event finishes, so trust the event when explicitly told
-- that ShaguTweaks-extras has just completed loading.
self:RecoverExtrasMetadata(forceExtrasLoaded)
-- DragonflightUI builds the ShaguTweaks page from its config-ready callback.
-- Insert our metadata immediately before that page is drawn.
if DFRL.shagu
and DFRL.shagu.configCallbacks
and not DFRL.shagu.configReady
and not self.configCallbackHooked then
table.insert(DFRL.shagu.configCallbacks, 1, function()
Compat:InjectAll()
end)
self.configCallbackHooked = true
end
-- Extras can be loaded after the core page already exists. DragonflightUI
-- exposes this builder, so inject the new Extras entries before it appends
-- that section.
if DFRL.gui.shaguBuildExtras
and not self.extrasBuilderHooked then
local originalBuildExtras = DFRL.gui.shaguBuildExtras
DFRL.gui.shaguBuildExtras = function()
Compat:InjectExtras()
Compat:GrowDragonflightPanel()
return originalBuildExtras()
end
self.extrasBuilderHooked = true
end
-- Also covers cases where DragonflightUI already prepared its metadata.
self:InjectAll()
end
-- DragonflightUI normally loads before ShaguTweaks, but keep the bridge
-- tolerant of other addon load orders as well.
local watcher = CreateFrame("Frame")
watcher:RegisterEvent("ADDON_LOADED")
watcher:RegisterEvent("VARIABLES_LOADED")
watcher.elapsed = 0
watcher.retries = 0
watcher:SetScript("OnEvent", function()
if event == "ADDON_LOADED" then
if arg1 == "ShaguTweaks-extras" then
-- All Extras files have finished loading at this point, but
-- IsAddOnLoaded("ShaguTweaks-extras") may not flip to true until after
-- this event returns. Force the recovery now, before VARIABLES_LOADED
-- lets DragonflightUI build its ShaguTweaks page.
Compat:InstallDragonflightHooks(true)
elseif arg1 == "DragonflightUI-Reforged" then
Compat:InstallDragonflightHooks()
end
elseif event == "VARIABLES_LOADED" then
Compat:InstallDragonflightHooks()
end
end)
watcher:SetScript("OnUpdate", function()
this.elapsed = this.elapsed + arg1
if this.elapsed < 0.25 then return end
this.elapsed = 0
this.retries = this.retries + 1
Compat:InstallDragonflightHooks()
-- Newer DragonflightUI versions don't expose configCallbacks or
-- shaguBuildExtras, so the old hook-based stop condition could poll forever.
-- Stop once the core metadata exists and Extras is either absent or repaired.
local coreReady = DFRL and DFRL.gui and DFRL.gui.shaguCoreData
local extrasReady = not ExtrasLoaded()
or (DFRL and DFRL.gui and DFRL.gui.shaguExtrasData)
if Compat.active and coreReady and extrasReady then
this:SetScript("OnUpdate", nil)
elseif this.retries >= 40 then
-- Never leave a permanent compatibility poll running on unusual loadouts.
this:SetScript("OnUpdate", nil)
end
end)
Compat:InstallDragonflightHooks()