feat(chat): CT-C2 Escape leaves the entry; CT-C3 the timestamp is grey
Campaign CT slices C2 and C3. **C2 — Escape in the chat input did nothing at all.** Not "did the wrong thing": nothing. Two independent facts had to hold for that. UiField has no Escape case, AND a focused field reports IsEditControl, which makes UiRoot skip its own fallback and the input dispatcher withhold game actions — so the player had no way out of the bar except the mouse. Retail maps Escape to input action 0x0B, which runs ChatInterface::DeactivateChatEntry @0x004F2FC0: RelinquishFocus, then Deactivate. It does NOT clear the field. That is worth stating because the obvious guess — "Escape clears the input" — is wrong and would have looked perfectly reasonable; a half-written message survives stepping away from the bar, and the test pins that rather than just pinning "handled". **C3 — the timestamp took the message's colour.** Retail appends it as its own run at a FIXED colour index (0x0C, which BuildChatColorLookupTable @0x004F31C0 fills with colorGrey) rather than the line's, so it stays grey whether the message is red combat text or white speech. Most of C3 was already done and stayed untouched: the DisplayTimeStamps option is polled, and FormatTimestampPrefix already matches retail's "%#H:%M:%S ". Only the colour was wrong, and it was only fixable now because A1/A4 made a line able to carry more than one colour. The stamp is a span ROLE rather than a second tag type: it is not clickable and carries no payload, so modelling it as a tag would have made it hit-testable for no reason. Its colour comes from the same runtime table every message colour comes from, unlike the tagged-name colour, which is authored per element (0x1D) and deliberately lives elsewhere. One consequence worth naming: a timestamped line now needs runs even when its sender is not tagged, because the stamp alone is reason enough. Before this, only tagged lines got runs. Also verified and NOT changed, having checked rather than assumed: C1's auto-scroll half is already retail-faithful — UiScrollable.SetExtents samples "was at the end" BEFORE applying new extents and only re-sticks if so, which is exactly retail's IsAtVerticalEnd rule, and chat gets it by default. C1 reduces to the unread indicator, which does not exist yet. Solution builds clean; full hermetic gate green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
2f97052e4f
commit
9f6d79b7e0
6 changed files with 167 additions and 10 deletions
|
|
@ -51,8 +51,26 @@ public readonly record struct ChatTextTag(string Type, string Format, string Dat
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>What a span IS, for colouring purposes.</summary>
|
||||
public enum ChatSpanRole
|
||||
{
|
||||
/// <summary>Ordinary message text; takes the line's own colour.</summary>
|
||||
Body,
|
||||
|
||||
/// <summary>
|
||||
/// The leading timestamp. Retail appends it as its own run at a FIXED
|
||||
/// colour index (<c>0x0C</c>) rather than the line's
|
||||
/// (<c>ChatInterface::RecvNotice_DisplayFinalStringInfo @0x004F4640</c>),
|
||||
/// so it stays grey whatever colour the message is.
|
||||
/// </summary>
|
||||
Timestamp,
|
||||
}
|
||||
|
||||
/// <summary>One stretch of chat text, and the tag covering it (if any).</summary>
|
||||
public readonly record struct ChatTextSpan(string Text, ChatTextTag? Tag);
|
||||
public readonly record struct ChatTextSpan(
|
||||
string Text,
|
||||
ChatTextTag? Tag,
|
||||
ChatSpanRole Role = ChatSpanRole.Body);
|
||||
|
||||
/// <summary>
|
||||
/// Parses retail's inline chat tag markup into spans.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue