diff --git a/Perspex.Input/KeyboardNavigationHandler.cs b/Perspex.Input/KeyboardNavigationHandler.cs
index 018027067c..b8891da8a0 100644
--- a/Perspex.Input/KeyboardNavigationHandler.cs
+++ b/Perspex.Input/KeyboardNavigationHandler.cs
@@ -65,6 +65,8 @@ namespace Perspex.Input
case KeyboardNavigationMode.Cycle:
return GetNextInContainer(element, container) ??
GetDescendents(container).FirstOrDefault();
+ case KeyboardNavigationMode.Contained:
+ return GetNextInContainer(element, container);
default:
return GetFirstInNextContainer(container);
}
@@ -98,6 +100,8 @@ namespace Perspex.Input
case KeyboardNavigationMode.Cycle:
return GetPreviousInContainer(element, container) ??
GetDescendents(container).LastOrDefault();
+ case KeyboardNavigationMode.Contained:
+ return GetPreviousInContainer(element, container);
default:
return GetLastInPreviousContainer(container);
}
@@ -164,7 +168,7 @@ namespace Perspex.Input
{
var mode = KeyboardNavigation.GetTabNavigation((InputElement)element);
- if (mode == KeyboardNavigationMode.Never)
+ if (mode == KeyboardNavigationMode.None)
{
yield break;
}
diff --git a/Perspex.Input/KeyboardNavigationMode.cs b/Perspex.Input/KeyboardNavigationMode.cs
index 346b4099e7..b339d9f89c 100644
--- a/Perspex.Input/KeyboardNavigationMode.cs
+++ b/Perspex.Input/KeyboardNavigationMode.cs
@@ -6,11 +6,41 @@
namespace Perspex.Input
{
+ ///
+ /// Defines the mode of keyboard traversal within a container when the tab or arrow keys are
+ /// pressed.
+ ///
public enum KeyboardNavigationMode
{
+ ///
+ /// Items in the container will be cycled through, and focus will be moved to the
+ /// previous/next container after the first/last control in the container.
+ ///
Continue,
+
+ ///
+ /// Items in the container will be cycled through, and moving past the first or last
+ /// control in the container will cause the last/first control to be focused.
+ ///
Cycle,
+
+ ///
+ /// Items in the container will be cycled through and focus will stop moving when the edge
+ /// of the container is reached.
+ ///
+ Contained,
+
+ ///
+ /// When focus is moved into the container, the control described by the
+ /// attached property on the
+ /// container will be focused. When focus moves away from this control, focus will move to
+ /// the previous/next container.
+ ///
Once,
- Never,
+
+ ///
+ /// The container's children will not be focused when using the tab key.
+ ///
+ None,
}
}
\ No newline at end of file
diff --git a/Tests/Perspex.Input.UnitTests/GlobalSuppressions.cs b/Tests/Perspex.Input.UnitTests/GlobalSuppressions.cs
new file mode 100644
index 0000000000..72d1f6b6a3
--- /dev/null
+++ b/Tests/Perspex.Input.UnitTests/GlobalSuppressions.cs
@@ -0,0 +1,13 @@
+// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project.
+// Project-level suppressions either have no target or are given
+// a specific target and scoped to a namespace, type, member, etc.
+
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "StyleCop.CSharp.MaintainabilityRules",
+ "SA1401:Fields must be private",
+ Justification = "PerspexProperty fields should not be private.")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "StyleCop.CSharp.DocumentationRules",
+ "SA1600:Elements must be documented",
+ Justification = "Tests should be self-documenting")]
\ No newline at end of file
diff --git a/Tests/Perspex.Input.UnitTests/KeyboardNavigationTests.cs b/Tests/Perspex.Input.UnitTests/KeyboardNavigationTests_Tab.cs
similarity index 83%
rename from Tests/Perspex.Input.UnitTests/KeyboardNavigationTests.cs
rename to Tests/Perspex.Input.UnitTests/KeyboardNavigationTests_Tab.cs
index f7373454fc..0adfd365e8 100644
--- a/Tests/Perspex.Input.UnitTests/KeyboardNavigationTests.cs
+++ b/Tests/Perspex.Input.UnitTests/KeyboardNavigationTests_Tab.cs
@@ -1,5 +1,5 @@
// -----------------------------------------------------------------------
-//
+//
// Copyright 2015 MIT Licence. See licence.md for more information.
//
// -----------------------------------------------------------------------
@@ -9,7 +9,7 @@ namespace Perspex.Input.UnitTests
using Perspex.Controls;
using Xunit;
- public class KeyboardNavigationTests
+ public class KeyboardNavigationTests_Tab
{
[Fact]
public void GetNextInTabOrder_Continue_Returns_Next_Control_In_Container()
@@ -294,6 +294,82 @@ namespace Perspex.Input.UnitTests
Assert.Equal(next, result);
}
+ [Fact]
+ public void GetNextInTabOrder_Contained_Returns_Next_Control_In_Container()
+ {
+ StackPanel container;
+ Button current;
+ Button next;
+
+ var top = new StackPanel
+ {
+ Children = new Controls
+ {
+ (container = new StackPanel
+ {
+ [KeyboardNavigation.TabNavigationProperty] = 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.GetNextInTabOrder(current);
+
+ Assert.Equal(next, result);
+ }
+
+ [Fact]
+ public void GetNextInTabOrder_Contained_Stops_At_End()
+ {
+ StackPanel container;
+ Button current;
+ Button next;
+
+ var top = new StackPanel
+ {
+ Children = new Controls
+ {
+ (container = new StackPanel
+ {
+ [KeyboardNavigation.TabNavigationProperty] = 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.GetNextInTabOrder(current);
+
+ Assert.Null(result);
+ }
+
[Fact]
public void GetNextInTabOrder_Once_Moves_To_Next_Container()
{
@@ -385,7 +461,7 @@ namespace Perspex.Input.UnitTests
{
(container = new StackPanel
{
- [KeyboardNavigation.TabNavigationProperty] = KeyboardNavigationMode.Never,
+ [KeyboardNavigation.TabNavigationProperty] = KeyboardNavigationMode.None,
Children = new Controls
{
new Button { Name = "Button1" },
@@ -423,7 +499,7 @@ namespace Perspex.Input.UnitTests
{
(container = new StackPanel
{
- [KeyboardNavigation.TabNavigationProperty] = KeyboardNavigationMode.Never,
+ [KeyboardNavigation.TabNavigationProperty] = KeyboardNavigationMode.None,
Children = new Controls
{
new Button { Name = "Button1" },
@@ -715,6 +791,82 @@ namespace Perspex.Input.UnitTests
Assert.Equal(next, result);
}
+ [Fact]
+ public void GetPreviousInTabOrder_Contained_Returns_Previous_Control_In_Container()
+ {
+ StackPanel container;
+ Button current;
+ Button next;
+
+ var top = new StackPanel
+ {
+ Children = new Controls
+ {
+ (container = new StackPanel
+ {
+ [KeyboardNavigation.TabNavigationProperty] = 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.GetPreviousInTabOrder(current);
+
+ Assert.Equal(next, result);
+ }
+
+ [Fact]
+ public void GetPreviousInTabOrder_Contained_Stops_At_Beginning()
+ {
+ StackPanel container;
+ Button current;
+ Button next;
+
+ var top = new StackPanel
+ {
+ Children = new Controls
+ {
+ (container = new StackPanel
+ {
+ [KeyboardNavigation.TabNavigationProperty] = KeyboardNavigationMode.Contained,
+ 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.GetPreviousInTabOrder(current);
+
+ Assert.Null(result);
+ }
+
[Fact]
public void GetPreviousInTabOrder_Once_Moves_To_Previous_Container()
{
diff --git a/Tests/Perspex.Input.UnitTests/Perspex.Input.UnitTests.csproj b/Tests/Perspex.Input.UnitTests/Perspex.Input.UnitTests.csproj
index 110b6453ee..66082fdad8 100644
--- a/Tests/Perspex.Input.UnitTests/Perspex.Input.UnitTests.csproj
+++ b/Tests/Perspex.Input.UnitTests/Perspex.Input.UnitTests.csproj
@@ -59,7 +59,8 @@
-
+
+