Command status
This commit is contained in:
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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--);
|
||||||
|
|||||||
@@ -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>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
|
|||||||
8
src/Core/FileTime.Core/Command/OperationProgress.cs
Normal file
8
src/Core/FileTime.Core/Command/OperationProgress.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace FileTime.Core.Command
|
||||||
|
{
|
||||||
|
public class OperationProgress
|
||||||
|
{
|
||||||
|
public int Progress { get; set; }
|
||||||
|
public int TotalCount { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace FileTime.Core.StateManagement
|
|
||||||
{
|
|
||||||
public class ElementCreationStates
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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));
|
||||||
@@ -624,15 +649,11 @@ 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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 Items="{Binding ParallelCommands}">
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<TextBlock Text="{Binding}"/>
|
<StackPanel>
|
||||||
|
<TextBlock Text="{Binding Name}"/>
|
||||||
|
<ProgressBar Margin="0,5,0,0" Maximum="100" Value="{Binding Progress}"/>
|
||||||
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
|
</Border>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user