refactor(tabs): split TabIconGlyphResolver, single-source glyph pool, polish
This commit is contained in:
@@ -395,9 +395,8 @@ public class Tab
|
|||||||
public string Name = Language.Tab_DefaultName;
|
public string Name = Language.Tab_DefaultName;
|
||||||
|
|
||||||
// v1.2.0 — optionaler FontAwesome-Glyph-Name. Null bedeutet:
|
// v1.2.0 — optionaler FontAwesome-Glyph-Name. Null bedeutet:
|
||||||
// Default-Mapping aus TabIconMapping greift (basiert auf Tab-Name
|
// Default-Mapping aus TabIconMapping greift (basiert auf Tab-Name).
|
||||||
// oder erstem SelectedChannels-Eintrag). User können hier per
|
// User können hier per Settings → Tabs einen eigenen Glyph setzen.
|
||||||
// Settings → Tabs einen eigenen Glyph setzen.
|
|
||||||
public string? Icon = null;
|
public string? Icon = null;
|
||||||
|
|
||||||
[Obsolete("Removed in favor of SelectedChannels")]
|
[Obsolete("Removed in favor of SelectedChannels")]
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
namespace HellionChat.Ui;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reine String-Resolver-Logik ohne Dalamud-Dependency. Bewusst in
|
||||||
|
/// eigener Datei (Dependency-Boundary auf File-Level sichtbar), damit
|
||||||
|
/// Tests (HellionChat.Tests, Microsoft.NET.Sdk ohne Dalamud-Reference)
|
||||||
|
/// sie aufrufen können, ohne dass die JIT beim Methodenaufruf die
|
||||||
|
/// Dalamud-Assembly laden muss.
|
||||||
|
///
|
||||||
|
/// Wird im Settings-UI (T7) für die Glyph-Picker-Combobox und im
|
||||||
|
/// Render-Code indirekt über <see cref="TabIconMapping.Resolve(Tab)"/>
|
||||||
|
/// verwendet.
|
||||||
|
/// </summary>
|
||||||
|
internal static class TabIconGlyphResolver
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Picker-Options-Pool — Single Source of Truth für das Glyph-Set.
|
||||||
|
/// Reihenfolge ist die UI-Reihenfolge im Settings-Tab Icon-Combobox.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly IReadOnlyList<string> PickerOptions =
|
||||||
|
["comment", "comments", "cog", "users", "user-friends", "link",
|
||||||
|
"envelope", "clock", "hashtag", "star", "heart", "bell",
|
||||||
|
"bookmark", "flag", "fire"];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Glyph-Set, das überhaupt als Override akzeptiert wird. Aus
|
||||||
|
/// <see cref="PickerOptions"/> abgeleitet — KnownGlyphs nie
|
||||||
|
/// manuell pflegen.
|
||||||
|
/// </summary>
|
||||||
|
private static readonly HashSet<string> KnownGlyphs =
|
||||||
|
new(PickerOptions, StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tab-Name → Default-Glyph-Name. Tab.Name wird per Lokalisierung
|
||||||
|
/// gesetzt; wir matchen daher gegen einen Pool aus DE/EN-Synonymen.
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Dictionary<string, string> NameDefaults = new(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
["allgemein"] = "comment",
|
||||||
|
["general"] = "comment",
|
||||||
|
["system"] = "cog",
|
||||||
|
["free company"] = "users",
|
||||||
|
["fc"] = "users",
|
||||||
|
["gruppe"] = "user-friends",
|
||||||
|
["group"] = "user-friends",
|
||||||
|
["party"] = "user-friends",
|
||||||
|
["linkshell"] = "link",
|
||||||
|
["ls"] = "link",
|
||||||
|
["cwls"] = "link",
|
||||||
|
["tells"] = "envelope",
|
||||||
|
["tell"] = "envelope",
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test-Surface: Glyph-Name-Resolver ohne Dalamud-Dependency.
|
||||||
|
/// Reihenfolge:
|
||||||
|
/// 1. Tab.Icon-Override (falls gesetzt und nicht nur Whitespace):
|
||||||
|
/// 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"
|
||||||
|
/// 3. Tab-Name-Default (<see cref="NameDefaults"/>-Lookup)
|
||||||
|
/// 4. Fallback "hashtag"
|
||||||
|
/// </summary>
|
||||||
|
public static string ResolveGlyphName(Tab tab)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(tab.Icon))
|
||||||
|
return KnownGlyphs.Contains(tab.Icon) ? tab.Icon : "hashtag";
|
||||||
|
|
||||||
|
if (tab.IsTempTab)
|
||||||
|
return "clock";
|
||||||
|
|
||||||
|
if (tab.Name is { } name && NameDefaults.TryGetValue(name, out var byName))
|
||||||
|
return byName;
|
||||||
|
|
||||||
|
return "hashtag";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,21 +8,24 @@ namespace HellionChat.Ui;
|
|||||||
/// User können in Settings → Tabs per Tab.Icon-Override eigene
|
/// User können in Settings → Tabs per Tab.Icon-Override eigene
|
||||||
/// FontAwesome-Glyphen setzen.
|
/// FontAwesome-Glyphen setzen.
|
||||||
///
|
///
|
||||||
/// Aufteilung:
|
/// Diese Klasse ist Dalamud-abhängig (FontAwesomeIcon-Enum). Die
|
||||||
/// - <see cref="ResolveGlyphName(Tab)"/> + <see cref="PickerOptions"/>
|
/// reine String-Resolver-Logik liegt bewusst in
|
||||||
/// sind reine String-Logik und stehen aus Test-Sicht ohne
|
/// <see cref="TabIconGlyphResolver"/> (eigene Datei, ohne
|
||||||
/// Dalamud-Dependency zur Verfügung (siehe Hilfsklasse
|
/// Dalamud-Imports), damit Tests sie ohne Dalamud-Reference aufrufen
|
||||||
/// <see cref="TabIconGlyphResolver"/> in derselben Datei, die ohne
|
/// können.
|
||||||
/// Dalamud-Imports auskommt).
|
|
||||||
/// - <see cref="Resolve(Tab)"/> liefert den FontAwesomeIcon-Enum-Wert
|
|
||||||
/// für Render-Code (T3/T5/T7) und ist daher Dalamud-abhängig.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class TabIconMapping
|
internal static class TabIconMapping
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// FontAwesome-Glyph-Name → Icon-Enum-Lookup. Wird für die
|
/// FontAwesome-Glyph-Name → Icon-Enum-Lookup. Wird für die
|
||||||
/// Production-Resolve-API benötigt. Enthält nur Glyphen aus
|
/// Production-Resolve-API benötigt.
|
||||||
/// <see cref="TabIconGlyphResolver.PickerOptions"/>.
|
///
|
||||||
|
/// INVARIANTE: Jeder Key in <see cref="GlyphLookup"/> muss auch in
|
||||||
|
/// <see cref="TabIconGlyphResolver.PickerOptions"/> stehen. Wird
|
||||||
|
/// ein Glyph zu PickerOptions hinzugefügt, aber nicht hier, fällt
|
||||||
|
/// die Override-Auflösung still auf <see cref="FontAwesomeIcon.Hashtag"/>
|
||||||
|
/// zurück (degraded, kein Crash). Build-Time-Enforcement ist nicht
|
||||||
|
/// möglich, weil PickerOptions ohne Dalamud-Reference auskommt.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly Dictionary<string, FontAwesomeIcon> GlyphLookup = new(StringComparer.OrdinalIgnoreCase)
|
private static readonly Dictionary<string, FontAwesomeIcon> GlyphLookup = new(StringComparer.OrdinalIgnoreCase)
|
||||||
{
|
{
|
||||||
@@ -43,22 +46,10 @@ internal static class TabIconMapping
|
|||||||
["fire"] = FontAwesomeIcon.Fire,
|
["fire"] = FontAwesomeIcon.Fire,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Picker-Options-Pool — Pass-through zu <see cref="TabIconGlyphResolver"/>.
|
|
||||||
/// </summary>
|
|
||||||
public static IReadOnlyList<string> PickerOptions => TabIconGlyphResolver.PickerOptions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pass-through zu <see cref="TabIconGlyphResolver.ResolveGlyphName(Tab)"/>.
|
|
||||||
/// Liegt hier nochmal als Convenience-Alias, damit Aufrufer nur
|
|
||||||
/// <see cref="TabIconMapping"/> kennen müssen.
|
|
||||||
/// </summary>
|
|
||||||
public static string ResolveGlyphName(Tab tab) => TabIconGlyphResolver.ResolveGlyphName(tab);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Production-Surface: liefert das Icon für einen Tab. Wrapper um
|
/// Production-Surface: liefert das Icon für einen Tab. Wrapper um
|
||||||
/// <see cref="TabIconGlyphResolver.ResolveGlyphName(Tab)"/> plus
|
/// <see cref="TabIconGlyphResolver.ResolveGlyphName(Tab)"/> plus
|
||||||
/// Enum-Lookup. Wird von Render-Code (T3, T5, T7) verwendet.
|
/// Enum-Lookup. Wird von Render-Code (T3, T5) verwendet.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static FontAwesomeIcon Resolve(Tab tab)
|
public static FontAwesomeIcon Resolve(Tab tab)
|
||||||
{
|
{
|
||||||
@@ -68,78 +59,3 @@ internal static class TabIconMapping
|
|||||||
: FontAwesomeIcon.Hashtag;
|
: FontAwesomeIcon.Hashtag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reine String-Resolver-Logik ohne Dalamud-Dependency. Bewusst
|
|
||||||
/// separat, damit Tests (HellionChat.Tests, Microsoft.NET.Sdk ohne
|
|
||||||
/// Dalamud-Reference) sie aufrufen können, ohne dass die JIT beim
|
|
||||||
/// Methodenaufruf die Dalamud-Assembly laden muss.
|
|
||||||
/// </summary>
|
|
||||||
internal static class TabIconGlyphResolver
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Glyph-Set, das überhaupt als Override akzeptiert wird. Spiegelt
|
|
||||||
/// die Keys aus <see cref="TabIconMapping.GlyphLookup"/>.
|
|
||||||
/// </summary>
|
|
||||||
private static readonly HashSet<string> KnownGlyphs = new(StringComparer.OrdinalIgnoreCase)
|
|
||||||
{
|
|
||||||
"comment", "comments", "cog", "users", "user-friends", "link",
|
|
||||||
"envelope", "clock", "hashtag", "star", "heart", "bell",
|
|
||||||
"bookmark", "flag", "fire",
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Picker-Options-Pool. Wird im Settings-Tab Icon-Combobox angezeigt.
|
|
||||||
/// Reihenfolge ist die UI-Reihenfolge.
|
|
||||||
/// </summary>
|
|
||||||
public static readonly IReadOnlyList<string> PickerOptions =
|
|
||||||
["comment", "comments", "cog", "users", "user-friends", "link",
|
|
||||||
"envelope", "clock", "hashtag", "star", "heart", "bell",
|
|
||||||
"bookmark", "flag", "fire"];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tab-Name → Default-Glyph-Name. Tab.Name wird per Lokalisierung
|
|
||||||
/// gesetzt; wir matchen daher gegen einen Pool aus DE/EN-Synonymen.
|
|
||||||
/// </summary>
|
|
||||||
private static readonly Dictionary<string, string> NameDefaults = new(StringComparer.OrdinalIgnoreCase)
|
|
||||||
{
|
|
||||||
["allgemein"] = "comment",
|
|
||||||
["general"] = "comment",
|
|
||||||
["system"] = "cog",
|
|
||||||
["free company"] = "users",
|
|
||||||
["fc"] = "users",
|
|
||||||
["gruppe"] = "user-friends",
|
|
||||||
["group"] = "user-friends",
|
|
||||||
["party"] = "user-friends",
|
|
||||||
["linkshell"] = "link",
|
|
||||||
["ls"] = "link",
|
|
||||||
["cwls"] = "link",
|
|
||||||
["tells"] = "envelope",
|
|
||||||
["tell"] = "envelope",
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Test-Surface: Glyph-Name-Resolver ohne Dalamud-Dependency.
|
|
||||||
/// Reihenfolge:
|
|
||||||
/// 1. Tab.Icon-Override (falls gesetzt):
|
|
||||||
/// 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"
|
|
||||||
/// 3. Tab-Name-Default (<see cref="NameDefaults"/>-Lookup)
|
|
||||||
/// 4. Fallback "hashtag"
|
|
||||||
/// </summary>
|
|
||||||
public static string ResolveGlyphName(Tab tab)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(tab.Icon))
|
|
||||||
return KnownGlyphs.Contains(tab.Icon) ? tab.Icon : "hashtag";
|
|
||||||
|
|
||||||
if (tab.IsTempTab)
|
|
||||||
return "clock";
|
|
||||||
|
|
||||||
if (NameDefaults.TryGetValue(tab.Name, out var byName))
|
|
||||||
return byName;
|
|
||||||
|
|
||||||
return "hashtag";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user