fix(ui): clickable channel pill, auto-seed channel, kill outer scrollbar

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.
This commit is contained in:
2026-05-23 20:58:50 +02:00
parent 52b0fa7c67
commit 2f099fd4e1
3 changed files with 69 additions and 3 deletions
+37 -2
View File
@@ -110,8 +110,43 @@ internal sealed class InputBar
dl.AddRectFilled(origin, max, pillAbgr, 6f); dl.AddRectFilled(origin, max, pillAbgr, 6f);
dl.AddText(origin + new Vector2(PillPaddingX, 3f), textAbgr, label); dl.AddText(origin + new Vector2(PillPaddingX, 3f), textAbgr, label);
// Reserve the layout slot so SameLine after the pill knows the width. // Hit area over the rendered pill so a click opens the channel
ImGui.Dummy(new Vector2(width, PillHeight)); // 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) private void DrawInputField(Tab? activeTab)
+29 -1
View File
@@ -117,7 +117,10 @@ internal sealed class Sidebar
ImGui.InvisibleButton("row", new Vector2(tabHitWidth, RowHeight)); ImGui.InvisibleButton("row", new Vector2(tabHitWidth, RowHeight));
var rowHovered = ImGui.IsItemHovered(); var rowHovered = ImGui.IsItemHovered();
if (ImGui.IsItemClicked()) if (ImGui.IsItemClicked())
{
activeTab = tab; activeTab = tab;
EnsureCurrentChannel(tab);
}
dl.DrawHoverSheen( dl.DrawHoverSheen(
origin, origin,
@@ -209,11 +212,36 @@ internal sealed class Sidebar
ChatType.NoviceNetwork or ChatType.NoviceNetworkSystem => FontAwesomeIcon.Users, ChatType.NoviceNetwork or ChatType.NoviceNetworkSystem => FontAwesomeIcon.Users,
ChatType.PvpTeam or ChatType.PvpTeamAnnouncement or ChatType.PvpTeamLoginLogout => ChatType.PvpTeam or ChatType.PvpTeamAnnouncement or ChatType.PvpTeamLoginLogout =>
FontAwesomeIcon.Users, 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, ChatType.CustomEmote or ChatType.StandardEmote => FontAwesomeIcon.Comments,
_ => FontAwesomeIcon.Comment, _ => 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) private void LogPopOutStub(Tab tab)
{ {
// The channel-popout pool is built in a later cycle; logging here // The channel-popout pool is built in a later cycle; logging here
+3
View File
@@ -51,6 +51,9 @@ internal sealed class MainWindow : Window
MinimumSize = new Vector2(MinWidth, MinHeight), MinimumSize = new Vector2(MinWidth, MinHeight),
MaximumSize = new Vector2(float.MaxValue, float.MaxValue), 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; IsOpen = Plugin.Config.MainWindowOpen;
RespectCloseHotkey = false; RespectCloseHotkey = false;
} }