Files
HellionChat/HellionChat/Ui/HellionStyle.cs
T
JonKazama-Hellion 9ead8098f5 fix: card-overview subtext wrap + v16 default bumps + chat-colour preset
UI:
- SettingsOverview cards now wrap subtext to two lines (DrawList wrap-
  width) and the card height grew from 96 to 110 px. Single-line
  fitting clipped most of the bilingual subtitles.
- HellionStyle pushes ChildBg with alpha 0 when WindowOpacity < 1.0
  to keep stacked BeginChild layers from compounding the deckgrade
  past what the slider suggests.
- WindowOpacity slider helpmarker now points to Dalamud's per-window
  hamburger menu for opacity / blur / pin / click-through overrides.

UX defaults (v15 → v16 migration adopts new values only when the user
is still on the previous default — bool flips are heuristic, the prior
defaults are from the v1.2.0 cycle and rarely toggled):
- UseCompactDensity false → true (single-line message style is cleaner)
- HideInNewGamePlusMenu false → true (consistent with other hide-flags)
- HideSameTimestamps false → true (cleaner log)
- MaxLinesToRender 5000 → 2500 (mid-range hardware friendlier)
- ChatColours empty → Hellion brand preset (the first-run wizard does
  not offer a preset choice, so fresh installs get the brand colours
  out of the box)
2026-05-06 11:35:59 +02:00

