diff --git a/EllesmereUIDamageMeters/EllesmereUIDamageMeters.lua b/EllesmereUIDamageMeters/EllesmereUIDamageMeters.lua index 92cdce4a2..c880a1a1e 100644 --- a/EllesmereUIDamageMeters/EllesmereUIDamageMeters.lua +++ b/EllesmereUIDamageMeters/EllesmereUIDamageMeters.lua @@ -996,24 +996,22 @@ local function PhysicalPixels(userValue) return value end --- Row geometry with both terms on ONE pixel grid. barHeight is stored as a --- physical pixel count (plain slider), barSpacing in coordinate units (pixel --- slider), so it carries the UI scale it was set at. Snapping only the height --- left the stride between two grids and -((i-1) * stride) drifted down the --- list: a spacing of 1 then rendered as 0px on some rows and 2px on others, at --- a fractional UI scale and equally at a pixel-perfect one whenever the value --- had been saved at another scale. +-- Row geometry with both terms on ONE pixel grid. barHeight and barSpacing are +-- both coordinate units, like the window width and fonts, so bars keep their +-- proportion to the window at any UI scale and a shared profile renders the +-- same relative size for everyone. Both are snapped against the same effective +-- scale: a stride between two grids drifts down the list (-((i-1) * stride)), +-- rendering a spacing of 1 as 0px on some rows and 2px on others. -- Returns barH, barSp, stride and one physical pixel, in coordinate units. -local function RowMetrics(heightPx, spacingCoord, es) +local function RowMetrics(height, spacingCoord, es) local PP = EUI and EUI.PP if PP and PP.perfect and PP.SnapForES then if not es or es <= 0 then es = (UIParent and UIParent:GetEffectiveScale()) or 1 end - local onePixel = PP.perfect / es - local barH = PP.SnapForES((heightPx or 18) * onePixel, es) + local barH = PP.SnapForES(height or 18, es) local barSp = PP.SnapForES(spacingCoord or 2, es) - return barH, barSp, barH + barSp, onePixel + return barH, barSp, barH + barSp, PP.perfect / es end - local barH, barSp = PhysicalPixels(heightPx or 18), spacingCoord or 2 + local barH, barSp = height or 18, spacingCoord or 2 return barH, barSp, barH + barSp, (PP and PP.mult) or 1 end -- On ns as well: CreateDMWindow sits at Lua 5.1's 60-upvalue cap, so its call diff --git a/EllesmereUIDamageMeters/EllesmereUIDamageMeters_SpellHistory.lua b/EllesmereUIDamageMeters/EllesmereUIDamageMeters_SpellHistory.lua index c5a730c1b..38a91e561 100644 --- a/EllesmereUIDamageMeters/EllesmereUIDamageMeters_SpellHistory.lua +++ b/EllesmereUIDamageMeters/EllesmereUIDamageMeters_SpellHistory.lua @@ -112,14 +112,12 @@ local function SetFont(fs, size) fs:SetFont(font, size, flags) end --- Snapped through PP.Scale so accumulated row offsets (stride * index) don't drift off the pixel --- grid from float dust -- without this, a spacing of 1 can round to 0px on some rows and 2px on others. -local function PhysicalPixels(val) +-- Icon size is a coordinate value like the window width and icon spacing, so it +-- keeps its proportion at any UI scale; only snapped onto the pixel grid. +local function SnapSize(val) local PP = EUI and EUI.PP - local mult = (PP and PP.mult) or 1 - local value = (val or 0) * mult - if PP and PP.Scale then return PP.Scale(value) end - return value + if PP and PP.Snap then return PP.Snap(val or 0) end + return val or 0 end local function GetBarTexturePath() @@ -485,7 +483,7 @@ local ANIM_SLIDE_PX = 6 -- the history length or growth direction never makes the row wander. local function IconStripGeometry(count) local sh = DB() - local iconSz = PhysicalPixels(sh.iconSize or 24) + local iconSz = SnapSize(sh.iconSize or 24) local gap = sh.iconSpacing or 1 local dir = sh.growDirection or "LEFT" count = max(1, count or sh.iconCount or 5) @@ -807,7 +805,7 @@ BuildIconStrip = function() -- during animation. No strip-level background needed. end - local iconSz = PhysicalPixels(sh.iconSize or 24) + local iconSz = SnapSize(sh.iconSize or 24) local gap = sh.iconSpacing or 1 local dir = sh.growDirection or "LEFT" local iconZoom = sh.iconZoom or 0.08 diff --git a/EllesmereUI_Migration.lua b/EllesmereUI_Migration.lua index 5477c0fbe..9c709930b 100644 --- a/EllesmereUI_Migration.lua +++ b/EllesmereUI_Migration.lua @@ -4226,3 +4226,63 @@ EllesmereUI.RegisterMigration({ strip(ctx.profile.condOverrides) end, }) + +-- Damage Meters barHeight and Spell History shBarHeight/iconSize now render in UI +-- units instead of physical pixels (value * perfect / ppUIScale, UIParent-parented +-- frames). Multiply by that factor once so existing profiles keep their exact size. +-- GLOBAL on purpose: a profile stamp rides exports, so an old string would get the +-- RECIPIENT's factor frozen in; imports after this ran are read as UI units. +-- Per-profile stamps keep it idempotent when a full-account import resets flags. +EllesmereUI.RegisterMigration({ + id = "dm_bar_height_ui_units_v1", + scope = "global", + description = "Convert Damage Meters bar height and Spell History bar height/icon size from physical pixels to UI units, keeping every existing profile's rendered size.", + body = function(ctx) + local ID = "dm_bar_height_ui_units_v1" + local db = ctx.db + if not db or type(db.profiles) ~= "table" then return end + -- No stamp without the real factor: an error retries next session + -- (Startup seeds ppUIScale at login). + local _, physH = GetPhysicalScreenSize() + if type(physH) ~= "number" or physH <= 0 then error("physical screen size not available") end + local uiScale = db.ppUIScale + if type(uiScale) ~= "number" or uiScale <= 0 then error("ppUIScale not set yet") end + -- Same legacy normalization EllesmereUI_Startup applies before SetScale. + if uiScale == 0.53 then uiScale = 0.5333333333 + elseif uiScale == 0.71 then uiScale = 0.7111111111 end + local factor = (768 / physH) / uiScale + -- Within 1% the 40px slider maximum moves by under half a pixel, so the + -- snapped size is identical: pixel-perfect setups keep their values as-is. + local convert = math.abs(factor - 1) > 0.01 + local function conv(v, default) + local n = type(v) == "number" and v or default + return floor(n * factor * 10000 + 0.5) / 10000 + end + for _, profData in pairs(db.profiles) do + if type(profData) == "table" then + local stamps = profData._migrations + if type(stamps) ~= "table" then + stamps = {} + profData._migrations = stamps + end + if not stamps[ID] then + -- Folder present = DM ran in this profile; dm itself is + -- missing when every value was a stripped default. + local addon = convert and type(profData.addons) == "table" + and profData.addons.EllesmereUIDamageMeters + if type(addon) == "table" then + if type(addon.dm) ~= "table" then addon.dm = {} end + local dm = addon.dm + dm.barHeight = conv(dm.barHeight, 18) + local sh = dm.spellHistory + if type(sh) == "table" then + sh.shBarHeight = conv(sh.shBarHeight, 20) + sh.iconSize = conv(sh.iconSize, 36) + end + end + stamps[ID] = true + end + end + end + end, +})