Skip to content

Commit 3cb87bc

Browse files
author
mergetest
committed
Text Designer: mirrors sync at the source + group-element categories (T1 fixes)
Two in-game probe failures fixed. (1) Wrong glyph size: copying font values back out of the rendered FontString loses what SafeSetFont does on the way in (pixel-perfect adjustments), so the cover rendered at a different size. The mirror is now fed inside updateOne from the SAME resolved inputs as the real element - same SafeSetFont args, same SafeText value - making the cover glyph-identical by construction (and eliminating the GetText round-trip entirely). (2) Health text never mirrored: the user's health display is a 'group' element, which stamps category 'all'; the matcher now treats a group as belonging to every category its items contain. A mirror whose element stops matching (group items re-typed) hides rather than lingering stale. Known limit documented: inline |c codes embedded by group items keep their embedded colour under the cover.
1 parent 10fb47d commit 3cb87bc

1 file changed

Lines changed: 91 additions & 75 deletions

File tree

TextDesigner/Render.lua

Lines changed: 91 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ local function applyAppearance(fs, frame, elem, globalDefaults)
170170
fs._useClassColor = false
171171
fs:SetTextColor(app.color.r, app.color.g, app.color.b, app.color.a or 1)
172172
end
173+
return app -- resolved appearance, reused by the AD mirror sync
173174
end
174175

175176
-- Applies position to the FontString (separate from appearance so we can
@@ -181,6 +182,90 @@ local function applyPosition(fs, frame, elem, fontStringsById, enabledById)
181182
elem.offsetX or 0, elem.offsetY or 0)
182183
end
183184

185+
-- ============================================================
186+
-- AURA DESIGNER MIRRORS (12.1 name/health text colour-by-cover)
187+
-- A mirror is a DUPLICATE FontString parented to a region the Aura Designer
188+
-- owns (an aura-slot child whose visibility Blizzard drives from the tracked
189+
-- aura's secret presence). Aura present -> the slot shows -> the coloured
190+
-- cover renders over the real text; absent -> the cover hides and the real
191+
-- text shows. Recolour-by-cover: the REAL FontString is never touched, so
192+
-- nothing is gated on a secret.
193+
-- SYNC-AT-SOURCE: the mirror is fed inside updateOne from the SAME resolved
194+
-- inputs as the real FontString — DF:SafeSetFont with the same font/size/
195+
-- outline (so pixel-perfect adjustments match, glyph-identical cover) and
196+
-- SetText with the same SafeText value (secrets pass through unread; never
197+
-- compare/measure/string-op mirror text). Position tracks via SetAllPoints
198+
-- on the real FontString (render-side, secret rects fine). Colour is the one
199+
-- property never copied — the mirror keeps the AD override colour.
200+
-- KNOWN LIMIT: inline |c colour codes embedded by group items keep their
201+
-- embedded colour under the cover (SetTextColor can't override them, and
202+
-- stripping possibly-secret text is forbidden).
203+
-- ============================================================
204+
205+
-- Does this element belong to a mirror category? Single elements match via
206+
-- CONTENT_HINTS; a "group" element matches when ANY of its items does (the
207+
-- category set a group renders is its items' union).
208+
local function mirrorCategoryMatches(elem, category)
209+
if CONTENT_HINTS[elem.contentType] == category then return true end
210+
if elem.contentType == "group" and type(elem.items) == "table" then
211+
for _, raw in ipairs(elem.items) do
212+
local ct = type(raw) == "table" and (raw.contentType or raw.type) or raw
213+
if CONTENT_HINTS[ct] == category then return true end
214+
end
215+
end
216+
return false
217+
end
218+
219+
-- Feed one rendered element to every matching mirror (called from updateOne
220+
-- with the element's resolved appearance + the exact safe text just set on
221+
-- the real FontString). A registered-but-no-longer-matching mirror (the user
222+
-- re-typed a group's items) is hidden so it can't linger as a stale cover.
223+
local function mirrorElement(frame, elem, fs, app, safeText)
224+
for _, reg in pairs(frame._tdMirrors) do
225+
local m = reg.byId[elem.id]
226+
if mirrorCategoryMatches(elem, reg.category) then
227+
if not m then
228+
m = reg.parent:CreateFontString(nil, "OVERLAY")
229+
m:SetAllPoints(fs) -- anchors track the real text render-side
230+
local c = reg.color
231+
m:SetTextColor(c.r, c.g, c.b, c.a or 1)
232+
reg.byId[elem.id] = m
233+
end
234+
-- Same setter, same resolved inputs -> identical rendering.
235+
DF:SafeSetFont(m, fontPath(app.font), app.fontSize, app.outline)
236+
-- Same SafeText value the real FontString just received (secret
237+
-- passthrough; pcall + warn-once purely as a degrade path).
238+
local ok = pcall(m.SetText, m, safeText)
239+
if ok then
240+
m:SetShown(fs:IsShown())
241+
else
242+
m:Hide()
243+
if not Render._mirrorWarned then
244+
Render._mirrorWarned = true
245+
DF:DebugWarn("TD", "mirror SetText failed — AD text colour degraded off")
246+
end
247+
end
248+
elseif m then
249+
m:Hide()
250+
end
251+
end
252+
end
253+
254+
-- Post-render visibility pass: mirrors follow their real FontStrings' shown
255+
-- state (covers disabled/deleted elements and the master-off path — every
256+
-- hide funnels through here). Content/appearance are owned by mirrorElement.
257+
local function syncMirrors(frame)
258+
local regs = frame._tdMirrors
259+
if not regs then return end
260+
local fss = frame._tdFontStrings
261+
for _, reg in pairs(regs) do
262+
for id, m in pairs(reg.byId) do
263+
local fs = fss and fss[id]
264+
if fs then m:SetShown(fs:IsShown()) else m:Hide() end
265+
end
266+
end
267+
end
268+
184269
-- ============================================================
185270
-- RENDER ONE ELEMENT
186271
-- ============================================================
@@ -196,7 +281,7 @@ local function updateOne(frame, elem, source, globalDefaults, enabledById)
196281
return
197282
end
198283
local fs = acquireFontString(frame, elem)
199-
applyAppearance(fs, frame, elem, globalDefaults)
284+
local app = applyAppearance(fs, frame, elem, globalDefaults)
200285
applyPosition(fs, frame, elem, frame._tdFontStrings, enabledById)
201286
-- Apply class color if requested
202287
if fs._useClassColor then
@@ -218,81 +303,12 @@ local function updateOne(frame, elem, source, globalDefaults, enabledById)
218303
fs:SetTextColor(ov.r, ov.g, ov.b, ov.a or 1)
219304
end
220305
local text = getResolver():Resolve(elem, source)
221-
fs:SetText(getMS().SafeText(text))
306+
local safeText = getMS().SafeText(text)
307+
fs:SetText(safeText)
222308
fs:Show()
223-
end
224-
225-
-- ============================================================
226-
-- AURA DESIGNER MIRRORS (12.1 name/health text colour-by-cover)
227-
-- A mirror is a DUPLICATE FontString parented to a region the Aura Designer
228-
-- owns (an aura-slot child whose visibility Blizzard drives from the tracked
229-
-- aura's secret presence). TD keeps every mirror glyph-identical to its real
230-
-- FontString — font, anchors, justify, shadow, text — EXCEPT the colour,
231-
-- which stays the AD override colour. Aura present -> the slot shows -> the
232-
-- coloured cover renders over the real text; absent -> the cover hides and
233-
-- the real text shows. Recolour-by-cover: the REAL FontString is never
234-
-- touched, so nothing is gated on a secret.
235-
-- SECRET RULES: the text copy passes the real FontString's content through
236-
-- GetText -> SetText without inspecting it (FontStrings accept secret
237-
-- values; only Lua string ops on them error). Never compare, measure or
238-
-- string-op mirror text. Everything else copied is a plain getter.
239-
-- ============================================================
240-
241-
local function syncOneMirror(reg, id, fs)
242-
local m = reg.byId[id]
243-
if not m then
244-
m = reg.parent:CreateFontString(nil, "OVERLAY")
245-
m:SetAllPoints(fs) -- anchors track the real text render-side (secret rects OK)
246-
local c = reg.color
247-
m:SetTextColor(c.r, c.g, c.b, c.a or 1)
248-
reg.byId[id] = m
249-
end
250-
-- Font / justify / shadow: plain getters, never secret.
251-
pcall(function()
252-
local path, size, flags = fs:GetFont()
253-
if path then m:SetFont(path, size, flags) end
254-
m:SetJustifyH(fs:GetJustifyH())
255-
m:SetJustifyV(fs:GetJustifyV())
256-
local sr, sg, sb, sa = fs:GetShadowColor()
257-
if sr then m:SetShadowColor(sr, sg, sb, sa) end
258-
local sx, sy = fs:GetShadowOffset()
259-
if sx then m:SetShadowOffset(sx, sy) end
260-
end)
261-
-- Text: SECRET PASSTHROUGH — hand the value straight across, never inspect
262-
-- it. pcall + warn-once: if a client build ever rejects the round-trip we
263-
-- degrade to a hidden mirror instead of an error storm.
264-
local ok = pcall(function() m:SetText(fs:GetText()) end)
265-
if ok then
266-
m:SetShown(fs:IsShown())
267-
else
268-
m:Hide()
269-
if not Render._mirrorWarned then
270-
Render._mirrorWarned = true
271-
DF:DebugWarn("TD", "mirror text passthrough failed (GetText->SetText) — AD text colour degraded off")
272-
end
273-
end
274-
end
275-
276-
-- Post-render pass: sync every registered mirror to its real FontString and
277-
-- hide mirrors whose element vanished. Runs at the END of UpdateFrame (after
278-
-- every hide in the pass has landed), on the master-off early-out, and from
279-
-- EnableMirrors. Zero work when no mirrors are registered.
280-
local function syncMirrors(frame)
281-
local regs = frame._tdMirrors
282-
if not regs then return end
283-
local fss = frame._tdFontStrings
284-
for _, reg in pairs(regs) do
285-
for id, m in pairs(reg.byId) do
286-
local fs = fss and fss[id]
287-
if not fs or fs._tdCategory ~= reg.category then m:Hide() end
288-
end
289-
if fss then
290-
for id, fs in pairs(fss) do
291-
if fs._tdCategory == reg.category then
292-
syncOneMirror(reg, id, fs)
293-
end
294-
end
295-
end
309+
-- AD mirrors: feed the coloured cover the SAME resolved appearance + text.
310+
if frame._tdMirrors then
311+
mirrorElement(frame, elem, fs, app, safeText)
296312
end
297313
end
298314

0 commit comments

Comments
 (0)