Skip to content

Instantly share code, notes, and snippets.

@rcdailey
Created March 28, 2026 18:57
Show Gist options
  • Select an option

  • Save rcdailey/5c37bc152bd792e8ee2f2c6149fe2dd4 to your computer and use it in GitHub Desktop.

Select an option

Save rcdailey/5c37bc152bd792e8ee2f2c6149fe2dd4 to your computer and use it in GitHub Desktop.
Workaround for Terminal.Gui OptionSelector Space-cycling and initial visual desync
// Workaround for OptionSelector Space-cycling and initial visual desync.
//
// Problem: OptionSelector always calls Cycle() when Space is pressed on the
// currently selected CheckBox. There's no way to disable this. Additionally,
// OptionSelector defaults Value=0 but doesn't call UpdateChecked(), so child
// CheckBoxes are visually unchecked even though the selector thinks the first
// item is selected.
//
// Solution: Intercept Space at the app keyboard level, suppress it entirely
// for OptionSelectors, and manually set the value or force the visual sync.
// In your app setup:
app.Keyboard.KeyDown += OnKeyDown;
// Key handler (Enter/Esc are wizard-specific; the Space case is the workaround)
private void OnKeyDown(object? sender, Key key)
{
if (sender is not IKeyboard { App.TopRunnableView: WizardMainView wizard })
{
return;
}
// ... other key handling ...
if (key == Key.Space)
{
SelectFocusedOption(wizard);
key.Handled = true;
}
}
// Select the focused option without triggering Cycle().
// When the value is already correct but CheckBoxes are visually out of sync
// (initial load where Value=0 is the default but no CheckBox is checked),
// UpdateChecked() forces the visual state to match.
private void SelectFocusedOption(View root)
{
var selector = FindFocusedOptionSelector(root);
if (selector is not { Focused: CheckBox focused })
{
return;
}
var targetValue = selector.GetCheckBoxValue(focused);
if (selector.Value != targetValue)
{
selector.Value = targetValue;
}
else
{
// Value matches but CheckBox may be unchecked visually
selector.UpdateChecked();
}
}
// Recursive tree walk to find the OptionSelector that currently has focus.
// HasFocus propagates up the hierarchy, so the OptionSelector itself reports
// HasFocus=true when any child CheckBox is focused.
private static OptionSelector? FindFocusedOptionSelector(View root)
{
foreach (var child in root.SubViews)
{
if (child is OptionSelector { HasFocus: true } selector)
{
return selector;
}
if (FindFocusedOptionSelector(child) is { } found)
{
return found;
}
}
return null;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment