ui: the stock map art has to follow the detail frame it is drawn into
`WOW_MAP_TRACE=1` named it on grom's first run, in one line:
detail 1431x954 @scale 0.70 | 1 256x256 @(10,1122) | 2 256x256 @(266,1122) | ...
1.12's `WorldMapDetailFrame` is always 1002x668, so everything drawn into it is placed in absolute
pixels against that: twelve 256x256 tiles anchor-chained from its TOPLEFT, each exploration overlay
at the offset `WorldMapOverlay.dbc` gives it. The canvas breaks that the moment it sizes the frame
to anything else, and it does — `OnCanvasSizeChanged` falls back to the container's own rect
whenever it has no art layer to read, which is every moment the canvas has no `mapID`, including
the world-view blip the same log shows on every map open.
So: a 1024x768 sheet of art pinned to the top-left of a 1431x954 frame, covering 72% of the width
and 80% of the height, with the rest of the map drawing nothing. That is the grey he reported,
with the art magnified relative to what was left of the frame.
The art is now rescaled to whatever size the frame has. The tiles need only their size set, because
the XML chains them TOPRIGHT-to-TOPLEFT and BOTTOMLEFT-to-TOPLEFT. The overlays carry absolute
offsets, so they are captured from what the stock repaint last wrote — hooked on
`WorldMapFrame_Update`, the only moment those base values are readable — and scaled from that base,
so a second pass cannot compound. Measured: a 1431x954 frame now holds a 1462x1097 grid, the same
1024/1002 and 768/668 proportion the reference frames.
Two smaller faults fell out and are fixed with it: the container resizing was not treated as a
canvas size change at all (the canvas was re-sized only on a map change), and a derived resize
fires no `OnSizeChanged` here, so the rescale takes the size its caller just set rather than
reading the frame back, which would answer the old one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
@@ -706,6 +706,88 @@ function MapCanvasScrollControllerMixin:ResetZoom()
|
||||
self:InstantPanAndZoom(scale, 0.5, 0.5)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- The stock art, at a canvas size that is not 1.12's
|
||||
--
|
||||
-- 1.12's `WorldMapDetailFrame` is always 1002x668, so everything drawn into it is placed in
|
||||
-- absolute pixels against that: twelve 256x256 tiles anchor-chained from its TOPLEFT (1024x768 of
|
||||
-- art, the overhang clipped), each exploration overlay at the offset and size
|
||||
-- `WorldMapOverlay.dbc` gives it, and the hover highlight likewise. The canvas breaks that
|
||||
-- assumption the moment it sizes the frame to anything else — and it does, every time
|
||||
-- `OnCanvasSizeChanged` has no art layer to read and falls back to the container's own rect.
|
||||
--
|
||||
-- Measured in grom's 2026-11-02 run, through `WOW_MAP_TRACE=1`:
|
||||
--
|
||||
-- detail 1431x954 @scale 0.70 | 1 256x256 @(10,1122) | 2 256x256 @(266,1122) | ...
|
||||
--
|
||||
-- A 1024x768 sheet of art pinned to the top-left of a 1431x954 frame: it covered 72% of the width
|
||||
-- and 80% of the height and the rest of the map drew **nothing**, which is the grey he reported,
|
||||
-- with the art magnified relative to what was left.
|
||||
--
|
||||
-- So the art is rescaled to whatever size the frame has, by the same factors on each axis. The
|
||||
-- tiles need only their size set, because 1.12's XML chains them TOPRIGHT-to-TOPLEFT and
|
||||
-- BOTTOMLEFT-to-TOPLEFT — resize one and the grid follows. The overlays and the highlight carry
|
||||
-- absolute offsets, so those are scaled from the values the stock repaint last wrote, kept per
|
||||
-- texture so a second pass cannot compound.
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local DETAIL_BASE_W, DETAIL_BASE_H = 1002, 668
|
||||
local DETAIL_TILE, DETAIL_TILES = 256, 12
|
||||
|
||||
-- Base geometry per overlay texture, as `WorldMapFrame_Update` wrote it: [texture] = {x,y,w,h}.
|
||||
local overlayBase = {}
|
||||
|
||||
-- `w`,`h` are the size the detail frame is ABOUT to have, passed by the caller that just set it.
|
||||
-- Reading the frame instead would answer the old size: the detail frame is `SetAllPoints` to the
|
||||
-- canvas, so its own size is derived, and a derived size does not resolve until the layout pass
|
||||
-- — measured, a child answered `0x0` immediately after its parent was resized. Without them the
|
||||
-- frame's current size is read, which is right for a caller that is not resizing it (the overlay
|
||||
-- capture after a repaint).
|
||||
local function ScaleDetailArt(w, h)
|
||||
local d = WorldMapDetailFrame
|
||||
if not d then
|
||||
return
|
||||
end
|
||||
w = w or d:GetWidth()
|
||||
h = h or d:GetHeight()
|
||||
if not w or not h or w <= 0 or h <= 0 then
|
||||
return
|
||||
end
|
||||
local sx, sy = w / DETAIL_BASE_W, h / DETAIL_BASE_H
|
||||
for i = 1, DETAIL_TILES do
|
||||
local tile = _G["WorldMapDetailTile" .. i]
|
||||
if tile then
|
||||
tile:SetWidth(DETAIL_TILE * sx)
|
||||
tile:SetHeight(DETAIL_TILE * sy)
|
||||
end
|
||||
end
|
||||
for tex, base in pairs(overlayBase) do
|
||||
tex:SetWidth(base.w * sx)
|
||||
tex:SetHeight(base.h * sy)
|
||||
tex:ClearAllPoints()
|
||||
tex:SetPoint("TOPLEFT", WorldMapDetailFrame, "TOPLEFT", base.x * sx, base.y * sy)
|
||||
end
|
||||
end
|
||||
|
||||
-- After the stock repaint: every overlay it just placed is at its 1002x668 offset and size, which
|
||||
-- is the only moment those base values are readable. Captured, then scaled.
|
||||
local function CaptureOverlays()
|
||||
for tex in pairs(overlayBase) do
|
||||
overlayBase[tex] = nil
|
||||
end
|
||||
local n = NUM_WORLDMAP_OVERLAYS or 0
|
||||
for i = 1, n do
|
||||
local tex = _G["WorldMapOverlay" .. i]
|
||||
if tex and tex:IsShown() then
|
||||
local _, _, _, x, y = tex:GetPoint()
|
||||
if x then
|
||||
overlayBase[tex] = { x = x, y = y, w = tex:GetWidth(), h = tex:GetHeight() }
|
||||
end
|
||||
end
|
||||
end
|
||||
ScaleDetailArt()
|
||||
end
|
||||
|
||||
-- Called when the container or the canvas changes size, and on the first show: the canvas is
|
||||
-- sized from the map's own art layer, the zoom ladder rebuilt and the view reset.
|
||||
function MapCanvasScrollControllerMixin:OnCanvasSizeChanged()
|
||||
@@ -729,6 +811,10 @@ function MapCanvasScrollControllerMixin:OnCanvasSizeChanged()
|
||||
end
|
||||
child:SetWidth(w)
|
||||
child:SetHeight(h)
|
||||
-- The stock art is placed in 1002x668 pixels; this is where the frame holding it stops being
|
||||
-- that size (see "The stock art, at a canvas size that is not 1.12's"). The size just set is
|
||||
-- passed in, because the detail frame will not answer it until the layout pass.
|
||||
ScaleDetailArt(w, h)
|
||||
self:CreateZoomLevels()
|
||||
self:CalculateScrollExtents()
|
||||
self.currentScrollX, self.currentScrollY = self:ClampScrollToExtents(
|
||||
@@ -1562,6 +1648,27 @@ local function BuildCanvas()
|
||||
detail:SetAllPoints(canvas)
|
||||
button:SetParent(canvas)
|
||||
|
||||
-- The stock repaint places the exploration overlays in 1002x668 pixels and is the only moment
|
||||
-- those base offsets are readable; after it, they are scaled to whatever size the canvas has
|
||||
-- given the detail frame (see "The stock art, at a canvas size that is not 1.12's").
|
||||
if WorldMapFrame_Update then
|
||||
hooksecurefunc("WorldMapFrame_Update", CaptureOverlays)
|
||||
end
|
||||
CaptureOverlays()
|
||||
|
||||
-- The container resizing IS a canvas size change, and until now nothing said so: the canvas
|
||||
-- was re-sized only when the map changed (`SetMapID`, `SyncFromSelection`), so a window
|
||||
-- resized while the map stayed put left the canvas at the old size. openmap resizes this
|
||||
-- container explicitly, which is the case that reached grom.
|
||||
--
|
||||
-- Only an explicit `SetWidth`/`SetHeight` is covered, because that is all this client fires
|
||||
-- `OnSizeChanged` for — measured: a child `SetAllPoints` to a parent that is then resized
|
||||
-- gets no event at all. That is why the art is rescaled inside `OnCanvasSizeChanged` rather
|
||||
-- than hung off the detail frame's own resize, which would never run.
|
||||
sc:SetScript("OnSizeChanged", function(self)
|
||||
self:OnCanvasSizeChanged()
|
||||
end)
|
||||
|
||||
-- Everything the button carries is map content except one frame: `WorldMapFrameAreaFrame`,
|
||||
-- the hovered-zone name. 1.12's XML hangs it off the mouse surface (TOP, 400x128) because
|
||||
-- that is what covers the art, but it is chrome — it names what the cursor is over and sits
|
||||
|
||||
Reference in New Issue
Block a user