Browse Source

Don't focus unfocusable controls.

- Check `CanFocus` in `FocusManager.SetFocusedElement`
- Check for `IsEffectivelyVisible` in `CanFocus`
- Clear focus when control made invisible
- Update tests that relied on unfocusable controls being focused
Steven Kirk 2 years ago
parent
commit
531fed2b4e

+ 6 - 1
src/Avalonia.Base/Input/FocusManager.cs

@@ -122,6 +122,11 @@ namespace Avalonia.Input
         {
             scope = scope ?? throw new ArgumentNullException(nameof(scope));
 
+            if (element is not null && !CanFocus(element))
+            {
+                return;
+            }
+
             if (_focusScopes.TryGetValue(scope, out var existingElement))
             {
                 if (element != existingElement)
@@ -242,6 +247,6 @@ namespace Avalonia.Input
             }
         }
 
-        private static bool IsVisible(IInputElement e) => (e as Visual)?.IsVisible ?? true;
+        private static bool IsVisible(IInputElement e) => (e as Visual)?.IsEffectivelyVisible ?? true;
     }
 }

+ 4 - 0
src/Avalonia.Base/Input/InputElement.cs

@@ -647,6 +647,10 @@ namespace Avalonia.Input
             {
                 PseudoClasses.Set(":focus-within", change.GetNewValue<bool>());
             }
+            else if (change.Property == IsVisibleProperty && !change.GetNewValue<bool>() && IsFocused)
+            {
+                FocusManager.Instance?.Focus(null);
+            }
         }
 
         /// <summary>

+ 0 - 1
tests/Avalonia.Controls.UnitTests/MaskedTextBoxTests.cs

@@ -657,7 +657,6 @@ namespace Avalonia.Controls.UnitTests
                 {
                     Template = CreateTemplate(),
                     Text = "1234",
-                    IsVisible = false
                 };
 
                 var root = new TestRoot { Child = target1 };

+ 0 - 1
tests/Avalonia.Controls.UnitTests/TextBoxTests.cs

@@ -594,7 +594,6 @@ namespace Avalonia.Controls.UnitTests
                 {
                     Template = CreateTemplate(),
                     Text = "1234",
-                    IsVisible = false
                 };
 
                 var root = new TestRoot { Child = target1 };

+ 7 - 1
tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs

@@ -298,7 +298,9 @@ namespace Avalonia.Controls.UnitTests
             using var app = App();
             var (target, scroll, itemsControl) = CreateTarget();
 
-            target.GetRealizedElements().First()!.Focus();
+            var focused = target.GetRealizedElements().First()!;
+            focused.Focusable = true;
+            focused.Focus();
             Assert.True(target.GetRealizedElements().First()!.IsKeyboardFocusWithin);
 
             scroll.Offset = new Vector(0, 200);
@@ -314,6 +316,7 @@ namespace Avalonia.Controls.UnitTests
             var (target, scroll, itemsControl) = CreateTarget();
 
             var focused = target.GetRealizedElements().First()!;
+            focused.Focusable = true;
             focused.Focus();
             Assert.True(focused.IsKeyboardFocusWithin);
 
@@ -331,6 +334,7 @@ namespace Avalonia.Controls.UnitTests
             var (target, scroll, itemsControl) = CreateTarget();
 
             var focused = target.GetRealizedElements().First()!;
+            focused.Focusable = true;
             focused.Focus();
             Assert.True(focused.IsKeyboardFocusWithin);
 
@@ -350,12 +354,14 @@ namespace Avalonia.Controls.UnitTests
             var (target, scroll, itemsControl) = CreateTarget();
 
             var originalFocused = target.GetRealizedElements().First()!;
+            originalFocused.Focusable = true;
             originalFocused.Focus();
 
             scroll.Offset = new Vector(0, 500);
             Layout(target);
 
             var newFocused = target.GetRealizedElements().First()!;
+            newFocused.Focusable = true;
             newFocused.Focus();
 
             Assert.False(originalFocused.IsVisible);

+ 2 - 2
tests/Avalonia.LeakTests/ControlTests.cs

@@ -558,7 +558,7 @@ namespace Avalonia.LeakTests
                     control.ContextMenu = null;
                 }
 
-                var window = new Window();
+                var window = new Window { Focusable = true };
                 window.Show();
 
                 Assert.Same(window, FocusManager.Instance.Current);
@@ -605,7 +605,7 @@ namespace Avalonia.LeakTests
                     contextMenu.Close();
                 }
 
-                var window = new Window();
+                var window = new Window { Focusable = true };
                 window.Show();
 
                 Assert.Same(window, FocusManager.Instance.Current);