Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using System.Linq;
using System.Threading;

#nullable enable

namespace Files.App.Helpers
{
[DebuggerTypeProxy(typeof(CollectionDebugView<>))]
Expand All @@ -18,7 +20,8 @@ public class BulkConcurrentObservableCollection<T> : INotifyCollectionChanged, I
private readonly object syncRoot = new object();
private readonly List<T> collection = new List<T>();

public BulkConcurrentObservableCollection<GroupedCollection<T>> GroupedCollection { get; private set; }
// When 'GroupOption' is set to 'None' or when a folder is opened, 'GroupedCollection' is assigned 'null' by 'ItemGroupKeySelector'
public BulkConcurrentObservableCollection<GroupedCollection<T>>? GroupedCollection { get; private set; }
public bool IsSorted { get; set; }

public int Count
Expand All @@ -40,17 +43,18 @@ public int Count

public object SyncRoot => syncRoot;

public bool IsGrouped => !(ItemGroupKeySelector is null);
public bool IsGrouped => ItemGroupKeySelector is not null;

object IList.this[int index]
object? IList.this[int index]
{
get
{
return this[index];
}
set
{
this[index] = (T)value;
if (value is not null)
this[index] = (T)value;
}
}

Expand All @@ -76,39 +80,35 @@ public T this[int index]
}
}

public event NotifyCollectionChangedEventHandler CollectionChanged;
public event NotifyCollectionChangedEventHandler? CollectionChanged;

public event PropertyChangedEventHandler PropertyChanged;
public event PropertyChangedEventHandler? PropertyChanged;

private Func<T, string> itemGroupKeySelector;
private Func<T, string>? itemGroupKeySelector;

public Func<T, string> ItemGroupKeySelector
public Func<T, string>? ItemGroupKeySelector
{
get => itemGroupKeySelector;
set
{
itemGroupKeySelector = value;
if (value != null)
{
GroupedCollection ??= new BulkConcurrentObservableCollection<GroupedCollection<T>>();
}
else
{
GroupedCollection = null;
}
}
}

private Func<T, object> itemSortKeySelector;
private Func<T, object>? itemSortKeySelector;

public Func<T, object> ItemSortKeySelector
public Func<T, object>? ItemSortKeySelector
{
get => itemSortKeySelector;
set => itemSortKeySelector = value;
}

public Action<GroupedCollection<T>> GetGroupHeaderInfo { get; set; }
public Action<GroupedCollection<T>> GetExtendedGroupHeaderInfo { get; set; }
public Action<GroupedCollection<T>>? GetGroupHeaderInfo { get; set; }
public Action<GroupedCollection<T>>? GetExtendedGroupHeaderInfo { get; set; }

public BulkConcurrentObservableCollection()
{
Expand All @@ -131,54 +131,53 @@ protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e, bool coun
if (!isBulkOperationStarted)
{
if (countChanged)
{
PropertyChanged?.Invoke(this, EventArgsCache.CountPropertyChanged);
}

PropertyChanged?.Invoke(this, EventArgsCache.IndexerPropertyChanged);
CollectionChanged?.Invoke(this, e);
}

if (IsGrouped)
{
if (!(e.NewItems is null))
{
if (e.NewItems is not null)
AddItemsToGroup(e.NewItems.Cast<T>());
}
if (!(e.OldItems is null))
{

if (e.OldItems is not null)
RemoveItemsFromGroup(e.OldItems.Cast<T>());
}
}
}

public void ResetGroups(CancellationToken token = default)
{
if (!IsGrouped)
{
return;
}

// Prevents any unwanted errors caused by bindings updating
GroupedCollection.ForEach(x => x.Model.PausePropertyChangedNotifications());
GroupedCollection.Clear();
GroupedCollection?.ForEach(x => x.Model.PausePropertyChangedNotifications());
GroupedCollection?.Clear();
AddItemsToGroup(collection, token);
}