155 lines
6.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using HellionChat.Themes;
using HellionChat.Util;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility.Raii;
namespace HellionChat.Ui;
/// <summary>
/// ImGui style override for Hellion Chat. v1.1.0 ist die Engine
/// theme-getrieben: PushGlobal nimmt eine Theme-Instance + Window-
/// Opacity, die gesamten Color- und Style-Slots werden aus dem Theme
/// gelesen statt aus einer fixen Konstanten-Tabelle.
/// </summary>
internal static class HellionStyle
{
/// <summary>
/// Local color stack auf Basis des aktiven Themes. Cheap. Use inside a
/// `using var _ = HellionStyle.Push(theme);` block.
/// </summary>
internal static IDisposable Push(Theme theme)
{
var c = theme.Colors;
var stack = new StackHandle();
stack.PushColor(ImGuiCol.Button, c.Primary);
stack.PushColor(ImGuiCol.ButtonHovered, c.PrimaryLight);
stack.PushColor(ImGuiCol.ButtonActive, c.PrimaryDark);
stack.PushColor(ImGuiCol.FrameBg, c.FrameBg);
stack.PushColor(ImGuiCol.FrameBgHovered, c.SurfaceHover);
stack.PushColor(ImGuiCol.FrameBgActive, c.Surface);
stack.PushColor(ImGuiCol.Border, c.Border);
stack.PushColor(ImGuiCol.Header, c.Surface);
stack.PushColor(ImGuiCol.HeaderHovered, c.SurfaceHover);
stack.PushColor(ImGuiCol.HeaderActive, c.Identity);
stack.PushColor(ImGuiCol.CheckMark, c.Primary);
stack.PushColor(ImGuiCol.SliderGrab, c.Primary);
stack.PushColor(ImGuiCol.SliderGrabActive, c.PrimaryLight);
return stack;
}
/// <summary>
/// Global color and style-variable stack pushed once per frame in
/// Plugin.Draw. Drives every Hellion-rendered window from the active
/// theme's palette and layout values.
/// </summary>
/// <param name="theme">Active theme from ThemeRegistry.</param>
/// <param name="windowOpacity">Window background alpha (0.51.0).</param>
internal static IDisposable PushGlobal(Theme theme, float windowOpacity = 1.0f)
{
var c = theme.Colors;
var l = theme.Layout;
var stack = new StackHandle();
var alphaByte = (uint)Math.Clamp((int)(windowOpacity * 255f), 0x55, 0xFF);
var windowBgWithAlpha = (c.WindowBg & 0xFFFFFF00u) | alphaByte;
// ChildBg-Alpha: Sub-Bereiche (Tab-Sidebar, Message-Area, Input-Bar)
// werden im ChatLog-Window als BeginChild gezeichnet. Würde der ChildBg
// mit dem gleichen Alpha wie WindowBg gerendert, multiplizieren sich
// die Layer (1 - (1-α)² Deckung), und 50 % WindowOpacity kommt mit
// 75 % Deckung im Child-Bereich an — das Fenster wirkt solider als der
// Slider verspricht. Bei voller Opacity bleibt der Theme-Akzent
// erhalten (Theme-eigene Alpha-Komponente, i.d.R. FF); sobald der User
// Transparenz zieht, wird ChildBg vollständig durchsichtig damit nur
// der WindowBg-Layer die finale Deckung bestimmt.
var childBgAlpha = windowOpacity >= 0.999f ? (c.ChildBg & 0xFFu) : 0u;
var childBgWithAlpha = (c.ChildBg & 0xFFFFFF00u) | childBgAlpha;
// Layout
stack.PushStyleVar(ImGuiStyleVar.WindowRounding, l.WindowRounding);
stack.PushStyleVar(ImGuiStyleVar.ChildRounding, l.ChildRounding);
stack.PushStyleVar(ImGuiStyleVar.PopupRounding, l.PopupRounding);
stack.PushStyleVar(ImGuiStyleVar.FrameRounding, l.FrameRounding);
stack.PushStyleVar(ImGuiStyleVar.GrabRounding, l.GrabRounding);
stack.PushStyleVar(ImGuiStyleVar.TabRounding, l.TabRounding);
stack.PushStyleVar(ImGuiStyleVar.ScrollbarRounding, l.ScrollbarRounding);
stack.PushStyleVar(ImGuiStyleVar.WindowBorderSize, l.WindowBorderSize);
stack.PushStyleVar(ImGuiStyleVar.FrameBorderSize, l.FrameBorderSize);
// Surfaces
stack.PushColor(ImGuiCol.WindowBg, windowBgWithAlpha);
stack.PushColor(ImGuiCol.ChildBg, childBgWithAlpha);
stack.PushColor(ImGuiCol.PopupBg, c.ChildBg);
stack.PushColor(ImGuiCol.Border, c.Border);
stack.PushColor(ImGuiCol.BorderShadow, 0u);
// Frames
stack.PushColor(ImGuiCol.FrameBg, c.FrameBg);
stack.PushColor(ImGuiCol.FrameBgHovered, c.SurfaceHover);
stack.PushColor(ImGuiCol.FrameBgActive, c.Surface);
// Title bars
stack.PushColor(ImGuiCol.TitleBg, c.WindowBg);
stack.PushColor(ImGuiCol.TitleBgActive, c.Identity);
stack.PushColor(ImGuiCol.TitleBgCollapsed, c.WindowBg);
// Buttons
stack.PushColor(ImGuiCol.Button, c.Primary);
stack.PushColor(ImGuiCol.ButtonHovered, c.PrimaryLight);
stack.PushColor(ImGuiCol.ButtonActive, c.PrimaryDark);
// Headers / selectables
stack.PushColor(ImGuiCol.Header, c.Surface);
stack.PushColor(ImGuiCol.HeaderHovered, c.SurfaceHover);
stack.PushColor(ImGuiCol.HeaderActive, c.Identity);
// Tabs
stack.PushColor(ImGuiCol.Tab, c.FrameBg);
stack.PushColor(ImGuiCol.TabHovered, c.PrimaryLight);
stack.PushColor(ImGuiCol.TabActive, c.Identity);
stack.PushColor(ImGuiCol.TabUnfocused, c.ChildBg);
stack.PushColor(ImGuiCol.TabUnfocusedActive, c.PrimaryDark);
// Scrollbar
stack.PushColor(ImGuiCol.ScrollbarBg, c.WindowBg);
stack.PushColor(ImGuiCol.ScrollbarGrab, c.Surface);
stack.PushColor(ImGuiCol.ScrollbarGrabHovered, c.AccentLight);
stack.PushColor(ImGuiCol.ScrollbarGrabActive, c.Accent);
// Resize grip
stack.PushColor(ImGuiCol.ResizeGrip, c.FrameBg);
stack.PushColor(ImGuiCol.ResizeGripHovered, c.AccentLight);
stack.PushColor(ImGuiCol.ResizeGripActive, c.Accent);
// Check mark + slider grab
stack.PushColor(ImGuiCol.CheckMark, c.Primary);
stack.PushColor(ImGuiCol.SliderGrab, c.Primary);
stack.PushColor(ImGuiCol.SliderGrabActive, c.PrimaryLight);
// Separator
stack.PushColor(ImGuiCol.Separator, c.Border);
stack.PushColor(ImGuiCol.SeparatorHovered, c.PrimaryLight);
stack.PushColor(ImGuiCol.SeparatorActive, c.Primary);
return stack;
}
private sealed class StackHandle : IDisposable
{
private readonly List<IDisposable> _items = new(64);
internal void PushColor(ImGuiCol slot, uint rgba)
=> _items.Add(ImRaii.PushColor(slot, ColourUtil.RgbaToAbgr(rgba)));
internal void PushStyleVar(ImGuiStyleVar var, float value)
=> _items.Add(ImRaii.PushStyle(var, value));
public void Dispose()
{
for (var i = _items.Count - 1; i >= 0; i--)
_items[i].Dispose();
_items.Clear();
}
}
}