Clipboard

This commit is contained in:
2022-05-07 18:58:12 +02:00
parent 60ab58659e
commit b161ded92e
16 changed files with 214 additions and 33 deletions

View File

@@ -0,0 +1,16 @@
using FileTime.Core.Command;
using FileTime.Core.Models;
namespace FileTime.App.Core.Services
{
public interface IClipboardService
{
Type? CommandType { get; }
IReadOnlyList<IAbsolutePath> Content { get; }
void AddContent(IAbsolutePath absolutePath);
void RemoveContent(IAbsolutePath absolutePath);
void Clear();
void SetCommand<T>() where T : ITransportationCommand;
}
}

View File

@@ -15,7 +15,7 @@ namespace FileTime.App.Core.ViewModels
IObservable<IContainer?> CurrentLocation { get; }
IObservable<IItemViewModel?> CurrentSelectedItem { get; }
IObservable<IObservable<IChangeSet<IItemViewModel>>?> CurrentItems { get; }
IObservable<IEnumerable<FullName>> MarkedItems { get; }
IObservable<IChangeSet<IAbsolutePath>> MarkedItems { get; }
IObservable<IObservable<IChangeSet<IItemViewModel>>?> SelectedsChildren { get; }
IObservable<IObservable<IChangeSet<IItemViewModel>>?> ParentsChildren { get; }
BindedCollection<IItemViewModel>? CurrentItemsCollection { get; }
@@ -23,8 +23,8 @@ namespace FileTime.App.Core.ViewModels
BindedCollection<IItemViewModel>? ParentsChildrenCollection { get; }
IObservable<IReadOnlyCollection<IItemViewModel>?> CurrentItemsCollectionObservable { get; }
void ClearMarkedItems();
void RemoveMarkedItem(FullName item);
void AddMarkedItem(FullName item);
void ToggleMarkedItem(FullName item);
void RemoveMarkedItem(IAbsolutePath item);
void AddMarkedItem(IAbsolutePath item);
void ToggleMarkedItem(IAbsolutePath item);
}
}

View File

@@ -18,6 +18,7 @@
<ProjectReference Include="..\..\Core\FileTime.Core.Models\FileTime.Core.Models.csproj" />
<ProjectReference Include="..\..\Tools\FileTime.Tools\FileTime.Tools.csproj" />
<ProjectReference Include="..\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
<ProjectReference Include="..\..\Core\FileTime.Core.Command\FileTime.Core.Command.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,51 @@
using FileTime.Core.Command;
using FileTime.Core.Models;
namespace FileTime.App.Core.Services
{
public class ClipboardService : IClipboardService
{
private List<IAbsolutePath> _content;
public IReadOnlyList<IAbsolutePath> Content { get; private set; }
public Type? CommandType { get; private set; }
public ClipboardService()
{
_content = new List<IAbsolutePath>();
Content = _content.AsReadOnly();
}
public void AddContent(IAbsolutePath absolutePath)
{
foreach (var content in _content)
{
if (content.Equals(absolutePath)) return;
}
_content.Add(absolutePath);
}
public void RemoveContent(IAbsolutePath absolutePath)
{
for (var i = 0; i < _content.Count; i++)
{
if (_content[i].Equals(absolutePath))
{
_content.RemoveAt(i--);
}
}
}
public void Clear()
{
_content = new List<IAbsolutePath>();
Content = _content.AsReadOnly();
CommandType = null;
}
public void SetCommand<T>() where T : ITransportationCommand
{
CommandType = typeof(T);
}
}
}

View File

@@ -1,16 +1,17 @@
using System.Reactive.Linq;
using DynamicData;
using FileTime.App.Core.Command;
using FileTime.App.Core.ViewModels;
using FileTime.Core.Models;
namespace FileTime.App.Core.Services.CommandHandler
{
public abstract class CommandHanderBase : ICommandHandler
public abstract class CommandHandlerBase : ICommandHandler
{
private readonly Dictionary<Commands, Func<Task>> _commandHandlers = new();
private readonly IAppState? _appState;
protected CommandHanderBase(IAppState? appState = null)
protected CommandHandlerBase(IAppState? appState = null)
{
_appState = appState;
}
@@ -40,9 +41,12 @@ namespace FileTime.App.Core.Services.CommandHandler
protected IDisposable SaveCurrentItems(Action<IEnumerable<IItemViewModel>> handler)
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t?.CurrentItemsCollectionObservable ?? Observable.Return((IEnumerable<IItemViewModel>?)Enumerable.Empty<IItemViewModel>())).Switch().Subscribe(i => handler(i ?? Enumerable.Empty<IItemViewModel>())));
protected IDisposable SaveMarkedItems(Action<IChangeSet<IAbsolutePath>> handler)
=> RunWithAppState(appstate => appstate.SelectedTab.Select(t => t == null ? Observable.Empty<IChangeSet<IAbsolutePath>>() : t.MarkedItems).Switch().Subscribe(handler));
private IDisposable RunWithAppState(Func<IAppState, IDisposable> act)
{
if (_appState == null) throw new NullReferenceException($"AppState is nit initialized in {nameof(CommandHanderBase)}.");
if (_appState == null) throw new NullReferenceException($"AppState is nit initialized in {nameof(CommandHandlerBase)}.");
return act(_appState);
}

