Skip to content

Commit

Permalink
fix(dragdrop): Fix support of TabView reordering
Browse files Browse the repository at this point in the history
  • Loading branch information
dr1rrb committed Dec 1, 2020
1 parent 7725576 commit 6157b15
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 65 deletions.
22 changes: 17 additions & 5 deletions src/Uno.Foundation/Collections/IObservableVector.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
namespace Windows.Foundation.Collections
{
internal interface IObservableVector
{
{
/// <summary>
/// Occurs when the vector changes.
/// </summary>
event VectorChangedEventHandler UntypedVectorChanged;
}
/// Occurs when the vector changes.
/// </summary>
event VectorChangedEventHandler UntypedVectorChanged;

public object this[int index] { get; }

public int Count { get; }

public int IndexOf(object item);

public void Add(object item);

public void Insert(int index, object item);

public void RemoveAt(int index);
}
}
4 changes: 4 additions & 0 deletions src/Uno.Foundation/Collections/ObservableVector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ internal class ObservableVector<T> : IObservableVector<T>, IObservableVector
{
private readonly List<T> _list = new List<T>();

object IObservableVector.this[int index] => this[index];
public virtual T this[int index]
{
get { return _list[index]; }
Expand All @@ -33,6 +34,7 @@ public virtual T this[int index]
public event VectorChangedEventHandler<T> VectorChanged;
public event VectorChangedEventHandler UntypedVectorChanged;

void IObservableVector.Add(object item) => Add((T)item);
public virtual void Add(T item)
{
_list.Add(item);
Expand All @@ -53,8 +55,10 @@ public virtual void Clear()

public virtual IEnumerator<T> GetEnumerator() => _list.GetEnumerator();

int IObservableVector.IndexOf(object item) => item is T t ? IndexOf(t) : -1;
public int IndexOf(T item) => _list.IndexOf(item);

void IObservableVector.Insert(int index, object item) => Insert(index, (T)item);
public virtual void Insert(int index, T item)
{
_list.Insert(index, item);
Expand Down
149 changes: 89 additions & 60 deletions src/Uno.UI/UI/Xaml/Controls/ListViewBase/ListViewBase.DragDrop.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
using System;
#nullable enable

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Uno.Extensions;
using Uno.Extensions.Specialized;
using Uno.Logging;
Expand Down Expand Up @@ -80,18 +84,18 @@ private static void OnItemContainerDragCompleted(UIElement sender, DropCompleted

if (ItemsControlFromItemContainer(sender) is ListViewBase that)
{
that.DragEnter -= OnReorderUpdated;
that.DragOver -= OnReorderUpdated;
that.DragLeave -= OnReorderCompleted;
that.Drop -= OnReorderCompleted;

if (that.CanDragItems)
{
var items = innerArgs.Info.Data.FindRawData(DragItemsFormatId) as IReadOnlyList<object> ?? new List<object>(0);
var args = new DragItemsCompletedEventArgs(innerArgs, items);

that.DragItemsCompleted?.Invoke(that, args);
}

that.DragEnter -= OnReorderUpdated;
that.DragOver -= OnReorderUpdated;
that.DragLeave -= OnReorderCompleted;
that.Drop -= OnReorderCompleted;
}
}

Expand Down Expand Up @@ -127,26 +131,17 @@ private static void OnReorderCompleted(object sender, DragEventArgs dragEventArg
var updatedIndex = default(IndexPath?);
that.CompleteReordering(container, item, ref updatedIndex);

if (!that.IsGrouping
&& updatedIndex.HasValue
&& dragEventArgs.DataView.FindRawData(DragItemsFormatId) is List<object> movedItems)
if (that.IsGrouping
|| !updatedIndex.HasValue
|| !(dragEventArgs.DataView.FindRawData(DragItemsFormatId) is List<object> movedItems))
{
if (that.UnwrapItemsSource() is {} unwrappedSource)
{
// The UWP ListView seems to automatically push back changes only if the ItemsSource inherits from ObservableCollection
var srcType = unwrappedSource.GetType();
if (srcType.IsGenericType
&& srcType.GetGenericTypeDefinition() == typeof(ObservableCollection<>)
&& srcType.GetMethod(nameof(ObservableCollection<object>.Move), new[] {typeof(int), typeof(int)}) is {} mv)
{
ProcessMove(
((ICollection)unwrappedSource).Count,
((ICollection)unwrappedSource).IndexOf,
(oldIndex, newIndex) => mv.Invoke(unwrappedSource, new object[] {oldIndex, newIndex}));
}
}
else // The ListView was created with items defined in XAML
{
return;
}

var unwrappedSource = that.UnwrapItemsSource();
switch (unwrappedSource)
{
case null: // The ListView was created with items defined in XAML
var items = that.Items;
ProcessMove(
items.Count,
Expand All @@ -164,52 +159,86 @@ private static void OnReorderCompleted(object sender, DragEventArgs dragEventArg
items.Insert(newIndex, item);
}
});
}
break;

void ProcessMove(
int count,
Func<object, int> indexOf,
Action<int, int> mv)
{
var indexOfDraggedItem = indexOf(item);
if (indexOfDraggedItem < 0)
case IObservableVector vector:
ProcessMove(
vector.Count,
vector.IndexOf,
(oldIndex, newIndex) =>
{
var item = vector[oldIndex];
vector.RemoveAt(oldIndex);
if (newIndex >= vector.Count)
{
vector.Add(item);
}
else
{
vector.Insert(newIndex, item);
}
});
break;

default:
// The UWP ListView seems to automatically push back changes only if the ItemsSource inherits from ObservableCollection
var srcType = unwrappedSource.GetType();
if (srcType.IsGenericType
&& srcType.GetGenericTypeDefinition() == typeof(ObservableCollection<>)
&& srcType.GetMethod(nameof(ObservableCollection<object>.Move), new[] { typeof(int), typeof(int) }) is { } mv)
{
ProcessMove(
((ICollection)unwrappedSource).Count,
((ICollection)unwrappedSource).IndexOf,
(oldIndex, newIndex) => mv.Invoke(unwrappedSource, new object[] { oldIndex, newIndex }));
return;
}
break;
}

int newIndex;
if (updatedIndex.Value.Row == int.MaxValue)
{
// I.e. we are at the end, there is no items below
newIndex = count - 1;
}
else
void ProcessMove(
int count,
Func<object, int> indexOf,
Action<int, int> mv)
{
var indexOfDraggedItem = indexOf(item);
if (indexOfDraggedItem < 0)
{
return;
}

int newIndex;
if (updatedIndex.Value.Row == int.MaxValue)
{
// I.e. we are at the end, there is no items below
newIndex = count - 1;
}
else
{
newIndex = that.GetIndexFromIndexPath(updatedIndex.Value);
if (indexOfDraggedItem < newIndex)
{
newIndex = that.GetIndexFromIndexPath(updatedIndex.Value);
if (indexOfDraggedItem < newIndex)
{
// If we've moved items down, we have to take in consideration that the updatedIndex
// is already assuming that the item has been removed, so it's offsetted by 1.
newIndex--;
}
// If we've moved items down, we have to take in consideration that the updatedIndex
// is already assuming that the item has been removed, so it's offsetted by 1.
newIndex--;
}
}

for (var i = 0; i < movedItems.Count; i++)
{
var movedItem = movedItems[i];
var oldIndex = indexOf(movedItem);
for (var i = 0; i < movedItems.Count; i++)
{
var movedItem = movedItems[i];
var oldIndex = indexOf(movedItem);

if (oldIndex < 0 || oldIndex == newIndex)
{
continue; // Item removed or already at the right place, nothing to do.
}
if (oldIndex < 0 || oldIndex == newIndex)
{
continue; // Item removed or already at the right place, nothing to do.
}

mv(oldIndex, newIndex);
mv(oldIndex, newIndex);

if (oldIndex > newIndex)
{
newIndex++;
}
if (oldIndex > newIndex)
{
newIndex++;
}
}
}
Expand Down

0 comments on commit 6157b15

Please sign in to comment.