Added TabIndex and TabStop properties.

This commit is contained in:
BDisp
2020-07-06 17:29:22 +01:00
parent f7a2c9e35f
commit 5741cf3171
2 changed files with 415 additions and 70 deletions

View File

@@ -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);

View File

@@ -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 };
v.Added += (View e) => {
Assert.True (v.SuperView == e);
};
r.Add (v1, v2, v3);
v.Removed += (View e) => {
Assert.True (v.SuperView == null);
};
Assert.True (r.Subviews.IndexOf (v1) == 0);
Assert.True (r.Subviews.IndexOf (v2) == 1);
Assert.True (r.Subviews.IndexOf (v3) == 2);
t.Add (v);
Assert.True (t.Subviews.Count == 1);
Assert.True (r.TabIndexes.IndexOf (v1) == 0);
Assert.True (r.TabIndexes.IndexOf (v2) == 1);
Assert.True (r.TabIndexes.IndexOf (v3) == 2);
t.Remove (v);
Assert.True (t.Subviews.Count == 0);
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 };
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 };
r.Add (v1, v2, v3);
r.FocusNext ();
Assert.False (v1.HasFocus);
Assert.False (v2.HasFocus);
Assert.False (v3.HasFocus);
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);
}
}
}