Fixed nested tabgroup nav!

This commit is contained in:
Tig
2024-08-28 23:47:43 -07:00
parent cd89d5c6be
commit 1359e43066
6 changed files with 179 additions and 71 deletions

View File

@@ -300,7 +300,21 @@ public static partial class Application // Keyboard handling
// TODO: This OverlapppedTop tomfoolery goes away in addressing #2491
if (ApplicationOverlapped.OverlappedTop is null && Current is { })
{
return Current.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup);
if (Current.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup))
{
return true;
};
//// Go back down the focus chain and focus the first TabGroup
//View []? views = Current.GetSubviewFocusChain (NavigationDirection.Forward, TabBehavior.TabGroup);
//if (views.Length > 0)
//{
// View []? subViews = views [0].GetSubviewFocusChain (NavigationDirection.Forward, TabBehavior.TabStop);
// return subViews? [0].SetFocus ();
//}
return false;
}
ApplicationOverlapped.OverlappedMoveNext ();
@@ -316,7 +330,21 @@ public static partial class Application // Keyboard handling
// TODO: This OverlapppedTop tomfoolery goes away in addressing #2491
if (ApplicationOverlapped.OverlappedTop is null && Current is { })
{
return Current.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup);
if (Current.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup))
{
return true;
};
//// Go back down the focus chain and focus the first TabGroup
//View []? views = Current.GetSubviewFocusChain (NavigationDirection.Backward, TabBehavior.TabGroup);
//if (views.Length > 0)
//{
// View []? subViews = views [0].GetSubviewFocusChain (NavigationDirection.Backward, TabBehavior.TabStop);
// return subViews? [0].SetFocus ();
//}
return false;
}
ApplicationOverlapped.OverlappedMovePrevious ();

View File

