Browse Source

More work on directional key handling.

Added tests, most passing - one failing in both arrows and tabs.
pull/72/merge
Steven Kirk 11 years ago
parent
commit
296c7a5f85
  1. 34
      Perspex.Input/KeyboardNavigationHandler.cs
  2. 791
      Tests/Perspex.Input.UnitTests/KeyboardNavigationTests_Arrows.cs
  3. 27
      Tests/Perspex.Input.UnitTests/KeyboardNavigationTests_Tab.cs
  4. 1
      Tests/Perspex.Input.UnitTests/Perspex.Input.UnitTests.csproj

34
Perspex.Input/KeyboardNavigationHandler.cs

@ -61,21 +61,15 @@ namespace Perspex.Input
if (container != null)
{
KeyboardNavigationMode mode;
if (direction == FocusNavigationDirection.Next || direction == FocusNavigationDirection.Previous)
{
mode = KeyboardNavigation.GetTabNavigation((InputElement)container);
}
else
{
mode = KeyboardNavigation.GetDirectionalNavigation((InputElement)container);
}
bool forward = direction == FocusNavigationDirection.Next ||
var tab = direction == FocusNavigationDirection.Next ||
direction == FocusNavigationDirection.Previous;
var forward = direction == FocusNavigationDirection.Next ||
direction == FocusNavigationDirection.Last ||
direction == FocusNavigationDirection.Right ||
direction == FocusNavigationDirection.Down;
var mode = tab ?
KeyboardNavigation.GetTabNavigation((InputElement)container) :
KeyboardNavigation.GetDirectionalNavigation((InputElement)container);
switch (mode)
{
@ -88,7 +82,7 @@ namespace Perspex.Input
case KeyboardNavigationMode.Contained:
return GetNextInContainer(element, container, direction);
default:
return GetFirstInNextContainer(container, forward);
return tab ? GetFirstInNextContainer(container, forward) : null;
}
}
else
@ -202,13 +196,17 @@ namespace Perspex.Input
IInputElement container,
FocusNavigationDirection direction)
{
var descendent = GetFocusableDescendents(element).FirstOrDefault();
if (descendent != null)
if (direction == FocusNavigationDirection.Next || direction == FocusNavigationDirection.Down)
{
return descendent;
var descendent = GetFocusableDescendents(element).FirstOrDefault();
if (descendent != null)
{
return descendent;
}
}
else if (container != null)
if (container != null)
{
var navigable = container as INavigableContainer;

791
Tests/Perspex.Input.UnitTests/KeyboardNavigationTests_Arrows.cs

@ -0,0 +1,791 @@
// -----------------------------------------------------------------------
// <copyright file="KeyboardNavigationTests_Arrows.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Input.UnitTests
{
using Perspex.Controls;
using Xunit;
public class KeyboardNavigationTests_Arrows
{
[Fact]
public void Down_Continue_Returns_Down_Control_In_Container()
{
StackPanel container;
Button current;
Button next;
var top = new StackPanel
{
Children = new Controls
{
(container = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Continue,
Children = new Controls
{
new Button { Name = "Button1" },
(current = new Button { Name = "Button2" }),
(next = new Button { Name = "Button3" }),
}
}),
new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Continue,
Children = new Controls
{
new Button { Name = "Button4" },
new Button { Name = "Button5" },
new Button { Name = "Button6" },
}
},
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Down);
Assert.Equal(next, result);
}
[Fact]
public void Down_Continue_Returns_First_Control_In_Down_Sibling_Container()
{
StackPanel container;
Button current;
Button next;
var top = new StackPanel
{
Children = new Controls
{
(container = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Continue,
Children = new Controls
{
new Button { Name = "Button1" },
new Button { Name = "Button2" },
(current = new Button { Name = "Button3" }),
}
}),
new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Continue,
Children = new Controls
{
(next = new Button { Name = "Button4" }),
new Button { Name = "Button5" },
new Button { Name = "Button6" },
}
},
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Down);
Assert.Equal(next, result);
}
[Fact]
public void Down_Continue_Returns_Down_Sibling()
{
StackPanel container;
Button current;
Button next;
var top = new StackPanel
{
Children = new Controls
{
(container = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Continue,
Children = new Controls
{
new Button { Name = "Button1" },
new Button { Name = "Button2" },
(current = new Button { Name = "Button3" }),
}
}),
(next = new Button { Name = "Button4" }),
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Down);
Assert.Equal(next, result);
}
[Fact]
public void Down_Continue_Returns_First_Control_In_Down_Uncle_Container()
{
StackPanel container;
Button current;
Button next;
var top = new StackPanel
{
Children = new Controls
{
new StackPanel
{
Children = new Controls
{
(container = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Continue,
Children = new Controls
{
new Button { Name = "Button1" },
new Button { Name = "Button2" },
(current = new Button { Name = "Button3" }),
}
}),
},
},
new StackPanel
{
Children = new Controls
{
(next = new Button { Name = "Button4" }),
new Button { Name = "Button5" },
new Button { Name = "Button6" },
}
},
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Down);
Assert.Equal(next, result);
}
[Fact]
public void Down_Continue_Returns_Child_Of_Top_Level()
{
Button next;
var top = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Continue,
Children = new Controls
{
(next = new Button { Name = "Button1" }),
}
};
var result = KeyboardNavigationHandler.GetNext(top, FocusNavigationDirection.Down);
Assert.Equal(next, result);
}
[Fact]
public void Down_Continue_Wraps()
{
StackPanel container;
Button current;
Button next;
var top = new StackPanel
{
Children = new Controls
{
new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Continue,
Children = new Controls
{
(container = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Continue,
Children = new Controls
{
(next = new Button { Name = "Button1" }),
new Button { Name = "Button2" },
new Button { Name = "Button3" },
}
}),
},
},
new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Continue,
Children = new Controls
{
new Button { Name = "Button4" },
new Button { Name = "Button5" },
(current = new Button { Name = "Button6" }),
}
},
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Down);
Assert.Equal(next, result);
}
[Fact]
public void Down_Cycle_Returns_Down_Control_In_Container()
{
StackPanel container;
Button current;
Button next;
var top = new StackPanel
{
Children = new Controls
{
(container = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Cycle,
Children = new Controls
{
new Button { Name = "Button1" },
(current = new Button { Name = "Button2" }),
(next = new Button { Name = "Button3" }),
}
}),
new StackPanel
{
Children = new Controls
{
new Button { Name = "Button4" },
new Button { Name = "Button5" },
new Button { Name = "Button6" },
}
},
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Down);
Assert.Equal(next, result);
}
[Fact]
public void Down_Cycle_Wraps_To_First()
{
StackPanel container;
Button current;
Button next;
var top = new StackPanel
{
Children = new Controls
{
(container = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Cycle,
Children = new Controls
{
(next = new Button { Name = "Button1" }),
new Button { Name = "Button2" },
(current = new Button { Name = "Button3" }),
}
}),
new StackPanel
{
Children = new Controls
{
new Button { Name = "Button4" },
new Button { Name = "Button5" },
new Button { Name = "Button6" },
}
},
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Down);
Assert.Equal(next, result);
}
[Fact]
public void Down_Contained_Returns_Down_Control_In_Container()
{
StackPanel container;
Button current;
Button next;
var top = new StackPanel
{
Children = new Controls
{
(container = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Contained,
Children = new Controls
{
new Button { Name = "Button1" },
(current = new Button { Name = "Button2" }),
(next = new Button { Name = "Button3" }),
}
}),
new StackPanel
{
Children = new Controls
{
new Button { Name = "Button4" },
new Button { Name = "Button5" },
new Button { Name = "Button6" },
}
},
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Down);
Assert.Equal(next, result);
}
[Fact]
public void Down_Contained_Stops_At_End()
{
StackPanel container;
Button current;
Button next;
var top = new StackPanel
{
Children = new Controls
{
(container = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Contained,
Children = new Controls
{
(next = new Button { Name = "Button1" }),
new Button { Name = "Button2" },
(current = new Button { Name = "Button3" }),
}
}),
new StackPanel
{
Children = new Controls
{
new Button { Name = "Button4" },
new Button { Name = "Button5" },
new Button { Name = "Button6" },
}
},
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Down);
Assert.Null(result);
}
[Fact]
public void Down_None_Does_Nothing()
{
StackPanel container;
Button current;
var top = new StackPanel
{
Children = new Controls
{
(container = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.None,
Children = new Controls
{
new Button { Name = "Button1" },
(current = new Button { Name = "Button2" }),
new Button { Name = "Button3" },
}
}),
new StackPanel
{
Children = new Controls
{
new Button { Name = "Button4" },
new Button { Name = "Button5" },
new Button { Name = "Button6" },
}
},
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Down);
Assert.Null(result);
}
[Fact]
public void Up_Continue_Returns_Up_Control_In_Container()
{
StackPanel container;
Button current;
Button next;
var top = new StackPanel
{
Children = new Controls
{
(container = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Continue,
Children = new Controls
{
new Button { Name = "Button1" },
(next = new Button { Name = "Button2" }),
(current = new Button { Name = "Button3" }),
}
}),
new StackPanel
{
Children = new Controls
{
new Button { Name = "Button4" },
new Button { Name = "Button5" },
new Button { Name = "Button6" },
}
},
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Up);
Assert.Equal(next, result);
}
[Fact]
public void Up_Continue_Returns_Last_Control_In_Up_Sibling_Container()
{
StackPanel container;
Button current;
Button next;
var top = new StackPanel
{
Children = new Controls
{
(container = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Continue,
Children = new Controls
{
new Button { Name = "Button1" },
new Button { Name = "Button2" },
(next = new Button { Name = "Button3" }),
}
}),
new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Continue,
Children = new Controls
{
(current = new Button { Name = "Button4" }),
new Button { Name = "Button5" },
new Button { Name = "Button6" },
}
},
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Up);
Assert.Equal(next, result);
}
[Fact]
public void Up_Continue_Returns_Last_Child_Of_Sibling()
{
StackPanel container;
Button current;
Button next;
var top = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Continue,
Children = new Controls
{
(container = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Continue,
Children = new Controls
{
new Button { Name = "Button1" },
new Button { Name = "Button2" },
(next = new Button { Name = "Button3" }),
}
}),
(current = new Button { Name = "Button4" }),
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Up);
Assert.Equal(next, result);
}
[Fact]
public void Up_Continue_Returns_Last_Control_In_Up_Nephew_Container()
{
StackPanel container;
Button current;
Button next;
var top = new StackPanel
{
Children = new Controls
{
new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Continue,
Children = new Controls
{
(container = new StackPanel
{
Children = new Controls
{
new Button { Name = "Button1" },
new Button { Name = "Button2" },
(next = new Button { Name = "Button3" }),
}
}),
},
},
new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Continue,
Children = new Controls
{
(current = new Button { Name = "Button4" }),
new Button { Name = "Button5" },
new Button { Name = "Button6" },
}
},
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Up);
Assert.Equal(next, result);
}
[Fact]
public void Up_Continue_Wraps()
{
StackPanel container;
Button current;
Button next;
var top = new StackPanel
{
Children = new Controls
{
new StackPanel
{
Children = new Controls
{
(container = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Continue,
Children = new Controls
{
(current = new Button { Name = "Button1" }),
new Button { Name = "Button2" },
new Button { Name = "Button3" },
}
}),
},
},
new StackPanel
{
Children = new Controls
{
new Button { Name = "Button4" },
new Button { Name = "Button5" },
(next = new Button { Name = "Button6" }),
}
},
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Up);
Assert.Equal(next, result);
}
[Fact]
public void Up_Cycle_Returns_Up_Control_In_Container()
{
StackPanel container;
Button current;
Button next;
var top = new StackPanel
{
Children = new Controls
{
(container = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Cycle,
Children = new Controls
{
(next = new Button { Name = "Button1" }),
(current = new Button { Name = "Button2" }),
new Button { Name = "Button3" },
}
}),
new StackPanel
{
Children = new Controls
{
new Button { Name = "Button4" },
new Button { Name = "Button5" },
new Button { Name = "Button6" },
}
},
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Up);
Assert.Equal(next, result);
}
[Fact]
public void Up_Cycle_Wraps_To_Last()
{
StackPanel container;
Button current;
Button next;
var top = new StackPanel
{
Children = new Controls
{
(container = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Cycle,
Children = new Controls
{
(current = new Button { Name = "Button1" }),
new Button { Name = "Button2" },
(next = new Button { Name = "Button3" }),
}
}),
new StackPanel
{
Children = new Controls
{
new Button { Name = "Button4" },
new Button { Name = "Button5" },
new Button { Name = "Button6" },
}
},
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Up);
Assert.Equal(next, result);
}
[Fact]
public void Up_Contained_Returns_Up_Control_In_Container()
{
StackPanel container;
Button current;
Button next;
var top = new StackPanel
{
Children = new Controls
{
(container = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Contained,
Children = new Controls
{
(next = new Button { Name = "Button1" }),
(current = new Button { Name = "Button2" }),
new Button { Name = "Button3" },
}
}),
new StackPanel
{
Children = new Controls
{
new Button { Name = "Button4" },
new Button { Name = "Button5" },
new Button { Name = "Button6" },
}
},
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Up);
Assert.Equal(next, result);
}
[Fact]
public void Up_Contained_Stops_At_Beginning()
{
StackPanel container;
Button current;
var top = new StackPanel
{
Children = new Controls
{
(container = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Contained,
Children = new Controls
{
(current = new Button { Name = "Button1" }),
new Button { Name = "Button2" },
new Button { Name = "Button3" },
}
}),
new StackPanel
{
Children = new Controls
{
new Button { Name = "Button4" },
new Button { Name = "Button5" },
new Button { Name = "Button6" },
}
},
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Up);
Assert.Null(result);
}
[Fact]
public void Up_Contained_Doesnt_Select_Child_Control()
{
Decorator current;
var top = new StackPanel
{
[KeyboardNavigation.DirectionalNavigationProperty] = KeyboardNavigationMode.Contained,
Children = new Controls
{
(current = new Decorator
{
Focusable = true,
Child = new Button(),
})
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Up);
Assert.Null(result);
}
}
}