View File

@@ -1,24 +1,44 @@
using DynamicData;
using FileTime.App.Core.Command;
using FileTime.App.Core.Models;
using FileTime.App.Core.ViewModels;
using FileTime.Core.Command;
using FileTime.Core.Command.Copy;
using FileTime.Core.Models;
namespace FileTime.App.Core.Services.CommandHandler
{
public class ItemManipulationCommandHandler : CommandHanderBase
public class ItemManipulationCommandHandler : CommandHandlerBase
{
private ITabViewModel? _selectedTab;
private IItemViewModel? _currentSelectedItem;
private readonly ICommandHandlerService _commandHandlerService;
private readonly IClipboardService _clipboardService;
private BindedCollection<IAbsolutePath>? _markedItems;
public ItemManipulationCommandHandler(IAppState appState, ICommandHandlerService commandHandlerService) : base(appState)
public ItemManipulationCommandHandler(
IAppState appState,
ICommandHandlerService commandHandlerService,
IClipboardService clipboardService) : base(appState)
{
_commandHandlerService = commandHandlerService;
_clipboardService = clipboardService;
SaveSelectedTab(t => _selectedTab = t);
SaveSelectedTab(t =>
{
_selectedTab = t;
_markedItems?.Dispose();
_markedItems = t == null ? null : new BindedCollection<IAbsolutePath>(t.MarkedItems);
});
SaveCurrentSelectedItem(i => _currentSelectedItem = i);
AddCommandHandlers(new (Commands, Func<Task>)[]
{
(Commands.Mark, MarkItem)
(Commands.Copy, Copy),
(Commands.Mark, MarkItem),
(Commands.PasteMerge, PasteMerge),
(Commands.PasteOverwrite, PasteOverwrite),
(Commands.PasteSkip, PasteSkip),
});
}
@@ -26,8 +46,51 @@ namespace FileTime.App.Core.Services.CommandHandler
{
if (_selectedTab == null || _currentSelectedItem?.BaseItem?.FullName == null) return;
_selectedTab.ToggleMarkedItem(_currentSelectedItem.BaseItem.FullName);
_selectedTab.ToggleMarkedItem(new AbsolutePath(_currentSelectedItem.BaseItem));
await _commandHandlerService.HandleCommandAsync(Commands.MoveCursorDown);
}
private Task Copy()
{
_clipboardService.Clear();
_clipboardService.SetCommand<CopyCommand>();
if ((_markedItems?.Collection.Count ?? 0) > 0)
{
foreach (var item in _markedItems!.Collection)
{
_clipboardService.AddContent(item);
}
_selectedTab?.ClearMarkedItems();
}
else if (_currentSelectedItem?.BaseItem != null)
{
_clipboardService.AddContent(new AbsolutePath(_currentSelectedItem.BaseItem));
}
return Task.CompletedTask;
}
private async Task PasteMerge()
{
await Paste(TransportMode.Merge);
}
private async Task PasteOverwrite()
{
await Paste(TransportMode.Overwrite);
}
private async Task PasteSkip()
{
await Paste(TransportMode.Skip);
}
private Task Paste(TransportMode skip)
{
if (_clipboardService.CommandType is null) return Task.CompletedTask;
return Task.CompletedTask;
}
}
}

View File

