refactor(settings): reduce SettingsWindow to a stub and drop the tab system
The seven settings tabs and the overview helper are removed. The new settings UI lands in a later cycle and will be rebuilt from scratch on the v2.x component layer; keeping the v1.5.6 tab classes around in the meantime would only carry dead dependencies through the rest of this cycle. The window stays registered so /hellion settings, the slash command path, and the UiBuilder open handlers all still resolve.
This commit is contained in:
+16
-284
@@ -1,314 +1,46 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using HellionChat.Resources;
|
using HellionChat.Resources;
|
||||||
using HellionChat.Ui.SettingsTabs;
|
|
||||||
using HellionChat.Util;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace HellionChat.Ui;
|
namespace HellionChat.Ui;
|
||||||
|
|
||||||
internal enum SettingsView
|
// Placeholder window kept alive so the slash-command paths, UiBuilder
|
||||||
|
// open-handlers and the WindowSystem registration stay functional until
|
||||||
|
// the new settings UI lands in a later cycle. The body just points users
|
||||||
|
// at the JSON config and at /hellion reset for theme recovery.
|
||||||
|
public sealed class SettingsWindow : Window
|
||||||
{
|
{
|
||||||
Overview,
|
private readonly Plugin _plugin;
|
||||||
Detail,
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class SettingsWindow : Dalamud.Interface.Windowing.Window
|
|
||||||
{
|
|
||||||
internal readonly Plugin Plugin;
|
|
||||||
|
|
||||||
private Configuration Mutable { get; }
|
|
||||||
private List<ISettingsTab> Tabs { get; }
|
|
||||||
private int CurrentTab;
|
|
||||||
private SettingsView View = SettingsView.Overview;
|
|
||||||
|
|
||||||
// Set when a section is freshly entered; the first Draw afterwards reads it
|
|
||||||
// and clears it, so each section starts collapsed every time it is opened.
|
|
||||||
private bool _sectionJustEntered;
|
|
||||||
private readonly SettingsOverview Overview;
|
|
||||||
|
|
||||||
internal SettingsWindow(Plugin plugin, ILoggerFactory loggerFactory)
|
internal SettingsWindow(Plugin plugin, ILoggerFactory loggerFactory)
|
||||||
: base($"{Language.Settings_Title.Format(Plugin.PluginName)}###chat2-settings")
|
: base($"{Language.Settings_Title.Format(Plugin.PluginName)}###chat2-settings")
|
||||||
{
|
{
|
||||||
Flags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse;
|
_plugin = plugin;
|
||||||
|
_ = loggerFactory;
|
||||||
SizeCondition = ImGuiCond.FirstUseEver;
|
SizeCondition = ImGuiCond.FirstUseEver;
|
||||||
SizeConstraints = new WindowSizeConstraints
|
SizeConstraints = new WindowSizeConstraints
|
||||||
{
|
{
|
||||||
MinimumSize = new Vector2(475, 600),
|
MinimumSize = new Vector2(400, 200),
|
||||||
MaximumSize = new Vector2(float.MaxValue, float.MaxValue),
|
MaximumSize = new Vector2(float.MaxValue, float.MaxValue),
|
||||||
};
|
};
|
||||||
|
|
||||||
Plugin = plugin;
|
|
||||||
Mutable = new Configuration();
|
|
||||||
|
|
||||||
Overview = new SettingsOverview(this);
|
|
||||||
|
|
||||||
Tabs =
|
|
||||||
[
|
|
||||||
new General(Plugin, Mutable),
|
|
||||||
new Appearance(Plugin, Mutable, loggerFactory.CreateLogger<Appearance>()),
|
|
||||||
new Chat(Plugin, Mutable),
|
|
||||||
new SettingsTabs.Window(Plugin, Mutable),
|
|
||||||
new SettingsTabs.Tabs(Plugin, Mutable),
|
|
||||||
new DataAndPrivacy(Plugin, Mutable, loggerFactory.CreateLogger<DataAndPrivacy>()),
|
|
||||||
new About(Plugin, Mutable),
|
|
||||||
];
|
|
||||||
|
|
||||||
RespectCloseHotkey = false;
|
RespectCloseHotkey = false;
|
||||||
DisableWindowSounds = true;
|
DisableWindowSounds = true;
|
||||||
|
|
||||||
Initialise();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
// Slash-command + OpenConfigUi tear-down moved to Plugin.TearDownCommands.
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Initialise()
|
|
||||||
{
|
|
||||||
Mutable.UpdateFrom(Plugin.Config, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
{
|
{
|
||||||
if (ImGui.IsWindowAppearing())
|
using (_plugin.FontManager.FontAwesome.Push())
|
||||||
{
|
ImGui.TextUnformatted(FontAwesomeIcon.InfoCircle.ToIconString());
|
||||||
Initialise();
|
|
||||||
View = SettingsView.Overview;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ESC in Detail view returns to Overview. Window focus check is
|
|
||||||
// required so ESC doesn't fire when the user targets a different window.
|
|
||||||
if (
|
|
||||||
View == SettingsView.Detail
|
|
||||||
&& ImGui.IsWindowFocused(ImGuiFocusedFlags.RootAndChildWindows)
|
|
||||||
&& ImGui.IsKeyPressed(ImGuiKey.Escape)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
View = SettingsView.Overview;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (View == SettingsView.Overview)
|
|
||||||
Overview.Draw();
|
|
||||||
else
|
|
||||||
DrawDetail();
|
|
||||||
|
|
||||||
ImGui.Separator();
|
|
||||||
DrawSaveButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void OpenSection(int tabIndex)
|
|
||||||
{
|
|
||||||
CurrentTab = tabIndex;
|
|
||||||
View = SettingsView.Detail;
|
|
||||||
_sectionJustEntered = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void OpenOverview()
|
|
||||||
{
|
|
||||||
View = SettingsView.Overview;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawDetail()
|
|
||||||
{
|
|
||||||
// Breadcrumb header -- accent cyan, clickable, returns to Overview.
|
|
||||||
using (ImRaii.PushColor(ImGuiCol.Text, 0xFF00BED2u))
|
|
||||||
using (ImRaii.PushColor(ImGuiCol.Button, 0u))
|
|
||||||
using (ImRaii.PushColor(ImGuiCol.ButtonHovered, 0x33FFFFFFu))
|
|
||||||
using (ImRaii.PushColor(ImGuiCol.ButtonActive, 0x55FFFFFFu))
|
|
||||||
{
|
|
||||||
if (ImGui.SmallButton("<- Settings"))
|
|
||||||
{
|
|
||||||
View = SettingsView.Overview;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.TextUnformatted("·");
|
ImGui.TextUnformatted("Settings UI lands in a later cycle.");
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextUnformatted(Tabs[CurrentTab].Name.Split("###")[0]);
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
ImGui.Separator();
|
ImGui.TextWrapped(
|
||||||
ImGui.Spacing();
|
"For now, edit the plugin config JSON directly. Use /hellion reset to "
|
||||||
|
+ "drop a broken custom theme out of the loader cache without touching the file on disk."
|
||||||
// Section content fills full width. Navigation back to another
|
|
||||||
// section goes via the breadcrumb or ESC.
|
|
||||||
var style = ImGui.GetStyle();
|
|
||||||
var height =
|
|
||||||
ImGui.GetContentRegionAvail().Y
|
|
||||||
- style.FramePadding.Y * 2
|
|
||||||
- style.ItemSpacing.Y
|
|
||||||
- style.ItemInnerSpacing.Y * 2
|
|
||||||
- ImGui.CalcTextSize("A").Y;
|
|
||||||
|
|
||||||
using var child = ImRaii.Child("##chat2-settings-detail", new Vector2(-1, height));
|
|
||||||
if (child.Success)
|
|
||||||
{
|
|
||||||
Tabs[CurrentTab].Draw(_sectionJustEntered);
|
|
||||||
_sectionJustEntered = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawSaveButtons()
|
|
||||||
{
|
|
||||||
var save = ImGui.Button(Language.Settings_Save);
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
if (ImGui.Button(Language.Settings_SaveAndClose))
|
|
||||||
{
|
|
||||||
save = true;
|
|
||||||
IsOpen = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
if (ImGui.Button(Language.Settings_Discard))
|
|
||||||
IsOpen = false;
|
|
||||||
|
|
||||||
const string buttonLabel = "Anna's Ko-fi";
|
|
||||||
const string buttonLabel2 = "Infi's Ko-fi";
|
|
||||||
|
|
||||||
using (ImRaii.PushColor(ImGuiCol.Button, ColourUtil.RgbaToAbgr(0xFF5E5BFF)))
|
|
||||||
using (ImRaii.PushColor(ImGuiCol.ButtonHovered, ColourUtil.RgbaToAbgr(0xFF7775FF)))
|
|
||||||
using (ImRaii.PushColor(ImGuiCol.ButtonActive, ColourUtil.RgbaToAbgr(0xFF4542FF)))
|
|
||||||
using (ImRaii.PushColor(ImGuiCol.Text, 0xFFFFFFFF))
|
|
||||||
{
|
|
||||||
var buttonWidth =
|
|
||||||
ImGui.CalcTextSize(buttonLabel).X + ImGui.GetStyle().FramePadding.X * 2;
|
|
||||||
var buttonWidth2 =
|
|
||||||
ImGui.CalcTextSize(buttonLabel2).X + ImGui.GetStyle().FramePadding.X * 2;
|
|
||||||
ImGui.SameLine(
|
|
||||||
ImGui.GetContentRegionAvail().X
|
|
||||||
- buttonWidth
|
|
||||||
- buttonWidth2
|
|
||||||
- ImGui.GetStyle().ItemSpacing.X
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (ImGui.Button(buttonLabel2))
|
|
||||||
Plugin.PlatformUtil.OpenLink("https://ko-fi.com/infiii");
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
if (ImGui.Button(buttonLabel))
|
|
||||||
Plugin.PlatformUtil.OpenLink("https://ko-fi.com/lojewalo");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!save)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var hideChanged = !Mutable.HideChat && Mutable.HideChat != Plugin.Config.HideChat;
|
|
||||||
var languageChanged = Mutable.LanguageOverride != Plugin.Config.LanguageOverride;
|
|
||||||
|
|
||||||
// v1.5.3: Auto-enable the ExtraGlyphRanges flag matching the new
|
|
||||||
// locale so non-Latin scripts render immediately. Without this,
|
|
||||||
// a user switching to Korean would see "===" until they manually
|
|
||||||
// tick the Korean range in Fonts & Colours.
|
|
||||||
if (languageChanged)
|
|
||||||
{
|
|
||||||
var required = Mutable.LanguageOverride.RequiredGlyphRanges();
|
|
||||||
if (required != 0)
|
|
||||||
Mutable.ExtraGlyphRanges |= required;
|
|
||||||
}
|
|
||||||
|
|
||||||
var fontChanged =
|
|
||||||
Mutable.GlobalFontV2 != Plugin.Config.GlobalFontV2
|
|
||||||
|| Mutable.JapaneseFontV2 != Plugin.Config.JapaneseFontV2
|
|
||||||
|| Mutable.ItalicFontV2 != Plugin.Config.ItalicFontV2
|
|
||||||
|| Mutable.ExtraGlyphRanges != Plugin.Config.ExtraGlyphRanges
|
|
||||||
|| Mutable.UseHellionFont != Plugin.Config.UseHellionFont;
|
|
||||||
var fontSizeChanged =
|
|
||||||
Math.Abs(Mutable.SymbolsFontSizeV2 - Plugin.Config.SymbolsFontSizeV2) > 0.001
|
|
||||||
|| Math.Abs(Mutable.FontSizeV2 - Plugin.Config.FontSizeV2) > 0.001;
|
|
||||||
var italicStateChanged = Mutable.ItalicEnabled != Plugin.Config.ItalicEnabled;
|
|
||||||
|
|
||||||
// Only refilter when filter-relevant settings changed. Clear+Refilter
|
|
||||||
// reloads from the DB and silently drops in-session messages that
|
|
||||||
// weren't persisted (Privacy-First blocks most channels). Cosmetic
|
|
||||||
// changes (theme, icons, layout) skip the cycle.
|
|
||||||
var filtersChanged = HasFilterRelevantChanges();
|
|
||||||
|
|
||||||
Plugin.Config.UpdateFrom(Mutable, true);
|
|
||||||
|
|
||||||
// Defer save by 60 frames to avoid committing changes that cause a crash.
|
|
||||||
Plugin.DeferredSaveFrames = 60;
|
|
||||||
if (filtersChanged)
|
|
||||||
{
|
|
||||||
Plugin.MessageManager.ClearAllTabs();
|
|
||||||
Plugin.MessageManager.FilterAllTabsAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fontChanged || fontSizeChanged || italicStateChanged)
|
|
||||||
Plugin.FontManager.RebuildDelegateFonts();
|
|
||||||
|
|
||||||
if (languageChanged)
|
|
||||||
Plugin.LanguageChanged(Plugin.Interface.UiLanguage);
|
|
||||||
|
|
||||||
if (hideChanged)
|
|
||||||
GameFunctions.GameFunctions.SetChatInteractable(true);
|
|
||||||
|
|
||||||
if (Plugin.Config.ShowEmotes)
|
|
||||||
_ = EmoteCache.LoadData();
|
|
||||||
|
|
||||||
Initialise();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if any filter-relevant setting changed between Plugin.Config
|
|
||||||
// and the Mutable copy. Gates Clear+Refilter on Save so cosmetic changes
|
|
||||||
// don't wipe in-session chat history.
|
|
||||||
private bool HasFilterRelevantChanges()
|
|
||||||
{
|
|
||||||
if (Mutable.PrivacyFilterEnabled != Plugin.Config.PrivacyFilterEnabled)
|
|
||||||
return true;
|
|
||||||
if (Mutable.PrivacyPersistUnknownChannels != Plugin.Config.PrivacyPersistUnknownChannels)
|
|
||||||
return true;
|
|
||||||
if (!Mutable.PrivacyPersistChannels.SetEquals(Plugin.Config.PrivacyPersistChannels))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// FilterIncludePreviousSessions changes the GetMostRecentMessages
|
|
||||||
// window and is filter-relevant even outside the Privacy block.
|
|
||||||
if (Mutable.FilterIncludePreviousSessions != Plugin.Config.FilterIncludePreviousSessions)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Compare persistent tabs only -- TempTabs are never refiltered.
|
|
||||||
var origPersistent = Plugin.Config.Tabs.Where(t => !t.IsTempTab).ToList();
|
|
||||||
var newPersistent = Mutable.Tabs.Where(t => !t.IsTempTab).ToList();
|
|
||||||
|
|
||||||
if (origPersistent.Count != newPersistent.Count)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
for (var i = 0; i < origPersistent.Count; i++)
|
|
||||||
{
|
|
||||||
var orig = origPersistent[i];
|
|
||||||
var neu = newPersistent[i];
|
|
||||||
|
|
||||||
// Identifier mismatch means reorder or slot swap -- treat as filter-relevant.
|
|
||||||
if (orig.Identifier != neu.Identifier)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (orig.ExtraChatAll != neu.ExtraChatAll)
|
|
||||||
return true;
|
|
||||||
if (!orig.ExtraChatChannels.SetEquals(neu.ExtraChatChannels))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (orig.SelectedChannels.Count != neu.SelectedChannels.Count)
|
|
||||||
return true;
|
|
||||||
foreach (var pair in orig.SelectedChannels)
|
|
||||||
{
|
|
||||||
if (!neu.SelectedChannels.TryGetValue(pair.Key, out var nv))
|
|
||||||
return true;
|
|
||||||
if (!pair.Value.Equals(nv))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,132 +0,0 @@
|
|||||||
using System.Numerics;
|
|
||||||
using Dalamud.Bindings.ImGui;
|
|
||||||
using Dalamud.Interface;
|
|
||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using HellionChat.Resources;
|
|
||||||
using HellionChat.Util;
|
|
||||||
|
|
||||||
namespace HellionChat.Ui;
|
|
||||||
|
|
||||||
internal sealed class SettingsOverview
|
|
||||||
{
|
|
||||||
private readonly SettingsWindow _window;
|
|
||||||
|
|
||||||
// Card order matches the Tabs index in SettingsWindow 1:1.
|
|
||||||
private static (FontAwesomeIcon Icon, string Title, string Subtext)[] BuildCardDefs() =>
|
|
||||||
[
|
|
||||||
(
|
|
||||||
FontAwesomeIcon.SlidersH,
|
|
||||||
HellionStrings.Settings_Card_General_Title,
|
|
||||||
HellionStrings.Settings_Card_General_Subtext
|
|
||||||
),
|
|
||||||
(
|
|
||||||
FontAwesomeIcon.Palette,
|
|
||||||
HellionStrings.Settings_Card_Appearance_Title,
|
|
||||||
HellionStrings.Settings_Card_Appearance_Subtext
|
|
||||||
),
|
|
||||||
(
|
|
||||||
FontAwesomeIcon.Comments,
|
|
||||||
HellionStrings.Settings_Card_Chat_Title,
|
|
||||||
HellionStrings.Settings_Card_Chat_Subtext
|
|
||||||
),
|
|
||||||
(
|
|
||||||
FontAwesomeIcon.WindowMaximize,
|
|
||||||
HellionStrings.Settings_Card_Window_Title,
|
|
||||||
HellionStrings.Settings_Card_Window_Subtext
|
|
||||||
),
|
|
||||||
(
|
|
||||||
FontAwesomeIcon.FolderTree,
|
|
||||||
HellionStrings.Settings_Card_Tabs_Title,
|
|
||||||
HellionStrings.Settings_Card_Tabs_Subtext
|
|
||||||
),
|
|
||||||
(
|
|
||||||
FontAwesomeIcon.Database,
|
|
||||||
HellionStrings.Settings_Card_DataManagement_Title,
|
|
||||||
HellionStrings.Settings_Card_DataManagement_Subtext
|
|
||||||
),
|
|
||||||
(
|
|
||||||
FontAwesomeIcon.InfoCircle,
|
|
||||||
HellionStrings.Settings_Card_Information_Title,
|
|
||||||
HellionStrings.Settings_Card_Information_Subtext
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
public SettingsOverview(SettingsWindow window)
|
|
||||||
{
|
|
||||||
_window = window;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Draw()
|
|
||||||
{
|
|
||||||
var avail = ImGui.GetContentRegionAvail();
|
|
||||||
var columns = avail.X >= 700f ? 3 : 2;
|
|
||||||
var cardWidth = (avail.X - (columns - 1) * 8f) / columns;
|
|
||||||
// 110f accommodates two-line subtexts; wrap width is matched in DrawCard.
|
|
||||||
var cardHeight = 110f;
|
|
||||||
|
|
||||||
// One draw-list lookup per frame instead of one per card.
|
|
||||||
var drawList = ImGui.GetWindowDrawList();
|
|
||||||
var cardDefs = BuildCardDefs();
|
|
||||||
for (var i = 0; i < cardDefs.Length; i++)
|
|
||||||
{
|
|
||||||
var (icon, title, subtext) = cardDefs[i];
|
|
||||||
DrawCard(i, icon, title, subtext, cardWidth, cardHeight, drawList);
|
|
||||||
|
|
||||||
if ((i + 1) % columns != 0 && i != cardDefs.Length - 1)
|
|
||||||
ImGui.SameLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawCard(
|
|
||||||
int index,
|
|
||||||
FontAwesomeIcon icon,
|
|
||||||
string title,
|
|
||||||
string subtext,
|
|
||||||
float w,
|
|
||||||
float h,
|
|
||||||
ImDrawListPtr drawList
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// BeginGroup makes the card a single layout item so SameLine works
|
|
||||||
// in the caller loop -- without it ImGui tracks each child separately.
|
|
||||||
ImGui.BeginGroup();
|
|
||||||
|
|
||||||
var cursorBefore = ImGui.GetCursorScreenPos();
|
|
||||||
var clicked = ImGui.InvisibleButton($"##settings-card-{index}", new Vector2(w, h));
|
|
||||||
var hovered = ImGui.IsItemHovered();
|
|
||||||
var bgColor = hovered ? 0xFF22303Fu : 0xFF1A2538u;
|
|
||||||
|
|
||||||
drawList.AddRectFilled(cursorBefore, cursorBefore + new Vector2(w, h), bgColor, 4f);
|
|
||||||
|
|
||||||
var iconPos = cursorBefore + new Vector2(16f, 12f);
|
|
||||||
var titlePos = cursorBefore + new Vector2(16f, 40f);
|
|
||||||
var subtextPos = cursorBefore + new Vector2(16f, 62f);
|
|
||||||
|
|
||||||
var titleColor = ColourUtil.RgbaToAbgr(0xE6F4F1FFu);
|
|
||||||
var subtextColor = ColourUtil.RgbaToAbgr(0x8FA3B5FFu);
|
|
||||||
|
|
||||||
using (_window.Plugin.FontManager.FontAwesome.Push())
|
|
||||||
{
|
|
||||||
drawList.AddText(iconPos, titleColor, icon.ToIconString());
|
|
||||||
}
|
|
||||||
|
|
||||||
drawList.AddText(titlePos, titleColor, title);
|
|
||||||
|
|
||||||
// Subtext wraps at card inner width (16px padding each side) via DrawList
|
|
||||||
// to avoid expanding the group bounds and breaking SameLine in the card row.
|
|
||||||
var subtextWrapWidth = w - 32f;
|
|
||||||
drawList.AddText(
|
|
||||||
ImGui.GetFont(),
|
|
||||||
ImGui.GetFontSize(),
|
|
||||||
subtextPos,
|
|
||||||
subtextColor,
|
|
||||||
subtext,
|
|
||||||
subtextWrapWidth
|
|
||||||
);
|
|
||||||
|
|
||||||
ImGui.EndGroup();
|
|
||||||
|
|
||||||
if (clicked)
|
|
||||||
_window.OpenSection(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,493 +0,0 @@
|
|||||||
using System.Numerics;
|
|
||||||
using Dalamud.Bindings.ImGui;
|
|
||||||
using Dalamud.Interface;
|
|
||||||
using Dalamud.Interface.Colors;
|
|
||||||
using Dalamud.Interface.Utility;
|
|
||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using HellionChat.Branding;
|
|
||||||
using HellionChat.Integrations;
|
|
||||||
using HellionChat.Resources;
|
|
||||||
using HellionChat.Util;
|
|
||||||
|
|
||||||
namespace HellionChat.Ui.SettingsTabs;
|
|
||||||
|
|
||||||
// The About tab absorbs the former Integrations tab (now the first section)
|
|
||||||
// and organises its remaining content into four thematic sections.
|
|
||||||
internal sealed class About : ISettingsTab
|
|
||||||
{
|
|
||||||
private Plugin Plugin { get; }
|
|
||||||
private Configuration Mutable { get; }
|
|
||||||
|
|
||||||
public string Name => HellionStrings.Settings_Tab_Information + "###tabs-information";
|
|
||||||
|
|
||||||
private readonly List<string> Translators =
|
|
||||||
[
|
|
||||||
"q673135110",
|
|
||||||
"Akizem",
|
|
||||||
"d0tiKs",
|
|
||||||
"Moonlight_Everlit",
|
|
||||||
"Dark32",
|
|
||||||
"andreycout",
|
|
||||||
"Button_",
|
|
||||||
"Cali666",
|
|
||||||
"cassandra308",
|
|
||||||
"lokinmodar",
|
|
||||||
"jtabox",
|
|
||||||
"AkiraYorumoto",
|
|
||||||
"MKhayle",
|
|
||||||
"elena.space",
|
|
||||||
"imlisa",
|
|
||||||
"andrei5125",
|
|
||||||
"ShivaMaheshvara",
|
|
||||||
"aislinn87",
|
|
||||||
"nishinatsu051",
|
|
||||||
"lichuyuan",
|
|
||||||
"Risu64",
|
|
||||||
"yummypillow",
|
|
||||||
"witchymary",
|
|
||||||
"Yuzumi",
|
|
||||||
"zomsakura",
|
|
||||||
"Sirayuki",
|
|
||||||
];
|
|
||||||
|
|
||||||
internal About(Plugin plugin, Configuration mutable)
|
|
||||||
{
|
|
||||||
Plugin = plugin;
|
|
||||||
Mutable = mutable;
|
|
||||||
Translators.Sort(
|
|
||||||
(a, b) =>
|
|
||||||
string.Compare(a.ToLowerInvariant(), b.ToLowerInvariant(), StringComparison.Ordinal)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Draw(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
using var wrap = ImRaii.TextWrapPos(0.0f);
|
|
||||||
|
|
||||||
DrawExtensionsSection(sectionJustEntered);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawPluginInfoSection(sectionJustEntered);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawProjectSection(sectionJustEntered);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawTranslatorsSection(sectionJustEntered);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawChangelogSection(sectionJustEntered);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Extensions ──────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
private void DrawExtensionsSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Extensions);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
ImGui.TextWrapped(HellionStrings.Settings_Integrations_Intro);
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
DrawHonorificSection();
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
DrawComingSoonSection();
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
DrawGotAnIdeaSection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawHonorificSection()
|
|
||||||
{
|
|
||||||
DrawSectionHeader(HellionStrings.Settings_Integrations_Honorific_SectionHeader);
|
|
||||||
|
|
||||||
DrawHonorificStatus();
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
// Toggle works regardless of detection state: "show when available,
|
|
||||||
// hide otherwise". Disabling it when Honorific is missing would force
|
|
||||||
// the user to retoggle on every reload.
|
|
||||||
if (
|
|
||||||
ImGui.Checkbox(
|
|
||||||
HellionStrings.Settings_Integrations_Honorific_Toggle,
|
|
||||||
ref Mutable.ShowHonorificTitleInHeader
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Plugin.SaveConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent())
|
|
||||||
{
|
|
||||||
using (
|
|
||||||
ImRaii.PushColor(
|
|
||||||
ImGuiCol.Text,
|
|
||||||
ColourUtil.RgbaToAbgr(Plugin.ThemeRegistry.Active.Colors.TextMuted)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
ImGui.TextWrapped(HellionStrings.Settings_Integrations_Honorific_ToggleHint);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
ImGui.Checkbox(
|
|
||||||
HellionStrings.Settings_Integrations_Honorific_Glow_Toggle,
|
|
||||||
ref Mutable.ShowHonorificGlow
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Plugin.SaveConfig();
|
|
||||||
}
|
|
||||||
ImGuiUtil.HelpMarker(HellionStrings.Settings_Integrations_Honorific_Glow_Hint);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Honorific has no LICENSE in its repo so we link upstream and author
|
|
||||||
// instead of bundling assets. Text labels because FA Brands isn't
|
|
||||||
// guaranteed in Dalamud's font set.
|
|
||||||
ImGui.Spacing();
|
|
||||||
if (ImGui.Button(HellionStrings.Settings_Integrations_Honorific_LinkRepo))
|
|
||||||
{
|
|
||||||
Plugin.PlatformUtil.OpenLink(IntegrationLinks.HonorificRepo);
|
|
||||||
}
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (ImGui.Button(HellionStrings.Settings_Integrations_Honorific_LinkAuthor))
|
|
||||||
{
|
|
||||||
Plugin.PlatformUtil.OpenLink(IntegrationLinks.HonorificAuthor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawHonorificStatus()
|
|
||||||
{
|
|
||||||
var theme = Plugin.ThemeRegistry.Active;
|
|
||||||
var service = Plugin.HonorificService;
|
|
||||||
|
|
||||||
if (service.IsAvailable && service.DetectedApiVersion is { } version)
|
|
||||||
{
|
|
||||||
DrawStatusGlyph('●', theme.Colors.StatusSuccess);
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextUnformatted(
|
|
||||||
string.Format(
|
|
||||||
HellionStrings.Settings_Integrations_Honorific_Status_Detected,
|
|
||||||
version.Major,
|
|
||||||
version.Minor
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if (service.DetectedApiVersion is { } incompatibleVersion)
|
|
||||||
{
|
|
||||||
DrawStatusGlyph('⚠', theme.Colors.StatusWarning);
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextUnformatted(
|
|
||||||
string.Format(
|
|
||||||
HellionStrings.Settings_Integrations_Honorific_Status_Incompatible,
|
|
||||||
HonorificService.ExpectedApiMajor,
|
|
||||||
incompatibleVersion.Major,
|
|
||||||
incompatibleVersion.Minor
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DrawStatusGlyph('○', theme.Colors.TextMuted);
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextUnformatted(
|
|
||||||
HellionStrings.Settings_Integrations_Honorific_Status_NotInstalled
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DrawStatusGlyph(char glyph, uint rgba)
|
|
||||||
{
|
|
||||||
using (ImRaii.PushColor(ImGuiCol.Text, ColourUtil.RgbaToAbgr(rgba)))
|
|
||||||
{
|
|
||||||
ImGui.TextUnformatted(glyph.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawComingSoonSection()
|
|
||||||
{
|
|
||||||
DrawSectionHeader(HellionStrings.Settings_Integrations_ComingSoon_SectionHeader);
|
|
||||||
ImGui.TextWrapped(HellionStrings.Settings_Integrations_ComingSoon_Intro);
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
// Each integration cycle removes its stub here and adds a full section above.
|
|
||||||
DrawComingSoonItem(
|
|
||||||
HellionStrings.Settings_Integrations_ComingSoon_ContextMenu_Title,
|
|
||||||
HellionStrings.Settings_Integrations_ComingSoon_ContextMenu_Description
|
|
||||||
);
|
|
||||||
DrawComingSoonItem(
|
|
||||||
HellionStrings.Settings_Integrations_ComingSoon_Notifications_Title,
|
|
||||||
HellionStrings.Settings_Integrations_ComingSoon_Notifications_Description
|
|
||||||
);
|
|
||||||
DrawComingSoonItem(
|
|
||||||
HellionStrings.Settings_Integrations_ComingSoon_RPStatus_Title,
|
|
||||||
HellionStrings.Settings_Integrations_ComingSoon_RPStatus_Description
|
|
||||||
);
|
|
||||||
DrawComingSoonItem(
|
|
||||||
HellionStrings.Settings_Integrations_ComingSoon_ExtraChat_Title,
|
|
||||||
HellionStrings.Settings_Integrations_ComingSoon_ExtraChat_Description
|
|
||||||
);
|
|
||||||
DrawComingSoonItem(
|
|
||||||
HellionStrings.Settings_Integrations_ComingSoon_QuickDM_Title,
|
|
||||||
HellionStrings.Settings_Integrations_ComingSoon_QuickDM_Description
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawComingSoonItem(string title, string description)
|
|
||||||
{
|
|
||||||
var theme = Plugin.ThemeRegistry.Active;
|
|
||||||
using (ImRaii.PushColor(ImGuiCol.Text, ColourUtil.RgbaToAbgr(theme.Colors.TextMuted)))
|
|
||||||
using (Plugin.FontManager.FontAwesome.Push())
|
|
||||||
{
|
|
||||||
ImGui.TextUnformatted(FontAwesomeIcon.Hourglass.ToIconString());
|
|
||||||
}
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextUnformatted(title);
|
|
||||||
using (ImRaii.PushIndent())
|
|
||||||
{
|
|
||||||
using (ImRaii.PushColor(ImGuiCol.Text, ColourUtil.RgbaToAbgr(theme.Colors.TextMuted)))
|
|
||||||
{
|
|
||||||
ImGui.TextWrapped(description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui.Spacing();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawGotAnIdeaSection()
|
|
||||||
{
|
|
||||||
DrawSectionHeader(HellionStrings.Settings_Integrations_GotAnIdea_SectionHeader);
|
|
||||||
ImGui.TextWrapped(HellionStrings.Settings_Integrations_GotAnIdea_Body);
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
if (ImGui.Button(HellionStrings.Settings_Integrations_GotAnIdea_LinkLabel))
|
|
||||||
{
|
|
||||||
Plugin.PlatformUtil.OpenLink(BrandingLinks.HellionForgeDiscordInvite);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawSectionHeader(string label)
|
|
||||||
{
|
|
||||||
var theme = Plugin.ThemeRegistry.Active;
|
|
||||||
using (ImRaii.PushColor(ImGuiCol.Text, ColourUtil.RgbaToAbgr(theme.Colors.Primary)))
|
|
||||||
{
|
|
||||||
ImGui.TextUnformatted("── " + label + " ──");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Plugin info ──────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
private void DrawPluginInfoSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_PluginInfo);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
DrawFoxBanner();
|
|
||||||
ImGuiHelpers.ScaledDummy(6.0f);
|
|
||||||
|
|
||||||
ImGui.TextUnformatted(string.Format(Language.Options_About_Opening, Plugin.PluginName));
|
|
||||||
|
|
||||||
ImGuiHelpers.ScaledDummy(10.0f);
|
|
||||||
|
|
||||||
ImGui.TextUnformatted(Language.Options_About_Authors);
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextColored(ImGuiColors.ParsedGold, Plugin.Interface.Manifest.Author);
|
|
||||||
|
|
||||||
ImGui.TextUnformatted(Language.Options_About_Discord);
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextColored(ImGuiColors.ParsedGold, "@j.j_kazama");
|
|
||||||
|
|
||||||
ImGui.TextUnformatted(Language.Options_About_Version);
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextColored(
|
|
||||||
ImGuiColors.ParsedOrange,
|
|
||||||
Plugin.Interface.Manifest.AssemblyVersion.ToString(3)
|
|
||||||
);
|
|
||||||
|
|
||||||
ImGuiHelpers.ScaledDummy(10.0f);
|
|
||||||
|
|
||||||
ImGui.TextUnformatted(Language.Options_About_Github_Issues);
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (ImGuiUtil.IconButton(FontAwesomeIcon.ExternalLinkAlt, "githubIssues"))
|
|
||||||
Plugin.PlatformUtil.OpenLink(
|
|
||||||
"https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/issues"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawFoxBanner()
|
|
||||||
{
|
|
||||||
var banner = FoxBannerTexture.Shared.GetWrapOrDefault();
|
|
||||||
if (banner is null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const uint CardColor = 0xFFE8E8E8; // off-white fill so the dark fox pops
|
|
||||||
var imgHeight = 170f * ImGuiHelpers.GlobalScale;
|
|
||||||
var imgWidth = imgHeight * banner.Size.X / banner.Size.Y;
|
|
||||||
var pad = 14f * ImGuiHelpers.GlobalScale;
|
|
||||||
var cardWidth = imgWidth + pad * 2f;
|
|
||||||
var cardHeight = imgHeight + pad * 2f;
|
|
||||||
var rounding = 8f * ImGuiHelpers.GlobalScale;
|
|
||||||
|
|
||||||
// Left-aligned: card origin stays at the current layout cursor position.
|
|
||||||
var cardOrigin = ImGui.GetCursorScreenPos();
|
|
||||||
|
|
||||||
// Draw the rounded card behind the image, then place the image on top.
|
|
||||||
ImGui
|
|
||||||
.GetWindowDrawList()
|
|
||||||
.AddRectFilled(
|
|
||||||
cardOrigin,
|
|
||||||
cardOrigin + new Vector2(cardWidth, cardHeight),
|
|
||||||
CardColor,
|
|
||||||
rounding
|
|
||||||
);
|
|
||||||
ImGui.SetCursorScreenPos(cardOrigin + new Vector2(pad, pad));
|
|
||||||
ImGui.Image(banner.Handle, new Vector2(imgWidth, imgHeight));
|
|
||||||
|
|
||||||
// Advance the layout cursor past the full card so content below does not overlap.
|
|
||||||
ImGui.SetCursorScreenPos(cardOrigin);
|
|
||||||
ImGui.Dummy(new Vector2(cardWidth, cardHeight));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── The Project ──────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
private void DrawProjectSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Project);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
ImGui.TextColored(ImGuiColors.ParsedGold, HellionStrings.About_Maintainer_Heading);
|
|
||||||
ImGui.TextUnformatted(HellionStrings.About_Maintainer_Body);
|
|
||||||
ImGui.TextUnformatted(HellionStrings.About_Maintainer_Website_Label);
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (ImGuiUtil.IconButton(FontAwesomeIcon.ExternalLinkAlt, "hellionMedia"))
|
|
||||||
Plugin.PlatformUtil.OpenLink("https://hellion-media.de");
|
|
||||||
|
|
||||||
ImGuiHelpers.ScaledDummy(10.0f);
|
|
||||||
|
|
||||||
ImGui.TextColored(ImGuiColors.ParsedGold, HellionStrings.About_Mission_Heading);
|
|
||||||
ImGui.TextUnformatted(HellionStrings.About_Mission_P1);
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGui.TextUnformatted(HellionStrings.About_Mission_P2);
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGui.TextUnformatted(HellionStrings.About_Mission_P3);
|
|
||||||
|
|
||||||
ImGuiHelpers.ScaledDummy(10.0f);
|
|
||||||
|
|
||||||
ImGui.TextColored(ImGuiColors.ParsedGold, HellionStrings.About_BuiltOn_Heading);
|
|
||||||
ImGui.TextUnformatted(HellionStrings.About_BuiltOn_P1);
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGui.TextUnformatted(HellionStrings.About_BuiltOn_P2);
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGui.TextUnformatted(HellionStrings.About_BuiltOn_Upstream_Label);
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (ImGuiUtil.IconButton(FontAwesomeIcon.ExternalLinkAlt, "chatTwoUpstream"))
|
|
||||||
Plugin.PlatformUtil.OpenLink("https://github.com/Infiziert90/ChatTwo");
|
|
||||||
|
|
||||||
ImGuiHelpers.ScaledDummy(10.0f);
|
|
||||||
|
|
||||||
ImGui.TextColored(ImGuiColors.ParsedGold, HellionStrings.About_License_Heading);
|
|
||||||
ImGui.TextUnformatted(HellionStrings.About_License_P1);
|
|
||||||
ImGui.TextUnformatted(HellionStrings.About_License_P2);
|
|
||||||
ImGui.TextUnformatted(HellionStrings.About_License_P3);
|
|
||||||
|
|
||||||
ImGuiHelpers.ScaledDummy(10.0f);
|
|
||||||
|
|
||||||
ImGui.TextColored(ImGuiColors.DalamudOrange, HellionStrings.About_SE_Heading);
|
|
||||||
ImGui.TextUnformatted(HellionStrings.About_SE_P1);
|
|
||||||
ImGui.TextUnformatted(HellionStrings.About_SE_P2);
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
ImGui.TextColored(ImGuiColors.ParsedGold, HellionStrings.About_Localization_Heading);
|
|
||||||
ImGui.TextUnformatted(HellionStrings.About_Localization_P1);
|
|
||||||
ImGui.TextUnformatted(HellionStrings.About_Localization_P2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Translators ──────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
private void DrawTranslatorsSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Translators);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
// The translator list belongs to the Chat 2 upstream Crowdin project.
|
|
||||||
using var translatorTree = ImRaii.TreeNode(HellionStrings.About_Translators_TreeNode);
|
|
||||||
if (translatorTree)
|
|
||||||
{
|
|
||||||
using var indent = ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false);
|
|
||||||
foreach (var translator in Translators)
|
|
||||||
ImGui.TextUnformatted(translator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Changelog ────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
private void DrawChangelogSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Changelog);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
ImGui.Checkbox(Language.Options_PrintChangelog_Name, ref Mutable.PrintChangelog);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_PrintChangelog_Description);
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGui.Separator();
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
var changelog = Plugin.Interface.Manifest.Changelog;
|
|
||||||
if (changelog == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ImGui.TextUnformatted(Language.Options_Changelog_Header);
|
|
||||||
ImGui.TextUnformatted(
|
|
||||||
$"Version {Plugin.Interface.Manifest.AssemblyVersion.ToString(3)}"
|
|
||||||
);
|
|
||||||
ImGui.Spacing();
|
|
||||||
foreach (var sentence in changelog.Split("\n"))
|
|
||||||
{
|
|
||||||
if (sentence == string.Empty)
|
|
||||||
{
|
|
||||||
ImGui.NewLine();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var indented = sentence.StartsWith('-') || sentence.StartsWith(" -");
|
|
||||||
using var indent = ImRaii.PushIndent(10.0f, true, indented);
|
|
||||||
ImGui.TextUnformatted(sentence);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,695 +0,0 @@
|
|||||||
using System.Numerics;
|
|
||||||
using Dalamud;
|
|
||||||
using Dalamud.Bindings.ImGui;
|
|
||||||
using Dalamud.Interface;
|
|
||||||
using Dalamud.Interface.FontIdentifier;
|
|
||||||
using Dalamud.Interface.Utility;
|
|
||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using HellionChat.Code;
|
|
||||||
using HellionChat.Resources;
|
|
||||||
using HellionChat.Themes;
|
|
||||||
using HellionChat.Util;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace HellionChat.Ui.SettingsTabs;
|
|
||||||
|
|
||||||
internal sealed class Appearance : ISettingsTab
|
|
||||||
{
|
|
||||||
private Plugin Plugin { get; }
|
|
||||||
private Configuration Mutable { get; }
|
|
||||||
private readonly ILogger<Appearance> _logger;
|
|
||||||
|
|
||||||
private string? _applyDismissedFor;
|
|
||||||
|
|
||||||
public string Name => HellionStrings.Settings_Tab_Appearance + "###tabs-appearance";
|
|
||||||
|
|
||||||
internal Appearance(Plugin plugin, Configuration mutable, ILogger<Appearance> logger)
|
|
||||||
{
|
|
||||||
Plugin = plugin;
|
|
||||||
Mutable = mutable;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Draw(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
DrawThemeSection(sectionJustEntered);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawFontsSection(sectionJustEntered);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawColoursSection(sectionJustEntered);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawWindowStyleSection(sectionJustEntered);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawTimestampSection(sectionJustEntered);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawAnimationsSection(sectionJustEntered);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Theme ──────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
private void DrawThemeSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Theme);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
var registry = Plugin.ThemeRegistry;
|
|
||||||
var active = registry.Get(Mutable.Theme);
|
|
||||||
|
|
||||||
ImGui.TextUnformatted(
|
|
||||||
string.Format(HellionStrings.Settings_Themes_Active, active.Name)
|
|
||||||
);
|
|
||||||
using (ImRaii.PushColor(ImGuiCol.Text, 0xFF8FA3B5u))
|
|
||||||
ImGui.TextUnformatted(active.Author);
|
|
||||||
|
|
||||||
DrawChatColorsApplyBanner(active);
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGui.Separator();
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
ImGui.TextUnformatted(HellionStrings.Settings_Themes_BuiltIns);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawThemeGrid(registry.AllBuiltIns(), active.Slug);
|
|
||||||
|
|
||||||
var customs = registry.AllCustom().ToList();
|
|
||||||
if (customs.Count > 0)
|
|
||||||
{
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGui.Separator();
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGui.TextUnformatted(HellionStrings.Settings_Themes_Custom);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawThemeGrid(customs, active.Slug);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGui.Separator();
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
if (ImGui.Button(HellionStrings.Settings_Themes_OpenFolder))
|
|
||||||
{
|
|
||||||
var dir = Path.Combine(Plugin.Interface.ConfigDirectory.FullName, "themes");
|
|
||||||
Directory.CreateDirectory(dir);
|
|
||||||
Plugin.PlatformUtil.OpenLink(dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (ImGui.Button(HellionStrings.Settings_Themes_ExportActive))
|
|
||||||
{
|
|
||||||
var dir = Path.Combine(Plugin.Interface.ConfigDirectory.FullName, "themes");
|
|
||||||
Directory.CreateDirectory(dir);
|
|
||||||
var fileName = $"{active.Slug}.export.json";
|
|
||||||
var path = Path.Combine(dir, fileName);
|
|
||||||
var json = ThemeJsonWriter.Serialize(active);
|
|
||||||
File.WriteAllText(path, json);
|
|
||||||
_logger.LogInformation($"Exported active theme '{active.Slug}' to {path}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawThemeGrid(IEnumerable<Theme> themes, string activeSlug)
|
|
||||||
{
|
|
||||||
var avail = ImGui.GetContentRegionAvail();
|
|
||||||
var columns = avail.X >= 700f ? 3 : 2;
|
|
||||||
var cardWidth = (avail.X - (columns - 1) * 8f) / columns;
|
|
||||||
var cardHeight = 140f;
|
|
||||||
|
|
||||||
var list = themes.ToList();
|
|
||||||
for (var i = 0; i < list.Count; i++)
|
|
||||||
{
|
|
||||||
DrawThemeCard(list[i], activeSlug, cardWidth, cardHeight);
|
|
||||||
|
|
||||||
if ((i + 1) % columns != 0 && i != list.Count - 1)
|
|
||||||
ImGui.SameLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawThemeCard(Theme theme, string activeSlug, float w, float h)
|
|
||||||
{
|
|
||||||
ImGui.BeginGroup();
|
|
||||||
|
|
||||||
var isActive = string.Equals(theme.Slug, activeSlug, StringComparison.OrdinalIgnoreCase);
|
|
||||||
var cursorBefore = ImGui.GetCursorScreenPos();
|
|
||||||
var clicked = ImGui.InvisibleButton($"##theme-card-{theme.Slug}", new Vector2(w, h));
|
|
||||||
var hovered = ImGui.IsItemHovered();
|
|
||||||
|
|
||||||
var draw = ImGui.GetWindowDrawList();
|
|
||||||
var bg = ColourUtil.RgbaToAbgr(theme.Colors.WindowBg | 0xFFu);
|
|
||||||
draw.AddRectFilled(cursorBefore, cursorBefore + new Vector2(w, h), bg, 4f);
|
|
||||||
|
|
||||||
if (isActive)
|
|
||||||
{
|
|
||||||
var border = ColourUtil.RgbaToAbgr(theme.Colors.Primary);
|
|
||||||
draw.AddRect(
|
|
||||||
cursorBefore,
|
|
||||||
cursorBefore + new Vector2(w, h),
|
|
||||||
border,
|
|
||||||
4f,
|
|
||||||
ImDrawFlags.None,
|
|
||||||
2f
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if (hovered)
|
|
||||||
{
|
|
||||||
var border = ColourUtil.RgbaToAbgr(theme.Colors.PrimaryLight & 0xFFFFFF99u);
|
|
||||||
draw.AddRect(
|
|
||||||
cursorBefore,
|
|
||||||
cursorBefore + new Vector2(w, h),
|
|
||||||
border,
|
|
||||||
4f,
|
|
||||||
ImDrawFlags.None,
|
|
||||||
1f
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
var mockupOrigin = cursorBefore + new Vector2(12f, 12f);
|
|
||||||
var mockupSize = new Vector2(w - 24f, 60f);
|
|
||||||
ThemeMockup.Draw(mockupOrigin, mockupSize, theme);
|
|
||||||
|
|
||||||
var textColor = ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary);
|
|
||||||
var mutedColor = ColourUtil.RgbaToAbgr(theme.Colors.TextMuted);
|
|
||||||
draw.AddText(cursorBefore + new Vector2(12f, 80f), textColor, theme.Name);
|
|
||||||
draw.AddText(cursorBefore + new Vector2(12f, 100f), mutedColor, theme.Author);
|
|
||||||
|
|
||||||
ImGui.EndGroup();
|
|
||||||
|
|
||||||
if (clicked)
|
|
||||||
{
|
|
||||||
Mutable.Theme = theme.Slug;
|
|
||||||
Plugin.ThemeRegistry.Switch(theme.Slug);
|
|
||||||
_applyDismissedFor = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawChatColorsApplyBanner(Theme active)
|
|
||||||
{
|
|
||||||
if (active.ChatColors is not { Channels.Count: > 0 } themeChatColors)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_applyDismissedFor == active.Slug)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var alreadyMatching = themeChatColors.Channels.All(kvp =>
|
|
||||||
Mutable.ChatColours.TryGetValue(kvp.Key, out var current) && current == kvp.Value
|
|
||||||
);
|
|
||||||
if (alreadyMatching)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
var border = ColourUtil.RgbaToAbgr(active.Colors.Primary);
|
|
||||||
var bgFill = ColourUtil.RgbaToAbgr((active.Colors.Surface & 0xFFFFFF00u) | 0xCCu);
|
|
||||||
var origin = ImGui.GetCursorScreenPos();
|
|
||||||
var width = ImGui.GetContentRegionAvail().X;
|
|
||||||
var height = 64f;
|
|
||||||
var draw = ImGui.GetWindowDrawList();
|
|
||||||
draw.AddRectFilled(origin, origin + new Vector2(width, height), bgFill, 4f);
|
|
||||||
draw.AddRect(origin, origin + new Vector2(width, height), border, 4f, ImDrawFlags.None, 1f);
|
|
||||||
|
|
||||||
var textColor = ColourUtil.RgbaToAbgr(active.Colors.TextPrimary);
|
|
||||||
draw.AddText(
|
|
||||||
origin + new Vector2(12f, 10f),
|
|
||||||
textColor,
|
|
||||||
HellionStrings.Settings_Themes_ApplyChatColors_Hint
|
|
||||||
);
|
|
||||||
|
|
||||||
using (ImRaii.PushColor(ImGuiCol.Button, active.Colors.Primary))
|
|
||||||
using (ImRaii.PushColor(ImGuiCol.ButtonHovered, active.Colors.PrimaryLight))
|
|
||||||
using (ImRaii.PushColor(ImGuiCol.ButtonActive, active.Colors.PrimaryDark))
|
|
||||||
{
|
|
||||||
ImGui.SetCursorScreenPos(origin + new Vector2(12f, 32f));
|
|
||||||
if (ImGui.Button(HellionStrings.Settings_Themes_ApplyChatColors_Apply))
|
|
||||||
{
|
|
||||||
foreach (var kvp in themeChatColors.Channels)
|
|
||||||
Mutable.ChatColours[kvp.Key] = kvp.Value;
|
|
||||||
_applyDismissedFor = active.Slug;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (ImGui.Button(HellionStrings.Settings_Themes_ApplyChatColors_Keep))
|
|
||||||
{
|
|
||||||
_applyDismissedFor = active.Slug;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SetCursorScreenPos(origin + new Vector2(0f, height + 8f));
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Fonts ──────────────────────────────────────────────────────────────
|
|
||||||
// R3 deliberately NOT applied here — the UseHellionFont/FontsEnabled
|
|
||||||
// visibility chain has priority over type grouping (R4).
|
|
||||||
|
|
||||||
private void DrawFontsSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Fonts);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
ImGui.Checkbox(HellionStrings.Theme_UseHellionFont_Name, ref Mutable.UseHellionFont)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (Mutable.UseHellionFont)
|
|
||||||
Mutable.FontsEnabled = false;
|
|
||||||
}
|
|
||||||
ImGuiUtil.HelpMarker(HellionStrings.Theme_UseHellionFont_Description);
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
if (Mutable.UseHellionFont)
|
|
||||||
{
|
|
||||||
// Bundled-font path: only the base font size matters; the
|
|
||||||
// global / japanese / italic chooser pickers do not apply.
|
|
||||||
ImGuiUtil.FontSizeCombo(Language.Options_FontSize_Name, ref Mutable.FontSizeV2);
|
|
||||||
ImGui.Spacing();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.Checkbox(Language.Options_FontsEnabled, ref Mutable.FontsEnabled);
|
|
||||||
ImGui.Spacing();
|
|
||||||
}
|
|
||||||
|
|
||||||
var unused = false;
|
|
||||||
if (!Mutable.UseHellionFont && !Mutable.FontsEnabled)
|
|
||||||
{
|
|
||||||
ImGuiUtil.FontSizeCombo(Language.Options_FontSize_Name, ref Mutable.FontSizeV2);
|
|
||||||
}
|
|
||||||
else if (!Mutable.UseHellionFont)
|
|
||||||
{
|
|
||||||
var globalChooser = ImGuiUtil.FontChooser(
|
|
||||||
Language.Options_Font_Name,
|
|
||||||
Mutable.GlobalFontV2,
|
|
||||||
false,
|
|
||||||
ref unused
|
|
||||||
);
|
|
||||||
globalChooser?.ResultTask.ContinueWith(r =>
|
|
||||||
{
|
|
||||||
if (r.IsCompletedSuccessfully)
|
|
||||||
{
|
|
||||||
Plugin.Framework.Run(() => Mutable.GlobalFontV2 = r.Result);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (ImGui.Button("Reset##global"))
|
|
||||||
{
|
|
||||||
Mutable.GlobalFontV2 = new SingleFontSpec
|
|
||||||
{
|
|
||||||
FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkRegular),
|
|
||||||
SizePt = 12.75f,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGuiUtil.HelpMarker(
|
|
||||||
string.Format(Language.Options_Font_Description, Plugin.PluginName)
|
|
||||||
);
|
|
||||||
ImGuiUtil.WarningText(Language.Options_Font_Warning);
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
var japaneseChooser = ImGuiUtil.FontChooser(
|
|
||||||
Language.Options_JapaneseFont_Name,
|
|
||||||
Mutable.JapaneseFontV2,
|
|
||||||
false,
|
|
||||||
ref unused,
|
|
||||||
id => !id.LocaleNames?.ContainsKey("ja-jp") ?? false,
|
|
||||||
"いろはにほへと ちりぬるを"
|
|
||||||
);
|
|
||||||
japaneseChooser?.ResultTask.ContinueWith(r =>
|
|
||||||
{
|
|
||||||
if (r.IsCompletedSuccessfully)
|
|
||||||
{
|
|
||||||
Plugin.Framework.Run(() => Mutable.JapaneseFontV2 = r.Result);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (ImGui.Button("Reset##japanese"))
|
|
||||||
{
|
|
||||||
Mutable.JapaneseFontV2 = new SingleFontSpec
|
|
||||||
{
|
|
||||||
FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkMedium),
|
|
||||||
SizePt = 12.75f,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGuiUtil.HelpMarker(
|
|
||||||
string.Format(Language.Options_JapaneseFont_Description, Plugin.PluginName)
|
|
||||||
);
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
var italicChooser = ImGuiUtil.FontChooser(
|
|
||||||
Language.Options_ItalicFont_Name,
|
|
||||||
Mutable.ItalicFontV2,
|
|
||||||
true,
|
|
||||||
ref Mutable.ItalicEnabled
|
|
||||||
);
|
|
||||||
italicChooser?.ResultTask.ContinueWith(r =>
|
|
||||||
{
|
|
||||||
if (r.IsCompletedSuccessfully)
|
|
||||||
{
|
|
||||||
Plugin.Framework.Run(() => Mutable.ItalicFontV2 = r.Result);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (ImGui.Button("Reset##italic"))
|
|
||||||
{
|
|
||||||
Mutable.ItalicEnabled = false;
|
|
||||||
Mutable.ItalicFontV2 = new SingleFontSpec
|
|
||||||
{
|
|
||||||
FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansCjkRegular),
|
|
||||||
SizePt = 12.75f,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGuiUtil.HelpMarker(
|
|
||||||
string.Format(Language.Options_Italic_Description, Plugin.PluginName)
|
|
||||||
);
|
|
||||||
ImGui.Spacing();
|
|
||||||
}
|
|
||||||
|
|
||||||
// v1.5.3: ExtraGlyphRanges is an atlas-wide property and stays
|
|
||||||
// reachable regardless of UseHellionFont / FontsEnabled state so
|
|
||||||
// users can verify or override the auto-activation on language change.
|
|
||||||
ImGui.Spacing();
|
|
||||||
if (ImGui.CollapsingHeader(Language.Options_ExtraGlyphs_Name))
|
|
||||||
{
|
|
||||||
ImGuiUtil.HelpMarker(
|
|
||||||
string.Format(Language.Options_ExtraGlyphs_Description, Plugin.PluginName)
|
|
||||||
);
|
|
||||||
|
|
||||||
var range = (int)Mutable.ExtraGlyphRanges;
|
|
||||||
foreach (var extra in Enum.GetValues<ExtraGlyphRanges>())
|
|
||||||
{
|
|
||||||
ImGui.CheckboxFlags(extra.Name(), ref range, (int)extra);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mutable.ExtraGlyphRanges = (ExtraGlyphRanges)range;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGuiUtil.FontSizeCombo(
|
|
||||||
Language.Options_SymbolsFontSize_Name,
|
|
||||||
ref Mutable.SymbolsFontSizeV2
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_SymbolsFontSize_Description);
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Colours ────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
private void DrawColoursSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Colours);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
DrawColourPresetButtons();
|
|
||||||
ImGui.TextDisabled(HellionStrings.Settings_Appearance_Colours_PresetsHint);
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGui.Separator();
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
ImGui.Checkbox(
|
|
||||||
Language.Options_ColorSelectedInputChannelButton_Name,
|
|
||||||
ref Mutable.ColorSelectedInputChannelButton
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_ColorSelectedInputChannelButton_Description);
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
foreach (var (_, types) in ChatTypeExt.SortOrder)
|
|
||||||
{
|
|
||||||
foreach (var type in types)
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
ImGuiUtil.IconButton(
|
|
||||||
FontAwesomeIcon.UndoAlt,
|
|
||||||
$"{type}",
|
|
||||||
Language.Options_ChatColours_Reset
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Mutable.ChatColours.Remove(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
if (
|
|
||||||
ImGuiUtil.IconButton(
|
|
||||||
FontAwesomeIcon.LongArrowAltDown,
|
|
||||||
$"{type}",
|
|
||||||
Language.Options_ChatColours_Import
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var gameColour = Plugin.Functions.Chat.GetChannelColor(type);
|
|
||||||
Mutable.ChatColours[type] = gameColour ?? type.DefaultColor() ?? 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
var vec = Mutable.ChatColours.TryGetValue(type, out var colour)
|
|
||||||
? ColourUtil.RgbaToVector3(colour)
|
|
||||||
: ColourUtil.RgbaToVector3(type.DefaultColor() ?? 0);
|
|
||||||
if (ImGui.ColorEdit3(type.Name(), ref vec, ImGuiColorEditFlags.NoInputs))
|
|
||||||
{
|
|
||||||
Mutable.ChatColours[type] = ColourUtil.Vector3ToRgba(vec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawColourPresetButtons()
|
|
||||||
{
|
|
||||||
var first = true;
|
|
||||||
foreach (var (_, preset) in ChatColourPresets.All)
|
|
||||||
{
|
|
||||||
if (!first)
|
|
||||||
{
|
|
||||||
ImGui.SameLine();
|
|
||||||
}
|
|
||||||
first = false;
|
|
||||||
|
|
||||||
if (preset.IsBrandPreset)
|
|
||||||
{
|
|
||||||
var border = ColourUtil.RgbaToVector3(ColourUtil.ComponentsToRgba(255, 128, 200));
|
|
||||||
var btn = ColourUtil.RgbaToVector3(ColourUtil.ComponentsToRgba(74, 42, 106));
|
|
||||||
ImGui.PushStyleColor(
|
|
||||||
ImGuiCol.Border,
|
|
||||||
new System.Numerics.Vector4(border.X, border.Y, border.Z, 1f)
|
|
||||||
);
|
|
||||||
ImGui.PushStyleColor(
|
|
||||||
ImGuiCol.Button,
|
|
||||||
new System.Numerics.Vector4(btn.X, btn.Y, btn.Z, 1f)
|
|
||||||
);
|
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.FrameBorderSize, 1.5f);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui.Button(GetPresetLabel(preset)))
|
|
||||||
{
|
|
||||||
ApplyPreset(preset);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preset.IsBrandPreset)
|
|
||||||
{
|
|
||||||
ImGui.PopStyleVar();
|
|
||||||
ImGui.PopStyleColor(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetPresetLabel(ChatColourPreset preset)
|
|
||||||
{
|
|
||||||
var localized = HellionStrings.ResourceManager.GetString(
|
|
||||||
preset.LocalizationKey,
|
|
||||||
HellionStrings.Culture
|
|
||||||
);
|
|
||||||
return string.IsNullOrEmpty(localized) ? preset.DisplayName : localized;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyPreset(ChatColourPreset preset)
|
|
||||||
{
|
|
||||||
foreach (var (channel, colour) in preset.Colours)
|
|
||||||
{
|
|
||||||
Mutable.ChatColours[channel] = colour;
|
|
||||||
}
|
|
||||||
Plugin.SaveConfig();
|
|
||||||
GlobalParametersCache.Refresh();
|
|
||||||
_logger.LogDebug($"Applied chat colour preset: {preset.DisplayName}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Window style ───────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
private void DrawWindowStyleSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_WindowStyle);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
ImGui.Checkbox(Language.Options_ShowTitleBar_Name, ref Mutable.ShowTitleBar);
|
|
||||||
|
|
||||||
ImGui.Checkbox(
|
|
||||||
Language.Options_ShowPopOutTitleBar_Name,
|
|
||||||
ref Mutable.ShowPopOutTitleBar
|
|
||||||
);
|
|
||||||
|
|
||||||
ImGui.Checkbox(Language.Options_ShowHideButton_Name, ref Mutable.ShowHideButton);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_ShowHideButton_Description);
|
|
||||||
|
|
||||||
ImGui.Checkbox(Language.Options_SidebarTabView_Name, ref Mutable.SidebarTabView);
|
|
||||||
ImGuiUtil.HelpMarker(
|
|
||||||
string.Format(Language.Options_SidebarTabView_Description, Plugin.PluginName)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (Mutable.SidebarTabView)
|
|
||||||
{
|
|
||||||
var sidebarWidth = Mutable.SidebarWidth;
|
|
||||||
if (
|
|
||||||
ImGui.SliderInt(
|
|
||||||
HellionStrings.Settings_ThemeAndLayout_SidebarWidth_Name,
|
|
||||||
ref sidebarWidth,
|
|
||||||
44,
|
|
||||||
160,
|
|
||||||
$"{sidebarWidth} px"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Mutable.SidebarWidth = sidebarWidth;
|
|
||||||
}
|
|
||||||
ImGuiUtil.HelpMarker(
|
|
||||||
HellionStrings.Settings_ThemeAndLayout_SidebarWidth_Description
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGui.Separator();
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
// Slider range 50-100% maps to 0.5-1.0 internally. Floor at 50% prevents
|
|
||||||
// accidentally hiding the chat background (v1.2.0 bug at WindowAlpha=0).
|
|
||||||
var opacityPercent = Mutable.WindowOpacity * 100f;
|
|
||||||
if (
|
|
||||||
ImGuiUtil.DragFloatVertical(
|
|
||||||
HellionStrings.Settings_ThemeAndLayout_WindowOpacity_Name,
|
|
||||||
ref opacityPercent,
|
|
||||||
.25f,
|
|
||||||
50f,
|
|
||||||
100f,
|
|
||||||
$"{opacityPercent:N0}%%",
|
|
||||||
ImGuiSliderFlags.AlwaysClamp
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Mutable.WindowOpacity = opacityPercent / 100f;
|
|
||||||
}
|
|
||||||
ImGuiUtil.HelpMarker(HellionStrings.Settings_ThemeAndLayout_WindowOpacity_Description);
|
|
||||||
|
|
||||||
// UI-12: inactive-window opacity, same 50-100% range and clamp.
|
|
||||||
var inactiveOpacityPercent = Mutable.WindowOpacityInactive * 100f;
|
|
||||||
if (
|
|
||||||
ImGuiUtil.DragFloatVertical(
|
|
||||||
HellionStrings.Settings_ThemeAndLayout_WindowOpacityInactive_Name,
|
|
||||||
ref inactiveOpacityPercent,
|
|
||||||
.25f,
|
|
||||||
50f,
|
|
||||||
100f,
|
|
||||||
$"{inactiveOpacityPercent:N0}%%",
|
|
||||||
ImGuiSliderFlags.AlwaysClamp
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Mutable.WindowOpacityInactive = inactiveOpacityPercent / 100f;
|
|
||||||
}
|
|
||||||
ImGuiUtil.HelpMarker(
|
|
||||||
HellionStrings.Settings_ThemeAndLayout_WindowOpacityInactive_Description
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Timestamps ─────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
private void DrawTimestampSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Timestamps);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
ImGui.Checkbox(
|
|
||||||
Language.Options_PrettierTimestamps_Name,
|
|
||||||
ref Mutable.PrettierTimestamps
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_PrettierTimestamps_Description);
|
|
||||||
|
|
||||||
if (Mutable.PrettierTimestamps)
|
|
||||||
{
|
|
||||||
ImGui.Checkbox(
|
|
||||||
Language.Options_MoreCompactPretty_Name,
|
|
||||||
ref Mutable.MoreCompactPretty
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_MoreCompactPretty_Description);
|
|
||||||
|
|
||||||
ImGui.Checkbox(
|
|
||||||
HellionStrings.Appearance_UseCompactDensity_Name,
|
|
||||||
ref Mutable.UseCompactDensity
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(HellionStrings.Appearance_UseCompactDensity_Description);
|
|
||||||
|
|
||||||
ImGui.Checkbox(
|
|
||||||
Language.Options_HideSameTimestamps_Name,
|
|
||||||
ref Mutable.HideSameTimestamps
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_HideSameTimestamps_Description);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Checkbox(Language.Options_Use24HourClock_Name, ref Mutable.Use24HourClock);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_Use24HourClock_Description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Animations ─────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
private void DrawAnimationsSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Animations);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
// Master accessibility toggle for the v1.5.4 motion work: the
|
|
||||||
// theme crossfade, the sidebar/card hover lerps and the
|
|
||||||
// unread-tab pulse all read Config.ReduceMotion and snap
|
|
||||||
// instantly when it is on.
|
|
||||||
ImGui.Checkbox(
|
|
||||||
HellionStrings.Settings_ThemeAndLayout_ReduceMotion_Name,
|
|
||||||
ref Mutable.ReduceMotion
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(HellionStrings.Settings_ThemeAndLayout_ReduceMotion_Description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,423 +0,0 @@
|
|||||||
using System.Numerics;
|
|
||||||
using Dalamud.Bindings.ImGui;
|
|
||||||
using Dalamud.Interface;
|
|
||||||
using Dalamud.Interface.Colors;
|
|
||||||
using Dalamud.Interface.Utility;
|
|
||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using HellionChat.Resources;
|
|
||||||
using HellionChat.Util;
|
|
||||||
|
|
||||||
namespace HellionChat.Ui.SettingsTabs;
|
|
||||||
|
|
||||||
// Six sections: Messages, Input & preview, Auto-tell tabs, Emotes, Links & tooltips, Novice network.
|
|
||||||
internal sealed class Chat : ISettingsTab
|
|
||||||
{
|
|
||||||
private Plugin Plugin { get; }
|
|
||||||
private Configuration Mutable { get; }
|
|
||||||
|
|
||||||
public string Name => HellionStrings.Settings_Tab_Chat + "###tabs-chat";
|
|
||||||
|
|
||||||
private SearchSelector.SelectorPopupOptions WordPopupOptions;
|
|
||||||
|
|
||||||
// Tracks which EmoteCache state WordPopupOptions was built for so we
|
|
||||||
// don't refill every frame when FilteredSheet is empty.
|
|
||||||
private EmoteCache.LoadingState? WordPopupOptionsBuiltFor;
|
|
||||||
|
|
||||||
internal Chat(Plugin plugin, Configuration mutable)
|
|
||||||
{
|
|
||||||
Plugin = plugin;
|
|
||||||
Mutable = mutable;
|
|
||||||
|
|
||||||
WordPopupOptions = RefillSheet();
|
|
||||||
WordPopupOptionsBuiltFor = EmoteCache.State;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SearchSelector.SelectorPopupOptions RefillSheet() =>
|
|
||||||
new SearchSelector.SelectorPopupOptions
|
|
||||||
{
|
|
||||||
FilteredSheet = EmoteCache
|
|
||||||
.SortedCodeArray.Where(w => !Mutable.BlockedEmotes.Contains(w))
|
|
||||||
.ToArray(),
|
|
||||||
};
|
|
||||||
|
|
||||||
public void Draw(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
DrawMessagesSection(sectionJustEntered);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawInputPreviewSection(sectionJustEntered);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawAutoTellTabsSection(sectionJustEntered);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawEmotesSection(sectionJustEntered);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawLinksTooltipsSection(sectionJustEntered);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawNoviceNetworkSection(sectionJustEntered);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawMessagesSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Messages);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
// Checkboxes first.
|
|
||||||
ImGui.Checkbox(
|
|
||||||
Language.Options_CollapseDuplicateMessages_Name,
|
|
||||||
ref Mutable.CollapseDuplicateMessages
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_CollapseDuplicateMessages_Description);
|
|
||||||
|
|
||||||
// Conditional child: only visible when parent is on (R4).
|
|
||||||
if (Mutable.CollapseDuplicateMessages)
|
|
||||||
{
|
|
||||||
ImGui.Checkbox(
|
|
||||||
Language.Options_CollapseDuplicateMsgUniqueLink_Name,
|
|
||||||
ref Mutable.CollapseKeepUniqueLinks
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_CollapseDuplicateMsgUniqueLink_Description);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Checkbox(
|
|
||||||
HellionStrings.Settings_Chat_NotifyFailedTell_Name,
|
|
||||||
ref Mutable.NotifyFailedTell
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(HellionStrings.Settings_Chat_NotifyFailedTell_Description);
|
|
||||||
|
|
||||||
ImGui.Checkbox(
|
|
||||||
HellionStrings.Settings_Chat_NotifyPluginDisclosure_Name,
|
|
||||||
ref Mutable.NotifyPluginDisclosure
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(HellionStrings.Settings_Chat_NotifyPluginDisclosure_Description);
|
|
||||||
|
|
||||||
// Dropdowns after checkboxes (R3).
|
|
||||||
// UI-7: name display options.
|
|
||||||
using (
|
|
||||||
var combo = ImGuiUtil.BeginComboVertical(
|
|
||||||
HellionStrings.Settings_Chat_WorldSuffix_Name,
|
|
||||||
Mutable.WorldSuffixMode.Name()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (combo.Success)
|
|
||||||
{
|
|
||||||
foreach (var mode in Enum.GetValues<WorldSuffixMode>())
|
|
||||||
{
|
|
||||||
if (ImGui.Selectable(mode.Name(), Mutable.WorldSuffixMode == mode))
|
|
||||||
Mutable.WorldSuffixMode = mode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGuiUtil.HelpMarker(HellionStrings.Settings_Chat_WorldSuffix_Description);
|
|
||||||
|
|
||||||
using (
|
|
||||||
var combo = ImGuiUtil.BeginComboVertical(
|
|
||||||
HellionStrings.Settings_Chat_NameForm_Name,
|
|
||||||
Mutable.NameFormMode.Name()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (combo.Success)
|
|
||||||
{
|
|
||||||
foreach (var mode in Enum.GetValues<NameFormMode>())
|
|
||||||
{
|
|
||||||
if (ImGui.Selectable(mode.Name(), Mutable.NameFormMode == mode))
|
|
||||||
Mutable.NameFormMode = mode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGuiUtil.HelpMarker(HellionStrings.Settings_Chat_NameForm_Description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawInputPreviewSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_InputPreview);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
// Checkboxes first.
|
|
||||||
ImGui.Checkbox(
|
|
||||||
HellionStrings.Settings_Chat_SymbolPicker_Enable_Name,
|
|
||||||
ref Mutable.SymbolPickerEnabled
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(HellionStrings.Settings_Chat_SymbolPicker_Enable_Description);
|
|
||||||
|
|
||||||
ImGui.Checkbox(Language.Options_PreviewOnlyIf_Name, ref Mutable.OnlyPreviewIf);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_PreviewOnlyIf_Description);
|
|
||||||
|
|
||||||
// Dropdown after checkboxes (R3).
|
|
||||||
using (
|
|
||||||
var combo = ImGuiUtil.BeginComboVertical(
|
|
||||||
Language.Options_Preview_Name,
|
|
||||||
Mutable.PreviewPosition.Name()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (combo)
|
|
||||||
{
|
|
||||||
foreach (var position in Enum.GetValues<PreviewPosition>())
|
|
||||||
{
|
|
||||||
if (ImGui.Selectable(position.Name(), Mutable.PreviewPosition == position))
|
|
||||||
Mutable.PreviewPosition = position;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_Preview_Description);
|
|
||||||
|
|
||||||
// Number input last (R3).
|
|
||||||
if (
|
|
||||||
ImGuiUtil.InputIntVertical(
|
|
||||||
Language.Options_PreviewMinimum_Name,
|
|
||||||
Language.Options_PreviewMinimum_Description,
|
|
||||||
ref Mutable.PreviewMinimum
|
|
||||||
)
|
|
||||||
)
|
|
||||||
Mutable.PreviewMinimum = Math.Clamp(Mutable.PreviewMinimum, 1, 250);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawAutoTellTabsSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_AutoTellTabs);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
// Checkboxes first (R3).
|
|
||||||
ImGui.Checkbox(
|
|
||||||
HellionStrings.ChatLog_AutoTellTabs_Enable_Name,
|
|
||||||
ref Mutable.EnableAutoTellTabs
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(HellionStrings.ChatLog_AutoTellTabs_Enable_Description);
|
|
||||||
|
|
||||||
ImGui.Checkbox(
|
|
||||||
HellionStrings.ChatLog_AutoTellTabs_Compact_Name,
|
|
||||||
ref Mutable.AutoTellTabsCompactDisplay
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(HellionStrings.ChatLog_AutoTellTabs_Compact_Description);
|
|
||||||
|
|
||||||
ImGui.Checkbox(
|
|
||||||
HellionStrings.ChatLog_AutoTellTabs_OpenAsPopout_Name,
|
|
||||||
ref Mutable.AutoTellTabsOpenAsPopout
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(HellionStrings.ChatLog_AutoTellTabs_OpenAsPopout_Description);
|
|
||||||
|
|
||||||
ImGui.Checkbox(
|
|
||||||
HellionStrings.ChatLog_AutoTellTabs_GreetedToggle_Name,
|
|
||||||
ref Mutable.AutoTellTabsShowGreetedToggle
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(HellionStrings.ChatLog_AutoTellTabs_GreetedToggle_Description);
|
|
||||||
|
|
||||||
// Sliders after checkboxes (R3).
|
|
||||||
ImGui.SetNextItemWidth(200f * ImGuiHelpers.GlobalScale);
|
|
||||||
var limit = Mutable.AutoTellTabsLimit;
|
|
||||||
if (ImGui.SliderInt(HellionStrings.ChatLog_AutoTellTabs_Limit_Name, ref limit, 1, 50))
|
|
||||||
Mutable.AutoTellTabsLimit = limit;
|
|
||||||
ImGuiUtil.HelpMarker(HellionStrings.ChatLog_AutoTellTabs_Limit_Description);
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGuiUtil.HelpText(HellionStrings.ChatLog_AutoTellTabs_PreloadHint);
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGuiUtil.WarningText(HellionStrings.ChatLog_AutoTellTabs_ConflictHint);
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGui.Separator();
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
var preload = Mutable.AutoTellTabsHistoryPreload;
|
|
||||||
ImGui.SetNextItemWidth(200f * ImGuiHelpers.GlobalScale);
|
|
||||||
if (
|
|
||||||
ImGui.SliderInt(
|
|
||||||
HellionStrings.Privacy_AutoTellTabs_Preload_Name,
|
|
||||||
ref preload,
|
|
||||||
0,
|
|
||||||
100
|
|
||||||
)
|
|
||||||
)
|
|
||||||
Mutable.AutoTellTabsHistoryPreload = preload;
|
|
||||||
ImGuiUtil.HelpMarker(HellionStrings.Privacy_AutoTellTabs_Preload_Description);
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGuiUtil.HelpText(HellionStrings.Privacy_AutoTellTabs_Preload_Hint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawEmotesSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Emotes);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
// Checkbox first (R3).
|
|
||||||
ImGui.Checkbox(Language.Options_ShowEmotes_Name, ref Mutable.ShowEmotes);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_ShowEmotes_Desc);
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGui.TextUnformatted(Language.Options_Emote_BlockedEmotes);
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
if (
|
|
||||||
EmoteCache.State is EmoteCache.LoadingState.Done
|
|
||||||
&& WordPopupOptions.FilteredSheet.Length == 0
|
|
||||||
&& WordPopupOptionsBuiltFor != EmoteCache.LoadingState.Done
|
|
||||||
)
|
|
||||||
{
|
|
||||||
WordPopupOptions = RefillSheet();
|
|
||||||
WordPopupOptionsBuiltFor = EmoteCache.LoadingState.Done;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Button to add blocked emotes (R3 — button before table).
|
|
||||||
var buttonWidth = ImGui.GetContentRegionAvail().X / 3;
|
|
||||||
using (Plugin.FontManager.FontAwesome.Push())
|
|
||||||
ImGui.Button(FontAwesomeIcon.Plus.ToIconString(), new Vector2(buttonWidth, 0));
|
|
||||||
|
|
||||||
// OpenPopup on click because SelectorPopup uses ContextPopupItem
|
|
||||||
// which only triggers on right-click by default.
|
|
||||||
if (ImGui.IsItemClicked())
|
|
||||||
ImGui.OpenPopup("WordAddPopup");
|
|
||||||
|
|
||||||
if (SearchSelector.SelectorPopup("WordAddPopup", out var newWord, WordPopupOptions))
|
|
||||||
Mutable.BlockedEmotes.Add(newWord);
|
|
||||||
|
|
||||||
using (
|
|
||||||
var table = ImRaii.Table(
|
|
||||||
"##BlockedWords",
|
|
||||||
2,
|
|
||||||
ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersInner
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (table)
|
|
||||||
{
|
|
||||||
ImGui.TableSetupColumn(Language.Options_Emote_EmoteTable);
|
|
||||||
ImGui.TableSetupColumn("##Del", ImGuiTableColumnFlags.WidthStretch, 0.07f);
|
|
||||||
ImGui.TableHeadersRow();
|
|
||||||
|
|
||||||
foreach (var word in Mutable.BlockedEmotes.ToArray())
|
|
||||||
{
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
ImGui.TextUnformatted(word);
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
if (
|
|
||||||
ImGuiUtil.Button(
|
|
||||||
$"##{word}Del",
|
|
||||||
FontAwesomeIcon.Trash,
|
|
||||||
!ImGui.GetIO().KeyCtrl
|
|
||||||
)
|
|
||||||
)
|
|
||||||
Mutable.BlockedEmotes.Remove(word);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGui.Separator();
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
ImGui.TextUnformatted(Language.Options_Emote_EmoteStats);
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
if (EmoteCache.State is EmoteCache.LoadingState.Done)
|
|
||||||
ImGui.TextColored(ImGuiColors.HealerGreen, Language.Options_Emote_Ready);
|
|
||||||
else
|
|
||||||
ImGui.TextColored(ImGuiColors.DPSRed, Language.Options_Emote_NotReady);
|
|
||||||
|
|
||||||
ImGui.TextUnformatted(
|
|
||||||
$"{Language.Options_Emote_Loaded} {EmoteCache.SortedCodeArray.Length}"
|
|
||||||
);
|
|
||||||
|
|
||||||
// 5-column loaded-emotes display table.
|
|
||||||
using (
|
|
||||||
var emoteTable = ImRaii.Table(
|
|
||||||
"##LoadedEmotes",
|
|
||||||
5,
|
|
||||||
ImGuiTableFlags.RowBg | ImGuiTableFlags.BordersInner
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (emoteTable)
|
|
||||||
{
|
|
||||||
ImGui.TableSetupColumn("##word1");
|
|
||||||
ImGui.TableSetupColumn("##word2");
|
|
||||||
ImGui.TableSetupColumn("##word3");
|
|
||||||
ImGui.TableSetupColumn("##word4");
|
|
||||||
ImGui.TableSetupColumn("##word5");
|
|
||||||
|
|
||||||
foreach (var word in EmoteCache.SortedCodeArray)
|
|
||||||
{
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
ImGui.TextUnformatted(word);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawLinksTooltipsSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_LinksTooltips);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
ImGui.Checkbox(
|
|
||||||
Language.Options_NativeItemTooltips_Name,
|
|
||||||
ref Mutable.NativeItemTooltips
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(
|
|
||||||
string.Format(Language.Options_NativeItemTooltips_Description, Plugin.PluginName)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Conditional slider: only shown when native tooltips are enabled (R4).
|
|
||||||
if (Mutable.NativeItemTooltips)
|
|
||||||
{
|
|
||||||
ImGuiUtil.DragFloatVertical(
|
|
||||||
Language.Options_TooltipOffset_Name,
|
|
||||||
Language.Options_TooltipOffset_Desc,
|
|
||||||
ref Mutable.TooltipOffset,
|
|
||||||
1,
|
|
||||||
0f,
|
|
||||||
400f,
|
|
||||||
$"{Mutable.TooltipOffset:N0}px",
|
|
||||||
ImGuiSliderFlags.AlwaysClamp
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawNoviceNetworkSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_NoviceNetwork);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
ImGui.Checkbox(Language.Options_ShowNoviceNetwork_Name, ref Mutable.ShowNoviceNetwork);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_ShowNoviceNetwork_Description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,217 +0,0 @@
|
|||||||
using Dalamud.Bindings.ImGui;
|
|
||||||
using Dalamud.Interface.Utility;
|
|
||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using HellionChat.Resources;
|
|
||||||
using HellionChat.Util;
|
|
||||||
|
|
||||||
namespace HellionChat.Ui.SettingsTabs;
|
|
||||||
|
|
||||||
internal sealed class General : ISettingsTab
|
|
||||||
{
|
|
||||||
private Plugin Plugin { get; }
|
|
||||||
private Configuration Mutable { get; }
|
|
||||||
|
|
||||||
public string Name => HellionStrings.Settings_Tab_General + "###tabs-general";
|
|
||||||
|
|
||||||
internal General(Plugin plugin, Configuration mutable)
|
|
||||||
{
|
|
||||||
Plugin = plugin;
|
|
||||||
Mutable = mutable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Draw(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
DrawInputSection(sectionJustEntered);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawSoundSection(sectionJustEntered);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawLanguageSection(sectionJustEntered);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawPerformanceSection(sectionJustEntered);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawInputSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
// Collapse every time the tab is freshly entered so state doesn't bleed across sessions.
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Input);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
ImGui.Checkbox(Language.Options_KeepInputFocus_Name, ref Mutable.KeepInputFocus);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_KeepInputFocus_Description);
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
ImGui.TextUnformatted(Language.Options_ChatTabForwardKeybind_Name);
|
|
||||||
ImGui.SetNextItemWidth(-1);
|
|
||||||
ImGuiUtil.KeybindInput("ChatTabForwardKeybind", ref Mutable.ChatTabForward);
|
|
||||||
|
|
||||||
ImGui.TextUnformatted(Language.Options_ChatTabBackwardKeybind_Name);
|
|
||||||
ImGui.SetNextItemWidth(-1);
|
|
||||||
ImGuiUtil.KeybindInput("ChatTabBackwardKeybind", ref Mutable.ChatTabBackward);
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
using (
|
|
||||||
var combo = ImGuiUtil.BeginComboVertical(
|
|
||||||
Language.Options_KeybindMode_Name,
|
|
||||||
Mutable.KeybindMode.Name()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (combo.Success)
|
|
||||||
{
|
|
||||||
foreach (var mode in Enum.GetValues<KeybindMode>())
|
|
||||||
{
|
|
||||||
if (ImGui.Selectable(mode.Name(), Mutable.KeybindMode == mode))
|
|
||||||
{
|
|
||||||
Mutable.KeybindMode = mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
{
|
|
||||||
ImGuiUtil.Tooltip(mode.Tooltip() ?? "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGuiUtil.HelpMarker(
|
|
||||||
string.Format(Language.Options_KeybindMode_Description, Plugin.PluginName)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawSoundSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
// Collapse every time the tab is freshly entered so state doesn't bleed across sessions.
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Sound);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
ImGui.Checkbox(Language.Options_PlaySounds_Name, ref Mutable.PlaySounds);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_PlaySounds_Description);
|
|
||||||
// Volume is stored as a 0-1 float but shown as 0-100% to match user
|
|
||||||
// intuition. Full range — unlike opacity there is no unsafe floor.
|
|
||||||
var customSoundVolumePercent = Mutable.CustomSoundVolume * 100f;
|
|
||||||
if (
|
|
||||||
ImGuiUtil.DragFloatVertical(
|
|
||||||
HellionStrings.Settings_General_CustomSoundVolume_Name,
|
|
||||||
ref customSoundVolumePercent,
|
|
||||||
1f,
|
|
||||||
0f,
|
|
||||||
100f,
|
|
||||||
$"{customSoundVolumePercent:N0}%%",
|
|
||||||
ImGuiSliderFlags.AlwaysClamp
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Mutable.CustomSoundVolume = customSoundVolumePercent / 100f;
|
|
||||||
}
|
|
||||||
// Show the functional description and the per-tab navigation hint together.
|
|
||||||
ImGuiUtil.HelpMarker(
|
|
||||||
HellionStrings.Settings_General_CustomSoundVolume_Description
|
|
||||||
+ "\n\n"
|
|
||||||
+ HellionStrings.Settings_Section_Sound_TabsHint
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawLanguageSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
// Collapse every time the tab is freshly entered so state doesn't bleed across sessions.
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Language);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
ImGui.Checkbox(Language.Options_SortAutoTranslate_Name, ref Mutable.SortAutoTranslate);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_SortAutoTranslate_Description);
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
using (
|
|
||||||
var combo = ImGuiUtil.BeginComboVertical(
|
|
||||||
Language.Options_Language_Name,
|
|
||||||
Mutable.LanguageOverride.Name()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (combo.Success)
|
|
||||||
{
|
|
||||||
// None pinned first, then alphabetical by endonym so source order
|
|
||||||
// (append-only for serialisation safety) is not visible to users.
|
|
||||||
var sortedLanguages = Enum.GetValues<LanguageOverride>()
|
|
||||||
.OrderBy(l => l == LanguageOverride.None ? 0 : 1)
|
|
||||||
.ThenBy(l => l.Name(), StringComparer.InvariantCulture);
|
|
||||||
foreach (var language in sortedLanguages)
|
|
||||||
{
|
|
||||||
if (ImGui.Selectable(language.Name()))
|
|
||||||
{
|
|
||||||
Mutable.LanguageOverride = language;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGuiUtil.HelpMarker(
|
|
||||||
string.Format(Language.Options_Language_Description, Plugin.PluginName)
|
|
||||||
);
|
|
||||||
// v1.5.3: HellionChat's font stack covers 24 languages but FFXIV's
|
|
||||||
// engine only supports EN/DE/FR/JA for chat input/sending.
|
|
||||||
ImGuiUtil.WarningText(HellionStrings.Settings_Language_FFXIVCoverage_Warning);
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
using (
|
|
||||||
var combo = ImGuiUtil.BeginComboVertical(
|
|
||||||
Language.Options_CommandHelpSide_Name,
|
|
||||||
Mutable.CommandHelpSide.Name()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (combo.Success)
|
|
||||||
{
|
|
||||||
foreach (var side in Enum.GetValues<CommandHelpSide>())
|
|
||||||
{
|
|
||||||
if (ImGui.Selectable(side.Name(), Mutable.CommandHelpSide == side))
|
|
||||||
{
|
|
||||||
Mutable.CommandHelpSide = side;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGuiUtil.HelpMarker(
|
|
||||||
string.Format(Language.Options_CommandHelpSide_Description, Plugin.PluginName)
|
|
||||||
);
|
|
||||||
ImGui.Spacing();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawPerformanceSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
// Collapse every time the tab is freshly entered so state doesn't bleed across sessions.
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Performance);
|
|
||||||
if (!tree.Success)
|
|
||||||
return;
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
ImGui.SetNextItemWidth(200f * ImGuiHelpers.GlobalScale);
|
|
||||||
if (ImGui.InputInt(Language.Options_MaxLinesToShow_Name, ref Mutable.MaxLinesToRender))
|
|
||||||
{
|
|
||||||
Mutable.MaxLinesToRender = Math.Clamp(Mutable.MaxLinesToRender, 1, 10_000);
|
|
||||||
}
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_MaxLinesToShow_Description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace HellionChat.Ui.SettingsTabs;
|
|
||||||
|
|
||||||
internal interface ISettingsTab
|
|
||||||
{
|
|
||||||
string Name { get; }
|
|
||||||
void Draw(bool sectionJustEntered);
|
|
||||||
}
|
|
||||||
@@ -1,601 +0,0 @@
|
|||||||
using Dalamud.Bindings.ImGui;
|
|
||||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
|
||||||
using Dalamud.Interface;
|
|
||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
|
||||||
using HellionChat.Code;
|
|
||||||
using HellionChat.Resources;
|
|
||||||
using HellionChat.Util;
|
|
||||||
|
|
||||||
namespace HellionChat.Ui.SettingsTabs;
|
|
||||||
|
|
||||||
internal sealed class Tabs : ISettingsTab
|
|
||||||
{
|
|
||||||
private Plugin Plugin { get; }
|
|
||||||
private Configuration Mutable { get; }
|
|
||||||
|
|
||||||
public string Name => HellionStrings.Settings_Tab_Tabs + "###tabs-tabs";
|
|
||||||
|
|
||||||
private int ToOpen = -2;
|
|
||||||
|
|
||||||
internal Tabs(Plugin plugin, Configuration mutable)
|
|
||||||
{
|
|
||||||
Plugin = plugin;
|
|
||||||
Mutable = mutable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Draw(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
const string addTabPopup = "add-tab-popup";
|
|
||||||
|
|
||||||
ImGuiUtil.HelpText(HellionStrings.Tabs_Presets_Linkshell_Hint);
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
if (ImGuiUtil.IconButton(FontAwesomeIcon.Plus, tooltip: Language.Options_Tabs_Add))
|
|
||||||
ImGui.OpenPopup(addTabPopup);
|
|
||||||
|
|
||||||
using (var popup = ImRaii.Popup(addTabPopup))
|
|
||||||
{
|
|
||||||
if (popup)
|
|
||||||
{
|
|
||||||
if (ImGui.Selectable(Language.Options_Tabs_NewTab))
|
|
||||||
Mutable.Tabs.Add(new Tab());
|
|
||||||
|
|
||||||
ImGui.Separator();
|
|
||||||
|
|
||||||
if (
|
|
||||||
ImGui.Selectable(
|
|
||||||
string.Format(Language.Options_Tabs_Preset, Language.Tabs_Presets_General)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
Mutable.Tabs.Add(TabsUtil.VanillaGeneral);
|
|
||||||
|
|
||||||
if (
|
|
||||||
ImGui.Selectable(
|
|
||||||
string.Format(Language.Options_Tabs_Preset, Language.Tabs_Presets_Event)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
Mutable.Tabs.Add(TabsUtil.VanillaEvent);
|
|
||||||
|
|
||||||
if (
|
|
||||||
ImGui.Selectable(
|
|
||||||
string.Format(Language.Options_Tabs_Preset, Language.Tabs_Presets_Tell)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
Mutable.Tabs.Add(TabsUtil.VanillaTellExclusive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var toRemove = -1;
|
|
||||||
var doOpens = ToOpen > -2;
|
|
||||||
for (var i = 0; i < Mutable.Tabs.Count; i++)
|
|
||||||
{
|
|
||||||
var tab = Mutable.Tabs[i];
|
|
||||||
|
|
||||||
// Sub-sections (Channels/Display/Notification/Input/Pop-out) are inlined into
|
|
||||||
// this loop body rather than extracted to helpers, because each one closes over
|
|
||||||
// the per-iteration `i` and `tab` state. Extraction would mean passing both
|
|
||||||
// into every helper without meaningful encapsulation gain.
|
|
||||||
|
|
||||||
// ToOpen controls which tab-item TreeNode is open (e.g. after add/move).
|
|
||||||
// This is the outer level — not touched by sectionJustEntered.
|
|
||||||
if (doOpens)
|
|
||||||
ImGui.SetNextItemOpen(i == ToOpen);
|
|
||||||
|
|
||||||
using var treeNode = ImRaii.TreeNode($"{tab.Name}###tab-{i}");
|
|
||||||
if (!treeNode.Success)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
using var pushedId = ImRaii.PushId($"tab-{i}");
|
|
||||||
|
|
||||||
if (
|
|
||||||
ImGuiUtil.IconButton(
|
|
||||||
FontAwesomeIcon.TrashAlt,
|
|
||||||
tooltip: Language.Options_Tabs_Delete
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
toRemove = i;
|
|
||||||
ToOpen = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
if (
|
|
||||||
ImGuiUtil.IconButton(FontAwesomeIcon.ArrowUp, tooltip: Language.Options_Tabs_MoveUp)
|
|
||||||
&& i > 0
|
|
||||||
)
|
|
||||||
{
|
|
||||||
(Mutable.Tabs[i - 1], Mutable.Tabs[i]) = (Mutable.Tabs[i], Mutable.Tabs[i - 1]);
|
|
||||||
ToOpen = i - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
if (
|
|
||||||
ImGuiUtil.IconButton(
|
|
||||||
FontAwesomeIcon.ArrowDown,
|
|
||||||
tooltip: Language.Options_Tabs_MoveDown
|
|
||||||
)
|
|
||||||
&& i < Mutable.Tabs.Count - 1
|
|
||||||
)
|
|
||||||
{
|
|
||||||
(Mutable.Tabs[i + 1], Mutable.Tabs[i]) = (Mutable.Tabs[i], Mutable.Tabs[i + 1]);
|
|
||||||
ToOpen = i + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name and Icon are always visible — no sub-section collapse for these.
|
|
||||||
ImGui.InputText(
|
|
||||||
Language.Options_Tabs_Name,
|
|
||||||
ref tab.Name,
|
|
||||||
512,
|
|
||||||
ImGuiInputTextFlags.EnterReturnsTrue
|
|
||||||
);
|
|
||||||
|
|
||||||
// Per-tab icon override added in v1.2.0. Falls back to default mapping if unset.
|
|
||||||
ImGui.TextUnformatted(HellionStrings.Tabs_Icon_Label);
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGuiUtil.HelpMarker(HellionStrings.Tabs_Icon_HelpMarker);
|
|
||||||
|
|
||||||
var iconCurrent = string.IsNullOrEmpty(tab.Icon) ? "" : tab.Icon;
|
|
||||||
var iconPreview =
|
|
||||||
iconCurrent.Length == 0 ? HellionStrings.Tabs_Icon_DefaultOption : iconCurrent;
|
|
||||||
using (var combo = ImRaii.Combo($"##icon-{i}", iconPreview))
|
|
||||||
{
|
|
||||||
if (combo.Success)
|
|
||||||
{
|
|
||||||
// First option clears the icon and lets the default mapping take over.
|
|
||||||
if (
|
|
||||||
ImGui.Selectable(
|
|
||||||
HellionStrings.Tabs_Icon_DefaultOption,
|
|
||||||
iconCurrent.Length == 0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
tab.Icon = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Separator();
|
|
||||||
|
|
||||||
// Options sourced from TabIconGlyphResolver.PickerOptions (single source of truth).
|
|
||||||
foreach (var option in TabIconGlyphResolver.PickerOptions)
|
|
||||||
{
|
|
||||||
var isSelected = string.Equals(
|
|
||||||
iconCurrent,
|
|
||||||
option,
|
|
||||||
StringComparison.OrdinalIgnoreCase
|
|
||||||
);
|
|
||||||
if (ImGui.Selectable(option, isSelected))
|
|
||||||
{
|
|
||||||
tab.Icon = option;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
// ── Sub-section: Channels ─────────────────────────────────────────
|
|
||||||
// First because it answers "what does this tab collect?" — most important.
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using (
|
|
||||||
var secChannels = ImRaii.TreeNode(
|
|
||||||
HellionStrings.Settings_Section_Tab_Channels + $"##sec-channels-{i}"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (secChannels.Success)
|
|
||||||
{
|
|
||||||
using var indent = ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false);
|
|
||||||
ImGuiUtil.ChannelSelector(Language.Options_Tabs_Channels, tab.SelectedChannels);
|
|
||||||
ImGuiUtil.ExtraChatSelector(
|
|
||||||
Language.Options_Tabs_ExtraChatChannels,
|
|
||||||
ref tab.ExtraChatAll,
|
|
||||||
tab.ExtraChatChannels
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
// ── Sub-section: Display ──────────────────────────────────────────
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using (
|
|
||||||
var secDisplay = ImRaii.TreeNode(
|
|
||||||
HellionStrings.Settings_Section_Tab_Display + $"##sec-display-{i}"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (secDisplay.Success)
|
|
||||||
{
|
|
||||||
using var indent = ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false);
|
|
||||||
|
|
||||||
ImGui.Checkbox(Language.Options_Tabs_ShowTimestamps, ref tab.DisplayTimestamp);
|
|
||||||
|
|
||||||
using (
|
|
||||||
var combo = ImGuiUtil.BeginComboVertical(
|
|
||||||
Language.Options_Tabs_UnreadMode,
|
|
||||||
tab.UnreadMode.Name()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (combo.Success)
|
|
||||||
{
|
|
||||||
foreach (var mode in Enum.GetValues<UnreadMode>())
|
|
||||||
{
|
|
||||||
if (ImGui.Selectable(mode.Name(), tab.UnreadMode == mode))
|
|
||||||
tab.UnreadMode = mode;
|
|
||||||
|
|
||||||
if (mode.Tooltip() is { } tooltip && ImGui.IsItemHovered())
|
|
||||||
ImGuiUtil.Tooltip(tooltip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only relevant when the global hide-when-inactive is on.
|
|
||||||
if (Mutable.HideWhenInactive)
|
|
||||||
ImGui.Checkbox(
|
|
||||||
Language.Options_Tabs_InactivityBehaviour,
|
|
||||||
ref tab.UnhideOnActivity
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
// ── Sub-section: Notification ─────────────────────────────────────
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using (
|
|
||||||
var secNotif = ImRaii.TreeNode(
|
|
||||||
HellionStrings.Settings_Section_Tab_Notification + $"##sec-notif-{i}"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (secNotif.Success)
|
|
||||||
{
|
|
||||||
using var indent = ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false);
|
|
||||||
|
|
||||||
ImGui.Checkbox(
|
|
||||||
HellionStrings.Tabs_NotificationSound_Enable_Name,
|
|
||||||
ref tab.EnableNotificationSound
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(HellionStrings.Tabs_NotificationSound_Description);
|
|
||||||
if (tab.EnableNotificationSound)
|
|
||||||
{
|
|
||||||
using var notifIndent = ImRaii.PushIndent(10.0f);
|
|
||||||
// Build a readable preview label for the currently selected sound.
|
|
||||||
var soundPreview =
|
|
||||||
tab.NotificationSoundId <= 16
|
|
||||||
? $"{HellionStrings.Tabs_NotificationSound_Option} {tab.NotificationSoundId}"
|
|
||||||
: $"{HellionStrings.Tabs_NotificationSound_CustomOption} {tab.NotificationSoundId - 16}";
|
|
||||||
using (var combo = ImRaii.Combo($"##notif-sound-{i}", soundPreview))
|
|
||||||
{
|
|
||||||
if (combo.Success)
|
|
||||||
{
|
|
||||||
for (uint s = 1; s <= 16; s++)
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
ImGui.Selectable(
|
|
||||||
$"{HellionStrings.Tabs_NotificationSound_Option} {s}",
|
|
||||||
tab.NotificationSoundId == s
|
|
||||||
)
|
|
||||||
)
|
|
||||||
tab.NotificationSoundId = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Separator();
|
|
||||||
|
|
||||||
// Bundled custom sounds (ids 17-19).
|
|
||||||
for (uint n = 1; n <= 3; n++)
|
|
||||||
{
|
|
||||||
var customId = 16 + n;
|
|
||||||
if (
|
|
||||||
ImGui.Selectable(
|
|
||||||
$"{HellionStrings.Tabs_NotificationSound_CustomOption} {n}",
|
|
||||||
tab.NotificationSoundId == customId
|
|
||||||
)
|
|
||||||
)
|
|
||||||
tab.NotificationSoundId = customId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let the user hear the currently selected sound without waiting
|
|
||||||
// for a real message to arrive in this tab.
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (
|
|
||||||
ImGuiUtil.IconButton(
|
|
||||||
FontAwesomeIcon.Play,
|
|
||||||
tooltip: HellionStrings.Tabs_NotificationSound_Preview
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var previewId = tab.NotificationSoundId;
|
|
||||||
if (previewId <= 16)
|
|
||||||
{
|
|
||||||
Plugin.Framework.RunOnFrameworkThread(() =>
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
UIGlobals.PlaySoundEffect(previewId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Plugin.CustomAudioPlayer.Play(
|
|
||||||
(int)previewId - 16,
|
|
||||||
Mutable.CustomSoundVolume
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Volume is stored as a 0-1 float but shown as 0-100%.
|
|
||||||
// Same field as General → Sound; shown here for convenience.
|
|
||||||
// DragFloatVertical derives its widget ID from the label text and exposes no
|
|
||||||
// override. We inline the equivalent (text label + SetNextItemWidth + DragFloat)
|
|
||||||
// to keep an explicit ##tab-volume-{i} ID, which reads more clearly than relying
|
|
||||||
// on the surrounding PushId("tab-{i}") scope to disambiguate identical labels.
|
|
||||||
// Volume is global (Mutable.CustomSoundVolume) and applies to every tab's
|
|
||||||
// notification sound, so it is shown unconditionally — not gated by the
|
|
||||||
// per-tab EnableNotificationSound toggle.
|
|
||||||
ImGui.TextUnformatted(HellionStrings.Settings_General_CustomSoundVolume_Name);
|
|
||||||
ImGui.SetNextItemWidth(-1);
|
|
||||||
var customSoundVolumePercent = Mutable.CustomSoundVolume * 100f;
|
|
||||||
if (
|
|
||||||
ImGui.DragFloat(
|
|
||||||
$"##tab-volume-{i}",
|
|
||||||
ref customSoundVolumePercent,
|
|
||||||
1f,
|
|
||||||
0f,
|
|
||||||
100f,
|
|
||||||
$"{customSoundVolumePercent:N0}%%",
|
|
||||||
ImGuiSliderFlags.AlwaysClamp
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Mutable.CustomSoundVolume = customSoundVolumePercent / 100f;
|
|
||||||
}
|
|
||||||
// Applies globally — same value as in General → Sound.
|
|
||||||
ImGuiUtil.HelpMarker(
|
|
||||||
HellionStrings.Settings_General_CustomSoundVolume_Description
|
|
||||||
+ "\n\n"
|
|
||||||
+ HellionStrings.Settings_Section_Tab_Volume_AllTabsHint
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
// ── Sub-section: Input ────────────────────────────────────────────
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using (
|
|
||||||
var secInput = ImRaii.TreeNode(
|
|
||||||
HellionStrings.Settings_Section_Tab_Input + $"##sec-input-{i}"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (secInput.Success)
|
|
||||||
{
|
|
||||||
using var indent = ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false);
|
|
||||||
|
|
||||||
ImGui.Checkbox(Language.Options_Tabs_NoInput, ref tab.InputDisabled);
|
|
||||||
if (!tab.InputDisabled)
|
|
||||||
{
|
|
||||||
var input =
|
|
||||||
tab.Channel?.ToChatType().Name()
|
|
||||||
?? Language.Options_Tabs_NoInputChannel;
|
|
||||||
using (
|
|
||||||
var combo = ImGuiUtil.BeginComboVertical(
|
|
||||||
Language.Options_Tabs_InputChannel,
|
|
||||||
input
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (combo.Success)
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
ImGui.Selectable(
|
|
||||||
Language.Options_Tabs_NoInputChannel,
|
|
||||||
tab.Channel == null
|
|
||||||
)
|
|
||||||
)
|
|
||||||
tab.Channel = null;
|
|
||||||
|
|
||||||
foreach (var channel in Enum.GetValues<InputChannel>())
|
|
||||||
if (
|
|
||||||
ImGui.Selectable(
|
|
||||||
channel.ToChatType().Name(),
|
|
||||||
tab.Channel == channel
|
|
||||||
)
|
|
||||||
)
|
|
||||||
tab.Channel = channel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var player = Plugin.ObjectTable.LocalPlayer;
|
|
||||||
if (tab.Channel == InputChannel.Tell && player != null)
|
|
||||||
{
|
|
||||||
ImGui.Checkbox(
|
|
||||||
Language.Options_Tabs_SenderMessages,
|
|
||||||
ref tab.AllSenderMessages
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpText(Language.Options_Help_SenderMessages);
|
|
||||||
|
|
||||||
var worlds = Sheets
|
|
||||||
.WorldsOnDatacenter(player)
|
|
||||||
.OrderByDescending(world => world.DataCenter.RowId)
|
|
||||||
.ThenBy(world => world.Name.ToString())
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
using (ImRaii.ItemWidth(ImGui.GetWindowWidth() / 3f))
|
|
||||||
{
|
|
||||||
ImGui.Text(Language.Options_Header_Target);
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
var name = tab.TellTarget.Name;
|
|
||||||
if (ImGui.InputText("##targetInput", ref name, 21))
|
|
||||||
tab.TellTarget.Name = name;
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
|
|
||||||
// Guard against an empty worlds list (character switch or sheet not yet populated)
|
|
||||||
// to avoid an out-of-bounds crash on worlds[selectedWorld].
|
|
||||||
if (worlds.Count == 0)
|
|
||||||
{
|
|
||||||
ImGui.TextDisabled("(no worlds available)");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var selectedWorld = worlds.FindIndex(world =>
|
|
||||||
world.RowId == tab.TellTarget.World
|
|
||||||
);
|
|
||||||
if (selectedWorld == -1)
|
|
||||||
selectedWorld = 0;
|
|
||||||
|
|
||||||
using (
|
|
||||||
var combo = ImRaii.Combo(
|
|
||||||
"###player-world",
|
|
||||||
worlds[selectedWorld].Name.ToString()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (combo.Success)
|
|
||||||
{
|
|
||||||
var lastDc = worlds.First().DataCenter.RowId;
|
|
||||||
foreach (var (idx, world) in worlds.Index())
|
|
||||||
{
|
|
||||||
if (
|
|
||||||
ImGui.Selectable(
|
|
||||||
world.Name.ToString(),
|
|
||||||
selectedWorld == idx
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
selectedWorld = idx;
|
|
||||||
tab.TellTarget.World = worlds[
|
|
||||||
selectedWorld
|
|
||||||
].RowId;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastDc == world.DataCenter.RowId)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
lastDc = world.DataCenter.RowId;
|
|
||||||
ImGui.Separator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var target =
|
|
||||||
(Plugin.TargetManager.SoftTarget ?? Plugin.TargetManager.Target)
|
|
||||||
as IPlayerCharacter;
|
|
||||||
using (ImRaii.Disabled(target == null))
|
|
||||||
{
|
|
||||||
if (ImGui.Button("Set to target") && target != null)
|
|
||||||
tab.TellTarget.FromTarget(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
// ── Sub-section: Pop-out window ───────────────────────────────────
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using (
|
|
||||||
var secPopOut = ImRaii.TreeNode(
|
|
||||||
HellionStrings.Settings_Section_Tab_PopOut + $"##sec-popout-{i}"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (secPopOut.Success)
|
|
||||||
{
|
|
||||||
using var indent = ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false);
|
|
||||||
|
|
||||||
ImGui.Checkbox(Language.Options_Tabs_PopOut, ref tab.PopOut);
|
|
||||||
if (tab.PopOut)
|
|
||||||
{
|
|
||||||
using var _ = ImRaii.PushIndent(10.0f);
|
|
||||||
ImGui.Checkbox(
|
|
||||||
Language.Options_Tabs_IndependentOpacity,
|
|
||||||
ref tab.IndependentOpacity
|
|
||||||
);
|
|
||||||
if (tab.IndependentOpacity)
|
|
||||||
ImGuiUtil.DragFloatVertical(
|
|
||||||
Language.Options_Tabs_Opacity,
|
|
||||||
ref tab.Opacity,
|
|
||||||
0.25f,
|
|
||||||
0f,
|
|
||||||
100f,
|
|
||||||
$"{tab.Opacity:N2}%%",
|
|
||||||
ImGuiSliderFlags.AlwaysClamp
|
|
||||||
);
|
|
||||||
|
|
||||||
ImGui.Checkbox(
|
|
||||||
Language.Options_Tabs_IndependentHide,
|
|
||||||
ref tab.IndependentHide
|
|
||||||
);
|
|
||||||
if (tab.IndependentHide)
|
|
||||||
{
|
|
||||||
using var __ = ImRaii.PushIndent(10.0f);
|
|
||||||
ImGuiUtil.OptionCheckbox(
|
|
||||||
ref tab.HideDuringCutscenes,
|
|
||||||
Language.Options_HideDuringCutscenes_Name
|
|
||||||
);
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
ImGuiUtil.OptionCheckbox(
|
|
||||||
ref tab.HideWhenNotLoggedIn,
|
|
||||||
Language.Options_HideWhenNotLoggedIn_Name
|
|
||||||
);
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
ImGuiUtil.OptionCheckbox(
|
|
||||||
ref tab.HideWhenUiHidden,
|
|
||||||
Language.Options_HideWhenUiHidden_Name
|
|
||||||
);
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
ImGuiUtil.OptionCheckbox(
|
|
||||||
ref tab.HideInLoadingScreens,
|
|
||||||
Language.Options_HideInLoadingScreens_Name
|
|
||||||
);
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
ImGuiUtil.OptionCheckbox(
|
|
||||||
ref tab.HideInBattle,
|
|
||||||
Language.Options_HideInBattle_Name
|
|
||||||
);
|
|
||||||
ImGui.Spacing();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGuiUtil.OptionCheckbox(ref tab.CanMove, Language.Popout_CanMove_Name);
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
ImGuiUtil.OptionCheckbox(ref tab.CanResize, Language.Popout_CanResize_Name);
|
|
||||||
ImGui.Spacing();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toRemove > -1)
|
|
||||||
{
|
|
||||||
Mutable.Tabs.RemoveAt(toRemove);
|
|
||||||
Plugin.WantedTab = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (doOpens)
|
|
||||||
ToOpen = -2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
using System.Numerics;
|
|
||||||
using Dalamud.Bindings.ImGui;
|
|
||||||
using HellionChat.Themes;
|
|
||||||
using HellionChat.Util;
|
|
||||||
|
|
||||||
namespace HellionChat.Ui.SettingsTabs;
|
|
||||||
|
|
||||||
internal static class ThemeMockup
|
|
||||||
{
|
|
||||||
// Mini chat window mockup drawn directly into the WindowDrawList.
|
|
||||||
// No textures, no per-frame allocations — pure AddRectFilled/AddText.
|
|
||||||
public static void Draw(Vector2 origin, Vector2 size, Theme theme)
|
|
||||||
{
|
|
||||||
var draw = ImGui.GetWindowDrawList();
|
|
||||||
var c = theme.Colors;
|
|
||||||
|
|
||||||
// Window background
|
|
||||||
draw.AddRectFilled(
|
|
||||||
origin,
|
|
||||||
origin + size,
|
|
||||||
ColourUtil.RgbaToAbgr(c.WindowBg | 0xFFu),
|
|
||||||
theme.Layout.WindowRounding
|
|
||||||
);
|
|
||||||
|
|
||||||
// Title bar
|
|
||||||
var titleHeight = 14f;
|
|
||||||
draw.AddRectFilled(
|
|
||||||
origin,
|
|
||||||
new Vector2(origin.X + size.X, origin.Y + titleHeight),
|
|
||||||
ColourUtil.RgbaToAbgr(c.Identity),
|
|
||||||
theme.Layout.WindowRounding
|
|
||||||
);
|
|
||||||
|
|
||||||
// Tab bar (3 tabs)
|
|
||||||
var tabY = origin.Y + titleHeight + 4f;
|
|
||||||
var tabHeight = 12f;
|
|
||||||
for (var i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
var tabX = origin.X + 6f + i * 28f;
|
|
||||||
var color = i == 0 ? c.FrameBg : c.ChildBg;
|
|
||||||
draw.AddRectFilled(
|
|
||||||
new Vector2(tabX, tabY),
|
|
||||||
new Vector2(tabX + 26f, tabY + tabHeight),
|
|
||||||
ColourUtil.RgbaToAbgr(color),
|
|
||||||
theme.Layout.TabRounding
|
|
||||||
);
|
|
||||||
|
|
||||||
if (i == 0) // active pill
|
|
||||||
{
|
|
||||||
draw.AddRectFilled(
|
|
||||||
new Vector2(tabX, tabY + tabHeight - 2f),
|
|
||||||
new Vector2(tabX + 26f, tabY + tabHeight),
|
|
||||||
ColourUtil.RgbaToAbgr(c.Primary)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Message card row
|
|
||||||
var rowY = tabY + tabHeight + 6f;
|
|
||||||
var rowHeight = 18f;
|
|
||||||
draw.AddRectFilled(
|
|
||||||
new Vector2(origin.X + 6f, rowY),
|
|
||||||
new Vector2(origin.X + size.X - 6f, rowY + rowHeight),
|
|
||||||
ColourUtil.RgbaToAbgr(c.Surface),
|
|
||||||
2f
|
|
||||||
);
|
|
||||||
|
|
||||||
// Accent button (bottom right)
|
|
||||||
var btnW = 28f;
|
|
||||||
var btnH = 10f;
|
|
||||||
var btnX = origin.X + size.X - btnW - 6f;
|
|
||||||
var btnY = origin.Y + size.Y - btnH - 6f;
|
|
||||||
draw.AddRectFilled(
|
|
||||||
new Vector2(btnX, btnY),
|
|
||||||
new Vector2(btnX + btnW, btnY + btnH),
|
|
||||||
ColourUtil.RgbaToAbgr(c.Accent),
|
|
||||||
theme.Layout.FrameRounding
|
|
||||||
);
|
|
||||||
|
|
||||||
// Mockup border
|
|
||||||
draw.AddRect(
|
|
||||||
origin,
|
|
||||||
origin + size,
|
|
||||||
ColourUtil.RgbaToAbgr(c.Border),
|
|
||||||
theme.Layout.WindowRounding
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
using Dalamud.Bindings.ImGui;
|
|
||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using HellionChat.Resources;
|
|
||||||
using HellionChat.Util;
|
|
||||||
|
|
||||||
namespace HellionChat.Ui.SettingsTabs;
|
|
||||||
|
|
||||||
internal sealed class Window : ISettingsTab
|
|
||||||
{
|
|
||||||
private Plugin Plugin { get; }
|
|
||||||
private Configuration Mutable { get; }
|
|
||||||
|
|
||||||
public string Name => HellionStrings.Settings_Tab_Window + "###tabs-window";
|
|
||||||
|
|
||||||
internal Window(Plugin plugin, Configuration mutable)
|
|
||||||
{
|
|
||||||
Plugin = plugin;
|
|
||||||
Mutable = mutable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Draw(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
DrawHideSection(sectionJustEntered);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawInactivityHideSection(sectionJustEntered);
|
|
||||||
ImGui.Spacing();
|
|
||||||
DrawFrameSection(sectionJustEntered);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawHideSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Hide);
|
|
||||||
if (!tree.Success)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
ImGui.Checkbox(Language.Options_HideChat_Name, ref Mutable.HideChat);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_HideChat_Description);
|
|
||||||
|
|
||||||
ImGui.Checkbox(
|
|
||||||
Language.Options_HideDuringCutscenes_Name,
|
|
||||||
ref Mutable.HideDuringCutscenes
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(
|
|
||||||
string.Format(Language.Options_HideDuringCutscenes_Description, Plugin.PluginName)
|
|
||||||
);
|
|
||||||
|
|
||||||
ImGui.Checkbox(
|
|
||||||
Language.Options_HideWhenNotLoggedIn_Name,
|
|
||||||
ref Mutable.HideWhenNotLoggedIn
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(
|
|
||||||
string.Format(Language.Options_HideWhenNotLoggedIn_Description, Plugin.PluginName)
|
|
||||||
);
|
|
||||||
|
|
||||||
ImGui.Checkbox(Language.Options_HideWhenUiHidden_Name, ref Mutable.HideWhenUiHidden);
|
|
||||||
ImGuiUtil.HelpMarker(
|
|
||||||
string.Format(Language.Options_HideWhenUiHidden_Description, Plugin.PluginName)
|
|
||||||
);
|
|
||||||
|
|
||||||
ImGui.Checkbox(
|
|
||||||
Language.Options_HideInLoadingScreens_Name,
|
|
||||||
ref Mutable.HideInLoadingScreens
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(
|
|
||||||
string.Format(Language.Options_HideInLoadingScreens_Description, Plugin.PluginName)
|
|
||||||
);
|
|
||||||
|
|
||||||
ImGui.Checkbox(Language.Options_HideInBattle_Name, ref Mutable.HideInBattle);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_HideInBattle_Description);
|
|
||||||
|
|
||||||
ImGui.Checkbox(
|
|
||||||
Language.Options_HideInNewGamePlusMenu_Name,
|
|
||||||
ref Mutable.HideInNewGamePlusMenu
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_HideInNewGamePlusMenu_Description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawInactivityHideSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_InactivityHide);
|
|
||||||
if (!tree.Success)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
ImGui.Checkbox(Language.Options_HideWhenInactive_Name, ref Mutable.HideWhenInactive);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_HideWhenInactive_Description);
|
|
||||||
|
|
||||||
if (!Mutable.HideWhenInactive)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGuiUtil.InputIntVertical(
|
|
||||||
Language.Options_InactivityHideTimeout_Name,
|
|
||||||
Language.Options_InactivityHideTimeout_Description,
|
|
||||||
ref Mutable.InactivityHideTimeout,
|
|
||||||
1,
|
|
||||||
10
|
|
||||||
);
|
|
||||||
// Floor at 2 seconds to prevent self-soft-lock.
|
|
||||||
Mutable.InactivityHideTimeout = Math.Max(2, Mutable.InactivityHideTimeout);
|
|
||||||
|
|
||||||
using (ImRaii.Disabled(Mutable.HideInBattle))
|
|
||||||
{
|
|
||||||
ImGui.Checkbox(
|
|
||||||
Language.Options_InactivityHideActiveDuringBattle_Name,
|
|
||||||
ref Mutable.InactivityHideActiveDuringBattle
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(Language.Options_InactivityHideActiveDuringBattle_Description);
|
|
||||||
}
|
|
||||||
|
|
||||||
using var channelTree = ImRaii.TreeNode(Language.Options_InactivityHideChannels_Name);
|
|
||||||
if (!channelTree.Success)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
ImGuiUtil.CtrlShiftButton(
|
|
||||||
Language.Options_InactivityHideChannels_All_Label,
|
|
||||||
Language.Options_InactivityHideChannels_Button_Tooltip
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Mutable.InactivityHideChannelsV2 = TabsUtil.AllChannels();
|
|
||||||
Mutable.InactivityHideExtraChatAll = true;
|
|
||||||
Mutable.InactivityHideExtraChatChannels = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.SameLine();
|
|
||||||
if (
|
|
||||||
ImGuiUtil.CtrlShiftButton(
|
|
||||||
Language.Options_InactivityHideChannels_None_Label,
|
|
||||||
Language.Options_InactivityHideChannels_Button_Tooltip
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Mutable.InactivityHideChannelsV2 = [];
|
|
||||||
Mutable.InactivityHideExtraChatAll = false;
|
|
||||||
Mutable.InactivityHideExtraChatChannels = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
ImGuiUtil.ChannelSelector(
|
|
||||||
Language.Options_Tabs_Channels,
|
|
||||||
Mutable.InactivityHideChannelsV2
|
|
||||||
);
|
|
||||||
ImGuiUtil.ExtraChatSelector(
|
|
||||||
Language.Options_Tabs_ExtraChatChannels,
|
|
||||||
ref Mutable.InactivityHideExtraChatAll,
|
|
||||||
Mutable.InactivityHideExtraChatChannels
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawFrameSection(bool sectionJustEntered)
|
|
||||||
{
|
|
||||||
if (sectionJustEntered)
|
|
||||||
ImGui.SetNextItemOpen(false);
|
|
||||||
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Section_Frame);
|
|
||||||
if (!tree.Success)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
||||||
{
|
|
||||||
ImGui.Checkbox(Language.Options_CanMove_Name, ref Mutable.CanMove);
|
|
||||||
ImGui.Checkbox(Language.Options_CanResize_Name, ref Mutable.CanResize);
|
|
||||||
|
|
||||||
ImGui.Checkbox(
|
|
||||||
HellionStrings.Settings_Window_PopOutInputEnabled_Name,
|
|
||||||
ref Mutable.PopOutInputEnabled
|
|
||||||
);
|
|
||||||
ImGuiUtil.HelpMarker(HellionStrings.Settings_Window_PopOutInputEnabled_Description);
|
|
||||||
|
|
||||||
ImGui.Spacing();
|
|
||||||
|
|
||||||
// Fallback for off-screen windows after a display layout change.
|
|
||||||
if (ImGui.Button(HellionStrings.Settings_Window_ResetPosition_Name))
|
|
||||||
Plugin.ChatLogWindow.RequestPositionReset = true;
|
|
||||||
ImGuiUtil.HelpMarker(HellionStrings.Settings_Window_ResetPosition_Description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user