From 2f099fd4e170e3825e45afb3be75818e1f12c6fc Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Sat, 23 May 2026 20:58:50 +0200 Subject: [PATCH] fix(ui): clickable channel pill, auto-seed channel, kill outer scrollbar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three smoke bugs from the second in-game test: 1. The channel pill was draw-list only, so it didn't react to clicks and there was no way to switch channels inside a tab. The pill now has a hit area on top and opens a popup that lists every ChatType in tab.SelectedChannels with a ToInputChannel mapping; selecting one writes through CurrentChannel.SetChannel. 2. Switching to Allgemein / Gruppe / Linkshell still showed "—" because tab.CurrentChannel.Channel stayed at Invalid until somebody set it. The sidebar now seeds CurrentChannel on tab activation by walking SelectedChannels for the first key with a valid mapping, so every tab opens with its own real channel instead of inheriting the FC default. 3. MainWindow still surfaced an outer scrollbar next to the message list's own scroll. Adding NoScrollbar + NoScrollWithMouse to the window flags strips the second bar — the body child owns scroll on its own. Plus the system-icon path: System / BattleSystem / GatheringSystem / Error / Notice / LootNotice all map to fa-cog now, so the System tab renders the gear instead of falling back to the generic comment. --- HellionChat/Ui/Components/InputBar.cs | 39 +++++++++++++++++++++++++-- HellionChat/Ui/Components/Sidebar.cs | 30 ++++++++++++++++++++- HellionChat/Ui/Windows/MainWindow.cs | 3 +++ 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/HellionChat/Ui/Components/InputBar.cs b/HellionChat/Ui/Components/InputBar.cs index 0601264..54cef39 100644 --- a/HellionChat/Ui/Components/InputBar.cs +++ b/HellionChat/Ui/Components/InputBar.cs @@ -110,8 +110,43 @@ internal sealed class InputBar dl.AddRectFilled(origin, max, pillAbgr, 6f); dl.AddText(origin + new Vector2(PillPaddingX, 3f), textAbgr, label); - // Reserve the layout slot so SameLine after the pill knows the width. - ImGui.Dummy(new Vector2(width, PillHeight)); + // Hit area over the rendered pill so a click opens the channel + // picker. InvisibleButton both reserves the layout slot and gives + // the popup a stable anchor item. + ImGui.InvisibleButton("##hellion-pill", new Vector2(width, PillHeight)); + if (ImGui.IsItemClicked() && tab is not null) + ImGui.OpenPopup("##hellion-channel-picker"); + + DrawChannelPickerPopup(tab); + } + + private static void DrawChannelPickerPopup(Tab? tab) + { + if (!ImGui.BeginPopup("##hellion-channel-picker")) + return; + + try + { + if (tab is null || tab.SelectedChannels.Count == 0) + { + ImGui.TextDisabled("No channels"); + return; + } + + foreach (var chatType in tab.SelectedChannels.Keys) + { + if (chatType.ToInputChannel() is not { } input) + continue; + + var isCurrent = tab.CurrentChannel.Channel == input; + if (ImGui.Selectable(input.ToChatType().Name(), isCurrent)) + tab.CurrentChannel.SetChannel(input); + } + } + finally + { + ImGui.EndPopup(); + } } private void DrawInputField(Tab? activeTab) diff --git a/HellionChat/Ui/Components/Sidebar.cs b/HellionChat/Ui/Components/Sidebar.cs index 3b3bda0..1efed53 100644 --- a/HellionChat/Ui/Components/Sidebar.cs +++ b/HellionChat/Ui/Components/Sidebar.cs @@ -117,7 +117,10 @@ internal sealed class Sidebar ImGui.InvisibleButton("row", new Vector2(tabHitWidth, RowHeight)); var rowHovered = ImGui.IsItemHovered(); if (ImGui.IsItemClicked()) + { activeTab = tab; + EnsureCurrentChannel(tab); + } dl.DrawHoverSheen( origin, @@ -209,11 +212,36 @@ internal sealed class Sidebar ChatType.NoviceNetwork or ChatType.NoviceNetworkSystem => FontAwesomeIcon.Users, ChatType.PvpTeam or ChatType.PvpTeamAnnouncement or ChatType.PvpTeamLoginLogout => FontAwesomeIcon.Users, - ChatType.System or ChatType.Echo => FontAwesomeIcon.Cog, + ChatType.System + or ChatType.BattleSystem + or ChatType.GatheringSystem + or ChatType.Error + or ChatType.Notice + or ChatType.LootNotice + or ChatType.Echo => FontAwesomeIcon.Cog, ChatType.CustomEmote or ChatType.StandardEmote => FontAwesomeIcon.Comments, _ => FontAwesomeIcon.Comment, }; + // Pick a sensible input channel for the tab if it has none yet — + // walking SelectedChannels for the first key with a ToInputChannel + // mapping lets the channel pill render the tab's actual channel + // instead of falling back to "—" on first activation. + private static void EnsureCurrentChannel(Tab tab) + { + if (tab.CurrentChannel.Channel != InputChannel.Invalid) + return; + + foreach (var chatType in tab.SelectedChannels.Keys) + { + if (chatType.ToInputChannel() is { } input) + { + tab.CurrentChannel.SetChannel(input); + return; + } + } + } + private void LogPopOutStub(Tab tab) { // The channel-popout pool is built in a later cycle; logging here diff --git a/HellionChat/Ui/Windows/MainWindow.cs b/HellionChat/Ui/Windows/MainWindow.cs index 0d16e9a..e38c87f 100644 --- a/HellionChat/Ui/Windows/MainWindow.cs +++ b/HellionChat/Ui/Windows/MainWindow.cs @@ -51,6 +51,9 @@ internal sealed class MainWindow : Window MinimumSize = new Vector2(MinWidth, MinHeight), MaximumSize = new Vector2(float.MaxValue, float.MaxValue), }; + // The message list owns its own scroll inside the body child; + // the outer window must not show a second scrollbar. + Flags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse; IsOpen = Plugin.Config.MainWindowOpen; RespectCloseHotkey = false; }