private void AddItemsToGroup(IEnumerable<T> items, CancellationToken token = default)
{
if (GroupedCollection is null)
return;

foreach (var item in items)
{
if (token.IsCancellationRequested)
{
return;
}

var key = GetGroupKeyForItem(item);
var groups = GroupedCollection.Where(x => x.Model.Key == key);
if (key is null)
return;

var groups = GroupedCollection?.Where(x => x.Model.Key == key);
if (item is IGroupableItem groupable)
{
groupable.Key = key;
}
if (groups.Count() > 0)

if (groups is not null &&
groups.Any())
{
var gp = groups.First();
gp.Add(item);
Expand All @@ -192,12 +191,11 @@ private void AddItemsToGroup(IEnumerable<T> items, CancellationToken token = def
};

group.GetExtendedGroupHeaderInfo = GetExtendedGroupHeaderInfo;
if (!(GetGroupHeaderInfo is null))
{
if (GetGroupHeaderInfo is not null)
GetGroupHeaderInfo.Invoke(group);
}
GroupedCollection.Add(group);
GroupedCollection.IsSorted = false;

GroupedCollection?.Add(group);
GroupedCollection!.IsSorted = false;
}
}
}
Expand All @@ -208,25 +206,26 @@ private void RemoveItemsFromGroup(IEnumerable<T> items)
{
var key = GetGroupKeyForItem(item);

var groups = GroupedCollection.Where(x => x.Model.Key == key);
if (groups.Count() > 0)
var group = GroupedCollection?.Where(x => x.Model.Key == key).FirstOrDefault();
if (group is not null)
{
groups.First().Remove(item);
group.Remove(item);
if (group.Count == 0)
GroupedCollection?.Remove(group);
}
}
}

private string GetGroupKeyForItem(T item)
private string? GetGroupKeyForItem(T item)
{
return ItemGroupKeySelector?.Invoke(item);
}

public virtual void EndBulkOperation()
{
if (!isBulkOperationStarted)
{
return;
}

isBulkOperationStarted = false;
GroupedCollection?.ForEach(gp => gp.EndBulkOperation());
GroupedCollection?.EndBulkOperation();
Expand All @@ -236,8 +235,11 @@ public virtual void EndBulkOperation()
PropertyChanged?.Invoke(this, EventArgsCache.IndexerPropertyChanged);
}

public void Add(T item)
public void Add(T? item)
{
if (item is null)
return;

lock (syncRoot)
{
collection.Add(item);
Expand All @@ -257,8 +259,11 @@ public void Clear()
OnCollectionChanged(EventArgsCache.ResetCollectionChanged);
}

public bool Contains(T item)
public bool Contains(T? item)
{
if (item is null)
return false;

lock (syncRoot)
{
return collection.Contains(item);
Expand All @@ -273,18 +278,19 @@ public void CopyTo(T[] array, int arrayIndex)
}
}

public bool Remove(T item)
public bool Remove(T? item)
{
if (item is null)
return false;

int index;

lock (syncRoot)
{
index = collection.IndexOf(item);

if (index == -1)
{
return false;
}

collection.RemoveAt(index);
}
Expand All @@ -303,16 +309,22 @@ IEnumerator IEnumerable.GetEnumerator()
return GetEnumerator();
}

public int IndexOf(T item)
public int IndexOf(T? item)
{
if (item is null)
return -1;

lock (syncRoot)
{
return collection.IndexOf(item);
}
}

public void Insert(int index, T item)
public void Insert(int index, T? item)
{
if (item is null)
return;

lock (syncRoot)
{
collection.Insert(index, item);
Expand All @@ -337,9 +349,7 @@ public void RemoveAt(int index)
public void AddRange(IEnumerable<T> items)
{
if (!items.Any())
{
return;
}

lock (syncRoot)
{
Expand All @@ -352,9 +362,7 @@ public void AddRange(IEnumerable<T> items)
public void InsertRange(int index, IEnumerable<T> items)
{
if (!items.Any())
{
return;
}

lock (syncRoot)
{
Expand All @@ -367,9 +375,7 @@ public void InsertRange(int index, IEnumerable<T> items)
public void RemoveRange(int index, int count)
{
if (count <= 0)
{
return;
}

List<T> items;

Expand All @@ -387,9 +393,7 @@ public void ReplaceRange(int index, IEnumerable<T> items)
var count = items.Count();

if (count == 0)
{
return;
}

List<T> oldItems;
List<T> newItems;
Expand Down Expand Up @@ -443,13 +447,14 @@ public void OrderOne(Func<List<T>, IEnumerable<T>> func, T item)
Remove(item);
var index = result.IndexOf(item);
if (index != -1)
{
Insert(index, item);
}
}

int IList.Add(object value)
int IList.Add(object? value)
{
if (value is null)
return -1;

int index;

lock (syncRoot)
Expand All @@ -461,13 +466,13 @@ int IList.Add(object value)
return index;
}

bool IList.Contains(object value) => Contains((T)value);
bool IList.Contains(object? value) => Contains((T?)value);

int IList.IndexOf(object value) => IndexOf((T)value);
int IList.IndexOf(object? value) => IndexOf((T?)value);

void IList.Insert(int index, object value) => Insert(index, (T)value);
void IList.Insert(int index, object? value) => Insert(index, (T?)value);

void IList.Remove(object value) => Remove((T)value);
void IList.Remove(object? value) => Remove((T?)value);

void ICollection.CopyTo(Array array, int index) => CopyTo((T[])array, index);

Expand Down