Command status

This commit is contained in:
2022-02-02 18:59:37 +01:00
parent d795ddcd11
commit f40c84a123
21 changed files with 331 additions and 70 deletions

View File

@@ -6,20 +6,19 @@ namespace FileTime.App.Core.Clipboard
public class Clipboard : IClipboard public class Clipboard : IClipboard
{ {
private List<AbsolutePath> _content; private List<AbsolutePath> _content;
public IReadOnlyList<AbsolutePath> Content { get; } public IReadOnlyList<AbsolutePath> Content { get; private set; }
public Type? CommandType { get; private set; } public Type? CommandType { get; private set; }
public Clipboard() public Clipboard()
{ {
_content = new List<AbsolutePath>(); ResetContent();
Content = _content.AsReadOnly();
} }
public void AddContent(AbsolutePath absolutePath) public void AddContent(AbsolutePath absolutePath)
{ {
foreach (var content in _content) foreach (var content in _content)
{ {
if (content.IsEqual(absolutePath)) return; if (content.Equals(absolutePath)) return;
} }
_content.Add(new AbsolutePath(absolutePath)); _content.Add(new AbsolutePath(absolutePath));
@@ -29,7 +28,7 @@ namespace FileTime.App.Core.Clipboard
{ {
for (var i = 0; i < _content.Count; i++) for (var i = 0; i < _content.Count; i++)
{ {
if (_content[i].IsEqual(absolutePath)) if (_content[i].Equals(absolutePath))
{ {
_content.RemoveAt(i--); _content.RemoveAt(i--);
} }
@@ -38,7 +37,7 @@ namespace FileTime.App.Core.Clipboard
public void Clear() public void Clear()
{ {
_content = new List<AbsolutePath>(); ResetContent();
CommandType = null; CommandType = null;
} }
@@ -46,5 +45,11 @@ namespace FileTime.App.Core.Clipboard
{ {
CommandType = typeof(T); CommandType = typeof(T);
} }
private void ResetContent()
{
_content = new List<AbsolutePath>();
Content = _content.AsReadOnly();
}
} }
} }

View File

@@ -35,7 +35,7 @@ namespace FileTime.App.Core.Tab
foreach (var content in _markedItems[container]) foreach (var content in _markedItems[container])
{ {
if (content.IsEqual(path)) return; if (content.Equals(path)) return;
} }
var tabItem = new AbsolutePath(path); var tabItem = new AbsolutePath(path);
@@ -50,7 +50,7 @@ namespace FileTime.App.Core.Tab
var markedItems = _markedItems[container]; var markedItems = _markedItems[container];
for (var i = 0; i < markedItems.Count; i++) for (var i = 0; i < markedItems.Count; i++)
{ {
if (markedItems[i].IsEqual(path)) if (markedItems[i].Equals(path))
{ {
await ItemUnmarked.InvokeAsync(this, markedItems[i]); await ItemUnmarked.InvokeAsync(this, markedItems[i]);
markedItems.RemoveAt(i--); markedItems.RemoveAt(i--);

View File

@@ -1,7 +1,6 @@
using FileTime.App.Core.Clipboard; using FileTime.App.Core.Clipboard;
using FileTime.Core.Command; using FileTime.Core.Command;
using FileTime.Core.Providers; using FileTime.Core.Providers;
using FileTime.Core.StateManagement;
using FileTime.Core.Timeline; using FileTime.Core.Timeline;
using FileTime.Providers.Local; using FileTime.Providers.Local;
using FileTime.Providers.Smb; using FileTime.Providers.Smb;
@@ -21,7 +20,6 @@ namespace FileTime.App.Core
.AddSingleton<LocalContentProvider>() .AddSingleton<LocalContentProvider>()
.AddSingleton<IContentProvider, LocalContentProvider>(sp => sp.GetService<LocalContentProvider>() ?? throw new Exception($"No {nameof(LocalContentProvider)} instance found")) .AddSingleton<IContentProvider, LocalContentProvider>(sp => sp.GetService<LocalContentProvider>() ?? throw new Exception($"No {nameof(LocalContentProvider)} instance found"))
.AddSingleton<IContentProvider, SmbContentProvider>() .AddSingleton<IContentProvider, SmbContentProvider>()
.AddSingleton<ElementCreationStates>()
.AddSingleton<CommandExecutor>() .AddSingleton<CommandExecutor>()
.AddSingleton<TimeRunner>(); .AddSingleton<TimeRunner>();
} }

View File

@@ -1,3 +1,4 @@
using AsyncEvent;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Timeline; using FileTime.Core.Timeline;
@@ -5,9 +6,10 @@ namespace FileTime.Core.Command
{ {
public class CopyCommand : ITransportationCommand public class CopyCommand : ITransportationCommand
{ {
private Action<AbsolutePath, AbsolutePath>? _copyOperation; private Func<AbsolutePath, AbsolutePath, Task>? _copyOperation;
private Dictionary<AbsolutePath, OperationProgress> _operationStatuses = new();
private Func<IContainer, string, Task<IContainer>>? _createContainer; private Func<IContainer, string, Task<IContainer>>? _createContainer;
private TimeRunner? _timeRunner; private Func<AbsolutePath, Task>? _containerCopyDone;
public IList<AbsolutePath> Sources { get; } = new List<AbsolutePath>(); public IList<AbsolutePath> Sources { get; } = new List<AbsolutePath>();
@@ -15,6 +17,27 @@ namespace FileTime.Core.Command
public TransportMode? TransportMode { get; set; } = Command.TransportMode.Merge; public TransportMode? TransportMode { get; set; } = Command.TransportMode.Merge;
public int Progress { get; private set; }
public AsyncEventHandler ProgressChanged { get; } = new();
public string DisplayLabel { get; } = "Copy";
private async Task UpdateProgress()
{
var total = 0;
var current = 0;
foreach (var item in _operationStatuses.Values)
{
current += item.Progress;
total += item.TotalCount;
}
Progress = current * 100 / total;
await ProgressChanged.InvokeAsync(this, AsyncEventArgs.Empty);
}
public async Task<PointInTime> SimulateCommand(PointInTime startPoint) public async Task<PointInTime> SimulateCommand(PointInTime startPoint)
{ {
if (Sources == null) throw new ArgumentException(nameof(Sources) + " can not be null"); if (Sources == null) throw new ArgumentException(nameof(Sources) + " can not be null");
@@ -33,6 +56,8 @@ namespace FileTime.Core.Command
DifferenceActionType.Create, DifferenceActionType.Create,
to to
)); ));
return Task.CompletedTask;
}; };
_createContainer = async (IContainer target, string name) => _createContainer = async (IContainer target, string name) =>
@@ -59,13 +84,62 @@ namespace FileTime.Core.Command
if (Target == null) throw new ArgumentException(nameof(Target) + " can not be null"); if (Target == null) throw new ArgumentException(nameof(Target) + " can not be null");
if (TransportMode == null) throw new ArgumentException(nameof(TransportMode) + " can not be null"); if (TransportMode == null) throw new ArgumentException(nameof(TransportMode) + " can not be null");
_copyOperation = copy; await CalculateProgress();
_copyOperation = async (from, to) =>
{
copy(from, to);
var parentPath = to.GetParentAsAbsolutePath();
if (_operationStatuses.ContainsKey(parentPath))
{
_operationStatuses[parentPath].Progress++;
}
await UpdateProgress();
};
_createContainer = async (IContainer target, string name) => await target.CreateContainer(name); _createContainer = async (IContainer target, string name) => await target.CreateContainer(name);
_timeRunner = timeRunner; _containerCopyDone = async (path) =>
{
_operationStatuses[path].Progress = _operationStatuses[path].TotalCount;
if (timeRunner != null)
{
await timeRunner.RefreshContainer.InvokeAsync(this, path);
}
};
await DoCopy(Sources, Target, TransportMode.Value); await DoCopy(Sources, Target, TransportMode.Value);
} }
private async Task CalculateProgress()
{
if (Sources == null) throw new ArgumentException(nameof(Sources) + " can not be null");
if (Target == null) throw new ArgumentException(nameof(Target) + " can not be null");
if (TransportMode == null) throw new ArgumentException(nameof(TransportMode) + " can not be null");
var operationStatuses = new Dictionary<AbsolutePath, OperationProgress>();
_copyOperation = (_, to) =>
{
var parentPath = to.GetParentAsAbsolutePath();
OperationProgress operation;
if (operationStatuses.ContainsKey(parentPath))
{
operation = operationStatuses[parentPath];
}
else
{
operation = new OperationProgress();
operationStatuses.Add(parentPath, operation);
}
operation.TotalCount++;
return Task.CompletedTask;
};
await DoCopy(Sources, Target, TransportMode.Value);
_operationStatuses = operationStatuses;
}
private async Task DoCopy( private async Task DoCopy(
IEnumerable<AbsolutePath> sources, IEnumerable<AbsolutePath> sources,
IContainer target, IContainer target,
@@ -86,7 +160,7 @@ namespace FileTime.Core.Command
var childFiles = (await container.GetElements())!.Select(f => new AbsolutePath(f)); var childFiles = (await container.GetElements())!.Select(f => new AbsolutePath(f));
await DoCopy(childDirectories.Concat(childFiles), targetContainer, transportMode); await DoCopy(childDirectories.Concat(childFiles), targetContainer, transportMode);
_timeRunner?.RefreshContainer.InvokeAsync(this, new AbsolutePath(container)); if (_containerCopyDone != null) await _containerCopyDone.Invoke(new AbsolutePath(container));
} }
else if (item is IElement element) else if (item is IElement element)
{ {

View File

@@ -1,3 +1,4 @@
using AsyncEvent;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Timeline; using FileTime.Core.Timeline;
@@ -8,6 +9,11 @@ namespace FileTime.Core.Command
public AbsolutePath Container { get; } public AbsolutePath Container { get; }
public string NewContainerName { get; } public string NewContainerName { get; }
public int Progress => 100;
public AsyncEventHandler ProgressChanged { get; } = new();
public string DisplayLabel { get; } = "CreateContainer";
public CreateContainerCommand(AbsolutePath container, string newContainerName) public CreateContainerCommand(AbsolutePath container, string newContainerName)
{ {
Container = container; Container = container;

View File

@@ -1,3 +1,4 @@
using AsyncEvent;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Timeline; using FileTime.Core.Timeline;
@@ -8,6 +9,10 @@ namespace FileTime.Core.Command
public AbsolutePath Container { get; } public AbsolutePath Container { get; }
public string NewElementName { get; } public string NewElementName { get; }
public int Progress => 100;
public AsyncEventHandler ProgressChanged { get; } = new();
public string DisplayLabel { get; } = "CreateElement";
public CreateElementCommand(AbsolutePath container, string newElementName) public CreateElementCommand(AbsolutePath container, string newElementName)
{ {
Container = container; Container = container;

View File

@@ -1,3 +1,4 @@
using AsyncEvent;
using FileTime.Core.Extensions; using FileTime.Core.Extensions;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Timeline; using FileTime.Core.Timeline;
@@ -6,7 +7,12 @@ namespace FileTime.Core.Command
{ {
public class DeleteCommand : IExecutableCommand public class DeleteCommand : IExecutableCommand
{ {
public int Progress => 100;
public AsyncEventHandler ProgressChanged { get; } = new();
public IList<AbsolutePath> ItemsToDelete { get; } = new List<AbsolutePath>(); public IList<AbsolutePath> ItemsToDelete { get; } = new List<AbsolutePath>();
public string DisplayLabel { get; } = "DeleteCommand";
public async Task<PointInTime> SimulateCommand(PointInTime startPoint) public async Task<PointInTime> SimulateCommand(PointInTime startPoint)
{ {

View File

@@ -1,10 +1,14 @@
using AsyncEvent;
using FileTime.Core.Timeline; using FileTime.Core.Timeline;
namespace FileTime.Core.Command namespace FileTime.Core.Command
{ {
public interface ICommand public interface ICommand
{ {
string DisplayLabel { get; }
Task<CanCommandRun> CanRun(PointInTime startPoint); Task<CanCommandRun> CanRun(PointInTime startPoint);
Task<PointInTime> SimulateCommand(PointInTime startPoint); Task<PointInTime> SimulateCommand(PointInTime startPoint);
int Progress { get; }
AsyncEventHandler ProgressChanged { get; }
} }
} }

View File

@@ -1,3 +1,4 @@
using AsyncEvent;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Timeline; using FileTime.Core.Timeline;
@@ -10,6 +11,10 @@ namespace FileTime.Core.Command
public IContainer? Target { get; set; } public IContainer? Target { get; set; }
public TransportMode? TransportMode { get; set; } = Command.TransportMode.Merge; public TransportMode? TransportMode { get; set; } = Command.TransportMode.Merge;
public int Progress => 100;
public AsyncEventHandler ProgressChanged { get; } = new();
public string DisplayLabel { get; } = "MoveCommand";
public Task<CanCommandRun> CanRun(PointInTime startPoint) public Task<CanCommandRun> CanRun(PointInTime startPoint)
{ {
throw new NotImplementedException(); throw new NotImplementedException();

View File

@@ -0,0 +1,8 @@
namespace FileTime.Core.Command
{
public class OperationProgress
{
public int Progress { get; set; }
public int TotalCount { get; set; }
}
}

View File

@@ -1,3 +1,5 @@
using AsyncEvent;
using FileTime.Core.Extensions;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Timeline; using FileTime.Core.Timeline;
@@ -8,6 +10,10 @@ namespace FileTime.Core.Command
public AbsolutePath Source { get; } public AbsolutePath Source { get; }
public string Target { get; } public string Target { get; }
public int Progress => 100;
public AsyncEventHandler ProgressChanged { get; } = new();
public string DisplayLabel { get; } = "RenameCommand";
public RenameCommand(AbsolutePath source, string target) public RenameCommand(AbsolutePath source, string target)
{ {
Source = source; Source = source;
@@ -24,14 +30,25 @@ namespace FileTime.Core.Command
} }
} }
public Task<PointInTime> SimulateCommand(PointInTime startPoint) public async Task<PointInTime> SimulateCommand(PointInTime startPoint)
{ {
throw new NotImplementedException(); var item = await Source.Resolve();
if (item == null) throw new FileNotFoundException();
var newDifferences = new List<Difference>()
{
new Difference(item.ToDifferenceItemType(),
DifferenceActionType.Delete,
Source),
new Difference(item.ToDifferenceItemType(),
DifferenceActionType.Delete,
Source)
};
return startPoint.WithDifferences(newDifferences);
} }
public Task<CanCommandRun> CanRun(PointInTime startPoint) public Task<CanCommandRun> CanRun(PointInTime startPoint)
{ {
throw new NotImplementedException(); return Task.FromResult(Source.Resolve() != null ? CanCommandRun.True : CanCommandRun.False);
} }
} }
} }

View File

@@ -3,7 +3,7 @@ using FileTime.Core.Timeline;
namespace FileTime.Core.Models namespace FileTime.Core.Models
{ {
public sealed class AbsolutePath public sealed class AbsolutePath : IEquatable<AbsolutePath>
{ {
public IContentProvider ContentProvider { get; } public IContentProvider ContentProvider { get; }
public IContentProvider? VirtualContentProvider { get; } public IContentProvider? VirtualContentProvider { get; }
@@ -67,12 +67,6 @@ namespace FileTime.Core.Models
return new AbsolutePath(contentProvider, path, virtualContentProvider); return new AbsolutePath(contentProvider, path, virtualContentProvider);
} }
public bool IsEqual(AbsolutePath path)
{
//TODO: sure??
return path.ContentProvider == ContentProvider && path.Path == Path;
}
public async Task<IItem?> Resolve() public async Task<IItem?> Resolve()
{ {
var result = VirtualContentProvider != null && (await VirtualContentProvider.IsExists(Path)) var result = VirtualContentProvider != null && (await VirtualContentProvider.IsExists(Path))
@@ -87,11 +81,36 @@ namespace FileTime.Core.Models
public string GetParent() public string GetParent()
{ {
var pathParts = Path.Split(Constants.SeparatorChar); var pathParts = Path.Split(Constants.SeparatorChar);
return string.Join(Constants.SeparatorChar, pathParts); return string.Join(Constants.SeparatorChar, pathParts[..^1]);
} }
public AbsolutePath GetParentAsAbsolutePath() => new(ContentProvider, GetParent(), VirtualContentProvider); public AbsolutePath GetParentAsAbsolutePath() => new(ContentProvider, GetParent(), VirtualContentProvider);
public string GetName() => Path.Split(Constants.SeparatorChar).Last(); public string GetName() => Path.Split(Constants.SeparatorChar).Last();
public override bool Equals(object? obj) => this.Equals(obj as AbsolutePath);
public bool Equals(AbsolutePath? other) =>
other is not null && other.ContentProvider == ContentProvider && other.Path == Path;
public override int GetHashCode() => (ContentProvider.Name, Path).GetHashCode();
public static bool operator ==(AbsolutePath? lhs, AbsolutePath? rhs)
{
if (lhs is null)
{
if (rhs is null)
{
return true;
}
// Only the left side is null.
return false;
}
// Equals handles case of null on right side.
return lhs.Equals(rhs);
}
public static bool operator !=(AbsolutePath? lhs, AbsolutePath? rhs) => !(lhs == rhs);
} }
} }

View File

@@ -1,7 +0,0 @@
namespace FileTime.Core.StateManagement
{
public class ElementCreationStates
{
}
}

View File

@@ -3,9 +3,12 @@ namespace FileTime.Core.Timeline
public class ReadOnlyParallelCommands public class ReadOnlyParallelCommands
{ {
public IReadOnlyList<ReadOnlyCommandTimeState> Commands { get; } public IReadOnlyList<ReadOnlyCommandTimeState> Commands { get; }
public ushort Id { get; }
public ReadOnlyParallelCommands(ParallelCommands parallelCommands) public ReadOnlyParallelCommands(ParallelCommands parallelCommands)
{ {
Commands = parallelCommands.Commands.Select(c => new ReadOnlyCommandTimeState(c)).ToList().AsReadOnly(); Commands = parallelCommands.Commands.Select(c => new ReadOnlyCommandTimeState(c)).ToList().AsReadOnly();
Id = parallelCommands.Id;
} }
} }
} }

View File

@@ -12,7 +12,7 @@ namespace FileTime.Core.Timeline
private bool _resourceIsInUse; private bool _resourceIsInUse;
private readonly List<Thread> _commandRunners = new(); private readonly List<Thread> _commandRunners = new();
private bool _enableRunning = true; private bool _enableRunning = false;//true
public bool EnableRunning public bool EnableRunning
{ {
@@ -98,6 +98,8 @@ namespace FileTime.Core.Timeline
private void RunCommands() private void RunCommands()
{ {
while (_commandsToRun.Count > 0 && _commandsToRun[0].Commands.Count == 0) _commandsToRun.RemoveAt(0);
if (_commandsToRun.Count > 0) if (_commandsToRun.Count > 0)
{ {
foreach (var command in _commandsToRun[0].Commands) foreach (var command in _commandsToRun[0].Commands)
@@ -139,6 +141,10 @@ namespace FileTime.Core.Timeline
if (command != null) if (command != null)
{ {
_commandsToRun[0].Remove(command); _commandsToRun[0].Remove(command);
if (_commandsToRun[0].Commands.Count == 0)
{
_commandsToRun.RemoveAt(0);
}
} }
_commandRunners.Remove(thread); _commandRunners.Remove(thread);
@@ -150,12 +156,8 @@ namespace FileTime.Core.Timeline
public async Task Refresh() public async Task Refresh()
{ {
await RunWithLockAsync(async () => await RunWithLockAsync(async () => await RefreshCommands(PointInTime.CreateEmpty()));
{
await RefreshCommands(PointInTime.CreateEmpty());
});
await UpdateReadOnlyCommands(); await UpdateReadOnlyCommands();
} }
private async Task RefreshCommands(PointInTime? fullStartTime = null) private async Task RefreshCommands(PointInTime? fullStartTime = null)
@@ -171,10 +173,10 @@ namespace FileTime.Core.Timeline
private async Task UpdateReadOnlyCommands() private async Task UpdateReadOnlyCommands()
{ {
await RunWithLockAsync(() => var wait = false;
{ await RunWithLockAsync(() => wait = _commandsToRun.Count == 1);
ParallelCommands = _commandsToRun.ConvertAll(c => new ReadOnlyParallelCommands(c)).AsReadOnly(); if (wait) await Task.Delay(100);
}); await RunWithLockAsync(() => ParallelCommands = _commandsToRun.ConvertAll(c => new ReadOnlyParallelCommands(c)).AsReadOnly());
CommandsChanged?.Invoke(this, EventArgs.Empty); CommandsChanged?.Invoke(this, EventArgs.Empty);
} }

View File

@@ -7,7 +7,6 @@
<ItemGroup> <ItemGroup>
<Folder Include="Models\" /> <Folder Include="Models\" />
<AvaloniaResource Include="Assets\**" /> <AvaloniaResource Include="Assets\**" />
<Folder Include="ViewModels\" />
<None Remove=".gitignore" /> <None Remove=".gitignore" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -73,7 +73,7 @@ namespace FileTime.Avalonia.ViewModels
[Property] [Property]
private ObservableCollection<string> _popupTexts = new ObservableCollection<string>(); private ObservableCollection<string> _popupTexts = new ObservableCollection<string>();
public IReadOnlyList<ReadOnlyParallelCommands> TimelineCommands => _timeRunner.ParallelCommands; public ObservableCollection<ParallelCommandsViewModel> TimelineCommands { get; } = new();
async partial void OnInitialize() async partial void OnInitialize()
{ {
@@ -85,7 +85,7 @@ namespace FileTime.Avalonia.ViewModels
inputInterface.InputHandler = ReadInputs; inputInterface.InputHandler = ReadInputs;
App.ServiceProvider.GetService<TopContainer>(); App.ServiceProvider.GetService<TopContainer>();
_timeRunner.CommandsChanged += (o, e) => OnPropertyChanged(nameof(TimelineCommands)); _timeRunner.CommandsChanged += UpdateParalellCommands;
InitCommandBindings(); InitCommandBindings();
_keysToSkip.Add(new KeyWithModifiers[] { new KeyWithModifiers(Key.Up) }); _keysToSkip.Add(new KeyWithModifiers[] { new KeyWithModifiers(Key.Up) });
@@ -179,6 +179,31 @@ namespace FileTime.Avalonia.ViewModels
Places = places; Places = places;
} }
private void UpdateParalellCommands(object? sender, EventArgs e)
{
foreach (var parallelCommand in _timeRunner.ParallelCommands)
{
if (!TimelineCommands.Any(c => c.Id == parallelCommand.Id))
{
TimelineCommands.Add(new ParallelCommandsViewModel(parallelCommand));
}
}
var itemsToRemove = new List<ParallelCommandsViewModel>();
foreach (var parallelCommandVm in TimelineCommands)
{
if (!_timeRunner.ParallelCommands.Any(c => c.Id == parallelCommandVm.Id))
{
itemsToRemove.Add(parallelCommandVm);
}
}
for (var i = 0; i < itemsToRemove.Count; i++)
{
itemsToRemove[i].Dispose();
TimelineCommands.Remove(itemsToRemove[i]);
}
}
private async Task<IContainer?> GetContainerForWindowsDrive(DriveInfo drive) private async Task<IContainer?> GetContainerForWindowsDrive(DriveInfo drive)
{ {
return (await LocalContentProvider.GetRootContainers()).FirstOrDefault(d => d.Name == drive.Name.TrimEnd(Path.DirectorySeparatorChar)); return (await LocalContentProvider.GetRootContainers()).FirstOrDefault(d => d.Name == drive.Name.TrimEnd(Path.DirectorySeparatorChar));
@@ -625,14 +650,10 @@ namespace FileTime.Avalonia.ViewModels
[Command] [Command]
public async void ProcessInputs() public async void ProcessInputs()
{ {
try if (_inputHandler != null)
{ {
if (_inputHandler != null) await _inputHandler.Invoke();
{
await _inputHandler.Invoke();
}
} }
catch { }
Inputs = null; Inputs = null;
_inputHandler = null; _inputHandler = null;
@@ -781,7 +802,7 @@ namespace FileTime.Avalonia.ViewModels
var selectedItemName = AppState.SelectedTab.SelectedItem?.Item.Name; var selectedItemName = AppState.SelectedTab.SelectedItem?.Item.Name;
var currentLocationItems = await AppState.SelectedTab.CurrentLocation.GetItems(); var currentLocationItems = await AppState.SelectedTab.CurrentLocation.GetItems();
if(currentLocationItems.FirstOrDefault(i => i.Item.Name.ToLower() == AppState.RapidTravelText.ToLower()) is IItemViewModel matchItem) if (currentLocationItems.FirstOrDefault(i => i.Item.Name.ToLower() == AppState.RapidTravelText.ToLower()) is IItemViewModel matchItem)
{ {
await AppState.SelectedTab.SetCurrentSelectedItem(matchItem.Item); await AppState.SelectedTab.SetCurrentSelectedItem(matchItem.Item);
} }

View File

@@ -0,0 +1,43 @@
using System.Linq;
using System.Collections.Generic;
using System;
using FileTime.Core.Timeline;
namespace FileTime.Avalonia.ViewModels
{
public class ParallelCommandsViewModel : IDisposable
{
private bool _disposed;
public IReadOnlyCollection<ParallelCommandViewModel> ParallelCommands { get; }
public ushort Id { get; }
public ParallelCommandsViewModel(ReadOnlyParallelCommands parallelCommands)
{
ParallelCommands = parallelCommands.Commands.Select(c => new ParallelCommandViewModel(c)).ToList().AsReadOnly();
Id = parallelCommands.Id;
}
~ParallelCommandsViewModel()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!_disposed && disposing)
{
foreach(var commandVm in ParallelCommands)
{
commandVm.Dispose();
}
}
_disposed = true;
}
}
}

View File

@@ -0,0 +1,56 @@
using System;
using System.Threading.Tasks;
using AsyncEvent;
using FileTime.Core.Command;
using FileTime.Core.Timeline;
using MvvmGen;
namespace FileTime.Avalonia.ViewModels
{
[ViewModel]
public partial class ParallelCommandViewModel : IDisposable
{
private bool _disposed;
private readonly ReadOnlyCommandTimeState _commandTimeState;
[Property]
private int _progress;
public CanCommandRun CanRun => _commandTimeState.CanRun;
public bool ForceRun => _commandTimeState.ForceRun;
public string Name => _commandTimeState.Command.DisplayLabel;
public ParallelCommandViewModel(ReadOnlyCommandTimeState commandTimeState)
{
_commandTimeState = commandTimeState;
_commandTimeState.Command.ProgressChanged.Add(HandleProgressChange);
}
private Task HandleProgressChange(object? sender, AsyncEventArgs e)
{
Progress = _commandTimeState.Command.Progress;
return Task.CompletedTask;
}
~ParallelCommandViewModel()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!_disposed && disposing)
{
_commandTimeState.Command.ProgressChanged.Remove(HandleProgressChange);
}
_disposed = true;
}
}
}

View File

@@ -139,13 +139,18 @@
</ItemsControl.ItemsPanel> </ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<ItemsControl Items="{Binding Commands}"> <Border Background="{DynamicResource ContainerBackgroundColor}" Padding="5">
<ItemsControl.ItemTemplate> <ItemsControl Items="{Binding ParallelCommands}">
<DataTemplate> <ItemsControl.ItemTemplate>
<TextBlock Text="{Binding}"/> <DataTemplate>
</DataTemplate> <StackPanel>
</ItemsControl.ItemTemplate> <TextBlock Text="{Binding Name}"/>
</ItemsControl> <ProgressBar Margin="0,5,0,0" Maximum="100" Value="{Binding Progress}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</ItemsControl> </ItemsControl>
@@ -290,14 +295,14 @@
<Grid <Grid
RowDefinitions="Auto, Auto" RowDefinitions="Auto, Auto"
IsVisible="{Binding AppState.SelectedTab.ChildContainer.Exceptions.Count, Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}"> IsVisible="{Binding AppState.SelectedTab.ChildContainer.Exceptions.Count, Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}">
<TextBlock <TextBlock
Margin="0,0,0,10" Margin="0,0,0,10"
HorizontalAlignment="Center" HorizontalAlignment="Center"
TextWrapping="Wrap" TextWrapping="Wrap"
Text="There were some errors while opening container." Text="There were some errors while opening container."
Foreground="{DynamicResource ErrorBrush}" /> Foreground="{DynamicResource ErrorBrush}" />
<ItemsRepeater Grid.Row="1" Items="{Binding AppState.SelectedTab.ChildContainer.Exceptions}"> <ItemsRepeater Grid.Row="1" Items="{Binding AppState.SelectedTab.ChildContainer.Exceptions}">
<ItemsRepeater.ItemTemplate> <ItemsRepeater.ItemTemplate>
<DataTemplate> <DataTemplate>

View File

@@ -1,19 +1,11 @@
using FileTime.Core.Command; using FileTime.Core.Command;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.StateManagement;
using FileTime.Core.Timeline; using FileTime.Core.Timeline;
namespace FileTime.Providers.Local.CommandHandlers namespace FileTime.Providers.Local.CommandHandlers
{ {
public class CopyCommandHandler : ICommandHandler public class CopyCommandHandler : ICommandHandler
{ {
private readonly ElementCreationStates _elementCreationStates;
public CopyCommandHandler(ElementCreationStates elementCreationStates)
{
_elementCreationStates = elementCreationStates;
}
public bool CanHandle(object command) public bool CanHandle(object command)
{ {
if (command is not CopyCommand copyCommand) return false; if (command is not CopyCommand copyCommand) return false;