fix(settings): card grid wraps correctly, detail view drops legacy tab list

SettingsOverview now wraps each card in BeginGroup/EndGroup so SameLine
in the loop can wrap rows. The card content is drawn directly into the
DrawList (icon, title, subtext) without cursor hopping that broke the
flow.

DrawDetail no longer renders the second-column tab list — the user has
already picked a section from the overview, the redundant column made
the detail view feel like the old vanilla settings layout. Section
content now uses the full width.
This commit is contained in:
2026-05-05 14:28:24 +02:00
parent c943a2cff3
commit d41cea0031
2 changed files with 30 additions and 52 deletions
+10 -29
View File
@@ -143,36 +143,17 @@ public sealed class SettingsWindow : Dalamud.Interface.Windowing.Window
ImGui.Separator();
ImGui.Spacing();
// Tab-Liste + Content (wie vorher)
using (var table = ImRaii.Table("##chat2-settings-table", 2))
{
if (table.Success)
{
ImGui.TableSetupColumn("tab", ImGuiTableColumnFlags.WidthFixed);
ImGui.TableSetupColumn("settings", ImGuiTableColumnFlags.WidthStretch);
// Section-Content in voller Breite. Die Tab-Liste links ist überholt:
// der User ist bereits über die Card-Übersicht navigiert, eine zweite
// Tab-Liste daneben würde nur den Vanilla-Look zurückbringen. Falls
// der User in eine andere Section will, geht er zurück zur Overview
// (Breadcrumb / 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;
ImGui.TableNextColumn();
var changed = false;
for (var i = 0; i < Tabs.Count; i++)
{
if (!ImGui.Selectable($"{Tabs[i].Name}###tab-{i}", CurrentTab == i))
continue;
CurrentTab = i;
changed = true;
}
ImGui.TableNextColumn();
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", new Vector2(-1, height));
if (child.Success)
Tabs[CurrentTab].Draw(changed);
}
}
using var child = ImRaii.Child("##chat2-settings-detail", new Vector2(-1, height));
if (child.Success)
Tabs[CurrentTab].Draw(false);
}
private void DrawSaveButtons()
+20 -23
View File
@@ -52,6 +52,11 @@ internal sealed class SettingsOverview
private void DrawCard(int index, FontAwesomeIcon icon, string title, string subtext, float w, float h)
{
// BeginGroup macht den Card-Bereich zu einem einzelnen ImGui-Layout-Item.
// Damit funktioniert SameLine() im Caller-Loop — sonst tracked ImGui die
// einzelnen InvisibleButton/Text-Items separat und das Wrapping bricht.
ImGui.BeginGroup();
var cursorBefore = ImGui.GetCursorScreenPos();
var clicked = ImGui.InvisibleButton($"##settings-card-{index}", new Vector2(w, h));
var hovered = ImGui.IsItemHovered();
@@ -60,35 +65,27 @@ internal sealed class SettingsOverview
var draw = ImGui.GetWindowDrawList();
draw.AddRectFilled(cursorBefore, cursorBefore + new Vector2(w, h), bgColor, 4f);
// ImGui hat den Cursor nach dem InvisibleButton bereits korrekt
// weitergesetzt (für die Layout-Engine). Wir merken uns die Position,
// verschieben den Cursor temporär für die Inhalts-Beschriftung und
// restoren ihn am Ende — sonst kollidieren manuelle Cursor-Sets mit
// SameLine im Caller und die Cards stapeln sich diagonal.
var cursorAfterButton = ImGui.GetCursorScreenPos();
// Inhalts-Overlay: Icon + Title + Subtext direkt mit DrawList in den
// Card-Bereich zeichnen, statt Cursor-Hopping mit SetCursorScreenPos.
// DrawList-Overlays ändern den Cursor nicht, BeginGroup/EndGroup
// hält den Layout-Anker stabil für SameLine.
var iconPos = cursorBefore + new Vector2(16f, 12f);
var titlePos = cursorBefore + new Vector2(16f, 40f);
var subtextPos = cursorBefore + new Vector2(16f, 62f);
var textPos = cursorBefore + new Vector2(16f, 12f);
ImGui.SetCursorScreenPos(textPos);
// Plugin ist hier Instanz, nicht Static-Type — daher über _window
// referenzieren (Codebase-Konvention, siehe ImGuiUtil.cs:22 für die
// alternative Static-Init-Pattern, das wir hier nicht nutzen).
var titleColor = ColourUtil.RgbaToAbgr(0xE6F4F1FFu);
var subtextColor = ColourUtil.RgbaToAbgr(0x8FA3B5FFu);
// Icon via FontAwesome — temporär den Font pushen, mit DrawList zeichnen
using (_window.Plugin.FontManager.FontAwesome.Push())
{
ImGui.Text(icon.ToIconString());
draw.AddText(iconPos, titleColor, icon.ToIconString());
}
ImGui.SetCursorScreenPos(textPos + new Vector2(0f, 28f));
ImGui.TextUnformatted(title);
draw.AddText(titlePos, titleColor, title);
draw.AddText(subtextPos, subtextColor, subtext);
ImGui.SetCursorScreenPos(textPos + new Vector2(0f, 50f));
using (ImRaii.PushColor(ImGuiCol.Text, 0xFF8FA3B5u))
{
ImGui.TextUnformatted(subtext);
}
// Restore cursor to post-button position, so SameLine + Wrap im Caller
// wieder mit dem ImGui-Layout-Flow arbeiten.
ImGui.SetCursorScreenPos(cursorAfterButton);
ImGui.EndGroup();
if (clicked)
{