From df224cc7f3886bc9b3f4c21a103172f11bfd830f Mon Sep 17 00:00:00 2001 From: Mbucari Date: Wed, 1 Mar 2023 09:33:17 -0700 Subject: [PATCH] Move TrackedQueue to LibationUiBase --- .../ViewModels/ProcessQueueViewModel.cs | 15 +- .../Views/ProcessQueueControl.axaml.cs | 18 +- .../TrackedQueue[T].cs | 88 ++++-- .../ProcessQueue/TrackedQueue[T].cs | 250 ------------------ 4 files changed, 76 insertions(+), 295 deletions(-) rename Source/{LibationAvalonia/ViewModels => LibationUiBase}/TrackedQueue[T].cs (82%) delete mode 100644 Source/LibationWinForms/ProcessQueue/TrackedQueue[T].cs diff --git a/Source/LibationAvalonia/ViewModels/ProcessQueueViewModel.cs b/Source/LibationAvalonia/ViewModels/ProcessQueueViewModel.cs index c1dda16e..fbcc46d9 100644 --- a/Source/LibationAvalonia/ViewModels/ProcessQueueViewModel.cs +++ b/Source/LibationAvalonia/ViewModels/ProcessQueueViewModel.cs @@ -1,4 +1,5 @@ using ApplicationServices; +using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Threading; using DataLayer; @@ -16,9 +17,8 @@ namespace LibationAvalonia.ViewModels public class ProcessQueueViewModel : ViewModelBase, ILogForm { public ObservableCollection LogEntries { get; } = new(); - public TrackedQueue Items { get; } = new(); - - private TrackedQueue Queue => Items; + public AvaloniaList Items { get; } = new(); + public TrackedQueue Queue { get; } public ProcessBookViewModel SelectedItem { get; set; } public Task QueueRunner { get; private set; } public bool Running => !QueueRunner?.IsCompleted ?? false; @@ -28,6 +28,7 @@ namespace LibationAvalonia.ViewModels public ProcessQueueViewModel() { Logger = LogMe.RegisterForm(this); + Queue = new(Items); Queue.QueuededCountChanged += Queue_QueuededCountChanged; Queue.CompletedCountChanged += Queue_CompletedCountChanged; @@ -88,19 +89,19 @@ namespace LibationAvalonia.ViewModels public decimal SpeedLimitIncrement { get; private set; } - private void Queue_CompletedCountChanged(object sender, int e) + private async void Queue_CompletedCountChanged(object sender, int e) { int errCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.FailedAbort or ProcessBookResult.FailedSkip or ProcessBookResult.FailedRetry or ProcessBookResult.ValidationFail); int completeCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.Success); ErrorCount = errCount; CompletedCount = completeCount; - Dispatcher.UIThread.Post(() => this.RaisePropertyChanged(nameof(Progress))); + await Dispatcher.UIThread.InvokeAsync(() => this.RaisePropertyChanged(nameof(Progress))); } - private void Queue_QueuededCountChanged(object sender, int cueCount) + private async void Queue_QueuededCountChanged(object sender, int cueCount) { QueuedCount = cueCount; - Dispatcher.UIThread.Post(() => this.RaisePropertyChanged(nameof(Progress))); + await Dispatcher.UIThread.InvokeAsync(() => this.RaisePropertyChanged(nameof(Progress))); } public void WriteLine(string text) diff --git a/Source/LibationAvalonia/Views/ProcessQueueControl.axaml.cs b/Source/LibationAvalonia/Views/ProcessQueueControl.axaml.cs index 66673914..ed6dfef0 100644 --- a/Source/LibationAvalonia/Views/ProcessQueueControl.axaml.cs +++ b/Source/LibationAvalonia/Views/ProcessQueueControl.axaml.cs @@ -15,7 +15,7 @@ namespace LibationAvalonia.Views { public partial class ProcessQueueControl : UserControl { - private TrackedQueue Queue => _viewModel.Items; + private TrackedQueue Queue => _viewModel.Queue; private ProcessQueueViewModel _viewModel => DataContext as ProcessQueueViewModel; public ProcessQueueControl() @@ -76,14 +76,14 @@ namespace LibationAvalonia.Views }, }; - vm.Items.Enqueue(testList); - vm.Items.MoveNext(); - vm.Items.MoveNext(); - vm.Items.MoveNext(); - vm.Items.MoveNext(); - vm.Items.MoveNext(); - vm.Items.MoveNext(); - vm.Items.MoveNext(); + vm.Queue.Enqueue(testList); + vm.Queue.MoveNext(); + vm.Queue.MoveNext(); + vm.Queue.MoveNext(); + vm.Queue.MoveNext(); + vm.Queue.MoveNext(); + vm.Queue.MoveNext(); + vm.Queue.MoveNext(); return; } #endregion diff --git a/Source/LibationAvalonia/ViewModels/TrackedQueue[T].cs b/Source/LibationUiBase/TrackedQueue[T].cs similarity index 82% rename from Source/LibationAvalonia/ViewModels/TrackedQueue[T].cs rename to Source/LibationUiBase/TrackedQueue[T].cs index f4889a77..11f1a27e 100644 --- a/Source/LibationAvalonia/ViewModels/TrackedQueue[T].cs +++ b/Source/LibationUiBase/TrackedQueue[T].cs @@ -1,9 +1,8 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; -namespace LibationAvalonia.ViewModels +namespace LibationUiBase { public enum QueuePosition { @@ -33,7 +32,7 @@ namespace LibationAvalonia.ViewModels * and is stored in ObservableCollection.Items. When the primary list changes, the * secondary list is cleared and reset to match the primary. */ - public class TrackedQueue : ObservableCollection where T : class + public class TrackedQueue where T : class { public event EventHandler CompletedCountChanged; public event EventHandler QueuededCountChanged; @@ -47,6 +46,60 @@ namespace LibationAvalonia.ViewModels private readonly List _completed = new(); private readonly object lockObject = new(); + private readonly ICollection _underlyingList; + + public TrackedQueue(ICollection underlyingList = null) + { + _underlyingList = underlyingList; + } + + public T this[int index] + { + get + { + lock (lockObject) + { + if (index < _completed.Count) + return _completed[index]; + index -= _completed.Count; + + if (index == 0 && Current != null) return Current; + + if (Current != null) index--; + + if (index < _queued.Count) return _queued.ElementAt(index); + + throw new IndexOutOfRangeException(); + } + } + } + + public int Count + { + get + { + lock (lockObject) + { + return _queued.Count + _completed.Count + (Current == null ? 0 : 1); + } + } + } + + public int IndexOf(T item) + { + lock (lockObject) + { + if (_completed.Contains(item)) + return _completed.IndexOf(item); + + if (Current == item) return _completed.Count; + + if (_queued.Contains(item)) + return _queued.IndexOf(item) + (Current is null ? 0 : 1); + return -1; + } + } + public bool RemoveQueued(T item) { bool itemsRemoved; @@ -188,29 +241,6 @@ namespace LibationAvalonia.ViewModels } } - public bool TryPeek(out T item) - { - lock (lockObject) - { - if (_queued.Count == 0) - { - item = null; - return false; - } - item = _queued[0]; - return true; - } - } - - public T Peek() - { - lock (lockObject) - { - if (_queued.Count == 0) throw new InvalidOperationException("Queue empty"); - return _queued.Count > 0 ? _queued[0] : default; - } - } - public void Enqueue(IEnumerable item) { int queueCount; @@ -220,15 +250,15 @@ namespace LibationAvalonia.ViewModels queueCount = _queued.Count; } foreach (var i in item) - base.Add(i); + _underlyingList?.Add(i); QueuededCountChanged?.Invoke(this, queueCount); } private void RebuildSecondary() { - base.ClearItems(); + _underlyingList?.Clear(); foreach (var item in GetAllItems()) - base.Add(item); + _underlyingList?.Add(item); } public IEnumerable GetAllItems() diff --git a/Source/LibationWinForms/ProcessQueue/TrackedQueue[T].cs b/Source/LibationWinForms/ProcessQueue/TrackedQueue[T].cs deleted file mode 100644 index 80991f34..00000000 --- a/Source/LibationWinForms/ProcessQueue/TrackedQueue[T].cs +++ /dev/null @@ -1,250 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace LibationWinForms.ProcessQueue -{ - public enum QueuePosition - { - Fisrt, - OneUp, - OneDown, - Last - } - - /* - * This data structure is like lifting a metal chain one link at a time. - * Each time you grab and lift a new link (MoveNext call): - * - * 1) you're holding a new link in your hand (Current) - * 2) the remaining chain to be lifted shortens by 1 link (Queued) - * 3) the pile of chain at your feet grows by 1 link (Completed) - * - * The index is the link position from the first link you lifted to the - * last one in the chain. - */ - public class TrackedQueue where T : class - { - public event EventHandler CompletedCountChanged; - public event EventHandler QueuededCountChanged; - public T Current { get; private set; } - - public IReadOnlyList Queued => _queued; - public IReadOnlyList Completed => _completed; - - private readonly List _queued = new(); - private readonly List _completed = new(); - private readonly object lockObject = new(); - - public T this[int index] - { - get - { - lock (lockObject) - { - if (index < _completed.Count) - return _completed[index]; - index -= _completed.Count; - - if (index == 0 && Current != null) return Current; - - if (Current != null) index--; - - if (index < _queued.Count) return _queued.ElementAt(index); - - throw new IndexOutOfRangeException(); - } - } - } - - public int Count - { - get - { - lock (lockObject) - { - return _queued.Count + _completed.Count + (Current == null ? 0 : 1); - } - } - } - - public int IndexOf(T item) - { - lock (lockObject) - { - if (_completed.Contains(item)) - return _completed.IndexOf(item); - - if (Current == item) return _completed.Count; - - if (_queued.Contains(item)) - return _queued.IndexOf(item) + (Current is null ? 0 : 1); - return -1; - } - } - - public bool RemoveQueued(T item) - { - bool itemsRemoved; - int queuedCount; - - lock (lockObject) - { - itemsRemoved = _queued.Remove(item); - queuedCount = _queued.Count; - } - - if (itemsRemoved) - QueuededCountChanged?.Invoke(this, queuedCount); - return itemsRemoved; - } - - public void ClearCurrent() - { - lock(lockObject) - Current = null; - } - - public bool RemoveCompleted(T item) - { - bool itemsRemoved; - int completedCount; - - lock (lockObject) - { - itemsRemoved = _completed.Remove(item); - completedCount = _completed.Count; - } - - if (itemsRemoved) - CompletedCountChanged?.Invoke(this, completedCount); - return itemsRemoved; - } - - public void ClearQueue() - { - lock (lockObject) - _queued.Clear(); - QueuededCountChanged?.Invoke(this, 0); - } - - public void ClearCompleted() - { - lock (lockObject) - _completed.Clear(); - CompletedCountChanged?.Invoke(this, 0); - } - - public bool Any(Func predicate) - { - lock (lockObject) - { - return (Current != null && predicate(Current)) || _completed.Any(predicate) || _queued.Any(predicate); - } - } - - public void MoveQueuePosition(T item, QueuePosition requestedPosition) - { - lock (lockObject) - { - if (_queued.Count == 0 || !_queued.Contains(item)) return; - - if ((requestedPosition == QueuePosition.Fisrt || requestedPosition == QueuePosition.OneUp) && _queued[0] == item) - return; - if ((requestedPosition == QueuePosition.Last || requestedPosition == QueuePosition.OneDown) && _queued[^1] == item) - return; - - int queueIndex = _queued.IndexOf(item); - - if (requestedPosition == QueuePosition.OneUp) - { - _queued.RemoveAt(queueIndex); - _queued.Insert(queueIndex - 1, item); - } - else if (requestedPosition == QueuePosition.OneDown) - { - _queued.RemoveAt(queueIndex); - _queued.Insert(queueIndex + 1, item); - } - else if (requestedPosition == QueuePosition.Fisrt) - { - _queued.RemoveAt(queueIndex); - _queued.Insert(0, item); - } - else - { - _queued.RemoveAt(queueIndex); - _queued.Insert(_queued.Count, item); - } - } - } - - public bool MoveNext() - { - int completedCount = 0, queuedCount = 0; - bool completedChanged = false; - try - { - lock (lockObject) - { - if (Current != null) - { - _completed.Add(Current); - completedCount = _completed.Count; - completedChanged = true; - } - if (_queued.Count == 0) - { - Current = null; - return false; - } - Current = _queued[0]; - _queued.RemoveAt(0); - - queuedCount = _queued.Count; - return true; - } - } - finally - { - if (completedChanged) - CompletedCountChanged?.Invoke(this, completedCount); - QueuededCountChanged?.Invoke(this, queuedCount); - } - } - - public bool TryPeek(out T item) - { - lock (lockObject) - { - if (_queued.Count == 0) - { - item = null; - return false; - } - item = _queued[0]; - return true; - } - } - - public T Peek() - { - lock (lockObject) - { - if (_queued.Count == 0) throw new InvalidOperationException("Queue empty"); - return _queued.Count > 0 ? _queued[0] : default; - } - } - - public void Enqueue(IEnumerable item) - { - int queueCount; - lock (lockObject) - { - _queued.AddRange(item); - queueCount = _queued.Count; - } - QueuededCountChanged?.Invoke(this, queueCount); - } - } -}