#nullable enable namespace Terminal.Gui; /// The shows an on/off toggle that the user can set public class CheckBox : View { private readonly Rune _charChecked; private readonly Rune _charNullChecked; private readonly Rune _charUnChecked; private bool _allowNullChecked; private bool? _checked = false; /// /// Initializes a new instance of . /// public CheckBox () { _charNullChecked = Glyphs.NullChecked; _charChecked = Glyphs.Checked; _charUnChecked = Glyphs.UnChecked; Width = Dim.Auto (DimAutoStyle.Text); Height = Dim.Auto (DimAutoStyle.Text, minimumContentDim: 1); CanFocus = true; // Things this view knows how to do AddCommand (Command.Accept, OnToggle); AddCommand (Command.HotKey, OnToggle); // Default keybindings for this view KeyBindings.Add (Key.Space, Command.Accept); TitleChanged += Checkbox_TitleChanged; HighlightStyle = Gui.HighlightStyle.PressedOutside | Gui.HighlightStyle.Pressed; MouseClick += CheckBox_MouseClick; } private void CheckBox_MouseClick (object? sender, MouseEventEventArgs e) { e.Handled = OnToggle () == true; } private void Checkbox_TitleChanged (object? sender, EventArgs e) { base.Text = e.CurrentValue; TextFormatter.HotKeySpecifier = HotKeySpecifier; } /// public override string Text { get => base.Title; set => base.Text = base.Title = value; } /// public override Rune HotKeySpecifier { get => base.HotKeySpecifier; set => TextFormatter.HotKeySpecifier = base.HotKeySpecifier = value; } /// /// If allows to be null, true, or false. If /// only allows to be true or false. /// public bool AllowNullChecked { get => _allowNullChecked; set { _allowNullChecked = value; Checked ??= false; } } /// /// The state of the . /// /// /// /// If and is , the /// will display the ConfigurationManager.Glyphs.NullChecked character (☒). /// /// /// If , the /// will display the ConfigurationManager.Glyphs.UnChecked character (☐). /// /// /// If , the /// will display the ConfigurationManager.Glyphs.Checked character (☑). /// /// public bool? Checked { get => _checked; set { if (value is null && !AllowNullChecked) { return; } _checked = value; UpdateTextFormatterText (); OnResizeNeeded (); } } /// Called when the property changes. Invokes the cancelable event. /// /// /// If the event was canceled. public bool? OnToggle () { bool ? oldValue = Checked; CancelEventArgs e = new (ref _checked, ref oldValue); if (AllowNullChecked) { switch (Checked) { case null: e.NewValue = true; break; case true: e.NewValue = false; break; case false: e.NewValue = null; break; } } else { e.NewValue = !Checked; } Toggle?.Invoke (this, e); if (e.Cancel) { return e.Cancel; } // By default, Command.Accept calls OnAccept, so we need to call it here to ensure that the event is fired. if (OnAccept () == true) { return true; } Checked = e.NewValue; return true; } /// Toggle event, raised when the is toggled. /// /// /// This event can be cancelled. If cancelled, the will not change its state. /// /// public event EventHandler>? Toggle; /// protected override void UpdateTextFormatterText () { switch (TextAlignment) { case Alignment.Start: case Alignment.Center: case Alignment.Fill: TextFormatter.Text = $"{GetCheckedState ()} {Text}"; break; case Alignment.End: TextFormatter.Text = $"{Text} {GetCheckedState ()}"; break; } } private Rune GetCheckedState () { return Checked switch { true => _charChecked, false => _charUnChecked, var _ => _charNullChecked }; } }