fix(mosstank): unclickable button, empty skill list, dev font, and chat output
Four defects from the first in-world look, three of them with a definite root cause rather than a plausible one. **The Buff button did nothing.** Not a hit-testing problem -- the pointer found the button perfectly. UiRoot's press handling asks the pressed widget whether it owns the pointer; a widget that does not claim the press falls through to "move the ancestor window", and a window drag returns early on release without ever emitting a Click. UiButton and UiClickablePanel both override HandlesClick for exactly this reason; UiSimpleButton never did. Latent since that class was written, and invisible until it was put inside a draggable window -- which is precisely what a markup plugin panel is. Found by reproducing it headlessly through the real UiRoot dispatcher rather than by reasoning about it: MarkupPanelClickTests drives press-and-release over the button and asserts the bound action ran, with a separate test asserting the pointer finds the button at all, so a future failure says which half broke. My earlier guess -- that a modal at character select was swallowing the click -- was wrong, and the screenshot of the panel live in world disproved it. **"0 trained skills".** The skill-name table was read in OnLoad *before* GameWindowCompositionPipeline.Run, which is what publishes the DAT collection, so _dats was still null, the whole block was skipped, and the surface reported an empty skill list with nothing to explain it. Bound in PublishDatCollection instead -- the moment the data exists -- so it cannot run early again whatever the phase ordering does, and a genuinely missing SkillTable now says so. **Plugin text used the development bitmap font.** UiLabel and UiSimpleButton gained a DatFont, and MarkupDocument now takes the retail interface font from the host, so plugin panels render through the same glyph path (including retail's two-plane outline) as authored panels. **MossTank now writes to chat.** New BCL-only IPluginChat routes to retail's ClientLocal log type (0x1A) -- the channel the client uses for its own notices, local to this client, so a plugin cannot speak in the player's name. MossTank announces the start, the finish with a cast count, and a stall. Not addressed here: the cursor showing blue rather than amber. Traced but not fixed -- CursorFeedbackController picks the cursor family from combat mode, and CombatMode.Magic selects the blue Magic cursor where Default is amber. That is a combat-mode question, unrelated to this change, and worth its own look rather than a speculative fix folded in here. Solution builds clean; 14,437 tests pass on the standard hermetic lane filter, 0 failures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
17ebfc434d
commit
b9674b1f1e
8 changed files with 240 additions and 22 deletions
97
tests/AcDream.App.Tests/UI/MarkupPanelClickTests.cs
Normal file
97
tests/AcDream.App.Tests/UI/MarkupPanelClickTests.cs
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
using AcDream.App.UI;
|
||||
using Xunit;
|
||||
|
||||
namespace AcDream.App.Tests.UI;
|
||||
|
||||
/// <summary>
|
||||
/// End-to-end click routing for a markup-built plugin panel: press and release
|
||||
/// over the button, through the real <see cref="UiRoot"/> dispatcher, and
|
||||
/// assert the bound action ran.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Written because a MossTank panel rendered correctly in world and its Buff
|
||||
/// button did nothing when clicked. Unit-testing the markup builder proved the
|
||||
/// handler was bound; only driving the actual pointer path can show whether the
|
||||
/// click reaches it.
|
||||
/// </remarks>
|
||||
public class MarkupPanelClickTests
|
||||
{
|
||||
private sealed class Binding
|
||||
{
|
||||
public int Clicks { get; private set; }
|
||||
public Action Go => () => Clicks++;
|
||||
public bool Shown { get; set; } = true;
|
||||
public string Status => "ok";
|
||||
}
|
||||
|
||||
private const string Markup =
|
||||
"<panel x=\"40\" y=\"120\" w=\"360\" h=\"132\" title=\"MossTank\" visible=\"{Shown}\">"
|
||||
+ " <label x=\"12\" y=\"30\" text=\"{Status}\"/>"
|
||||
+ " <button x=\"12\" y=\"94\" w=\"108\" h=\"28\" text=\"Buff\" onclick=\"{Go}\"/>"
|
||||
+ "</panel>";
|
||||
|
||||
private static (UiRoot Root, Binding Bound) Mount()
|
||||
{
|
||||
var bound = new Binding();
|
||||
UiNineSlicePanel panel =
|
||||
MarkupDocument.Build(Markup, bound, _ => ((uint)1, 32, 32));
|
||||
var root = new UiRoot { Width = 1280, Height = 720 };
|
||||
root.AddChild(panel);
|
||||
return (root, bound);
|
||||
}
|
||||
|
||||
/// <summary>Centre of the button in root space: panel(40,120) + button(12,94) + half.</summary>
|
||||
private static (int X, int Y) ButtonCentre() => (40 + 12 + 54, 120 + 94 + 14);
|
||||
|
||||
[Fact]
|
||||
public void ClickingTheButtonRunsTheBoundAction()
|
||||
{
|
||||
var (root, bound) = Mount();
|
||||
(int x, int y) = ButtonCentre();
|
||||
|
||||
root.OnMouseDown(UiMouseButton.Left, x, y);
|
||||
root.OnMouseUp(UiMouseButton.Left, x, y);
|
||||
|
||||
Assert.Equal(1, bound.Clicks);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ThePointerFindsTheButtonAtAll()
|
||||
{
|
||||
// Separated from the click test so a failure says whether the problem is
|
||||
// hit-testing or event dispatch.
|
||||
var (root, _) = Mount();
|
||||
(int x, int y) = ButtonCentre();
|
||||
|
||||
UiElement? hit = root.Pick(x, y);
|
||||
|
||||
Assert.NotNull(hit);
|
||||
Assert.IsType<UiSimpleButton>(hit);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ClickingOutsideTheButtonDoesNotRunTheAction()
|
||||
{
|
||||
var (root, bound) = Mount();
|
||||
|
||||
root.OnMouseDown(UiMouseButton.Left, 900, 600);
|
||||
root.OnMouseUp(UiMouseButton.Left, 900, 600);
|
||||
|
||||
Assert.Equal(0, bound.Clicks);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AHiddenPanelSwallowsNothing()
|
||||
{
|
||||
var (root, bound) = Mount();
|
||||
bound.Shown = false;
|
||||
root.Tick(0.016, 0); // visibility source is evaluated on tick
|
||||
(int x, int y) = ButtonCentre();
|
||||
|
||||
root.OnMouseDown(UiMouseButton.Left, x, y);
|
||||
root.OnMouseUp(UiMouseButton.Left, x, y);
|
||||
|
||||
Assert.Equal(0, bound.Clicks);
|
||||
Assert.Null(root.Pick(x, y));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue