using System; using System.Numerics; using AcDream.UI.Abstractions; namespace AcDream.App.UI; /// /// Chat channel selector (the "Chat ▸" button). Port of retail /// UIElement_Menu as used by gmMainChatUI::InitTalkFocusMenu @0x4cdc50: /// a button whose label is the active channel; clicking opens a popup of channels; /// selecting one calls SetTalkFocus (here: ). /// public sealed class UiChannelMenu : UiElement { public readonly record struct Item(string Label, ChatChannelKind Channel); /// Retail talk-focus channels (subset acdream's ChatInputParser routes). public static readonly Item[] Channels = { new("Say", ChatChannelKind.Say), new("General", ChatChannelKind.General), new("Trade", ChatChannelKind.Trade), new("LFG", ChatChannelKind.Lfg), new("Fellowship", ChatChannelKind.Fellowship), new("Allegiance", ChatChannelKind.Allegiance), new("Patron", ChatChannelKind.Patron), new("Vassals", ChatChannelKind.Vassals), new("Monarch", ChatChannelKind.Monarch), new("Roleplay", ChatChannelKind.Roleplay), new("Society", ChatChannelKind.Society), new("Olthoi", ChatChannelKind.Olthoi), }; public ChatChannelKind Selected { get; private set; } = ChatChannelKind.Say; public Action? OnChannelChanged { get; set; } public UiDatFont? DatFont { get; set; } public AcDream.App.Rendering.BitmapFont? Font { get; set; } public Func? SpriteResolve { get; set; } public uint NormalSprite { get; set; } public uint PressedSprite { get; set; } public Vector4 TextColor { get; set; } = new(1f, 0.85f, 0.4f, 1f); private bool _open; private const float ItemH = 16f; private const float PopupW = 90f; public UiChannelMenu() { CapturesPointerDrag = true; } private string Label => FindLabel(Selected); private static string FindLabel(ChatChannelKind k) { foreach (var it in Channels) if (it.Channel == k) return it.Label; return "Chat"; } protected override void OnDraw(UiRenderContext ctx) { if (SpriteResolve is { } resolve) { var (tex, _, _) = resolve(_open ? PressedSprite : NormalSprite); if (tex != 0) ctx.DrawSprite(tex, 0, 0, Width, Height, 0f, 0f, 1f, 1f, Vector4.One); } DrawLabel(ctx, Label + " >", 2f, (Height - LineH()) * 0.5f); if (_open) { float h = Channels.Length * ItemH; float top = -h; // popup opens UPWARD (chat sits at screen bottom) ctx.DrawRect(0, top, MathF.Max(Width, PopupW), h, new(0f, 0f, 0f, 0.85f)); for (int i = 0; i < Channels.Length; i++) DrawLabel(ctx, Channels[i].Label, 2f, top + i * ItemH); } } private float LineH() => DatFont?.LineHeight ?? Font?.LineHeight ?? 14f; private void DrawLabel(UiRenderContext ctx, string s, float x, float y) { if (DatFont is { } df) ctx.DrawStringDat(df, s, x, y, TextColor); else ctx.DrawString(s, x, y, TextColor, Font); } protected override bool OnHitTest(float lx, float ly) => _open ? (lx >= 0 && lx < MathF.Max(Width, PopupW) && ly >= -Channels.Length * ItemH && ly < Height) : base.OnHitTest(lx, ly); public override bool OnEvent(in UiEvent e) { if (e.Type == UiEventType.MouseDown) { float ly = e.Data2; if (_open && ly < 0) { int idx = (int)((ly + Channels.Length * ItemH) / ItemH); if (idx >= 0 && idx < Channels.Length) { Selected = Channels[idx].Channel; OnChannelChanged?.Invoke(Selected); } _open = false; return true; } _open = !_open; return true; } return false; } }