diff --git a/HellionChat/Ui/StyleEngine/Widgets/SectionHeader.cs b/HellionChat/Ui/StyleEngine/Widgets/SectionHeader.cs index 3aac2b6..f456543 100644 --- a/HellionChat/Ui/StyleEngine/Widgets/SectionHeader.cs +++ b/HellionChat/Ui/StyleEngine/Widgets/SectionHeader.cs @@ -60,7 +60,16 @@ internal static class SectionHeader var origin = ImGui.GetCursorScreenPos(); var width = ImGui.GetContentRegionAvail().X; var titleHeight = ImGui.GetTextLineHeight(); - var descHeight = description is null ? 0f : titleHeight; + + // The description starts after the chevron, so its wrap width is the + // remainder of the row. Measuring it matters more here than in a setting + // row: nothing clips this text, so an unreserved second line would draw + // straight over the border line and whatever the caller renders next. + var leadWidth = (style.AccentBarWidth + style.ChevronInset * 2f) * scale; + var descWrap = width - leadWidth; + var descHeight = description is null + ? 0f + : ImGui.CalcTextSize(description, false, descWrap).Y; var size = WidgetGeometry.SectionHeader(width, titleHeight, descHeight, padY, 1f * scale); ImGui.SetCursorScreenPos(origin); @@ -95,7 +104,7 @@ internal static class SectionHeader ColourUtil.ApplyAlpha(colors.AccentAbgr, alpha) ); - var textX = origin.X + barWidth + style.ChevronInset * scale * 2f; + var textX = origin.X + leadWidth; dl.AddText( new Vector2(textX, origin.Y + padY), ColourUtil.ApplyAlpha(colors.TitleAbgr, alpha), @@ -114,14 +123,18 @@ internal static class SectionHeader ); if (description is not null) + { + dl.PushClipRect(origin, max, true); dl.AddText( ImGui.GetFont(), ImGui.GetFontSize(), new Vector2(textX, origin.Y + padY + titleHeight), ColourUtil.ApplyAlpha(colors.DescriptionAbgr, alpha), description, - width - (textX - origin.X) + descWrap ); + dl.PopClipRect(); + } dl.AddLine( new Vector2(origin.X, max.Y - 1f * scale), diff --git a/HellionChat/Ui/StyleEngine/Widgets/SegmentedControl.cs b/HellionChat/Ui/StyleEngine/Widgets/SegmentedControl.cs index f04049a..1735b62 100644 --- a/HellionChat/Ui/StyleEngine/Widgets/SegmentedControl.cs +++ b/HellionChat/Ui/StyleEngine/Widgets/SegmentedControl.cs @@ -127,8 +127,15 @@ internal static class SegmentedControl rounding ); + // Explicit, rather than inheriting whatever the last InvisibleButton left + // behind. The loop re-pins the cursor before every segment, so the run + // happens to end in the right place today -- but that is a side effect, + // and a caller cannot tell from here whether it still needs to reserve + // the row itself. Same contract as SettingRow and SectionHeader now: + // the widget owns its own advance. + ImGui.SetCursorScreenPos(origin); + ImGuiP.ItemSize(new Vector2(width, height)); + return picked; } - - internal static Vector2 CalcSize(float width) => new(width, ImGui.GetFrameHeight()); } diff --git a/HellionChat/Ui/StyleEngine/Widgets/SettingRow.cs b/HellionChat/Ui/StyleEngine/Widgets/SettingRow.cs index 32385ba..71cdf75 100644 --- a/HellionChat/Ui/StyleEngine/Widgets/SettingRow.cs +++ b/HellionChat/Ui/StyleEngine/Widgets/SettingRow.cs @@ -73,14 +73,23 @@ internal static class SettingRow var origin = ImGui.GetCursorScreenPos(); var width = ImGui.GetContentRegionAvail().X; var lineHeight = ImGui.GetFrameHeight(); - var descHeight = description is null ? 0f : ImGui.GetTextLineHeight(); - var size = WidgetGeometry.SettingRow(width, lineHeight, descHeight, padY); + + // Split first: the description is drawn with the label column as its + // wrap width, so its height cannot be known before that width is. var (labelWidth, controlX, controlWidth) = WidgetGeometry.SettingRowSplit( width, style.PreferredControlWidth * scale, gap ); + // Measured, not assumed to be one line. AddText wraps at labelWidth, so + // a description long enough to need a second line used to be drawn into + // height the row never reserved, and the clip rect below cut it off. + var descHeight = description is null + ? 0f + : ImGui.CalcTextSize(description, false, labelWidth).Y; + var size = WidgetGeometry.SettingRow(width, lineHeight, descHeight, padY); + // The label half is the hit area: the control column submits its own // item and would fight with a button underneath it. ImGui.SetCursorScreenPos(origin); diff --git a/HellionChat/Ui/Windows/WidgetGalleryWindow.cs b/HellionChat/Ui/Windows/WidgetGalleryWindow.cs index cb9c9fa..7695600 100644 --- a/HellionChat/Ui/Windows/WidgetGalleryWindow.cs +++ b/HellionChat/Ui/Windows/WidgetGalleryWindow.cs @@ -304,7 +304,6 @@ internal sealed class WidgetGalleryWindow : Window _segmentTwo, colors ); - ImGui.Dummy(SegmentedControl.CalcSize(width)); _segmentThree = SegmentedControl.Draw( ImGui.GetID("gallery.segmented.three"), @@ -314,7 +313,6 @@ internal sealed class WidgetGalleryWindow : Window _segmentThree, colors ); - ImGui.Dummy(SegmentedControl.CalcSize(width)); // Odd width over three segments: the edges must stay flush with no seam // and no overhang on the right. @@ -327,7 +325,6 @@ internal sealed class WidgetGalleryWindow : Window colors, disabled: true ); - ImGui.Dummy(SegmentedControl.CalcSize(201f)); ImGui.Spacing(); }