feat(style): let a setting row render disabled

Six settings across the window only apply while another one is on. Today they
sit at full contrast and simply do nothing when clicked.

BeginDisabled cannot carry this: it pushes an alpha that ImGui applies inside
its own widgets, and every part of a setting row that a user reads -- label,
description, separator, hover fill -- is draw-list output that never sees it.
So the row fades itself, drops its click, and stops tracking hover.

The flag is passed through to the control callback as well. A draw-list control
handed into the row has exactly the same problem and no other way to learn about
it.

Note the parameter sits before styleOverride, so the one existing positional
call site had to name its argument.
This commit is contained in:
2026-08-18 13:37:43 +02:00
parent 688c05cd77
commit fa15130468
2 changed files with 98 additions and 9 deletions
@@ -34,6 +34,10 @@ internal readonly record struct SettingRowContext
public float ControlHeight { get; init; }
public float HoverAmount { get; init; }
// Passed through so a draw-list control can fade itself. BeginDisabled only
// reaches ImGui's own widgets, so the row cannot dim its control for it.
public bool Disabled { get; init; }
// Right edge of the control column, vertically centred.
public Vector2 AlignRight(Vector2 size) =>
new(
@@ -56,10 +60,12 @@ internal static class SettingRow
string? description,
SettingRowColors colors,
Action<SettingRowContext> drawControl,
bool disabled = false,
SettingRowStyle? styleOverride = null
)
{
var style = styleOverride ?? new SettingRowStyle();
var alpha = disabled ? 0.5f : 1f;
var scale = Metrics.Scale;
var padY = style.PadY * scale;
var gap = style.Gap * scale;
@@ -78,11 +84,13 @@ internal static class SettingRow
// 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);
var labelClicked = ImGui.InvisibleButton(
$"##hellion-srow-{id}",
new Vector2(labelWidth, size.Y)
);
var hovered = ImGui.IsMouseHoveringRect(origin, origin + size) && ImGui.IsWindowHovered();
var labelClicked =
ImGui.InvisibleButton($"##hellion-srow-{id}", new Vector2(labelWidth, size.Y))
&& !disabled;
var hovered =
!disabled
&& ImGui.IsMouseHoveringRect(origin, origin + size)
&& ImGui.IsWindowHovered();
var hoverAmount = HoverState.Query(id, hovered);
// Chrome first, all of it draw-list only: nothing between here and the
@@ -98,7 +106,7 @@ internal static class SettingRow
SurfaceHoverAbgr = colors.SurfaceHoverAbgr,
SurfaceActiveAbgr = colors.SurfaceHoverAbgr,
AccentAbgr = colors.BorderAbgr,
BorderAbgr = colors.BorderAbgr,
BorderAbgr = ColourUtil.ApplyAlpha(colors.BorderAbgr, alpha),
},
new RowStyle { AccentBarWidth = 0f, DrawSeparator = style.DrawSeparator }
);
@@ -107,13 +115,17 @@ 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);
dl.AddText(new Vector2(origin.X, origin.Y + padY), colors.LabelAbgr, label);
dl.AddText(
new Vector2(origin.X, origin.Y + padY),
ColourUtil.ApplyAlpha(colors.LabelAbgr, alpha),
label
);
if (description is not null)
dl.AddText(
ImGui.GetFont(),
ImGui.GetFontSize(),
new Vector2(origin.X, origin.Y + padY + lineHeight),
colors.DescriptionAbgr,
ColourUtil.ApplyAlpha(colors.DescriptionAbgr, alpha),
description,
labelWidth
);
@@ -129,6 +141,7 @@ internal static class SettingRow
ControlWidth = controlWidth,
ControlHeight = lineHeight,
HoverAmount = hoverAmount,
Disabled = disabled,
}
);
+77 -1
View File
@@ -48,6 +48,7 @@ internal sealed class WidgetGalleryWindow : Window
DrawSectionHeaderSection(c);
DrawRowSection(c);
DrawToggleSection(c);
DrawSegmentedSection(c);
DrawSettingRowSection(c);
DrawBadgeSection(c);
DrawPillSection(c);
@@ -250,9 +251,84 @@ internal sealed class WidgetGalleryWindow : Window
null,
colors,
_ => ImGui.SliderInt("##gallery-sr-d", ref _badgeCount, 0, 150),
new SettingRowStyle { DrawSeparator = false, PreferredControlWidth = 90f }
styleOverride: new SettingRowStyle
{
DrawSeparator = false,
PreferredControlWidth = 90f,
}
);
// Disabled: the label fades, the row stops lighting up, and the click
// does not reach the setting.
using (ImRaii.Disabled())
{
SettingRow.Draw(
ImGui.GetID("gallery.settingrow.disabled"),
"Disabled row",
"Neither the row nor its control may respond.",
colors,
_ => ImGui.SliderInt("##gallery-sr-e", ref _badgeCount, 0, 150),
disabled: true
);
}
ImGui.Spacing();
}
// Segment labels are held rather than built per frame: a collection
// expression in the draw call would allocate a fresh array every frame.
private static readonly string[] SegmentLabelsTwo = ["Sidebar", "Top tabs"];
private static readonly string[] SegmentLabelsThree = ["Off", "Compact", "Full"];
private int _segmentTwo;
private int _segmentThree = 1;
private void DrawSegmentedSection(ThemeColors c)
{
var colors = new SegmentedControlColors
{
TrackAbgr = _palette.Abgr(Token.SurfaceBase, c),
SelectedAbgr = _palette.Abgr(Token.AccentPrimary, c),
HoverAbgr = _palette.Abgr(Token.SurfaceHover, c),
LabelAbgr = _palette.Abgr(Token.TextMuted, c),
SelectedLabelAbgr = _palette.Abgr(Token.Text, c),
BorderAbgr = _palette.Abgr(Token.Border, c),
};
var width = MathF.Min(260f * Metrics.Scale, ImGui.GetContentRegionAvail().X);
_segmentTwo = SegmentedControl.Draw(
ImGui.GetID("gallery.segmented.two"),
ImGui.GetCursorScreenPos(),
width,
SegmentLabelsTwo,
_segmentTwo,
colors
);
ImGui.Dummy(SegmentedControl.CalcSize(width));
_segmentThree = SegmentedControl.Draw(
ImGui.GetID("gallery.segmented.three"),
ImGui.GetCursorScreenPos(),
width,
SegmentLabelsThree,
_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.
SegmentedControl.Draw(
ImGui.GetID("gallery.segmented.odd"),
ImGui.GetCursorScreenPos(),
201f,
SegmentLabelsThree,
0,
colors,
disabled: true
);
ImGui.Dummy(SegmentedControl.CalcSize(201f));
ImGui.Spacing();
}