Browse Source

Allow selection of elements outside viewport.

Allow selection of elements outside viewport when using keyboard to select.
pull/11455/head
Steven Kirk 3 years ago
parent
commit
c4fe776b96
  1. 42
      src/Avalonia.Controls/VirtualizingStackPanel.cs
  2. 31
      tests/Avalonia.Controls.UnitTests/ListBoxTests.cs

42
src/Avalonia.Controls/VirtualizingStackPanel.cs

@ -70,8 +70,8 @@ namespace Avalonia.Controls
private ScrollViewer? _scrollViewer;
private Rect _viewport = s_invalidViewport;
private Dictionary<object, Stack<Control>>? _recyclePool;
private Control? _unrealizedFocusedElement;
private int _unrealizedFocusedIndex = -1;
private Control? _outOfViewportFocusedElement;
private int _outOfViewportFocusedIndex = -1;
public VirtualizingStackPanel()
{
@ -332,14 +332,21 @@ namespace Avalonia.Controls
{
if (index < 0 || index >= Items.Count)
return null;
if (_realizedElements?.GetElement(index) is { } realized)
if (GetRealizedElement(index) is { } realized)
return realized;
if (Items[index] is Control c && c.GetValue(RecycleKeyProperty) == s_itemIsItsOwnContainer)
return c;
return null;
}
protected internal override int IndexFromContainer(Control container) => _realizedElements?.GetIndex(container) ?? -1;
protected internal override int IndexFromContainer(Control container)
{
if (container == _scrollToElement)
return _scrollToIndex;
if (container == _outOfViewportFocusedElement)
return _outOfViewportFocusedIndex;
return _realizedElements?.GetIndex(container) ?? -1;
}
protected internal override Control? ScrollIntoView(int index)
{
@ -590,6 +597,8 @@ namespace Avalonia.Controls
{
if (_scrollToIndex == index)
return _scrollToElement;
if (_outOfViewportFocusedIndex == index)
return _outOfViewportFocusedElement;
return _realizedElements?.GetElement(index);
}
@ -621,15 +630,6 @@ namespace Avalonia.Controls
var generator = ItemContainerGenerator!;
if (_unrealizedFocusedIndex == index && _unrealizedFocusedElement is not null)
{
var element = _unrealizedFocusedElement;
_unrealizedFocusedElement.LostFocus -= OnUnrealizedFocusedElementLostFocus;
_unrealizedFocusedElement = null;
_unrealizedFocusedIndex = -1;
return element;
}
if (_recyclePool?.TryGetValue(recycleKey, out var recyclePool) == true && recyclePool.Count > 0)
{
var recycled = recyclePool.Pop();
@ -672,9 +672,9 @@ namespace Avalonia.Controls
}
else if (element.IsKeyboardFocusWithin)
{
_unrealizedFocusedElement = element;
_unrealizedFocusedIndex = index;
_unrealizedFocusedElement.LostFocus += OnUnrealizedFocusedElementLostFocus;
_outOfViewportFocusedElement = element;
_outOfViewportFocusedIndex = index;
_outOfViewportFocusedElement.LostFocus += OnUnrealizedFocusedElementLostFocus;
}
else
{
@ -744,13 +744,13 @@ namespace Avalonia.Controls
private void OnUnrealizedFocusedElementLostFocus(object? sender, RoutedEventArgs e)
{
if (_unrealizedFocusedElement is null || sender != _unrealizedFocusedElement)
if (_outOfViewportFocusedElement is null || sender != _outOfViewportFocusedElement)
return;
_unrealizedFocusedElement.LostFocus -= OnUnrealizedFocusedElementLostFocus;
RecycleElement(_unrealizedFocusedElement, _unrealizedFocusedIndex);
_unrealizedFocusedElement = null;
_unrealizedFocusedIndex = -1;
_outOfViewportFocusedElement.LostFocus -= OnUnrealizedFocusedElementLostFocus;
RecycleElement(_outOfViewportFocusedElement, _outOfViewportFocusedIndex);
_outOfViewportFocusedElement = null;
_outOfViewportFocusedIndex = -1;
}
/// <inheritdoc/>

31
tests/Avalonia.Controls.UnitTests/ListBoxTests.cs

@ -893,6 +893,37 @@ namespace Avalonia.Controls.UnitTests
Assert.True(target.ContainerFromIndex(1).IsFocused);
}
[Fact]
public void Down_Key_Brings_Unrealized_Selection_Into_View()
{
using var app = UnitTestApplication.Start(TestServices.RealFocus);
var items = Enumerable.Range(0, 100).Select(x => $"Item {x}").ToArray();
var target = new ListBox
{
Template = ListBoxTemplate(),
ItemsSource = items,
ItemTemplate = new FuncDataTemplate<string>((x, _) => new TextBlock { Width = 20, Height = 10 }),
Width = 100,
Height = 100,
SelectedIndex = 0,
};
Prepare(target);
target.ContainerFromIndex(0)!.Focus();
target.Scroll.Offset = new Vector(0, 100);
Layout(target);
var panel = (VirtualizingStackPanel)target.ItemsPanelRoot;
Assert.Equal(10, panel.FirstRealizedIndex);
RaiseKeyEvent(target, Key.Down);
Assert.Equal(1, target.SelectedIndex);
Assert.True(target.ContainerFromIndex(1).IsFocused);
Assert.Equal(new Vector(0, 10), target.Scroll.Offset);
}
[Fact]
public void WrapSelection_Should_Wrap()
{

Loading…
Cancel
Save