From 6f7dac83d18b2a18a0a7ab9e7cef186f70622ae1 Mon Sep 17 00:00:00 2001 From: Maksym Katsydan Date: Wed, 1 Jul 2020 19:53:34 -0400 Subject: [PATCH] Add focus-visible support with tests --- src/Avalonia.Input/InputElement.cs | 7 +- .../InputElement_Focus.cs | 79 +++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Input/InputElement.cs b/src/Avalonia.Input/InputElement.cs index 407b28b665..0616c70d82 100644 --- a/src/Avalonia.Input/InputElement.cs +++ b/src/Avalonia.Input/InputElement.cs @@ -158,6 +158,7 @@ namespace Avalonia.Input private bool _isEffectivelyEnabled = true; private bool _isFocused; + private bool _isFocusVisible; private bool _isPointerOver; private GestureRecognizerCollection _gestureRecognizers; @@ -427,7 +428,9 @@ namespace Avalonia.Input /// The event args. protected virtual void OnGotFocus(GotFocusEventArgs e) { - IsFocused = e.Source == this; + var isFocused = e.Source == this; + _isFocusVisible = isFocused && (e.NavigationMethod == NavigationMethod.Directional || e.NavigationMethod == NavigationMethod.Tab); + IsFocused = isFocused; } /// @@ -436,6 +439,7 @@ namespace Avalonia.Input /// The event args. protected virtual void OnLostFocus(RoutedEventArgs e) { + _isFocusVisible = false; IsFocused = false; } @@ -602,6 +606,7 @@ namespace Avalonia.Input if (isFocused.HasValue) { PseudoClasses.Set(":focus", isFocused.Value); + PseudoClasses.Set(":focus-visible", _isFocusVisible); } if (isPointerOver.HasValue) diff --git a/tests/Avalonia.Input.UnitTests/InputElement_Focus.cs b/tests/Avalonia.Input.UnitTests/InputElement_Focus.cs index d396119555..09fae7207f 100644 --- a/tests/Avalonia.Input.UnitTests/InputElement_Focus.cs +++ b/tests/Avalonia.Input.UnitTests/InputElement_Focus.cs @@ -42,5 +42,84 @@ namespace Avalonia.Input.UnitTests Assert.Null(FocusManager.Instance.Current); } } + + [Fact] + public void Focus_Pseudoclass_Should_Be_Applied_On_Focus() + { + using (UnitTestApplication.Start(TestServices.RealFocus)) + { + var target1 = new Decorator(); + var target2 = new Decorator(); + var root = new TestRoot + { + Child = new StackPanel + { + Children = + { + target1, + target2 + } + } + }; + + target1.ApplyTemplate(); + target2.ApplyTemplate(); + + + FocusManager.Instance?.Focus(target1); + Assert.True(target1.IsFocused); + Assert.True(target1.Classes.Contains(":focus")); + Assert.False(target2.IsFocused); + Assert.False(target2.Classes.Contains(":focus")); + + FocusManager.Instance?.Focus(target2, NavigationMethod.Tab); + Assert.False(target1.IsFocused); + Assert.False(target1.Classes.Contains(":focus")); + Assert.True(target2.IsFocused); + Assert.True(target2.Classes.Contains(":focus")); + } + } + + [Fact] + public void Control_FocusVsisible_Pseudoclass_Should_Be_Applied_On_Tab_And_DirectionalFocus() + { + using (UnitTestApplication.Start(TestServices.RealFocus)) + { + var target1 = new Decorator(); + var target2 = new Decorator(); + var root = new TestRoot + { + Child = new StackPanel + { + Children = + { + target1, + target2 + } + } + }; + + target1.ApplyTemplate(); + target2.ApplyTemplate(); + + FocusManager.Instance?.Focus(target1); + Assert.True(target1.IsFocused); + Assert.False(target1.Classes.Contains(":focus-visible")); + Assert.False(target2.IsFocused); + Assert.False(target2.Classes.Contains(":focus-visible")); + + FocusManager.Instance?.Focus(target2, NavigationMethod.Tab); + Assert.False(target1.IsFocused); + Assert.False(target1.Classes.Contains(":focus-visible")); + Assert.True(target2.IsFocused); + Assert.True(target2.Classes.Contains(":focus-visible")); + + FocusManager.Instance?.Focus(target1, NavigationMethod.Directional); + Assert.True(target1.IsFocused); + Assert.True(target1.Classes.Contains(":focus-visible")); + Assert.False(target2.IsFocused); + Assert.False(target2.Classes.Contains(":focus-visible")); + } + } } }