From ae3c1a38ed50c99a776c474d10c95544bf467746 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 25 Aug 2024 09:55:58 +0100 Subject: [PATCH 01/10] Add support for NumericUpDown generic view --- src/Design.cs | 8 +++++++- src/TTypes.cs | 12 ++++++++++++ src/ViewFactory.cs | 13 ++++++++----- tests/NumericUpDownTests.cs | 36 ++++++++++++++++++++++++++++++++++++ tests/Tests.cs | 2 +- tests/ViewFactoryTests.cs | 3 --- 6 files changed, 64 insertions(+), 10 deletions(-) create mode 100644 tests/NumericUpDownTests.cs diff --git a/src/Design.cs b/src/Design.cs index 71716320..bba3b11c 100644 --- a/src/Design.cs +++ b/src/Design.cs @@ -50,6 +50,7 @@ public class Design typeof(TabView), typeof(TreeView), typeof(Dialog), + typeof(NumericUpDown) }; /// @@ -850,6 +851,11 @@ private IEnumerable LoadDesignableProperties() { yield return this.CreateProperty(nameof(RadioGroup.RadioLabels)); } + + if (viewType.IsGenericType(typeof(NumericUpDown<>))) + { + yield return this.CreateProperty(nameof(NumericUpDown.Value)); + } } private Property CreateTreeObjectsProperty(Type viewType) @@ -879,7 +885,7 @@ private bool ShowTextProperty() } // Do not let Text be set on Slider or Slider<> implementations as weird stuff happens - if(this.View.GetType().Name.StartsWith("Slider") || View is RadioGroup) + if(this.View.GetType().Name.StartsWith("Slider") || View is RadioGroup || View.GetType().IsGenericType(typeof(NumericUpDown<>))) { return false; } diff --git a/src/TTypes.cs b/src/TTypes.cs index 54891b8f..a8846499 100644 --- a/src/TTypes.cs +++ b/src/TTypes.cs @@ -45,6 +45,18 @@ public static IEnumerable GetSupportedTTypesForGenericViewOfType(Type view return new[] { typeof(int), typeof(string), typeof(int), typeof(double), typeof(bool) }; } + if (viewType == typeof(NumericUpDown<>)) + { + return new[] + { + typeof(int), + typeof(long), + typeof(double), + typeof(float), + typeof(decimal) + }; + } + if (viewType == typeof(TreeView<>)) { return new[] { typeof(object), typeof(FileSystemInfo) }; diff --git a/src/ViewFactory.cs b/src/ViewFactory.cs index 3b2c7267..5d786d07 100644 --- a/src/ViewFactory.cs +++ b/src/ViewFactory.cs @@ -40,11 +40,7 @@ public static class ViewFactory // BUG These seem to cause stack overflows in CreateSubControlDesigns (see TestAddView_RoundTrip) typeof( Wizard ), typeof( WizardStep ), - - // TODO: Requires tests and comprehensive testing, also its generic so that's more complicated - typeof(NumericUpDown), - typeof(NumericUpDown<>), - + // Ignore menu bar v2 for now typeof(MenuBarv2), @@ -134,6 +130,8 @@ public static T Create(int? width = null, int? height = null, string? text = T newView = new( ); + + switch ( newView ) { case Button: @@ -150,6 +148,11 @@ public static T Create(int? width = null, int? height = null, string? text = // leave with default dimensions case ColorPicker16: + case NumericUpDown: + case NumericUpDown: + case NumericUpDown: + case NumericUpDown: + case NumericUpDown: break; case TextView: SetDefaultDimensions(newView, width ?? 10, height ?? 4); diff --git a/tests/NumericUpDownTests.cs b/tests/NumericUpDownTests.cs new file mode 100644 index 00000000..1dc96115 --- /dev/null +++ b/tests/NumericUpDownTests.cs @@ -0,0 +1,36 @@ +namespace UnitTests; + +[Category("Core")] +[TestFixture(typeof(int))] +[TestFixture(typeof(long))] +[TestFixture(typeof(double))] +[TestFixture(typeof(float))] +[TestFixture(typeof(decimal))] +public class NumericUpDownTests : Tests + where T : notnull +{ + + [Test] + [Category("Code Generation")] + public void NumericUpDownPreserveValue() + { + T testValue = (T)Convert.ChangeType(1.25, typeof(T)); + using var backIn = RoundTrip>( (d, v) => + { + + // Expected designable properties + Assert.That(d.GetDesignableProperty("Text"), Is.Null); + + // Expected default properties when created by ViewFactory + Assert.That(v.Width?.IsAuto(out _, out _, out _), Is.True); + + // Your test logic here + v.Value = testValue; + + }, out _); + + // Assertions here + Assert.That(backIn.Value,Is.EqualTo(testValue)); + } +} + diff --git a/tests/Tests.cs b/tests/Tests.cs index 9a01b5e9..af59c7ef 100644 --- a/tests/Tests.cs +++ b/tests/Tests.cs @@ -5,7 +5,7 @@ namespace UnitTests; [RequiresThread] -internal class Tests +public class Tests { [ThreadStatic] private static bool? _init; diff --git a/tests/ViewFactoryTests.cs b/tests/ViewFactoryTests.cs index ab836ab7..ba0f48ec 100644 --- a/tests/ViewFactoryTests.cs +++ b/tests/ViewFactoryTests.cs @@ -188,9 +188,6 @@ private static Type[] KnownUnsupportedTypes_ExpectedTypes( ) typeof( Wizard ), typeof( WizardStep ), - - typeof(NumericUpDown), - typeof(NumericUpDown<>), typeof( MenuBarv2 ), typeof( Shortcut ) }; From ea935525454ffd303e6d2c9a465671fb6930e3a8 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 25 Aug 2024 10:03:09 +0100 Subject: [PATCH 02/10] Fix tests --- src/ViewFactory.cs | 3 ++- tests/ColorPickerTests.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ViewFactory.cs b/src/ViewFactory.cs index 5d786d07..8b8b3154 100644 --- a/src/ViewFactory.cs +++ b/src/ViewFactory.cs @@ -143,7 +143,8 @@ public static T Create(int? width = null, int? height = null, string? text = newView.Width = Dim.Auto(); break; case ColorPicker: - SetDefaultDimensions(newView, width ?? 20, height ?? 4); + newView.Width = width ?? 20; + newView.Height = Dim.Auto(); break; // leave with default dimensions diff --git a/tests/ColorPickerTests.cs b/tests/ColorPickerTests.cs index bc23f969..1ef49a4d 100644 --- a/tests/ColorPickerTests.cs +++ b/tests/ColorPickerTests.cs @@ -26,7 +26,7 @@ public void ColorPickerCanSerializeStyle() { Assume.That(v.Style.ColorModel,Is.Not.EqualTo(ColorModel.RGB)); - var prop = d.GetDesignableProperty(nameof(ColorPicker.Style) + "." + nameof(ColorPickerStyle.ColorModel)) + var prop = d.GetDesignableProperty(nameof(ColorPickerStyle.ColorModel)) ?? throw new("Property was unexpectedly not designable"); // We change to RGB From 8ec52d5424517d66b414ca8ce733873ebe335bf3 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 25 Aug 2024 10:40:49 +0100 Subject: [PATCH 03/10] Add Increment as designable --- src/Design.cs | 4 ++++ tests/NumericUpDownTests.cs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/Design.cs b/src/Design.cs index bba3b11c..17330527 100644 --- a/src/Design.cs +++ b/src/Design.cs @@ -855,6 +855,10 @@ private IEnumerable LoadDesignableProperties() if (viewType.IsGenericType(typeof(NumericUpDown<>))) { yield return this.CreateProperty(nameof(NumericUpDown.Value)); + yield return this.CreateProperty(nameof(NumericUpDown.Increment)); + + // TODO: Probably needs some thought + // yield return this.CreateProperty(nameof(NumericUpDown.Format)); } } diff --git a/tests/NumericUpDownTests.cs b/tests/NumericUpDownTests.cs index 1dc96115..3a621516 100644 --- a/tests/NumericUpDownTests.cs +++ b/tests/NumericUpDownTests.cs @@ -20,17 +20,21 @@ public void NumericUpDownPreserveValue() // Expected designable properties Assert.That(d.GetDesignableProperty("Text"), Is.Null); + Assert.That(v.Text, Is.EqualTo("0")); + Assert.That(v.GetActualText(),Is.EqualTo("0")); // Expected default properties when created by ViewFactory Assert.That(v.Width?.IsAuto(out _, out _, out _), Is.True); // Your test logic here v.Value = testValue; + v.Increment = testValue; }, out _); // Assertions here Assert.That(backIn.Value,Is.EqualTo(testValue)); + Assert.That(backIn.Increment, Is.EqualTo(testValue)); } } From 446c48fe3c01660a73e7803607ed09f4efdd28a6 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 25 Aug 2024 11:19:48 +0100 Subject: [PATCH 04/10] Dangerous changes to prevent clicks in non designable subcomponents (requires thorough testing. --- src/Design.cs | 24 +++++++++++++++++++----- src/ViewExtensions.cs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/Design.cs b/src/Design.cs index 17330527..ce08ea95 100644 --- a/src/Design.cs +++ b/src/Design.cs @@ -214,14 +214,14 @@ public Design CreateSubControlDesign(string name, View subView) if (subView is TextView txt) { // prevent control from responding to events - txt.MouseClick += this.SuppressNativeClickEvents; + txt.MouseClick += (s, e) => this.SuppressNativeClickEvents(s, e); txt.KeyDown += this.SuppressNativeKeyboardEvents; } if (subView is TextField tf) { // prevent control from responding to events - tf.MouseClick += this.SuppressNativeClickEvents; + tf.MouseClick += (s,e)=>this.SuppressNativeClickEvents(s,e); tf.KeyDown += SuppressNativeKeyboardEvents; } @@ -245,6 +245,13 @@ public Design CreateSubControlDesign(string name, View subView) tree.AddObject(new TreeNode($"Example Leaf {l}")); } } + + // Disable click events for sub controls. This allows you to prevent clicking + // in non designed subcomponents e.g. the bar of a true color picker. + foreach (var v in subView.GetAllNonDesignableSubviews()) + { + v.MouseClick += (s,e)=>this.SuppressNativeClickEvents(s,e,true); + } var d = new Design(this.SourceCode, name, subView); return d; @@ -611,10 +618,17 @@ private void CreateSubControlDesigns(View view) } } - private void SuppressNativeClickEvents(object? sender, MouseEventEventArgs obj) + private void SuppressNativeClickEvents(object? sender, MouseEventEventArgs obj, bool alsoSuppressClick = false) { - // Suppress everything except single click (selection) - obj.Handled = obj.MouseEvent.Flags != MouseFlags.Button1Clicked; + if (alsoSuppressClick) + { + obj.Handled = true; + } + else + { + // Suppress everything except single click (selection) + obj.Handled = obj.MouseEvent.Flags != MouseFlags.Button1Clicked; + } } private void SuppressNativeKeyboardEvents(object? sender, Key e) diff --git a/src/ViewExtensions.cs b/src/ViewExtensions.cs index 7253b95e..fc9de70b 100644 --- a/src/ViewExtensions.cs +++ b/src/ViewExtensions.cs @@ -252,6 +252,8 @@ public static bool IsBorderlessContainerView(this View v) /// The at the given screen location or null if none found. public static View? HitTest(this View w, MouseEvent m, out bool isBorder, out bool isLowerRight, params View[] ignoring) { + ignoring = ignoring.Union(w.GetAllNonDesignableSubviews()).ToArray(); + // hide the views while we perform the hit test foreach (View v in ignoring) { @@ -301,6 +303,7 @@ public static bool IsBorderlessContainerView(this View v) return hit is Adornment a ? a.Parent : hit; } + /// /// /// Sometimes returns what the library considers @@ -394,6 +397,32 @@ public static IEnumerable OrderViewsByScreenPosition(IEnumerable vie .ThenBy(v => v.Frame.X); } + /// + /// Returns all subviews that are not Designable. Beware that designs can be nested + /// e.g. calling this on a root view with return subviews that belong to other designed + /// components that were added to the root designed window. + /// + /// + /// + public static IEnumerable GetAllNonDesignableSubviews(this View view) + { + var alsoIgnore = new List(); + RecursivelyIgnoreAllNonDesignableSubviews(view, alsoIgnore); + return alsoIgnore; + } + private static void RecursivelyIgnoreAllNonDesignableSubviews(View view, List alsoIgnore) + { + foreach (var sub in view.Subviews) + { + if (sub.Data is not Design) + { + alsoIgnore.Add(sub); + } + + RecursivelyIgnoreAllNonDesignableSubviews(sub, alsoIgnore); + } + } + private static bool HasNoBorderProperty(this View v) { if (v.Border == null || v.BorderStyle == LineStyle.None) @@ -403,4 +432,6 @@ private static bool HasNoBorderProperty(this View v) return false; } + + } From 928fc59161c293ec6c3532491331b020a49ffa1b Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 25 Aug 2024 11:33:30 +0100 Subject: [PATCH 05/10] Suppress more mouse events --- src/Design.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Design.cs b/src/Design.cs index ce08ea95..88b56ba6 100644 --- a/src/Design.cs +++ b/src/Design.cs @@ -251,6 +251,7 @@ public Design CreateSubControlDesign(string name, View subView) foreach (var v in subView.GetAllNonDesignableSubviews()) { v.MouseClick += (s,e)=>this.SuppressNativeClickEvents(s,e,true); + v.MouseEvent += (s, e) => this.SuppressNativeClickEvents(s, e, true); } var d = new Design(this.SourceCode, name, subView); From 2cb286acbf35253b28fa042c73c0c95f317c30e9 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 25 Aug 2024 15:38:20 +0100 Subject: [PATCH 06/10] Start simplifying ValueFactory --- src/UI/ValueFactory.cs | 150 ++++++--------------------- src/UI/Windows/ColorPicker.cs | 4 +- src/UI/Windows/ColorSchemeEditor.cs | 4 +- src/UI/Windows/DimEditor.cs | 7 +- src/UI/Windows/IValueGetterDialog.cs | 8 ++ src/UI/Windows/PointEditor.cs | 7 +- src/UI/Windows/PosEditor.cs | 7 +- src/UI/Windows/SizeEditor.cs | 4 +- src/UI/Windows/SliderOptionEditor.cs | 5 +- 9 files changed, 65 insertions(+), 131 deletions(-) create mode 100644 src/UI/Windows/IValueGetterDialog.cs diff --git a/src/UI/ValueFactory.cs b/src/UI/ValueFactory.cs index eda75e2d..838b18fb 100644 --- a/src/UI/ValueFactory.cs +++ b/src/UI/ValueFactory.cs @@ -17,141 +17,39 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(SliderOption<>)) { - var designer = new SliderOptionEditor(type.GetGenericArguments()[0], oldValue); - Application.Run(designer); - - if (!designer.Cancelled) - { - newValue = designer.Result; - return true; - } - else - { - // user canceled designing the Option - return false; - } + return RunEditor(new SliderOptionEditor(type.GetGenericArguments()[0], oldValue), out newValue); } - else - if (type== typeof(ColorScheme)) + else if (type == typeof(ColorScheme)) { return GetNewColorSchemeValue(design, out newValue); } - else - if (type == typeof(Attribute) || - type == typeof(Attribute?)) + else if (type == typeof(Attribute) || type == typeof(Attribute?)) { - // if its an Attribute or nullableAttribute - var picker = new ColorPicker((Attribute?)oldValue); - Application.Run(picker); - - if (!picker.Cancelled) - { - newValue = picker.Result; - return true; - } - else - { - // user cancelled designing the Color - newValue = null; - return false; - } + return RunEditor(new ColorPicker((Attribute?)oldValue), out newValue); } - else - if (type== typeof(ITextValidateProvider)) + else if (type == typeof(Pos)) { - string? oldPattern = oldValue is TextRegexProvider r ? (string?)r.Pattern.ToPrimitive() : null; - if (Modals.GetString("New Validation Pattern", "Regex Pattern", oldPattern, out var newPattern)) - { - newValue = string.IsNullOrWhiteSpace(newPattern) ? null : new TextRegexProvider(newPattern); - return true; - } - - // user cancelled entering a pattern - newValue = null; - return false; + return RunEditor(new PosEditor(design, (Pos)oldValue ?? throw new Exception("Pos property was unexpectedly null")), out newValue); } - else - if (type== typeof(Pos)) + else if (type == typeof(Size)) { - // user is editing a Pos - var designer = new PosEditor(design, (Pos)oldValue?? throw new Exception("Pos property was unexpectedly null")); - - Application.Run(designer); - - if (!designer.Cancelled) - { - newValue = designer.Result; - return true; - } - else - { - // user cancelled designing the Pos - newValue = null; - return false; - } + return RunEditor(new SizeEditor((Size)(oldValue ?? throw new Exception($"Property {propertyName} is of Type Size but its current value is null"))), out newValue); } - else - if (type== typeof(Size)) + else if (type == typeof(PointF)) { - // user is editing a Size - var oldSize = (Size)(oldValue ?? throw new Exception($"Property {propertyName} is of Type Size but it's current value is null")); - var designer = new SizeEditor(oldSize); - - Application.Run(designer); - - if (!designer.Cancelled) - { - newValue = designer.Result; - return true; - } - else - { - // user cancelled designing the Pos - newValue = null; - return false; - } + var oldPointF = (PointF)(oldValue ?? throw new Exception($"Property {propertyName} is of Type PointF but its current value is null")); + return RunEditor(new PointEditor(oldPointF.X, oldPointF.Y), out newValue); } - else - if (type== typeof(PointF)) + else if (type == typeof(Dim)) { - // user is editing a PointF - var oldPointF = (PointF)(oldValue ?? throw new Exception($"Property {propertyName} is of Type PointF but it's current value is null")); - var designer = new PointEditor(oldPointF.X, oldPointF.Y); - - Application.Run(designer); - - if (!designer.Cancelled) - { - newValue = new PointF(designer.ResultX, designer.ResultY); - return true; - } - else - { - // user cancelled designing the Pos - newValue = null; - return false; - } + return RunEditor(new DimEditor(design, (Dim)oldValue), out newValue); } - else - if (type== typeof(Dim)) + else if (type == typeof(bool)) { - // user is editing a Dim - var designer = new DimEditor(design, (Dim)oldValue); - Application.Run(designer); - - if (!designer.Cancelled) - { - newValue = designer.Result; - return true; - } - else - { - // user cancelled designing the Dim - newValue = null; - return false; - } + int answer = ChoicesDialog.Query(propertyName, $"New value for {type}", "Yes", "No"); + newValue = answer == 0 ? true : false; + return answer != -1; } - else if (type== typeof(bool)) { int answer = ChoicesDialog.Query(propertyName, $"New value for {type}", "Yes", "No"); @@ -316,6 +214,20 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, newValue = null; return false; } + + private static bool RunEditor(T editor, out object? result) where T : Dialog, IValueGetterDialog + { + Application.Run(editor); + if (editor.Cancelled) + { + result = null; + return false; + } + + result = editor.Result; + return true; + } + internal static bool GetNewValue(Design design, Property property, object? oldValue, out object? newValue) { if (property is InstanceOfProperty inst) diff --git a/src/UI/Windows/ColorPicker.cs b/src/UI/Windows/ColorPicker.cs index 7cc28c5e..0959281d 100644 --- a/src/UI/Windows/ColorPicker.cs +++ b/src/UI/Windows/ColorPicker.cs @@ -15,12 +15,12 @@ namespace TerminalGuiDesigner.UI.Windows; /// /// Prompts user to pick a two to make an . /// -public partial class ColorPicker +public partial class ColorPicker : IValueGetterDialog { /// /// The combination of foreground and background the user chose. /// - public Terminal.Gui.Attribute? Result { get; internal set; } + public object Result { get; internal set; } /// /// True if user closed dialog with cancel. diff --git a/src/UI/Windows/ColorSchemeEditor.cs b/src/UI/Windows/ColorSchemeEditor.cs index 3903fc24..c91f1dc9 100644 --- a/src/UI/Windows/ColorSchemeEditor.cs +++ b/src/UI/Windows/ColorSchemeEditor.cs @@ -115,10 +115,10 @@ private MutableColorScheme Clone(ColorScheme scheme) private Terminal.Gui.Attribute PickNewColorsFor(Terminal.Gui.Attribute current) { - var pick = new ColorPicker(current); + var pick = new Windows.ColorPicker(current); Application.Run(pick); - return pick.Cancelled ? current : pick.Result ?? current; + return pick.Cancelled ? current : (Attribute?)pick.Result ?? current; } private void SetColorPatches() diff --git a/src/UI/Windows/DimEditor.cs b/src/UI/Windows/DimEditor.cs index 1697ed5d..3ac81a44 100644 --- a/src/UI/Windows/DimEditor.cs +++ b/src/UI/Windows/DimEditor.cs @@ -7,6 +7,8 @@ // //------------------------------------------------------------------------------ +using JetBrains.Annotations; + namespace TerminalGuiDesigner.UI.Windows; using Terminal.Gui; @@ -16,7 +18,7 @@ namespace TerminalGuiDesigner.UI.Windows; /// /// Editor for the class. /// -public partial class DimEditor : Dialog +public partial class DimEditor : Dialog, IValueGetterDialog { private Design design; @@ -24,7 +26,8 @@ public partial class DimEditor : Dialog /// The final value user has configured based on /// radio buttons and text box values. /// - public Dim Result { get; private set; } + [CanBeNull] + public object Result { get; private set; } /// /// True if dialog was canceled. diff --git a/src/UI/Windows/IValueGetterDialog.cs b/src/UI/Windows/IValueGetterDialog.cs new file mode 100644 index 00000000..7bca25f4 --- /dev/null +++ b/src/UI/Windows/IValueGetterDialog.cs @@ -0,0 +1,8 @@ +#nullable disable +namespace TerminalGuiDesigner.UI.Windows; + +public interface IValueGetterDialog +{ + public object? Result { get; } + public bool Cancelled { get; } +} \ No newline at end of file diff --git a/src/UI/Windows/PointEditor.cs b/src/UI/Windows/PointEditor.cs index a495bd0c..4daaef18 100644 --- a/src/UI/Windows/PointEditor.cs +++ b/src/UI/Windows/PointEditor.cs @@ -7,6 +7,9 @@ // //------------------------------------------------------------------------------ +using JetBrains.Annotations; +using TerminalGuiDesigner.UI.Windows; + namespace TerminalGuiDesigner; using System; using Terminal.Gui; @@ -14,7 +17,7 @@ namespace TerminalGuiDesigner; /// /// Pop-up editor for creating a . /// -public partial class PointEditor { +public partial class PointEditor : IValueGetterDialog { /// /// Gets a value indicating whether user cancelled the dialog before @@ -32,6 +35,8 @@ public partial class PointEditor { /// public float ResultY {get;private set;} + [CanBeNull] public object Result => new PointF(ResultX, ResultY); + /// /// Creates a new instance of the class. /// diff --git a/src/UI/Windows/PosEditor.cs b/src/UI/Windows/PosEditor.cs index a1ea77ec..7b65d2b9 100644 --- a/src/UI/Windows/PosEditor.cs +++ b/src/UI/Windows/PosEditor.cs @@ -7,6 +7,8 @@ // //------------------------------------------------------------------------------ +using JetBrains.Annotations; + namespace TerminalGuiDesigner.UI.Windows; using Terminal.Gui; @@ -18,7 +20,7 @@ namespace TerminalGuiDesigner.UI.Windows; /// /// Editor for the type. /// -public partial class PosEditor : Dialog { +public partial class PosEditor : Dialog, IValueGetterDialog { private Design design; @@ -26,7 +28,8 @@ public partial class PosEditor : Dialog { /// Users configured (assembled from radio button /// selected and text values entered - offset etc). /// - public Pos Result { get; private set; } + [CanBeNull] + public object Result { get; private set; } /// /// True if user cancelled the dialog instead of hitting Ok. diff --git a/src/UI/Windows/SizeEditor.cs b/src/UI/Windows/SizeEditor.cs index 5b35961c..afe00549 100644 --- a/src/UI/Windows/SizeEditor.cs +++ b/src/UI/Windows/SizeEditor.cs @@ -14,13 +14,13 @@ namespace TerminalGuiDesigner.UI.Windows; /// /// Popup editor for the class. /// -public partial class SizeEditor +public partial class SizeEditor : IValueGetterDialog { /// /// The users edited /// - public Size Result { get; private set; } + public object? Result { get; private set; } /// /// True if user cancelled the dialog instead of hitting "Ok". diff --git a/src/UI/Windows/SliderOptionEditor.cs b/src/UI/Windows/SliderOptionEditor.cs index fcaab054..c30255db 100644 --- a/src/UI/Windows/SliderOptionEditor.cs +++ b/src/UI/Windows/SliderOptionEditor.cs @@ -7,13 +7,16 @@ // You can make changes to this file and they will not be overwritten when saving. // // ----------------------------------------------------------------------------- + + namespace TerminalGuiDesigner.UI.Windows { using System.Reflection; using System.Text; using Terminal.Gui; - public partial class SliderOptionEditor { + public partial class SliderOptionEditor : IValueGetterDialog + { private readonly Type genericTypeArgument; private readonly Type sliderOptionType; From 48233b5f573914375fcb96e1e3ac7014da15aeea Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 25 Aug 2024 19:46:06 +0100 Subject: [PATCH 07/10] Simplify ValueFactory --- src/UI/ValueFactory.cs | 49 ++++++++++++++---------------- src/UI/Windows/ArrayEditor.cs | 56 +++++++++++++++++++---------------- src/UI/Windows/Modals.cs | 21 ------------- 3 files changed, 52 insertions(+), 74 deletions(-) diff --git a/src/UI/ValueFactory.cs b/src/UI/ValueFactory.cs index 838b18fb..8212d342 100644 --- a/src/UI/ValueFactory.cs +++ b/src/UI/ValueFactory.cs @@ -19,45 +19,50 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, { return RunEditor(new SliderOptionEditor(type.GetGenericArguments()[0], oldValue), out newValue); } - else if (type == typeof(ColorScheme)) + if (type == typeof(ColorScheme)) { return GetNewColorSchemeValue(design, out newValue); } - else if (type == typeof(Attribute) || type == typeof(Attribute?)) + if (type == typeof(Attribute) || type == typeof(Attribute?)) { return RunEditor(new ColorPicker((Attribute?)oldValue), out newValue); } - else if (type == typeof(Pos)) + if (type == typeof(Pos)) { return RunEditor(new PosEditor(design, (Pos)oldValue ?? throw new Exception("Pos property was unexpectedly null")), out newValue); } - else if (type == typeof(Size)) + if (type == typeof(Size)) { return RunEditor(new SizeEditor((Size)(oldValue ?? throw new Exception($"Property {propertyName} is of Type Size but its current value is null"))), out newValue); } - else if (type == typeof(PointF)) + if (type == typeof(Point)) + { + var oldPoint = (Point)(oldValue ?? throw new Exception($"Property {propertyName} is of Type Point but its current value is null")); + var result = RunEditor(new PointEditor(oldPoint.X, oldPoint.Y), out newValue); + + if (newValue != null) + { + newValue = new Point((int)((PointF)newValue).X, (int)((PointF)newValue).Y); + } + return result; + + } + if (type == typeof(PointF)) { var oldPointF = (PointF)(oldValue ?? throw new Exception($"Property {propertyName} is of Type PointF but its current value is null")); return RunEditor(new PointEditor(oldPointF.X, oldPointF.Y), out newValue); } - else if (type == typeof(Dim)) + if (type == typeof(Dim)) { return RunEditor(new DimEditor(design, (Dim)oldValue), out newValue); } - else if (type == typeof(bool)) + if (type == typeof(bool)) { int answer = ChoicesDialog.Query(propertyName, $"New value for {type}", "Yes", "No"); newValue = answer == 0 ? true : false; return answer != -1; } - if (type== typeof(bool)) - { - int answer = ChoicesDialog.Query(propertyName, $"New value for {type}", "Yes", "No"); - newValue = answer == 0 ? true : false; - return answer != -1; - } - else if ( type.IsGenericType(typeof(IEnumerable<>)) || type.IsAssignableTo(typeof(IList)) @@ -86,7 +91,7 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, if (!designer.Cancelled) { - newValue = designer.Result; + newValue = designer.ResultAsList; return true; } else @@ -96,7 +101,6 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, return false; } } - } else if (type== typeof(IListDataSource)) @@ -130,7 +134,8 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, } } else - if (type== typeof(int) + if ( + type== typeof(int) || type== typeof(int?) || type== typeof(uint) || type== typeof(uint?)) @@ -146,16 +151,6 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, } } else - if (type== typeof(int) - || type== typeof(int?)) - { - if (Modals.Getint(propertyName, "New int Value", (int?)oldValue, out var resultInt)) - { - newValue = resultInt; - return true; - } - } - else if (type== typeof(char?) || type== typeof(char)) { diff --git a/src/UI/Windows/ArrayEditor.cs b/src/UI/Windows/ArrayEditor.cs index 5d493ef7..15f820ba 100644 --- a/src/UI/Windows/ArrayEditor.cs +++ b/src/UI/Windows/ArrayEditor.cs @@ -9,13 +9,14 @@ // ----------------------------------------------------------------------------- using System.Collections.ObjectModel; +using JetBrains.Annotations; namespace TerminalGuiDesigner.UI.Windows { using System.Collections; using Terminal.Gui; using TerminalGuiDesigner.ToCode; - public partial class ArrayEditor { + public partial class ArrayEditor : IValueGetterDialog { /// /// True if the editing was aborted. @@ -28,7 +29,10 @@ public partial class ArrayEditor { /// /// The new array /// - public IList Result { get; private set; } + [CanBeNull] + public object Result => ResultAsList; + public IList ResultAsList { get; private set; } + /// /// Creates a new instance of the editor configured to build lists of @@ -43,14 +47,14 @@ public ArrayEditor(Design design, Type elementType, IList oldValue) { this.elementType = elementType; Type listType = typeof(List<>).MakeGenericType(elementType); - Result = (IList)Activator.CreateInstance(listType); + ResultAsList = (IList)Activator.CreateInstance(listType); foreach(var e in oldValue) { - Result.Add(e); + ResultAsList.Add(e); } - lvElements.Source = Result.ToListDataSource(); + lvElements.Source = ResultAsList.ToListDataSource(); lvElements.KeyDown += LvElements_KeyDown; btnOk.Accept += BtnOk_Clicked; btnCancel.Accept += BtnCancel_Clicked; @@ -67,14 +71,14 @@ private void BtnMoveUp_Clicked(object sender, EventArgs e) // Moving up means reducing the index by 1 var idx = lvElements.SelectedItem; - if (idx >= 1 && idx < Result.Count) + if (idx >= 1 && idx < ResultAsList.Count) { - var toMove = Result[idx]; + var toMove = ResultAsList[idx]; var newIndex = idx - 1; - Result.RemoveAt(idx); - Result.Insert(newIndex, toMove); + ResultAsList.RemoveAt(idx); + ResultAsList.Insert(newIndex, toMove); - lvElements.Source = Result.ToListDataSource(); + lvElements.Source = ResultAsList.ToListDataSource(); lvElements.SelectedItem = newIndex; lvElements.SetNeedsDisplay(); } @@ -85,14 +89,14 @@ private void BtnMoveDown_Clicked(object sender, EventArgs e) // Moving up means increasing the index by 1 var idx = lvElements.SelectedItem; - if (idx >= 0 && idx < Result.Count-1) + if (idx >= 0 && idx < ResultAsList.Count-1) { - var toMove = Result[idx]; + var toMove = ResultAsList[idx]; var newIndex = idx + 1; - Result.RemoveAt(idx); - Result.Insert(newIndex, toMove); + ResultAsList.RemoveAt(idx); + ResultAsList.Insert(newIndex, toMove); - lvElements.Source = Result.ToListDataSource(); + lvElements.Source = ResultAsList.ToListDataSource(); lvElements.SelectedItem = newIndex; lvElements.SetNeedsDisplay(); } @@ -111,11 +115,11 @@ private void DeleteSelectedItem() { var idx = lvElements.SelectedItem; - if (idx >= 0 && idx < Result.Count) + if (idx >= 0 && idx < ResultAsList.Count) { - Result.RemoveAt(idx); + ResultAsList.RemoveAt(idx); - lvElements.Source = Result.ToListDataSource(); + lvElements.Source = ResultAsList.ToListDataSource(); lvElements.SetNeedsDisplay(); lvElements.SelectedItem = 0; } @@ -125,29 +129,29 @@ private void BtnAddElement_Clicked(object sender, EventArgs e) { if(ValueFactory.GetNewValue("Element Value", design, this.elementType,null, out var newValue,true)) { - Result.Add(newValue); + ResultAsList.Add(newValue); } - lvElements.Source = Result.ToListDataSource(); - lvElements.SelectedItem = Result.Count - 1; + lvElements.Source = ResultAsList.ToListDataSource(); + lvElements.SelectedItem = ResultAsList.Count - 1; lvElements.SetNeedsDisplay(); } private void BtnEdit_Clicked(object sender, EventArgs e) { var idx = lvElements.SelectedItem; - if (idx >= 0 && idx < Result.Count) + if (idx >= 0 && idx < ResultAsList.Count) { - var toEdit = Result[idx]; + var toEdit = ResultAsList[idx]; if (ValueFactory.GetNewValue("Element Value", design, this.elementType, toEdit, out var newValue, true)) { // Replace old with new - Result.RemoveAt(idx); - Result.Insert(idx, newValue); + ResultAsList.RemoveAt(idx); + ResultAsList.Insert(idx, newValue); } - lvElements.Source = Result.ToListDataSource(); + lvElements.Source = ResultAsList.ToListDataSource(); lvElements.SelectedItem = idx; lvElements.SetNeedsDisplay(); } diff --git a/src/UI/Windows/Modals.cs b/src/UI/Windows/Modals.cs index 12205c30..51f68295 100644 --- a/src/UI/Windows/Modals.cs +++ b/src/UI/Windows/Modals.cs @@ -39,27 +39,6 @@ public static bool GetInt(string windowTitle, string entryLabel, int? initialVal return false; } - internal static bool Getint(string windowTitle, string entryLabel, int? initialValue, out int? result) - { - if (GetString(windowTitle, entryLabel, initialValue.ToString(), out var newValue)) - { - if (string.IsNullOrWhiteSpace(newValue)) - { - result = null; - return true; - } - - if (int.TryParse(newValue, out var r)) - { - result = r; - return true; - } - } - - result = 0; - return false; - } - internal static bool GetArray(string windowTitle, string entryLabel, Type arrayElement, Array? initialValue, out Array? result) { var dlg = new GetTextDialog( From 365bcd3e27f3e9851a2fb2e2ff2ce899ddfb00c2 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 1 Sep 2024 10:20:39 +0100 Subject: [PATCH 08/10] Simplify ValueFactory so it works with all value types using Convert.ChangeType --- src/UI/ValueFactory.cs | 72 ++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/src/UI/ValueFactory.cs b/src/UI/ValueFactory.cs index 8212d342..8f38fbdd 100644 --- a/src/UI/ValueFactory.cs +++ b/src/UI/ValueFactory.cs @@ -11,6 +11,40 @@ namespace TerminalGuiDesigner.UI { static class ValueFactory { + public static readonly Type[] ConvertChangeTypeSupports = new Type[] + { + typeof(byte), + typeof(sbyte), + typeof(short), + typeof(ushort), + typeof(int), + typeof(uint), + typeof(long), + typeof(ulong), + typeof(float), + typeof(double), + typeof(decimal), + typeof(bool), + typeof(char), + typeof(string), + typeof(DateTime), + + typeof(byte?), + typeof(sbyte?), + typeof(short?), + typeof(ushort?), + typeof(int?), + typeof(uint?), + typeof(long?), + typeof(ulong?), + typeof(float?), + typeof(double?), + typeof(decimal?), + typeof(bool?), + typeof(char?), + typeof(DateTime?) + }; + internal static bool GetNewValue(string propertyName, Design design, Type type, object? oldValue, out object? newValue, bool allowMultiLine) { newValue = null; @@ -45,7 +79,7 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, newValue = new Point((int)((PointF)newValue).X, (int)((PointF)newValue).Y); } return result; - + } if (type == typeof(PointF)) { @@ -86,7 +120,7 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, } else { - var designer = new ArrayEditor(design,type.GetElementTypeEx(), (IList)oldValue); + var designer = new ArrayEditor(design, type.GetElementTypeEx(), (IList)oldValue); Application.Run(designer); if (!designer.Cancelled) @@ -103,7 +137,7 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, } } else - if (type== typeof(IListDataSource)) + if (type == typeof(IListDataSource)) { // TODO : Make this work with non strings e.g. // if user types a bunch of numbers in or dates @@ -134,29 +168,12 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, } } else - if ( - type== typeof(int) - || type== typeof(int?) - || type== typeof(uint) - || type== typeof(uint?)) - { - // deals with null, int and uint - var v = oldValue == null ? null : (int?)Convert.ToInt32(oldValue); - - if (Modals.GetInt(propertyName, "New Int Value", v, out var resultInt)) - { - // change back to uint/int/null - newValue = resultInt == null ? null : Convert.ChangeType(resultInt, type); - return true; - } - } - else - if (type== typeof(char?) - || type== typeof(char)) + if (ConvertChangeTypeSupports.Contains(type)) { - if (Modals.GetChar(propertyName, "New Single Character", oldValue is null ? null : (char?)oldValue.ToPrimitive() ?? null, out var resultChar)) + var oldValueConverted = oldValue == null ? null : Convert.ChangeType(oldValue, type); + if (Modals.GetString(propertyName, $"New {type.Name} Value", oldValueConverted?.ToString(), out var result, allowMultiLine)) { - newValue = resultChar; + newValue = string.IsNullOrWhiteSpace(result) ? null : Convert.ChangeType(result, type); return true; } } @@ -176,7 +193,6 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, var fd = new FileDialog(); fd.AllowsMultipleSelection = false; - int answer = ChoicesDialog.Query(propertyName, $"Directory or File?", "Directory", "File", "Cancel"); if (answer < 0 || answer >= 2) @@ -194,7 +210,7 @@ internal static bool GetNewValue(string propertyName, Design design, Type type, } else { - newValue = pickDir ? + newValue = pickDir ? new DirectoryInfo(fd.Path) : new FileInfo(fd.Path); return true; } @@ -250,7 +266,7 @@ internal static bool GetNewValue(Design design, Property property, object? oldVa } else { - return GetNewValue(property.PropertyInfo.Name, design, property.PropertyInfo.PropertyType,oldValue, out newValue, ValueFactory.AllowMultiLine(property)); + return GetNewValue(property.PropertyInfo.Name, design, property.PropertyInfo.PropertyType, oldValue, out newValue, ValueFactory.AllowMultiLine(property)); } } @@ -323,6 +339,6 @@ private static bool GetNewColorSchemeValue(Design design, out object? newValue) newValue = null; return false; } - } + } } } From 7f18bd31e4fa9856da5b1d12a7bce668fd556e37 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 1 Sep 2024 10:49:15 +0100 Subject: [PATCH 09/10] Add Showcase and implement for NumericUpDown --- Showcase/NumericUpDown.Designer.cs | 209 +++++++++++++++++++++++++++++ Showcase/NumericUpDown.cs | 20 +++ Showcase/Program.cs | 46 +++++++ Showcase/README.md | 5 + Showcase/Showcase.csproj | 14 ++ src/TerminalGuiDesigner.sln | 6 + 6 files changed, 300 insertions(+) create mode 100644 Showcase/NumericUpDown.Designer.cs create mode 100644 Showcase/NumericUpDown.cs create mode 100644 Showcase/Program.cs create mode 100644 Showcase/README.md create mode 100644 Showcase/Showcase.csproj diff --git a/Showcase/NumericUpDown.Designer.cs b/Showcase/NumericUpDown.Designer.cs new file mode 100644 index 00000000..5afbe074 --- /dev/null +++ b/Showcase/NumericUpDown.Designer.cs @@ -0,0 +1,209 @@ + +//------------------------------------------------------------------------------ + +// +// This code was generated by: +// TerminalGuiDesigner v2.0.0.0 +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ----------------------------------------------------------------------------- +namespace Showcase { + using System; + using Terminal.Gui; + using System.Collections; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Drawing; + + + public partial class NumericUpDown : Terminal.Gui.Window { + + private Terminal.Gui.Label lblInt; + + private Terminal.Gui.NumericUpDown numericUpDownInt; + + private Terminal.Gui.Label lblInt64; + + private Terminal.Gui.NumericUpDown numericUpDownInt64; + + private Terminal.Gui.Label label3; + + private Terminal.Gui.NumericUpDown numericUpDown2; + + private Terminal.Gui.Label label6; + + private Terminal.Gui.NumericUpDown numericUpDownSingle; + + private Terminal.Gui.Label label; + + private Terminal.Gui.NumericUpDown numericUpDownDecimal; + + private Terminal.Gui.Label label4; + + private Terminal.Gui.NumericUpDown numericUpDown3; + + private Terminal.Gui.Label label5; + + private void InitializeComponent() { + this.label5 = new Terminal.Gui.Label(); + this.numericUpDown3 = new Terminal.Gui.NumericUpDown(); + this.label4 = new Terminal.Gui.Label(); + this.numericUpDownDecimal = new Terminal.Gui.NumericUpDown(); + this.label = new Terminal.Gui.Label(); + this.numericUpDownSingle = new Terminal.Gui.NumericUpDown(); + this.label6 = new Terminal.Gui.Label(); + this.numericUpDown2 = new Terminal.Gui.NumericUpDown(); + this.label3 = new Terminal.Gui.Label(); + this.numericUpDownInt64 = new Terminal.Gui.NumericUpDown(); + this.lblInt64 = new Terminal.Gui.Label(); + this.numericUpDownInt = new Terminal.Gui.NumericUpDown(); + this.lblInt = new Terminal.Gui.Label(); + this.Width = Dim.Fill(0); + this.Height = Dim.Fill(0); + this.X = 0; + this.Y = 0; + this.Visible = true; + this.Arrangement = (Terminal.Gui.ViewArrangement.Movable | Terminal.Gui.ViewArrangement.Overlapped); + this.Modal = false; + this.TextAlignment = Terminal.Gui.Alignment.Start; + this.Title = ""; + this.lblInt.Width = Dim.Auto(); + this.lblInt.Height = 1; + this.lblInt.X = 0; + this.lblInt.Y = 0; + this.lblInt.Visible = true; + this.lblInt.Arrangement = Terminal.Gui.ViewArrangement.Fixed; + this.lblInt.Data = "lblInt"; + this.lblInt.Text = "Int:"; + this.lblInt.TextAlignment = Terminal.Gui.Alignment.Start; + this.Add(this.lblInt); + this.numericUpDownInt.Width = Dim.Auto(); + this.numericUpDownInt.Height = Dim.Auto(); + this.numericUpDownInt.X = 9; + this.numericUpDownInt.Y = 0; + this.numericUpDownInt.Visible = true; + this.numericUpDownInt.Arrangement = Terminal.Gui.ViewArrangement.Fixed; + this.numericUpDownInt.Data = "numericUpDownInt"; + this.numericUpDownInt.TextAlignment = Terminal.Gui.Alignment.Start; + this.numericUpDownInt.Value = 1; + this.numericUpDownInt.Increment = 2; + this.Add(this.numericUpDownInt); + this.lblInt64.Width = Dim.Auto(); + this.lblInt64.Height = 1; + this.lblInt64.X = 0; + this.lblInt64.Y = 1; + this.lblInt64.Visible = true; + this.lblInt64.Arrangement = Terminal.Gui.ViewArrangement.Fixed; + this.lblInt64.Data = "lblInt64"; + this.lblInt64.Text = "Int64:"; + this.lblInt64.TextAlignment = Terminal.Gui.Alignment.Start; + this.Add(this.lblInt64); + this.numericUpDownInt64.Width = Dim.Auto(); + this.numericUpDownInt64.Height = Dim.Auto(); + this.numericUpDownInt64.X = 9; + this.numericUpDownInt64.Y = 1; + this.numericUpDownInt64.Visible = true; + this.numericUpDownInt64.Arrangement = Terminal.Gui.ViewArrangement.Fixed; + this.numericUpDownInt64.Data = "numericUpDownInt64"; + this.numericUpDownInt64.TextAlignment = Terminal.Gui.Alignment.Start; + this.numericUpDownInt64.Value = 8943589458974; + this.numericUpDownInt64.Increment = 1; + this.Add(this.numericUpDownInt64); + this.label3.Width = Dim.Auto(); + this.label3.Height = 1; + this.label3.X = 0; + this.label3.Y = 2; + this.label3.Visible = true; + this.label3.Arrangement = Terminal.Gui.ViewArrangement.Fixed; + this.label3.Data = "label3"; + this.label3.Text = "Double:"; + this.label3.TextAlignment = Terminal.Gui.Alignment.Start; + this.Add(this.label3); + this.numericUpDown2.Width = Dim.Auto(); + this.numericUpDown2.Height = Dim.Auto(); + this.numericUpDown2.X = 9; + this.numericUpDown2.Y = 2; + this.numericUpDown2.Visible = true; + this.numericUpDown2.Arrangement = Terminal.Gui.ViewArrangement.Fixed; + this.numericUpDown2.Data = "numericUpDown2"; + this.numericUpDown2.TextAlignment = Terminal.Gui.Alignment.Start; + this.numericUpDown2.Value = 32.3D; + this.numericUpDown2.Increment = 0.01D; + this.Add(this.numericUpDown2); + this.label6.Width = Dim.Auto(); + this.label6.Height = 1; + this.label6.X = 0; + this.label6.Y = 3; + this.label6.Visible = true; + this.label6.Arrangement = Terminal.Gui.ViewArrangement.Fixed; + this.label6.Data = "label6"; + this.label6.Text = "Single:"; + this.label6.TextAlignment = Terminal.Gui.Alignment.Start; + this.Add(this.label6); + this.numericUpDownSingle.Width = Dim.Auto(); + this.numericUpDownSingle.Height = Dim.Auto(); + this.numericUpDownSingle.X = 9; + this.numericUpDownSingle.Y = 3; + this.numericUpDownSingle.Visible = true; + this.numericUpDownSingle.Arrangement = Terminal.Gui.ViewArrangement.Fixed; + this.numericUpDownSingle.Data = "numericUpDownSingle"; + this.numericUpDownSingle.TextAlignment = Terminal.Gui.Alignment.Start; + this.numericUpDownSingle.Value = 1F; + this.numericUpDownSingle.Increment = 1F; + this.Add(this.numericUpDownSingle); + this.label.Width = Dim.Auto(); + this.label.Height = 1; + this.label.X = 0; + this.label.Y = 4; + this.label.Visible = true; + this.label.Arrangement = Terminal.Gui.ViewArrangement.Fixed; + this.label.Data = "label"; + this.label.Text = "Decimal:"; + this.label.TextAlignment = Terminal.Gui.Alignment.Start; + this.Add(this.label); + this.numericUpDownDecimal.Width = Dim.Auto(); + this.numericUpDownDecimal.Height = Dim.Auto(); + this.numericUpDownDecimal.X = 9; + this.numericUpDownDecimal.Y = 4; + this.numericUpDownDecimal.Visible = true; + this.numericUpDownDecimal.Arrangement = Terminal.Gui.ViewArrangement.Fixed; + this.numericUpDownDecimal.Data = "numericUpDownDecimal"; + this.numericUpDownDecimal.TextAlignment = Terminal.Gui.Alignment.Start; + this.numericUpDownDecimal.Value = 0.00000000001m; + this.numericUpDownDecimal.Increment = 0.00000000000001m; + this.Add(this.numericUpDownDecimal); + this.label4.Width = Dim.Auto(); + this.label4.Height = 1; + this.label4.X = 0; + this.label4.Y = 7; + this.label4.Visible = true; + this.label4.Arrangement = Terminal.Gui.ViewArrangement.Fixed; + this.label4.Data = "label4"; + this.label4.Text = "Single:"; + this.label4.TextAlignment = Terminal.Gui.Alignment.Start; + this.Add(this.label4); + this.numericUpDown3.Width = Dim.Auto(); + this.numericUpDown3.Height = Dim.Auto(); + this.numericUpDown3.X = 8; + this.numericUpDown3.Y = 7; + this.numericUpDown3.Visible = true; + this.numericUpDown3.Arrangement = Terminal.Gui.ViewArrangement.Fixed; + this.numericUpDown3.Data = "numericUpDown3"; + this.numericUpDown3.TextAlignment = Terminal.Gui.Alignment.Start; + this.numericUpDown3.Value = float.PositiveInfinity; + this.numericUpDown3.Increment = 1F; + this.Add(this.numericUpDown3); + this.label5.Width = Dim.Auto(); + this.label5.Height = 1; + this.label5.X = 12; + this.label5.Y = 7; + this.label5.Visible = true; + this.label5.Arrangement = Terminal.Gui.ViewArrangement.Fixed; + this.label5.Data = "label5"; + this.label5.Text = "(Supports infinitiy!?)"; + this.label5.TextAlignment = Terminal.Gui.Alignment.Start; + this.Add(this.label5); + } + } +} diff --git a/Showcase/NumericUpDown.cs b/Showcase/NumericUpDown.cs new file mode 100644 index 00000000..30c732b0 --- /dev/null +++ b/Showcase/NumericUpDown.cs @@ -0,0 +1,20 @@ + +//------------------------------------------------------------------------------ + +// +// This code was generated by: +// TerminalGuiDesigner v2.0.0.0 +// You can make changes to this file and they will not be overwritten when saving. +// +// ----------------------------------------------------------------------------- +namespace Showcase { + using Terminal.Gui; + + + public partial class NumericUpDown { + + public NumericUpDown() { + InitializeComponent(); + } + } +} diff --git a/Showcase/Program.cs b/Showcase/Program.cs new file mode 100644 index 00000000..a34e6b7c --- /dev/null +++ b/Showcase/Program.cs @@ -0,0 +1,46 @@ +using System.Collections.ObjectModel; +using System.Runtime.InteropServices.ComTypes; +using Terminal.Gui; + +namespace Showcase +{ + internal class Program + { + private static Type[] views = new[] + { + typeof(NumericUpDown) + + }; + static void Main(string[] args) + { + Application.Init(); + + var w = new Window() + { + Title = "Showcase" + }; + + var lv = new ListView() + { + Width = Dim.Fill(), + Height = Dim.Fill(), + }; + w.Add(lv); + lv.SetSource(new ObservableCollection(views)); + + + lv.KeyDown += (_, e) => + { + if (e.KeyCode == KeyCode.Enter) + { + var v = (Toplevel)Activator.CreateInstance(views[lv.SelectedItem]); + e.Handled = true; + Application.Run(v); + } + }; + + Application.Run(w); + Application.Shutdown(); + } + } +} diff --git a/Showcase/README.md b/Showcase/README.md new file mode 100644 index 00000000..b6b689fe --- /dev/null +++ b/Showcase/README.md @@ -0,0 +1,5 @@ +# Showcase + +This project contains only .Designer.cs files created by TerminalGuiDesigner. + +The purpose of the project is to ensure backwards compatibility issues are detected quickly as well as providing more structure for manual testing \ No newline at end of file diff --git a/Showcase/Showcase.csproj b/Showcase/Showcase.csproj new file mode 100644 index 00000000..41aa7203 --- /dev/null +++ b/Showcase/Showcase.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + diff --git a/src/TerminalGuiDesigner.sln b/src/TerminalGuiDesigner.sln index a6ea7300..1cf838bd 100644 --- a/src/TerminalGuiDesigner.sln +++ b/src/TerminalGuiDesigner.sln @@ -16,6 +16,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests", "..\tests\UnitTests.csproj", "{A207B4E2-4821-4710-A0C2-25236DE7FE99}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Showcase", "..\Showcase\Showcase.csproj", "{32B7F842-1495-405B-B24B-55C48268FFA5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -30,6 +32,10 @@ Global {A207B4E2-4821-4710-A0C2-25236DE7FE99}.Debug|Any CPU.Build.0 = Debug|Any CPU {A207B4E2-4821-4710-A0C2-25236DE7FE99}.Release|Any CPU.ActiveCfg = Release|Any CPU {A207B4E2-4821-4710-A0C2-25236DE7FE99}.Release|Any CPU.Build.0 = Release|Any CPU + {32B7F842-1495-405B-B24B-55C48268FFA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {32B7F842-1495-405B-B24B-55C48268FFA5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {32B7F842-1495-405B-B24B-55C48268FFA5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {32B7F842-1495-405B-B24B-55C48268FFA5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From de1453d182e3234ecfcd13dfc3774c09129d4bf7 Mon Sep 17 00:00:00 2001 From: tznind Date: Sun, 1 Sep 2024 11:14:56 +0100 Subject: [PATCH 10/10] Slider showcase and bugfixes in designer --- Showcase/Slider.Designer.cs | 144 ++++++++++++++++++ Showcase/Slider.cs | 20 +++ src/Design.cs | 7 + src/UI/Windows/SliderOptionEditor.Designer.cs | 42 ++--- 4 files changed, 192 insertions(+), 21 deletions(-) create mode 100644 Showcase/Slider.Designer.cs create mode 100644 Showcase/Slider.cs diff --git a/Showcase/Slider.Designer.cs b/Showcase/Slider.Designer.cs new file mode 100644 index 00000000..7ad8e1f7 --- /dev/null +++ b/Showcase/Slider.Designer.cs @@ -0,0 +1,144 @@ + +//------------------------------------------------------------------------------ + +// +// This code was generated by: +// TerminalGuiDesigner v2.0.0.0 +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ----------------------------------------------------------------------------- +namespace Showcase { + using System; + using Terminal.Gui; + using System.Collections; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Drawing; + + + public partial class Slider : Terminal.Gui.Window { + + private Terminal.Gui.Label label; + + private Terminal.Gui.Slider slider1; + + private Terminal.Gui.Label lblStringSlider; + + private Terminal.Gui.Slider slider2; + + private Terminal.Gui.Label lblStringSliderThin; + + private Terminal.Gui.Slider slider3; + + private void InitializeComponent() { + this.slider3 = new Terminal.Gui.Slider(); + this.lblStringSliderThin = new Terminal.Gui.Label(); + this.slider2 = new Terminal.Gui.Slider(); + this.lblStringSlider = new Terminal.Gui.Label(); + this.slider1 = new Terminal.Gui.Slider(); + this.label = new Terminal.Gui.Label(); + this.Width = Dim.Fill(0); + this.Height = Dim.Fill(0); + this.X = 0; + this.Y = 0; + this.Visible = true; + this.Arrangement = (Terminal.Gui.ViewArrangement.Movable | Terminal.Gui.ViewArrangement.Overlapped); + this.Modal = false; + this.TextAlignment = Terminal.Gui.Alignment.Start; + this.Title = ""; + this.label.Width = Dim.Auto(); + this.label.Height = 1; + this.label.X = 0; + this.label.Y = 0; + this.label.Visible = true; + this.label.Arrangement = Terminal.Gui.ViewArrangement.Fixed; + this.label.Data = "label"; + this.label.Text = "Int Slider (0 to 1):"; + this.label.TextAlignment = Terminal.Gui.Alignment.Start; + this.Add(this.label); + this.slider1.Width = 20; + this.slider1.Height = 2; + this.slider1.X = 24; + this.slider1.Y = 0; + this.slider1.Visible = true; + this.slider1.Arrangement = Terminal.Gui.ViewArrangement.Fixed; + this.slider1.Options = new System.Collections.Generic.List>(new Terminal.Gui.SliderOption[] { + new Terminal.Gui.SliderOption("0", new System.Text.Rune('0'), 0), + new Terminal.Gui.SliderOption("1", new System.Text.Rune('1'), 1)}); + this.slider1.Orientation = Terminal.Gui.Orientation.Horizontal; + this.slider1.RangeAllowSingle = false; + this.slider1.AllowEmpty = false; + this.slider1.MinimumInnerSpacing = 1; + this.slider1.LegendsOrientation = Terminal.Gui.Orientation.Horizontal; + this.slider1.ShowLegends = true; + this.slider1.ShowEndSpacing = false; + this.slider1.Type = Terminal.Gui.SliderType.Single; + this.slider1.Data = "slider1"; + this.slider1.TextAlignment = Terminal.Gui.Alignment.Start; + this.Add(this.slider1); + this.lblStringSlider.Width = Dim.Auto(); + this.lblStringSlider.Height = 1; + this.lblStringSlider.X = 0; + this.lblStringSlider.Y = 2; + this.lblStringSlider.Visible = true; + this.lblStringSlider.Arrangement = Terminal.Gui.ViewArrangement.Fixed; + this.lblStringSlider.Data = "lblStringSlider"; + this.lblStringSlider.Text = "String Slider (Wide):"; + this.lblStringSlider.TextAlignment = Terminal.Gui.Alignment.Start; + this.Add(this.lblStringSlider); + this.slider2.Width = 31; + this.slider2.Height = 2; + this.slider2.X = Pos.Right(lblStringSlider) + 2; + this.slider2.Y = 2; + this.slider2.Visible = true; + this.slider2.Arrangement = Terminal.Gui.ViewArrangement.Fixed; + this.slider2.Options = new System.Collections.Generic.List>(new Terminal.Gui.SliderOption[] { + new Terminal.Gui.SliderOption("Fish", new System.Text.Rune('F'), "Fish"), + new Terminal.Gui.SliderOption("Cat", new System.Text.Rune('C'), "Cat"), + new Terminal.Gui.SliderOption("Ball", new System.Text.Rune('B'), "Ball")}); + this.slider2.Orientation = Terminal.Gui.Orientation.Horizontal; + this.slider2.RangeAllowSingle = false; + this.slider2.AllowEmpty = false; + this.slider2.MinimumInnerSpacing = 1; + this.slider2.LegendsOrientation = Terminal.Gui.Orientation.Horizontal; + this.slider2.ShowLegends = true; + this.slider2.ShowEndSpacing = false; + this.slider2.Type = Terminal.Gui.SliderType.Single; + this.slider2.Data = "slider2"; + this.slider2.TextAlignment = Terminal.Gui.Alignment.Start; + this.Add(this.slider2); + this.lblStringSliderThin.Width = Dim.Auto(); + this.lblStringSliderThin.Height = 1; + this.lblStringSliderThin.X = 0; + this.lblStringSliderThin.Y = 4; + this.lblStringSliderThin.Visible = true; + this.lblStringSliderThin.Arrangement = Terminal.Gui.ViewArrangement.Fixed; + this.lblStringSliderThin.Data = "lblStringSliderThin"; + this.lblStringSliderThin.Text = "String Slider (Thin):"; + this.lblStringSliderThin.TextAlignment = Terminal.Gui.Alignment.Start; + this.Add(this.lblStringSliderThin); + this.slider3.Width = 6; + this.slider3.Height = 2; + this.slider3.X = Pos.Right(lblStringSliderThin) + 2; + this.slider3.Y = 4; + this.slider3.Visible = true; + this.slider3.Arrangement = Terminal.Gui.ViewArrangement.Fixed; + this.slider3.Options = new System.Collections.Generic.List>(new Terminal.Gui.SliderOption[] { + new Terminal.Gui.SliderOption("Fish", new System.Text.Rune('F'), "Fish"), + new Terminal.Gui.SliderOption("Cat", new System.Text.Rune('C'), "Cat"), + new Terminal.Gui.SliderOption("Ball", new System.Text.Rune('B'), "Ball")}); + this.slider3.Orientation = Terminal.Gui.Orientation.Horizontal; + this.slider3.RangeAllowSingle = false; + this.slider3.AllowEmpty = false; + this.slider3.MinimumInnerSpacing = 1; + this.slider3.LegendsOrientation = Terminal.Gui.Orientation.Horizontal; + this.slider3.ShowLegends = true; + this.slider3.ShowEndSpacing = false; + this.slider3.Type = Terminal.Gui.SliderType.Single; + this.slider3.Data = "slider3"; + this.slider3.TextAlignment = Terminal.Gui.Alignment.Start; + this.Add(this.slider3); + } + } +} diff --git a/Showcase/Slider.cs b/Showcase/Slider.cs new file mode 100644 index 00000000..b74cacdd --- /dev/null +++ b/Showcase/Slider.cs @@ -0,0 +1,20 @@ + +//------------------------------------------------------------------------------ + +// +// This code was generated by: +// TerminalGuiDesigner v2.0.0.0 +// You can make changes to this file and they will not be overwritten when saving. +// +// ----------------------------------------------------------------------------- +namespace Showcase { + using Terminal.Gui; + + + public partial class Slider { + + public Slider() { + InitializeComponent(); + } + } +} diff --git a/src/Design.cs b/src/Design.cs index 88b56ba6..a789ef8b 100644 --- a/src/Design.cs +++ b/src/Design.cs @@ -225,6 +225,13 @@ public Design CreateSubControlDesign(string name, View subView) tf.KeyDown += SuppressNativeKeyboardEvents; } + if (subView.GetType().IsGenericType(typeof(Slider<>))) + { + // TODO: Does not seem to work + subView.MouseEvent += (s, e) => SuppressNativeClickEvents(s, e,true); + subView.MouseClick += (s, e) => SuppressNativeClickEvents(s,e, true); + } + if (subView is TreeView tree) { tree.AddObject(new TreeNode("Example Branch 1") diff --git a/src/UI/Windows/SliderOptionEditor.Designer.cs b/src/UI/Windows/SliderOptionEditor.Designer.cs index 38d01acb..b19f3a85 100644 --- a/src/UI/Windows/SliderOptionEditor.Designer.cs +++ b/src/UI/Windows/SliderOptionEditor.Designer.cs @@ -3,7 +3,7 @@ // // This code was generated by: -// TerminalGuiDesigner v1.1.0.0 +// TerminalGuiDesigner v2.0.0.0 // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // @@ -13,6 +13,8 @@ namespace TerminalGuiDesigner.UI.Windows { using Terminal.Gui; using System.Collections; using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Drawing; public partial class SliderOptionEditor : Terminal.Gui.Dialog { @@ -52,34 +54,23 @@ private void InitializeComponent() { this.label2 = new Terminal.Gui.Label(); this.tfLegend = new Terminal.Gui.TextField(); this.label = new Terminal.Gui.Label(); - this.redOnBlack = new Terminal.Gui.ColorScheme( - new Terminal.Gui.Attribute(Terminal.Gui.Color.Red, Terminal.Gui.Color.Black), - new Terminal.Gui.Attribute(Terminal.Gui.Color.Red, Terminal.Gui.Color.Yellow), - new Terminal.Gui.Attribute(Terminal.Gui.Color.BrightRed, Terminal.Gui.Color.Black), - new Terminal.Gui.Attribute(Terminal.Gui.Color.Gray, Terminal.Gui.Color.Black), - new Terminal.Gui.Attribute(Terminal.Gui.Color.BrightRed, Terminal.Gui.Color.Yellow) - ); - - this.tgDefault = new Terminal.Gui.ColorScheme( - new Terminal.Gui.Attribute(Terminal.Gui.Color.White, Terminal.Gui.Color.Blue), - new Terminal.Gui.Attribute(Terminal.Gui.Color.Black, Terminal.Gui.Color.Gray), - new Terminal.Gui.Attribute(Terminal.Gui.Color.BrightCyan, Terminal.Gui.Color.Blue), - new Terminal.Gui.Attribute(Terminal.Gui.Color.Yellow, Terminal.Gui.Color.Blue), - new Terminal.Gui.Attribute(Terminal.Gui.Color.BrightBlue, Terminal.Gui.Color.Gray) - ); + this.redOnBlack = new Terminal.Gui.ColorScheme(new Terminal.Gui.Attribute(4291104543u, 4278979596u), new Terminal.Gui.Attribute(4291104543u, 4286595104u), new Terminal.Gui.Attribute(4293347414u, 4278979596u), new Terminal.Gui.Attribute(4291611852u, 4278979596u), new Terminal.Gui.Attribute(4293347414u, 4286595104u)); + this.tgDefault = new Terminal.Gui.ColorScheme(new Terminal.Gui.Attribute(4294111986u, 4278204378u), new Terminal.Gui.Attribute(4278979596u, 4291611852u), new Terminal.Gui.Attribute(4284602070u, 4278204378u), new Terminal.Gui.Attribute(4286595104u, 4278204378u), new Terminal.Gui.Attribute(4282087679u, 4291611852u)); this.Width = 50; this.Height = 8; this.X = Pos.Center(); this.Y = Pos.Center(); this.Visible = true; + this.Arrangement = Terminal.Gui.ViewArrangement.Movable; this.Modal = true; this.TextAlignment = Terminal.Gui.Alignment.Start; this.Title = "OptionEditor"; - this.label.Width = 4; + this.label.Width = Dim.Auto(); this.label.Height = 1; this.label.X = 6; this.label.Y = 0; this.label.Visible = true; + this.label.Arrangement = Terminal.Gui.ViewArrangement.Fixed; this.label.Data = "label"; this.label.Text = "Legend:"; this.label.TextAlignment = Terminal.Gui.Alignment.Start; @@ -89,16 +80,18 @@ private void InitializeComponent() { this.tfLegend.X = 14; this.tfLegend.Y = 0; this.tfLegend.Visible = true; + this.tfLegend.Arrangement = Terminal.Gui.ViewArrangement.Fixed; this.tfLegend.Secret = false; this.tfLegend.Data = "tfLegend"; this.tfLegend.Text = ""; this.tfLegend.TextAlignment = Terminal.Gui.Alignment.Start; this.Add(this.tfLegend); - this.label2.Width = 4; + this.label2.Width = Dim.Auto(); this.label2.Height = 1; this.label2.X = 0; this.label2.Y = 1; this.label2.Visible = true; + this.label2.Arrangement = Terminal.Gui.ViewArrangement.Fixed; this.label2.Data = "label2"; this.label2.Text = "Abbreviation:"; this.label2.TextAlignment = Terminal.Gui.Alignment.Start; @@ -108,25 +101,28 @@ private void InitializeComponent() { this.tfLegendAbbr.X = 14; this.tfLegendAbbr.Y = 1; this.tfLegendAbbr.Visible = true; + this.tfLegendAbbr.Arrangement = Terminal.Gui.ViewArrangement.Fixed; this.tfLegendAbbr.Secret = false; this.tfLegendAbbr.Data = "tfLegendAbbr"; this.tfLegendAbbr.Text = ""; this.tfLegendAbbr.TextAlignment = Terminal.Gui.Alignment.Start; this.Add(this.tfLegendAbbr); - this.lblOneChar.Width = 10; + this.lblOneChar.Width = Dim.Auto(); this.lblOneChar.Height = 1; this.lblOneChar.X = 0; this.lblOneChar.Y = 2; this.lblOneChar.Visible = true; + this.lblOneChar.Arrangement = Terminal.Gui.ViewArrangement.Fixed; this.lblOneChar.Data = "lblOneChar"; this.lblOneChar.Text = "(Single Char) "; this.lblOneChar.TextAlignment = Terminal.Gui.Alignment.Center; this.Add(this.lblOneChar); - this.label3.Width = 4; + this.label3.Width = Dim.Auto(); this.label3.Height = 1; - this.label3.X = 7; + this.label3.X = 8; this.label3.Y = 3; this.label3.Visible = true; + this.label3.Arrangement = Terminal.Gui.ViewArrangement.Fixed; this.label3.Data = "label3"; this.label3.Text = "Data:"; this.label3.TextAlignment = Terminal.Gui.Alignment.Start; @@ -136,6 +132,7 @@ private void InitializeComponent() { this.tfData.X = 14; this.tfData.Y = 3; this.tfData.Visible = true; + this.tfData.Arrangement = Terminal.Gui.ViewArrangement.Fixed; this.tfData.Secret = false; this.tfData.Data = "tfData"; this.tfData.Text = ""; @@ -146,6 +143,7 @@ private void InitializeComponent() { this.lblType.X = 0; this.lblType.Y = 4; this.lblType.Visible = true; + this.lblType.Arrangement = Terminal.Gui.ViewArrangement.Fixed; this.lblType.Data = "lblType"; this.lblType.Text = "( Type ) "; this.lblType.TextAlignment = Terminal.Gui.Alignment.Center; @@ -155,6 +153,7 @@ private void InitializeComponent() { this.btnOk.X = 11; this.btnOk.Y = 5; this.btnOk.Visible = true; + this.btnOk.Arrangement = Terminal.Gui.ViewArrangement.Fixed; this.btnOk.Data = "btnOk"; this.btnOk.Text = "Ok"; this.btnOk.TextAlignment = Terminal.Gui.Alignment.Center; @@ -165,6 +164,7 @@ private void InitializeComponent() { this.btnCancel.X = 23; this.btnCancel.Y = 5; this.btnCancel.Visible = true; + this.btnCancel.Arrangement = Terminal.Gui.ViewArrangement.Fixed; this.btnCancel.Data = "btnCancel"; this.btnCancel.Text = "Cancel"; this.btnCancel.TextAlignment = Terminal.Gui.Alignment.Center;