From c37144b59633f8f52f917a77f5bceb7c0e678647 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Thu, 16 Jul 2020 13:03:26 +0300 Subject: [PATCH 1/3] add failing unit tests for #4306 --- .../ListBoxTests_Multiple.cs | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 tests/Avalonia.Controls.UnitTests/ListBoxTests_Multiple.cs diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests_Multiple.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests_Multiple.cs new file mode 100644 index 0000000000..7c7cdd08db --- /dev/null +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests_Multiple.cs @@ -0,0 +1,126 @@ +using System.Linq; +using Avalonia.Controls.Presenters; +using Avalonia.Controls.Templates; +using Avalonia.Input; +using Avalonia.Styling; +using Avalonia.UnitTests; +using Avalonia.VisualTree; +using Xunit; + +namespace Avalonia.Controls.UnitTests +{ + public class ListBoxTests_Multiple + { + [Fact] + public void Focusing_Item_With_Shift_And_Arrow_Key_Should_Add_To_Selection() + { + var target = new ListBox + { + Template = new FuncControlTemplate(CreateListBoxTemplate), + Items = new[] { "Foo", "Bar", "Baz " }, + SelectionMode = SelectionMode.Multiple + }; + + ApplyTemplate(target); + + target.SelectedItem = "Foo"; + + target.Presenter.Panel.Children[1].RaiseEvent(new GotFocusEventArgs + { + RoutedEvent = InputElement.GotFocusEvent, + NavigationMethod = NavigationMethod.Directional, + KeyModifiers = KeyModifiers.Shift + }); + + Assert.Equal(new[] { "Foo", "Bar" }, target.SelectedItems); + } + + [Fact] + public void Focusing_Item_With_Ctrl_And_Arrow_Key_Should_Add_To_Selection() + { + var target = new ListBox + { + Template = new FuncControlTemplate(CreateListBoxTemplate), + Items = new[] { "Foo", "Bar", "Baz " }, + SelectionMode = SelectionMode.Multiple + }; + + ApplyTemplate(target); + + target.SelectedItem = "Foo"; + + target.Presenter.Panel.Children[1].RaiseEvent(new GotFocusEventArgs + { + RoutedEvent = InputElement.GotFocusEvent, + NavigationMethod = NavigationMethod.Directional, + KeyModifiers = KeyModifiers.Control + }); + + Assert.Equal(new[] { "Foo", "Bar" }, target.SelectedItems); + } + + [Fact] + public void Focusing_Selected_Item_With_Ctrl_And_Arrow_Key_Should_Remove_From_Selection() + { + var target = new ListBox + { + Template = new FuncControlTemplate(CreateListBoxTemplate), + Items = new[] { "Foo", "Bar", "Baz " }, + SelectionMode = SelectionMode.Multiple + }; + + ApplyTemplate(target); + + target.SelectedItems.Add("Foo"); + target.SelectedItems.Add("Bar"); + + target.Presenter.Panel.Children[0].RaiseEvent(new GotFocusEventArgs + { + RoutedEvent = InputElement.GotFocusEvent, + NavigationMethod = NavigationMethod.Directional, + KeyModifiers = KeyModifiers.Control + }); + + Assert.Equal(new[] { "Bar" }, target.SelectedItems); + } + + private Control CreateListBoxTemplate(ITemplatedControl parent, INameScope scope) + { + return new ScrollViewer + { + Template = new FuncControlTemplate(CreateScrollViewerTemplate), + Content = new ItemsPresenter + { + Name = "PART_ItemsPresenter", + [~ItemsPresenter.ItemsProperty] = parent.GetObservable(ItemsControl.ItemsProperty).ToBinding(), + }.RegisterInNameScope(scope) + }; + } + + private Control CreateScrollViewerTemplate(ITemplatedControl parent, INameScope scope) + { + return new ScrollContentPresenter + { + Name = "PART_ContentPresenter", + [~ContentPresenter.ContentProperty] = + parent.GetObservable(ContentControl.ContentProperty).ToBinding(), + }.RegisterInNameScope(scope); + } + + private void ApplyTemplate(ListBox target) + { + // Apply the template to the ListBox itself. + target.ApplyTemplate(); + + // Then to its inner ScrollViewer. + var scrollViewer = (ScrollViewer)target.GetVisualChildren().Single(); + scrollViewer.ApplyTemplate(); + + // Then make the ScrollViewer create its child. + ((ContentPresenter)scrollViewer.Presenter).UpdateChild(); + + // Now the ItemsPresenter should be reigstered, so apply its template. + target.Presenter.ApplyTemplate(); + } + } +} From c08d88afcff791837d28b0a1dd73105da0d1cc6e Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Thu, 16 Jul 2020 12:47:26 +0300 Subject: [PATCH 2/3] pass key modifiers to focus --- src/Avalonia.Controls/ItemsControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 6e0ad66699..1aa7945901 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -295,7 +295,7 @@ namespace Avalonia.Controls if (next != null) { - focus.Focus(next, NavigationMethod.Directional); + focus.Focus(next, NavigationMethod.Directional, e.KeyModifiers); e.Handled = true; } From e5c373acd06489b0e715282158488712259723eb Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Thu, 16 Jul 2020 12:48:12 +0300 Subject: [PATCH 3/3] pass ctrl key state to selection logic in listbox --- src/Avalonia.Controls/ListBox.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ListBox.cs b/src/Avalonia.Controls/ListBox.cs index 3c21cd2c38..a085bfb6bc 100644 --- a/src/Avalonia.Controls/ListBox.cs +++ b/src/Avalonia.Controls/ListBox.cs @@ -136,7 +136,8 @@ namespace Avalonia.Controls e.Handled = UpdateSelectionFromEventSource( e.Source, true, - (e.KeyModifiers & KeyModifiers.Shift) != 0); + (e.KeyModifiers & KeyModifiers.Shift) != 0, + (e.KeyModifiers & KeyModifiers.Control) != 0); } }