fix(style): reserve the height a wrapped description actually needs
Both widgets drew their description with a wrap width and then sized the row as if it were always one line. Same bug, two different symptoms. A setting row clips to its label column, so the second line was simply cut off, directly under a comment claiming the text wraps rather than being cut. A section header has no clip rect at all, so the overflow was drawn over its own border line and into whatever the caller rendered next. Both now measure with CalcTextSize against the same wrap width they draw with. The section header also gained the clip rect it never had, and its lead offset is computed once instead of twice, so the measured width and the drawn width cannot drift apart. Third fix, same family: the segmented control relied on its last InvisibleButton to leave the cursor in the right place. It happened to work, because the loop re-pins the cursor before every segment, but a caller cannot see that from the outside -- and the gallery duly reserved the row a second time with Dummy, doubling the gap after every segmented control. The widget now owns its advance explicitly, like the other two, and CalcSize is gone with its only caller.
This commit is contained in:
@@ -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),
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user