From c5ddc6c313877c6f63ab449983b87c05787bc2ab Mon Sep 17 00:00:00 2001 From: Brues <5278969+brues-code@users.noreply.github.com> Date: Wed, 9 Sep 2026 23:22:16 -0500 Subject: [PATCH] Publish resolved macro actions to ClassicAPI's C_Macro.SetMacroDisplay Hands ClassicAPI the action this addon resolved for each macro, so the icon, tooltip, cooldown sweep, range and usable state come from the client instead of from replaced action-bar globals. That also reaches the drag cursor and the macro window grid, which Lua cannot touch, and lets the client answer range for a macro slot directly rather than borrowing a proxy slot. - CleveRoids.useClassicAPIDisplay feature-detects C_Macro.SetMacroDisplay rather than checking CLASSIC_API_VERSION, which reports a dev sentinel while the API is unreleased. ClassicAPIMacroDisplay tells ClassicAPI we drive macro display, which it otherwise stands down from when it sees this addon. - macro.actions.macroID back-references the Blizzard index, since the update loop walks actions objects rather than macros. nil for SuperMacro macros, which have no index and so cannot be published. - PublishDisplay sends false, not nothing, when no action matched: that claims the macro and shows the question mark, where silence would hand it back to ClassicAPI's own #showtooltip parser. - PublishAllDisplays runs once the addon is ready and after every re-parse, since ClassicAPI re-evaluates nothing for us. It covers macros that aren't on a bar, which is what keeps the macro window grid correct. Deferred past load because SetMacroDisplay returns false until the player is in the world. - ReleaseDisplays hands every macro back on DisableAddon. The per-slot ACTIONBAR_SLOT_CHANGED fan-out is kept for the non-integrated path. Publishing repaints every slot holding the macro through the client's own notifier, so it is redundant when the API is present, but removing it outright would leave buttons never repainting on a ClassicAPI build without the API. --- Core.lua | 87 ++++++++++++++++++++++++++++++++-- Extensions/Tooltip/Generic.lua | 4 +- Init.lua | 14 ++++++ 3 files changed, 100 insertions(+), 5 deletions(-) diff --git a/Core.lua b/Core.lua index 4930659..2bb068c 100644 --- a/Core.lua +++ b/Core.lua @@ -531,6 +531,12 @@ function CleveRoids.DisableAddon(reason) -- Mark state CleveRoids.disabled = true + -- Hand macro display back to ClassicAPI's own #showtooltip parser before we go + -- quiet, or every claimed macro would keep the last value we published. + if CleveRoids.ReleaseDisplays then + CleveRoids.ReleaseDisplays() + end + -- Stop main frame activity if CleveRoids.Frame then if CleveRoids.Frame.UnregisterAllEvents then @@ -1087,6 +1093,59 @@ function CleveRoids.TestForActiveAction(actions) return changed end +-------------------------------------------------------------------------------- +-- ClassicAPI macro display +-------------------------------------------------------------------------------- + +-- The value to hand C_Macro.SetMacroDisplay for an active action. The nested hop +-- mirrors the {MacroName} fallback the GameTooltip.SetAction override used: when +-- `action` is a macro reference, `action.macro` holds the inner macro's +-- #showtooltip action info (see CreateActionInfo), and `action.action` is the +-- literal "{Name}" text, which names no spell or item. +local function DisplayValueFor(action) + if not action then return nil end + if type(action.macro) == "table" and action.macro.action then + return action.macro.action + end + return action.action +end + +-- Publish one macro's resolved action. Nothing is re-evaluated for us, so this must +-- be called whenever the answer changes; the published value stands until replaced. +-- +-- `false` is deliberate when there is no active action: it means "this macro is mine, +-- nothing matched" and shows the question mark. Skipping the call instead would hand +-- the macro back to ClassicAPI's own #showtooltip parser. +function CleveRoids.PublishDisplay(actions) + if not CleveRoids.useClassicAPIDisplay then return end + local macroID = actions and actions.macroID + if not macroID then return end -- SuperMacro macros have no Blizzard index + C_Macro.SetMacroDisplay(macroID, DisplayValueFor(actions.active) or false) +end + +-- Republish every parsed macro. Used after login and after a re-parse, since +-- ClassicAPI re-evaluates nothing for us. Covers macros that aren't on a bar too, +-- which is what keeps the macro window grid's icons correct. +function CleveRoids.PublishAllDisplays() + if not CleveRoids.useClassicAPIDisplay then return end + for _, macro in pairs(CleveRoids.Macros) do + if type(macro) == "table" and macro.actions and macro.actions.macroID then + CleveRoids.TestForActiveAction(macro.actions) + CleveRoids.PublishDisplay(macro.actions) + end + end +end + +-- Hand every macro back to ClassicAPI's own parser and stop claiming ownership. +function CleveRoids.ReleaseDisplays() + if not CleveRoids.useClassicAPIDisplay then return end + for i = 1, 36 do + C_Macro.SetMacroDisplay(i, nil) + end + CleveRoids.ClassicAPIMacroDisplay = false + CleveRoids.useClassicAPIDisplay = false +end + -- PERFORMANCE: Static buffer references for hot path local _actionsToSlotsBuffer = CleveRoids._actionsToSlotsBuffer local _slotsBuffer = CleveRoids._slotsBuffer @@ -1130,10 +1189,16 @@ function CleveRoids.TestForAllActiveActions() local slots = actionsToSlots[actions] local stateChanged = CleveRoids.TestForActiveAction(actions) if stateChanged then - -- Send event to ALL slots that use this macro - local count = slots._count - for j = 1, count do - CleveRoids.SendEventForAction(slots[j], "ACTIONBAR_SLOT_CHANGED", slots[j]) + if CleveRoids.useClassicAPIDisplay then + -- Publishing repaints every slot holding this macro through the + -- client's own notifier, so the per-slot fan-out below is redundant. + CleveRoids.PublishDisplay(actions) + else + -- Send event to ALL slots that use this macro + local count = slots._count + for j = 1, count do + CleveRoids.SendEventForAction(slots[j], "ACTIONBAR_SLOT_CHANGED", slots[j]) + end end end -- Clear for reuse (reset count and clear buffer reference) @@ -1179,6 +1244,7 @@ function CleveRoids.GetAction(slot) if macro then actions = macro.actions CleveRoids.TestForActiveAction(actions) + CleveRoids.PublishDisplay(actions) CleveRoids.Actions[slot] = actions CleveRoids.SendEventForAction(slot, "ACTIONBAR_SLOT_CHANGED", slot) return actions @@ -1750,6 +1816,10 @@ local function BuildMacro(cacheKey, macroID, name, texture, body) body = body, actions = {}, } + -- Back-reference so the update loop, which walks `actions` objects rather than + -- macros, can name the macro to C_Macro.SetMacroDisplay. nil for SuperMacro + -- macros, which have no Blizzard index and so cannot be published. + macro.actions.macroID = macroID macro.actions.list = {} -- build a list of testable actions for the macro @@ -3975,6 +4045,12 @@ function CleveRoids.OnUpdate(self) CR._suppressActionHandlers = false CR.TestForAllActiveActions() + + -- First publish of the session. Deferred to here rather than done at + -- load because SetMacroDisplay returns false until the player exists + -- in the world. + CR.PublishAllDisplays() + CR.lastUpdate = time -- FIX: Force refresh ALL Blizzard action buttons after initialization @@ -5699,6 +5775,9 @@ function CleveRoids.RebuildMacros() -- updates out to Blizzard/pfUI/Bongos buttons. if CleveRoids.ready then CleveRoids.IndexActionBars() + -- Macros were re-parsed above, so every published value is stale. Covers + -- macros that aren't on a bar, which IndexActionBars never visits. + CleveRoids.PublishAllDisplays() end if CleveRoidMacros.realtime == 0 then diff --git a/Extensions/Tooltip/Generic.lua b/Extensions/Tooltip/Generic.lua index fbffc4e..7ef91cb 100644 --- a/Extensions/Tooltip/Generic.lua +++ b/Extensions/Tooltip/Generic.lua @@ -423,7 +423,9 @@ function CleveRoids.IndexActionSlot(slot) end end end - CleveRoids.TestForActiveAction(CleveRoids.GetAction(slot)) + local actions = CleveRoids.GetAction(slot) + CleveRoids.TestForActiveAction(actions) + CleveRoids.PublishDisplay(actions) CleveRoids.SendEventForAction(slot, "ACTIONBAR_SLOT_CHANGED", slot) end diff --git a/Init.lua b/Init.lua index 0cdd1bd..1848fb9 100644 --- a/Init.lua +++ b/Init.lua @@ -24,6 +24,20 @@ CleveRoids.hasSuperwow = SetAutoloot and true or false CleveRoids.hasTurtle = (type(_G.TURTLE_WOW_VERSION) ~= "nil") CleveRoids.supported = CleveRoids.hasTurtle +-- ClassicAPI macro display (C_Macro.SetMacroDisplay): hand ClassicAPI the resolved +-- action per macro instead of replacing the action-bar globals in Lua, so the icon, +-- tooltip, cooldown sweep, range and usable state all come from the client -- including +-- the drag cursor and the macro window grid, which Lua cannot reach. +-- +-- Feature-detect rather than version-check: the API is unreleased, so +-- CLASSIC_API_VERSION reports the dev sentinel. ClassicAPI stands down from macro +-- display entirely when it sees this addon loaded; ClassicAPIMacroDisplay is what +-- tells it we drive it instead. A fork that leaves the flag unset keeps the old +-- behavior -- both must never drive the same buttons. +CleveRoids.useClassicAPIDisplay = + (type(C_Macro) == "table" and C_Macro.SetMacroDisplay ~= nil) and true or false +CleveRoids.ClassicAPIMacroDisplay = CleveRoids.useClassicAPIDisplay + CleveRoids.ParsedMsg = {} CleveRoids.Items = {} CleveRoids.Spells = {}