diff --git a/Terminal.Gui/Views/Selectors/FlagSelector.cs b/Terminal.Gui/Views/Selectors/FlagSelector.cs index 932e396f8b..5ebbf00b4d 100644 --- a/Terminal.Gui/Views/Selectors/FlagSelector.cs +++ b/Terminal.Gui/Views/Selectors/FlagSelector.cs @@ -7,7 +7,7 @@ namespace Terminal.Gui.Views; // Item HotKey - Focus item. Activate (Toggle) item. Do NOT Accept. // Focused: // Space key - Activate (Toggle) focused item. Do NOT Accept. -// Enter key - Activate (Toggle) and Accept the focused item. +// Enter key - Accept. Do NOT Toggle. // HotKey - No-op. // Item HotKey - Focus item, Activate (Toggle), and do NOT Accept. @@ -60,6 +60,11 @@ public FlagSelector () /// protected override bool ConsumeDispatch => true; + /// + /// FlagSelector does not toggle on Enter — Enter only accepts. + /// + protected override bool ActivateOnAccept => false; + // Set by OnHandlingHotKey to suppress the Activate that DefaultHotKeyHandler // fires after RaiseHandlingHotKey. Checked and cleared in GetDispatchTarget. private bool _suppressHotKeyActivate; diff --git a/Terminal.Gui/Views/Selectors/SelectorBase.cs b/Terminal.Gui/Views/Selectors/SelectorBase.cs index 1fc8423991..3bdefd2dca 100644 --- a/Terminal.Gui/Views/Selectors/SelectorBase.cs +++ b/Terminal.Gui/Views/Selectors/SelectorBase.cs @@ -159,6 +159,13 @@ public SelectorStyles Styles } } + /// + /// Gets whether the Accept command (Enter key) should also invoke Activate (toggle/select) before accepting. + /// The default is (OptionSelector behavior). Override to return + /// to accept without activating (FlagSelector behavior). + /// + protected virtual bool ActivateOnAccept => true; + /// protected override bool OnAccepting (CommandEventArgs args) { @@ -167,7 +174,7 @@ protected override bool OnAccepting (CommandEventArgs args) return true; } - // Per spec: Enter key should Activate AND Accept for both OptionSelector and FlagSelector. + // Per spec: Enter key should Activate AND Accept for OptionSelector. // Enter only triggers Command.Accept (View's default key binding), so invoke Activate here // before continuing with Accept processing. Also handle direct programmatic Accept invocations // (Binding is null) by activating the currently focused checkbox. @@ -178,7 +185,7 @@ protected override bool OnAccepting (CommandEventArgs args) bool directAccept = args.Context?.Binding is null && Focused is CheckBox; - if (!enterFromCheckBox && !directAccept) + if ((!enterFromCheckBox && !directAccept) || !ActivateOnAccept) { return args.Context?.Binding switch { diff --git a/Tests/UnitTestsParallelizable/Views/FlagSelectorTests.cs b/Tests/UnitTestsParallelizable/Views/FlagSelectorTests.cs index 4b05d78796..25c2627d4a 100644 --- a/Tests/UnitTestsParallelizable/Views/FlagSelectorTests.cs +++ b/Tests/UnitTestsParallelizable/Views/FlagSelectorTests.cs @@ -151,6 +151,36 @@ public void FlagSelector_Command_HotKey_WhenFocused_Does_Not_Change_Value () Assert.Equal (valueBefore, flagSelector.Value); } + // Copilot - Sonnet 4 + // Per issue: Enter should just accept, not toggle, in FlagSelector + [Fact] + public void FlagSelector_Enter_Does_Not_Toggle_Value () + { + using FlagSelector flagSelector = new (); + + CheckBox firstCheckBox = flagSelector.SubViews.OfType ().ElementAt (0); + flagSelector.SetFocus (); + Assert.True (flagSelector.HasFocus); + + SelectorStyles? valueBefore = flagSelector.Value; + + int selectorValueChanged = 0; + flagSelector.ValueChanged += (_, _) => selectorValueChanged++; + + int acceptingFired = 0; + flagSelector.Accepting += (_, _) => acceptingFired++; + + // Press Enter on the focused checkbox + firstCheckBox.NewKeyDownEvent (Key.Enter); + + // Value should NOT change (Enter does not toggle in FlagSelector) + Assert.Equal (0, selectorValueChanged); + Assert.Equal (valueBefore, flagSelector.Value); + + // But Accepting should fire + Assert.Equal (1, acceptingFired); + } + // Tests for FlagSelector [Fact] public void GenericInitialization_ShouldSetDefaults ()