#nullable enable namespace Terminal.Gui; /// Shows a check box that can be toggled. public class CheckBox : View { private bool _allowNone; private CheckState _checked = CheckState.UnChecked; /// /// Initializes a new instance of . /// public CheckBox () { 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 . /// public bool AllowCheckStateNone { get => _allowNone; set { if (_allowNone == value) { return; } _allowNone = value; if (State == CheckState.None) { State = CheckState.UnChecked; } } } /// /// The state of the . /// /// /// /// If is and , the /// will display the ConfigurationManager.Glyphs.CheckStateNone character (☒). /// /// /// If , the /// will display the ConfigurationManager.Glyphs.CheckStateUnChecked character (☐). /// /// /// If , the /// will display the ConfigurationManager.Glyphs.CheckStateChecked character (☑). /// /// public CheckState State { get => _checked; set { if (_checked == value || (value is CheckState.None && !AllowCheckStateNone)) { return; } _checked = value; UpdateTextFormatterText (); OnResizeNeeded (); } } /// Called when the property changes. Invokes the cancelable event. /// /// /// If the event was canceled. /// /// Toggling cycles through the states , , and . /// public bool? OnToggle () { CheckState oldValue = State; CancelEventArgs e = new (ref _checked, ref oldValue); switch (State) { case CheckState.None: e.NewValue = CheckState.Checked; break; case CheckState.Checked: e.NewValue = CheckState.UnChecked; break; case CheckState.UnChecked: if (AllowCheckStateNone) { e.NewValue = CheckState.None; } else { e.NewValue = CheckState.Checked; } break; } 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; } State = 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 () { base.UpdateTextFormatterText(); switch (TextAlignment) { case Alignment.Start: case Alignment.Center: case Alignment.Fill: TextFormatter.Text = $"{GetCheckedGlyph ()} {Text}"; break; case Alignment.End: TextFormatter.Text = $"{Text} {GetCheckedGlyph ()}"; break; } } private Rune GetCheckedGlyph () { return State switch { CheckState.Checked => Glyphs.CheckStateChecked, CheckState.UnChecked => Glyphs.CheckStateUnChecked, CheckState.None => Glyphs.CheckStateNone, _ => throw new ArgumentOutOfRangeException () }; } }