fix(ui): Map/House panel — marker tooltips use the wrong property

Live verification (slice 5) found town-marker tooltips never appeared:
RetailTooltipPresenter.OnTooltipShow gates unconditionally on
AuthoredTooltipRootElementId == 0 -> return, with no fallback, but the
markers only set AuthoredTooltipText/Enabled (the DAT-authored P0x49
path). gmMapUI::AddMapNote's UIElement::SetTooltip call is retail's
RUNTIME m_TTText/SetTooltip mechanism, not the authored path — the
correct seam is UiButton.TooltipText (backing GetTooltipText()'s
override), which ResolveTooltipText consults before authored text.

The popup-skin locator (AuthoredTooltipRootElementId/LayoutDid) is
still required even on the runtime-text path with no built-in
fallback, so markers now hardcode the same shared popup skin
UiItemSlot already uses (0x10000395/0x21000041) — matching that
established precedent exactly.

Verified live: hovering a town marker (Aerlinthe Island) now renders
its tooltip correctly. 21/21 Map/House controller tests still pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-17 02:43:40 +02:00
parent 22b6281192
commit e5629d713d
2 changed files with 54 additions and 13 deletions

View file

@ -20,12 +20,22 @@ public sealed class MapHousePanelControllerTests
/// <summary>Serves BOTH the town-hotspot template (any (layoutId,
/// elementId) pair not the player/house icon ids) and the two icons
/// <see cref="MapPageController.Bind"/> re-resolves standalone
/// (<c>m_pMap</c>'s own button-swallowed children) — a real
/// <see cref="RowTemplateResolver"/> would set <c>DatElementId</c> the
/// same way <see cref="LayoutImporter.Build"/> does, so tests that need
/// to find these icons back by id after the fact need it too.</summary>
/// (<c>m_pMap</c>'s own button-swallowed children). Returns a
/// <see cref="UiButton"/> — matching the live template's own authored
/// Type 1 (<c>MapHousePanelSlotProbeTests</c>: "hotspot template
/// type=1") — so <see cref="MapPageController.BuildTownMarkers"/>'s
/// <c>marker is UiButton</c> tooltip-text branch is actually exercised
/// by these tests. A real <see cref="RowTemplateResolver"/> would set
/// <c>DatElementId</c> the same way <see cref="LayoutImporter.Build"/>
/// does, so tests that need to find these icons back by id after the
/// fact need it too.</summary>
private static UiElement? FakeHotspotTemplate(uint layoutId, uint elementId)
=> new UiText { Width = 10f, Height = 10f, DatElementId = elementId };
=> new UiButton(new ElementInfo(), static _ => (0u, 0, 0))
{
Width = 10f,
Height = 10f,
DatElementId = elementId,
};
private static MapHousePanelController.Callbacks MakeCallbacks(
List<string>? calls = null,
@ -151,8 +161,16 @@ public sealed class MapHousePanelControllerTests
Assert.Equal(53, townMarkers.Count);
Assert.All(townMarkers, c => Assert.Contains(
MapLocations.All, loc => loc.Width == c.Width && loc.Height == c.Height));
Assert.All(townMarkers, c => Assert.True(c.AuthoredTooltipEnabled));
Assert.Contains(townMarkers, c => c.AuthoredTooltipText == "Holtburg");
// Runtime tooltip text (UiButton.TooltipText, backing
// GetTooltipText()) — the retail SetTooltip/m_TTText mechanism, NOT
// the DAT-authored AuthoredTooltipText path. The popup-skin locator
// is unconditionally required even on the runtime-text path (see
// MapPageController.MarkerTooltipRootElementId's doc).
Assert.All(townMarkers, c => Assert.NotEqual(0u, c.AuthoredTooltipRootElementId));
Assert.All(townMarkers, c => Assert.NotEqual(0u, c.AuthoredTooltipLayoutDid));
Assert.All(townMarkers, c => Assert.IsType<UiButton>(c));
Assert.All(townMarkers, c => Assert.False(string.IsNullOrEmpty(((UiButton)c).TooltipText)));
Assert.Contains(townMarkers, c => ((UiButton)c).TooltipText == "Holtburg");
}
[Fact]