Timeline: use ObservableCollection instead of DynamicData
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
using FileTime.Core.Models;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace FileTime.App.Core.ViewModels.Timeline;
|
||||
|
||||
public interface IParallelCommandsViewModel
|
||||
{
|
||||
BindedCollection<ICommandTimeStateViewModel> Commands { get; }
|
||||
ObservableCollection<ICommandTimeStateViewModel> Commands { get; }
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
using FileTime.Core.Models;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace FileTime.App.Core.ViewModels.Timeline;
|
||||
|
||||
public interface ITimelineViewModel
|
||||
{
|
||||
BindedCollection<IParallelCommandsViewModel> ParallelCommandsGroups { get; }
|
||||
ObservableCollection<IParallelCommandsViewModel> ParallelCommandsGroups { get; }
|
||||
}
|
||||
@@ -1,19 +1,21 @@
|
||||
using DynamicData.Alias;
|
||||
using FileTime.Core.Extensions;
|
||||
using FileTime.Core.Models;
|
||||
using System.Collections.ObjectModel;
|
||||
using FileTime.Core.Timeline;
|
||||
using ObservableComputations;
|
||||
|
||||
namespace FileTime.App.Core.ViewModels.Timeline;
|
||||
|
||||
public class ParallelCommandsViewModel : IParallelCommandsViewModel
|
||||
public class ParallelCommandsViewModel : IParallelCommandsViewModel, IDisposable
|
||||
{
|
||||
public BindedCollection<ICommandTimeStateViewModel> Commands { get; }
|
||||
private readonly OcConsumer _ocConsumer = new();
|
||||
public ObservableCollection<ICommandTimeStateViewModel> Commands { get; }
|
||||
|
||||
public ParallelCommandsViewModel(ParallelCommands parallelCommands)
|
||||
{
|
||||
Commands = parallelCommands
|
||||
.Commands
|
||||
.Select(c => new CommandTimeStateViewModel(c) as ICommandTimeStateViewModel)
|
||||
.ToBindedCollection();
|
||||
.Selecting(c => new CommandTimeStateViewModel(c) as ICommandTimeStateViewModel)
|
||||
.For(_ocConsumer);
|
||||
}
|
||||
|
||||
public void Dispose() => _ocConsumer.Dispose();
|
||||
}
|
||||
@@ -1,20 +1,22 @@
|
||||
using DynamicData.Alias;
|
||||
using FileTime.Core.Extensions;
|
||||
using FileTime.Core.Models;
|
||||
using System.Collections.ObjectModel;
|
||||
using FileTime.Core.Timeline;
|
||||
using ObservableComputations;
|
||||
|
||||
namespace FileTime.App.Core.ViewModels.Timeline;
|
||||
|
||||
public class TimelineViewModel : ITimelineViewModel
|
||||
public class TimelineViewModel : ITimelineViewModel, IDisposable
|
||||
{
|
||||
public BindedCollection<IParallelCommandsViewModel> ParallelCommandsGroups { get; }
|
||||
private readonly OcConsumer _ocConsumer = new();
|
||||
public ObservableCollection<IParallelCommandsViewModel> ParallelCommandsGroups { get; }
|
||||
|
||||
public TimelineViewModel(ICommandScheduler commandScheduler)
|
||||
{
|
||||
ParallelCommandsGroups =
|
||||
commandScheduler
|
||||
.CommandsToRun
|
||||
.Select(p => new ParallelCommandsViewModel(p) as IParallelCommandsViewModel)
|
||||
.ToBindedCollection();
|
||||
.Selecting(p => new ParallelCommandsViewModel(p) as IParallelCommandsViewModel)
|
||||
.For(_ocConsumer);
|
||||
}
|
||||
|
||||
public void Dispose() => _ocConsumer.Dispose();
|
||||
}
|
||||
@@ -15,5 +15,7 @@ public interface ITheme
|
||||
IColor? MarkedSelectedItemBackgroundColor { get; }
|
||||
IColor? SelectedItemColor { get; }
|
||||
IColor? SelectedTabBackgroundColor { get; }
|
||||
IColor? WarningForegroundColor { get; }
|
||||
IColor? ErrorForegroundColor { get; }
|
||||
ListViewItemTheme ListViewItemTheme { get; }
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using DynamicData;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.Core.Extensions;
|
||||
|
||||
public static class BindedCollectionExtensions
|
||||
{
|
||||
public static BindedCollection<T> ToBindedCollection<T>(this IObservable<IChangeSet<T>> source)
|
||||
{
|
||||
return new BindedCollection<T>(source);
|
||||
}
|
||||
|
||||
public static BindedCollection<T> ToBindedCollection<T>(this IObservable<IObservable<IChangeSet<T>>?> source)
|
||||
{
|
||||
return new BindedCollection<T>(source);
|
||||
}
|
||||
|
||||
public static BindedCollection<T, TKey> ToBindedCollection<T, TKey>(this IObservable<IChangeSet<T, TKey>> source) where TKey : notnull
|
||||
{
|
||||
return new BindedCollection<T, TKey>(source);
|
||||
}
|
||||
|
||||
public static BindedCollection<T, TKey> ToBindedCollection<T, TKey>(this IObservable<IObservable<IChangeSet<T, TKey>>?> source) where TKey : notnull
|
||||
{
|
||||
return new BindedCollection<T, TKey>(source);
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using DynamicData;
|
||||
using PropertyChanged.SourceGenerator;
|
||||
|
||||
namespace FileTime.Core.Models;
|
||||
|
||||
public partial class BindedCollection<T> : IDisposable, INotifyPropertyChanged
|
||||
{
|
||||
private readonly IDisposable? _disposable;
|
||||
private IDisposable? _innerDisposable;
|
||||
|
||||
[Notify] private ReadOnlyObservableCollection<T>? _collection;
|
||||
|
||||
public BindedCollection()
|
||||
{
|
||||
}
|
||||
|
||||
public BindedCollection(IObservable<IChangeSet<T>> dynamicList)
|
||||
{
|
||||
_disposable = dynamicList
|
||||
.Bind(out var collection)
|
||||
.DisposeMany()
|
||||
.Subscribe();
|
||||
|
||||
_collection = collection;
|
||||
}
|
||||
|
||||
public BindedCollection(IObservable<IObservable<IChangeSet<T>>?> dynamicListSource)
|
||||
{
|
||||
_disposable = dynamicListSource.Subscribe(dynamicList =>
|
||||
{
|
||||
_innerDisposable?.Dispose();
|
||||
if (dynamicList is not null)
|
||||
{
|
||||
_innerDisposable = dynamicList
|
||||
.Bind(out var collection)
|
||||
.DisposeMany()
|
||||
.Subscribe();
|
||||
|
||||
Collection = collection;
|
||||
}
|
||||
else
|
||||
{
|
||||
Collection = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_disposable?.Dispose();
|
||||
_innerDisposable?.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class BindedCollection<T, TKey> : IDisposable, INotifyPropertyChanged where TKey : notnull
|
||||
{
|
||||
private readonly IDisposable? _disposable;
|
||||
private IDisposable? _innerDisposable;
|
||||
|
||||
[Notify] private ReadOnlyObservableCollection<T>? _collection;
|
||||
|
||||
public BindedCollection()
|
||||
{
|
||||
}
|
||||
|
||||
public BindedCollection(IObservable<IChangeSet<T, TKey>> dynamicList)
|
||||
{
|
||||
_disposable = dynamicList
|
||||
.Bind(out var collection)
|
||||
.DisposeMany()
|
||||
.Subscribe();
|
||||
|
||||
_collection = collection;
|
||||
}
|
||||
|
||||
public BindedCollection(IObservable<IObservable<IChangeSet<T, TKey>>?> dynamicListSource)
|
||||
{
|
||||
_disposable = dynamicListSource.Subscribe(dynamicList =>
|
||||
{
|
||||
_innerDisposable?.Dispose();
|
||||
if (dynamicList is not null)
|
||||
{
|
||||
_innerDisposable = dynamicList
|
||||
.Bind(out var collection)
|
||||
.DisposeMany()
|
||||
.Subscribe();
|
||||
|
||||
Collection = collection;
|
||||
}
|
||||
else
|
||||
{
|
||||
Collection = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_disposable?.Dispose();
|
||||
_innerDisposable?.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using DynamicData;
|
||||
using System.Collections.ObjectModel;
|
||||
using FileTime.Core.Command;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
@@ -8,7 +8,7 @@ public interface ICommandScheduler
|
||||
{
|
||||
Task AddCommand(ICommand command, int? batchId = null, bool toNewBatch = false);
|
||||
IObservable<FullName> ContainerToRefresh { get; }
|
||||
IObservable<IChangeSet<ParallelCommands>> CommandsToRun { get; }
|
||||
ReadOnlyObservableCollection<ParallelCommands> CommandsToRun { get; }
|
||||
bool IsRunningEnabled { get; }
|
||||
void RefreshContainer(FullName container);
|
||||
Task SetRunningEnabledAsync(bool value);
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
using DynamicData;
|
||||
using System.Collections.ObjectModel;
|
||||
using FileTime.Core.Command;
|
||||
using FileTime.Core.Extensions;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.Core.Timeline;
|
||||
|
||||
public class ParallelCommands : IDisposable
|
||||
public class ParallelCommands
|
||||
{
|
||||
private static ushort _idCounter;
|
||||
private readonly SourceList<CommandTimeState> _commands;
|
||||
private readonly ObservableCollection<CommandTimeState> _commands;
|
||||
private PointInTime? _startTime;
|
||||
|
||||
public ushort Id { get; }
|
||||
|
||||
public IObservable<IChangeSet<CommandTimeState>> Commands { get; }
|
||||
|
||||
public BindedCollection<CommandTimeState> CommandsCollection { get; }
|
||||
public ReadOnlyObservableCollection<CommandTimeState> Commands { get; }
|
||||
public int CommandCount => _commands.Count;
|
||||
|
||||
public PointInTime? Result { get; private set; }
|
||||
@@ -37,10 +33,8 @@ public class ParallelCommands : IDisposable
|
||||
{
|
||||
Id = _idCounter++;
|
||||
|
||||
_commands = new SourceList<CommandTimeState>();
|
||||
_commands.Edit((innerList) => innerList.AddRange(commands));
|
||||
Commands = _commands.Connect();
|
||||
CommandsCollection = Commands.ToBindedCollection();
|
||||
_commands = new ObservableCollection<CommandTimeState>(commands);
|
||||
Commands = new(_commands);
|
||||
|
||||
Result = result;
|
||||
}
|
||||
@@ -85,7 +79,7 @@ public class ParallelCommands : IDisposable
|
||||
|
||||
public async Task RemoveCommand(ICommand command)
|
||||
{
|
||||
var commandTimeState = _commands.Items.First(c => c.Command == command);
|
||||
var commandTimeState = _commands.First(c => c.Command == command);
|
||||
_commands.Remove(commandTimeState);
|
||||
await RefreshResult();
|
||||
}
|
||||
@@ -93,7 +87,7 @@ public class ParallelCommands : IDisposable
|
||||
public async Task<PointInTime?> RefreshResult()
|
||||
{
|
||||
var result = StartTime;
|
||||
foreach (var commandTimeState in _commands.Items)
|
||||
foreach (var commandTimeState in _commands)
|
||||
{
|
||||
await commandTimeState.UpdateStateAsync(result);
|
||||
if (result != null)
|
||||
@@ -113,9 +107,4 @@ public class ParallelCommands : IDisposable
|
||||
Result = result;
|
||||
return Result;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
CommandsCollection.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using DynamicData;
|
||||
using FileTime.Core.Command;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace FileTime.Core.Timeline;
|
||||
|
||||
public class CommandScheduler : ICommandScheduler
|
||||
{
|
||||
private readonly SourceList<ParallelCommands> _commandsToRun = new();
|
||||
private readonly ObservableCollection<ParallelCommands> _commandsToRun = new();
|
||||
private readonly List<ICommandExecutor> _commandExecutors = new();
|
||||
private readonly Subject<FullName> _containerToRefresh = new();
|
||||
|
||||
@@ -29,11 +29,11 @@ public class CommandScheduler : ICommandScheduler
|
||||
}
|
||||
}
|
||||
|
||||
public IObservable<IChangeSet<ParallelCommands>> CommandsToRun { get; }
|
||||
public ReadOnlyObservableCollection<ParallelCommands> CommandsToRun { get; }
|
||||
|
||||
public CommandScheduler(ILocalCommandExecutor localExecutor)
|
||||
{
|
||||
CommandsToRun = _commandsToRun.Connect();
|
||||
CommandsToRun = new(_commandsToRun);
|
||||
|
||||
ContainerToRefresh = _containerToRefresh.AsObservable();
|
||||
|
||||
@@ -55,16 +55,16 @@ public class CommandScheduler : ICommandScheduler
|
||||
}
|
||||
else if (toNewBatch)
|
||||
{
|
||||
batchToAdd = new ParallelCommands(_commandsToRun.Items.Last().Result);
|
||||
batchToAdd = new ParallelCommands(_commandsToRun.Last().Result);
|
||||
_commandsToRun.Add(batchToAdd);
|
||||
}
|
||||
else if (batchId != null && _commandsToRun.Items.First(b => b.Id == batchId) is { } parallelCommands)
|
||||
else if (batchId != null && _commandsToRun.First(b => b.Id == batchId) is { } parallelCommands)
|
||||
{
|
||||
batchToAdd = parallelCommands;
|
||||
}
|
||||
else
|
||||
{
|
||||
batchToAdd = _commandsToRun.Items.First();
|
||||
batchToAdd = _commandsToRun.First();
|
||||
}
|
||||
|
||||
await batchToAdd.AddCommand(command);
|
||||
@@ -88,7 +88,7 @@ public class CommandScheduler : ICommandScheduler
|
||||
{
|
||||
if (!_isRunningEnabled) return;
|
||||
|
||||
var commandsToExecute = _commandsToRun.Items.FirstOrDefault()?.CommandsCollection.Collection;
|
||||
var commandsToExecute = _commandsToRun.FirstOrDefault()?.Commands;
|
||||
if (commandsToExecute is null || commandsToExecute.All(c => c.ExecutionState != ExecutionState.Initializing && c.ExecutionState != ExecutionState.Waiting)) return;
|
||||
|
||||
foreach (var commandToExecute in commandsToExecute)
|
||||
@@ -113,17 +113,14 @@ public class CommandScheduler : ICommandScheduler
|
||||
return _commandExecutors[0];
|
||||
}
|
||||
|
||||
private async void ExecutorOnCommandFinished(object? sender, ICommand command)
|
||||
{
|
||||
private async void ExecutorOnCommandFinished(object? sender, ICommand command) =>
|
||||
await RunWithLockAsync(async () =>
|
||||
{
|
||||
var firstCommandBlock = _commandsToRun
|
||||
.Items
|
||||
.FirstOrDefault();
|
||||
var state = firstCommandBlock
|
||||
?.CommandsCollection
|
||||
.Collection
|
||||
?.FirstOrDefault(c => c.Command == command);
|
||||
?.Commands
|
||||
.FirstOrDefault(c => c.Command == command);
|
||||
|
||||
if (state is null) return;
|
||||
|
||||
@@ -137,13 +134,12 @@ public class CommandScheduler : ICommandScheduler
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async Task RefreshCommands()
|
||||
{
|
||||
var currentTime = PointInTime.CreateEmpty();
|
||||
|
||||
foreach (var batch in _commandsToRun.Items)
|
||||
foreach (var batch in _commandsToRun)
|
||||
{
|
||||
currentTime = await batch.SetStartTimeAsync(currentTime);
|
||||
}
|
||||
|
||||
@@ -321,7 +321,7 @@
|
||||
</ia:DataTriggerBehavior>
|
||||
</i:Interaction.Behaviors>
|
||||
<ItemsControl
|
||||
ItemsSource="{Binding TimelineViewModel.ParallelCommandsGroups.Collection}"
|
||||
ItemsSource="{Binding TimelineViewModel.ParallelCommandsGroups}"
|
||||
VerticalAlignment="Stretch"
|
||||
x:Name="CommandGroups">
|
||||
<ItemsControl.ItemsPanel>
|
||||
@@ -333,7 +333,7 @@
|
||||
<DataTemplate>
|
||||
<Grid ColumnDefinitions="300,10" VerticalAlignment="Stretch">
|
||||
<ScrollViewer>
|
||||
<ItemsControl ItemsSource="{Binding Commands.Collection}">
|
||||
<ItemsControl ItemsSource="{Binding Commands}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
|
||||
Reference in New Issue
Block a user