diff --git a/src/Avalonia.Base/Input/Navigation/XYFocus.FindElements.cs b/src/Avalonia.Base/Input/Navigation/XYFocus.FindElements.cs index 5cc0d1a564..a4eea7d88b 100644 --- a/src/Avalonia.Base/Input/Navigation/XYFocus.FindElements.cs +++ b/src/Avalonia.Base/Input/Navigation/XYFocus.FindElements.cs @@ -65,7 +65,7 @@ public partial class XYFocus private static bool IsValidCandidate(InputElement candidate, KeyDeviceType? inputKeyDeviceType) { - return candidate.Focusable && candidate.IsEnabled && candidate.IsVisible + return candidate.Focusable && candidate.IsEffectivelyEnabled && candidate.IsEffectivelyVisible // Only allow candidate focus, if original key device type could focus it. && XYFocusHelpers.IsAllowedXYNavigationMode(candidate, inputKeyDeviceType); } diff --git a/tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_XY.cs b/tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_XY.cs index fa5286a8d8..099343203e 100644 --- a/tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_XY.cs +++ b/tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_XY.cs @@ -459,4 +459,44 @@ public class KeyboardNavigationTests_XY : ScopedTestBase Assert.Null(KeyboardNavigationHandler.GetNext(current, NavigationDirection.Down)); } + + [Fact] + public void XYFocus_Skips_Effectively_Disabled_Controls() + { + using var _ = UnitTestApplication.Start(TestServices.FocusableWindow); + + var current = new TestControl() { Height = 20, Width = 20, IsEnabled = true, IsVisible = true, Focusable = true, ShouldEnable = true}; + var disabled = new TestControl() { Height = 20, Width = 20, IsEnabled = true, IsVisible = true, Focusable = true, ShouldEnable = false }; + var candidate = new TestControl() { Height = 20, Width = 20, IsEnabled = true, IsVisible = true, Focusable = true, ShouldEnable = true }; + + var parent = new StackPanel + { + Orientation = Orientation.Vertical, + Spacing = 20, + Children = { current, disabled, candidate } + }; + + var window = new Window + { + [XYFocus.NavigationModesProperty] = XYFocusNavigationModes.Enabled, + Content = parent, + Height = 300 + }; + window.Show(); + + Assert.Equal(candidate, KeyboardNavigationHandler.GetNext(current, NavigationDirection.Down)); + } + + private class TestControl : Decorator + { + private bool _shouldEnable; + + public bool ShouldEnable + { + get => _shouldEnable; + set { _shouldEnable = value; UpdateIsEffectivelyEnabled(); } + } + + protected override bool IsEnabledCore => IsEnabled && _shouldEnable; + } }