diff --git a/crates/benilla-app/assets/ui/MapCanvas.lua b/crates/benilla-app/assets/ui/MapCanvas.lua index 6501bb4c..8dd87468 100644 --- a/crates/benilla-app/assets/ui/MapCanvas.lua +++ b/crates/benilla-app/assets/ui/MapCanvas.lua @@ -434,7 +434,50 @@ function MapCanvasScrollControllerMixin:GetCanvas() return self.Child end +-- The ladder's two ends, or nil before there is a ladder. +function MapCanvasScrollControllerMixin:GetZoomBounds() + local levels = self.zoomLevels + local n = levels and table.getn(levels) or 0 + if n == 0 then + return nil + end + return levels[1].scale, levels[n].scale +end + +-- Pull every scale the controller holds onto the ladder it currently has. +-- +-- `CreateZoomLevels` is the documented extension point and a canvas addon replaces it wholesale, +-- so the ladder can change out from under a scale that was already set -- and `OnLoad` sets one, +-- 1, before any addon has run. OpenMapClassic's ladder is 27 steps from 0.0125 to 0.899, its +-- whole range *below* this file's own 1, because its stitched world is reached by zooming out +-- past a single zone; measured with it loaded, the canvas sat at 1.0, above its own ceiling, and +-- `ZoomIn`/`ZoomOut` walk the ladder **by value** -- they look for the first step above or below +-- the current scale -- so a scale outside it cannot be scrolled back onto at either end. +-- +-- Here rather than in `CreateZoomLevels`, because the addon's replacement is the one that will +-- not have it. +function MapCanvasScrollControllerMixin:ClampZoomToLevels() + local floor, ceil = self:GetZoomBounds() + if not floor then + return + end + local function clamp(v) + if not v then + return v + elseif v < floor then + return floor + elseif v > ceil then + return ceil + end + return v + end + self.currentScale = clamp(self.currentScale) + self.targetScale = clamp(self.targetScale) + self.zoomTarget = clamp(self.zoomTarget) +end + function MapCanvasScrollControllerMixin:GetCanvasScale() + self:ClampZoomToLevels() return self.currentScale or 1 end @@ -624,26 +667,10 @@ function MapCanvasScrollControllerMixin:CreateZoomLevels() end self.zoomLevels = levels self.baseScale = levels[1].scale - -- A view left outside the new ladder is pulled back onto its nearest end. `ZoomIn`/`ZoomOut` - -- walk the ladder **by value** — the first step above or below the current scale — so a view - -- outside it at either end cannot be climbed back onto by scrolling. Both ends happen: a - -- window that grew raises the floor, and a canvas addon that replaces the ladder wholesale - -- lowers the ceiling, which is OpenMapClassic's case (its stitched world is reached by - -- zooming *out* far past this file's own scale 1, so its whole ladder sits below ours). - local floor, ceil = levels[1].scale, levels[table.getn(levels)].scale - local clamped = self.currentScale - if clamped then - if clamped < floor then - clamped = floor - elseif clamped > ceil then - clamped = ceil - end - end - if clamped and clamped ~= self.currentScale then - self.currentScale = clamped - self.targetScale = clamped - self.zoomTarget = clamped - end + -- A window that grew raises the floor, so a view below it is pulled back on; see + -- `ClampZoomToLevels`, which is where the rule lives because a canvas addon's own + -- `CreateZoomLevels` will not have it. + self:ClampZoomToLevels() -- The scale itself is only seeded when there is none: `CreateZoomLevels` is called again on -- every map change and on every container resize, and a view the player has zoomed must not -- snap back because the window was dragged. diff --git a/docs/ADDON_COMPAT.md b/docs/ADDON_COMPAT.md index 97d5fe3b..b03e6e06 100644 --- a/docs/ADDON_COMPAT.md +++ b/docs/ADDON_COMPAT.md @@ -2728,3 +2728,67 @@ Two consequences had to follow it: The no-art-layer fallback changed with them: the canvas used to take the **container's** size when a map had no art layer, which was safe only while the container was 1002x668. It is 1.12's own art rect now in every case, and the fitted floor is what keeps it visible in a smaller window. + +## A canvas addon's zoom ladder, and the scale that was set before it existed + +`MapCanvasScrollControllerMixin:OnLoad` seeds `currentScale = 1`, and `CreateZoomLevels` is the +documented extension point a canvas addon replaces **wholesale**. OpenMapClassic's ladder is 27 +steps from **0.0125 to 0.899** — its whole range below this file's own 1, because its stitched +world is reached by zooming *out* far past a single zone. Measured with it loaded: + +``` +zoom: fit=1.0000 min=0.0125 max=0.8990 cur=1.0000 levels=27 +``` + +The canvas sat above its own ceiling, and `ZoomIn`/`ZoomOut` walk the ladder **by value** — the +first step above or below the current scale — so from 1.0 `ZoomIn` found nothing at all. The clamp +is in `ClampZoomToLevels`, called from `GetCanvasScale`, because `CreateZoomLevels` is exactly the +function the addon will have replaced. + +### Where OpenMapClassic actually stands, measured + +With it enabled and the map open, the canvas subtree holds **808 textures, 263 shown, 166 with a +resolved rect**, spanning roughly 4600x3270 canvas units of a 1002x668 canvas — and that is only +the part built so far, because the stitch is lazy per zone. Its ladder's floor of 0.0125 implies a +world about **80 000 units wide**: 1002 / 0.0125. So what a player sees is a correct crop of a +world far larger than the canvas, at whatever scale the view is on — which is why grom's +screenshot is a band of zone art with grey around it rather than misplaced art. + +The Era map tree underneath it answers correctly, which rules out the obvious suspects: + +``` +children of 947: 2 + 1414 Kalimdor rect=0.0888,0.4002,0.0791,0.8695 art=1002x668 + zone 1440 Ashenvale rectOnParent=0.4176,0.5743,0.3313,0.4879 + 1415 Eastern Kingdoms rect=0.6238,0.9232,0.0269,0.8713 art=1002x668 +``` + +`C_Map.GetMapArtLayers` answers a constant 1002x668 for **every** map, which is faithful for 1.12 +— every zone, continent and world sheet is 1002x668 of art in twelve 256 tiles — but it is the one +number OpenMapClassic's stitch is built on (`Map/Stitch.lua`'s `ArtSize`), together with +`GetMapRectOnMap`. That is where the next session starts. + +## Two port edits to BetterCharacterPanel + +Mirrored at `Addon_Ports/bettercharacterpanel_port/`, both one-liners, both the addon's own bug: + +- **`skins/paperdoll/BetterCharacterPanel-paperdoll-pfUI.lua:127`** — `SetCustomPositionCloseButton` + indexes `BCPPFUIUnifiedBackdrop` unguarded, where line 121 guards the same global three lines + up. FrameXML's own `ToggleCharacter` runs `ShowUIPanel(CharacterFrame)` — and so + `CharacterFrame_OnShow`, and so this — **before** `CharacterFrame_ShowSubFrame`, and the + backdrop is built only in `OnPaperDollShow`. So the first character-panel open of every session + raised, on any 1.12 client; it healed a moment later when `OnPaperDollShow` built the backdrop + and called `ChangeFrameValues` again. It was the only runtime error in grom's whole session log. +- **`BetterCharacterPanel-paperdoll.lua:581`** — `SetMinMaxValues(0, contentH - frameH - 20)`. The + stats content is 699 in a 451 window, so the range has to be 248 and the bar stopped at 228: + twenty units of the last card that no amount of scrolling could reach. The engine's own + `GetVerticalScrollRange()` answers 296 here and is not wrong — it measures the scroll child's + whole **subtree** (`0x786e30` → `0x786f80`), and the cards' pfUI shadows hang past + `contentFrame:SetHeight(yOffset)`. The edit keeps the addon's own measure and drops only the + fudge. + +What is left of "the info is bigger than the frame" is the addon's own arithmetic and is not a +client fault: `BCPPFUICharacterInformationFrame` is `PaperDollFrame:GetHeight() + 19 - padding` = +**522**, while `BCPPFUIUnifiedBackdrop.backdrop` is `512 - 10 - 72` = **430**, so the stats column +stands 46 units proud of the panel at the top and 46 at the bottom. `PaperDollFrame` is +`setAllPoints` to `CharacterFrame` in the realm's own XML, so the 512 it is derived from is right.