From c23fdc92de962f00ec43e24f4a09fc889dff294b Mon Sep 17 00:00:00 2001 From: Michael Bosschert Date: Fri, 29 Mar 2019 14:06:10 +0100 Subject: [PATCH 1/3] Added unittests for the Select All option of the TreeView. --- .../TreeViewTests.cs | 133 +++++++++++++++++- 1 file changed, 131 insertions(+), 2 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs index 519872f9f2..15081b184c 100644 --- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs @@ -10,6 +10,7 @@ using Avalonia.Controls.Templates; using Avalonia.Data; using Avalonia.Data.Core; using Avalonia.Input; +using Avalonia.Input.Platform; using Avalonia.LogicalTree; using Avalonia.UnitTests; using Xunit; @@ -425,7 +426,6 @@ namespace Avalonia.Controls.UnitTests Assert.True(called); } - [Fact] public void LogicalChildren_Should_Be_Set() { @@ -623,6 +623,135 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Pressing_SelectAll_Gesture_Should_Select_All_Nodes() + { + using (UnitTestApplication.Start()) + { + var tree = CreateTestTreeData(); + var target = new TreeView + { + Template = CreateTreeViewTemplate(), + Items = tree, + SelectionMode = SelectionMode.Multiple + }; + + var visualRoot = new TestRoot(); + visualRoot.Child = target; + + CreateNodeDataTemplate(target); + ApplyTemplates(target); + + var rootNode = tree[0]; + + var keymap = AvaloniaLocator.Current.GetService(); + var selectAllGesture = keymap.SelectAll.First(); + + var keyEvent = new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = selectAllGesture.Key, + Modifiers = selectAllGesture.Modifiers + }; + + target.RaiseEvent(keyEvent); + + TreeTestHelper.AssertChildrenSelected(target, rootNode); + } + } + + [Fact] + public void Pressing_SelectAll_Gesture_With_Downward_Range_Selected_Should_Select_All_Nodes() + { + using (UnitTestApplication.Start()) + { + var tree = CreateTestTreeData(); + var target = new TreeView + { + Template = CreateTreeViewTemplate(), + Items = tree, + SelectionMode = SelectionMode.Multiple + }; + + var visualRoot = new TestRoot(); + visualRoot.Child = target; + + CreateNodeDataTemplate(target); + ApplyTemplates(target); + + var rootNode = tree[0]; + + var from = rootNode.Children[0]; + var to = rootNode.Children.Last(); + + var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from); + var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to); + + TreeTestHelper.ClickContainer(fromContainer, InputModifiers.None); + TreeTestHelper.ClickContainer(toContainer, InputModifiers.Shift); + + var keymap = AvaloniaLocator.Current.GetService(); + var selectAllGesture = keymap.SelectAll.First(); + + var keyEvent = new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = selectAllGesture.Key, + Modifiers = selectAllGesture.Modifiers + }; + + target.RaiseEvent(keyEvent); + + TreeTestHelper.AssertChildrenSelected(target, rootNode); + } + } + + [Fact] + public void Pressing_SelectAll_Gesture_With_Upward_Range_Selected_Should_Select_All_Nodes() + { + using (UnitTestApplication.Start()) + { + var tree = CreateTestTreeData(); + var target = new TreeView + { + Template = CreateTreeViewTemplate(), + Items = tree, + SelectionMode = SelectionMode.Multiple + }; + + var visualRoot = new TestRoot(); + visualRoot.Child = target; + + CreateNodeDataTemplate(target); + ApplyTemplates(target); + + var rootNode = tree[0]; + + var from = rootNode.Children.Last(); + var to = rootNode.Children[0]; + + var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from); + var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to); + + TreeTestHelper.ClickContainer(fromContainer, InputModifiers.None); + TreeTestHelper.ClickContainer(toContainer, InputModifiers.Shift); + + var keymap = AvaloniaLocator.Current.GetService(); + var selectAllGesture = keymap.SelectAll.First(); + + var keyEvent = new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = selectAllGesture.Key, + Modifiers = selectAllGesture.Modifiers + }; + + target.RaiseEvent(keyEvent); + + TreeTestHelper.AssertChildrenSelected(target, rootNode); + } + } + private void ApplyTemplates(TreeView tree) { tree.ApplyTemplate(); @@ -765,7 +894,7 @@ namespace Avalonia.Controls.UnitTests } } - private class Node : NotifyingBase + private class Node : NotifyingBase { private IAvaloniaList _children; From de801ed27edb2c980fc5b81141d50c41a41be871 Mon Sep 17 00:00:00 2001 From: Michael Bosschert Date: Tue, 2 Apr 2019 15:18:50 +0200 Subject: [PATCH 2/3] Fixed SynchronizeItems adding duplicate items. --- .../Primitives/SelectingItemsControl.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index a64dbe0546..288d751aa9 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -639,20 +639,20 @@ namespace Avalonia.Controls.Primitives /// The desired items. internal static void SynchronizeItems(IList items, IEnumerable desired) { - int index = 0; + var index = 0; - foreach (var i in desired) + foreach (object item in desired) { - if (index < items.Count) + int itemIndex = items.IndexOf(item); + + if (itemIndex == -1) { - if (items[index] != i) - { - items[index] = i; - } + items.Insert(index, item); } - else + else if(itemIndex != index) { - items.Add(i); + items.RemoveAt(itemIndex); + items.Insert(index, item); } ++index; From 4ad4ba4a9ed45415601d5355aa76158cd7b9349d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 17 May 2019 17:00:20 +0200 Subject: [PATCH 3/3] Set InputModifiers on PointerEnter/Leave. Note that these will not be set when a pointer enter/leave occurs because of a control moving or appearing/disappearing. Fixes #2495 --- src/Avalonia.Input/MouseDevice.cs | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs index d3e62ece6f..c195209305 100644 --- a/src/Avalonia.Input/MouseDevice.cs +++ b/src/Avalonia.Input/MouseDevice.cs @@ -108,11 +108,11 @@ namespace Avalonia.Input { if (Captured == null) { - SetPointerOver(this, root, clientPoint); + SetPointerOver(this, root, clientPoint, InputModifiers.None); } else { - SetPointerOver(this, root, Captured); + SetPointerOver(this, root, Captured, InputModifiers.None); } } } @@ -128,7 +128,7 @@ namespace Avalonia.Input switch (e.Type) { case RawMouseEventType.LeaveWindow: - LeaveWindow(mouse, e.Root); + LeaveWindow(mouse, e.Root, e.InputModifiers); break; case RawMouseEventType.LeftButtonDown: case RawMouseEventType.RightButtonDown: @@ -157,12 +157,12 @@ namespace Avalonia.Input } } - private void LeaveWindow(IMouseDevice device, IInputRoot root) + private void LeaveWindow(IMouseDevice device, IInputRoot root, InputModifiers inputModifiers) { Contract.Requires(device != null); Contract.Requires(root != null); - ClearPointerOver(this, root); + ClearPointerOver(this, root, inputModifiers); } private bool MouseDown(IMouseDevice device, ulong timestamp, IInputElement root, Point p, MouseButton button, InputModifiers inputModifiers) @@ -218,11 +218,11 @@ namespace Avalonia.Input if (Captured == null) { - source = SetPointerOver(this, root, p); + source = SetPointerOver(this, root, p, inputModifiers); } else { - SetPointerOver(this, root, Captured); + SetPointerOver(this, root, Captured, inputModifiers); source = Captured; } @@ -306,7 +306,7 @@ namespace Avalonia.Input return Captured ?? root.InputHitTest(p); } - private void ClearPointerOver(IPointerDevice device, IInputRoot root) + private void ClearPointerOver(IPointerDevice device, IInputRoot root, InputModifiers inputModifiers) { Contract.Requires(device != null); Contract.Requires(root != null); @@ -316,6 +316,7 @@ namespace Avalonia.Input { RoutedEvent = InputElement.PointerLeaveEvent, Device = device, + InputModifiers = inputModifiers }; if (element!=null && !element.IsAttachedToVisualTree) @@ -353,7 +354,7 @@ namespace Avalonia.Input } } - private IInputElement SetPointerOver(IPointerDevice device, IInputRoot root, Point p) + private IInputElement SetPointerOver(IPointerDevice device, IInputRoot root, Point p, InputModifiers inputModifiers) { Contract.Requires(device != null); Contract.Requires(root != null); @@ -364,18 +365,18 @@ namespace Avalonia.Input { if (element != null) { - SetPointerOver(device, root, element); + SetPointerOver(device, root, element, inputModifiers); } else { - ClearPointerOver(device, root); + ClearPointerOver(device, root, inputModifiers); } } return element; } - private void SetPointerOver(IPointerDevice device, IInputRoot root, IInputElement element) + private void SetPointerOver(IPointerDevice device, IInputRoot root, IInputElement element, InputModifiers inputModifiers) { Contract.Requires(device != null); Contract.Requires(root != null); @@ -383,7 +384,7 @@ namespace Avalonia.Input IInputElement branch = null; - var e = new PointerEventArgs { Device = device, }; + var e = new PointerEventArgs { Device = device, InputModifiers = inputModifiers }; var el = element; while (el != null)