feat(D.2b): data-driven channel menu chrome + greying + scroll-arrow fix Investigation found the menu popup is fully dat-driven (UIElement_Menu::MakePopup @0x46d310 reads LayoutDesc 0x21000006 elements 0x1000001C/1D/1E — the "stray" top-level elements). Render the popup from the real sprites instead of a flat rect: - panel 0x0600124C, item row 0x0600124E, selected row 0x0600124D; 191x17 rows, 2 cols. - drawing rows as SPRITES also fixes the z-order (a DrawRect bg composited OVER the labels; sprites share the labels submission bucket so text lands on top). - item greying: available channels white, unavailable salmon (colorPink) — static approximation (Say/General/Trade/LFG) with an AvailabilityProvider hook for live TurbineChat state; unavailable items are inert on click. Ports ResetAllTalkFocusMenuButtons. - scroll arrows: both dat sprites point down (export-confirmed); V-flip the top button so it points up. Tabs confirmed to have NO digits in retail (blank gold frames) — acdream already matches. Build + 392 App tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> @
125 lines
4.6 KiB
C#
125 lines
4.6 KiB
C#
using System.Linq;
|
|
using AcDream.App.UI;
|
|
using AcDream.UI.Abstractions;
|
|
|
|
namespace AcDream.App.Tests.UI;
|
|
|
|
public class UiChannelMenuTests
|
|
{
|
|
// PopupH = Rows(7) * ItemH(17) = 119; popup opens upward so top = -119.
|
|
// Item idx -> col = idx/7, row = idx%7; row band y in [top+row*17, top+(row+1)*17).
|
|
// Right column needs lx >= ColW(191).
|
|
|
|
[Fact]
|
|
public void Items_HasExpected14Entries()
|
|
{
|
|
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);
|
|
}
|
|
|
|
[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()
|
|
{
|
|
Assert.Equal(ChatChannelKind.Say, new UiChannelMenu().Selected);
|
|
}
|
|
|
|
[Fact]
|
|
public void Select_AvailableLeftColumnItem_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;
|
|
|
|
// "Chat to All" (Say) is index 2 = left col, row 2: y in [-85,-68). Say is available.
|
|
Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, -76)));
|
|
Assert.Equal(ChatChannelKind.Say, fired);
|
|
Assert.Equal(ChatChannelKind.Say, menu.Selected);
|
|
}
|
|
|
|
[Fact]
|
|
public void Select_AvailableRightColumnItem_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 Trade Chat" (Trade) is index 11 = right col (lx>=191), row 4: y in [-51,-34).
|
|
Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 200, -42)));
|
|
Assert.Equal(ChatChannelKind.Trade, fired);
|
|
Assert.Equal(ChatChannelKind.Trade, 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 (null channel): y in [-119,-102).
|
|
Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, -110)));
|
|
Assert.Equal(0, fired);
|
|
}
|
|
|
|
[Fact]
|
|
public void Select_UnavailableChannel_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++;
|
|
|
|
// "Tell to Fellows" (Fellowship) is index 3 = left col, row 3: y in [-68,-51).
|
|
// Fellowship is unavailable by the default static gate, so the click is inert.
|
|
Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, -60)));
|
|
Assert.Equal(0, fired);
|
|
}
|
|
|
|
[Fact]
|
|
public void AvailabilityProvider_Overrides_DefaultGate()
|
|
{
|
|
var menu = new UiChannelMenu { Width = 80f, Height = 18f, AvailabilityProvider = _ => true };
|
|
Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, 5))); // open
|
|
|
|
ChatChannelKind? fired = null;
|
|
menu.OnChannelChanged = k => fired = k;
|
|
|
|
// With every channel available, "Tell to Fellows" (idx 3, row 3) now fires.
|
|
Assert.True(menu.OnEvent(new UiEvent(0, menu, UiEventType.MouseDown, 0, 10, -60)));
|
|
Assert.Equal(ChatChannelKind.Fellowship, fired);
|
|
}
|
|
}
|