diff --git a/ShaguTweaks.toc b/ShaguTweaks.toc index 56f4790..2c5febc 100644 --- a/ShaguTweaks.toc +++ b/ShaguTweaks.toc @@ -92,3 +92,6 @@ mods\improved-roll-frames.lua mods\improved-castbar.lua mods\smaller-errors-frame.lua mods\skip-gossip.lua + +# compatibility +compat-dragonflightui.lua diff --git a/compat-dragonflightui.lua b/compat-dragonflightui.lua new file mode 100644 index 0000000..bf751f6 --- /dev/null +++ b/compat-dragonflightui.lua @@ -0,0 +1,297 @@ +-- 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. + +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.legacyHits = Compat.legacyHits or 0 + +-- DragonflightUI still indexes these old module names directly. Keep them as +-- virtual entries so they don't appear in ShaguTweaks' own module list or +-- create SavedVariables entries. +local function LegacyStub() + return { + enabled = nil, + expansions = { ["vanilla"] = true }, + enable = function() end, + disable = function() end, + dragonflightCompatStub = true, + } +end + +local legacy = {} +legacy[T["Real Health Numbers"]] = LegacyStub() +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") +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 + +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() + if not DFRL or not DFRL.gui then return end + self.active = true + + -- 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:SetScript("OnEvent", function() + if event == "ADDON_LOADED" then + if arg1 == "DragonflightUI-Reforged" or arg1 == "ShaguTweaks-extras" 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 + Compat:InstallDragonflightHooks() + + if Compat.active and Compat.configCallbackHooked and Compat.extrasBuilderHooked then + this:SetScript("OnUpdate", nil) + end +end) + +Compat:InstallDragonflightHooks()