From fa15130468938b9a04867895ed6c9e0f5554704e Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Tue, 18 Aug 2026 13:37:43 +0200 Subject: [PATCH] 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. --- .../Ui/StyleEngine/Widgets/SettingRow.cs | 29 +++++-- HellionChat/Ui/Windows/WidgetGalleryWindow.cs | 78 ++++++++++++++++++- 2 files changed, 98 insertions(+), 9 deletions(-) diff --git a/HellionChat/Ui/StyleEngine/Widgets/SettingRow.cs b/HellionChat/Ui/StyleEngine/Widgets/SettingRow.cs index d380a2f..32385ba 100644 --- a/HellionChat/Ui/StyleEngine/Widgets/SettingRow.cs +++ b/HellionChat/Ui/StyleEngine/Widgets/SettingRow.cs @@ -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 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, } ); diff --git a/HellionChat/Ui/Windows/WidgetGalleryWindow.cs b/HellionChat/Ui/Windows/WidgetGalleryWindow.cs index 227ed6a..cb9c9fa 100644 --- a/HellionChat/Ui/Windows/WidgetGalleryWindow.cs +++ b/HellionChat/Ui/Windows/WidgetGalleryWindow.cs @@ -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(); }