From 99737a8395df0caaa854d87664546f803378e646 Mon Sep 17 00:00:00 2001 From: workgroupengineering Date: Tue, 7 Nov 2023 00:57:45 +0100 Subject: [PATCH] fix: Navigation when CanExecute is fasle (#13507) * test: Add test navigation with CanExecute is False * fix: Navigation when CanExecute is false --- .../Input/Navigation/TabNavigation.cs | 4 +- .../Input/KeyboardDeviceTests.cs | 15 +------- .../Input/KeyboardNavigationTests_Tab.cs | 38 +++++++++++++++++++ .../Utilities/DelegateCommand.cs | 19 ++++++++++ 4 files changed, 61 insertions(+), 15 deletions(-) create mode 100644 tests/Avalonia.Base.UnitTests/Utilities/DelegateCommand.cs diff --git a/src/Avalonia.Base/Input/Navigation/TabNavigation.cs b/src/Avalonia.Base/Input/Navigation/TabNavigation.cs index 9697e32926..3004a70bdc 100644 --- a/src/Avalonia.Base/Input/Navigation/TabNavigation.cs +++ b/src/Avalonia.Base/Input/Navigation/TabNavigation.cs @@ -649,12 +649,12 @@ namespace Avalonia.Input.Navigation private static bool IsTabStop(IInputElement e) { if (e is InputElement ie) - return ie.Focusable && KeyboardNavigation.GetIsTabStop(ie) && ie.IsVisible && ie.IsEnabled; + return ie.Focusable && KeyboardNavigation.GetIsTabStop(ie) && ie.IsVisible && ie.IsEffectivelyEnabled; return false; } private static bool IsTabStopOrGroup(IInputElement e) => IsTabStop(e) || IsGroup(e); private static bool IsVisible(IInputElement e) => (e as Visual)?.IsVisible ?? true; - private static bool IsVisibleAndEnabled(IInputElement e) => IsVisible(e) && e.IsEnabled; + private static bool IsVisibleAndEnabled(IInputElement e) => IsVisible(e) && e.IsEffectivelyEnabled; } } diff --git a/tests/Avalonia.Base.UnitTests/Input/KeyboardDeviceTests.cs b/tests/Avalonia.Base.UnitTests/Input/KeyboardDeviceTests.cs index 28cb9c9282..c486a66da0 100644 --- a/tests/Avalonia.Base.UnitTests/Input/KeyboardDeviceTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/KeyboardDeviceTests.cs @@ -1,6 +1,4 @@ -using System; -using System.Windows.Input; -using Avalonia.Controls; +using Avalonia.Controls; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.UnitTests; @@ -112,7 +110,7 @@ namespace Avalonia.Base.UnitTests.Input button.KeyBindings.Add(new KeyBinding { Gesture = new KeyGesture(Key.O, KeyModifiers.Control), - Command = new DelegateCommand(() => + Command = new Utilities.DelegateCommand(() => { button.KeyBindings.Clear(); ++raised; @@ -134,15 +132,6 @@ namespace Avalonia.Base.UnitTests.Input Assert.Equal(1, raised); } - private class DelegateCommand : ICommand - { - private readonly Action _action; - public DelegateCommand(Action action) => _action = action; - public event EventHandler CanExecuteChanged { add { } remove { } } - public bool CanExecute(object parameter) => true; - public void Execute(object parameter) => _action(); - } - [Fact] public void Control_Focus_Should_Be_Set_Before_FocusedElement_Raises_PropertyChanged() { diff --git a/tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Tab.cs b/tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Tab.cs index 0b3d1a275b..5d8ffd1e13 100644 --- a/tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Tab.cs +++ b/tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Tab.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using Avalonia.Controls; using Avalonia.Input; @@ -1273,5 +1274,42 @@ namespace Avalonia.Base.UnitTests.Input Assert.True(button.IsFocused); } + + [Fact] + public void Next_Skip_Button_When_Command_CanExecute_Is_False() + { + Button current; + Button expected; + bool executed = false; + + var top = new StackPanel + { + [KeyboardNavigation.TabNavigationProperty] = KeyboardNavigationMode.Cycle, + Children = + { + new StackPanel + { + Children = + { + (current = new Button { Name = "Button1" }), + new Button + { + Name = "Button2", + Command = new Utilities.DelegateCommand(()=>executed = true, + _ => false), + }, + (expected = new Button { Name = "Button3" }), + } + } + } + }; + + var result = KeyboardNavigationHandler.GetNext(current, NavigationDirection.Next) as Button; + + Assert.Equal(expected.Name, result?.Name); + Assert.False(executed); + } + + } } diff --git a/tests/Avalonia.Base.UnitTests/Utilities/DelegateCommand.cs b/tests/Avalonia.Base.UnitTests/Utilities/DelegateCommand.cs new file mode 100644 index 0000000000..0f9c3a0545 --- /dev/null +++ b/tests/Avalonia.Base.UnitTests/Utilities/DelegateCommand.cs @@ -0,0 +1,19 @@ +using System; +using System.Windows.Input; + +namespace Avalonia.Base.UnitTests.Utilities; + +internal class DelegateCommand : ICommand +{ + private readonly Action _action; + private readonly Func _canExecute; + public DelegateCommand(Action action, Func canExecute = default) + { + _action = action; + _canExecute = canExecute ?? new(_ => true); + } + + public event EventHandler CanExecuteChanged { add { } remove { } } + public bool CanExecute(object parameter) => _canExecute(parameter); + public void Execute(object parameter) => _action(); +}