瀏覽代碼

Added TabIndex and TabStop properties.

BDisp 5 年之前
父節點
當前提交
5741cf3171
共有 2 個文件被更改,包括 415 次插入70 次删除
  1. 78 57
      Terminal.Gui/Core/View.cs
  2. 337 13
      UnitTests/ViewTests.cs

+ 78 - 57
Terminal.Gui/Core/View.cs

@@ -124,16 +124,6 @@ namespace Terminal.Gui {
 
 		TextFormatter viewText;
 
-		/// <summary>
-		/// Event fired when a subview is being added to this view.
-		/// </summary>
-		public Action<View> Added;
-
-		/// <summary>
-		/// Event fired when a subview is being removed from this view.
-		/// </summary>
-		public Action<View> Removed;
-
 		/// <summary>
 		/// Event fired when the view gets focus.
 		/// </summary>
@@ -201,13 +191,61 @@ namespace Terminal.Gui {
 		internal IList<View> InternalSubviews => subviews ?? empty;
 
 		// This is null, and allocated on demand.
-		List<View> tabIndex;
+		List<View> tabIndexes;
 
 		/// <summary>
 		/// This returns a tab index list of the subviews contained by this view.
 		/// </summary>
-		/// <value>The tabIndex.</value>
-		public IList<View> TabIndex => tabIndex == null ? empty : tabIndex.AsReadOnly ();
+		/// <value>The tabIndexes.</value>
+		public IList<View> TabIndexes => tabIndexes == null ? empty : tabIndexes.AsReadOnly ();
+
+		int tabIndex = -1;
+
+		/// <summary>
+		/// Indicates the index of the current <see cref="View"/> from the <see cref="TabIndexes"/> list.
+		/// </summary>
+		public int TabIndex {
+			get { return tabIndex; }
+			set {
+				if (!CanFocus || SuperView?.tabIndexes == null || SuperView?.tabIndexes.Count == 1 || tabIndex == value) {
+					return;
+				}
+				tabIndex = value > SuperView.tabIndexes.Count - 1 ? SuperView.tabIndexes.Count - 1 : value < 0 ? 0 : value;
+				SuperView.tabIndexes.Remove (this);
+				SuperView.tabIndexes.Insert (tabIndex, this);
+			}
+		}
+
+		bool tabStop = true;
+
+		/// <summary>
+		/// This only be <c>true</c> if the <see cref="CanFocus"/> is also <c>true</c> and the focus can be avoided by setting this to <c>false</c>
+		/// </summary>
+		public bool TabStop {
+			get { return tabStop; }
+			set {
+				if (tabStop == value) {
+					return;
+				}
+				tabStop = CanFocus && value;
+			}
+		}
+
+		/// <inheritdoc/>
+		public override bool CanFocus {
+			get => base.CanFocus;
+			set {
+				if (base.CanFocus != value) {
+					base.CanFocus = value;
+					if (!value && tabIndex > -1) {
+						tabIndex = -1;
+					}
+					if (!value && tabStop) {
+						tabStop = false;
+					}
+				}
+			}
+		}
 
 		internal Rect NeedDisplay { get; private set; } = Rect.Empty;
 
@@ -570,15 +608,17 @@ namespace Terminal.Gui {
 			if (subviews == null) {
 				subviews = new List<View> ();
 			}
-			if (tabIndex == null) {
-				tabIndex = new List<View> ();
+			if (tabIndexes == null) {
+				tabIndexes = new List<View> ();
 			}
 			subviews.Add (view);
-			tabIndex.Add (view);
+			tabIndexes.Add (view);
 			view.container = this;
-			OnAdded (view);
-			if (view.CanFocus)
+			if (view.CanFocus) {
 				CanFocus = true;
+				view.tabIndex = tabIndexes.IndexOf (view);
+			}
+
 			SetNeedsLayout ();
 			SetNeedsDisplay ();
 		}
@@ -608,7 +648,7 @@ namespace Terminal.Gui {
 
 			while (subviews.Count > 0) {
 				Remove (subviews [0]);
-				Remove (tabIndex [0]);
+				Remove (tabIndexes [0]);
 			}
 		}
 
@@ -626,10 +666,9 @@ namespace Terminal.Gui {
 			SetNeedsDisplay ();
 			var touched = view.Frame;
 			subviews.Remove (view);
-			tabIndex.Remove (view);
+			tabIndexes.Remove (view);
 			view.container = null;
-			OnRemoved (view);
-
+			view.tabIndex = -1;
 			if (subviews.Count < 1)
 				this.CanFocus = false;
 
@@ -971,24 +1010,6 @@ namespace Terminal.Gui {
 			public View View { get; set; }
 		}
 
-		/// <summary>
-		/// Method invoked  when a subview is being added to this view.
-		/// </summary>
-		/// <param name="view">The subview being added.</param>
-		public virtual void OnAdded (View view)
-		{
-			view.Added?.Invoke (this);
-		}
-
-		/// <summary>
-		/// Method invoked when a subview is being removed from this view.
-		/// </summary>
-		/// <param name="view">The subview being removed.</param>
-		public virtual void OnRemoved (View view)
-		{
-			view.Removed?.Invoke (this);
-		}
-
 		/// <inheritdoc/>
 		public override bool OnEnter (View view)
 		{
@@ -1317,13 +1338,13 @@ namespace Terminal.Gui {
 		/// </summary>
 		public void FocusFirst ()
 		{
-			if (tabIndex == null) {
+			if (tabIndexes == null) {
 				SuperView?.SetFocus (this);
 				return;
 			}
 
-			foreach (var view in tabIndex) {
-				if (view.CanFocus) {
+			foreach (var view in tabIndexes) {
+				if (view.CanFocus && view.tabStop) {
 					SetFocus (view);
 					return;
 				}
@@ -1335,16 +1356,16 @@ namespace Terminal.Gui {
 		/// </summary>
 		public void FocusLast ()
 		{
-			if (tabIndex == null) {
+			if (tabIndexes == null) {
 				SuperView?.SetFocus (this);
 				return;
 			}
 
-			for (int i = tabIndex.Count; i > 0;) {
+			for (int i = tabIndexes.Count; i > 0;) {
 				i--;
 
-				View v = tabIndex [i];
-				if (v.CanFocus) {
+				View v = tabIndexes [i];
+				if (v.CanFocus && v.tabStop) {
 					SetFocus (v);
 					return;
 				}
@@ -1358,7 +1379,7 @@ namespace Terminal.Gui {
 		public bool FocusPrev ()
 		{
 			FocusDirection = Direction.Backward;
-			if (tabIndex == null || tabIndex.Count == 0)
+			if (tabIndexes == null || tabIndexes.Count == 0)
 				return false;
 
 			if (focused == null) {
@@ -1366,9 +1387,9 @@ namespace Terminal.Gui {
 				return focused != null;
 			}
 			int focused_idx = -1;
-			for (int i = tabIndex.Count; i > 0;) {
+			for (int i = tabIndexes.Count; i > 0;) {
 				i--;
-				View w = tabIndex [i];
+				View w = tabIndexes [i];
 
 				if (w.HasFocus) {
 					if (w.FocusPrev ())
@@ -1376,10 +1397,10 @@ namespace Terminal.Gui {
 					focused_idx = i;
 					continue;
 				}
-				if (w.CanFocus && focused_idx != -1) {
+				if (w.CanFocus && focused_idx != -1 && w.tabStop) {
 					focused.SetHasFocus (false, w);
 
-					if (w != null && w.CanFocus)
+					if (w != null && w.CanFocus && w.tabStop)
 						w.FocusLast ();
 
 					SetFocus (w);
@@ -1400,17 +1421,17 @@ namespace Terminal.Gui {
 		public bool FocusNext ()
 		{
 			FocusDirection = Direction.Forward;
-			if (tabIndex == null || tabIndex.Count == 0)
+			if (tabIndexes == null || tabIndexes.Count == 0)
 				return false;
 
 			if (focused == null) {
 				FocusFirst ();
 				return focused != null;
 			}
-			int n = tabIndex.Count;
+			int n = tabIndexes.Count;
 			int focused_idx = -1;
 			for (int i = 0; i < n; i++) {
-				View w = tabIndex [i];
+				View w = tabIndexes [i];
 
 				if (w.HasFocus) {
 					if (w.FocusNext ())
@@ -1418,10 +1439,10 @@ namespace Terminal.Gui {
 					focused_idx = i;
 					continue;
 				}
-				if (w.CanFocus && focused_idx != -1) {
+				if (w.CanFocus && focused_idx != -1 && w.tabStop) {
 					focused.SetHasFocus (false, w);
 
-					if (w != null && w.CanFocus)
+					if (w != null && w.CanFocus && w.tabStop)
 						w.FocusFirst ();
 
 					SetFocus (w);

+ 337 - 13
UnitTests/ViewTests.cs

@@ -137,24 +137,348 @@ namespace Terminal.Gui {
 		}
 
 		[Fact]
-		public void Added_Removing ()
+		public void Subviews_TabIndexes_AreEqual ()
 		{
-			var v = new View (new Rect (0, 0, 10, 24));
-			var t = new View ();
+			var r = new View ();
+			var v1 = new View () { CanFocus = true };
+			var v2 = new View () { CanFocus = true };
+			var v3 = new View () { CanFocus = true };
+
+			r.Add (v1, v2, v3);
+
+			Assert.True (r.Subviews.IndexOf (v1) == 0);
+			Assert.True (r.Subviews.IndexOf (v2) == 1);
+			Assert.True (r.Subviews.IndexOf (v3) == 2);
+
+			Assert.True (r.TabIndexes.IndexOf (v1) == 0);
+			Assert.True (r.TabIndexes.IndexOf (v2) == 1);
+			Assert.True (r.TabIndexes.IndexOf (v3) == 2);
+
+			Assert.Equal (r.Subviews.IndexOf (v1), r.TabIndexes.IndexOf (v1));
+			Assert.Equal (r.Subviews.IndexOf (v2), r.TabIndexes.IndexOf (v2));
+			Assert.Equal (r.Subviews.IndexOf (v3), r.TabIndexes.IndexOf (v3));
+		}
+
+		[Fact]
+		public void BringSubviewToFront_Subviews_vs_TabIndexes ()
+		{
+			var r = new View ();
+			var v1 = new View () { CanFocus = true };
+			var v2 = new View () { CanFocus = true };
+			var v3 = new View () { CanFocus = true };
+
+			r.Add (v1, v2, v3);
+
+			r.BringSubviewToFront (v1);
+			Assert.True (r.Subviews.IndexOf (v1) == 2);
+			Assert.True (r.Subviews.IndexOf (v2) == 0);
+			Assert.True (r.Subviews.IndexOf (v3) == 1);
+
+			Assert.True (r.TabIndexes.IndexOf (v1) == 0);
+			Assert.True (r.TabIndexes.IndexOf (v2) == 1);
+			Assert.True (r.TabIndexes.IndexOf (v3) == 2);
+		}
+
+		[Fact]
+		public void BringSubviewForward_Subviews_vs_TabIndexes ()
+		{
+			var r = new View ();
+			var v1 = new View () { CanFocus = true };
+			var v2 = new View () { CanFocus = true };
+			var v3 = new View () { CanFocus = true };
+
+			r.Add (v1, v2, v3);
+
+			r.BringSubviewForward (v1);
+			Assert.True (r.Subviews.IndexOf (v1) == 1);
+			Assert.True (r.Subviews.IndexOf (v2) == 0);
+			Assert.True (r.Subviews.IndexOf (v3) == 2);
+
+			Assert.True (r.TabIndexes.IndexOf (v1) == 0);
+			Assert.True (r.TabIndexes.IndexOf (v2) == 1);
+			Assert.True (r.TabIndexes.IndexOf (v3) == 2);
+		}
+
+		[Fact]
+		public void SendSubviewToBack_Subviews_vs_TabIndexes ()
+		{
+			var r = new View ();
+			var v1 = new View () { CanFocus = true };
+			var v2 = new View () { CanFocus = true };
+			var v3 = new View () { CanFocus = true };
+
+			r.Add (v1, v2, v3);
+
+			r.SendSubviewToBack (v3);
+			Assert.True (r.Subviews.IndexOf (v1) == 1);
+			Assert.True (r.Subviews.IndexOf (v2) == 2);
+			Assert.True (r.Subviews.IndexOf (v3) == 0);
+
+			Assert.True (r.TabIndexes.IndexOf (v1) == 0);
+			Assert.True (r.TabIndexes.IndexOf (v2) == 1);
+			Assert.True (r.TabIndexes.IndexOf (v3) == 2);
+		}
+
+		[Fact]
+		public void SendSubviewBackwards_Subviews_vs_TabIndexes ()
+		{
+			var r = new View ();
+			var v1 = new View () { CanFocus = true };
+			var v2 = new View () { CanFocus = true };
+			var v3 = new View () { CanFocus = true };
+
+			r.Add (v1, v2, v3);
+
+			r.SendSubviewBackwards (v3);
+			Assert.True (r.Subviews.IndexOf (v1) == 0);
+			Assert.True (r.Subviews.IndexOf (v2) == 2);
+			Assert.True (r.Subviews.IndexOf (v3) == 1);
+
+			Assert.True (r.TabIndexes.IndexOf (v1) == 0);
+			Assert.True (r.TabIndexes.IndexOf (v2) == 1);
+			Assert.True (r.TabIndexes.IndexOf (v3) == 2);
+		}
+
+		[Fact]
+		public void TabIndex_Set_CanFocus_ValidValues ()
+		{
+			var r = new View ();
+			var v1 = new View () { CanFocus = true };
+			var v2 = new View () { CanFocus = true };
+			var v3 = new View () { CanFocus = true };
+
+			r.Add (v1, v2, v3);
+
+			v1.TabIndex = 1;
+			Assert.True (r.Subviews.IndexOf (v1) == 0);
+			Assert.True (r.TabIndexes.IndexOf (v1) == 1);
+
+			v1.TabIndex = 2;
+			Assert.True (r.Subviews.IndexOf (v1) == 0);
+			Assert.True (r.TabIndexes.IndexOf (v1) == 2);
+		}
+
+		[Fact]
+		public void TabIndex_Set_CanFocus_HigherValues ()
+		{
+			var r = new View ();
+			var v1 = new View () { CanFocus = true };
+			var v2 = new View () { CanFocus = true };
+			var v3 = new View () { CanFocus = true };
+
+			r.Add (v1, v2, v3);
+
+			v1.TabIndex = 3;
+			Assert.True (r.Subviews.IndexOf (v1) == 0);
+			Assert.True (r.TabIndexes.IndexOf (v1) == 2);
+		}
+
+		[Fact]
+		public void TabIndex_Set_CanFocus_LowerValues ()
+		{
+			var r = new View ();
+			var v1 = new View () { CanFocus = true };
+			var v2 = new View () { CanFocus = true };
+			var v3 = new View () { CanFocus = true };
 
-			v.Added += (View e) => {
-				Assert.True (v.SuperView == e);
-			};
+			r.Add (v1, v2, v3);
+
+			v1.TabIndex = -1;
+			Assert.True (r.Subviews.IndexOf (v1) == 0);
+			Assert.True (r.TabIndexes.IndexOf (v1) == 0);
+		}
+
+		[Fact]
+		public void TabIndex_Set_CanFocus_False ()
+		{
+			var r = new View ();
+			var v1 = new View () { CanFocus = true };
+			var v2 = new View () { CanFocus = true };
+			var v3 = new View () { CanFocus = true };
+
+			r.Add (v1, v2, v3);
+
+			v1.CanFocus = false;
+			v1.TabIndex = 0;
+			Assert.True (r.Subviews.IndexOf (v1) == 0);
+			Assert.True (r.TabIndexes.IndexOf (v1) == 0);
+			Assert.Equal (-1, v1.TabIndex);
+		}
+
+		[Fact]
+		public void TabIndex_Set_CanFocus_False_To_True ()
+		{
+			var r = new View ();
+			var v1 = new View ();
+			var v2 = new View () { CanFocus = true };
+			var v3 = new View () { CanFocus = true };
+
+			r.Add (v1, v2, v3);
+
+			v1.CanFocus = true;
+			v1.TabIndex = 1;
+			Assert.True (r.Subviews.IndexOf (v1) == 0);
+			Assert.True (r.TabIndexes.IndexOf (v1) == 1);
+		}
+
+		[Fact]
+		public void TabStop_And_CanFocus_Are_All_True ()
+		{
+			var r = new View ();
+			var v1 = new View () { CanFocus = true };
+			var v2 = new View () { CanFocus = true };
+			var v3 = new View () { CanFocus = true };
+
+			r.Add (v1, v2, v3);
+
+			r.FocusNext ();
+			Assert.True (v1.HasFocus);
+			Assert.False (v2.HasFocus);
+			Assert.False (v3.HasFocus);
+			r.FocusNext ();
+			Assert.False (v1.HasFocus);
+			Assert.True (v2.HasFocus);
+			Assert.False (v3.HasFocus);
+			r.FocusNext ();
+			Assert.False (v1.HasFocus);
+			Assert.False (v2.HasFocus);
+			Assert.True (v3.HasFocus);
+		}
+
+		[Fact]
+		public void TabStop_Are_All_True_And_CanFocus_Are_All_False ()
+		{
+			var r = new View ();
+			var v1 = new View ();
+			var v2 = new View ();
+			var v3 = new View ();
+
+			r.Add (v1, v2, v3);
+
+			r.FocusNext ();
+			Assert.False (v1.HasFocus);
+			Assert.False (v2.HasFocus);
+			Assert.False (v3.HasFocus);
+			r.FocusNext ();
+			Assert.False (v1.HasFocus);
+			Assert.False (v2.HasFocus);
+			Assert.False (v3.HasFocus);
+			r.FocusNext ();
+			Assert.False (v1.HasFocus);
+			Assert.False (v2.HasFocus);
+			Assert.False (v3.HasFocus);
+		}
+
+		[Fact]
+		public void TabStop_Are_All_False_And_CanFocus_Are_All_True ()
+		{
+			var r = new View ();
+			var v1 = new View () { CanFocus = true, TabStop = false };
+			var v2 = new View () { CanFocus = true, TabStop = false };
+			var v3 = new View () { CanFocus = true, TabStop = false };
+
+			r.Add (v1, v2, v3);
+
+			r.FocusNext ();
+			Assert.False (v1.HasFocus);
+			Assert.False (v2.HasFocus);
+			Assert.False (v3.HasFocus);
+			r.FocusNext ();
+			Assert.False (v1.HasFocus);
+			Assert.False (v2.HasFocus);
+			Assert.False (v3.HasFocus);
+			r.FocusNext ();
+			Assert.False (v1.HasFocus);
+			Assert.False (v2.HasFocus);
+			Assert.False (v3.HasFocus);
+		}
+
+		[Fact]
+		public void TabStop_And_CanFocus_Mixed_And_BothFalse ()
+		{
+			var r = new View ();
+			var v1 = new View () { CanFocus = true, TabStop = false };
+			var v2 = new View () { CanFocus = false, TabStop = true };
+			var v3 = new View () { CanFocus = false, TabStop = false };
+
+			r.Add (v1, v2, v3);
+
+			r.FocusNext ();
+			Assert.False (v1.HasFocus);
+			Assert.False (v2.HasFocus);
+			Assert.False (v3.HasFocus);
+			r.FocusNext ();
+			Assert.False (v1.HasFocus);
+			Assert.False (v2.HasFocus);
+			Assert.False (v3.HasFocus);
+			r.FocusNext ();
+			Assert.False (v1.HasFocus);
+			Assert.False (v2.HasFocus);
+			Assert.False (v3.HasFocus);
+		}
+
+		[Fact]
+		public void TabStop_All_True_And_Changing_CanFocus_Later ()
+		{
+			var r = new View ();
+			var v1 = new View ();
+			var v2 = new View ();
+			var v3 = new View ();
+
+			r.Add (v1, v2, v3);
+
+			r.FocusNext ();
+			Assert.False (v1.HasFocus);
+			Assert.False (v2.HasFocus);
+			Assert.False (v3.HasFocus);
+
+			v1.CanFocus = true;
+			r.FocusNext ();
+			Assert.True (v1.HasFocus);
+			Assert.False (v2.HasFocus);
+			Assert.False (v3.HasFocus);
+			v2.CanFocus = true;
+			r.FocusNext ();
+			Assert.False (v1.HasFocus);
+			Assert.True (v2.HasFocus);
+			Assert.False (v3.HasFocus);
+			v3.CanFocus = true;
+			r.FocusNext ();
+			Assert.False (v1.HasFocus);
+			Assert.False (v2.HasFocus);
+			Assert.True (v3.HasFocus);
+		}
+
+		[Fact]
+		public void TabStop_All_False_And_All_True_And_Changing_TabStop_Later ()
+		{
+			var r = new View ();
+			var v1 = new View () { CanFocus = true, TabStop = false };
+			var v2 = new View () { CanFocus = true, TabStop = false };
+			var v3 = new View () { CanFocus = true, TabStop = false };
 
-			v.Removed += (View e) => {
-				Assert.True (v.SuperView == null);
-			};
+			r.Add (v1, v2, v3);
 
-			t.Add (v);
-			Assert.True (t.Subviews.Count == 1);
+			r.FocusNext ();
+			Assert.False (v1.HasFocus);
+			Assert.False (v2.HasFocus);
+			Assert.False (v3.HasFocus);
 
-			t.Remove (v);
-			Assert.True (t.Subviews.Count == 0);
+			v1.TabStop = true;
+			r.FocusNext ();
+			Assert.True (v1.HasFocus);
+			Assert.False (v2.HasFocus);
+			Assert.False (v3.HasFocus);
+			v2.TabStop = true;
+			r.FocusNext ();
+			Assert.False (v1.HasFocus);
+			Assert.True (v2.HasFocus);
+			Assert.False (v3.HasFocus);
+			v3.TabStop = true;
+			r.FocusNext ();
+			Assert.False (v1.HasFocus);
+			Assert.False (v2.HasFocus);
+			Assert.True (v3.HasFocus);
 		}
 	}
 }