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 @@ - + +