#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 ()
};
}
}