@@ -6,7 +6,7 @@ using FileTime.Core.Models;
namespace FileTime.App.Core.Services.CommandHandler
{
public class NavigationCommandHandler : CommandHanderBase
public class NavigationCommandHandler : CommandHandlerBase
{
private ITabViewModel? _selectedTab;
private IContainer? _currentLocation;

View File

@@ -15,6 +15,7 @@ namespace FileTime.App.Core
.AddTransient<IElementViewModel, ElementViewModel>()
.AddTransient<IItemNameConverterService, ItemNameConverterService>()
.AddSingleton<ICommandHandlerService, CommandHandlerService>()
.AddSingleton<IClipboardService, ClipboardService>()
.AddCommandHandlers();
}

View File

@@ -1,4 +1,5 @@
using System.Reactive.Linq;
using DynamicData;
using FileTime.App.Core.Models;
using FileTime.App.Core.Models.Enums;
using FileTime.App.Core.Services;
@@ -45,7 +46,7 @@ namespace FileTime.App.Core.ViewModels
BaseItem = item;
DisplayName = _appState.SearchText.Select(s => _itemNameConverterService.GetDisplayName(item.DisplayName, s));
DisplayNameText = item.DisplayName;
IsMarked = parentTab.MarkedItems.Select(m => m.Contains(item.FullName));
IsMarked = parentTab.MarkedItems.ToCollection().Select(m => m.Any(i => i.Path.Path == item.FullName?.Path));
IsSelected = parentTab.CurrentSelectedItem.Select(EqualsTo);
IsAlternative = parentTab.CurrentItemsCollectionObservable.Select(c => c?.Index().FirstOrDefault(i => EqualsTo(i.Value)).Key % 2 == 0);
ViewMode = Observable.CombineLatest(IsMarked, IsSelected, IsAlternative, GenerateViewMode);

View File

@@ -1,5 +1,4 @@
using System.Reactive.Linq;
using System.Reactive.Subjects;
using DynamicData;
using FileTime.App.Core.Extensions;
using FileTime.App.Core.Models;
@@ -19,8 +18,7 @@ namespace FileTime.App.Core.ViewModels
private readonly IItemNameConverterService _itemNameConverterService;
private readonly IAppState _appState;
private readonly IRxSchedulerService _rxSchedulerService;
private readonly BehaviorSubject<IEnumerable<FullName>> _markedItems = new(Enumerable.Empty<FullName>());
private readonly List<FullName> _currentMarkedItems = new();
private readonly SourceList<IAbsolutePath> _markedItems = new();
private readonly List<IDisposable> _disposables = new();
private bool disposed;
@@ -32,7 +30,7 @@ namespace FileTime.App.Core.ViewModels
public IObservable<IContainer?> CurrentLocation { get; private set; } = null!;
public IObservable<IItemViewModel?> CurrentSelectedItem { get; private set; } = null!;
public IObservable<IObservable<IChangeSet<IItemViewModel>>?> CurrentItems { get; private set; } = null!;
public IObservable<IEnumerable<FullName>> MarkedItems { get; }
public IObservable<IChangeSet<IAbsolutePath>> MarkedItems { get; }
public IObservable<IObservable<IChangeSet<IItemViewModel>>?> SelectedsChildren { get; private set; } = null!;
public IObservable<IObservable<IChangeSet<IItemViewModel>>?> ParentsChildren { get; private set; } = null!;
@@ -57,7 +55,7 @@ namespace FileTime.App.Core.ViewModels
_itemNameConverterService = itemNameConverterService;
_appState = appState;
MarkedItems = _markedItems.Select(e => e.ToList()).AsObservable();
MarkedItems = _markedItems.Connect().StartWithEmpty();
IsSelected = _appState.SelectedTab.Select(s => s == this);
_rxSchedulerService = rxSchedulerService;
}
@@ -117,7 +115,7 @@ namespace FileTime.App.Core.ViewModels
SelectedsChildrenCollection = children.MapNull(c => new BindedCollection<IItemViewModel>(c!));
});
tab.CurrentLocation.Subscribe((_) => _markedItems.OnNext(Enumerable.Empty<FullName>()));
tab.CurrentLocation.Subscribe((_) => _markedItems.Clear());
IObservable<IObservable<IChangeSet<IItemViewModel>>?> InitSelectedsChildren()
{
@@ -195,21 +193,18 @@ namespace FileTime.App.Core.ViewModels
throw new ArgumentException($"{nameof(item)} is not {nameof(IContainer)} neither {nameof(IElement)}");
}
public void AddMarkedItem(FullName item)
public void AddMarkedItem(IAbsolutePath item) => _markedItems.Add(item);
public void RemoveMarkedItem(IAbsolutePath item)
{
_currentMarkedItems.Add(item);
_markedItems.OnNext(_currentMarkedItems);
var itemsToRemove = _markedItems.Items.Where(i => i.Path.Path == item.Path.Path).ToList();
_markedItems.RemoveMany(itemsToRemove);
}
public void RemoveMarkedItem(FullName item)
public void ToggleMarkedItem(IAbsolutePath item)
{
_currentMarkedItems.RemoveAll(i => i.Path == item.Path);
_markedItems.OnNext(_currentMarkedItems);
}
public void ToggleMarkedItem(FullName item)
{
if (_currentMarkedItems.Any(i => i.Path == item.Path))
if (_markedItems.Items.Any(i => i.Path.Path == item.Path.Path))
{
RemoveMarkedItem(item);
}
@@ -221,8 +216,7 @@ namespace FileTime.App.Core.ViewModels
public void ClearMarkedItems()
{
_currentMarkedItems.Clear();
_markedItems.OnNext(_currentMarkedItems);
_markedItems.Clear();
}
~TabViewModel() => Dispose(false);