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:
parent
22b6281192
commit
e5629d713d
2 changed files with 54 additions and 13 deletions
|
|
@ -200,6 +200,22 @@ public sealed class MapPageController
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Live-DAT-confirmed 2026-08-17: the SAME shared popup skin
|
||||||
|
/// <see cref="UiItemSlot"/> hardcodes for its own runtime-text tooltips
|
||||||
|
/// (its own class doc has the full "one of the four popup skins
|
||||||
|
/// RetailTooltipPresenter already mounts" citation). The map-note
|
||||||
|
/// template (<c>0x100001F0</c>) authors no individual tooltip-popup
|
||||||
|
/// locator of its own (a plain 10x10 hotspot dot), so
|
||||||
|
/// <c>RetailTooltipPresenter.OnTooltipShow</c>'s unconditional
|
||||||
|
/// <c>AuthoredTooltipRootElementId == 0 -> return</c> guard needs one
|
||||||
|
/// supplied — reusing the item catalog's proven-working skin is the
|
||||||
|
/// same "best-evidenced inference, not a measured retail value" shape
|
||||||
|
/// TS-85's own <c>UpdateWorldHoverTooltip</c> fallback already uses.
|
||||||
|
/// </summary>
|
||||||
|
private const uint MarkerTooltipRootElementId = 0x10000395u;
|
||||||
|
private const uint MarkerTooltipLayoutDid = 0x21000041u;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Instantiates the 53 static town hotspots (<c>gmMapUI::AddMapNote</c>)
|
/// Instantiates the 53 static town hotspots (<c>gmMapUI::AddMapNote</c>)
|
||||||
/// from <c>m_pMap</c>'s own <c>0x47</c>/<c>0x48</c> template attrs. A
|
/// from <c>m_pMap</c>'s own <c>0x47</c>/<c>0x48</c> template attrs. A
|
||||||
|
|
@ -226,12 +242,19 @@ public sealed class MapPageController
|
||||||
marker.Width = loc.Width;
|
marker.Width = loc.Width;
|
||||||
marker.Height = loc.Height;
|
marker.Height = loc.Height;
|
||||||
// gmMapUI::AddMapNote's UIElement::SetTooltip call — a LITERAL
|
// gmMapUI::AddMapNote's UIElement::SetTooltip call — a LITERAL
|
||||||
// string (StringInfo::SetLiteralValue), not a DAT table lookup.
|
// string (StringInfo::SetLiteralValue), not a DAT table lookup —
|
||||||
// AuthoredTooltipText/Enabled is the exact seam
|
// i.e. retail's RUNTIME m_TTText mechanism, not the authored
|
||||||
// RetailTooltipPresenter already serves (closes register row
|
// P0x49 path. UiButton.TooltipText is the exact settable seam
|
||||||
// TS-85's last item, gmMapUI::AddMapNote @0x004A1C51).
|
// backing UiElement.GetTooltipText()'s override, which
|
||||||
marker.AuthoredTooltipText = loc.Name;
|
// RetailTooltipPresenter.ResolveTooltipText consults BEFORE the
|
||||||
marker.AuthoredTooltipEnabled = true;
|
// authored text (closes register row TS-85's last item,
|
||||||
|
// gmMapUI::AddMapNote @0x004A1C51). AuthoredTooltipRootElementId/
|
||||||
|
// LayoutDid still gate the popup SKIN unconditionally even on
|
||||||
|
// the runtime-text path — see MarkerTooltipRootElementId's doc.
|
||||||
|
if (marker is UiButton markerButton)
|
||||||
|
markerButton.TooltipText = loc.Name;
|
||||||
|
marker.AuthoredTooltipRootElementId = MarkerTooltipRootElementId;
|
||||||
|
marker.AuthoredTooltipLayoutDid = MarkerTooltipLayoutDid;
|
||||||
_map!.AddChild(marker);
|
_map!.AddChild(marker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,22 @@ public sealed class MapHousePanelControllerTests
|
||||||
/// <summary>Serves BOTH the town-hotspot template (any (layoutId,
|
/// <summary>Serves BOTH the town-hotspot template (any (layoutId,
|
||||||
/// elementId) pair not the player/house icon ids) and the two icons
|
/// elementId) pair not the player/house icon ids) and the two icons
|
||||||
/// <see cref="MapPageController.Bind"/> re-resolves standalone
|
/// <see cref="MapPageController.Bind"/> re-resolves standalone
|
||||||
/// (<c>m_pMap</c>'s own button-swallowed children) — a real
|
/// (<c>m_pMap</c>'s own button-swallowed children). Returns a
|
||||||
/// <see cref="RowTemplateResolver"/> would set <c>DatElementId</c> the
|
/// <see cref="UiButton"/> — matching the live template's own authored
|
||||||
/// same way <see cref="LayoutImporter.Build"/> does, so tests that need
|
/// Type 1 (<c>MapHousePanelSlotProbeTests</c>: "hotspot template
|
||||||
/// to find these icons back by id after the fact need it too.</summary>
|
/// 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)
|
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(
|
private static MapHousePanelController.Callbacks MakeCallbacks(
|
||||||
List<string>? calls = null,
|
List<string>? calls = null,
|
||||||
|
|
@ -151,8 +161,16 @@ public sealed class MapHousePanelControllerTests
|
||||||
Assert.Equal(53, townMarkers.Count);
|
Assert.Equal(53, townMarkers.Count);
|
||||||
Assert.All(townMarkers, c => Assert.Contains(
|
Assert.All(townMarkers, c => Assert.Contains(
|
||||||
MapLocations.All, loc => loc.Width == c.Width && loc.Height == c.Height));
|
MapLocations.All, loc => loc.Width == c.Width && loc.Height == c.Height));
|
||||||
Assert.All(townMarkers, c => Assert.True(c.AuthoredTooltipEnabled));
|
// Runtime tooltip text (UiButton.TooltipText, backing
|
||||||
Assert.Contains(townMarkers, c => c.AuthoredTooltipText == "Holtburg");
|
// 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]
|
[Fact]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue