From b8ed2a1ce5d28f02e7f264cae1b9399677e35111 Mon Sep 17 00:00:00 2001 From: JonKazama-Hellion Date: Tue, 5 May 2026 23:36:52 +0200 Subject: [PATCH] feat(sidebar): per-tell hashed icon variety + unread-dot indicator --- HellionChat/Ui/AutoTellTabTint.cs | 43 ++++++++++++++++++++++++++ HellionChat/Ui/ChatLogWindow.cs | 20 ++++++++++++ HellionChat/Ui/TabIconGlyphResolver.cs | 7 +++-- HellionChat/Ui/TabIconMapping.cs | 13 +++++++- 4 files changed, 79 insertions(+), 4 deletions(-) diff --git a/HellionChat/Ui/AutoTellTabTint.cs b/HellionChat/Ui/AutoTellTabTint.cs index da74d14..2ea733a 100644 --- a/HellionChat/Ui/AutoTellTabTint.cs +++ b/HellionChat/Ui/AutoTellTabTint.cs @@ -61,4 +61,47 @@ internal static class AutoTellTabTint var hash = (uint)(key.GetHashCode() & 0x7FFFFFFF); return Palette[(int)(hash % Palette.Count)]; } + + /// + /// Tell-spezifischer Icon-Pool. 7 visuell distinkte FontAwesome-Glyphen + /// die im Tell-Kontext sinnvoll wirken (envelope = Tell-Default, star/ + /// heart/bell = personalisiert, bookmark/flag/fire = markiert/wichtig). + /// Bewusst kein cog/comment/users — die wären für System-/Group-Tabs + /// reserviert und würden im Tell-Bereich verwirrend wirken. + /// + public static readonly IReadOnlyList IconPool = new[] + { + "envelope", + "star", + "heart", + "bell", + "bookmark", + "flag", + "fire", + }; + + /// + /// Fallback-Icon bei ungültigem Input. "envelope" passt semantisch zum + /// Tell-Kontext besser als das alte hardcoded "clock". + /// + public const string IconFallback = "envelope"; + + /// + /// Liefert ein konsistentes Icon-Glyph für einen Tell-Partner. + /// Nutzt einen anderen Hash-Bias als For() (Color), damit Icon und + /// Color unabhängig variieren — gibt 7 × 12 = 84 distinct Combinations. + /// + public static string IconFor(string name, uint world) + { + if (string.IsNullOrEmpty(name) || world == 0) + return IconFallback; + + // Anderer Hash-Bias als For() (verschiedene Modulo-Basis): wir + // nutzen "world@name" statt "name@world" damit Icon und Color + // nicht synchron variieren. Ohne Bias-Trennung würden alle Tells + // mit derselben Color auch dasselbe Icon haben. + var key = $"{world}@{name}"; + var hash = (uint)(key.GetHashCode() & 0x7FFFFFFF); + return IconPool[(int)(hash % IconPool.Count)]; + } } diff --git a/HellionChat/Ui/ChatLogWindow.cs b/HellionChat/Ui/ChatLogWindow.cs index 68c0a3b..c5ee315 100644 --- a/HellionChat/Ui/ChatLogWindow.cs +++ b/HellionChat/Ui/ChatLogWindow.cs @@ -1591,6 +1591,26 @@ public sealed class ChatLogWindow : Window 1.5f); // leichter Rounding } + // v1.2.0 — Unread-Dot oben rechts am Icon. Sichtbar ohne Hover, damit + // User Tabs mit ungelesenen Messages sofort erkennt. Aktive Tabs haben + // per Konvention Unread = 0 (LastTab-Branch in ChatLogWindow), daher + // kollidiert der Dot nicht mit der Active-Pill. + if (!isCurrentTab && tab.UnreadMode != UnreadMode.None && tab.Unread > 0) + { + var min = ImGui.GetItemRectMin(); + var max = ImGui.GetItemRectMax(); + const float dotRadius = 4f; + const float dotPadding = 3f; + var dotCenter = new Vector2( + max.X - dotRadius - dotPadding, + min.Y + dotRadius + dotPadding); + ImGui.GetWindowDrawList().AddCircleFilled( + dotCenter, + dotRadius, + ColourUtil.RgbaToAbgr(theme.Colors.StatusDanger), + 12); + } + // Tooltip mit Tab-Name + Unread-Counter beim Hover. if (ImGui.IsItemHovered()) { diff --git a/HellionChat/Ui/TabIconGlyphResolver.cs b/HellionChat/Ui/TabIconGlyphResolver.cs index f587c12..7986236 100644 --- a/HellionChat/Ui/TabIconGlyphResolver.cs +++ b/HellionChat/Ui/TabIconGlyphResolver.cs @@ -58,17 +58,18 @@ internal static class TabIconGlyphResolver /// a) bekannter Glyph → diesen Glyph /// b) unbekannter Glyph → harter Fallback "hashtag" (User hat /// bewusst etwas gesetzt, also überstimmt das die Defaults) - /// 2. Auto-Tell-Tab → "clock" + /// 2. Auto-Tell-Tab → falls + /// übergeben, sonst "clock". /// 3. Tab-Name-Default (-Lookup) /// 4. Fallback "hashtag" /// - public static string ResolveGlyphName(Tab tab) + public static string ResolveGlyphName(Tab tab, string? autoTellGlyph = null) { if (!string.IsNullOrWhiteSpace(tab.Icon)) return KnownGlyphs.Contains(tab.Icon) ? tab.Icon : "hashtag"; if (tab.IsTempTab) - return "clock"; + return autoTellGlyph ?? "clock"; if (tab.Name is { } name && NameDefaults.TryGetValue(name, out var byName)) return byName; diff --git a/HellionChat/Ui/TabIconMapping.cs b/HellionChat/Ui/TabIconMapping.cs index 208efee..c185231 100644 --- a/HellionChat/Ui/TabIconMapping.cs +++ b/HellionChat/Ui/TabIconMapping.cs @@ -53,7 +53,18 @@ internal static class TabIconMapping /// public static FontAwesomeIcon Resolve(Tab tab) { - var glyph = TabIconGlyphResolver.ResolveGlyphName(tab); + // v1.2.0 — Auto-Tell-Tabs bekommen ein per-Partner gehashtes + // Icon aus dem Tell-Pool. Damit unterscheiden sich parallele + // Tells nicht nur über die Color (For), sondern auch über die + // Glyph-Form. Berechnung bleibt hier (Dalamud-bound), weil + // TellTarget Dalamud-Imports hat. + string? autoTellGlyph = null; + if (tab.IsTempTab && tab.TellTarget != null && tab.TellTarget.IsSet()) + { + autoTellGlyph = AutoTellTabTint.IconFor(tab.TellTarget.Name, tab.TellTarget.World); + } + + var glyph = TabIconGlyphResolver.ResolveGlyphName(tab, autoTellGlyph); return GlyphLookup.TryGetValue(glyph, out var icon) ? icon : FontAwesomeIcon.Hashtag;