diff --git a/Terminal.Gui/Views/NumericUpDown.cs b/Terminal.Gui/Views/NumericUpDown.cs new file mode 100644 index 000000000..279cfd6cf --- /dev/null +++ b/Terminal.Gui/Views/NumericUpDown.cs @@ -0,0 +1,251 @@ +#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).Length)), + 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.Accept += OnDownButtonOnAccept; + _up.Accept += OnUpButtonOnAccept; + + Add (_down, _number, _up); + + AddCommand ( + Command.ScrollUp, + () => + { + if (type == typeof (object)) + { + return false; + } + + if (Value is { }) + { + Value = (dynamic)Value + (dynamic)Increment; + } + + return true; + }); + + AddCommand ( + Command.ScrollDown, + () => + { + if (type == typeof (object)) + { + return false; + } + + if (Value 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, HandledEventArgs e) { InvokeCommand (Command.ScrollDown); } + + void OnUpButtonOnAccept (object? s, HandledEventArgs e) { InvokeCommand (Command.ScrollUp); } + } + + 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 ((dynamic)_increment == (dynamic)value) + { + return; + } + + _increment = value; + + IncrementChanged?.Invoke (this, new (value)); + } + } + + /// + /// Raised when has changed. + /// + public event EventHandler>? IncrementChanged; +} + +/// +/// Enables the user to increase or decrease an by clicking on the up or down buttons. +/// +public class NumericUpDown : NumericUpDown +{ } diff --git a/UICatalog/Scenarios/AdornmentEditor.cs b/UICatalog/Scenarios/AdornmentEditor.cs index bae41aceb..e941a8a84 100644 --- a/UICatalog/Scenarios/AdornmentEditor.cs +++ b/UICatalog/Scenarios/AdornmentEditor.cs @@ -78,10 +78,10 @@ public class AdornmentEditor : View AdornmentChanged?.Invoke (this, EventArgs.Empty); } - private Buttons.NumericUpDown _topEdit; - private Buttons.NumericUpDown _leftEdit; - private Buttons.NumericUpDown _bottomEdit; - private Buttons.NumericUpDown _rightEdit; + private NumericUpDown _topEdit; + private NumericUpDown _leftEdit; + private NumericUpDown _bottomEdit; + private NumericUpDown _rightEdit; public AdornmentEditor () { @@ -102,6 +102,7 @@ public class AdornmentEditor : View _topEdit = new () { X = Pos.Center (), Y = 0, + Format = "{0, 2}", Enabled = false }; @@ -110,7 +111,8 @@ public class AdornmentEditor : View _leftEdit = new () { - X = Pos.Left (_topEdit) - Pos.Func (() => _topEdit.Digits) - 2, Y = Pos.Bottom (_topEdit), + X = Pos.Left (_topEdit) - Pos.Func (() => _topEdit.Text.Length) - 2, Y = Pos.Bottom (_topEdit), + Format = _topEdit.Format, Enabled = false }; @@ -120,6 +122,7 @@ public class AdornmentEditor : View _rightEdit = new () { X = Pos.Right (_leftEdit) + 5, Y = Pos.Bottom (_topEdit), + Format = _topEdit.Format, Enabled = false }; @@ -129,6 +132,7 @@ public class AdornmentEditor : View _bottomEdit = new () { X = Pos.Center (), Y = Pos.Bottom (_leftEdit), + Format = _topEdit.Format, Enabled = false }; diff --git a/UICatalog/Scenarios/Buttons.cs b/UICatalog/Scenarios/Buttons.cs index 5685913aa..8edcdf42b 100644 --- a/UICatalog/Scenarios/Buttons.cs +++ b/UICatalog/Scenarios/Buttons.cs @@ -336,8 +336,6 @@ public class Buttons : Scenario Value = 69, X = Pos.Right (label) + 1, Y = Pos.Top (label), - Width = 5, - Height = 1 }; numericUpDown.ValueChanged += NumericUpDown_ValueChanged; @@ -390,164 +388,24 @@ public class Buttons : Scenario enableCB.Toggle += (s, e) => { repeatButton.Enabled = !repeatButton.Enabled; }; main.Add (label, repeatButton, enableCB); - main.Ready += (s, e) => radioGroup.Refresh (); + var decNumericUpDown = new NumericUpDown + { + Value = 911, + Increment = 1, + Format = "{0:X}", + X = 0, + Y = Pos.Bottom (enableCB) + 1, + }; + + main.Add (decNumericUpDown); + + main.Ready += (s, e) => + { + radioGroup.Refresh (); + }; Application.Run (main); main.Dispose (); Application.Shutdown (); } - - /// - /// Enables the user to increase or decrease a value by clicking on the up or down buttons. - /// - /// - /// Supports the following types: , , , , . - /// Supports only one digit of precision. - /// - public class NumericUpDown : View - { - private readonly Button _down; - // TODO: Use a TextField instead of a Label - private readonly View _number; - private readonly Button _up; - - public NumericUpDown () - { - Type type = typeof (T); - if (!(type == typeof (int) || type == typeof (long) || type == typeof (float) || type == typeof (double) || type == typeof (decimal))) - { - throw new InvalidOperationException ("T must be a numeric type that supports addition and subtraction."); - } - - Width = Dim.Auto (DimAutoStyle.Content); //Dim.Function (() => Digits + 2); // button + 3 for number + button - Height = Dim.Auto (DimAutoStyle.Content); - - _down = new () - { - Height = 1, - Width = 1, - NoPadding = true, - NoDecorations = true, - Title = $"{CM.Glyphs.DownArrow}", - WantContinuousButtonPressed = true, - CanFocus = false, - ShadowStyle = ShadowStyle.None, - }; - - _number = new () - { - Text = Value.ToString (), - X = Pos.Right (_down), - Y = Pos.Top (_down), - Width = Dim.Func (() => Digits), - Height = 1, - TextAlignment = Alignment.Center, - CanFocus = true - }; - - _up = new () - { - X = Pos.AnchorEnd (), - Y = Pos.Top (_number), - Height = 1, - Width = 1, - NoPadding = true, - NoDecorations = true, - Title = $"{CM.Glyphs.UpArrow}", - WantContinuousButtonPressed = true, - CanFocus = false, - ShadowStyle = ShadowStyle.None, - }; - - CanFocus = true; - - _down.Accept += OnDownButtonOnAccept; - _up.Accept += OnUpButtonOnAccept; - - Add (_down, _number, _up); - - - AddCommand (Command.ScrollUp, () => - { - Value = (dynamic)Value + 1; - _number.Text = Value.ToString (); - - return true; - }); - AddCommand (Command.ScrollDown, () => - { - Value = (dynamic)Value - 1; - _number.Text = Value.ToString (); - - return true; - }); - - KeyBindings.Add (Key.CursorUp, Command.ScrollUp); - KeyBindings.Add (Key.CursorDown, Command.ScrollDown); - - return; - - void OnDownButtonOnAccept (object s, HandledEventArgs e) - { - InvokeCommand (Command.ScrollDown); - } - - void OnUpButtonOnAccept (object s, HandledEventArgs e) - { - InvokeCommand (Command.ScrollUp); - } - } - - private void _up_Enter (object sender, FocusEventArgs e) - { - throw new NotImplementedException (); - } - - private T _value; - - /// - /// The value that will be incremented or decremented. - /// - public T Value - { - get => _value; - set - { - if (_value.Equals (value)) - { - return; - } - - T oldValue = value; - CancelEventArgs args = new (ref _value, ref value); - ValueChanging?.Invoke (this, args); - - if (args.Cancel) - { - return; - } - - _value = value; - _number.Text = _value.ToString ()!; - ValueChanged?.Invoke (this, new (in _value)); - } - } - - /// - /// Fired when the value is about to change. Set to true to prevent the change. - /// - [CanBeNull] - public event EventHandler> ValueChanging; - - /// - /// Fired when the value has changed. - /// - [CanBeNull] - public event EventHandler> ValueChanged; - - /// - /// The number of digits to display. The will be resized to fit this number of characters plus the buttons. The default is 3. - /// - public int Digits { get; set; } = 3; - } } diff --git a/UICatalog/Scenarios/ContentScrolling.cs b/UICatalog/Scenarios/ContentScrolling.cs index 5d4a1a3c2..9e4416658 100644 --- a/UICatalog/Scenarios/ContentScrolling.cs +++ b/UICatalog/Scenarios/ContentScrolling.cs @@ -229,7 +229,7 @@ public class ContentScrolling : Scenario Y = Pos.Bottom (cbAllowYGreaterThanContentHeight) }; - Buttons.NumericUpDown contentSizeWidth = new Buttons.NumericUpDown + NumericUpDown contentSizeWidth = new NumericUpDown { Value = view.GetContentSize ().Width, X = Pos.Right (labelContentSize) + 1, @@ -256,7 +256,7 @@ public class ContentScrolling : Scenario Y = Pos.Top (labelContentSize) }; - Buttons.NumericUpDown contentSizeHeight = new Buttons.NumericUpDown + NumericUpDown contentSizeHeight = new NumericUpDown { Value = view.GetContentSize ().Height, X = Pos.Right (labelComma) + 1, diff --git a/UICatalog/Scenarios/NumericUpDownDemo.cs b/UICatalog/Scenarios/NumericUpDownDemo.cs new file mode 100644 index 000000000..b514a041a --- /dev/null +++ b/UICatalog/Scenarios/NumericUpDownDemo.cs @@ -0,0 +1,291 @@ +#nullable enable +using System; +using Terminal.Gui; + +namespace UICatalog.Scenarios; + +[ScenarioMetadata ("NumericUpDown", "Demonstrates the NumericUpDown View")] +[ScenarioCategory ("Controls")] +public class NumericUpDownDemo : Scenario +{ + public override void Main () + { + Application.Init (); + + Window app = new () + { + Title = GetQuitKeyAndName (), + TabStop = TabBehavior.TabGroup + }; + + var editor = new AdornmentsEditor + { + X = 0, + Y = 0, + AutoSelectViewToEdit = true, + TabStop = TabBehavior.NoStop + }; + app.Add (editor); + + NumericUpDownEditor intEditor = new () + { + X = Pos.Right (editor), + Y = 0, + Title = "int", + }; + + app.Add (intEditor); + + NumericUpDownEditor floatEditor = new () + { + X = Pos.Right (intEditor), + Y = 0, + Title = "float", + }; + app.Add (floatEditor); + + app.Initialized += AppInitialized; + + void AppInitialized (object? sender, EventArgs e) + { + floatEditor!.NumericUpDown!.Increment = 0.1F; + floatEditor!.NumericUpDown!.Format = "{0:0.0}"; + + } + + Application.Run (app); + app.Dispose (); + Application.Shutdown (); + + } + +} + +internal class NumericUpDownEditor : View where T : notnull +{ + private NumericUpDown? _numericUpDown; + + internal NumericUpDown? NumericUpDown + { + get => _numericUpDown; + set + { + if (value == _numericUpDown) + { + return; + } + _numericUpDown = value; + + if (_numericUpDown is { } && _value is { }) + { + _value.Text = _numericUpDown.Text; + } + } + } + + private TextField? _value; + private TextField? _format; + private TextField? _increment; + + internal NumericUpDownEditor () + { + _numericUpDown = null; + Title = "NumericUpDownEditor"; + BorderStyle = LineStyle.Single; + Width = Dim.Auto (DimAutoStyle.Content); + Height = Dim.Auto (DimAutoStyle.Content); + TabStop = TabBehavior.TabGroup; + + Initialized += NumericUpDownEditorInitialized; + + return; + + void NumericUpDownEditorInitialized (object? sender, EventArgs e) + { + Label label = new () + { + Title = "_Value: ", + Width = 12, + }; + label.TextFormatter.Alignment = Alignment.End; + _value = new () + { + X = Pos.Right (label), + Y = Pos.Top (label), + Width = 8, + Title = "Value", + }; + _value.Accept += ValuedOnAccept; + + void ValuedOnAccept (object? sender, EventArgs e) + { + if (_numericUpDown is null) + { + return; + } + + try + { + if (string.IsNullOrEmpty (_value.Text)) + { + // Handle empty or null text if needed + _numericUpDown.Value = default!; + } + else + { + // Parse _value.Text and then convert to type T + _numericUpDown.Value = (T)Convert.ChangeType (_value.Text, typeof (T)); + } + + _value.ColorScheme = SuperView.ColorScheme; + + } + catch (System.FormatException) + { + _value.ColorScheme = Colors.ColorSchemes ["Error"]; + } + catch (InvalidCastException) + { + _value.ColorScheme = Colors.ColorSchemes ["Error"]; + } + finally + { + } + + } + Add (label, _value); + + label = new () + { + Y = Pos.Bottom (_value), + Width = 12, + Title = "_Format: ", + }; + label.TextFormatter.Alignment = Alignment.End; + + _format = new () + { + X = Pos.Right (label), + Y = Pos.Top (label), + Title = "Format", + Width = Dim.Width (_value), + }; + _format.Accept += FormatOnAccept; + + void FormatOnAccept (object? o, EventArgs eventArgs) + { + if (_numericUpDown is null) + { + return; + } + + try + { + // Test format to ensure it's valid + _ = string.Format (_format.Text, _value); + _numericUpDown.Format = _format.Text; + + _format.ColorScheme = SuperView.ColorScheme; + + } + catch (System.FormatException) + { + _format.ColorScheme = Colors.ColorSchemes ["Error"]; + } + catch (InvalidCastException) + { + _format.ColorScheme = Colors.ColorSchemes ["Error"]; + } + finally + { + } + } + + Add (label, _format); + + label = new () + { + Y = Pos.Bottom (_format), + Width = 12, + Title = "_Increment: ", + }; + label.TextFormatter.Alignment = Alignment.End; + _increment = new () + { + X = Pos.Right (label), + Y = Pos.Top (label), + Title = "Increment", + Width = Dim.Width (_value), + }; + + _increment.Accept += IncrementOnAccept; + + void IncrementOnAccept (object? o, EventArgs eventArgs) + { + if (_numericUpDown is null) + { + return; + } + + try + { + if (string.IsNullOrEmpty (_value.Text)) + { + // Handle empty or null text if needed + _numericUpDown.Increment = default!; + } + else + { + // Parse _value.Text and then convert to type T + _numericUpDown.Increment = (T)Convert.ChangeType (_increment.Text, typeof (T)); + } + + _increment.ColorScheme = SuperView.ColorScheme; + + } + catch (System.FormatException) + { + _increment.ColorScheme = Colors.ColorSchemes ["Error"]; + } + catch (InvalidCastException) + { + _increment.ColorScheme = Colors.ColorSchemes ["Error"]; + } + finally + { + } + } + + Add (label, _increment); + + _numericUpDown = new () + { + X = Pos.Center (), + Y = Pos.Bottom (_increment) + 1, + Increment = (dynamic)1, + }; + + _numericUpDown.ValueChanged += NumericUpDownOnValueChanged; + + void NumericUpDownOnValueChanged (object? o, EventArgs eventArgs) + { + _value.Text = _numericUpDown.Text; + } + + _numericUpDown.IncrementChanged += NumericUpDownOnIncrementChanged; + + void NumericUpDownOnIncrementChanged (object? o, EventArgs eventArgs) + { + _increment.Text = _numericUpDown.Increment.ToString (); + } + + Add (_numericUpDown); + + _value.Text = _numericUpDown.Text; + _format.Text = _numericUpDown.Format; + _increment.Text = _numericUpDown.Increment.ToString (); + } + } + + +} diff --git a/UICatalog/Scenarios/PosAlignDemo.cs b/UICatalog/Scenarios/PosAlignDemo.cs index d7ae5146e..03e90b806 100644 --- a/UICatalog/Scenarios/PosAlignDemo.cs +++ b/UICatalog/Scenarios/PosAlignDemo.cs @@ -236,7 +236,7 @@ public sealed class PosAlignDemo : Scenario } ]; - Buttons.NumericUpDown addedViewsUpDown = new() + NumericUpDown addedViewsUpDown = new() { Width = 9, Title = "Added", diff --git a/UICatalog/Scenarios/Sliders.cs b/UICatalog/Scenarios/Sliders.cs index 37d97c467..40e98dd33 100644 --- a/UICatalog/Scenarios/Sliders.cs +++ b/UICatalog/Scenarios/Sliders.cs @@ -407,7 +407,7 @@ public class Sliders : Scenario Text = "Min _Inner Spacing:", }; - Buttons.NumericUpDown innerSpacingUpDown = new () + NumericUpDown innerSpacingUpDown = new () { X = Pos.Right (label) + 1 }; diff --git a/UnitTests/Views/NumericUpDownTests.cs b/UnitTests/Views/NumericUpDownTests.cs new file mode 100644 index 000000000..916053001 --- /dev/null +++ b/UnitTests/Views/NumericUpDownTests.cs @@ -0,0 +1,238 @@ +using System.Globalization; +using Xunit.Abstractions; + +namespace Terminal.Gui.ViewsTests; + +public class NumericUpDownTests (ITestOutputHelper _output) +{ + [Fact] + public void WhenCreated_ShouldHaveDefaultValues_int () + { + NumericUpDown numericUpDown = new (); + + Assert.Equal (0, numericUpDown.Value); + Assert.Equal (1, numericUpDown.Increment); + } + + [Fact] + public void WhenCreated_ShouldHaveDefaultValues_long () + { + NumericUpDown numericUpDown = new (); + + Assert.Equal (0, numericUpDown.Value); + Assert.Equal (1, numericUpDown.Increment); + } + + [Fact] + public void WhenCreated_ShouldHaveDefaultValues_float () + { + NumericUpDown numericUpDown = new (); + + Assert.Equal (0F, numericUpDown.Value); + Assert.Equal (1.0F, numericUpDown.Increment); + } + + [Fact] + public void WhenCreated_ShouldHaveDefaultValues_double () + { + NumericUpDown numericUpDown = new (); + + Assert.Equal (0F, numericUpDown.Value); + Assert.Equal (1.0F, numericUpDown.Increment); + } + + [Fact] + public void WhenCreated_ShouldHaveDefaultValues_decimal () + { + NumericUpDown numericUpDown = new (); + + Assert.Equal (0, numericUpDown.Value); + Assert.Equal (1, numericUpDown.Increment); + } + + [Fact] + public void WhenCreatedWithCustomValues_ShouldHaveCustomValues_int () + { + NumericUpDown numericUpDown = new() + { + Value = 10, + Increment = 2 + }; + + Assert.Equal (10, numericUpDown.Value); + Assert.Equal (2, numericUpDown.Increment); + } + + [Fact] + public void WhenCreatedWithCustomValues_ShouldHaveCustomValues_float () + { + NumericUpDown numericUpDown = new() + { + Value = 10.5F, + Increment = 2.5F + }; + + Assert.Equal (10.5F, numericUpDown.Value); + Assert.Equal (2.5F, numericUpDown.Increment); + } + + [Fact] + public void WhenCreatedWithCustomValues_ShouldHaveCustomValues_decimal () + { + NumericUpDown numericUpDown = new () + { + Value = 10.5m, + Increment = 2.5m + }; + + Assert.Equal (10.5m, numericUpDown.Value); + Assert.Equal (2.5m, numericUpDown.Increment); + } + + [Fact] + public void WhenCreatedWithInvalidType_ShouldThrowInvalidOperationException () + { + Assert.Throws (() => new NumericUpDown ()); + } + + [Fact] + public void WhenCreatedWithInvalidTypeObject_ShouldNotThrowInvalidOperationException () + { + NumericUpDown numericUpDown = new (); + } + + [Fact] + public void WhenCreated_ShouldHaveDefaultWidthAndHeight_int () + { + NumericUpDown numericUpDown = new (); + numericUpDown.SetRelativeLayout (Application.Screen.Size); + + Assert.Equal (3, numericUpDown.Frame.Width); + Assert.Equal (1, numericUpDown.Frame.Height); + } + + [Fact] + public void WhenCreated_ShouldHaveDefaultWidthAndHeight_float () + { + NumericUpDown numericUpDown = new (); + numericUpDown.SetRelativeLayout (Application.Screen.Size); + + Assert.Equal (3, numericUpDown.Frame.Width); + Assert.Equal (1, numericUpDown.Frame.Height); + } + + [Fact] + public void WhenCreated_ShouldHaveDefaultWidthAndHeight_double () + { + NumericUpDown numericUpDown = new (); + numericUpDown.SetRelativeLayout (Application.Screen.Size); + + Assert.Equal (3, numericUpDown.Frame.Width); + Assert.Equal (1, numericUpDown.Frame.Height); + } + + [Fact] + public void WhenCreated_ShouldHaveDefaultWidthAndHeight_long () + { + NumericUpDown numericUpDown = new (); + numericUpDown.SetRelativeLayout (Application.Screen.Size); + + Assert.Equal (3, numericUpDown.Frame.Width); + Assert.Equal (1, numericUpDown.Frame.Height); + } + + [Fact] + public void WhenCreated_ShouldHaveDefaultWidthAndHeight_decimal () + { + NumericUpDown numericUpDown = new (); + numericUpDown.SetRelativeLayout (Application.Screen.Size); + + Assert.Equal (3, numericUpDown.Frame.Width); + Assert.Equal (1, numericUpDown.Frame.Height); + } + + [Fact] + public void WhenCreated_Text_Should_Be_Correct_int () + { + NumericUpDown numericUpDown = new (); + + Assert.Equal ("0", numericUpDown.Text); + } + + [Fact] + public void WhenCreated_Text_Should_Be_Correct_float () + { + NumericUpDown numericUpDown = new (); + + Assert.Equal ("0", numericUpDown.Text); + } + + [Fact] + public void Format_Default () + { + NumericUpDown numericUpDown = new (); + + Assert.Equal ("{0}", numericUpDown.Format); + } + + [Theory] + [InlineData (0F, "{0}", "0")] + [InlineData (1.1F, "{0}", "1.1")] + [InlineData (0F, "{0:0%}", "0%")] + [InlineData (.75F, "{0:0%}", "75%")] + public void Format_decimal (float value, string format, string expectedText) + { + CultureInfo currentCulture = CultureInfo.CurrentCulture; + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + + NumericUpDown numericUpDown = new (); + + numericUpDown.Format = format; + numericUpDown.Value = value; + + Assert.Equal (expectedText, numericUpDown.Text); + + CultureInfo.CurrentCulture = currentCulture; + } + + [Theory] + [InlineData (0, "{0}", "0")] + [InlineData (11, "{0}", "11")] + [InlineData (-1, "{0}", "-1")] + [InlineData (911, "{0:X}", "38F")] + [InlineData (911, "0x{0:X04}", "0x038F")] + public void Format_int (int value, string format, string expectedText) + { + CultureInfo currentCulture = CultureInfo.CurrentCulture; + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + + NumericUpDown numericUpDown = new (); + + numericUpDown.Format = format; + numericUpDown.Value = value; + + Assert.Equal (expectedText, numericUpDown.Text); + + CultureInfo.CurrentCulture = currentCulture; + } + + [Fact] + public void KeDown_CursorUp_Increments () + { + NumericUpDown numericUpDown = new (); + + numericUpDown.NewKeyDownEvent (Key.CursorUp); + + Assert.Equal (1, numericUpDown.Value); + } + + [Fact] + public void KeyDown_CursorDown_Decrements () + { + NumericUpDown numericUpDown = new (); + + numericUpDown.NewKeyDownEvent (Key.CursorDown); + + Assert.Equal (-1, numericUpDown.Value); + } +}