From d5dd29dbda12e3254ba49e5cdc56f24e79ab33d3 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Tue, 9 Jun 2020 14:51:02 -0600 Subject: [PATCH 1/4] fixed #504 and updated tests --- Terminal.Gui/Core/PosDim.cs | 20 +-- Terminal.Gui/Views/Button.cs | 30 ++-- UnitTests/DimTests.cs | 120 +++++++++++++-- UnitTests/PosTests.cs | 281 ++++++++++++++++++++++++++++------- 4 files changed, 371 insertions(+), 80 deletions(-) diff --git a/Terminal.Gui/Core/PosDim.cs b/Terminal.Gui/Core/PosDim.cs index fd7b2641f..b899bca59 100644 --- a/Terminal.Gui/Core/PosDim.cs +++ b/Terminal.Gui/Core/PosDim.cs @@ -274,7 +274,7 @@ namespace Terminal.Gui { } internal override int Anchor (int width) { - switch(side) { + switch (side) { case 0: return Target.Frame.X; case 1: return Target.Frame.Y; case 2: return Target.Frame.Right; @@ -287,7 +287,7 @@ namespace Terminal.Gui { public override string ToString () { string tside; - switch(side) { + switch (side) { case 0: tside = "x"; break; case 1: tside = "y"; break; case 2: tside = "right"; break; @@ -303,42 +303,42 @@ namespace Terminal.Gui { /// /// The that depends on the other view. /// The that will be tracked. - public static Pos Left (View view) => new PosView (view, 0); + public static Pos Left (View view) => new PosCombine (true, new PosView (view, 0), new Pos.PosAbsolute (0)); /// /// Returns a object tracks the Left (X) position of the specified . /// /// The that depends on the other view. /// The that will be tracked. - public static Pos X (View view) => new PosView (view, 0); + public static Pos X (View view) => new PosCombine (true, new PosView (view, 0), new Pos.PosAbsolute (0)); /// /// Returns a object tracks the Top (Y) position of the specified . /// /// The that depends on the other view. /// The that will be tracked. - public static Pos Top (View view) => new PosView (view, 1); + public static Pos Top (View view) => new PosCombine (true, new PosView (view, 1), new Pos.PosAbsolute (0)); /// /// Returns a object tracks the Top (Y) position of the specified . /// /// The that depends on the other view. /// The that will be tracked. - public static Pos Y (View view) => new PosView (view, 1); + public static Pos Y (View view) => new PosCombine (true, new PosView (view, 1), new Pos.PosAbsolute (0)); /// /// Returns a object tracks the Right (X+Width) coordinate of the specified . /// /// The that depends on the other view. /// The that will be tracked. - public static Pos Right (View view) => new PosView (view, 2); + public static Pos Right (View view) => new PosCombine (true, new PosView (view, 2), new Pos.PosAbsolute (0)); /// /// Returns a object tracks the Bottom (Y+Height) coordinate of the specified /// /// The that depends on the other view. /// The that will be tracked. - public static Pos Bottom (View view) => new PosView (view, 3); + public static Pos Bottom (View view) => new PosCombine (true, new PosView (view, 3), new Pos.PosAbsolute (0)); } /// @@ -541,7 +541,7 @@ namespace Terminal.Gui { public override string ToString () { string tside; - switch(side) { + switch (side) { case 0: tside = "Height"; break; case 1: tside = "Width"; break; default: tside = "unknown"; break; @@ -551,7 +551,7 @@ namespace Terminal.Gui { internal override int Anchor (int width) { - switch(side) { + switch (side) { case 0: return Target.Frame.Height; case 1: return Target.Frame.Width; default: diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs index b9e475338..c542d3dee 100644 --- a/Terminal.Gui/Views/Button.cs +++ b/Terminal.Gui/Views/Button.cs @@ -62,7 +62,7 @@ namespace Terminal.Gui { /// The width of the is computed based on the /// text length. The height will always be 1. /// - public Button () : this (string.Empty) { } + public Button () : this (text: string.Empty, is_default: false) { } /// /// Initializes a new instance of using layout. @@ -78,11 +78,7 @@ namespace Terminal.Gui { /// public Button (ustring text, bool is_default = false) : base () { - CanFocus = true; - Text = text ?? string.Empty; - this.IsDefault = is_default; - int w = SetWidthHeight (text, is_default); - Frame = new Rect (Frame.Location, new Size (w, 1)); + Init (text, is_default); } /// @@ -114,9 +110,26 @@ namespace Terminal.Gui { public Button (int x, int y, ustring text, bool is_default) : base (new Rect (x, y, text.Length + 4 + (is_default ? 2 : 0), 1)) { + Init (text, is_default); + } + + Rune _leftBracket; + Rune _rightBracket; + Rune _leftDefault; + Rune _rightDefault; + + void Init (ustring text, bool is_default) + { + _leftBracket = new Rune (Driver != null ? Driver.LeftBracket : '['); + _rightBracket = new Rune (Driver != null ? Driver.RightBracket : ']'); + _leftDefault = new Rune (Driver != null ? Driver.LeftDefaultIndicator : '<'); + _rightDefault = new Rune (Driver != null ? Driver.RightDefaultIndicator : '>'); + CanFocus = true; Text = text ?? string.Empty; this.IsDefault = is_default; + int w = SetWidthHeight (text, is_default); + Frame = new Rect (Frame.Location, new Size (w, 1)); } int SetWidthHeight (ustring text, bool is_default) @@ -154,11 +167,6 @@ namespace Terminal.Gui { } } - Rune _leftBracket = new Rune (Driver.LeftBracket); - Rune _rightBracket = new Rune (Driver.RightBracket); - Rune _leftDefault = new Rune (Driver.LeftDefaultIndicator); - Rune _rightDefault = new Rune (Driver.RightDefaultIndicator); - internal void Update () { if (IsDefault) diff --git a/UnitTests/DimTests.cs b/UnitTests/DimTests.cs index 2b99510db..d77539cd6 100644 --- a/UnitTests/DimTests.cs +++ b/UnitTests/DimTests.cs @@ -27,9 +27,37 @@ namespace Terminal.Gui { int testVal = 5; dim = Dim.Sized (testVal); Assert.Equal ($"Dim.Absolute({testVal})", dim.ToString ()); + + testVal = -1; + dim = Dim.Sized (testVal); + Assert.Equal ($"Dim.Absolute({testVal})", dim.ToString ()); } - // TODO: Other Dim.Sized tests (e.g. Equal?) + [Fact] + public void Sized_Equals () + { + int n1 = 0; + int n2 = 0; + var dim1 = Dim.Sized (n1); + var dim2 = Dim.Sized (n2); + Assert.Equal (dim1, dim2); + + n1 = n2 = 1; + dim1 = Dim.Sized (n1); + dim2 = Dim.Sized (n2); + Assert.Equal (dim1, dim2); + + n1 = n2 = -1; + dim1 = Dim.Sized (n1); + dim2 = Dim.Sized (n2); + Assert.Equal (dim1, dim2); + + n1 = 0; + n2 = 1; + dim1 = Dim.Sized (n1); + dim2 = Dim.Sized (n2); + Assert.NotEqual (dim1, dim2); + } [Fact] public void Width_SetsValue () @@ -47,7 +75,46 @@ namespace Terminal.Gui { Assert.Equal ($"DimView(side=Width, target=View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", dim.ToString ()); } - // TODO: Other Dim.Width tests (e.g. Equal?) + [Fact] + public void Width_Equals () + { + var testRect1 = Rect.Empty; + var view1 = new View (testRect1); + var testRect2 = Rect.Empty; + var view2 = new View (testRect2); + + var dim1 = Dim.Width (view1); + var dim2 = Dim.Width (view1); + // BUGBUG: Dim.Width should support Equals() and this should change to Euqal. + Assert.NotEqual (dim1, dim2); + + dim2 = Dim.Width (view2); + Assert.NotEqual (dim1, dim2); + + testRect1 = new Rect (0, 1, 2, 3); + view1 = new View (testRect1); + testRect2 = new Rect (0, 1, 2, 3); + dim1 = Dim.Width (view1); + dim2 = Dim.Width (view1); + // BUGBUG: Dim.Width should support Equals() and this should change to Euqal. + Assert.NotEqual (dim1, dim2); + + testRect1 = new Rect (0, -1, -2, -3); + view1 = new View (testRect1); + testRect2 = new Rect (0, -1, -2, -3); + dim1 = Dim.Width (view1); + dim2 = Dim.Width (view1); + // BUGBUG: Dim.Width should support Equals() and this should change to Euqal. + Assert.NotEqual (dim1, dim2); + + testRect1 = new Rect (0, -1, -2, -3); + view1 = new View (testRect1); + testRect2 = Rect.Empty; + view2 = new View (testRect2); + dim1 = Dim.Width (view1); + dim2 = Dim.Width (view2); + Assert.NotEqual (dim1, dim2); + } [Fact] public void Height_SetsValue () @@ -72,7 +139,7 @@ namespace Terminal.Gui { { var testMargin = 0; var dim = Dim.Fill (); - Assert.Equal ($"Dim.Fill(margin={testMargin})", dim.ToString()); + Assert.Equal ($"Dim.Fill(margin={testMargin})", dim.ToString ()); testMargin = 0; dim = Dim.Fill (testMargin); @@ -85,7 +152,7 @@ namespace Terminal.Gui { [Fact] - public void Fill_Equal() + public void Fill_Equal () { var margin1 = 0; var margin2 = 0; @@ -99,19 +166,54 @@ namespace Terminal.Gui { { float f = 0; var dim = Dim.Percent (f); - Assert.Equal ($"Dim.Factor({f/100:0.###})", dim.ToString ()); + Assert.Equal ($"Dim.Factor({f / 100:0.###})", dim.ToString ()); f = 0.5F; dim = Dim.Percent (f); - Assert.Equal ($"Dim.Factor({f/100:0.###})", dim.ToString ()); + Assert.Equal ($"Dim.Factor({f / 100:0.###})", dim.ToString ()); f = 100; dim = Dim.Percent (f); - Assert.Equal ($"Dim.Factor({f/100:0.###})", dim.ToString ()); + Assert.Equal ($"Dim.Factor({f / 100:0.###})", dim.ToString ()); } - // TODO: Other Dim.Percent tests (e.g. Equal?) + [Fact] + public void Percent_Equals () + { + float n1 = 0; + float n2 = 0; + var dim1 = Dim.Percent (n1); + var dim2 = Dim.Percent (n2); + Assert.Equal (dim1, dim2); + + n1 = n2 = 1; + dim1 = Dim.Percent (n1); + dim2 = Dim.Percent (n2); + Assert.Equal (dim1, dim2); + + n1 = n2 = 0.5f; + dim1 = Dim.Percent (n1); + dim2 = Dim.Percent (n2); + Assert.Equal (dim1, dim2); + + n1 = n2 = 100f; + dim1 = Dim.Percent (n1); + dim2 = Dim.Percent (n2); + Assert.Equal (dim1, dim2); + + n1 = 0; + n2 = 1; + dim1 = Dim.Percent (n1); + dim2 = Dim.Percent (n2); + Assert.NotEqual (dim1, dim2); + + n1 = 0.5f; + n2 = 1.5f; + dim1 = Dim.Percent (n1); + dim2 = Dim.Percent (n2); + Assert.NotEqual (dim1, dim2); + } [Fact] - public void Percent_ThrowsOnIvalid() + public void Percent_ThrowsOnIvalid () { var dim = Dim.Percent (0); Assert.Throws (() => dim = Dim.Percent (-1)); diff --git a/UnitTests/PosTests.cs b/UnitTests/PosTests.cs index ff4c672e1..f686fc31e 100644 --- a/UnitTests/PosTests.cs +++ b/UnitTests/PosTests.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Data; using System.IO; using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; using Terminal.Gui; using Xunit; @@ -46,6 +48,14 @@ namespace Terminal.Gui { Assert.NotEqual (pos1, pos2); } + [Fact] + public void AnchorEnd_Negative_Throws () + { + Pos pos; + var n = -1; + Assert.Throws (() => pos = Pos.AnchorEnd (n)); + } + [Fact] public void At_SetsValue () { @@ -55,7 +65,8 @@ namespace Terminal.Gui { pos = Pos.At (5); Assert.Equal ("Pos.Absolute(5)", pos.ToString ()); - //Assert.Throws (() => pos = Pos.At (-1)); + pos = Pos.At (-1); + Assert.Equal ("Pos.Absolute(-1)", pos.ToString ()); } [Fact] @@ -66,83 +77,250 @@ namespace Terminal.Gui { var pos1 = Pos.At (n1); var pos2 = Pos.At (n2); - // BUGBUG: Pos should implement equality and this should change to Equal + // BUGBUG: #657 - Pos should implement equality and this should change to Equal Assert.NotEqual (pos1, pos2); } - [Fact] - public void Left_SetsValue () + [Fact] + public void SetSide_Null_Throws () { var pos = Pos.Left (null); Assert.Throws (() => pos.ToString ()); - var testVal = Rect.Empty; - pos = Pos.Left (new View ()); - Assert.Equal ($"Pos.View(side=x, target=View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", pos.ToString ()); + pos = Pos.X (null); + Assert.Throws (() => pos.ToString ()); - pos = Pos.Left (new View (testVal)); - Assert.Equal ($"Pos.View(side=x, target=View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", pos.ToString ()); + pos = Pos.Top (null); + Assert.Throws (() => pos.ToString ()); - testVal = new Rect (1, 2, 3, 4); - pos = Pos.Left (new View (testVal)); - Assert.Equal ($"Pos.View(side=x, target=View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", pos.ToString ()); + pos = Pos.Y(null); + Assert.Throws (() => pos.ToString ()); + + pos = Pos.Bottom (null); + Assert.Throws (() => pos.ToString ()); + + pos = Pos.Right (null); + Assert.Throws (() => pos.ToString ()); } // TODO: Test Left, Top, Right bottom Equal + /// + /// Tests Pos.Left, Pos.X, Pos.Top, Pos.Y, Pos.Right, and Pos.Bottom set operations + /// [Fact] - public void Top_SetsValue () + public void PosSide_SetsValue () { - var pos = Pos.Top (null); - Assert.Throws (() => pos.ToString ()); + string side; // used in format string + var testRect = Rect.Empty; + var testInt = 0; + Pos pos; - var testVal = Rect.Empty; + // Pos.Left + side = "x"; + testInt = 0; + testRect = Rect.Empty; + pos = Pos.Left (new View ()); + Assert.Equal ($"Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}})){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + + pos = Pos.Left (new View (testRect)); + Assert.Equal ($"Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}})){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + + testRect = new Rect (1, 2, 3, 4); + pos = Pos.Left (new View (testRect)); + Assert.Equal ($"Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}})){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + + // Pos.Left(win) + 0 + pos = Pos.Left (new View (testRect)) + testInt; + Assert.Equal ($"Pos.Combine(Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}}))+Pos.Absolute(0)){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + + testInt = 1; + // Pos.Left(win) +1 + pos = Pos.Left (new View (testRect)) + testInt; + Assert.Equal ($"Pos.Combine(Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}}))+Pos.Absolute(0)){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + + testInt = -1; + // Pos.Left(win) -1 + pos = Pos.Left (new View (testRect)) - testInt; + Assert.Equal ($"Pos.Combine(Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}}))+Pos.Absolute(0)){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + + // Pos.X + side = "x"; + testInt = 0; + testRect = Rect.Empty; + pos = Pos.X (new View ()); + Assert.Equal ($"Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}})){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + + pos = Pos.X (new View (testRect)); + Assert.Equal ($"Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}})){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + + testRect = new Rect (1, 2, 3, 4); + pos = Pos.X (new View (testRect)); + Assert.Equal ($"Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}})){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + + // Pos.X(win) + 0 + pos = Pos.X (new View (testRect)) + testInt; + Assert.Equal ($"Pos.Combine(Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}}))+Pos.Absolute(0)){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + + testInt = 1; + // Pos.X(win) +1 + pos = Pos.X (new View (testRect)) + testInt; + Assert.Equal ($"Pos.Combine(Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}}))+Pos.Absolute(0)){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + + testInt = -1; + // Pos.X(win) -1 + pos = Pos.X (new View (testRect)) - testInt; + Assert.Equal ($"Pos.Combine(Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}}))+Pos.Absolute(0)){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + + // Pos.Top + side = "y"; + testInt = 0; + testRect = Rect.Empty; pos = Pos.Top (new View ()); - Assert.Equal ($"Pos.View(side=y, target=View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", pos.ToString ()); + Assert.Equal ($"Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}})){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); - testVal = new Rect (1, 2, 3, 4); - pos = Pos.Top (new View (testVal)); - Assert.Equal ($"Pos.View(side=y, target=View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", pos.ToString ()); - } + pos = Pos.Top (new View (testRect)); + Assert.Equal ($"Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}})){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); - [Fact] - public void Right_SetsValue () - { - var pos = Pos.Right (null); - Assert.Throws (() => pos.ToString ()); + testRect = new Rect (1, 2, 3, 4); + pos = Pos.Top (new View (testRect)); + Assert.Equal ($"Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}})){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); - var testVal = Rect.Empty; - pos = Pos.Right (new View ()); - Assert.Equal ($"Pos.View(side=right, target=View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", pos.ToString ()); + // Pos.Top(win) + 0 + pos = Pos.Top (new View (testRect)) + testInt; + Assert.Equal ($"Pos.Combine(Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}}))+Pos.Absolute(0)){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); - testVal = Rect.Empty; - pos = Pos.Right (new View (testVal)); - Assert.Equal ($"Pos.View(side=right, target=View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", pos.ToString ()); + testInt = 1; + // Pos.Top(win) +1 + pos = Pos.Top (new View (testRect)) + testInt; + Assert.Equal ($"Pos.Combine(Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}}))+Pos.Absolute(0)){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); - testVal = new Rect (1, 2, 3, 4); - pos = Pos.Right (new View (testVal)); - Assert.Equal ($"Pos.View(side=right, target=View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", pos.ToString ()); - } + testInt = -1; + // Pos.Top(win) -1 + pos = Pos.Top (new View (testRect)) - testInt; + Assert.Equal ($"Pos.Combine(Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}}))+Pos.Absolute(0)){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); - [Fact] - public void Bottom_SetsValue () - { - var pos = Pos.Bottom (null); - Assert.Throws (() => pos.ToString ()); + // Pos.Y + side = "y"; + testInt = 0; + testRect = Rect.Empty; + pos = Pos.Y (new View ()); + Assert.Equal ($"Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}})){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); - var testVal = Rect.Empty; + pos = Pos.Y (new View (testRect)); + Assert.Equal ($"Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}})){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + + testRect = new Rect (1, 2, 3, 4); + pos = Pos.Y (new View (testRect)); + Assert.Equal ($"Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}})){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + + // Pos.Y(win) + 0 + pos = Pos.Y (new View (testRect)) + testInt; + Assert.Equal ($"Pos.Combine(Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}}))+Pos.Absolute(0)){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + + testInt = 1; + // Pos.Y(win) +1 + pos = Pos.Y (new View (testRect)) + testInt; + Assert.Equal ($"Pos.Combine(Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}}))+Pos.Absolute(0)){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + + testInt = -1; + // Pos.Y(win) -1 + pos = Pos.Y (new View (testRect)) - testInt; + Assert.Equal ($"Pos.Combine(Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}}))+Pos.Absolute(0)){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + + // Pos.Bottom + side = "bottom"; + testRect = Rect.Empty; + testInt = 0; pos = Pos.Bottom (new View ()); - Assert.Equal ($"Pos.View(side=bottom, target=View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", pos.ToString ()); + Assert.Equal ($"Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}})){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); - testVal = Rect.Empty; - pos = Pos.Bottom (new View (testVal)); - Assert.Equal ($"Pos.View(side=bottom, target=View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", pos.ToString ()); + pos = Pos.Bottom (new View (testRect)); + Assert.Equal ($"Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}})){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); - testVal = new Rect (1, 2, 3, 4); - pos = Pos.Bottom (new View (testVal)); - Assert.Equal ($"Pos.View(side=bottom, target=View()({{X={testVal.X},Y={testVal.Y},Width={testVal.Width},Height={testVal.Height}}}))", pos.ToString ()); + testRect = new Rect (1, 2, 3, 4); + pos = Pos.Bottom (new View (testRect)); + Assert.Equal ($"Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}})){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + + // Pos.Bottom(win) + 0 + pos = Pos.Bottom (new View (testRect)) + testInt; + Assert.Equal ($"Pos.Combine(Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}}))+Pos.Absolute(0)){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + + testInt = 1; + // Pos.Bottom(win) +1 + pos = Pos.Bottom (new View (testRect)) + testInt; + Assert.Equal ($"Pos.Combine(Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}}))+Pos.Absolute(0)){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + + testInt = -1; + // Pos.Bottom(win) -1 + pos = Pos.Bottom (new View (testRect)) - testInt; + Assert.Equal ($"Pos.Combine(Pos.Combine(Pos.View(side={side}, target=View()({{X={testRect.X},Y={testRect.Y},Width={testRect.Width},Height={testRect.Height}}}))+Pos.Absolute(0)){(testInt < 0 ? '-' : '+')}Pos.Absolute({testInt}))", pos.ToString ()); + } + + // See: https://github.com/migueldeicaza/gui.cs/issues/504 + [Fact] + public void LeftTopBottomRight_Win_ShouldNotThrow () + { + // Setup Fake driver + (Window win, Button button) setup () + { + Application.Init (new FakeDriver ()); + Application.Iteration = () => { + Application.RequestStop (); + }; + var win = new Window ("window") { + X = 0, + Y = 0, + Width = Dim.Fill (), + Height = Dim.Fill (), + }; + Application.Top.Add (win); + + var button = new Button ("button") { + X = Pos.Center (), + }; + win.Add (button); + + return (win, button); + } + + void cleanup () + { + // Cleanup + Application.Shutdown (); + } + + // Test cases: + var app = setup (); + app.button.Y = Pos.Left (app.win); + Application.Run (); + cleanup (); + + app = setup (); + app.button.Y = Pos.X (app.win); + Application.Run (); + cleanup (); + + app = setup (); + app.button.Y = Pos.Top (app.win); + Application.Run (); + cleanup (); + + app = setup (); + app.button.Y = Pos.Y (app.win); + Application.Run (); + cleanup (); + + app = setup (); + app.button.Y = Pos.Bottom (app.win); + Application.Run (); + cleanup (); + + app = setup (); + app.button.Y = Pos.Right (app.win); + Application.Run (); + cleanup (); - //Assert.Throws (() => pos = Pos.Bottom (new View (new Rect (0, 0, -3, -4)))); } [Fact] @@ -187,6 +365,9 @@ namespace Terminal.Gui { Assert.Throws (() => pos = Pos.Percent (1000001)); } + // TODO: Test PosCombine + + // TODO: Test operators } } From d91116b5b86050ea32763ea01a5b35475dfc4c21 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Wed, 10 Jun 2020 14:52:41 -0600 Subject: [PATCH 2/4] fixed non-printables --- UICatalog/Scenarios/CharacterMap.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index 60d30ee83..ca4b5303d 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using Terminal.Gui; +using Rune = System.Rune; namespace UICatalog { /// @@ -57,7 +58,7 @@ namespace UICatalog { jumpList.Y = Pos.Bottom (label); jumpList.Width = Dim.Fill (); jumpList.SelectedItemChanged = (args) => { - charMap.Start = radioItems[args.SelectedItem].start; + charMap.Start = radioItems [args.SelectedItem].start; }; Win.Add (jumpList); @@ -105,6 +106,15 @@ namespace UICatalog { #if true private void CharMap_DrawContent (Rect viewport) { + Rune ReplaceNonPrintables (Rune c) + { + if (c < 0x20) { + return new Rune (c + 0x2400); // U+25A1 □ WHITE SQUARE + } else { + return c; + } + } + for (int header = 0; header < 16; header++) { Move (viewport.X + RowHeaderWidth + (header * 2), 0); Driver.AddStr ($" {header:x} "); @@ -117,7 +127,8 @@ namespace UICatalog { Driver.AddStr (rowLabel); for (int col = 0; col < 16; col++) { Move (viewport.X + RowHeaderWidth + (col * 2), 0 + y + 1); - Driver.AddStr ($" {(char)((-viewport.Y + row) * 16 + col)}"); + Driver.AddRune (' '); + Driver.AddRune (ReplaceNonPrintables (new Rune (((uint)((uint)(-viewport.Y + row) * 16 + col))))); } } } From 8a3f2a73f66babd540ae1cfdc3f394213253d104 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Wed, 10 Jun 2020 18:57:20 -0600 Subject: [PATCH 3/4] addresses some non-printable stuff --- .../CursesDriver/CursesDriver.cs | 4 +-- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 1 + Terminal.Gui/ConsoleDrivers/NetDriver.cs | 1 + Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 1 + Terminal.Gui/Core/ConsoleDriver.cs | 24 ++++++++++++++++- Terminal.Gui/Views/Button.cs | 8 ------ UICatalog/Scenarios/CharacterMap.cs | 27 +++++++++++-------- 7 files changed, 44 insertions(+), 22 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 6a133064b..11ddcff6c 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -39,14 +39,14 @@ namespace Terminal.Gui { } static bool sync = false; - public override void AddRune (Rune rune) + public override void AddRune (Rune rune) { if (Clip.Contains (ccol, crow)) { if (needMove) { Curses.move (crow, ccol); needMove = false; } - Curses.addch ((int)(uint)rune); + Curses.addch ((int)(uint)MakePrintable(rune)); } else needMove = true; if (sync) diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 19196397c..f1c52c40d 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -90,6 +90,7 @@ namespace Terminal.Gui { /// public override void AddRune (Rune rune) { + rune = MakePrintable (rune); if (Clip.Contains (ccol, crow)) { if (needMove) { //MockConsole.CursorLeft = ccol; diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 91a39b3bf..c176d8fd2 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -70,6 +70,7 @@ namespace Terminal.Gui { public override void AddRune (Rune rune) { + rune = MakePrintable (rune); if (Clip.Contains (ccol, crow)) { if (needMove) { //Console.CursorLeft = ccol; diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 6397967e0..3618a849f 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1126,6 +1126,7 @@ namespace Terminal.Gui { public override void AddRune (Rune rune) { + rune = MakePrintable (rune); var position = crow * Cols + ccol; if (Clip.Contains (ccol, crow)) { diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index 77f61f1e2..dc709cd70 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -1,4 +1,4 @@ -// +// // ConsoleDriver.cs: Definition for the Console Driver API // // Authors: @@ -558,6 +558,28 @@ namespace Terminal.Gui { /// Rune to add. public abstract void AddRune (Rune rune); /// + /// Ensures a Rune is not a control character and can be displayed by translating characters below 0x20 + /// to equivalent, printable, Unicode chars. + /// + /// Rune to translate + /// + public static Rune MakePrintable (Rune c) + { + if (c <= 0x1F) { + // ASCII (C0) control characters. + return new Rune (c + 0x2400); + } else if (c >= 0x80 && c <= 0x9F) { + // C1 control characters (https://www.aivosto.com/articles/control-characters.html#c1) + return new Rune (0x25a1); // U+25A1, WHITE SQUARE, □: + } else if (Rune.ColumnWidth (c) > 1) { + // BUGBUG: Until we figure out how to fix #41. Note this still doesn't help when + // an Emoji or other char doesn't represent it's width correctly + return new Rune (0x25a1); // U+25A1, WHITE SQUARE, □: + } else { + return c; + } + } + /// /// Adds the specified /// /// String. diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs index 3e2aaaaf1..b9e475338 100644 --- a/Terminal.Gui/Views/Button.cs +++ b/Terminal.Gui/Views/Button.cs @@ -166,14 +166,6 @@ namespace Terminal.Gui { else shown_text = ustring.Make (_leftBracket) + " " + text + " " + ustring.Make (_rightBracket); - shown_text = shown_text - .Replace ("\f", "\u21a1") // U+21A1 ↡ DOWNWARDS TWO HEADED ARROW - .Replace ("\n", "\u240a") // U+240A (SYMBOL FOR LINE FEED, ␊) - .Replace ("\r", "\u240d") // U+240D (SYMBOL FOR CARRIAGE RETURN, ␍) - .Replace ("\t", "\u2409") // U+2409 ␉ SYMBOL FOR HORIZONTAL TABULATION - .Replace ("\v", "\u240b") // U+240B ␋ SYMBOL FOR VERTICAL TABULATION - .TrimSpace (); - shown_text = GetTextFromHotKey (shown_text, '_', out hot_pos, out hot_key); SetNeedsDisplay (); diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index ca4b5303d..a875c4658 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -37,6 +37,9 @@ namespace UICatalog { } var radioItems = new (ustring radioLabel, int start, int end) [] { + CreateRadio("ASCII Control Characterss", 0x00, 0x1F), + CreateRadio("C0 Control Characters", 0x80, 0x9f), + CreateRadio("Hangul Jamo", 0x1100, 0x11ff), // This is where wide chars tend to start CreateRadio("Currency Symbols", 0x20A0, 0x20CF), CreateRadio("Letterlike Symbols", 0x2100, 0x214F), CreateRadio("Arrows", 0x2190, 0x21ff), @@ -106,14 +109,14 @@ namespace UICatalog { #if true private void CharMap_DrawContent (Rect viewport) { - Rune ReplaceNonPrintables (Rune c) - { - if (c < 0x20) { - return new Rune (c + 0x2400); // U+25A1 □ WHITE SQUARE - } else { - return c; - } - } + //Rune ReplaceNonPrintables (Rune c) + //{ + // if (c < 0x20) { + // return new Rune (c + 0x2400); // U+25A1 □ WHITE SQUARE + // } else { + // return c; + // } + //} for (int header = 0; header < 16; header++) { Move (viewport.X + RowHeaderWidth + (header * 2), 0); @@ -125,10 +128,12 @@ namespace UICatalog { var rowLabel = $"U+{val / 16:x4}x"; Move (0, y + 1); Driver.AddStr (rowLabel); + var prevColWasWide = false; for (int col = 0; col < 16; col++) { - Move (viewport.X + RowHeaderWidth + (col * 2), 0 + y + 1); - Driver.AddRune (' '); - Driver.AddRune (ReplaceNonPrintables (new Rune (((uint)((uint)(-viewport.Y + row) * 16 + col))))); + var rune = new Rune ((uint)((uint)(-viewport.Y + row) * 16 + col)); + Move (viewport.X + RowHeaderWidth + (col * 2) + (prevColWasWide ? 0 : 1), 0 + y + 1); + Driver.AddRune (rune); + //prevColWasWide = Rune.ColumnWidth(rune) > 1; } } } From 893386563554b3cbe092b95663446db60237b7d4 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sun, 14 Jun 2020 21:15:15 +0100 Subject: [PATCH 4/4] Update FSharpExample. --- FSharpExample/Program.fs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/FSharpExample/Program.fs b/FSharpExample/Program.fs index f6f4d2462..079a5dd9a 100644 --- a/FSharpExample/Program.fs +++ b/FSharpExample/Program.fs @@ -140,7 +140,7 @@ type Demo() = class end container.Add (login, loginText, password, passText, new FrameView (new Rect (3, 10, 25, 6), ustr "Options", [|new CheckBox (1, 0, ustr "Remember me"); - new RadioGroup (1, 2, [|"_Personal"; "_Company"|])|] + new RadioGroup (1, 2, [|ustr "_Personal"; ustr "_Company"|])|] ), new ListView (new Rect(59, 6, 16, 4), [|"First row"; @@ -434,8 +434,7 @@ type Demo() = class end new StatusItem(Key.F2, ustr "~F2~ Load", Action(Load)); new StatusItem(Key.F3, ustr "~F3~ Save", Action(Save)); new StatusItem(Key.ControlX, ustr "~^X~ Quit", fun () -> if (Quit ()) then top.Running <- false) - |], - Parent = null + |] ) win.Add (drag, dragText) let mutable bottom = new Label(ustr "This should go on the bottom of the same top-level!")