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