Skip to content

fix: Prevent repetitive viewport analysis in PointerMoved #7366

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 28, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 44 additions & 15 deletions src/Files/UserControls/Selection/RectangleSelection_ListViewBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Toolkit.Uwp.UI;
using Windows.Foundation;
using Windows.System;
using Windows.UI.Xaml;
Expand All @@ -16,7 +17,7 @@ public class RectangleSelection_ListViewBase : RectangleSelection
private ListViewBase uiElement;
private ScrollViewer scrollViewer;
private SelectionChangedEventHandler selectionChanged;

private DispatcherQueueTimer timer;
private Point originDragPoint;
private Dictionary<object, System.Drawing.Rectangle> itemsPosition;
private List<object> prevSelectedItems;
Expand All @@ -28,6 +29,7 @@ public RectangleSelection_ListViewBase(ListViewBase uiElement, Rectangle selecti
this.selectionRectangle = selectionRectangle;
this.selectionChanged = selectionChanged;
itemsPosition = new Dictionary<object, System.Drawing.Rectangle>();
timer = DispatcherQueue.GetForCurrentThread().CreateTimer();
InitEvents(null, null);
}

Expand Down Expand Up @@ -58,19 +60,6 @@ private void RectangleSelection_PointerMoved(object sender, PointerRoutedEventAr
base.DrawRectangle(currentPoint, originDragPointShifted, uiElement);
// Selected area considering scrolled offset
var rect = new System.Drawing.Rectangle((int)Canvas.GetLeft(selectionRectangle), (int)Math.Min(originDragPoint.Y, currentPoint.Position.Y + verticalOffset), (int)selectionRectangle.Width, (int)Math.Abs(originDragPoint.Y - (currentPoint.Position.Y + verticalOffset)));
foreach (var item in uiElement.Items.ToList().Except(itemsPosition.Keys))
{
var listViewItem = (FrameworkElement)uiElement.ContainerFromItem(item); // Get ListViewItem
if (listViewItem == null)
{
continue; // Element is not loaded (virtualized list)
}

var gt = listViewItem.TransformToVisual(uiElement);
var itemStartPoint = gt.TransformPoint(new Point(0, verticalOffset)); // Get item position relative to the top of the list (considering scrolled offset)
var itemRect = new System.Drawing.Rectangle((int)itemStartPoint.X, (int)itemStartPoint.Y, (int)listViewItem.ActualWidth, (int)listViewItem.ActualHeight);
itemsPosition[item] = itemRect;
}

foreach (var item in itemsPosition.ToList())
{
Expand Down Expand Up @@ -108,17 +97,29 @@ private void RectangleSelection_PointerMoved(object sender, PointerRoutedEventAr

private void RectangleSelection_PointerPressed(object sender, PointerRoutedEventArgs e)
{
if (scrollViewer == null)
{
return;
}

itemsPosition.Clear();

scrollViewer.ViewChanged -= ScrollViewer_ViewChanged;
scrollViewer.ViewChanged += ScrollViewer_ViewChanged;

originDragPoint = new Point(e.GetCurrentPoint(uiElement).Position.X, e.GetCurrentPoint(uiElement).Position.Y); // Initial drag point relative to the topleft corner
prevSelectedItems = uiElement.SelectedItems.Cast<object>().ToList(); // Save current selected items
var verticalOffset = scrollViewer?.VerticalOffset ?? 0;

var verticalOffset = scrollViewer.VerticalOffset;
originDragPoint.Y += verticalOffset; // Initial drag point relative to the top of the list (considering scrolled offset)
if (!e.GetCurrentPoint(uiElement).Properties.IsLeftButtonPressed || e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Touch)
{
// Trigger only on left click, do not trigger with touch
return;
}

FetchItemsPosition();

selectionStrategy = e.KeyModifiers.HasFlag(VirtualKeyModifiers.Control) ?
new InvertPreviousItemSelectionStrategy(uiElement.SelectedItems, prevSelectedItems) :
e.KeyModifiers.HasFlag(VirtualKeyModifiers.Shift) ?
Expand All @@ -138,13 +139,41 @@ private void RectangleSelection_PointerPressed(object sender, PointerRoutedEvent
selectionState = SelectionState.Starting;
}

private void FetchItemsPosition()
{
var verticalOffset = scrollViewer.VerticalOffset;
foreach (var item in uiElement.Items.ToList().Except(itemsPosition.Keys))
{
var listViewItem = (FrameworkElement)uiElement.ContainerFromItem(item); // Get ListViewItem
if (listViewItem == null)
{
continue; // Element is not loaded (virtualized list)
}

var gt = listViewItem.TransformToVisual(uiElement);
var itemStartPoint = gt.TransformPoint(new Point(0, verticalOffset)); // Get item position relative to the top of the list (considering scrolled offset)
var itemRect = new System.Drawing.Rectangle((int)itemStartPoint.X, (int)itemStartPoint.Y, (int)listViewItem.ActualWidth, (int)listViewItem.ActualHeight);
itemsPosition[item] = itemRect;
}
}

private void ScrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
if (!timer.IsRunning)
{
timer.Debounce(FetchItemsPosition, TimeSpan.FromMilliseconds(1000));
}
}

private void RectangleSelection_PointerReleased(object sender, PointerRoutedEventArgs e)
{
Canvas.SetLeft(selectionRectangle, 0);
Canvas.SetTop(selectionRectangle, 0);
selectionRectangle.Width = 0;
selectionRectangle.Height = 0;
uiElement.PointerMoved -= RectangleSelection_PointerMoved;

scrollViewer.ViewChanged -= ScrollViewer_ViewChanged;
uiElement.ReleasePointerCapture(e.Pointer);
if (selectionChanged != null)
{
Expand Down