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"));
+ }
+ }
}
}