map: the Continent and Zone pickers were blank on the first map of a session
The realm's `WorldMapFrame.xml` fills them from exactly one place: the Zone dropdown's `<OnEvent>` on `WORLD_MAP_UPDATE` (:321), which re-runs both pickers' initialisers and then the two `WorldMap_Update*DropDownText` functions. That dropdown registers the event in its own `<OnShow>` (:318 — the realm commented the `OnLoad` out and moved it there), while `WorldMapFrame`'s own `<OnShow>` fires it through `SetMapToCurrentZone()` (:711), and a parent's OnShow runs before its children's. So the inline event landed in a gap where its only listener did not exist yet. Measured through the addon harness: the Zone dropdown answers `IsEventRegistered` nil before the open and 1 immediately after, and the pickers come up blank on the first map and correct on the second — which is what grom's recording shows, blank over Dun Morogh and filled over Elwynn later in the same clip. `SetMapToCurrentZone` now queues a second `WORLD_MAP_UPDATE` on the deferred lane as well as firing the inline one. The inline fire stays: it is what repaints the detail tiles inside the OnShow rather than a frame later (the world-map flash). Only this verb doubles up, and only this verb needs to — `SetMapZoom` is a click on a map that is already open. The first attempt at this was reverted and the reason is in the test's own comment: its pin opened the real map on the shipped interface and passed with *and* without the fix, because the stock ordering happens to be fine and the symptom needs the addons. The pin that replaced it asserts the engine half directly — a handler that registers after `SetMapToCurrentZone` returns still receives the event — and answers 0 without the change and 1 with it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
@@ -668,3 +668,58 @@ fn worldmap_overlays_reveal_by_explored_bits() {
|
||||
s.run("SetMapZoom(2)").unwrap();
|
||||
assert_eq!(s.eval::<i64>("return GetNumMapOverlays()").unwrap(), 0);
|
||||
}
|
||||
|
||||
/// `SetMapToCurrentZone` leaves a `WORLD_MAP_UPDATE` behind for a handler that was not registered
|
||||
/// when it fired.
|
||||
///
|
||||
/// The reference's map calls it from `WorldMapFrame`'s own `<OnShow>`, and the two map dropdowns
|
||||
/// register for the event in *their* `<OnShow>` — a parent's runs first, so a purely inline event
|
||||
/// is dispatched into a gap where its only listener does not exist yet. Measured through the addon
|
||||
/// harness, which is the oracle here because the symptom needs the real `WorldMapFrame.xml`: the
|
||||
/// Zone dropdown answers `IsEventRegistered` nil before the open and 1 immediately after, and the
|
||||
/// pickers come up blank on the first map of a session and correct on the second.
|
||||
///
|
||||
/// This pins the engine half of it, which is the part benilla owns: whatever registers late still
|
||||
/// gets told. A test that opened the real map instead would pass either way — the stock interface's
|
||||
/// own ordering happens to be fine — and that is exactly how the first attempt at this fix was
|
||||
/// waved through before the harness caught it out.
|
||||
#[test]
|
||||
fn set_map_to_current_zone_also_tells_a_handler_that_registers_during_the_show() {
|
||||
let mut s = script();
|
||||
push_catalog(&mut s);
|
||||
s.set_world_map_feed(
|
||||
Some((2, 1)),
|
||||
Some((0.5, 0.5)),
|
||||
0.0,
|
||||
None,
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
);
|
||||
// The catalog and feed pushes queue a `WORLD_MAP_UPDATE` of their own ("an open map repaints"),
|
||||
// so drain the setup before counting — otherwise the probe below sees two and the test is
|
||||
// measuring the harness.
|
||||
s.tick(0.0);
|
||||
s.run(
|
||||
r#"
|
||||
seen = 0
|
||||
probe = CreateFrame("Frame")
|
||||
probe:SetScript("OnEvent", function() seen = seen + 1 end)
|
||||
-- The verb first, the registration after: the order a child's OnShow sees.
|
||||
SetMapToCurrentZone()
|
||||
probe:RegisterEvent("WORLD_MAP_UPDATE")
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
s.eval::<i64>("return seen").unwrap(),
|
||||
0,
|
||||
"the inline fire is before the registration, by construction"
|
||||
);
|
||||
s.tick(0.0);
|
||||
assert_eq!(
|
||||
s.eval::<i64>("return seen").unwrap(),
|
||||
1,
|
||||
"the deferred follow-up must reach a handler that registered during the show"
|
||||
);
|
||||
assert!(s.errors().is_empty(), "{:?}", s.errors());
|
||||
}
|
||||
|
||||
@@ -793,6 +793,26 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
select_resolved(&mut model, c, z, direct);
|
||||
}
|
||||
signal_world_map_update(lua);
|
||||
// …and again on the deferred lane, for the handlers that register *during* the show
|
||||
// this call is inside. `WorldMapFrame.xml`'s `<OnShow>` runs `SetMapToCurrentZone()`
|
||||
// (:711) while the two map dropdowns register `WORLD_MAP_UPDATE` in their own
|
||||
// `<OnShow>` (:286, :318 — the realm commented their `OnLoad` out and moved it there),
|
||||
// and a parent's OnShow runs before its children's. Measured: the Zone dropdown answers
|
||||
// `IsEventRegistered` nil before the open and 1 immediately after, so the inline event
|
||||
// above lands in the gap, and the first map of a session opens with both pickers blank
|
||||
// and Zoom Out dead — which is what grom's screen recording shows, filled on a later
|
||||
// open in the same clip.
|
||||
//
|
||||
// The inline fire stays: it is what repaints the detail tiles inside the OnShow rather
|
||||
// than a frame later (the world-map flash, see `signal_world_map_update`). Only this
|
||||
// verb doubles up, and only this verb needs to — `SetMapZoom` is a click on a map that
|
||||
// is already open, where every handler registered long ago.
|
||||
{
|
||||
let mut model = lua.app_data_mut::<Model>().expect("model app_data");
|
||||
model
|
||||
.pending_events
|
||||
.push(("WORLD_MAP_UPDATE".to_string(), Vec::new()));
|
||||
}
|
||||
Ok(())
|
||||
})?,
|
||||
)?;
|
||||
|
||||
Reference in New Issue
Block a user