diff --git a/src/AcDream.App/UI/Layout/ChatWindowController.cs b/src/AcDream.App/UI/Layout/ChatWindowController.cs index 657a6353..74c4682f 100644 --- a/src/AcDream.App/UI/Layout/ChatWindowController.cs +++ b/src/AcDream.App/UI/Layout/ChatWindowController.cs @@ -855,8 +855,12 @@ public sealed class ChatWindowController : IRetainedWindowStateController, IReta /// Aims the chat entry at and focuses it. internal void StartTell(string name) { + // SetText already places the caret at the end. An explicit "move to + // the end" on top of it is not merely redundant: MoveCaret takes a + // DELTA, so int.MaxValue overflows _caret + delta to negative and the + // clamp lands the caret at column 0 — the player then has to click the + // bar to get behind their own prefix. Input.SetText($"@tell {name}, "); - Input.MoveCaret(int.MaxValue); FindRootOf(Input)?.SetKeyboardFocus(Input); } diff --git a/tests/AcDream.App.Tests/UI/Layout/ChatWindowControllerTests.cs b/tests/AcDream.App.Tests/UI/Layout/ChatWindowControllerTests.cs index 05d43166..46f7d5c4 100644 --- a/tests/AcDream.App.Tests/UI/Layout/ChatWindowControllerTests.cs +++ b/tests/AcDream.App.Tests/UI/Layout/ChatWindowControllerTests.cs @@ -175,6 +175,12 @@ public class ChatWindowControllerTests ctrl!.StartTell("Dww"); Assert.Equal("@tell Dww, ", ctrl.Input.Text); + + // ...and the caret sits AFTER the prefix, ready to type into. It landed + // at column 0 in the first cut because an explicit MoveCaret(int.MaxValue) + // was stacked on top of SetText, which already ends there — MoveCaret + // takes a delta, so that overflowed negative and clamped to zero. + Assert.Equal("@tell Dww, ".Length, ctrl.Input.CaretPos); } // ── Talk-focus specials: "Tell to X" / "Squelch (ignore) X" ─────────────