From 00b84ee234290914def55e82e9edbb2a96611302 Mon Sep 17 00:00:00 2001 From: Glyalith Date: Fri, 7 Aug 2026 18:29:58 -0600 Subject: [PATCH 1/2] Fix party threat border size writing to and reading the raid value The "Threat Borders" slider is drawn on the Health Bar row, so the party tab's blocking overlay lets you edit it whenever Health Bar is unsynced. threatBorderSize was filed under the Indicators party-sync section, so the write only produced a party value when Indicators was ALSO unsynced; otherwise it went to the shared raid key. File it under "healthBar" to match where its control lives. The runtime read had the same split. UpdateButton resolved the party proxy, but the two threat event handlers read db.profile directly, and the hub one resolves party buttons via ns._partyUnitToButton immediately before reading the raid value, so a party override was overwritten on every threat update. Both now resolve the same proxy chain as every other per-button read (the Extra Frames tracker uses the extra-aware form its sibling reads already use, since that pool holds no party buttons). A stored party_threatBorderSize normally implies both sections were custom, so it stays live and unchanged under the new section. The legacy showThreat -> slider migration could also write the key directly, leaving a value dormant under a synced Health Bar that would switch on here; a profile migration neutralizes only that case so no frame changes look. --- .../EllesmereUIRaidFrames.lua | 10 ++++-- EllesmereUI_Migration.lua | 31 +++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/EllesmereUIRaidFrames/EllesmereUIRaidFrames.lua b/EllesmereUIRaidFrames/EllesmereUIRaidFrames.lua index f86dcffa..afda1668 100644 --- a/EllesmereUIRaidFrames/EllesmereUIRaidFrames.lua +++ b/EllesmereUIRaidFrames/EllesmereUIRaidFrames.lua @@ -7732,7 +7732,8 @@ XF.EnsureBuilt = function(count) elseif event == "UNIT_THREAT_LIST_UPDATE" or event == "UNIT_THREAT_SITUATION_UPDATE" then local d = GetFFD(b) if d.threatFrame then - local bs = db.profile.threatBorderSize or 0 + local s = d._isExtra and ns._scaledExtraProxy or ns._scaledProfile + local bs = s.threatBorderSize or 0 if bs > 0 then local status = UnitThreatSituation(unit) if status and THREAT_ACTIVE[status] and PP then @@ -10297,7 +10298,8 @@ local function OnEvent(self, event, arg1, ...) if btn then local d = GetFFD(btn) if d.threatFrame then - local bs = db.profile.threatBorderSize or 0 + local s = d._isParty and ns._scaledPartyProxy or (d._isExtra and ns._scaledExtraProxy) or ns._scaledProfile + local bs = s.threatBorderSize or 0 if bs > 0 then local status = UnitThreatSituation(arg1) if status and THREAT_ACTIVE[status] and PP then @@ -10539,6 +10541,8 @@ do "customBgColor", "bgClassColored", "bgDarkness", "smoothBars", "healPrediction", "healPredOpacity", "healPredColor", "healthVerticalFill", + -- Drawn as "Threat Borders" on the Health Bar row, so it files here. + "threatBorderSize", }, absorbs = { "absorbStyle", "absorbOpacity", "absorbColor", "absorbEdgeMode", "showOvershield", @@ -10578,7 +10582,7 @@ do "borderBehind", "borderTextureOffset", "borderTextureOffsetY", "borderTextureShiftX", "borderTextureShiftY", "hoverBorderEnabled", "hoverBorderSize", "hoverBorderColor", "hoverBorderAlpha", - "targetBorderEnabled", "targetBorderSize", "targetBorderColor", "targetBorderAlpha", "threatBorderSize", + "targetBorderEnabled", "targetBorderSize", "targetBorderColor", "targetBorderAlpha", }, dispels = { "dispelBorderSize", "dispelOverlay", "dispelOverlayOpacity", "dispelShowAll", diff --git a/EllesmereUI_Migration.lua b/EllesmereUI_Migration.lua index 553a8134..0b960201 100644 --- a/EllesmereUI_Migration.lua +++ b/EllesmereUI_Migration.lua @@ -4356,3 +4356,34 @@ EllesmereUI.RegisterMigration({ if type(p) == "table" then p.showCastTarget = false end end, }) + +-------------------------------------------------------------------------------- +-- Raid Frames: threatBorderSize moved party-sync sections +-- +-- The "Threat Borders" slider is drawn on the Health Bar row but the key was +-- filed under the Indicators party-sync section, so editing it on the Party +-- tab wrote the shared raid value. The key now files under "healthBar", which +-- matches where its control lives. +-- +-- A stored party_threatBorderSize normally implies both sections were custom +-- (Health Bar to reach the control, Indicators to route the write), so it +-- stays live and unchanged. The exception is the legacy showThreat -> slider +-- migration, which wrote party_threatBorderSize directly: such a value could +-- be sitting dormant under a synced Health Bar and would switch on here. +-- Neutralize only that case so no party frame changes appearance. +-------------------------------------------------------------------------------- +EllesmereUI.RegisterMigration({ + id = "rf_threat_border_party_section_v1", + scope = "profile", + description = "Keep party threat borders rendering as-is after threatBorderSize moved to the Health Bar party-sync section.", + body = function(ctx) + local rf = ctx.profile.addons and ctx.profile.addons.EllesmereUIRaidFrames + if type(rf) ~= "table" or rf.party_threatBorderSize == nil then return end + local ss = type(rf.partySyncSections) == "table" and rf.partySyncSections or nil + local wasLive = ss and ss.indicators == false + local willBeLive = ss and ss.healthBar == false + if willBeLive and not wasLive and rf.party_threatBorderSize ~= rf.threatBorderSize then + rf.party_threatBorderSize = rf.threatBorderSize + end + end, +}) From a03f4e7e0db36ade4f01c6956013de07ba58b471 Mon Sep 17 00:00:00 2001 From: Glyalith Date: Fri, 7 Aug 2026 19:49:58 -0600 Subject: [PATCH 2/2] Rewrite the threat border migration: ordering, both directions, clear not copy Three defects in the first version. It ran at the parent ADDON_LOADED, before ERF:OnInitialize performs the legacy party_showThreat -> party_threatBorderSize conversion. For a profile carrying that legacy key the body saw nil, returned, and stamped its flag, and the dormant 0 written moments later went live under the now-custom Health Bar section, turning party aggro borders off. Exactly the case it existed to prevent, and it could never re-run. It now performs and consumes that conversion itself, so ordering cannot matter. It only handled dormant becoming live. A legacy value that was LIVE under a custom Indicators section goes dormant under the new mapping, and those party frames would start showing a border the user had turned off. Both directions are handled by keying on whether the state flips at all. It neutralized by copying the raid value, which pins a permanent party override: later raid slider edits stop propagating to party, and "Re-sync Health Bar" prompts about discarding a setting the user never made. Clearing the key is identical at migration time and avoids both. Id bumped to v2 so profiles that ran the broken v1 while testing this branch still get the corrected pass. --- EllesmereUI_Migration.lua | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/EllesmereUI_Migration.lua b/EllesmereUI_Migration.lua index 0b960201..3cb57a25 100644 --- a/EllesmereUI_Migration.lua +++ b/EllesmereUI_Migration.lua @@ -4365,25 +4365,37 @@ EllesmereUI.RegisterMigration({ -- tab wrote the shared raid value. The key now files under "healthBar", which -- matches where its control lives. -- --- A stored party_threatBorderSize normally implies both sections were custom --- (Health Bar to reach the control, Indicators to route the write), so it --- stays live and unchanged. The exception is the legacy showThreat -> slider --- migration, which wrote party_threatBorderSize directly: such a value could --- be sitting dormant under a synced Health Bar and would switch on here. --- Neutralize only that case so no party frame changes appearance. +-- Writing party_threatBorderSize through the UI required both sections custom, +-- but the legacy showThreat -> slider conversion writes it directly, bypassing +-- that gate. So the override can be live under one mapping and dormant under +-- the other, in either direction. Clear it whenever that state would flip: in +-- the flip-to-live case that preserves exactly what party renders today, and +-- in the flip-to-dormant case party inherits raid either way while clearing +-- stops the value reviving later. An override live under BOTH mappings (the +-- normal both-custom case) is left untouched. -------------------------------------------------------------------------------- EllesmereUI.RegisterMigration({ - id = "rf_threat_border_party_section_v1", + id = "rf_threat_border_party_section_v2", scope = "profile", - description = "Keep party threat borders rendering as-is after threatBorderSize moved to the Health Bar party-sync section.", + description = "Clear a party threat border override whose live/dormant state would flip when threatBorderSize moved to the Health Bar sync section.", body = function(ctx) local rf = ctx.profile.addons and ctx.profile.addons.EllesmereUIRaidFrames - if type(rf) ~= "table" or rf.party_threatBorderSize == nil then return end + if type(rf) ~= "table" then return end + -- Consume the legacy party conversion HERE. ERF:OnInitialize performs + -- the same rewrite, but child addons initialize after the parent's + -- ADDON_LOADED, so deferring to it would let a dormant 0 land after + -- this body had already decided and stamped. Clearing the flag makes + -- that later block a no-op; the raid showThreat key is untouched. + if rf.party_showThreat ~= nil then + if rf.party_showThreat == false then rf.party_threatBorderSize = 0 end + rf.party_showThreat = nil + end + if rf.party_threatBorderSize == nil then return end local ss = type(rf.partySyncSections) == "table" and rf.partySyncSections or nil - local wasLive = ss and ss.indicators == false - local willBeLive = ss and ss.healthBar == false - if willBeLive and not wasLive and rf.party_threatBorderSize ~= rf.threatBorderSize then - rf.party_threatBorderSize = rf.threatBorderSize + local wasLive = (ss and ss.indicators == false) and true or false + local willBeLive = (ss and ss.healthBar == false) and true or false + if wasLive ~= willBeLive then + rf.party_threatBorderSize = nil end end, })