2
0
Эх сурвалжийг харах

Fixes #623 Dim.Percent with an extra behavior. (#696)

BDisp 5 жил өмнө
parent
commit
ec73e0f2b6

+ 30 - 7
Terminal.Gui/Core/PosDim.cs

@@ -366,12 +366,14 @@ namespace Terminal.Gui {
 			return 0;
 		}
 
-		class DimFactor : Dim {
+		internal class DimFactor : Dim {
 			float factor;
+			bool remaining;
 
-			public DimFactor (float n)
+			public DimFactor (float n, bool r = false)
 			{
-				this.factor = n;
+				factor = n;
+				remaining = r;
 			}
 
 			internal override int Anchor (int width)
@@ -379,14 +381,19 @@ namespace Terminal.Gui {
 				return (int)(width * factor);
 			}
 
+			public bool IsFromRemaining ()
+			{
+				return remaining;
+			}
+
 			public override string ToString ()
 			{
-				return $"Dim.Factor({factor})";
+				return $"Dim.Factor(factor={factor}, remaining={remaining})";
 			}
 
 			public override int GetHashCode () => factor.GetHashCode ();
 
-			public override bool Equals (object other) => other is DimFactor f && f.factor == factor;
+			public override bool Equals (object other) => other is DimFactor f && f.factor == factor && f.remaining == remaining;
 
 		}
 
@@ -395,6 +402,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <returns>The percent <see cref="Dim"/> object.</returns>
 		/// <param name="n">A value between 0 and 100 representing the percentage.</param>
+		/// <param name="r">If <c>true</c> the Percent is computed based on the remaining space after the X/Y anchor positions. If <c>false</c> is computed based on the whole original space.</param>
 		/// <example>
 		/// This initializes a <see cref="TextField"/>that is centered horizontally, is 50% of the way down, 
 		/// is 30% the height, and is 80% the width of the <see cref="View"/> it added to.
@@ -407,12 +415,12 @@ namespace Terminal.Gui {
 		/// };
 		/// </code>
 		/// </example>
-		public static Dim Percent (float n)
+		public static Dim Percent (float n, bool r = false)
 		{
 			if (n < 0 || n > 100)
 				throw new ArgumentException ("Percent value must be between 0 and 100");
 
-			return new DimFactor (n / 100);
+			return new DimFactor (n / 100, r);
 		}
 
 		internal class DimAbsolute : Dim {
@@ -563,6 +571,11 @@ namespace Terminal.Gui {
 					return 0;
 				}
 			}
+
+			public override int GetHashCode () => Target.GetHashCode ();
+
+			public override bool Equals (object other) => other is DimView abs && abs.Target == Target;
+
 		}
 		/// <summary>
 		/// Returns a <see cref="Dim"/> object tracks the Width of the specified <see cref="View"/>.
@@ -577,5 +590,15 @@ namespace Terminal.Gui {
 		/// <returns>The <see cref="Dim"/> of the other <see cref="View"/>.</returns>
 		/// <param name="view">The view that will be tracked.</param>
 		public static Dim Height (View view) => new DimView (view, 0);
+
+		/// <summary>Serves as the default hash function. </summary>
+		/// <returns>A hash code for the current object.</returns>
+		public override int GetHashCode () => GetHashCode ();
+
+		/// <summary>Determines whether the specified object is equal to the current object.</summary>
+		/// <param name="other">The object to compare with the current object. </param>
+		/// <returns>
+		///     <see langword="true" /> if the specified object  is equal to the current object; otherwise, <see langword="false" />.</returns>
+		public override bool Equals (object other) => other is Dim abs && abs == this;
 	}
 }

+ 6 - 2
Terminal.Gui/Core/View.cs

@@ -1325,8 +1325,10 @@ namespace Terminal.Gui {
 					_x = x.Anchor (hostFrame.Width);
 				if (width == null)
 					w = hostFrame.Width;
+				else if (width is Dim.DimFactor && !((Dim.DimFactor)width).IsFromRemaining ())
+					w = width.Anchor (hostFrame.Width);
 				else
-					w = width.Anchor (hostFrame.Width - _x);
+					w = Math.Max (width.Anchor (hostFrame.Width - _x), 0);
 			}
 
 			if (y is Pos.PosCenter) {
@@ -1342,8 +1344,10 @@ namespace Terminal.Gui {
 					_y = y.Anchor (hostFrame.Height);
 				if (height == null)
 					h = hostFrame.Height;
+				else if (height is Dim.DimFactor && !((Dim.DimFactor)height).IsFromRemaining ())
+					h = height.Anchor (hostFrame.Height);
 				else
-					h = height.Anchor (hostFrame.Height - _y);
+					h = Math.Max (height.Anchor (hostFrame.Height - _y), 0);
 			}
 			Frame = new Rect (_x, _y, w, h);
 		}

+ 3 - 0
Terminal.Gui/Types/Point.cs

@@ -111,6 +111,9 @@ namespace Terminal.Gui
 
 		public static explicit operator Size (Point p)
 		{
+			if (p.X < 0 || p.Y < 0)
+				throw new ArgumentException ("Either Width and Height must be greater or equal to 0.");
+
 			return new Size (p.X, p.Y);
 		}
 

+ 29 - 8
Terminal.Gui/Types/Rect.cs

@@ -17,6 +17,9 @@ namespace Terminal.Gui
 	/// </summary>
 	public struct Rect
 	{
+		int width;
+		int height;
+
 		/// <summary>
 		/// Gets or sets the x-coordinate of the upper-left corner of this Rectangle structure.
 		/// </summary>
@@ -29,12 +32,26 @@ namespace Terminal.Gui
 		/// <summary>
 		/// Gets or sets the width of this Rect structure.
 		/// </summary>
-		public int Width;
+		public int Width {
+			get { return width; }
+			set {
+				if (value < 0)
+					throw new ArgumentException ("Width must be greater or equal to 0.");
+				width = value;
+			}
+		}
 
 		/// <summary>
 		/// Gets or sets the height of this Rectangle structure.
 		/// </summary>
-		public int Height;
+		public int Height {
+			get { return height; }
+			set {
+				if (value < 0)
+					throw new ArgumentException ("Height must be greater or equal to 0.");
+				height = value;
+			}
+		}
 
 		/// <summary>
 		///	Empty Shared Field
@@ -209,8 +226,10 @@ namespace Terminal.Gui
 		{
 			X = location.X;
 			Y = location.Y;
-			Width = size.Width;
-			Height = size.Height;
+			width = size.Width;
+			height = size.Height;
+			Width = width;
+			Height = height;
 		}
 
 		/// <summary>
@@ -224,10 +243,12 @@ namespace Terminal.Gui
 
 		public Rect (int x, int y, int width, int height)
 		{
-			this.X = x;
-			this.Y = y;
-			this.Width = width;
-			this.Height = height;
+			X = x;
+			Y = y;
+			this.width = width;
+			this.height = height;
+			Width = this.width;
+			Height = this.height;
 		}
 
 

+ 7 - 0
Terminal.Gui/Types/Size.cs

@@ -121,6 +121,9 @@ namespace Terminal.Gui {
 
 		public Size (int width, int height)
 		{
+			if (width < 0 || height < 0)
+				throw new ArgumentException ("Either Width and Height must be greater or equal to 0.");
+
 			this.width = width;
 			this.height = height;
 		}
@@ -152,6 +155,8 @@ namespace Terminal.Gui {
 				return width;
 			}
 			set {
+				if (value < 0)
+					throw new ArgumentException ("Width must be greater or equal to 0.");
 				width = value;
 			}
 		}
@@ -169,6 +174,8 @@ namespace Terminal.Gui {
 				return height;
 			}
 			set {
+				if (value < 0)
+					throw new ArgumentException ("Height must be greater or equal to 0.");
 				height = value;
 			}
 		}

+ 2 - 2
UICatalog/Scenarios/Buttons.cs

@@ -210,7 +210,7 @@ namespace UICatalog {
 			var moveHotKeyBtn = new Button (mhkb) {
 				X = 2,
 				Y = Pos.Bottom (radioGroup) + 1,
-				Width = mhkb.Length + 10,
+				Width = Dim.Width (computedFrame) - 2,
 				ColorScheme = Colors.TopLevel,
 			};
 			moveHotKeyBtn.Clicked = () => {
@@ -222,7 +222,7 @@ namespace UICatalog {
 			var moveUnicodeHotKeyBtn = new Button (muhkb) {
 				X = Pos.Left (absoluteFrame) + 1,
 				Y = Pos.Bottom (radioGroup) + 1,
-				Width = muhkb.Length + 30,
+				Width = Dim.Width (absoluteFrame) - 2, // BUGBUG: Not always the width isn't calculated correctly.
 				ColorScheme = Colors.TopLevel,
 			};
 			moveUnicodeHotKeyBtn.Clicked = () => {

+ 11 - 11
UICatalog/Scenarios/Keys.cs

@@ -7,11 +7,11 @@ namespace UICatalog {
 	[ScenarioCategory ("Input")]
 	class Keys : Scenario {
 
-		static List<string> _processKeyList = new List<string> ();
-		static List<string> _processHotKeyList = new List<string> ();
-		static List<string> _processColdKeyList = new List<string> ();
-
 		class TestWindow : Window {
+			public List<string> _processKeyList = new List<string> ();
+			public List<string> _processHotKeyList = new List<string> ();
+			public List<string> _processColdKeyList = new List<string> ();
+
 			public TestWindow (ustring title = null) : base (title)
 			{
 			}
@@ -112,7 +112,7 @@ namespace UICatalog {
 			var keyStrokeListView = new ListView (keyStrokelist) {
 				X = 0,
 				Y = Pos.Top (keyLogLabel) + yOffset,
-				Width = maxLogEntry,
+				Width = Dim.Percent (30),
 				Height = Dim.Fill (),
 			};
 			keyStrokeListView.ColorScheme = Colors.TopLevel;
@@ -127,10 +127,10 @@ namespace UICatalog {
 
 			maxLogEntry = $"{fakeKeyPress}".Length;
 			yOffset = (Top == Application.Top ? 1 : 6);
-			var processKeyListView = new ListView (_processKeyList) {
+			var processKeyListView = new ListView (((TestWindow)Win)._processKeyList) {
 				X = Pos.Left (processKeyLogLabel),
 				Y = Pos.Top (processKeyLogLabel) + yOffset,
-				Width = maxLogEntry,
+				Width = Dim.Percent(30),
 				Height = Dim.Fill (),
 			};
 			processKeyListView.ColorScheme = Colors.TopLevel;
@@ -145,10 +145,10 @@ namespace UICatalog {
 			Win.Add (processHotKeyLogLabel);
 
 			yOffset = (Top == Application.Top ? 1 : 6);
-			var processHotKeyListView = new ListView (_processHotKeyList) {
+			var processHotKeyListView = new ListView (((TestWindow)Win)._processHotKeyList) {
 				X = Pos.Left (processHotKeyLogLabel),
 				Y = Pos.Top (processHotKeyLogLabel) + yOffset,
-				Width = maxLogEntry,
+				Width = Dim.Percent (20),
 				Height = Dim.Fill (),
 			};
 			processHotKeyListView.ColorScheme = Colors.TopLevel;
@@ -163,10 +163,10 @@ namespace UICatalog {
 			Win.Add (processColdKeyLogLabel);
 
 			yOffset = (Top == Application.Top ? 1 : 6);
-			var processColdKeyListView = new ListView (_processColdKeyList) {
+			var processColdKeyListView = new ListView (((TestWindow)Win)._processColdKeyList) {
 				X = Pos.Left (processColdKeyLogLabel),
 				Y = Pos.Top (processColdKeyLogLabel) + yOffset,
-				Width = maxLogEntry,
+				Width = Dim.Percent (20),
 				Height = Dim.Fill (),
 			};
 

+ 2 - 2
UICatalog/Scenarios/ListsAndCombos.cs

@@ -42,14 +42,14 @@ namespace UICatalog.Scenarios {
 			var lbComboBox = new Label ("ComboBox") {
 				ColorScheme = Colors.TopLevel,
 				X = Pos.Right (lbListView) + 1,
-				Width = Dim.Percent(60)
+				Width = Dim.Percent(40)
 			};
 
 			var comboBox = new ComboBox () {
 				X = Pos.Right (listview) + 1,
 				Y = Pos.Bottom (lbListView) + 1,
 				Height = Dim.Fill (2),
-				Width = Dim.Percent(60)
+				Width = Dim.Percent(40)
 			};
 			comboBox.SetSource (items);
 

+ 23 - 12
UnitTests/DimTests.cs

@@ -85,8 +85,8 @@ namespace Terminal.Gui {
 
 			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);
+			// FIXED: Dim.Width should support Equals() and this should change to Equal.
+			Assert.Equal (dim1, dim2);
 
 			dim2 = Dim.Width (view2);
 			Assert.NotEqual (dim1, dim2);
@@ -96,18 +96,19 @@ namespace Terminal.Gui {
 			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);
+			// FIXED: Dim.Width should support Equals() and this should change to Equal.
+			Assert.Equal (dim1, dim2);
 
-			testRect1 = new Rect (0, -1, -2, -3);
+			Assert.Throws<ArgumentException> (() => new Rect (0, -1, -2, -3));
+			testRect1 = new Rect (0, -1, 2, 3);
 			view1 = new View (testRect1);
-			testRect2 = new Rect (0, -1, -2, -3);
+			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);
+			// FIXED: Dim.Width should support Equals() and this should change to Equal.
+			Assert.Equal (dim1, dim2);
 
-			testRect1 = new Rect (0, -1, -2, -3);
+			testRect1 = new Rect (0, -1, 2, 3);
 			view1 = new View (testRect1);
 			testRect2 = Rect.Empty;
 			view2 = new View (testRect2);
@@ -166,13 +167,13 @@ namespace Terminal.Gui {
 		{
 			float f = 0;
 			var dim = Dim.Percent (f);
-			Assert.Equal ($"Dim.Factor({f / 100:0.###})", dim.ToString ());
+			Assert.Equal ($"Dim.Factor(factor={f / 100:0.###}, remaining={false})", dim.ToString ());
 			f = 0.5F;
 			dim = Dim.Percent (f);
-			Assert.Equal ($"Dim.Factor({f / 100:0.###})", dim.ToString ());
+			Assert.Equal ($"Dim.Factor(factor={f / 100:0.###}, remaining={false})", dim.ToString ());
 			f = 100;
 			dim = Dim.Percent (f);
-			Assert.Equal ($"Dim.Factor({f / 100:0.###})", dim.ToString ());
+			Assert.Equal ($"Dim.Factor(factor={f / 100:0.###}, remaining={false})", dim.ToString ());
 		}
 
 		[Fact]
@@ -199,6 +200,16 @@ namespace Terminal.Gui {
 			dim2 = Dim.Percent (n2);
 			Assert.Equal (dim1, dim2);
 
+			n1 = n2 = 0.3f;
+			dim1 = Dim.Percent (n1, true);
+			dim2 = Dim.Percent (n2, true);
+			Assert.Equal (dim1, dim2);
+
+			n1 = n2 = 0.3f;
+			dim1 = Dim.Percent (n1);
+			dim2 = Dim.Percent (n2, true);
+			Assert.NotEqual (dim1, dim2);
+
 			n1 = 0;
 			n2 = 1;
 			dim1 = Dim.Percent (n1);

+ 78 - 0
UnitTests/PointTests.cs

@@ -0,0 +1,78 @@
+using System;
+using Xunit;
+
+namespace Terminal.Gui {
+	public class PointTests {
+		[Fact]
+		public void Point_New ()
+		{
+			var point = new Point ();
+			Assert.True (point.IsEmpty);
+
+			point = new Point (new Size ());
+			Assert.True (point.IsEmpty);
+
+			point = new Point (1, 2);
+			Assert.False (point.IsEmpty);
+
+			point = new Point (-1, -2);
+			Assert.False (point.IsEmpty);
+		}
+
+		[Fact]
+		public void Point__SetsValue ()
+		{
+			var point = new Point () {
+				X = 0,
+				Y = 0
+			};
+			Assert.True (point.IsEmpty);
+
+			point = new Point () {
+				X = 1,
+				Y = 2
+			};
+			Assert.False (point.IsEmpty);
+
+			point = new Point () {
+				X = -1,
+				Y = -2
+			};
+			Assert.False (point.IsEmpty);
+		}
+
+		[Fact]
+		public void Point_Equals ()
+		{
+			var point1 = new Point ();
+			var point2 = new Point ();
+			Assert.Equal (point1, point2);
+
+			point1 = new Point (1, 2);
+			point2 = new Point (1, 2);
+			Assert.Equal (point1, point2);
+
+			point1 = new Point (1, 2);
+			point2 = new Point (0, 2);
+			Assert.NotEqual (point1, point2);
+
+			point1 = new Point (1, 2);
+			point2 = new Point (0, 3);
+			Assert.NotEqual (point1, point2);
+		}
+
+		[Fact]
+		public void Point_Size ()
+		{
+			var point = new Point (1, 2);
+			var size = (Size)point;
+			Assert.False (size.IsEmpty);
+
+			point = new Point (-1, 2);
+			Action action = () => size = (Size)point;
+			var ex = Assert.Throws<ArgumentException> (action);
+			Assert.Equal ("Either Width and Height must be greater or equal to 0.", ex.Message);
+
+		}
+	}
+}

+ 114 - 0
UnitTests/RectTests.cs

@@ -0,0 +1,114 @@
+using System;
+using Xunit;
+
+namespace Terminal.Gui {
+	public class RectTests {
+		[Fact]
+		public void Rect_New ()
+		{
+			var rect = new Rect ();
+			Assert.True (rect.IsEmpty);
+
+			rect = new Rect (new Point (), new Size ());
+			Assert.True (rect.IsEmpty);
+
+			rect = new Rect (1, 2, 3, 4);
+			Assert.False (rect.IsEmpty);
+
+			rect = new Rect (-1, -2, 3, 4);
+			Assert.False (rect.IsEmpty);
+
+			Action action = () => new Rect (1, 2, -3, 4);
+			var ex = Assert.Throws<ArgumentException> (action);
+			Assert.Equal ("Width must be greater or equal to 0.", ex.Message);
+
+			action = () => new Rect (1, 2, 3, -4);
+			ex = Assert.Throws<ArgumentException> (action);
+			Assert.Equal ("Height must be greater or equal to 0.", ex.Message);
+
+			action = () => new Rect (1, 2, -3, -4);
+			ex = Assert.Throws<ArgumentException> (action);
+			Assert.Equal ("Width must be greater or equal to 0.", ex.Message);
+
+		}
+
+		[Fact]
+		public void Rect__SetsValue ()
+		{
+			var rect = new Rect () {
+				X = 0,
+				Y = 0
+			};
+			Assert.True (rect.IsEmpty);
+
+			rect = new Rect () {
+				X = -1,
+				Y = -2
+			};
+			Assert.False (rect.IsEmpty);
+
+			rect = new Rect () {
+				Width = 3,
+				Height = 4
+			};
+			Assert.False (rect.IsEmpty);
+
+			rect = new Rect () {
+				X = -1,
+				Y = -2,
+				Width = 3,
+				Height = 4
+			};
+			Assert.False (rect.IsEmpty);
+
+			Action action = () => {
+				rect = new Rect () {
+					X = -1,
+					Y = -2,
+					Width = -3,
+					Height = 4
+				};
+			};
+			var ex = Assert.Throws<ArgumentException> (action);
+			Assert.Equal ("Width must be greater or equal to 0.", ex.Message);
+
+			action = () => {
+				rect = new Rect () {
+					X = -1,
+					Y = -2,
+					Width = 3,
+					Height = -4
+				};
+			};
+			ex = Assert.Throws<ArgumentException> (action);
+			Assert.Equal ("Height must be greater or equal to 0.", ex.Message);
+
+			action = () => {
+				rect = new Rect () {
+					X = -1,
+					Y = -2,
+					Width = -3,
+					Height = -4
+				};
+			};
+			ex = Assert.Throws<ArgumentException> (action);
+			Assert.Equal ("Width must be greater or equal to 0.", ex.Message);
+		}
+
+		[Fact]
+		public void Rect_Equals ()
+		{
+			var rect1 = new Rect ();
+			var rect2 = new Rect ();
+			Assert.Equal (rect1, rect2);
+
+			rect1 = new Rect (1, 2, 3, 4);
+			rect2 = new Rect (1, 2, 3, 4);
+			Assert.Equal (rect1, rect2);
+
+			rect1 = new Rect (1, 2, 3, 4);
+			rect2 = new Rect (-1, 2, 3, 4);
+			Assert.NotEqual (rect1, rect2);
+		}
+	}
+}

+ 91 - 0
UnitTests/SizeTests.cs

@@ -0,0 +1,91 @@
+using System;
+using Xunit;
+
+namespace Terminal.Gui {
+	public class SizeTests {
+		[Fact]
+		public void Size_New ()
+		{
+			var size = new Size ();
+			Assert.True (size.IsEmpty);
+
+			size = new Size (new Point ());
+			Assert.True (size.IsEmpty);
+
+			size = new Size (3, 4);
+			Assert.False (size.IsEmpty);
+
+			Action action = () => new Size (-3, 4);
+			var ex = Assert.Throws<ArgumentException> (action);
+			Assert.Equal ("Either Width and Height must be greater or equal to 0.", ex.Message);
+
+			action = () => new Size (3, -4);
+			ex = Assert.Throws<ArgumentException> (action);
+			Assert.Equal ("Either Width and Height must be greater or equal to 0.", ex.Message);
+
+			action = () => new Size (-3, -4);
+			ex = Assert.Throws<ArgumentException> (action);
+			Assert.Equal ("Either Width and Height must be greater or equal to 0.", ex.Message);
+
+		}
+
+		[Fact]
+		public void Size__SetsValue ()
+		{
+			var size = new Size () {
+				Width = 0,
+				Height = 0
+			};
+			Assert.True (size.IsEmpty);
+
+			size = new Size () {
+				Width = 3,
+				Height = 4
+			};
+			Assert.False (size.IsEmpty);
+
+			Action action = () => {
+				size = new Size () {
+					Width = -3,
+					Height = 4
+				};
+			};
+			var ex = Assert.Throws<ArgumentException> (action);
+			Assert.Equal ("Width must be greater or equal to 0.", ex.Message);
+
+			action = () => {
+				size = new Size () {
+					Width = 3,
+					Height = -4
+				};
+			};
+			ex = Assert.Throws<ArgumentException> (action);
+			Assert.Equal ("Height must be greater or equal to 0.", ex.Message);
+
+			action = () => {
+				size = new Size () {
+					Width = -3,
+					Height = -4
+				};
+			};
+			ex = Assert.Throws<ArgumentException> (action);
+			Assert.Equal ("Width must be greater or equal to 0.", ex.Message);
+		}
+
+		[Fact]
+		public void Size_Equals ()
+		{
+			var size1 = new Size ();
+			var size2 = new Size ();
+			Assert.Equal (size1, size2);
+
+			size1 = new Size (3, 4);
+			size2 = new Size (3, 4);
+			Assert.Equal (size1, size2);
+
+			size1 = new Size (3, 4);
+			size2 = new Size (4, 4);
+			Assert.NotEqual (size1, size2);
+		}
+	}
+}