I'm building a wizard-style TUI where OptionSelector is used as a radio group. The keyboard model is pretty standard: arrow keys navigate between options, Space selects the focused option, Enter advances to the next screen. No cycling.
Getting this behavior required ~45 lines of workaround code, including a recursive view tree walk and app-level key interception. I think there are two underlying issues that make this harder than it should be.
1. Space unconditionally triggers Cycle() on the selected option
In ApplyActivation, when Space is pressed on a CheckBox whose value matches selector.Value, Cycle() is always called (line 96-103 in OptionSelector.cs). There's no property or style flag to disable this. The only workaround is intercepting Space at app.Keyboard.KeyDown, suppressing it entirely, and manually setting selector.Value.
What I'd expect: a SelectorStyles flag (something like NoCycleOnSpace) that makes Space behave like a click on the focused CheckBox without advancing to the next option.
2. Value defaults to 0 but CheckBoxes aren't visually synced
A freshly created OptionSelector<TEnum> has Value = 0 (first enum member), but none of the child CheckBoxes are in the Checked state. UpdateChecked() is public but isn't called during initialization. The result is that the selector internally thinks the first item is selected while all items appear unchecked to the user.
These interact badly: the user sees nothing selected, presses Space on the first option, and Cycle() fires because GetCheckBoxValue(checkBox) == Value is already true. The first option gets skipped and the second one is selected instead.
Workaround
For anyone hitting this, here's what I ended up doing: intercept Space at the app keyboard level, find the focused OptionSelector via a recursive tree walk, then either set selector.Value = targetValue (if different) or call selector.UpdateChecked() (if same, to force the visual sync). Full workaround code: https://gist.github.com/rcdailey/5c37bc152bd792e8ee2f2c6149fe2dd4
What I'd like to see
If both issues were fixed, consumers who want standard radio-button behavior (Space = select, no cycling) wouldn't need any app-level key interception. The keyboard handler I wrote would go from 45+ lines down to zero for the Space case.
I'm building a wizard-style TUI where OptionSelector is used as a radio group. The keyboard model is pretty standard: arrow keys navigate between options, Space selects the focused option, Enter advances to the next screen. No cycling.
Getting this behavior required ~45 lines of workaround code, including a recursive view tree walk and app-level key interception. I think there are two underlying issues that make this harder than it should be.
1. Space unconditionally triggers
Cycle()on the selected optionIn
ApplyActivation, when Space is pressed on a CheckBox whose value matchesselector.Value,Cycle()is always called (line 96-103 in OptionSelector.cs). There's no property or style flag to disable this. The only workaround is intercepting Space atapp.Keyboard.KeyDown, suppressing it entirely, and manually settingselector.Value.What I'd expect: a
SelectorStylesflag (something likeNoCycleOnSpace) that makes Space behave like a click on the focused CheckBox without advancing to the next option.2. Value defaults to 0 but CheckBoxes aren't visually synced
A freshly created
OptionSelector<TEnum>hasValue = 0(first enum member), but none of the child CheckBoxes are in theCheckedstate.UpdateChecked()is public but isn't called during initialization. The result is that the selector internally thinks the first item is selected while all items appear unchecked to the user.These interact badly: the user sees nothing selected, presses Space on the first option, and
Cycle()fires becauseGetCheckBoxValue(checkBox) == Valueis already true. The first option gets skipped and the second one is selected instead.Workaround
For anyone hitting this, here's what I ended up doing: intercept Space at the app keyboard level, find the focused OptionSelector via a recursive tree walk, then either set
selector.Value = targetValue(if different) or callselector.UpdateChecked()(if same, to force the visual sync). Full workaround code: https://gist.github.com/rcdailey/5c37bc152bd792e8ee2f2c6149fe2dd4What I'd like to see
If both issues were fixed, consumers who want standard radio-button behavior (Space = select, no cycling) wouldn't need any app-level key interception. The keyboard handler I wrote would go from 45+ lines down to zero for the Space case.