Repository: gui-cs/Terminal.Gui (2026-04-17T01:59:41Z)

Issue:
`OptionSelector<TEnum>` needs  `UpdateChecked()` called explicitly.
A fresh `OptionSelector<TEnum>` has `Value = 0` but none of the CheckBoxes show as `Checked` until `UpdateChecked()` gets called explicitly.

SubViews.Count <= 0` works but `Count` can't be negative, so `== 0` reads a bit more naturally.

_Originally posted by @rcdailey in https://github.com/gui-cs/Terminal.Gui/pull/4911#issuecomment-4240126247_


Discussion:

Patch:
diff --git a/Terminal.Gui/Views/Selectors/OptionSelectorTEnum.cs b/Terminal.Gui/Views/Selectors/OptionSelectorTEnum.cs
index 56bcb96432..773b6121bf 100644
--- a/Terminal.Gui/Views/Selectors/OptionSelectorTEnum.cs
+++ b/Terminal.Gui/Views/Selectors/OptionSelectorTEnum.cs
@@ -10,7 +10,18 @@ public sealed class OptionSelector<TEnum> : OptionSelector, IValue where TEnum :
     /// <summary>
     ///     Initializes a new instance of the <see cref="OptionSelector{TEnum}"/> class.
     /// </summary>
-    public OptionSelector () => Labels = Enum.GetValues<TEnum> ().Select (f => f.ToString ()).ToArray ();
+    public OptionSelector ()
+    {
+        // Reset Value so that the Values setter can auto-set it to the first actual enum value.
+        // The base OptionSelector constructor sets Value = 0, which may not be a valid enum member.
+        base.Value = null;
+
+        // Set actual enum int values (bypass the throwing override via base).
+        base.Values = Enum.GetValues<TEnum> ().Select (f => Convert.ToInt32 (f)).ToList ().AsReadOnly ();
+
+        // Set labels, which triggers CreateSubViews + UpdateChecked.
+        Labels = Enum.GetNames<TEnum> ();
+    }
 
     /// <summary>
     ///     Gets or sets the value of the selected option.
diff --git a/Terminal.Gui/Views/Selectors/SelectorBase.cs b/Terminal.Gui/Views/Selectors/SelectorBase.cs
index 04f4315245..49ce2e74c6 100644
--- a/Terminal.Gui/Views/Selectors/SelectorBase.cs
+++ b/Terminal.Gui/Views/Selectors/SelectorBase.cs
@@ -535,7 +535,7 @@ protected void SetLayout ()
         // total width via PosAlign.CalculateMinDimension, which reads Frame.Width.
         // Without this, newly-created subviews have Frame.Width == 0 on the first
         // layout pass, causing the selector to be sized too narrow.
-        if (Orientation != Orientation.Horizontal || SubViews.Count <= 0)
+        if (Orientation != Orientation.Horizontal || SubViews.Count == 0)
         {
             return;
         }


Test patch:
diff --git a/Tests/UnitTestsParallelizable/Views/OptionSelectorTests.cs b/Tests/UnitTestsParallelizable/Views/OptionSelectorTests.cs
index d22b41fc0e..915fb274b0 100644
--- a/Tests/UnitTestsParallelizable/Views/OptionSelectorTests.cs
+++ b/Tests/UnitTestsParallelizable/Views/OptionSelectorTests.cs
@@ -1071,4 +1071,103 @@ public void Activated_Event_Ancestor_Receives_OptionSelector_Value_Via_BubbleAct
     }
 
     #endregion
+
+    #region OptionSelector<TEnum> Tests
+
+    private enum TestEnumZero
+    {
+        Alpha,
+        Beta,
+        Gamma
+    }
+
+    private enum TestEnumNonZero
+    {
+        Alpha = 1,
+        Beta = 2,
+        Gamma = 3
+    }
+
+    private enum TestEnumSparse
+    {
+        Low = 5,
+        Mid = 10,
+        High = 20
+    }
+
+    [Fact]
+    // Copilot
+    public void Generic_Initialization_ZeroBased_FirstCheckBoxIsChecked ()
+    {
+        OptionSelector<TestEnumZero> selector = new ();
+
+        Assert.Equal (TestEnumZero.Alpha, selector.Value);
+        Assert.Equal (0, ((OptionSelector)selector).Value);
+
+        List<int> expectedValues = [0, 1, 2];
+        Assert.Equal (expectedValues, selector.Values);
+
+        CheckBox first = selector.SubViews.OfType<CheckBox> ().First (cb => selector.GetCheckBoxValue (cb) == 0);
+        Assert.Equal (CheckState.Checked, first.Value);
+    }
+
+    [Fact]
+    // Copilot
+    public void Generic_Initialization_NonZeroBased_FirstCheckBoxIsChecked ()
+    {
+        OptionSelector<TestEnumNonZero> selector = new ();
+
+        Assert.Equal (TestEnumNonZero.Alpha, selector.Value);
+        Assert.Equal (1, ((OptionSelector)selector).Value);
+
+        List<int> expectedValues = [1, 2, 3];
+        Assert.Equal (expectedValues, selector.Values);
+
+        CheckBox first = selector.SubViews.OfType<CheckBox> ().First (cb => selector.GetCheckBoxValue (cb) == 1);
+        Assert.Equal (CheckState.Checked, first.Value);
+
+        // Other checkboxes should be unchecked
+        foreach (CheckBox cb in selector.SubViews.OfType<CheckBox> ().Where (cb => selector.GetCheckBoxValue (cb) != 1))
+        {
+            Assert.Equal (CheckState.UnChecked, cb.Value);
+        }
+    }
+
+    [Fact]
+    // Copilot
+    public void Generic_Initialization_SparseEnum_FirstCheckBoxIsChecked ()
+    {
+        OptionSelector<TestEnumSparse> selector = new ();
+
+        Assert.Equal (TestEnumSparse.Low, selector.Value);
+        Assert.Equal (5, ((OptionSelector)selector).Value);
+
+        List<int> expectedValues = [5, 10, 20];
+        Assert.Equal (expectedValues, selector.Values);
+
+        CheckBox first = selector.SubViews.OfType<CheckBox> ().First (cb => selector.GetCheckBoxValue (cb) == 5);
+        Assert.Equal (CheckState.Checked, first.Value);
+    }
+
+    [Fact]
+    // Copilot
+    public void Generic_Initialization_HasCorrectLabels ()
+    {
+        OptionSelector<TestEnumZero> selector = new ();
+
+        List<string> expectedLabels = ["Alpha", "Beta", "Gamma"];
+        Assert.Equal (expectedLabels, selector.Labels);
+        Assert.Equal (3, selector.SubViews.OfType<CheckBox> ().Count ());
+    }
+
+    [Fact]
+    // Copilot
+    public void Generic_Values_Set_Throws ()
+    {
+        OptionSelector<TestEnumZero> selector = new ();
+
+        Assert.Throws<InvalidOperationException> (() => selector.Values = [1, 2]);
+    }
+
+    #endregion
 }