689 lines
26 KiB
C#
689 lines
26 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|