Browse Source

Skip disabled controls when moving to first/last item

pull/8514/head
Luis von der Eltz 4 years ago
parent
commit
d9ce64e1b0
  1. 29
      src/Avalonia.Controls/ItemsControl.cs
  2. 4
      src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
  3. 24
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs

29
src/Avalonia.Controls/ItemsControl.cs

@ -502,25 +502,34 @@ namespace Avalonia.Controls
IInputElement? from, IInputElement? from,
bool wrap) bool wrap)
{ {
IInputElement? result; for(;;)
var c = from;
do
{ {
result = container.GetControl(direction, c, wrap); var result = container.GetControl(direction, from, wrap);
if (result is null || result == from)
{
return null;
}
if (result != null && if (result.Focusable &&
result.Focusable &&
result.IsEffectivelyEnabled && result.IsEffectivelyEnabled &&
result.IsEffectivelyVisible) result.IsEffectivelyVisible)
{ {
return result; return result;
} }
c = result; direction = direction switch
} while (c != null && c != from && direction != NavigationDirection.First && direction != NavigationDirection.Last); {
//We did not find an enabled first item. Move downwards until we find one.
NavigationDirection.First => NavigationDirection.Down,
//We did not find an enabled last item. Move upwards until we find one.
NavigationDirection.Last => NavigationDirection.Up,
_ => direction
};
return null; from = result;
}
} }
private void PresenterChildIndexChanged(object? sender, ChildIndexChangedEventArgs e) private void PresenterChildIndexChanged(object? sender, ChildIndexChangedEventArgs e)

4
src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs

@ -53,7 +53,7 @@ namespace Avalonia.Controls.Platform
Menu.PointerPressed += PointerPressed; Menu.PointerPressed += PointerPressed;
Menu.PointerReleased += PointerReleased; Menu.PointerReleased += PointerReleased;
Menu.AddHandler(AccessKeyHandler.AccessKeyPressedEvent, AccessKeyPressed); Menu.AddHandler(AccessKeyHandler.AccessKeyPressedEvent, AccessKeyPressed);
Menu.AddHandler(Avalonia.Controls.Menu.MenuOpenedEvent, MenuOpened); Menu.AddHandler(MenuBase.MenuOpenedEvent, MenuOpened);
Menu.AddHandler(MenuItem.PointerEnteredItemEvent, PointerEntered); Menu.AddHandler(MenuItem.PointerEnteredItemEvent, PointerEntered);
Menu.AddHandler(MenuItem.PointerExitedItemEvent, PointerExited); Menu.AddHandler(MenuItem.PointerExitedItemEvent, PointerExited);
Menu.AddHandler(InputElement.PointerMovedEvent, PointerMoved); Menu.AddHandler(InputElement.PointerMovedEvent, PointerMoved);
@ -89,7 +89,7 @@ namespace Avalonia.Controls.Platform
Menu.PointerPressed -= PointerPressed; Menu.PointerPressed -= PointerPressed;
Menu.PointerReleased -= PointerReleased; Menu.PointerReleased -= PointerReleased;
Menu.RemoveHandler(AccessKeyHandler.AccessKeyPressedEvent, AccessKeyPressed); Menu.RemoveHandler(AccessKeyHandler.AccessKeyPressedEvent, AccessKeyPressed);
Menu.RemoveHandler(Avalonia.Controls.Menu.MenuOpenedEvent, MenuOpened); Menu.RemoveHandler(MenuBase.MenuOpenedEvent, MenuOpened);
Menu.RemoveHandler(MenuItem.PointerEnteredItemEvent, PointerEntered); Menu.RemoveHandler(MenuItem.PointerEnteredItemEvent, PointerEntered);
Menu.RemoveHandler(MenuItem.PointerExitedItemEvent, PointerExited); Menu.RemoveHandler(MenuItem.PointerExitedItemEvent, PointerExited);
Menu.RemoveHandler(InputElement.PointerMovedEvent, PointerMoved); Menu.RemoveHandler(InputElement.PointerMovedEvent, PointerMoved);

24
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs

@ -1615,48 +1615,44 @@ namespace Avalonia.Controls.UnitTests.Primitives
target.MoveSelection(NavigationDirection.Next, true); target.MoveSelection(NavigationDirection.Next, true);
} }
[Fact(Timeout = 2000)] [Fact]
public async Task MoveSelection_Does_Not_Hang_With_No_Focusable_Controls_And_Moving_Selection_To_The_First_Item() public void MoveSelection_Skips_Non_Focusable_Controls_When_Moving_To_Last_Item()
{ {
var target = new TestSelector var target = new TestSelector
{ {
Template = Template(), Template = Template(),
Items = new[] Items = new[]
{ {
new ListBoxItem { Focusable = false },
new ListBoxItem(), new ListBoxItem(),
new ListBoxItem { Focusable = false },
} }
}; };
target.Measure(new Size(100, 100)); target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100)); target.Arrange(new Rect(0, 0, 100, 100));
target.MoveSelection(NavigationDirection.Last, true);
// Timeout in xUnit doesen't work with synchronous methods so we need to apply hack below. Assert.Equal(0, target.SelectedIndex);
// https://github.com/xunit/xunit/issues/2222
await Task.Run(() => target.MoveSelection(NavigationDirection.First, true));
Assert.Equal(-1, target.SelectedIndex);
} }
[Fact(Timeout = 2000)] [Fact]
public async Task MoveSelection_Does_Not_Hang_With_No_Focusable_Controls_And_Moving_Selection_To_The_Last_Item() public void MoveSelection_Skips_Non_Focusable_Controls_When_Moving_To_First_Item()
{ {
var target = new TestSelector var target = new TestSelector
{ {
Template = Template(), Template = Template(),
Items = new[] Items = new[]
{ {
new ListBoxItem(),
new ListBoxItem { Focusable = false }, new ListBoxItem { Focusable = false },
new ListBoxItem(),
} }
}; };
target.Measure(new Size(100, 100)); target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100)); target.Arrange(new Rect(0, 0, 100, 100));
target.MoveSelection(NavigationDirection.Last, true);
// Timeout in xUnit doesen't work with synchronous methods so we need to apply hack below. Assert.Equal(1, target.SelectedIndex);
// https://github.com/xunit/xunit/issues/2222
await Task.Run(() => target.MoveSelection(NavigationDirection.Last, true));
Assert.Equal(-1, target.SelectedIndex);
} }
[Fact] [Fact]

Loading…
Cancel
Save