The map's mouse surface shrank with the zoom, and four things fell through the hole

The canvas gave the map no mouse surface of its own, on the reasoning that 1.12's
`WorldMapButton` covers the same rect and sits above it. The button is pinned INSIDE the canvas
child, so it scales with it: measured at the world view with OpenMapClassic driving, at grom's
own 2560x1440, a 23x16 px button inside a 701x468 px window. Every press on the other 99%
reached the next frame that takes the mouse — `WorldMapFrame`, which pfUI makes a
`RegisterForDrag("LeftButton")` handle for the whole window. So dragging the map moved the
window, and a click never reached `ProcessCanvasClickHandlers`, which is the only way a map pin
is ever dropped.

The container takes the mouse and the wheel now and drives the same controller. The frame level
mattered as much: the hit sweep probes one `(strata, level)` plane in link order and a parent is
linked before its children (`0x764aa0`), so a container tied with `WorldMapFrame` still lost
every press to its own parent. It is re-seated to `map:GetFrameLevel() + 1` on every show,
because the window is level 0 while the canvas builds and level 1 once `ShowUIPanel` has raised
it.

With the click arriving, a pin still stored nothing: `Map/Pins.lua:57` trims the note with
`strtrim`, which was deliberately absent. `script/stdlib.rs` still refuses it on the 1.12
surface, for the reason written there; this build claims ClassicAPI, whose client does have it,
so it lands in `compat/classic_api/string_trim.rs` with both spellings. Eight bare call sites in
the corpus, one of them in pfUI's search box on every keystroke.