@@ -37,6 +37,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
return true;
}
// AdvanceFocus did not advance
View [] index = GetSubviewFocusChain (direction, behavior);
if (index.Length == 0)
@@ -44,6 +45,49 @@ public partial class View // Focus and cross-view navigation management (TabStop
return false;
}
if (behavior == TabBehavior.TabGroup)
{
if (direction == NavigationDirection.Forward && focused == index [^1] && SuperView is null)
{
// We're at the top of the focus chain. Go back down the focus chain and focus the first TabGroup
View [] views = GetSubviewFocusChain (NavigationDirection.Forward, TabBehavior.TabGroup);
if (views.Length > 0)
{
View [] subViews = views [0].GetSubviewFocusChain (NavigationDirection.Forward, TabBehavior.TabStop);
if (subViews.Length > 0)
{
if (subViews [0].SetFocus ())
{
return true;
}
}
}
}
if (direction == NavigationDirection.Backward && focused == index [0])
{
// We're at the bottom of the focus chain
View [] views = GetSubviewFocusChain (NavigationDirection.Forward, TabBehavior.TabGroup);
if (views.Length > 0)
{
View [] subViews = views [^1].GetSubviewFocusChain (NavigationDirection.Forward, TabBehavior.TabStop);
if (subViews.Length > 0)
{
if (subViews [0].SetFocus ())
{
return true;
}
}
}
}
}
int focusedIndex = index.IndexOf (Focused); // Will return -1 if Focused can't be found or is null
var next = 0;
@@ -57,21 +101,20 @@ public partial class View // Focus and cross-view navigation management (TabStop
// We're moving beyond the last subview
// Determine if focus should remain in this focus chain, or move to the superview's focus chain
// BUGBUG: The logic below is sketchy and barely works. In fact, it doesn't work propertly for all nested TabGroups.
// - If we are TabStop and our SuperView has at least one other TabStop subview, move to the SuperView's chain
// If we are TabStop and our SuperView has at least one other TabStop subview, move to the SuperView's chain
if (TabStop == TabBehavior.TabStop && SuperView is { } && SuperView.GetSubviewFocusChain (direction, behavior).Length > 1)
{
return false;
}
// - If we are TabGroup and our SuperView has at least one other TabGroup subview, move to the SuperView's chain
if (TabStop == TabBehavior.TabGroup && SuperView is { TabStop: TabBehavior.TabGroup })
// TabGroup is special-cased.
if (focused?.TabStop == TabBehavior.TabGroup)
{
if (behavior == TabBehavior.TabGroup)
if (SuperView?.GetSubviewFocusChain (direction, TabBehavior.TabGroup)?.Length > 0)
{
// Wrap to first focusable views
// BUGBUG: This should do a Restore Focus instead
index = GetSubviewFocusChain (direction, null);
// Our superview has a TabGroup subview; signal we couldn't move so we nav out to it
return false;
}
}
}
@@ -560,7 +603,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
if (appFocused is { } || appFocused == this)
{
Application.Navigation.SetFocused (newFocusedView ?? SuperView);
Application.Navigation.SetFocused (newFocusedView ?? SuperView);
}
}
@@ -635,7 +678,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
/// <param name="behavior"></param>
/// <returns></returns>
/// GetScopedTabIndexes
private View [] GetSubviewFocusChain (NavigationDirection direction, TabBehavior? behavior)
internal View [] GetSubviewFocusChain (NavigationDirection direction, TabBehavior? behavior)
{
IEnumerable<View>? fitleredSubviews;

View File

@@ -189,7 +189,6 @@ public class DatePicker : View
Date = date;
_dateLabel = new Label { X = 0, Y = 0, Text = "Date: " };
CanFocus = true;
TabStop = TabBehavior.TabGroup;
_calendar = new TableView
{

View File

@@ -64,15 +64,19 @@ public class Navigation : Scenario
View overlappedView2 = CreateOverlappedView (3, Pos.Center () + 10, Pos.Center () + 5);
// BUGBUG: F6 through nested tab groups doesn't work yet.
#if NESTED_TABGROUPS
var overlappedInOverlapped1 = CreateOverlappedView (4, 1, 4);
overlappedView2.Add (overlappedInOverlapped1);
var overlappedInOverlapped2 = CreateOverlappedView (5, 10, 7);
overlappedView2.Add (overlappedInOverlapped2);
#endif
CheckBox cb = new ()
{
X = Pos.AnchorEnd (),
Y = Pos.AnchorEnd (),
Title = "Checkbo_x"
};
overlappedView2.Add (cb);
testFrame.Add (overlappedView1);
testFrame.Add (overlappedView2);

View File

@@ -73,33 +73,35 @@ public class AdvanceFocusTests ()
}
[Fact]
public void AdvanceFocus_Compound_Subview ()
public void AdvanceFocus_Compound_Subview_TabStop ()
{
TabBehavior behavior = TabBehavior.TabStop;
var top = new View { Id = "top", CanFocus = true };
var compoundSubview = new View
{
CanFocus = true,
Id = "compoundSubview"
Id = "compoundSubview",
TabStop = behavior
};
var v1 = new View { Id = "v1", CanFocus = true };
var v2 = new View { Id = "v2", CanFocus = true };
var v3 = new View { Id = "v3", CanFocus = false };
var v1 = new View { Id = "v1", CanFocus = true, TabStop = behavior };
var v2 = new View { Id = "v2", CanFocus = true, TabStop = behavior };
var v3 = new View { Id = "v3", CanFocus = false, TabStop = behavior };
compoundSubview.Add (v1, v2, v3);
top.Add (compoundSubview);
// Cycle through v1 & v2
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
top.AdvanceFocus (NavigationDirection.Forward, behavior);
Assert.True (v1.HasFocus);
Assert.False (v2.HasFocus);
Assert.False (v3.HasFocus);
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
top.AdvanceFocus (NavigationDirection.Forward, behavior);
Assert.False (v1.HasFocus);
Assert.True (v2.HasFocus);
Assert.False (v3.HasFocus);
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
top.AdvanceFocus (NavigationDirection.Forward, behavior);
Assert.True (v1.HasFocus);
Assert.False (v2.HasFocus);
Assert.False (v3.HasFocus);
@@ -108,6 +110,7 @@ public class AdvanceFocusTests ()
View otherSubview = new ()
{
CanFocus = true,
TabStop = behavior,
Id = "otherSubview"
};
@@ -118,15 +121,15 @@ public class AdvanceFocusTests ()
Assert.False (v1.HasFocus);
// Cycle through v1 & v2
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
top.AdvanceFocus (NavigationDirection.Forward, behavior);
Assert.True (v1.HasFocus);
Assert.False (v2.HasFocus);
Assert.False (v3.HasFocus);
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
top.AdvanceFocus (NavigationDirection.Forward, behavior);
Assert.False (v1.HasFocus);
Assert.True (v2.HasFocus);
Assert.False (v3.HasFocus);
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
top.AdvanceFocus (NavigationDirection.Forward, behavior);
Assert.False (v1.HasFocus);
Assert.False (v2.HasFocus);
Assert.False (v3.HasFocus);
@@ -134,7 +137,7 @@ public class AdvanceFocusTests ()
Assert.True (otherSubview.HasFocus);
// v2 was previously focused down the compoundSubView focus chain
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
top.AdvanceFocus (NavigationDirection.Forward, behavior);
Assert.False (v1.HasFocus);
Assert.True (v2.HasFocus);
Assert.False (v3.HasFocus);
@@ -142,6 +145,76 @@ public class AdvanceFocusTests ()
top.Dispose ();
}
[Fact]
public void AdvanceFocus_Compound_Subview_TabGroup ()
{
var top = new View { Id = "top", CanFocus = true, TabStop = TabBehavior.TabGroup };
var compoundSubview = new View
{
CanFocus = true,
Id = "compoundSubview",
TabStop = TabBehavior.TabGroup
};
var tabStopView = new View { Id = "tabStop", CanFocus = true, TabStop = TabBehavior.TabStop };
var tabGroupView1 = new View { Id = "tabGroup1", CanFocus = true, TabStop = TabBehavior.TabGroup };
var tabGroupView2 = new View { Id = "tabGroup2", CanFocus = true, TabStop = TabBehavior.TabGroup };
compoundSubview.Add (tabStopView, tabGroupView1, tabGroupView2);
top.Add (compoundSubview);
top.SetFocus ();
Assert.True (tabStopView.HasFocus);
// TabGroup should cycle to tabGroup1 then tabGroup2
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup);
Assert.False (tabStopView.HasFocus);
Assert.True (tabGroupView1.HasFocus);
Assert.False (tabGroupView2.HasFocus);
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup);
Assert.False (tabStopView.HasFocus);
Assert.False (tabGroupView1.HasFocus);
Assert.True (tabGroupView2.HasFocus);
// Add another TabGroup subview
View otherTabGroupSubview = new ()
{
CanFocus = true,
TabStop = TabBehavior.TabGroup,
Id = "otherTabGroupSubview"
};
top.Add (otherTabGroupSubview);
// Adding a focusable subview causes advancefocus
Assert.True (otherTabGroupSubview.HasFocus);
Assert.False (tabStopView.HasFocus);
// TagBroup navs to the other subview
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup);
Assert.Equal (compoundSubview, top.Focused);
Assert.True (tabStopView.HasFocus);
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup);
Assert.Equal (compoundSubview, top.Focused);
Assert.True (tabGroupView1.HasFocus);
top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup);
Assert.Equal (compoundSubview, top.Focused);
Assert.True (tabGroupView2.HasFocus);
// Now go backwards
top.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup);
Assert.Equal (compoundSubview, top.Focused);
Assert.True (tabGroupView1.HasFocus);
top.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup);
Assert.Equal (otherTabGroupSubview, top.Focused);
Assert.True (otherTabGroupSubview.HasFocus);
top.Dispose ();
}
[Fact]
public void AdvanceFocus_NoStop_And_CanFocus_True_No_Focus ()
{

View File

@@ -206,7 +206,10 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
break;
case TabBehavior.TabGroup:
Application.OnKeyDown (Key.F6);
if (!Application.OnKeyDown (Key.F6))
{
view.SetFocus ();
}
break;
case null:
@@ -316,7 +319,6 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
// View.Focused - No subviews
[Fact]
[Trait ("BUGBUG", "Fix in Issue #3444")]
public void Focused_NoSubviews ()
{
var view = new View ();
@@ -324,47 +326,6 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
view.CanFocus = true;
view.SetFocus ();
Assert.True (view.HasFocus);
Assert.Null (view.Focused); // BUGBUG: Should be view
}
[Fact]
public void FocusNearestView_Ensure_Focus_Ordered ()
{
Application.Top = Application.Current = new Toplevel ();
var win = new Window ();
var winSubview = new View { CanFocus = true, Text = "WindowSubview" };
win.Add (winSubview);
Application.Current.Add (win);
var frm = new FrameView ();
var frmSubview = new View { CanFocus = true, Text = "FrameSubview" };
frm.Add (frmSubview);
Application.Current.Add (frm);
Application.Current.SetFocus ();
Assert.Equal (winSubview, Application.Current.MostFocused);
Application.OnKeyDown (Key.Tab); // Move to the next TabStop. There is none. So we should stay.
Assert.Equal (winSubview, Application.Current.MostFocused);
Application.OnKeyDown (Key.F6);
Assert.Equal (frmSubview, Application.Current.MostFocused);
Application.OnKeyDown (Key.Tab);
Assert.Equal (frmSubview, Application.Current.MostFocused);
Application.OnKeyDown (Key.F6);
Assert.Equal (winSubview, Application.Current.MostFocused);
Application.OnKeyDown (Key.F6.WithShift);
Assert.Equal (frmSubview, Application.Current.MostFocused);
Application.OnKeyDown (Key.F6.WithShift);
Assert.Equal (winSubview, Application.Current.MostFocused);
Application.Current.Dispose ();
}
[Fact]