TimeTravel

This commit is contained in:
2022-01-31 23:13:39 +01:00
parent 80570d8895
commit c2dcb49016
78 changed files with 2294 additions and 363 deletions

View File

@@ -18,6 +18,10 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Avalonia.Input;
using FileTime.App.Core.Clipboard;
using Microsoft.Extensions.DependencyInjection;
using FileTime.Core.Command;
using FileTime.Core.Timeline;
namespace FileTime.Avalonia.ViewModels
{
@@ -34,6 +38,9 @@ namespace FileTime.Avalonia.ViewModels
private List<CommandBinding> _commandBindings = new();
private List<CommandBinding> _universalCommandBindings = new();
private IClipboard _clipboard;
private TimeRunner _timeRunner;
private Action? _inputHandler;
[Property]
@@ -51,10 +58,16 @@ namespace FileTime.Avalonia.ViewModels
[Property]
private List<RootDriveInfo> _rootDriveInfos;
public Action? FocusDefaultElement { get; set; }
[Property]
private string _messageBoxText;
public IReadOnlyList<ReadOnlyParallelCommands> TimelineCommands => _timeRunner.ParallelCommands;
async partial void OnInitialize()
{
_clipboard = App.ServiceProvider.GetService<IClipboard>()!;
_timeRunner = App.ServiceProvider.GetService<TimeRunner>()!;
_timeRunner.CommandsChanged += (o, e) => OnPropertyChanged(nameof(TimelineCommands));
InitCommandBindings();
_keysToSkip.Add(new KeyWithModifiers[] { new KeyWithModifiers(Key.Up) });
@@ -62,6 +75,7 @@ namespace FileTime.Avalonia.ViewModels
_keysToSkip.Add(new KeyWithModifiers[] { new KeyWithModifiers(Key.Tab) });
_keysToSkip.Add(new KeyWithModifiers[] { new KeyWithModifiers(Key.PageDown) });
_keysToSkip.Add(new KeyWithModifiers[] { new KeyWithModifiers(Key.PageUp) });
_keysToSkip.Add(new KeyWithModifiers[] { new KeyWithModifiers(Key.F4, alt: true) });
var tab = new Tab();
await tab.Init(LocalContentProvider);
@@ -176,7 +190,6 @@ namespace FileTime.Avalonia.ViewModels
_previousKeys.Clear();
PossibleCommands = new();
FocusDefaultElement?.Invoke();
return Task.CompletedTask;
}
@@ -190,7 +203,6 @@ namespace FileTime.Avalonia.ViewModels
AppState.RapidTravelText = "";
await AppState.SelectedTab.OpenContainer(await AppState.SelectedTab.CurrentLocation.Container.WithoutVirtualContainer(RAPIDTRAVEL));
FocusDefaultElement?.Invoke();
}
public async Task SwitchToTab(int number)
@@ -260,7 +272,9 @@ namespace FileTime.Avalonia.ViewModels
{
if (Inputs != null)
{
AppState.SelectedTab.CreateContainer(Inputs[0].Value).Wait();
var container = AppState.SelectedTab.CurrentLocation.Container;
var createContainerCommand = new CreateContainerCommand(new Core.Models.AbsolutePath(container), Inputs[0].Value);
_timeRunner.AddCommand(createContainerCommand).Wait();
Inputs = null;
}
};
@@ -270,10 +284,222 @@ namespace FileTime.Avalonia.ViewModels
return Task.CompletedTask;
}
public Task CreateElement()
{
var handler = () =>
{
if (Inputs != null)
{
var container = AppState.SelectedTab.CurrentLocation.Container;
var createElementCommand = new CreateElementCommand(new Core.Models.AbsolutePath(container), Inputs[0].Value);
_timeRunner.AddCommand(createElementCommand).Wait();
Inputs = null;
}
};
ReadInputs(new List<Core.Interactions.InputElement>() { new Core.Interactions.InputElement("Element name", InputType.Text) }, handler);
return Task.CompletedTask;
}
public async Task MarkCurrentItem()
{
await AppState.SelectedTab.MarkCurrentItem();
}
public async Task Copy()
{
_clipboard.Clear();
_clipboard.SetCommand<CopyCommand>();
var currentSelectedItems = await AppState.SelectedTab.TabState.GetCurrentMarkedItems();
if (currentSelectedItems.Count > 0)
{
foreach (var selectedItem in currentSelectedItems)
{
_clipboard.AddContent(selectedItem);
}
}
else
{
var currentSelectedItem = AppState.SelectedTab.SelectedItem?.Item;
if (currentSelectedItem != null)
{
_clipboard.AddContent(new AbsolutePath(currentSelectedItem));
}
}
}
public Task Cut()
{
_clipboard.Clear();
_clipboard.SetCommand<MoveCommand>();
return Task.CompletedTask;
}
public async Task Delete()
{
IList<Core.Models.AbsolutePath>? itemsToDelete = null;
var askForDelete = false;
var questionText = "";
var shouldDelete = false;
var currentSelectedItems = await AppState.SelectedTab.TabState.GetCurrentMarkedItems();
var currentSelectedItem = AppState.SelectedTab.SelectedItem?.Item;
if (currentSelectedItems.Count > 0)
{
itemsToDelete = currentSelectedItems.Cast<Core.Models.AbsolutePath>().ToList();
//FIXME: check 'is Container'
if (currentSelectedItems.Count == 1)
{
if ((await currentSelectedItems[0].Resolve()) is IContainer container
&& (await container.GetItems())?.Count > 0)
{
askForDelete = true;
questionText = $"The container '{container.Name}' is not empty. Proceed with delete?";
}
else
{
shouldDelete = true;
}
}
else
{
askForDelete = true;
questionText = $"Are you sure you want to delete {itemsToDelete.Count} item?";
}
}
else if (currentSelectedItem != null)
{
itemsToDelete = new List<Core.Models.AbsolutePath>()
{
new Core.Models.AbsolutePath(currentSelectedItem)
};
if (currentSelectedItem is IContainer container && (await container.GetItems())?.Count > 0)
{
askForDelete = true;
questionText = $"The container '{container.Name}' is not empty. Proceed with delete?";
}
else
{
shouldDelete = true;
}
}
if (itemsToDelete?.Count > 0)
{
if (askForDelete)
{
ShowMessageBox(questionText, HandleDelete);
}
else if (shouldDelete)
{
HandleDelete();
}
}
void HandleDelete()
{
var deleteCommand = new DeleteCommand();
foreach (var itemToDelete in itemsToDelete!)
{
deleteCommand.ItemsToDelete.Add(itemToDelete);
}
_timeRunner.AddCommand(deleteCommand).Wait();
_clipboard.Clear();
}
}
public async Task PasteMerge()
{
await Paste(TransportMode.Merge);
}
public async Task PasteOverwrite()
{
await Paste(TransportMode.Overwrite);
}
public async Task PasteSkip()
{
await Paste(TransportMode.Skip);
}
private async Task Paste(TransportMode transportMode)
{
if (_clipboard.CommandType != null)
{
var command = (ITransportationCommand)Activator.CreateInstance(_clipboard.CommandType!)!;
command.TransportMode = transportMode;
command.Sources.Clear();
foreach (var item in _clipboard.Content)
{
command.Sources.Add(item);
}
var currentLocation = AppState.SelectedTab.CurrentLocation.Container;
command.Target = currentLocation is VirtualContainer virtualContainer
? virtualContainer.BaseContainer
: currentLocation;
await _timeRunner.AddCommand(command);
_clipboard.Clear();
}
}
private Task Rename()
{
var selectedItem = AppState.SelectedTab.SelectedItem?.Item;
if (selectedItem != null)
{
var handler = () =>
{
if (Inputs != null)
{
var renameCommand = new RenameCommand(new Core.Models.AbsolutePath(selectedItem), Inputs[0].Value);
_timeRunner.AddCommand(renameCommand).Wait();
}
};
ReadInputs(new List<Core.Interactions.InputElement>() { new Core.Interactions.InputElement("New name", InputType.Text, selectedItem.Name) }, handler);
}
return Task.CompletedTask;
}
private async Task RefreshCurrentLocation()
{
await AppState.SelectedTab.CurrentLocation.Container.Refresh();
await AppState.SelectedTab.UpdateCurrentSelectedItem();
}
private Task PauseTimeline()
{
_timeRunner.EnableRunning = false;
return Task.CompletedTask;
}
private async Task ContinueTimeline()
{
_timeRunner.EnableRunning = true;
await _timeRunner.TryStartCommandRunner();
}
private async Task RefreshTimeline()
{
await _timeRunner.Refresh();
}
[Command]
public void ProcessInputs()
{
_inputHandler();
_inputHandler?.Invoke();
Inputs = null;
_inputHandler = null;
@@ -286,8 +512,31 @@ namespace FileTime.Avalonia.ViewModels
_inputHandler = null;
}
[Command]
public void ProcessMessageBoxCommand()
{
_inputHandler?.Invoke();
MessageBoxText = null;
_inputHandler = null;
}
[Command]
public void CancelMessageBoxCommand()
{
MessageBoxText = null;
_inputHandler = null;
}
public async Task<bool> ProcessKeyDown(Key key, KeyModifiers keyModifiers)
{
if (key == Key.LeftAlt
|| key == Key.RightAlt
|| key == Key.LeftShift
|| key == Key.RightShift
|| key == Key.LeftCtrl
|| key == Key.RightCtrl) return false;
NoCommandFound = false;
var isAltPressed = (keyModifiers & KeyModifiers.Alt) == KeyModifiers.Alt;
@@ -312,13 +561,12 @@ namespace FileTime.Avalonia.ViewModels
await selectedCommandBinding.InvokeAsync();
_previousKeys.Clear();
PossibleCommands = new();
FocusDefaultElement?.Invoke();
}
else if (_keysToSkip.Any(k => AreKeysEqual(k, _previousKeys)))
{
_previousKeys.Clear();
PossibleCommands = new();
return false;
}
else if (_previousKeys.Count == 2)
{
@@ -370,7 +618,11 @@ namespace FileTime.Avalonia.ViewModels
if (selectedCommandBinding != null)
{
await selectedCommandBinding.InvokeAsync();
FocusDefaultElement?.Invoke();
return true;
}
else
{
return false;
}
}
@@ -399,8 +651,6 @@ namespace FileTime.Avalonia.ViewModels
{
await AppState.SelectedTab.MoveCursorToFirst();
}
FocusDefaultElement?.Invoke();
}
}
@@ -415,7 +665,13 @@ namespace FileTime.Avalonia.ViewModels
private void ReadInputs(List<Core.Interactions.InputElement> inputs, Action inputHandler)
{
Inputs = inputs.Select(i => new InputElementWrapper(i)).ToList();
Inputs = inputs.Select(i => new InputElementWrapper(i, i.DefaultValue)).ToList();
_inputHandler = inputHandler;
}
private void ShowMessageBox(string text, Action inputHandler)
{
MessageBoxText = text;
_inputHandler = inputHandler;
}
@@ -451,6 +707,11 @@ namespace FileTime.Avalonia.ViewModels
FileTime.App.Core.Command.Commands.CreateContainer,
new KeyWithModifiers[]{new KeyWithModifiers(Key.C),new KeyWithModifiers(Key.C)},
CreateContainer),
new CommandBinding(
"create element",
FileTime.App.Core.Command.Commands.CreateElement,
new KeyWithModifiers[]{new KeyWithModifiers(Key.C),new KeyWithModifiers(Key.E)},
CreateElement),
new CommandBinding(
"move to first",
FileTime.App.Core.Command.Commands.MoveToTop,
@@ -526,6 +787,66 @@ namespace FileTime.Avalonia.ViewModels
FileTime.App.Core.Command.Commands.GoToHome,
new KeyWithModifiers[]{new KeyWithModifiers(Key.Q)},
CloseTab),
new CommandBinding(
"select",
FileTime.App.Core.Command.Commands.Select,
new KeyWithModifiers[]{new KeyWithModifiers(Key.Space)},
MarkCurrentItem),
new CommandBinding(
"copy",
FileTime.App.Core.Command.Commands.Copy,
new KeyWithModifiers[]{new KeyWithModifiers(Key.Y),new KeyWithModifiers(Key.Y)},
Copy),
new CommandBinding(
"cut",
FileTime.App.Core.Command.Commands.Cut,
new KeyWithModifiers[]{new KeyWithModifiers(Key.D),new KeyWithModifiers(Key.D)},
Cut),
new CommandBinding(
"delete",
FileTime.App.Core.Command.Commands.Delete,
new KeyWithModifiers[]{new KeyWithModifiers(Key.D),new KeyWithModifiers(Key.D, shift: true)},
Delete),
new CommandBinding(
"paste merge",
FileTime.App.Core.Command.Commands.PasteMerge,
new KeyWithModifiers[]{new KeyWithModifiers(Key.P),new KeyWithModifiers(Key.P)},
PasteMerge),
new CommandBinding(
"paste (overwrite)",
FileTime.App.Core.Command.Commands.PasteOverwrite,
new KeyWithModifiers[]{new KeyWithModifiers(Key.P),new KeyWithModifiers(Key.O)},
PasteOverwrite),
new CommandBinding(
"paste (skip)",
FileTime.App.Core.Command.Commands.PasteSkip,
new KeyWithModifiers[]{new KeyWithModifiers(Key.P),new KeyWithModifiers(Key.S)},
PasteSkip),
new CommandBinding(
"rename",
FileTime.App.Core.Command.Commands.Rename,
new KeyWithModifiers[]{new KeyWithModifiers(Key.C),new KeyWithModifiers(Key.W)},
Rename),
new CommandBinding(
"timeline pause",
FileTime.App.Core.Command.Commands.Dummy,
new KeyWithModifiers[]{new KeyWithModifiers(Key.T),new KeyWithModifiers(Key.P)},
PauseTimeline),
new CommandBinding(
"timeline start",
FileTime.App.Core.Command.Commands.Dummy,
new KeyWithModifiers[]{new KeyWithModifiers(Key.T),new KeyWithModifiers(Key.S)},
ContinueTimeline),
new CommandBinding(
"refresh timeline",
FileTime.App.Core.Command.Commands.Dummy,
new KeyWithModifiers[]{new KeyWithModifiers(Key.T),new KeyWithModifiers(Key.R)},
RefreshTimeline),
new CommandBinding(
"refresh",
FileTime.App.Core.Command.Commands.Refresh,
new KeyWithModifiers[]{new KeyWithModifiers(Key.R)},
RefreshCurrentLocation),
};
var universalCommandBindings = new List<CommandBinding>()
{