`SCALE_RATE` 10 -> 24. A 0.7 s half-life was tolerable only while a wheel notch could not
accumulate; now that they add up the glide is what you feel ("scrolls past where I stop
scrolling").

`BenillaMapTextures(uiMapID)` is `GetExploredMapTextures` without the explored filter. Reveal
mode draws unexplored overlays out of a retail FileDataID table this client resolves none of —
captured with the fog default and with reveal, the same view came back pixel-identical, 0 by
`compare -metric AE`. The overlays were never missing, only their names: `Map/RevealFromClient.lua`
in `Addon_Ports/openmap_port` rebuilds the table from this, with paths.

Also in the port, not here: the arrow is placed through OpenMap's own coordinates, because
1.12 measures `GetPlayerMapPosition` against Blizzard's world and continent art and the stitched
map is a different projection.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
2026-11-04 09:34:00 -07:00
co-authored by Claude Opus 5
parent c92f078c39
commit d5beabab42
7 changed files with 390 additions and 6 deletions
+101
View File
@@ -3372,3 +3372,104 @@ the fog `default` and `reveal` is pixel-indistinguishable — and switching `lod
`zoneart` off one at a time changed nothing in a capture either (those take a rebuild the slash
command alone does not force). What a capture cannot reproduce is which zones the player has
actually explored, and that is the one input the complaint turns on.
## The map's mouse surface shrank with the zoom, and four things fell through the hole
grom, 2026-11-04, after the rail tabs came back: *"still can't drop a pin like intended … scroll
is better but feels pretty slippery, like scrolls past where I stop scrolling, and my arrow does
not stay where it should on scroll … drag on the map is weird, sometimes its panning the map as
it should and sometimes its grabbing and moving the whole window, this should only happen on the
borders."*
### One cause behind the drag and the pin: `WorldMapButton` is pinned to the canvas child
The canvas was built with the container taking no mouse at all, on the stated reasoning that
1.12's `WorldMapButton` "covers the same rect and is above this in the tree, so a mouse-enabled
container here would never see a press anyway". That holds at rest and stops holding the moment
a canvas addon scales the canvas: the button is **inside** the child, so it scales with it.
Measured at the world view with OpenMapClassic driving, at grom's own 2560x1440:
```
container SCREEN L=695 R=1396 B=552 T=1020 (701 x 468)
child SCREEN L=1242 R=1265 B=926 T=942 (23 x 16) scale 0.033
```
A 23x16 mouse surface in a 701x468 window. Every press on the other 99% reached the next frame
that takes the mouse — `WorldMapFrame`, which pfUI's `modules/map.lua:45-47` makes a
`RegisterForDrag("LeftButton")` handle for the whole window. So dragging the map moved the
window, and whether it did depended on the zoom and where you grabbed; and a click never reached
`ProcessCanvasClickHandlers`, which is the only way a pin is ever dropped.
The container now takes the mouse and the wheel and drives the same controller. **The frame level
mattered as much as the mouse bit**: the hit sweep probes one `(strata, level)` plane in link
order and a parent is linked before its children (`0x764aa0`, `TargetFrame.lua:33` relies on it),
so the container tied with `WorldMapFrame` at level 1 still lost every press to its own parent.
It is re-seated to `map:GetFrameLevel() + 1` on every show, because the window is level 0 while
the canvas builds and level 1 once `ShowUIPanel` has raised it — a level computed once at build
time ties again the first time the map opens.
Measured after: a drag on the bare map area gives `WorldMapFrame` nothing, the container's
`OnMouseDown` once, and the scroll moves.
### `strtrim` was nil, and that ate every pin
With the click arriving, dropping a pin still stored nothing. `Map/Pins.lua:57` is
`text = strtrim(text)` and `strtrim` was deliberately absent — `script/stdlib.rs` lists it under
"NOT here" with the reasoning that an addon reaching for a later client's API should find what it
would find there: nothing. That reasoning is about the **1.12** surface and still holds there,
which is why the name is now in `compat/classic_api/string_trim.rs` instead: this build sets
`CLASSIC_API_VERSION`, and on the client these addons are written for the name exists. Both
spellings, because `AceConfigCmd-3.0.lua:31` binds `string.trim` while everything else calls the
global; the string metatable then gives `("x"):trim()` for free. Corpus call sites, bare ones
only: 5 in OpenMapClassic's AceConfigCmd, 2 in its own map files, 1 in
`pfUI/api/ui-widgets.lua:1472` on every keystroke of a search box.
Pins now store: `pins 0 → 1` on a driven click over land.
### The zoom's glide
`SCALE_RATE` was 10 — a 0.7 s half-life, still visibly moving two seconds after the last notch.
That was tolerable only while a notch could not accumulate, because the target was never more
than one rung away. Now that notches add up, the glide is the thing you feel, so the rate is 24
(a 0.29 s half-life). The pan's is untouched: a drag writes the scroll directly and never eases.
### The player arrow is on the wrong projection
`WorldMapUnit_Update` places `WorldMapPlayer` at `GetPlayerMapPosition("player")` as a fraction of
`WorldMapDetailFrame`, which the canvas pins to the canvas child. Both halves of that check out —
the arrow sits at exactly the fraction the game reports (measured: `arrowFrac 0.7351,0.6348` for
`GetPlayerMapPosition 0.7351,0.6348`), and the child **is** the host map's rect, confirmed against
OpenMap's own coordinates by mapping the view centre through `MapFromWorld` and landing on the
container's centre to the pixel.
What is wrong is which map that fraction is *of*. On a continent or the world, 1.12 measures
against Blizzard's `WorldMap\World` and `WorldMapContinent` art — an illustration, not OpenMap's
grid — so the arrow lands somewhere plausible and wrong; and over a zone the player is not in,
1.12 answers (0, 0) and hides it. `Addon_Ports/openmap_port`'s `Map/StockArrow.lua` places both
the anchor and the Model through OpenMap's own chain instead: the player's zone and his position
in it (`C_Map.GetPlayerMapPosition`, which takes a map id and does not care what the window has
selected) → `WorldFromMap` → `MapFromWorld`. The offsets are scaled by `child / self`, because a
`SetPoint` offset is in the **anchored** frame's units and the Model hangs off `WorldMapFrame`
while the child is scaled by the zoom — without that the error grows with the zoom, which is the
shape of "my arrow does not stay where it should on scroll". Every step is guarded, so a client
that cannot answer leaves 1.12's own placement alone. **Not verified offline**: a capture's player
has no zone (`GetBestMapForUnit` answers the continent), so this one needs a play test.
### "Show unexplored areas" had no art to show
Reveal mode draws the overlays the client has not reported as explored, out of `ns.MapReveal` —
a table scraped from the retail client, `"w:h:x:y"` → a list of **FileDataIDs**. Those ids name
retail files; this client resolves none of them (`SetTexture(1396762)` → nil), so every reveal
overlay asked for a texture that does not exist. Captured with the fog `default` and with
`reveal`, the same view came back **pixel-identical** — `compare -metric AE` says 0.
The overlays are not missing, only their names: `WorldMapOverlay.dbc` carries every one with its
texture and rect, and the explored half already draws from exactly that data through
`C_MapExplorationInfo.GetExploredMapTextures`. `BenillaMapTextures(uiMapID)` is the same list
without the explored filter (benilla-named, as `BenillaMapIDForSelection` is, so no addon mistakes
it for an API it can expect elsewhere), and `Map/RevealFromClient.lua` rebuilds `ns.MapReveal`
from it with paths in place of ids. One line of `Map/Stitch.lua` had to stop `tonumber`-ing every
entry. Measured: Dun Morogh has 20 overlays, 9 unexplored, and `ns.MapReveal[1426]` now carries
those 9 as `Interface\WorldMap\DunMorogh\…` paths where it used to carry retail ids. The render
is **not** verified — the stitch is built before a capture's setup chunk can run, and the addon's
own switch says `/reload required (overlays built on map open)`.