Quellcode durchsuchen

Merge branch 'main' into toplevel-improvement

BDisp vor 4 Jahren
Ursprung
Commit
1a8316799d

+ 4 - 4
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -77,10 +77,10 @@ namespace Terminal.Gui {
 		public override void Refresh ()
 		{
 			Curses.refresh ();
-			//if (Curses.CheckWinChange ()) {
-			//	Clip = new Rect (0, 0, Cols, Rows);
-			//	TerminalResized?.Invoke ();
-			//}
+			if (Curses.CheckWinChange ()) {
+				Clip = new Rect (0, 0, Cols, Rows);
+				TerminalResized?.Invoke ();
+			}
 		}
 
 		public override void UpdateCursor () => Refresh ();

+ 1 - 1
Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs

@@ -116,7 +116,7 @@ namespace Terminal.Gui {
 			this.mainLoop = mainLoop;
 			pipe (wakeupPipes);
 			AddWatch (wakeupPipes [0], Condition.PollIn, ml => {
-				read (wakeupPipes [0], ignore, (IntPtr)0);
+				read (wakeupPipes [0], ignore, (IntPtr)1);
 				return true;
 			});
 		}

+ 20 - 17
Terminal.Gui/Core/View.cs

@@ -2098,17 +2098,20 @@ namespace Terminal.Gui {
 
 		bool SetWidthHeight (Rect nBounds)
 		{
-			bool aSize;
+			bool aSize = false;
 			var canSizeW = SetWidth (nBounds.Width, out int rW);
 			var canSizeH = SetHeight (nBounds.Height, out int rH);
-			if (canSizeW && canSizeH) {
+			if (canSizeW) {
 				aSize = true;
-				Bounds = nBounds;
 				width = rW;
+			}
+			if (canSizeH) {
+				aSize = true;
 				height = rH;
+			}
+			if (aSize) {
+				Bounds = new Rect (Bounds.X, Bounds.Y, canSizeW ? rW : Bounds.Width, canSizeH ? rH : Bounds.Height);
 				textFormatter.Size = Bounds.Size;
-			} else {
-				aSize = false;
 			}
 
 			return aSize;
@@ -2266,15 +2269,13 @@ namespace Terminal.Gui {
 			return true;
 		}
 
-		bool CanSetWidth (int desiredWidth, out int resultWidth, out int currentWidth)
+		bool CanSetWidth (int desiredWidth, out int resultWidth)
 		{
 			int w = desiredWidth;
-			currentWidth = Width != null ? Width.Anchor (0) : 0;
 			bool canSetWidth;
 			if (Width is Dim.DimCombine || Width is Dim.DimView || Width is Dim.DimFill) {
 				// It's a Dim.DimCombine and so can't be assigned. Let it have it's width anchored.
 				w = Width.Anchor (w);
-				currentWidth = Width.Anchor (w);
 				canSetWidth = false;
 			} else if (Width is Dim.DimFactor factor) {
 				// Tries to get the SuperView width otherwise the view width.
@@ -2283,7 +2284,6 @@ namespace Terminal.Gui {
 					sw -= Frame.X;
 				}
 				w = Width.Anchor (sw);
-				currentWidth = Width.Anchor (sw);
 				canSetWidth = false;
 			} else {
 				canSetWidth = true;
@@ -2293,15 +2293,13 @@ namespace Terminal.Gui {
 			return canSetWidth;
 		}
 
-		bool CanSetHeight (int desiredHeight, out int resultHeight, out int currentHeight)
+		bool CanSetHeight (int desiredHeight, out int resultHeight)
 		{
 			int h = desiredHeight;
-			currentHeight = Height != null ? Height.Anchor (0) : 0;
 			bool canSetHeight;
 			if (Height is Dim.DimCombine || Height is Dim.DimView || Height is Dim.DimFill) {
 				// It's a Dim.DimCombine and so can't be assigned. Let it have it's height anchored.
 				h = Height.Anchor (h);
-				currentHeight = Height.Anchor (h);
 				canSetHeight = false;
 			} else if (Height is Dim.DimFactor factor) {
 				// Tries to get the SuperView height otherwise the view height.
@@ -2310,7 +2308,6 @@ namespace Terminal.Gui {
 					sh -= Frame.Y;
 				}
 				h = Height.Anchor (sh);
-				currentHeight = Height.Anchor (sh);
 				canSetHeight = false;
 			} else {
 				canSetHeight = true;
@@ -2328,7 +2325,7 @@ namespace Terminal.Gui {
 		/// <returns><c>true</c> if the width can be directly assigned, <c>false</c> otherwise.</returns>
 		public bool SetWidth (int desiredWidth, out int resultWidth)
 		{
-			return CanSetWidth (desiredWidth, out resultWidth, out _);
+			return CanSetWidth (desiredWidth, out resultWidth);
 		}
 
 		/// <summary>
@@ -2339,7 +2336,7 @@ namespace Terminal.Gui {
 		/// <returns><c>true</c> if the height can be directly assigned, <c>false</c> otherwise.</returns>
 		public bool SetHeight (int desiredHeight, out int resultHeight)
 		{
-			return CanSetHeight (desiredHeight, out resultHeight, out _);
+			return CanSetHeight (desiredHeight, out resultHeight);
 		}
 
 		/// <summary>
@@ -2349,7 +2346,10 @@ namespace Terminal.Gui {
 		/// <returns><c>true</c> if the width can be directly assigned, <c>false</c> otherwise.</returns>
 		public bool GetCurrentWidth (out int currentWidth)
 		{
-			return CanSetWidth (0, out _, out currentWidth);
+			SetRelativeLayout (SuperView == null ? Frame : SuperView.Frame);
+			currentWidth = Frame.Width;
+
+			return CanSetWidth (0, out _);
 		}
 
 		/// <summary>
@@ -2359,7 +2359,10 @@ namespace Terminal.Gui {
 		/// <returns><c>true</c> if the height can be directly assigned, <c>false</c> otherwise.</returns>
 		public bool GetCurrentHeight (out int currentHeight)
 		{
-			return CanSetHeight (0, out _, out currentHeight);
+			SetRelativeLayout (SuperView == null ? Frame : SuperView.Frame);
+			currentHeight = Frame.Height;
+
+			return CanSetHeight (0, out _);
 		}
 	}
 }

+ 31 - 22
Terminal.Gui/Core/Window.cs

@@ -23,6 +23,7 @@ namespace Terminal.Gui {
 	public class Window : Toplevel {
 		View contentView;
 		ustring title;
+		int padding;
 
 		/// <summary>
 		/// The title to be displayed for this window.
@@ -52,7 +53,7 @@ namespace Terminal.Gui {
 		/// <param name="frame">Superview-relative rectangle specifying the location and size</param>
 		/// <param name="title">Title</param>
 		/// <remarks>
-		/// This constructor intitalizes a Window with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Absolute"/>. Use constructors
+		/// This constructor initializes a Window with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Absolute"/>. Use constructors
 		/// that do not take <c>Rect</c> parameters to initialize a Window with <see cref="LayoutStyle.Computed"/>. 
 		/// </remarks>
 		public Window (Rect frame, ustring title = null) : this (frame, title, padding: 0)
@@ -64,7 +65,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="title">Title.</param>
 		/// <remarks>
-		///   This constructor intitalize a View with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Computed"/>. 
+		///   This constructor initializes a View with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Computed"/>. 
 		///   Use <see cref="View.X"/>, <see cref="View.Y"/>, <see cref="View.Width"/>, and <see cref="View.Height"/> properties to dynamically control the size and location of the view.
 		/// </remarks>
 		public Window (ustring title = null) : this (title, padding: 0)
@@ -76,7 +77,6 @@ namespace Terminal.Gui {
 		/// </summary>
 		public Window () : this (title: null) { }
 
-		int padding;
 		/// <summary>
 		/// Initializes a new instance of the <see cref="Window"/> using <see cref="LayoutStyle.Absolute"/> positioning with the specified frame for its location, with the specified frame padding,
 		/// and an optional title.
@@ -85,40 +85,48 @@ namespace Terminal.Gui {
 		/// <param name="padding">Number of characters to use for padding of the drawn frame.</param>
 		/// <param name="title">Title</param>
 		/// <remarks>
-		/// This constructor intitalizes a Window with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Absolute"/>. Use constructors
+		/// This constructor initializes a Window with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Absolute"/>. Use constructors
 		/// that do not take <c>Rect</c> parameters to initialize a Window with  <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Computed"/> 
 		/// </remarks>
 		public Window (Rect frame, ustring title = null, int padding = 0) : base (frame)
 		{
-			this.Title = title;
-			int wb = 2 * (1 + padding);
-			this.padding = padding;
-			var cFrame = new Rect (1 + padding, 1 + padding, frame.Width - wb, frame.Height - wb);
-			contentView = new ContentView (cFrame);
-			base.Add (contentView);
+			Initialize (title, frame, padding);
 		}
 
 		/// <summary>
-		/// Initializes a new instance of the <see cref="Window"/> using <see cref="LayoutStyle.Absolute"/> positioning with the specified frame for its location, with the specified frame padding,
+		/// Initializes a new instance of the <see cref="Window"/> using <see cref="LayoutStyle.Computed"/> positioning,
 		/// and an optional title.
 		/// </summary>
 		/// <param name="padding">Number of characters to use for padding of the drawn frame.</param>
 		/// <param name="title">Title.</param>
 		/// <remarks>
-		///   This constructor intitalize a View with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Computed"/>. 
+		///   This constructor initializes a View with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Computed"/>. 
 		///   Use <see cref="View.X"/>, <see cref="View.Y"/>, <see cref="View.Width"/>, and <see cref="View.Height"/> properties to dynamically control the size and location of the view.
 		/// </remarks>
 		public Window (ustring title = null, int padding = 0) : base ()
 		{
-			this.Title = title;
-			int wb = 1 + padding;
+			Initialize (title, Rect.Empty, padding);
+		}
+
+		void Initialize (ustring title, Rect frame, int padding = 0)
+		{
+			ColorScheme = Colors.Base;
+			Title = title;
+			int wb;
+			if (frame == Rect.Empty) {
+				wb = 1 + padding;
+				contentView = new ContentView () {
+					X = wb,
+					Y = wb,
+					Width = Dim.Fill (wb),
+					Height = Dim.Fill (wb)
+				};
+			} else {
+				wb = 2 * (1 + padding);
+				var cFrame = new Rect (1 + padding, 1 + padding, frame.Width - wb, frame.Height - wb);
+				contentView = new ContentView (cFrame);
+			}
 			this.padding = padding;
-			contentView = new ContentView () {
-				X = wb,
-				Y = wb,
-				Width = Dim.Fill (wb),
-				Height = Dim.Fill (wb)
-			};
 			base.Add (contentView);
 		}
 
@@ -179,7 +187,7 @@ namespace Terminal.Gui {
 
 			var savedClip = ClipToBounds ();
 
-			// Redraw our contenetView
+			// Redraw our contentView
 			// TODO: smartly constrict contentView.Bounds to just be what intersects with the 'bounds' we were passed
 			contentView.Redraw (contentView.Bounds);
 			Driver.Clip = savedClip;
@@ -216,7 +224,8 @@ namespace Terminal.Gui {
 			// a pending mouse event activated.
 
 			int nx, ny;
-			if (!dragPosition.HasValue && mouseEvent.Flags == (MouseFlags.Button1Pressed)) {
+			if (!dragPosition.HasValue && (mouseEvent.Flags == MouseFlags.Button1Pressed
+				|| mouseEvent.Flags == (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition))) {
 				// Only start grabbing if the user clicks on the title bar.
 				if (mouseEvent.Y == 0) {
 					start = new Point (mouseEvent.X, mouseEvent.Y);

+ 2 - 1
Terminal.Gui/Views/FrameView.cs

@@ -9,6 +9,7 @@
 //  - Does not support IEnumerable
 // Any udpates done here should probably be done in Window as well; TODO: Merge these classes
 
+using System;
 using System.Linq;
 using NStack;
 
@@ -50,7 +51,7 @@ namespace Terminal.Gui {
 		/// <param name="title">Title.</param>
 		public FrameView (Rect frame, ustring title = null) : base (frame)
 		{
-			var cFrame = new Rect (1, 1, frame.Width - 2, frame.Height - 2);
+			var cFrame = new Rect (1, 1, Math.Max (frame.Width - 2, 0), Math.Max (frame.Height - 2, 0));
 			this.title = title;
 			contentView = new ContentView (cFrame);
 			Initialize ();

+ 9 - 0
Terminal.Gui/Views/Label.cs

@@ -23,6 +23,7 @@ namespace Terminal.Gui {
 		/// <inheritdoc/>
 		public Label ()
 		{
+			Initialize ();
 		}
 
 		/// <inheritdoc/>
@@ -33,6 +34,7 @@ namespace Terminal.Gui {
 		/// <inheritdoc/>
 		public Label (ustring text) : base (text)
 		{
+			Initialize ();
 		}
 
 		/// <inheritdoc/>
@@ -43,12 +45,19 @@ namespace Terminal.Gui {
 		/// <inheritdoc/>
 		public Label (int x, int y, ustring text) : base (x, y, text)
 		{
+			Initialize ();
 		}
 
 		/// <inheritdoc/>
 		public Label (ustring text, TextDirection direction)
 			: base (text, direction)
 		{
+			Initialize ();
+		}
+
+		void Initialize ()
+		{
+			AutoSize = true;
 		}
 
 		/// <summary>

+ 8 - 2
UICatalog/Scenarios/LabelsAsButtons.cs

@@ -117,8 +117,14 @@ namespace UICatalog {
 				CanFocus = true,
 			};
 			Win.Add (removeLabel);
-			// This in intresting test case because `moveBtn` and below are laid out relative to this one!
-			removeLabel.Clicked += () => Win.Remove (removeLabel);
+			// This in interesting test case because `moveBtn` and below are laid out relative to this one!
+			removeLabel.Clicked += () => {
+				// Now this throw a InvalidOperationException on the TopologicalSort method as is expected.
+				//Win.Remove (removeLabel);
+
+				removeLabel.Visible = false;
+				Win.SetNeedsDisplay ();
+			};
 
 			var computedFrame = new FrameView ("Computed Layout") {
 				X = 0,

+ 112 - 0
UnitTests/AllViewsTests.cs

@@ -0,0 +1,112 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Xunit;
+using System.IO;
+
+namespace Terminal.Gui.Views {
+	public class AllViewsTests {
+		[Fact]
+		public void AllViews_Tests_All_Constructors ()
+		{
+			Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
+
+			foreach (var type in GetAllViewClassesCollection ()) {
+				Assert.True (Constructors_FullTest (type));
+			}
+
+			Application.Shutdown ();
+		}
+
+		public bool Constructors_FullTest (Type type)
+		{
+			foreach (var ctor in type.GetConstructors ()) {
+				if (type.IsGenericType && type.IsTypeDefinition) {
+					List<Type> gTypes = new List<Type> ();
+
+					foreach (var args in type.GetGenericArguments ()) {
+						gTypes.Add (typeof (object));
+					}
+					type = type.MakeGenericType (gTypes.ToArray ());
+
+					Assert.IsType (type, (View)Activator.CreateInstance (type));
+
+				} else {
+					ParameterInfo [] paramsInfo = ctor.GetParameters ();
+					Type paramType;
+					List<object> pTypes = new List<object> ();
+
+					if (type.IsGenericType) {
+						foreach (var args in type.GetGenericArguments ()) {
+							paramType = args.GetType ();
+							if (args.Name == "T") {
+								pTypes.Add (typeof (object));
+							} else {
+								AddArguments (paramType, pTypes);
+							}
+						}
+					}
+
+					foreach (var p in paramsInfo) {
+						paramType = p.ParameterType;
+						if (p.HasDefaultValue) {
+							pTypes.Add (p.DefaultValue);
+						} else {
+							AddArguments (paramType, pTypes);
+						}
+
+					}
+
+					if (type.IsGenericType && !type.IsTypeDefinition) {
+						Assert.IsType (type, (View)Activator.CreateInstance (type));
+					} else {
+						Assert.IsType (type, ctor.Invoke (pTypes.ToArray ()));
+					}
+				}
+			}
+
+			return true;
+		}
+
+		private static void AddArguments (Type paramType, List<object> pTypes)
+		{
+			if (paramType == typeof (Rect)) {
+				pTypes.Add (Rect.Empty);
+			} else if (paramType == typeof (NStack.ustring)) {
+				pTypes.Add (NStack.ustring.Empty);
+			} else if (paramType == typeof (int)) {
+				pTypes.Add (0);
+			} else if (paramType == typeof (bool)) {
+				pTypes.Add (true);
+			} else if (paramType.Name == "IList") {
+				pTypes.Add (new List<object> ());
+			} else if (paramType.Name == "View") {
+				var top = new Toplevel ();
+				var view = new View ();
+				top.Add (view);
+				pTypes.Add (view);
+			} else if (paramType.Name == "View[]") {
+				pTypes.Add (new View [] { });
+			} else if (paramType.Name == "Stream") {
+				pTypes.Add (new MemoryStream ());
+			} else if (paramType.Name == "String") {
+				pTypes.Add (string.Empty);
+			} else if (paramType.Name == "TreeView`1[T]") {
+				pTypes.Add (string.Empty);
+			} else {
+				pTypes.Add (null);
+			}
+		}
+
+		List<Type> GetAllViewClassesCollection ()
+		{
+			List<Type> types = new List<Type> ();
+			foreach (Type type in typeof (View).Assembly.GetTypes ()
+			 .Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsPublic && myType.IsSubclassOf (typeof (View)))) {
+				types.Add (type);
+			}
+			return types;
+		}
+	}
+}

+ 162 - 0
UnitTests/ViewTests.cs

@@ -1170,5 +1170,167 @@ namespace Terminal.Gui.Views {
 			// Shutdown must be called to safely clean up Application if Init has been called
 			Application.Shutdown ();
 		}
+
+		[Fact]
+		public void SetWidth_CanSetWidth ()
+		{
+			var top = new View () {
+				X = 0,
+				Y = 0,
+				Width = 80,
+			};
+
+			var v = new View () {
+				Width = Dim.Fill ()
+			};
+			top.Add (v);
+
+			Assert.False (v.SetWidth (70, out int rWidth));
+			Assert.Equal (70, rWidth);
+
+			v.Width = Dim.Fill (1);
+			Assert.False (v.SetWidth (70, out rWidth));
+			Assert.Equal (69, rWidth);
+
+			v.Width = null;
+			Assert.True (v.SetWidth (70, out rWidth));
+			Assert.Equal (70, rWidth);
+
+			v.IsInitialized = true;
+			v.Width = Dim.Fill (1);
+			Assert.Throws<ArgumentException> (() => v.Width = 75);
+			v.LayoutStyle = LayoutStyle.Absolute;
+			v.Width = 75;
+			Assert.True (v.SetWidth (60, out rWidth));
+			Assert.Equal (60, rWidth);
+		}
+
+		[Fact]
+		public void SetHeight_CanSetHeight ()
+		{
+			var top = new View () {
+				X = 0,
+				Y = 0,
+				Height = 20
+			};
+
+			var v = new View () {
+				Height = Dim.Fill ()
+			};
+			top.Add (v);
+
+			Assert.False (v.SetHeight (10, out int rHeight));
+			Assert.Equal (10, rHeight);
+
+			v.Height = Dim.Fill (1);
+			Assert.False (v.SetHeight (10, out rHeight));
+			Assert.Equal (9, rHeight);
+
+			v.Height = null;
+			Assert.True (v.SetHeight (10, out rHeight));
+			Assert.Equal (10, rHeight);
+
+			v.IsInitialized = true;
+			v.Height = Dim.Fill (1);
+			Assert.Throws<ArgumentException> (() => v.Height = 15);
+			v.LayoutStyle = LayoutStyle.Absolute;
+			v.Height = 15;
+			Assert.True (v.SetHeight (5, out rHeight));
+			Assert.Equal (5, rHeight);
+		}
+
+		[Fact]
+		public void GetCurrentWidth_CanSetWidth ()
+		{
+			var top = new View () {
+				X = 0,
+				Y = 0,
+				Width = 80,
+			};
+
+			var v = new View () {
+				Width = Dim.Fill ()
+			};
+			top.Add (v);
+
+			Assert.False (v.GetCurrentWidth (out int cWidth));
+			Assert.Equal (80, cWidth);
+
+			v.Width = Dim.Fill (1);
+			Assert.False (v.GetCurrentWidth (out cWidth));
+			Assert.Equal (79, cWidth);
+		}
+
+		[Fact]
+		public void GetCurrentHeight_CanSetHeight ()
+		{
+			var top = new View () {
+				X = 0,
+				Y = 0,
+				Height = 20
+			};
+
+			var v = new View () {
+				Height = Dim.Fill ()
+			};
+			top.Add (v);
+
+			Assert.False (v.GetCurrentHeight (out int cHeight));
+			Assert.Equal (20, cHeight);
+
+			v.Height = Dim.Fill (1);
+			Assert.False (v.GetCurrentHeight (out cHeight));
+			Assert.Equal (19, cHeight);
+		}
+
+		[Fact]
+		public void AutoSize_False_ResizeView_Is_Always_False ()
+		{
+			var label = new Label () { AutoSize = false };
+
+			label.Text = "New text";
+
+			Assert.False (label.AutoSize);
+			Assert.Equal ("{X=0,Y=0,Width=0,Height=0}", label.Bounds.ToString ());
+		}
+
+		[Fact]
+		public void AutoSize_True_ResizeView_With_Dim_Absolute ()
+		{
+			var label = new Label ();
+
+			label.Text = "New text";
+
+			Assert.True (label.AutoSize);
+			Assert.Equal ("{X=0,Y=0,Width=8,Height=1}", label.Bounds.ToString ());
+		}
+
+		[Fact]
+		public void AutoSize_True_ResizeView_With_Dim_Fill ()
+		{
+			var win = new Window (new Rect (0, 0, 30, 80), "");
+			var label = new Label () { Width = Dim.Fill (), Height = Dim.Fill () };
+			win.Add (label);
+
+			label.Text = "New text\nNew line";
+			win.LayoutSubviews ();
+
+			Assert.True (label.AutoSize);
+			Assert.Equal ("{X=0,Y=0,Width=28,Height=78}", label.Bounds.ToString ());
+		}
+
+		[Fact]
+		public void AutoSize_True_SetWidthHeight_With_Dim_Fill_And_Dim_Absolute ()
+		{
+			var win = new Window (new Rect (0, 0, 30, 80), "");
+			var label = new Label () { Width = Dim.Fill () };
+			win.Add (label);
+
+			label.Text = "New text\nNew line";
+			win.LayoutSubviews ();
+
+			Assert.True (label.AutoSize);
+			Assert.Equal ("{X=0,Y=0,Width=28,Height=2}", label.Bounds.ToString ());
+		}
 	}
 }