瀏覽代碼

Added Pos/Dim Function feature to automate layout. (#1792)

* Fixes #1791. Added Pos/Dim Function feature to automate layout.

* Added PosFunc/DimFunc class. and some more features.

* Done requested changes.
BDisp 3 年之前
父節點
當前提交
3001d43006
共有 4 個文件被更改,包括 285 次插入25 次删除
  1. 97 15
      Terminal.Gui/Core/PosDim.cs
  2. 97 3
      UnitTests/ButtonTests.cs
  3. 29 0
      UnitTests/DimTests.cs
  4. 62 7
      UnitTests/PosTests.cs

+ 97 - 15
Terminal.Gui/Core/PosDim.cs

@@ -36,6 +36,40 @@ namespace Terminal.Gui {
 			return 0;
 		}
 
+		// Helper class to provide dynamic value by the execution of a function that returns an integer.
+		internal class PosFunc : Pos {
+			Func<int> function;
+
+			public PosFunc (Func<int> n)
+			{
+				this.function = n;
+			}
+
+			internal override int Anchor (int width)
+			{
+				return function ();
+			}
+
+			public override string ToString ()
+			{
+				return $"Pos.PosFunc({function ()})";
+			}
+
+			public override int GetHashCode () => function.GetHashCode ();
+
+			public override bool Equals (object other) => other is PosFunc f && f.function () == function ();
+		}
+
+		/// <summary>
+		/// Creates a "PosFunc" from the specified function.
+		/// </summary>
+		/// <param name="function">The function to be executed.</param>
+		/// <returns>The <see cref="Pos"/> returned from the function.</returns>
+		public static Pos Function (Func<int> function)
+		{
+			return new PosFunc (function);
+		}
+
 		internal class PosFactor : Pos {
 			float factor;
 
@@ -53,6 +87,10 @@ namespace Terminal.Gui {
 			{
 				return $"Pos.Factor({factor})";
 			}
+
+			public override int GetHashCode () => factor.GetHashCode ();
+
+			public override bool Equals (object other) => other is PosFactor f && f.factor == factor;
 		}
 
 		/// <summary>
@@ -182,7 +220,6 @@ namespace Terminal.Gui {
 			public override int GetHashCode () => n.GetHashCode ();
 
 			public override bool Equals (object other) => other is PosAbsolute abs && abs.n == n;
-
 		}
 
 		/// <summary>
@@ -310,6 +347,10 @@ namespace Terminal.Gui {
 				}
 				return $"Pos.View(side={tside}, target={Target.ToString ()})";
 			}
+
+			public override int GetHashCode () => Target.GetHashCode ();
+
+			public override bool Equals (object other) => other is PosView abs && abs.Target == Target;
 		}
 
 		/// <summary>
@@ -353,6 +394,16 @@ namespace Terminal.Gui {
 		/// <returns>The <see cref="Pos"/> that depends on the other view.</returns>
 		/// <param name="view">The <see cref="View"/>  that will be tracked.</param>
 		public static Pos Bottom (View view) => new PosCombine (true, new PosView (view, 3), new Pos.PosAbsolute (0));
+
+		/// <summary>Serves as the default hash function. </summary>
+		/// <returns>A hash code for the current object.</returns>
+		public override int GetHashCode () => Anchor (0).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 Pos abs && abs == this;
 	}
 
 	/// <summary>
@@ -375,6 +426,40 @@ namespace Terminal.Gui {
 			return 0;
 		}
 
+		// Helper class to provide dynamic value by the execution of a function that returns an integer.
+		internal class DimFunc : Dim {
+			Func<int> function;
+
+			public DimFunc (Func<int> n)
+			{
+				this.function = n;
+			}
+
+			internal override int Anchor (int width)
+			{
+				return function ();
+			}
+
+			public override string ToString ()
+			{
+				return $"Dim.DimFunc({function ()})";
+			}
+
+			public override int GetHashCode () => function.GetHashCode ();
+
+			public override bool Equals (object other) => other is DimFunc f && f.function () == function ();
+		}
+
+		/// <summary>
+		/// Creates a "DimFunc" from the specified function.
+		/// </summary>
+		/// <param name="function">The function to be executed.</param>
+		/// <returns>The <see cref="Dim"/> returned from the function.</returns>
+		public static Dim Function (Func<int> function)
+		{
+			return new DimFunc (function);
+		}
+
 		internal class DimFactor : Dim {
 			float factor;
 			bool remaining;
@@ -403,7 +488,6 @@ namespace Terminal.Gui {
 			public override int GetHashCode () => factor.GetHashCode ();
 
 			public override bool Equals (object other) => other is DimFactor f && f.factor == factor && f.remaining == remaining;
-
 		}
 
 		/// <summary>
@@ -449,7 +533,6 @@ namespace Terminal.Gui {
 			public override int GetHashCode () => n.GetHashCode ();
 
 			public override bool Equals (object other) => other is DimAbsolute abs && abs.n == n;
-
 		}
 
 		internal class DimFill : Dim {
@@ -590,6 +673,16 @@ namespace Terminal.Gui {
 				this.side = side;
 			}
 
+			internal override int Anchor (int width)
+			{
+				switch (side) {
+				case 0: return Target.Frame.Height;
+				case 1: return Target.Frame.Width;
+				default:
+					return 0;
+				}
+			}
+
 			public override string ToString ()
 			{
 				string tside;
@@ -601,20 +694,9 @@ namespace Terminal.Gui {
 				return $"DimView(side={tside}, target={Target.ToString ()})";
 			}
 
-			internal override int Anchor (int width)
-			{
-				switch (side) {
-				case 0: return Target.Frame.Height;
-				case 1: return Target.Frame.Width;
-				default:
-					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"/>.
@@ -632,7 +714,7 @@ namespace Terminal.Gui {
 
 		/// <summary>Serves as the default hash function. </summary>
 		/// <returns>A hash code for the current object.</returns>
-		public override int GetHashCode () => GetHashCode ();
+		public override int GetHashCode () => Anchor (0).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>

+ 97 - 3
UnitTests/ButtonTests.cs

@@ -26,7 +26,7 @@ namespace Terminal.Gui.Views {
 			Assert.Equal (new Rect (0, 0, 4, 1), btn.Frame);
 			Assert.Equal (Key.Null, btn.HotKey);
 
-			btn = new Button ("ARGS", true) {Text="Test"};
+			btn = new Button ("ARGS", true) { Text = "Test" };
 			Assert.Equal ("Test", btn.Text);
 			Application.Top.Add (btn);
 			btn.Redraw (btn.Bounds);
@@ -166,7 +166,7 @@ namespace Terminal.Gui.Views {
 		[Fact]
 		public void TestAssignTextToButton ()
 		{
-			View b = new Button () {Text="heya"};
+			View b = new Button () { Text = "heya" };
 			Assert.Equal ("heya", b.Text);
 			Assert.True (b.TextFormatter.Text.Contains ("heya"));
 			b.Text = "heyb";
@@ -228,7 +228,7 @@ namespace Terminal.Gui.Views {
 			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			Assert.Equal (new Rect (0, 0, 30, 5), pos);
 		}
-		
+
 		[Fact, AutoInitShutdown]
 		public void Update_Parameterless_Only_On_Or_After_Initialize ()
 		{
@@ -266,5 +266,99 @@ namespace Terminal.Gui.Views {
 			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			Assert.Equal (new Rect (0, 0, 30, 5), pos);
 		}
+
+		[Fact, AutoInitShutdown]
+		public void AutoSize_Stays_True_Center ()
+		{
+			var btn = new Button () {
+				X = Pos.Center (),
+				Y = Pos.Center (),
+				Text = "Say Hello 你",
+				AutoSize = true
+			};
+
+			var win = new Window () {
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+				Title = "Test Demo 你"
+			};
+			win.Add (btn);
+			Application.Top.Add (win);
+
+			Assert.True (btn.AutoSize);
+
+			Application.Begin (Application.Top);
+			((FakeDriver)Application.Driver).SetBufferSize (30, 5);
+			var expected = @"
+┌ Test Demo 你 ──────────────┐
+│                            │
+│      [ Say Hello 你 ]      │
+│                            │
+└────────────────────────────┘
+";
+
+			GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+
+			Assert.True (btn.AutoSize);
+			btn.Text = "Say Hello 你 changed";
+			Assert.True (btn.AutoSize);
+			Application.Refresh ();
+			expected = @"
+┌ Test Demo 你 ──────────────┐
+│                            │
+│  [ Say Hello 你 changed ]  │
+│                            │
+└────────────────────────────┘
+";
+
+			GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void AutoSize_Stays_True_AnchorEnd ()
+		{
+			var btn = new Button () {
+				Y = Pos.Center (),
+				Text = "Say Hello 你",
+				AutoSize = true
+			};
+			btn.X = Pos.AnchorEnd () - Pos.Function (() => TextFormatter.GetTextWidth  (btn.TextFormatter.Text));
+
+			var win = new Window () {
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+				Title = "Test Demo 你"
+			};
+			win.Add (btn);
+			Application.Top.Add (win);
+
+			Assert.True (btn.AutoSize);
+
+			Application.Begin (Application.Top);
+			((FakeDriver)Application.Driver).SetBufferSize (30, 5);
+			var expected = @"
+┌ Test Demo 你 ──────────────┐
+│                            │
+│            [ Say Hello 你 ]│
+│                            │
+└────────────────────────────┘
+";
+
+			GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+
+			Assert.True (btn.AutoSize);
+			btn.Text = "Say Hello 你 changed";
+			Assert.True (btn.AutoSize);
+			Application.Refresh ();
+			expected = @"
+┌ Test Demo 你 ──────────────┐
+│                            │
+│    [ Say Hello 你 changed ]│
+│                            │
+└────────────────────────────┘
+";
+
+			GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+		}
 	}
 }

+ 29 - 0
UnitTests/DimTests.cs

@@ -836,5 +836,34 @@ namespace Terminal.Gui.Core {
 			var dimViewWidth = new Dim.DimView (view, 1);
 			Assert.Equal (20, dimViewWidth.Anchor (0));
 		}
+
+		[Fact]
+		public void Function_SetsValue ()
+		{
+			var text = "Test";
+			var dim = Dim.Function (() => text.Length);
+			Assert.Equal ("Dim.DimFunc(4)", dim.ToString ());
+
+			text = "New Test";
+			Assert.Equal ("Dim.DimFunc(8)", dim.ToString ());
+
+			text = "";
+			Assert.Equal ("Dim.DimFunc(0)", dim.ToString ());
+		}
+
+		[Fact]
+		public void Function_Equal ()
+		{
+			var f1 = () => 0;
+			var f2 = () => 0;
+
+			var dim1 = Dim.Function (f1);
+			var dim2 = Dim.Function (f2);
+			Assert.Equal (dim1, dim2);
+
+			f2 = () => 1;
+			dim2 = Dim.Function (f2);
+			Assert.NotEqual (dim1, dim2);
+		}
 	}
 }

+ 62 - 7
UnitTests/PosTests.cs

@@ -70,11 +70,11 @@ namespace Terminal.Gui.Core {
 
 			Assert.Equal (new Rect (0, 0, 80, 25), top.Frame);
 			Assert.Equal (new Rect (0, 0, 80, 25), win.Frame);
-			Assert.Equal (new Rect (1, 1, 78, 23), win.Subviews[0].Frame);
-			Assert.Equal ("ContentView()({X=1,Y=1,Width=78,Height=23})", win.Subviews [0].ToString());
+			Assert.Equal (new Rect (1, 1, 78, 23), win.Subviews [0].Frame);
+			Assert.Equal ("ContentView()({X=1,Y=1,Width=78,Height=23})", win.Subviews [0].ToString ());
 			Assert.Equal (new Rect (1, 1, 79, 24), new Rect (
-				win.Subviews[0].Frame.Left, win.Subviews [0].Frame.Top,
-				win.Subviews [0].Frame.Right, win.Subviews[0].Frame.Bottom));
+				win.Subviews [0].Frame.Left, win.Subviews [0].Frame.Top,
+				win.Subviews [0].Frame.Right, win.Subviews [0].Frame.Bottom));
 			Assert.Equal (new Rect (68, 22, 10, 1), tv.Frame);
 		}
 
@@ -488,11 +488,37 @@ namespace Terminal.Gui.Core {
 		[Fact]
 		public void Percent_Equal ()
 		{
-			var n1 = 0;
-			var n2 = 0;
+			float n1 = 0;
+			float n2 = 0;
 			var pos1 = Pos.Percent (n1);
 			var pos2 = Pos.Percent (n2);
-			// BUGBUG: Pos.Percent should support equality 
+			Assert.Equal (pos1, pos2);
+
+			n1 = n2 = 1;
+			pos1 = Pos.Percent (n1);
+			pos2 = Pos.Percent (n2);
+			Assert.Equal (pos1, pos2);
+
+			n1 = n2 = 0.5f;
+			pos1 = Pos.Percent (n1);
+			pos2 = Pos.Percent (n2);
+			Assert.Equal (pos1, pos2);
+
+			n1 = n2 = 100f;
+			pos1 = Pos.Percent (n1);
+			pos2 = Pos.Percent (n2);
+			Assert.Equal (pos1, pos2);
+
+			n1 = 0;
+			n2 = 1;
+			pos1 = Pos.Percent (n1);
+			pos2 = Pos.Percent (n2);
+			Assert.NotEqual (pos1, pos2);
+
+			n1 = 0.5f;
+			n2 = 1.5f;
+			pos1 = Pos.Percent (n1);
+			pos2 = Pos.Percent (n2);
 			Assert.NotEqual (pos1, pos2);
 		}
 
@@ -811,5 +837,34 @@ namespace Terminal.Gui.Core {
 			var posViewBottom = new Pos.PosView (view, 3);
 			Assert.Equal (11, posViewBottom.Anchor (0));
 		}
+
+		[Fact]
+		public void Function_SetsValue ()
+		{
+			var text = "Test";
+			var pos = Pos.Function (() => text.Length);
+			Assert.Equal ("Pos.PosFunc(4)", pos.ToString ());
+
+			text = "New Test";
+			Assert.Equal ("Pos.PosFunc(8)", pos.ToString ());
+
+			text = "";
+			Assert.Equal ("Pos.PosFunc(0)", pos.ToString ());
+		}
+
+		[Fact]
+		public void Function_Equal ()
+		{
+			var f1 = () => 0;
+			var f2 = () => 0;
+
+			var pos1 = Pos.Function (f1);
+			var pos2 = Pos.Function (f2);
+			Assert.Equal (pos1, pos2);
+
+			f2 = () => 1;
+			pos2 = Pos.Function (f2);
+			Assert.NotEqual (pos1, pos2);
+		}
 	}
 }