Skip to content

Commit

Permalink
Merge pull request #8514 from pr8x/move-first-last-skip-disabled
Browse files Browse the repository at this point in the history
Skip disabled controls when moving to first/last item
  • Loading branch information
maxkatz6 committed Jul 21, 2022
2 parents 55e3d51 + 2885a66 commit c7c6450
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 21 deletions.
40 changes: 31 additions & 9 deletions src/Avalonia.Controls/ItemsControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -502,25 +502,47 @@ private void UpdatePseudoClasses(int itemCount)
IInputElement? from,
bool wrap)
{
IInputElement? result;
var c = from;
var current = from;

do
for (;;)
{
result = container.GetControl(direction, c, wrap);
var result = container.GetControl(direction, current, wrap);

if (result != null &&
result.Focusable &&
if (result is null)
{
return null;
}

if (result.Focusable &&
result.IsEffectivelyEnabled &&
result.IsEffectivelyVisible)
{
return result;
}

c = result;
} while (c != null && c != from && direction != NavigationDirection.First && direction != NavigationDirection.Last);
current = result;

return null;
if (current == from)
{
return null;
}

switch (direction)
{
//We did not find an enabled first item. Move downwards until we find one.
case NavigationDirection.First:
direction = NavigationDirection.Down;
from = result;
break;

//We did not find an enabled last item. Move upwards until we find one.
case NavigationDirection.Last:
direction = NavigationDirection.Up;
from = result;
break;

}
}
}

private void PresenterChildIndexChanged(object? sender, ChildIndexChangedEventArgs e)
Expand Down
10 changes: 7 additions & 3 deletions src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public virtual void Attach(IMenu menu)
Menu.PointerPressed += PointerPressed;
Menu.PointerReleased += PointerReleased;
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.PointerExitedItemEvent, PointerExited);
Menu.AddHandler(InputElement.PointerMovedEvent, PointerMoved);
Expand Down Expand Up @@ -89,7 +89,7 @@ public virtual void Detach(IMenu menu)
Menu.PointerPressed -= PointerPressed;
Menu.PointerReleased -= PointerReleased;
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.PointerExitedItemEvent, PointerExited);
Menu.RemoveHandler(InputElement.PointerMovedEvent, PointerMoved);
Expand Down Expand Up @@ -175,7 +175,11 @@ protected internal virtual void KeyDown(IMenuItem? item, KeyEventArgs e)

case Key.Left:
{
if (item?.Parent is IMenuItem parent && !parent.IsTopLevel && parent.IsSubMenuOpen)
if (item is { IsSubMenuOpen: true, SelectedItem: null })
{
item.Close();
}
else if (item?.Parent is IMenuItem { IsTopLevel: false, IsSubMenuOpen: true } parent)
{
parent.Close();
parent.Focus();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1595,8 +1595,8 @@ public void Can_Set_Both_SelectedItem_And_SelectedItems_During_Initialization()
Assert.Equal(new[] { "Bar" }, selectedItems);
}

[Fact]
public void MoveSelection_Wrap_Does_Not_Hang_With_No_Focusable_Controls()
[Fact(Timeout = 2000)]
public async Task MoveSelection_Wrap_Does_Not_Hang_With_No_Focusable_Controls()
{
// Issue #3094.
var target = new TestSelector
Expand All @@ -1612,11 +1612,34 @@ public void MoveSelection_Wrap_Does_Not_Hang_With_No_Focusable_Controls()

target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
target.MoveSelection(NavigationDirection.Next, true);

// Timeout in xUnit doesn't work with synchronous methods so we need to apply hack below.
// https://github.com/xunit/xunit/issues/2222
await Task.Run(() => target.MoveSelection(NavigationDirection.Next, true));
}

[Fact(Timeout = 2000)]
public async Task MoveSelection_Does_Not_Hang_With_No_Focusable_Controls_And_Moving_Selection_To_The_First_Item()
[Fact]
public void MoveSelection_Skips_Non_Focusable_Controls_When_Moving_To_Last_Item()
{
var target = new TestSelector
{
Template = Template(),
Items = new[]
{
new ListBoxItem(),
new ListBoxItem { Focusable = false },
}
};

target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
target.MoveSelection(NavigationDirection.Last, true);

Assert.Equal(0, target.SelectedIndex);
}

[Fact]
public void MoveSelection_Skips_Non_Focusable_Controls_When_Moving_To_First_Item()
{
var target = new TestSelector
{
Expand All @@ -1630,32 +1653,54 @@ public async Task MoveSelection_Does_Not_Hang_With_No_Focusable_Controls_And_Mov

target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
target.MoveSelection(NavigationDirection.Last, true);

Assert.Equal(1, target.SelectedIndex);
}

// Timeout in xUnit doesen't work with synchronous methods so we need to apply hack below.
[Fact(Timeout = 2000)]
public async Task MoveSelection_Does_Not_Hang_When_All_Items_Are_Non_Focusable_And_We_Move_To_First_Item()
{
var target = new TestSelector
{
Template = Template(),
Items = new[]
{
new ListBoxItem { Focusable = false },
new ListBoxItem { Focusable = false },
}
};

target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));

// Timeout in xUnit doesn't work with synchronous methods so we need to apply hack below.
// https://github.com/xunit/xunit/issues/2222
await Task.Run(() => target.MoveSelection(NavigationDirection.First, true));

Assert.Equal(-1, target.SelectedIndex);
}

[Fact(Timeout = 2000)]
public async Task MoveSelection_Does_Not_Hang_With_No_Focusable_Controls_And_Moving_Selection_To_The_Last_Item()
public async Task MoveSelection_Does_Not_Hang_When_All_Items_Are_Non_Focusable_And_We_Move_To_Last_Item()
{
var target = new TestSelector
{
Template = Template(),
Items = new[]
{
new ListBoxItem(),
new ListBoxItem { Focusable = false },
new ListBoxItem { Focusable = false },
}
};

target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));

// Timeout in xUnit doesen't work with synchronous methods so we need to apply hack below.
// Timeout in xUnit doesn't work with synchronous methods so we need to apply hack below.
// https://github.com/xunit/xunit/issues/2222
await Task.Run(() => target.MoveSelection(NavigationDirection.Last, true));

Assert.Equal(-1, target.SelectedIndex);
}

Expand Down

0 comments on commit c7c6450

Please sign in to comment.