acdream/tests/AcDream.App.Tests/UI/UiChannelMenuTests.cs
Erik 1da697ec2a @
feat(D.2b): chat polish — typing fix, opacity, scrollbar 3-slice, retail channel menu

Visual-iteration batch (decomp-grounded), each fix verified against the retail screenshots:
- typing: UiElement.HitTest aborted on ClickThrough BEFORE walking children, so the
  ClickThrough UiDatElement panels blocked hit-testing to the input/transcript inside
  them. Check ClickThrough AFTER the child walk (it only gates whether THIS element
  claims the hit). Restores input focus + typing.
- opacity: UiElement.Opacity + a UiRenderContext alpha stack applied to sprite/rect
  draws (text bypasses it, stays sharp); chat frame Opacity=0.75 → translucent chat.
- brown sliver: grow the transcript panel up 9px to cover the dropped resize-bar strip.
- scrollbar: real 3-slice thumb (caps 0x06004C60/66 + tiled mid) + tiled track.
- max/min: shifted one button-width left of the scrollbar (dat right-anchors collide).
- system text now green (retail ChatMessageType 5; was yellow).
- word-wrap: transcript lines wrap to the panel width (greedy, ports GlyphList::Recalculate).
- channel menu reworked to retail gmMainChatUI::InitTalkFocusMenu: "Chat" button + a
  TWO-COLUMN popup of the 14 talk-focus items (Squelch, Tell to Selected, Chat to All,
  Tell to Fellows, ...) on a tan panel; channel items set the active outbound channel.

Build + 392 App tests green. Visual confirmation in progress.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@
2026-06-16 09:37:40 +02:00

100 lines
3.6 KiB
C#

using System.Linq;
using AcDream.App.UI;
using AcDream.UI.Abstractions;
namespace AcDream.App.Tests.UI;
public class UiChannelMenuTests
{
[Fact]
public void Items_HasExpected14Entries()
{
// Retail gmMainChatUI::InitTalkFocusMenu: squelch + tell-selected + 12 channels.
Assert.Equal(14, UiChannelMenu.Items.Length);
}
[Fact]
public void Items_FirstEntry_IsSquelch_Special()
{
Assert.Equal("Squelch (ignore)", UiChannelMenu.Items[0].Label);
Assert.Null(UiChannelMenu.Items[0].Channel); // special item, no channel
}
[Fact]
public void Items_LastEntry_IsOlthoi()
{
var last = UiChannelMenu.Items[^1];
Assert.Equal("Tell to Olthoi Chat", last.Label);
Assert.Equal(ChatChannelKind.Olthoi, last.Channel);
}
[Fact]
public void Items_ContainAll12ChannelKinds()
{
var kinds = new HashSet<ChatChannelKind>(
UiChannelMenu.Items.Where(i => i.Channel is not null).Select(i => i.Channel!.Value));
foreach (var k in new[]
{
ChatChannelKind.Say, ChatChannelKind.General, ChatChannelKind.Trade, ChatChannelKind.Lfg,
ChatChannelKind.Fellowship, ChatChannelKind.Allegiance, ChatChannelKind.Patron,
ChatChannelKind.Vassals, ChatChannelKind.Monarch, ChatChannelKind.Roleplay,
ChatChannelKind.Society, ChatChannelKind.Olthoi,
})
Assert.Contains(k, kinds);
}
[Fact]
public void DefaultSelected_IsSay()
{
var menu = new UiChannelMenu();
Assert.Equal(ChatChannelKind.Say, menu.Selected);
}
[Fact]
public void Select_LeftColumnItem_FiresChannel()
{
var menu = new UiChannelMenu { Width = 80f, Height = 18f };
var openEvt = new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, 5); // open
Assert.True(menu.OnEvent(openEvt));
ChatChannelKind? fired = null;
menu.OnChannelChanged = k => fired = k;
// PopupH = 7*16 = 112, top = -112. "Chat to All" (Say) is index 2 = left col, row 2:
// y in [-112+32, -112+48) = [-80,-64). Click (lx=10 < ColW, ly=-72) → idx 2 → Say.
var selEvt = new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, -72);
Assert.True(menu.OnEvent(selEvt));
Assert.Equal(ChatChannelKind.Say, fired);
Assert.Equal(ChatChannelKind.Say, menu.Selected);
}
[Fact]
public void Select_RightColumnItem_FiresChannel()
{
var menu = new UiChannelMenu { Width = 80f, Height = 18f };
Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, 5))); // open
ChatChannelKind? fired = null;
menu.OnChannelChanged = k => fired = k;
// "Tell to Monarch" is index 7 = right col (lx >= ColW 150), row 0:
// y in [-112, -96). Click (lx=160, ly=-104) → col 1, row 0 → idx 7 → Monarch.
Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 160, -104)));
Assert.Equal(ChatChannelKind.Monarch, fired);
Assert.Equal(ChatChannelKind.Monarch, menu.Selected);
}
[Fact]
public void Select_SpecialItem_DoesNotFire()
{
var menu = new UiChannelMenu { Width = 80f, Height = 18f };
Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, 5))); // open
int fired = 0;
menu.OnChannelChanged = _ => fired++;
// "Squelch (ignore)" is index 0 = left col, row 0: y in [-112, -96). No channel.
Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, -104)));
Assert.Equal(0, fired); // special item is a no-op
}
}