Forráskód Böngészése

Merge branch 'develop' into v1_14_0

Tigger Kindel 1 éve
szülő
commit
568c251ed7

+ 2 - 1
Terminal.Gui/Core/Application.cs

@@ -1053,7 +1053,8 @@ namespace Terminal.Gui {
 					MdiTop.OnAllChildClosed ();
 				} else {
 					SetCurrentAsTop ();
-					Current.OnEnter (Current);
+					runState.Toplevel.OnLeave (Current);
+					Current.OnEnter (runState.Toplevel);
 				}
 				Refresh ();
 			}

+ 17 - 15
Terminal.Gui/Core/Border.cs

@@ -284,7 +284,7 @@ namespace Terminal.Gui {
 			/// <inheritdoc/>
 			public override void OnCanFocusChanged ()
 			{
-				if (Border.Child != null) {
+				if (Border?.Child != null) {
 					Border.Child.CanFocus = CanFocus;
 				}
 				base.OnCanFocusChanged ();
@@ -375,8 +375,10 @@ namespace Terminal.Gui {
 		public Color BorderBrush {
 			get => borderBrush != null ? (Color)borderBrush : (Color)(-1);
 			set {
-				borderBrush = value;
-				OnBorderChanged ();
+				if (Enum.IsDefined (typeof (Color), value)) {
+					borderBrush = value;
+					OnBorderChanged ();
+				}
 			}
 		}
 
@@ -386,8 +388,10 @@ namespace Terminal.Gui {
 		public Color Background {
 			get => background != null ? (Color)background : (Color)(-1);
 			set {
-				background = value;
-				OnBorderChanged ();
+				if (Enum.IsDefined (typeof (Color), value)) {
+					background = value;
+					OnBorderChanged ();
+				}
 			}
 		}
 
@@ -445,12 +449,10 @@ namespace Terminal.Gui {
 
 		private void Parent_Removed (View obj)
 		{
-			if (borderBrush != null)
-			{
+			if (borderBrush != null) {
 				BorderBrush = default;
 			}
-			if (background != null)
-			{
+			if (background != null) {
 				Background = default;
 			}
 			child.Removed -= Parent_Removed;
@@ -800,7 +802,7 @@ namespace Terminal.Gui {
 			SetBorderBrush (driver);
 
 			// Draw the upper BorderThickness
-			for (int r = frame.Y;
+			for (int r = Math.Max (frame.Y, 0);
 				r < Math.Min (frame.Y + borderThickness.Top, frame.Bottom); r++) {
 				for (int c = frame.X;
 					c < Math.Min (frame.Right, driver.Cols); c++) {
@@ -810,7 +812,7 @@ namespace Terminal.Gui {
 			}
 
 			// Draw the left BorderThickness
-			for (int r = Math.Min (frame.Y + borderThickness.Top, frame.Bottom);
+			for (int r = Math.Max (Math.Min (frame.Y + borderThickness.Top, frame.Bottom), 0);
 				r < Math.Min (frame.Bottom - borderThickness.Bottom, driver.Rows); r++) {
 				for (int c = frame.X;
 					c < Math.Min (frame.X + borderThickness.Left, frame.Right); c++) {
@@ -820,7 +822,7 @@ namespace Terminal.Gui {
 			}
 
 			// Draw the right BorderThickness
-			for (int r = Math.Min (frame.Y + borderThickness.Top, frame.Bottom);
+			for (int r = Math.Max (Math.Min (frame.Y + borderThickness.Top, frame.Bottom), 0);
 				r < Math.Min (frame.Bottom - borderThickness.Bottom, driver.Rows); r++) {
 				for (int c = Math.Max (frame.Right - borderThickness.Right, frame.X);
 					c < Math.Min (frame.Right, driver.Cols); c++) {
@@ -842,7 +844,7 @@ namespace Terminal.Gui {
 			SetBackground (driver);
 
 			// Draw the upper Padding
-			for (int r = frame.Y + borderThickness.Top;
+			for (int r = Math.Max (frame.Y + borderThickness.Top, 0);
 				r < Math.Min (frame.Y + sumThickness.Top, frame.Bottom - borderThickness.Bottom); r++) {
 				for (int c = frame.X + borderThickness.Left;
 					c < Math.Min (frame.Right - borderThickness.Right, driver.Cols); c++) {
@@ -852,7 +854,7 @@ namespace Terminal.Gui {
 			}
 
 			// Draw the left Padding
-			for (int r = frame.Y + sumThickness.Top;
+			for (int r = Math.Max (frame.Y + sumThickness.Top, 0);
 				r < Math.Min (frame.Bottom - sumThickness.Bottom, driver.Rows); r++) {
 				for (int c = frame.X + borderThickness.Left;
 					c < Math.Min (frame.X + sumThickness.Left, frame.Right - borderThickness.Right); c++) {
@@ -862,7 +864,7 @@ namespace Terminal.Gui {
 			}
 
 			// Draw the right Padding
-			for (int r = frame.Y + sumThickness.Top;
+			for (int r = Math.Max (frame.Y + sumThickness.Top, 0);
 				r < Math.Min (frame.Bottom - sumThickness.Bottom, driver.Rows); r++) {
 				for (int c = Math.Max (frame.Right - sumThickness.Right, frame.X + sumThickness.Left);
 					c < Math.Max (frame.Right - borderThickness.Right, frame.X + sumThickness.Left); c++) {

+ 1 - 0
Terminal.Gui/Core/Toplevel.cs

@@ -552,6 +552,7 @@ namespace Terminal.Gui {
 		///<inheritdoc/>
 		public override void Add (View view)
 		{
+			CanFocus = true;
 			AddMenuStatusBar (view);
 			base.Add (view);
 		}

+ 0 - 3
Terminal.Gui/Core/View.cs

@@ -995,9 +995,6 @@ namespace Terminal.Gui {
 			view.tabIndex = -1;
 			SetNeedsLayout ();
 			SetNeedsDisplay ();
-			if (subviews.Count < 1) {
-				CanFocus = false;
-			}
 			foreach (var v in subviews) {
 				if (v.Frame.IntersectsWith (touched))
 					view.SetNeedsDisplay ();

+ 0 - 3
Terminal.Gui/Core/Window.cs

@@ -270,9 +270,6 @@ namespace Terminal.Gui {
 			SetNeedsDisplay ();
 			contentView.Remove (view);
 
-			if (contentView.InternalSubviews.Count < 1) {
-				CanFocus = false;
-			}
 			RemoveMenuStatusBar (view);
 			if (view != contentView && Focused == null) {
 				FocusFirst ();

+ 1 - 1
Terminal.Gui/Views/ContextMenu.cs

@@ -162,7 +162,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		public void Hide ()
 		{
-			menuBar.CleanUp ();
+			menuBar?.CleanUp ();
 			Dispose ();
 		}
 

+ 1 - 1
UICatalog/UICatalog.csproj

@@ -2,7 +2,7 @@
   <PropertyGroup>
     <OutputType>Exe</OutputType>
     <TargetFramework>net7.0</TargetFramework>
-    <LangVersion>8.0</LangVersion>
+    <LangVersion>9.0</LangVersion>
     <StartupObject>UICatalog.UICatalogApp</StartupObject>
     <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
     <!-- Version numbers are automatically updated by gitversion when a release is released -->

+ 24 - 0
UnitTests/Core/BorderTests.cs

@@ -619,5 +619,29 @@ At 0,0
 ████████████████████
 At 0,4              ", output);
 		}
+
+		[Fact]
+		public void BorderBrush_Background_Only_Is_Set_To_Valid_Color_Enum ()
+		{
+			var border = new Border ();
+			Assert.Equal ((Color)(-1), border.BorderBrush);
+			Assert.Equal ((Color)(-1), border.Background);
+			Assert.Null (border.GetFieldValue<string> ("borderBrush"));
+			Assert.Null (border.GetFieldValue<string> ("background"));
+
+			border.BorderBrush = (Color)(-1);
+			border.Background = (Color)(-1);
+			Assert.Equal ((Color)(-1), border.BorderBrush);
+			Assert.Equal ((Color)(-1), border.Background);
+			Assert.Null (border.GetFieldValue<Color?> ("borderBrush"));
+			Assert.Null (border.GetFieldValue<Color?> ("background"));
+
+			border.BorderBrush = Color.Blue;
+			border.Background = Color.White;
+			Assert.Equal (Color.Blue, border.BorderBrush);
+			Assert.Equal (Color.White, border.Background);
+			Assert.Equal (Color.Blue, border.GetFieldValue<Color> ("borderBrush"));
+			Assert.Equal (Color.White, border.GetFieldValue<Color?> ("background"));
+		}
 	}
 }

+ 69 - 4
UnitTests/Menus/ContextMenuTests.cs

@@ -49,16 +49,21 @@ namespace Terminal.Gui.MenuTests {
 			Assert.NotNull (cm.Host);
 		}
 
-		[Fact]
-		[AutoInitShutdown]
-		public void Show_Hide_IsShow ()
+		private ContextMenu Create_ContextMenu_With_Two_MenuItem (int x, int y)
 		{
-			var cm = new ContextMenu (10, 5,
+			return new ContextMenu (x, y,
 				new MenuBarItem (new MenuItem [] {
 					new MenuItem ("One", "", null),
 					new MenuItem ("Two", "", null)
 				})
 			);
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void Show_Hide_IsShow ()
+		{
+			var cm = Create_ContextMenu_With_Two_MenuItem (10, 5);
 
 			cm.Show ();
 			Assert.True (ContextMenu.IsShow);
@@ -902,5 +907,65 @@ namespace Terminal.Gui.MenuTests {
 			Assert.True (top.Subviews [1].ProcessKey (new KeyEvent (Key.F10 | Key.ShiftMask, new KeyModifiers ())));
 			Assert.Null (tf.ContextMenu.MenuBar);
 		}
+
+		[Fact, AutoInitShutdown]
+		public void RequestStop_While_ContextMenu_Is_Open_Does_Not_Throws ()
+		{
+			var cm = Create_ContextMenu_With_Two_MenuItem (10, 5);
+			var top = Application.Top;
+			var isMenuAllClosed = false;
+			MenuBarItem mi = null;
+			var iterations = -1;
+			Application.Iteration += () => {
+				iterations++;
+				if (iterations == 0) {
+					cm.Show ();
+					Assert.True (ContextMenu.IsShow);
+					mi = cm.MenuBar.Menus [0];
+					mi.Action = () => {
+						var dialog1 = new Dialog ();
+						Application.Run (dialog1);
+
+						Assert.False (ContextMenu.IsShow);
+						Assert.True (isMenuAllClosed);
+					};
+
+					cm.MenuBar.MenuAllClosed += () => isMenuAllClosed = true;
+
+				} else if (iterations == 1) {
+					mi.Action ();
+				} else if (iterations == 2) {
+					Application.RequestStop ();
+				} else if (iterations == 3) {
+					isMenuAllClosed = false;
+					cm.Show ();
+					Assert.True (ContextMenu.IsShow);
+
+					cm.MenuBar.MenuAllClosed += () => isMenuAllClosed = true;
+				} else if (iterations == 4) {
+					var exception = Record.Exception (() => Application.RequestStop ());
+					Assert.Null (exception);
+				} else {
+					Application.RequestStop ();
+				}
+			};
+
+			var isTopClosed = false;
+			top.Closing += (_) => {
+				var dialog2 = new Dialog ();
+				Application.Run (dialog2);
+
+				Assert.False (ContextMenu.IsShow);
+				Assert.True (isMenuAllClosed);
+
+				isTopClosed = true;
+			};
+
+			Application.Run ();
+
+			Assert.True (isTopClosed);
+			Assert.False (ContextMenu.IsShow);
+			Assert.True (isMenuAllClosed);
+		}
 	}
 }

+ 8 - 0
UnitTests/ReflectionTools.cs

@@ -32,4 +32,12 @@ public static class ReflectionTools {
 		}
 		return null;
 	}
+
+	public static T GetFieldValue<T> (this object obj, string name)
+	{
+		// Set the flags so that private and public fields from instances will be found
+		var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
+		var field = obj.GetType ().GetField (name, bindingFlags);
+		return (T)field?.GetValue (obj);
+	}
 }

+ 109 - 1
UnitTests/TopLevels/ToplevelTests.cs

@@ -1009,7 +1009,98 @@ namespace Terminal.Gui.TopLevelTests {
 			Application.End (rs);
 
 			Assert.True (isEnter);
-			Assert.True (isLeave);  // Leave event is now also invoked on Application.End allowing preform same output action
+			Assert.False (isLeave);  // Leave event cannot be trigger because it v.Enter was performed and v is focused
+			Assert.True (v.HasFocus);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void OnEnter_OnLeave_Triggered_On_Application_Begin_End_With_More_Toplevels ()
+		{
+			var iterations = 0;
+			var steps = new int [5];
+			var isEnterTop = false;
+			var isLeaveTop = false;
+			var vt = new View ();
+			var top = Application.Top;
+			var diag = new Dialog ();
+
+			vt.Enter += (e) => {
+				iterations++;
+				isEnterTop = true;
+				if (iterations == 1) {
+					steps [0] = iterations;
+					Assert.Null (e.View);
+				} else {
+					steps [4] = iterations;
+					Assert.Equal (diag, e.View);
+				}
+			};
+			vt.Leave += (e) => {
+				iterations++;
+				steps [1] = iterations;
+				isLeaveTop = true;
+				Assert.Equal (diag, e.View);
+			};
+			top.Add (vt);
+
+			Assert.False (vt.CanFocus);
+			var exception = Record.Exception (() => top.OnEnter (top));
+			Assert.Null (exception);
+			exception = Record.Exception (() => top.OnLeave (top));
+			Assert.Null (exception);
+
+			vt.CanFocus = true;
+			Application.Begin (top);
+
+			Assert.True (isEnterTop);
+			Assert.False (isLeaveTop);
+
+			isEnterTop = false;
+			var isEnterDiag = false;
+			var isLeaveDiag = false;
+			var vd = new View ();
+			vd.Enter += (e) => {
+				iterations++;
+				steps [2] = iterations;
+				isEnterDiag = true;
+				Assert.Null (e.View);
+			};
+			vd.Leave += (e) => {
+				iterations++;
+				steps [3] = iterations;
+				isLeaveDiag = true;
+				Assert.Equal (top, e.View);
+			};
+			diag.Add (vd);
+
+			Assert.False (vd.CanFocus);
+			exception = Record.Exception (() => diag.OnEnter (diag));
+			Assert.Null (exception);
+			exception = Record.Exception (() => diag.OnLeave (diag));
+			Assert.Null (exception);
+
+			vd.CanFocus = true;
+			var rs = Application.Begin (diag);
+
+			Assert.True (isEnterDiag);
+			Assert.False (isLeaveDiag);
+			Assert.False (isEnterTop);
+			Assert.True (isLeaveTop);
+
+			isEnterDiag = false;
+			isLeaveTop = false;
+			Application.End (rs);
+
+			Assert.False (isEnterDiag);
+			Assert.True (isLeaveDiag);
+			Assert.True (isEnterTop);
+			Assert.False (isLeaveTop);  // Leave event cannot be trigger because it v.Enter was performed and v is focused
+			Assert.True (vt.HasFocus);
+			Assert.Equal (1, steps [0]);
+			Assert.Equal (2, steps [1]);
+			Assert.Equal (3, steps [2]);
+			Assert.Equal (4, steps [3]);
+			Assert.Equal (5, steps [^1]);
 		}
 
 		[Fact, AutoInitShutdown]
@@ -1031,5 +1122,22 @@ namespace Terminal.Gui.TopLevelTests {
 			Application.Driver.GetCursorVisibility (out cursor);
 			Assert.Equal (CursorVisibility.Invisible, cursor);
 		}
+
+		[Fact, AutoInitShutdown]
+		public void Activating_MenuBar_By_Alt_Key_Does_Not_Throw ()
+		{
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem ("Child", new MenuItem [] {
+					new MenuItem ("_Create Child", "", null)
+				})
+			});
+			var topChild = new Toplevel ();
+			topChild.Add (menu);
+			Application.Top.Add (topChild);
+			Application.Begin (Application.Top);
+
+			var exception = Record.Exception (() => topChild.ProcessHotKey (new KeyEvent (Key.AltMask, new KeyModifiers { Alt = true })));
+			Assert.Null (exception);
+		}
 	}
 }

+ 2 - 2
UnitTests/UnitTests.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <TargetFramework>net7.0</TargetFramework>
     <!-- https://stackoverflow.com/questions/294216/why-does-c-sharp-forbid-generic-attribute-types -->
@@ -18,7 +18,7 @@
     <DefineConstants>TRACE;DEBUG_IDISPOSABLE</DefineConstants>
   </PropertyGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
     <PackageReference Include="ReportGenerator" Version="5.1.24" />
     <PackageReference Include="System.Collections" Version="4.3.0" />
     <PackageReference Include="xunit" Version="2.5.0" />