#nullable enable
using System.ComponentModel;
namespace Terminal.Gui;
///
/// Enables the user to increase or decrease a value with the mouse or keyboard.
///
///
/// Supports the following types: , , , ,
/// . Attempting to use any other type will result in an .
///
public class NumericUpDown : View where T : notnull
{
private readonly Button _down;
// TODO: Use a TextField instead of a Label
private readonly View _number;
private readonly Button _up;
///
/// Initializes a new instance of the class.
///
///
public NumericUpDown ()
{
Type type = typeof (T);
if (!(type == typeof (object)
|| type == typeof (int)
|| type == typeof (long)
|| type == typeof (double)
|| type == typeof (float)
|| type == typeof (double)
|| type == typeof (decimal)))
{
throw new InvalidOperationException ("T must be a numeric type that supports addition and subtraction.");
}
// `object` is supported only for AllViewsTester
if (type != typeof (object))
{
Increment = (dynamic)1;
Value = (dynamic)0;
}
Width = Dim.Auto (DimAutoStyle.Content);
Height = Dim.Auto (DimAutoStyle.Content);
_down = new ()
{
Height = 1,
Width = 1,
NoPadding = true,
NoDecorations = true,
Title = $"{Glyphs.DownArrow}",
WantContinuousButtonPressed = true,
CanFocus = false,
ShadowStyle = ShadowStyle.None,
};
_number = new ()
{
Text = Value?.ToString () ?? "Err",
X = Pos.Right (_down),
Y = Pos.Top (_down),
Width = Dim.Auto (minimumContentDim: Dim.Func (() => string.Format (Format, Value).GetColumns())),
Height = 1,
TextAlignment = Alignment.Center,
CanFocus = true,
};
_up = new ()
{
X = Pos.Right (_number),
Y = Pos.Top (_number),
Height = 1,
Width = 1,
NoPadding = true,
NoDecorations = true,
Title = $"{Glyphs.UpArrow}",
WantContinuousButtonPressed = true,
CanFocus = false,
ShadowStyle = ShadowStyle.None,
};
CanFocus = true;
_down.Accepting += OnDownButtonOnAccept;
_up.Accepting += OnUpButtonOnAccept;
Add (_down, _number, _up);
AddCommand (
Command.ScrollUp,
(ctx) =>
{
if (type == typeof (object))
{
return false;
}
if (RaiseSelecting (ctx) is true)
{
return true;
}
if (Value is { } && Increment is { })
{
Value = (dynamic)Value + (dynamic)Increment;
}
return true;
});
AddCommand (
Command.ScrollDown,
(ctx) =>
{
if (type == typeof (object))
{
return false;
}
if (RaiseSelecting (ctx) is true)
{
return true;
}
if (Value is { } && Increment is { })
{
Value = (dynamic)Value - (dynamic)Increment;
}
return true;
});
KeyBindings.Add (Key.CursorUp, Command.ScrollUp);
KeyBindings.Add (Key.CursorDown, Command.ScrollDown);
SetText ();
return;
void OnDownButtonOnAccept (object? s, CommandEventArgs e)
{
InvokeCommand (Command.ScrollDown);
e.Cancel = true;
}
void OnUpButtonOnAccept (object? s, CommandEventArgs e)
{
InvokeCommand (Command.ScrollUp);
e.Cancel = true;
}
}
private T _value = default!;
///
/// Gets or sets the value that will be incremented or decremented.
///
///
///
/// and events are raised when the value changes.
/// The event can be canceled the change setting
/// .Cancel to .
///
///
public T Value
{
get => _value;
set
{
if (_value.Equals (value))
{
return;
}
T oldValue = value;
CancelEventArgs args = new (in _value, ref value);
ValueChanging?.Invoke (this, args);
if (args.Cancel)
{
return;
}
_value = value;
SetText ();
ValueChanged?.Invoke (this, new (in value));
}
}
///
/// Raised when the value is about to change. Set .Cancel to true to prevent the
/// change.
///
public event EventHandler>? ValueChanging;
///
/// Raised when the value has changed.
///
public event EventHandler>? ValueChanged;
private string _format = "{0}";
///
/// Gets or sets the format string used to display the value. The default is "{0}".
///
public string Format
{
get => _format;
set
{
if (_format == value)
{
return;
}
_format = value;
FormatChanged?.Invoke (this, new (value));
SetText ();
}
}
///
/// Raised when has changed.
///
public event EventHandler>? FormatChanged;
private void SetText ()
{
_number.Text = string.Format (Format, _value);
Text = _number.Text;
}
private T? _increment;
///
///
public T? Increment
{
get => _increment;
set
{
if (_increment is { } && value is { } && (dynamic)_increment == (dynamic)value)
{
return;
}
_increment = value;
IncrementChanged?.Invoke (this, new (value!));
}
}
///
/// Raised when has changed.
///
public event EventHandler>? IncrementChanged;
// Prevent the drawing of Text
///
protected override bool OnDrawingText (Rectangle viewport) { return true; }
}
///
/// Enables the user to increase or decrease an by clicking on the up or down buttons.
///
public class NumericUpDown : NumericUpDown
{ }