fix(style): close the remaining unreserved-space cases
Three more instances of the pattern the previous fix only half caught. The section header clipped its description and not its title, so a translated heading wider than the pane ran out over the scrollbar. Only the window clip rect stopped it, not the content width. Row's separator used an unscaled 1px offset with a scaled stroke. ImGui strokes centred on the path, so at UI scale 2 half the line sits below the rectangle the row reserved -- and rows stack flush, so it landed in the first pixel row of the next one. Offset now derives from the thickness. The segmented control claimed in its own comment to share the setting row's contract. It did not. It took a position as a parameter and then re-pinned the layout cursor to it, which moves a caller's layout whenever that position was not the cursor, and it advanced by the full height where the other two subtract ItemSpacing.Y. It now reads the cursor like they do, and the comment says why the advance still differs: those two stack flush because they are list entries, this is a single control and takes the normal gap. Also: the label centres against the control band when it has no description below it, since a slider draws its text at FramePadding.Y and a top-aligned label sits visibly high next to it. Gallery arrays are held rather than rebuilt per frame, which its own comment already demanded two sections earlier. And SectionHeader.Reset is gone -- no caller, and section state keys off ids rather than labels, so nothing needs to clear it on a language change.
This commit is contained in:
@@ -67,11 +67,19 @@ internal static class Row
|
||||
}
|
||||
|
||||
if (style.DrawSeparator)
|
||||
{
|
||||
// Offset scales with the thickness. ImGui strokes centred on the
|
||||
// path, so an unscaled 1px offset with a scaled stroke puts half the
|
||||
// line below max.Y -- and rows stack flush, so that half lands in the
|
||||
// first pixel row of the next one.
|
||||
var thickness = Metrics.Scale;
|
||||
var y = max.Y - thickness * 0.5f;
|
||||
dl.AddLine(
|
||||
new Vector2(origin.X, max.Y - 1f),
|
||||
new Vector2(max.X, max.Y - 1f),
|
||||
new Vector2(origin.X, y),
|
||||
new Vector2(max.X, y),
|
||||
state.BorderAbgr,
|
||||
Metrics.Scale
|
||||
thickness
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +35,6 @@ internal static class SectionHeader
|
||||
{
|
||||
private static readonly Dictionary<uint, bool> Open = [];
|
||||
|
||||
internal static void Reset() => Open.Clear();
|
||||
|
||||
internal static bool Draw(
|
||||
uint key,
|
||||
string title,
|
||||
@@ -105,11 +103,13 @@ internal static class SectionHeader
|
||||
);
|
||||
|
||||
var textX = origin.X + leadWidth;
|
||||
dl.PushClipRect(origin, max, true);
|
||||
dl.AddText(
|
||||
new Vector2(textX, origin.Y + padY),
|
||||
ColourUtil.ApplyAlpha(colors.TitleAbgr, alpha),
|
||||
title
|
||||
);
|
||||
dl.PopClipRect();
|
||||
|
||||
DrawChevron(
|
||||
dl,
|
||||
|
||||
@@ -38,9 +38,13 @@ internal static class SegmentedControl
|
||||
private static uint AnimKey(uint id, int index) =>
|
||||
(id ^ (uint)(index + 1) * 0x85EBCA6Bu) * 2654435761u + 0x9E3779B9u;
|
||||
|
||||
// Reads the cursor rather than taking a position, like SettingRow and
|
||||
// SectionHeader. It cannot avoid reserving space -- every segment submits an
|
||||
// InvisibleButton, and those always advance -- so taking an origin would let
|
||||
// a caller place it somewhere the reservation does not match.
|
||||
// To right-align it inside a setting row, set the cursor before calling.
|
||||
internal static int Draw(
|
||||
uint id,
|
||||
Vector2 origin,
|
||||
float width,
|
||||
ReadOnlySpan<string> labels,
|
||||
int selected,
|
||||
@@ -52,6 +56,7 @@ internal static class SegmentedControl
|
||||
if (labels.Length == 0)
|
||||
return selected;
|
||||
|
||||
var origin = ImGui.GetCursorScreenPos();
|
||||
var style = styleOverride ?? new SegmentedControlStyle();
|
||||
var scale = Metrics.Scale;
|
||||
var rounding = style.Rounding * scale;
|
||||
@@ -128,11 +133,12 @@ internal static class SegmentedControl
|
||||
);
|
||||
|
||||
// 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.
|
||||
// behind: the loop re-pins the cursor before every segment, so the run
|
||||
// only ends in the right place as a side effect.
|
||||
//
|
||||
// The full height, unlike SettingRow and SectionHeader, which subtract
|
||||
// ItemSpacing.Y. Those two stack flush on purpose because they are list
|
||||
// entries. This is a single control and takes the normal gap.
|
||||
ImGui.SetCursorScreenPos(origin);
|
||||
ImGuiP.ItemSize(new Vector2(width, height));
|
||||
|
||||
|
||||
@@ -124,8 +124,15 @@ internal static class SettingRow
|
||||
// control. The description wraps instead of being cut.
|
||||
var dl = ImGui.GetWindowDrawList();
|
||||
dl.PushClipRect(origin, new Vector2(origin.X + labelWidth, origin.Y + size.Y), true);
|
||||
// Without a description the label is alone next to the control, so it
|
||||
// centres against the control band. A slider draws its text at
|
||||
// FramePadding.Y, so a top-aligned label sits visibly high next to it.
|
||||
// With a description the pair is top-aligned and this must not apply.
|
||||
var labelY = description is null
|
||||
? origin.Y + padY + MetricsMath.Center(lineHeight, ImGui.GetTextLineHeight())
|
||||
: origin.Y + padY;
|
||||
dl.AddText(
|
||||
new Vector2(origin.X, origin.Y + padY),
|
||||
new Vector2(origin.X, labelY),
|
||||
ColourUtil.ApplyAlpha(colors.LabelAbgr, alpha),
|
||||
label
|
||||
);
|
||||
|
||||
@@ -282,6 +282,16 @@ internal sealed class WidgetGalleryWindow : Window
|
||||
private int _segmentTwo;
|
||||
private int _segmentThree = 1;
|
||||
|
||||
// Held, not rebuilt per frame: a collection expression inside Draw
|
||||
// allocates a fresh array on every frame the window is open.
|
||||
private readonly int[] _badgeSamples = [1, 9, 0, 120];
|
||||
private static readonly FontAwesomeIcon[] GalleryGlyphs =
|
||||
[
|
||||
FontAwesomeIcon.ArrowUpRightFromSquare,
|
||||
FontAwesomeIcon.Check,
|
||||
FontAwesomeIcon.CheckCircle,
|
||||
];
|
||||
|
||||
private void DrawSegmentedSection(ThemeColors c)
|
||||
{
|
||||
var colors = new SegmentedControlColors
|
||||
@@ -298,7 +308,6 @@ internal sealed class WidgetGalleryWindow : Window
|
||||
|
||||
_segmentTwo = SegmentedControl.Draw(
|
||||
ImGui.GetID("gallery.segmented.two"),
|
||||
ImGui.GetCursorScreenPos(),
|
||||
width,
|
||||
SegmentLabelsTwo,
|
||||
_segmentTwo,
|
||||
@@ -307,7 +316,6 @@ internal sealed class WidgetGalleryWindow : Window
|
||||
|
||||
_segmentThree = SegmentedControl.Draw(
|
||||
ImGui.GetID("gallery.segmented.three"),
|
||||
ImGui.GetCursorScreenPos(),
|
||||
width,
|
||||
SegmentLabelsThree,
|
||||
_segmentThree,
|
||||
@@ -318,7 +326,6 @@ internal sealed class WidgetGalleryWindow : Window
|
||||
// and no overhang on the right.
|
||||
SegmentedControl.Draw(
|
||||
ImGui.GetID("gallery.segmented.odd"),
|
||||
ImGui.GetCursorScreenPos(),
|
||||
201f,
|
||||
SegmentLabelsThree,
|
||||
0,
|
||||
@@ -334,7 +341,8 @@ internal sealed class WidgetGalleryWindow : Window
|
||||
ImGui.TextUnformatted("Badge");
|
||||
ImGui.SliderInt("count##badge", ref _badgeCount, 0, 150);
|
||||
|
||||
int[] samples = [1, 9, _badgeCount, 120];
|
||||
_badgeSamples[2] = _badgeCount;
|
||||
var samples = _badgeSamples;
|
||||
var origin = ImGui.GetCursorScreenPos();
|
||||
var x = origin.X;
|
||||
foreach (var n in samples)
|
||||
@@ -393,12 +401,7 @@ internal sealed class WidgetGalleryWindow : Window
|
||||
ImGui.TextUnformatted("IconButton");
|
||||
|
||||
var size = new Vector2(Metrics.SidebarPopOutHitWidth, Metrics.SidebarRowHeight);
|
||||
FontAwesomeIcon[] glyphs =
|
||||
[
|
||||
FontAwesomeIcon.ArrowUpRightFromSquare,
|
||||
FontAwesomeIcon.Check,
|
||||
FontAwesomeIcon.CheckCircle,
|
||||
];
|
||||
var glyphs = GalleryGlyphs;
|
||||
|
||||
for (var i = 0; i < glyphs.Length; i++)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user