27
Tests/Perspex.Input.UnitTests/KeyboardNavigationTests_Tab.cs

@ -449,7 +449,7 @@ namespace Perspex.Input.UnitTests
}
[Fact]
public void Next_Never_Moves_To_Next_Container()
public void Next_None_Moves_To_Next_Container()
{
StackPanel container;
Button current;
@ -487,7 +487,7 @@ namespace Perspex.Input.UnitTests
}
[Fact]
public void Next_Never_Skips_Container()
public void Next_None_Skips_Container()
{
StackPanel container;
Button current;
@ -982,5 +982,28 @@ namespace Perspex.Input.UnitTests
Assert.Equal(next, result);
}
[Fact]
public void Previous_Contained_Doesnt_Select_Child_Control()
{
Decorator current;
var top = new StackPanel
{
[KeyboardNavigation.TabNavigationProperty] = KeyboardNavigationMode.Contained,
Children = new Controls
{
(current = new Decorator
{
Focusable = true,
Child = new Button(),
})
}
};
var result = KeyboardNavigationHandler.GetNext(current, FocusNavigationDirection.Previous);
Assert.Null(result);
}
}
}

1
Tests/Perspex.Input.UnitTests/Perspex.Input.UnitTests.csproj

@ -60,6 +60,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="KeyboardNavigationTests_Arrows.cs" />
<Compile Include="KeyboardNavigationTests_Tab.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>

Loading…
Cancel
Save