Contaienr size scan WIP

This commit is contained in:
2023-08-02 08:25:19 +02:00
parent c95be170ed
commit 1713973c3a
35 changed files with 760 additions and 125 deletions

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>FileTime.App.ContainerSizeScanner</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Core\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
<ProjectReference Include="..\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,9 @@
using FileTime.Core.ContentAccess;
using FileTime.Core.Models;
namespace FileTime.App.ContainerSizeScanner;
public interface IContainerScanSnapshotProvider : IContentProvider
{
ISizeScanTask StartSizeScan(IContainer scanSizeOf);
}

View File

@@ -0,0 +1,13 @@
using System.Collections.ObjectModel;
using DeclarativeProperty;
using FileTime.Core.Models;
namespace FileTime.App.ContainerSizeScanner;
public interface IContainerSizeScanContainer : ISizeItem, IContainer
{
public Task AddSizeSourceAsync(IDeclarativeProperty<long> sizeElement);
ObservableCollection<IContainerSizeScanContainer> ChildContainers { get; }
IContainer RealContainer { get; init; }
IDeclarativeProperty<long> Size { get; }
}

View File

@@ -0,0 +1,8 @@
using FileTime.Core.Models;
namespace FileTime.App.ContainerSizeScanner;
public interface ISizeItem : IItem
{
}

View File

@@ -0,0 +1,8 @@
using FileTime.App.Core.ViewModels.ItemPreview;
namespace FileTime.App.ContainerSizeScanner;
public interface ISizePreviewItem : IItemPreviewViewModel
{
}

View File

@@ -0,0 +1,10 @@
using FileTime.Core.Models;
using InitableService;
namespace FileTime.App.ContainerSizeScanner;
public interface ISizeScanTask : IInitable<IContainer>
{
IContainerSizeScanContainer SizeContainer { get; }
void Start();
}

View File

@@ -0,0 +1,113 @@
using FileTime.App.Core.Exceptions;
using FileTime.Core.ContentAccess;
using FileTime.Core.Enums;
using FileTime.Core.Models;
using FileTime.Core.Timeline;
using InitableService;
namespace FileTime.App.ContainerSizeScanner;
public class ContainerScanSnapshotProvider : ContentProviderBase, IContainerScanSnapshotProvider
{
private readonly ITimelessContentProvider _timelessContentProvider;
private readonly IServiceProvider _serviceProvider;
private readonly List<ISizeScanTask> _sizeScanTasks = new();
internal const string ContentProviderName = "container-size-scan";
public ContainerScanSnapshotProvider(
ITimelessContentProvider timelessContentProvider,
IServiceProvider serviceProvider)
: base(ContentProviderName, timelessContentProvider)
{
_timelessContentProvider = timelessContentProvider;
_serviceProvider = serviceProvider;
}
public override async Task<IItem> GetItemByFullNameAsync(
FullName fullName,
PointInTime pointInTime,
bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = null
)
{
if (fullName.Path == ContentProviderName)
return this;
var pathParts = fullName.Path.Split(Constants.SeparatorChar);
var item = _sizeScanTasks.FirstOrDefault(t => t.SizeContainer.Name == pathParts[1])?.SizeContainer;
if (pathParts.Length == 2)
return item ?? throw new ItemNotFoundException(fullName);
for (var i = 2; i < pathParts.Length - 1 && item != null; i++)
{
var childName = pathParts[i];
item = item.ChildContainers.FirstOrDefault(c => c.Name == childName);
}
if (item is not null)
{
var container = item.ChildContainers.FirstOrDefault(c => c.Name == pathParts[^1]);
if (container is not null) return container;
var childName = item.RealContainer.FullName?.GetChild(pathParts[^1]);
if (childName is null) throw new ItemNotFoundException(fullName);
return await _timelessContentProvider.GetItemByFullNameAsync(
childName,
pointInTime,
forceResolve,
forceResolvePathType,
itemInitializationSettings
);
}
throw new ItemNotFoundException(fullName);
}
public override async Task<IItem> GetItemByNativePathAsync(
NativePath nativePath,
PointInTime pointInTime,
bool forceResolve = false,
AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown,
ItemInitializationSettings itemInitializationSettings = default
) =>
await GetItemByFullNameAsync(
new FullName(nativePath.Path),
pointInTime,
forceResolve,
forceResolvePathType,
itemInitializationSettings
);
public override NativePath GetNativePath(FullName fullName)
=> new(fullName.Path);
public override FullName GetFullName(NativePath nativePath)
=> new(nativePath.Path);
public override Task<byte[]?> GetContentAsync(
IElement element,
int? maxLength = null,
CancellationToken cancellationToken = default)
//TODO read from original source
=> Task.FromResult((byte[]?) null);
public override bool CanHandlePath(NativePath path)
=> path.Path.StartsWith(ContentProviderName);
public ISizeScanTask StartSizeScan(IContainer scanSizeOf)
{
var searchTask = _serviceProvider
.GetInitableResolver(scanSizeOf)
.GetRequiredService<ISizeScanTask>();
_sizeScanTasks.Add(searchTask);
searchTask.Start();
Items.Add(new AbsolutePath(_timelessContentProvider, searchTask.SizeContainer));
return searchTask;
}
}

View File

@@ -0,0 +1,22 @@
using System.Collections.ObjectModel;
using FileTime.App.Core.ViewModels.ItemPreview;
using ObservableComputations;
namespace FileTime.App.ContainerSizeScanner;
public class ContainerSizeContainerPreview : ISizePreviewItem
{
public const string PreviewName = "SizePreviewContainer";
public string Name => PreviewName;
public ObservableCollection<ISizePreviewItem> Items { get; }
public ContainerSizeContainerPreview(IContainerSizeScanContainer container)
{
Items = container
.ChildContainers
.Ordering(c => c.Size)
.Taking(0, 10)
.Selecting();
}
}

View File

@@ -0,0 +1,17 @@
using FileTime.App.Core.Services;
using FileTime.App.Core.ViewModels.ItemPreview;
using FileTime.Core.Models;
namespace FileTime.App.ContainerSizeScanner;
public class ContainerSizePreviewProvider : IItemPreviewProvider
{
public bool CanHandle(IItem item) => item is ContainerSizeScanContainer;
public Task<IItemPreviewViewModel> CreatePreviewAsync(IItem item)
{
if(item is not ContainerSizeScanContainer container) throw new NotSupportedException();
return Task.FromResult((IItemPreviewViewModel)new ContainerSizeContainerPreview(container));
}
}

View File

@@ -0,0 +1,74 @@
using System.Collections.ObjectModel;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using DeclarativeProperty;
using FileTime.Core.ContentAccess;
using FileTime.Core.Enums;
using FileTime.Core.Models;
using FileTime.Core.Timeline;
namespace FileTime.App.ContainerSizeScanner;
//TODO: create readonly version
public class ContainerSizeScanContainer : IContainerSizeScanContainer
{
private readonly ReadOnlyExtensionCollection _readOnlyExtensions;
private readonly BehaviorSubject<bool> _isLoading = new(false);
private readonly CombineProperty<long, long> _size;
public required string Name { get; init; }
public required string DisplayName { get; init; }
public required FullName? FullName { get; init; }
public required NativePath? NativePath { get; init; }
public AbsolutePath? Parent { get; init; }
public bool IsHidden => false;
public bool IsExists => true;
public DateTime? CreatedAt { get; }
public SupportsDelete CanDelete => SupportsDelete.True;
public bool CanRename => false;
public IContentProvider Provider { get; }
public string? Attributes => null;
public AbsolutePathType Type => AbsolutePathType.Container;
public PointInTime PointInTime => PointInTime.Present;
public ObservableCollection<Exception> Exceptions { get; } = new();
public ExtensionCollection Extensions { get; } = new();
ReadOnlyExtensionCollection IItem.Extensions => _readOnlyExtensions;
public IItem WithParent(AbsolutePath parent) => throw new NotImplementedException();
public ObservableCollection<AbsolutePath> Items { get; } = new();
public IObservable<bool> IsLoading { get; }
public bool? IsLoaded { get; private set; }
public Task WaitForLoaded(CancellationToken token = default) => throw new NotImplementedException();
public bool AllowRecursiveDeletion => false;
public IDeclarativeProperty<long> Size => _size;
public ObservableCollection<IContainerSizeScanContainer> ChildContainers { get; } = new();
public required IContainer RealContainer { get; init; }
public ContainerSizeScanContainer(IContainerScanSnapshotProvider provider)
{
_readOnlyExtensions = new ReadOnlyExtensionCollection(Extensions);
IsLoading = _isLoading.AsObservable();
_size = new(childContainerSizes => Task.FromResult(childContainerSizes.Sum()));
CreatedAt = DateTime.Now;
Provider = provider;
}
public async Task AddSizeSourceAsync(IDeclarativeProperty<long> sizeElement)
=> await _size.AddSourceAsync(sizeElement);
public Task StartLoadingAsync()
{
_isLoading.OnNext(true);
IsLoaded = false;
return Task.CompletedTask;
}
public Task StopLoadingAsync()
{
_isLoading.OnNext(false);
IsLoaded = true;
return Task.CompletedTask;
}
}

View File

@@ -0,0 +1,29 @@
using System.Collections.ObjectModel;
using FileTime.Core.ContentAccess;
using FileTime.Core.Enums;
using FileTime.Core.Models;
using FileTime.Core.Timeline;
namespace FileTime.App.ContainerSizeScanner;
public record ContainerSizeScanElement(
string Name,
string DisplayName,
FullName FullName,
NativePath NativePath,
AbsolutePath? Parent,
bool IsHidden,
bool IsExists,
DateTime? CreatedAt,
SupportsDelete CanDelete,
bool CanRename,
string? Attributes,
IContentProvider Provider,
PointInTime PointInTime,
ObservableCollection<Exception> Exceptions,
ReadOnlyExtensionCollection Extensions) : IElement
{
public AbsolutePathType Type => AbsolutePathType.Element;
public IItem WithParent(AbsolutePath parent) => this with { Parent = parent };
}

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Core\FileTime.Core.ContentAccess\FileTime.Core.ContentAccess.csproj" />
<ProjectReference Include="..\FileTime.App.ContainerSizeScanner.Abstractions\FileTime.App.ContainerSizeScanner.Abstractions.csproj" />
<ProjectReference Include="..\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,117 @@
using DeclarativeProperty;
using FileTime.Core.Enums;
using FileTime.Core.Models;
using FileTime.Core.Models.Extensions;
using FileTime.Core.Timeline;
using Microsoft.Extensions.Logging;
namespace FileTime.App.ContainerSizeScanner;
public class SizeScanTask : ISizeScanTask
{
private IContainer _scanSizeOf = null!;
private readonly IContainerScanSnapshotProvider _containerScanSnapshotProvider;
private readonly ITimelessContentProvider _timelessContentProvider;
private readonly ILogger<SizeScanTask> _logger;
private Thread? _sizeScanThread;
private static int _searchId = 1;
public IContainerSizeScanContainer SizeContainer { get; private set; } = null!;
public SizeScanTask(
IContainerScanSnapshotProvider containerScanSnapshotProvider,
ITimelessContentProvider timelessContentProvider,
ILogger<SizeScanTask> logger)
{
_containerScanSnapshotProvider = containerScanSnapshotProvider;
_timelessContentProvider = timelessContentProvider;
_logger = logger;
}
public void Init(IContainer scanSizeOf)
{
_scanSizeOf = scanSizeOf;
var name = $"{_searchId++}_{scanSizeOf.Name}";
var randomId = ContainerScanSnapshotProvider.ContentProviderName + Constants.SeparatorChar + name;
SizeContainer = new ContainerSizeScanContainer(_containerScanSnapshotProvider)
{
Name = name,
DisplayName = scanSizeOf.DisplayName,
FullName = new FullName(randomId),
NativePath = new NativePath(randomId),
Parent = new AbsolutePath(_timelessContentProvider, _containerScanSnapshotProvider),
RealContainer = scanSizeOf
};
}
public void Start()
{
if (_sizeScanThread != null) return;
var sizeScanThread = new Thread(Run);
sizeScanThread.Start();
_sizeScanThread = sizeScanThread;
}
private async void Run()
{
try
{
await TraverseTree(_scanSizeOf, SizeContainer);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while scanning container {ContainerName}", _scanSizeOf.Name);
}
}
//TODO: make static
private async Task TraverseTree(
IContainer realContainer,
IContainerSizeScanContainer container)
{
var resolvedItems = new List<IItem>(realContainer.Items.Count);
foreach (var item in realContainer.Items)
{
var resolvedItem = await item.ResolveAsync();
resolvedItems.Add(resolvedItem);
}
foreach (var element in resolvedItems.OfType<IElement>())
{
var fileExtension = element.GetExtension<FileExtension>();
if (fileExtension?.Size is not { } size) continue;
var childName = container.FullName!.GetChild(element.Name).Path;
await container.AddSizeSourceAsync(new DeclarativeProperty<long>(size));
container.Items.Add(new AbsolutePath(
_timelessContentProvider,
PointInTime.Present,
new FullName(childName),
AbsolutePathType.Element));
}
foreach (var childContainer in resolvedItems.OfType<IContainer>())
{
var childName = container.FullName!.GetChild(childContainer.Name).Path;
var childSearchContainer = new ContainerSizeScanContainer(_containerScanSnapshotProvider)
{
Name = childContainer.Name,
DisplayName = childContainer.DisplayName,
FullName = new FullName(childName),
NativePath = new NativePath(childName),
Parent = new AbsolutePath(_timelessContentProvider, container),
RealContainer = childContainer
};
container.ChildContainers.Add(childSearchContainer);
await container.AddSizeSourceAsync(childSearchContainer.Size);
container.Items.Add(new AbsolutePath(
_timelessContentProvider,
PointInTime.Present,
new FullName(childName),
AbsolutePathType.Container));
await TraverseTree(childContainer, childSearchContainer);
}
}
}

View File

@@ -0,0 +1,18 @@
using FileTime.App.Core.Services;
using FileTime.Core.ContentAccess;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace FileTime.App.ContainerSizeScanner;
public static class Startup
{
public static IServiceCollection AddContainerSizeScanner(this IServiceCollection services)
{
services.TryAddSingleton<IContainerScanSnapshotProvider, ContainerScanSnapshotProvider>();
services.AddSingleton<IContentProvider>(sp => sp.GetRequiredService<IContainerScanSnapshotProvider>());
services.AddTransient<ISizeScanTask, SizeScanTask>();
services.AddTransient<IItemPreviewProvider, ContainerSizePreviewProvider>();
return services;
}
}

View File

@@ -0,0 +1,10 @@
using FileTime.App.Core.ViewModels.ItemPreview;
using FileTime.Core.Models;
namespace FileTime.App.Core.Services;
public interface IItemPreviewProvider
{
bool CanHandle(IItem item);
Task<IItemPreviewViewModel> CreatePreviewAsync(IItem item);
}

View File

@@ -0,0 +1,14 @@
namespace FileTime.App.Core.UserCommand;
public sealed class ScanSizeCommand : IIdentifiableUserCommand
{
public const string ScanSizeCommandName = "scan_size";
public static readonly ScanSizeCommand Instance = new();
private ScanSizeCommand()
{
}
public string UserCommandID => ScanSizeCommandName;
public string Title => "Scan size";
}

View File

@@ -0,0 +1,8 @@
using FileTime.App.Core.Models;
namespace FileTime.App.Core.ViewModels.ItemPreview;
public interface IElementPreviewViewModel : IItemPreviewViewModel
{
ItemPreviewMode Mode { get; }
}

View File

@@ -1,8 +1,6 @@
using FileTime.App.Core.Models;
namespace FileTime.App.Core.ViewModels.ItemPreview; namespace FileTime.App.Core.ViewModels.ItemPreview;
public interface IItemPreviewViewModel public interface IItemPreviewViewModel
{ {
ItemPreviewMode Mode { get; } string Name { get; }
} }

View File

@@ -27,6 +27,7 @@
<ProjectReference Include="..\..\Tools\FileTime.Tools\FileTime.Tools.csproj" /> <ProjectReference Include="..\..\Tools\FileTime.Tools\FileTime.Tools.csproj" />
<ProjectReference Include="..\FileTime.App.CommandPalette.Abstractions\FileTime.App.CommandPalette.Abstractions.csproj" /> <ProjectReference Include="..\FileTime.App.CommandPalette.Abstractions\FileTime.App.CommandPalette.Abstractions.csproj" />
<ProjectReference Include="..\FileTime.App.CommandPalette\FileTime.App.CommandPalette.csproj" /> <ProjectReference Include="..\FileTime.App.CommandPalette\FileTime.App.CommandPalette.csproj" />
<ProjectReference Include="..\FileTime.App.ContainerSizeScanner.Abstractions\FileTime.App.ContainerSizeScanner.Abstractions.csproj" />
<ProjectReference Include="..\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" /> <ProjectReference Include="..\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
<ProjectReference Include="..\..\Core\FileTime.Core.Command\FileTime.Core.Command.csproj" /> <ProjectReference Include="..\..\Core\FileTime.Core.Command\FileTime.Core.Command.csproj" />
<ProjectReference Include="..\FileTime.App.FrequencyNavigation.Abstractions\FileTime.App.FrequencyNavigation.Abstractions.csproj" /> <ProjectReference Include="..\FileTime.App.FrequencyNavigation.Abstractions\FileTime.App.FrequencyNavigation.Abstractions.csproj" />

View File

@@ -0,0 +1,26 @@
using FileTime.App.Core.ViewModels.ItemPreview;
using FileTime.Core.Models;
using InitableService;
namespace FileTime.App.Core.Services;
public class ElementPreviewProvider : IItemPreviewProvider
{
private readonly IServiceProvider _serviceProvider;
public ElementPreviewProvider(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public bool CanHandle(IItem item) => item is IElement;
public async Task<IItemPreviewViewModel> CreatePreviewAsync(IItem item)
{
if (item is not IElement element) throw new NotSupportedException();
return await _serviceProvider
.GetAsyncInitableResolver(element)
.GetRequiredServiceAsync<ElementPreviewViewModel>();
}
}

View File

@@ -9,14 +9,21 @@ namespace FileTime.App.Core.Services;
public class ItemPreviewService : IItemPreviewService public class ItemPreviewService : IItemPreviewService
{ {
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
private readonly IEnumerable<IItemPreviewProvider> _itemPreviewProviders;
public IObservable<IItemPreviewViewModel?> ItemPreview { get; } public IObservable<IItemPreviewViewModel?> ItemPreview { get; }
public ItemPreviewService(IAppState appState, IServiceProvider serviceProvider) public ItemPreviewService(
IAppState appState,
IServiceProvider serviceProvider,
IEnumerable<IItemPreviewProvider> itemPreviewProviders)
{ {
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
_itemPreviewProviders = itemPreviewProviders;
ItemPreview = appState ItemPreview = appState
.SelectedTab .SelectedTab
.Select(t => t?.CurrentSelectedItem.Throttle(TimeSpan.FromMilliseconds(250)) ?? Observable.Return<IItemViewModel?>(null)) .Select(t =>
t?.CurrentSelectedItem.Throttle(TimeSpan.FromMilliseconds(250))
?? Observable.Return<IItemViewModel?>(null))
.Switch() .Switch()
.Select(item => .Select(item =>
item == null item == null
@@ -30,11 +37,12 @@ public class ItemPreviewService : IItemPreviewService
private async Task<IItemPreviewViewModel?> Map(IItemViewModel itemViewModel) private async Task<IItemPreviewViewModel?> Map(IItemViewModel itemViewModel)
{ {
return itemViewModel.BaseItem switch ArgumentNullException.ThrowIfNull(itemViewModel.BaseItem);
{
IElement element => await _serviceProvider.GetAsyncInitableResolver(element) var itemPreviewProvider = _itemPreviewProviders.FirstOrDefault(p => p.CanHandle(itemViewModel.BaseItem));
.GetRequiredServiceAsync<ElementPreviewViewModel>(),
_ => null return itemPreviewProvider is null
}; ? null
: await itemPreviewProvider.CreatePreviewAsync(itemViewModel.BaseItem);
} }
} }

View File

@@ -18,7 +18,8 @@ public class TabPersistenceService : ITabPersistenceService
//TODO: make this a configuration maybe? //TODO: make this a configuration maybe?
private readonly List<string> _contentProvidersNotToRestore = new() private readonly List<string> _contentProvidersNotToRestore = new()
{ {
"search" "search",
"container-size-scan"
}; };
private record PersistenceRoot(TabStates? TabStates); private record PersistenceRoot(TabStates? TabStates);

View File

@@ -1,8 +1,10 @@
using System.Diagnostics; using System.Diagnostics;
using DeclarativeProperty; using DeclarativeProperty;
using FileTime.App.ContainerSizeScanner;
using FileTime.App.Core.UserCommand; using FileTime.App.Core.UserCommand;
using FileTime.App.Core.ViewModels; using FileTime.App.Core.ViewModels;
using FileTime.App.Search; using FileTime.App.Search;
using FileTime.Core.Command;
using FileTime.Core.ContentAccess; using FileTime.Core.ContentAccess;
using FileTime.Core.Enums; using FileTime.Core.Enums;
using FileTime.Core.Interactions; using FileTime.Core.Interactions;
@@ -20,6 +22,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
private readonly ITimelessContentProvider _timelessContentProvider; private readonly ITimelessContentProvider _timelessContentProvider;
private readonly IUserCommandHandlerService _userCommandHandlerService; private readonly IUserCommandHandlerService _userCommandHandlerService;
private readonly IContentAccessorFactory _contentAccessorFactory; private readonly IContentAccessorFactory _contentAccessorFactory;
private readonly IContainerScanSnapshotProvider _containerScanSnapshotProvider;
private IDeclarativeProperty<IContainer?>? _currentLocation; private IDeclarativeProperty<IContainer?>? _currentLocation;
private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem; private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem;
private ITabViewModel? _currentSelectedTab; private ITabViewModel? _currentSelectedTab;
@@ -32,7 +35,8 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
IItemNameConverterService itemNameConverterService, IItemNameConverterService itemNameConverterService,
ITimelessContentProvider timelessContentProvider, ITimelessContentProvider timelessContentProvider,
IUserCommandHandlerService userCommandHandlerService, IUserCommandHandlerService userCommandHandlerService,
IContentAccessorFactory contentAccessorFactory) : base(appState) IContentAccessorFactory contentAccessorFactory,
IContainerScanSnapshotProvider containerScanSnapshotProvider) : base(appState)
{ {
_systemClipboardService = systemClipboardService; _systemClipboardService = systemClipboardService;
_userCommunicationService = userCommunicationService; _userCommunicationService = userCommunicationService;
@@ -41,6 +45,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
_timelessContentProvider = timelessContentProvider; _timelessContentProvider = timelessContentProvider;
_userCommandHandlerService = userCommandHandlerService; _userCommandHandlerService = userCommandHandlerService;
_contentAccessorFactory = contentAccessorFactory; _contentAccessorFactory = contentAccessorFactory;
_containerScanSnapshotProvider = containerScanSnapshotProvider;
SaveCurrentLocation(l => _currentLocation = l); SaveCurrentLocation(l => _currentLocation = l);
SaveCurrentSelectedItem(i => _currentSelectedItem = i); SaveCurrentSelectedItem(i => _currentSelectedItem = i);
SaveSelectedTab(t => _currentSelectedTab = t); SaveSelectedTab(t => _currentSelectedTab = t);
@@ -51,10 +56,20 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
new TypeUserCommandHandler<CopyNativePathCommand>(CopyNativePath), new TypeUserCommandHandler<CopyNativePathCommand>(CopyNativePath),
new TypeUserCommandHandler<CopyBase64Command>(CopyBase64), new TypeUserCommandHandler<CopyBase64Command>(CopyBase64),
new TypeUserCommandHandler<SearchCommand>(Search), new TypeUserCommandHandler<SearchCommand>(Search),
new TypeUserCommandHandler<ScanSizeCommand>(ScanSize),
new TypeUserCommandHandler<SortItemsCommand>(SortItems), new TypeUserCommandHandler<SortItemsCommand>(SortItems),
}); });
} }
private async Task ScanSize()
{
if (_currentLocation?.Value is null) return;
var searchTask = _containerScanSnapshotProvider.StartSizeScan(_currentLocation.Value);
var openContainerCommand = new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, searchTask.SizeContainer));
await _userCommandHandlerService.HandleCommandAsync(openContainerCommand);
}
private async Task SortItems(SortItemsCommand sortItemsCommand) private async Task SortItems(SortItemsCommand sortItemsCommand)
{ {
if (_currentSelectedTab is null) return; if (_currentSelectedTab is null) return;

View File

@@ -27,6 +27,7 @@ public static class Startup
serviceCollection.TryAddSingleton<IItemPreviewService, ItemPreviewService>(); serviceCollection.TryAddSingleton<IItemPreviewService, ItemPreviewService>();
serviceCollection.TryAddSingleton<ITimelineViewModel, TimelineViewModel>(); serviceCollection.TryAddSingleton<ITimelineViewModel, TimelineViewModel>();
serviceCollection.TryAddSingleton<IRefreshSmoothnessCalculator, RefreshSmoothnessCalculator>(); serviceCollection.TryAddSingleton<IRefreshSmoothnessCalculator, RefreshSmoothnessCalculator>();
serviceCollection.TryAddSingleton<IItemPreviewProvider, ElementPreviewProvider>();
return serviceCollection return serviceCollection
.AddCommandHandlers() .AddCommandHandlers()

View File

@@ -48,6 +48,7 @@ public class DefaultIdentifiableCommandHandlerRegister : IStartupHandler
AddUserCommand(RefreshCommand.Instance); AddUserCommand(RefreshCommand.Instance);
AddUserCommand(RenameCommand.Instance); AddUserCommand(RenameCommand.Instance);
AddUserCommand(RunOrOpenCommand.Instance); AddUserCommand(RunOrOpenCommand.Instance);
AddUserCommand(ScanSizeCommand.Instance);
AddUserCommand(StartCommandSchedulerCommand.Instance); AddUserCommand(StartCommandSchedulerCommand.Instance);
AddUserCommand(SortItemsCommand.OrderByNameCommand); AddUserCommand(SortItemsCommand.OrderByNameCommand);
AddUserCommand(SortItemsCommand.OrderByNameDescCommand); AddUserCommand(SortItemsCommand.OrderByNameDescCommand);

View File

@@ -7,13 +7,14 @@ using MvvmGen;
namespace FileTime.App.Core.ViewModels.ItemPreview; namespace FileTime.App.Core.ViewModels.ItemPreview;
[ViewModel] [ViewModel]
public partial class ElementPreviewViewModel : IItemPreviewViewModel, IAsyncInitable<IElement> public partial class ElementPreviewViewModel : IElementPreviewViewModel, IAsyncInitable<IElement>
{ {
public const string PreviewName = "ElementPreview";
private record EncodingResult(char BinaryChar, string PartialResult); private record EncodingResult(char BinaryChar, string PartialResult);
private const int MaxTextPreviewSize = 1024 * 1024; private const int MaxTextPreviewSize = 1024 * 1024;
private static readonly List<Encoding> _encodings = new() private static readonly List<Encoding> Encodings = new()
{ {
Encoding.UTF8, Encoding.UTF8,
Encoding.Unicode, Encoding.Unicode,
@@ -27,6 +28,8 @@ public partial class ElementPreviewViewModel : IItemPreviewViewModel, IAsyncInit
[Property] private string? _textContent; [Property] private string? _textContent;
[Property] private string? _textEncoding; [Property] private string? _textEncoding;
public string Name => PreviewName;
public async Task InitAsync(IElement element) public async Task InitAsync(IElement element)
{ {
try try
@@ -59,7 +62,7 @@ public partial class ElementPreviewViewModel : IItemPreviewViewModel, IAsyncInit
(string, Encoding?) GetNormalizedText(byte[] data) (string, Encoding?) GetNormalizedText(byte[] data)
{ {
var binaryCharacter = new Dictionary<string, EncodingResult>(); var binaryCharacter = new Dictionary<string, EncodingResult>();
foreach (var encoding in _encodings) foreach (var encoding in Encodings)
{ {
var text = encoding.GetString(data); var text = encoding.GetString(data);
var binary = false; var binary = false;

View File

@@ -1,7 +1,6 @@
using System.Collections.ObjectModel; 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.Enums; using FileTime.Core.Enums;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Timeline; using FileTime.Core.Timeline;

View File

@@ -1,5 +1,4 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using DynamicData;
using FileTime.Core.ContentAccess; using FileTime.Core.ContentAccess;
using FileTime.Core.Enums; using FileTime.Core.Enums;
using FileTime.Core.Timeline; using FileTime.Core.Timeline;

View File

@@ -111,6 +111,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Tools.Compression"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Tools.Compression.Core", "Tools\FileTime.Tools.Compression.Core\FileTime.Tools.Compression.Core.csproj", "{EE1721A0-D15A-4E40-BEA5-8AB6BAB8FD44}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.Tools.Compression.Core", "Tools\FileTime.Tools.Compression.Core\FileTime.Tools.Compression.Core.csproj", "{EE1721A0-D15A-4E40-BEA5-8AB6BAB8FD44}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.App.ContainerSizeScanner", "AppCommon\FileTime.App.ContainerSizeScanner\FileTime.App.ContainerSizeScanner.csproj", "{E5FD38ED-6E4B-42AA-850B-470B939B836B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileTime.App.ContainerSizeScanner.Abstractions", "AppCommon\FileTime.App.ContainerSizeScanner.Abstractions\FileTime.App.ContainerSizeScanner.Abstractions.csproj", "{826AFD32-E36B-48BA-BC1E-1476B393CF24}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -297,6 +301,14 @@ Global
{EE1721A0-D15A-4E40-BEA5-8AB6BAB8FD44}.Debug|Any CPU.Build.0 = Debug|Any CPU {EE1721A0-D15A-4E40-BEA5-8AB6BAB8FD44}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EE1721A0-D15A-4E40-BEA5-8AB6BAB8FD44}.Release|Any CPU.ActiveCfg = Release|Any CPU {EE1721A0-D15A-4E40-BEA5-8AB6BAB8FD44}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EE1721A0-D15A-4E40-BEA5-8AB6BAB8FD44}.Release|Any CPU.Build.0 = Release|Any CPU {EE1721A0-D15A-4E40-BEA5-8AB6BAB8FD44}.Release|Any CPU.Build.0 = Release|Any CPU
{E5FD38ED-6E4B-42AA-850B-470B939B836B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E5FD38ED-6E4B-42AA-850B-470B939B836B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E5FD38ED-6E4B-42AA-850B-470B939B836B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E5FD38ED-6E4B-42AA-850B-470B939B836B}.Release|Any CPU.Build.0 = Release|Any CPU
{826AFD32-E36B-48BA-BC1E-1476B393CF24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{826AFD32-E36B-48BA-BC1E-1476B393CF24}.Debug|Any CPU.Build.0 = Debug|Any CPU
{826AFD32-E36B-48BA-BC1E-1476B393CF24}.Release|Any CPU.ActiveCfg = Release|Any CPU
{826AFD32-E36B-48BA-BC1E-1476B393CF24}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -348,6 +360,8 @@ Global
{9062F7D2-34DE-44B7-A2D6-8B3AFDEBB606} = {778AAF38-20FF-438C-A9C3-60850C8B5A27} {9062F7D2-34DE-44B7-A2D6-8B3AFDEBB606} = {778AAF38-20FF-438C-A9C3-60850C8B5A27}
{58243F14-15A6-4601-A071-F99BE896D027} = {8C3CFEFE-78A5-4940-B388-D15FCE02ECE9} {58243F14-15A6-4601-A071-F99BE896D027} = {8C3CFEFE-78A5-4940-B388-D15FCE02ECE9}
{EE1721A0-D15A-4E40-BEA5-8AB6BAB8FD44} = {8C3CFEFE-78A5-4940-B388-D15FCE02ECE9} {EE1721A0-D15A-4E40-BEA5-8AB6BAB8FD44} = {8C3CFEFE-78A5-4940-B388-D15FCE02ECE9}
{E5FD38ED-6E4B-42AA-850B-470B939B836B} = {A5291117-3001-498B-AC8B-E14F71F72570}
{826AFD32-E36B-48BA-BC1E-1476B393CF24} = {A5291117-3001-498B-AC8B-E14F71F72570}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {859FB3DF-C60A-46B1-82E5-90274905D1EF} SolutionGuid = {859FB3DF-C60A-46B1-82E5-90274905D1EF}

View File

@@ -2,6 +2,7 @@ using Avalonia;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using FileTime.App.CommandPalette; using FileTime.App.CommandPalette;
using FileTime.App.ContainerSizeScanner;
using FileTime.App.DependencyInjection; using FileTime.App.DependencyInjection;
using FileTime.App.FrequencyNavigation; using FileTime.App.FrequencyNavigation;
using FileTime.App.Search; using FileTime.App.Search;
@@ -25,6 +26,7 @@ public class App : Application
.AddServerCoreServices() .AddServerCoreServices()
.AddFrequencyNavigation() .AddFrequencyNavigation()
.AddCommandPalette() .AddCommandPalette()
.AddContainerSizeScanner()
.AddSearch() .AddSearch()
.AddCompression() .AddCompression()
.AddConfiguration(configuration) .AddConfiguration(configuration)

View File

@@ -41,6 +41,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.CommandPalette\FileTime.App.CommandPalette.csproj" /> <ProjectReference Include="..\..\..\AppCommon\FileTime.App.CommandPalette\FileTime.App.CommandPalette.csproj" />
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.ContainerSizeScanner\FileTime.App.ContainerSizeScanner.csproj" />
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.Core\FileTime.App.Core.csproj" /> <ProjectReference Include="..\..\..\AppCommon\FileTime.App.Core\FileTime.App.Core.csproj" />
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.DependencyInjection\FileTime.App.DependencyInjection.csproj" /> <ProjectReference Include="..\..\..\AppCommon\FileTime.App.DependencyInjection\FileTime.App.DependencyInjection.csproj" />
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.FrequencyNavigation\FileTime.App.FrequencyNavigation.csproj" /> <ProjectReference Include="..\..\..\AppCommon\FileTime.App.FrequencyNavigation\FileTime.App.FrequencyNavigation.csproj" />

View File

@@ -39,6 +39,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.CommandPalette.Abstractions\FileTime.App.CommandPalette.Abstractions.csproj" /> <ProjectReference Include="..\..\..\AppCommon\FileTime.App.CommandPalette.Abstractions\FileTime.App.CommandPalette.Abstractions.csproj" />
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.ContainerSizeScanner\FileTime.App.ContainerSizeScanner.csproj" />
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" /> <ProjectReference Include="..\..\..\AppCommon\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
<ProjectReference Include="..\..\..\AppCommon\FileTime.App.FrequencyNavigation.Abstractions\FileTime.App.FrequencyNavigation.Abstractions.csproj" /> <ProjectReference Include="..\..\..\AppCommon\FileTime.App.FrequencyNavigation.Abstractions\FileTime.App.FrequencyNavigation.Abstractions.csproj" />
<ProjectReference Include="..\..\..\Providers\FileTime.Providers.Local.Abstractions\FileTime.Providers.Local.Abstractions.csproj" /> <ProjectReference Include="..\..\..\Providers\FileTime.Providers.Local.Abstractions\FileTime.Providers.Local.Abstractions.csproj" />

View File

@@ -8,7 +8,7 @@
<ResourceInclude Source="avares://FileTime.GuiApp/Resources/Brushes.axaml" /> <ResourceInclude Source="avares://FileTime.GuiApp/Resources/Brushes.axaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
</ResourceDictionary> </ResourceDictionary>
</ResourceDictionary.MergedDictionaries> <ResourceDictionary>
<converters:ItemViewModeToBrushConverter <converters:ItemViewModeToBrushConverter
AlternativeBrush="{StaticResource AlternativeItemForegroundBrush}" AlternativeBrush="{StaticResource AlternativeItemForegroundBrush}"
DefaultBrush="{StaticResource ForegroundBrush}" DefaultBrush="{StaticResource ForegroundBrush}"
@@ -27,14 +27,17 @@
x:Key="ItemViewModeToBackgroundConverter" /> x:Key="ItemViewModeToBackgroundConverter" />
<converters:NamePartShrinkerConverter x:Key="NamePartShrinkerConverter" /> <converters:NamePartShrinkerConverter x:Key="NamePartShrinkerConverter" />
<converters:ItemViewModelIsAttributeTypeConverter x:Key="ItemViewModelIsAttributeTypeConverter" /> <converters:ItemViewModelIsAttributeTypeConverter x:Key="ItemViewModelIsAttributeTypeConverter" />
<converters:ItemViewModelIsAttributeTypeConverter Invert="true" x:Key="ItemViewModelIsNotAttributeTypeConverter" /> <converters:ItemViewModelIsAttributeTypeConverter Invert="true"
x:Key="ItemViewModelIsNotAttributeTypeConverter" />
<converters:GetFileExtensionConverter x:Key="GetFileExtensionConverter" /> <converters:GetFileExtensionConverter x:Key="GetFileExtensionConverter" />
<converters:FormatSizeConverter x:Key="FormatSizeConverter" /> <converters:FormatSizeConverter x:Key="FormatSizeConverter" />
<converters:DateTimeConverter x:Key="DateTimeConverter" /> <converters:DateTimeConverter x:Key="DateTimeConverter" />
<converters:SplitStringConverter x:Key="SplitStringConverter" /> <converters:SplitStringConverter x:Key="SplitStringConverter" />
<converters:CompareConverter x:Key="EqualsConverter" /> <converters:CompareConverter x:Key="EqualsConverter" />
<converters:CompareConverter ComparisonCondition="{x:Static converters:ComparisonCondition.NotEqual}" x:Key="NotEqualsConverter" /> <converters:CompareConverter ComparisonCondition="{x:Static converters:ComparisonCondition.NotEqual}"
<converters:CompareConverter ComparisonCondition="{x:Static converters:ComparisonCondition.GreaterThan}" x:Key="GreaterThanConverter" /> x:Key="NotEqualsConverter" />
<converters:CompareConverter ComparisonCondition="{x:Static converters:ComparisonCondition.GreaterThan}"
x:Key="GreaterThanConverter" />
<converters:ExceptionToStringConverter x:Key="ExceptionToStringConverter" /> <converters:ExceptionToStringConverter x:Key="ExceptionToStringConverter" />
<converters:CommandToCommandNameConverter x:Key="CommandToCommandNameConverter" /> <converters:CommandToCommandNameConverter x:Key="CommandToCommandNameConverter" />
<converters:ItemToImageConverter x:Key="ItemToImageConverter" /> <converters:ItemToImageConverter x:Key="ItemToImageConverter" />
@@ -44,4 +47,6 @@
x:Key="PathPreformatter" /> x:Key="PathPreformatter" />
<converters:ContextMenuGenerator x:Key="ContextMenuGenerator" /> <converters:ContextMenuGenerator x:Key="ContextMenuGenerator" />
<converters:TextDecorationConverter x:Key="TextDecorationConverter" /> <converters:TextDecorationConverter x:Key="TextDecorationConverter" />
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary> </ResourceDictionary>

View File

@@ -27,8 +27,10 @@
xmlns:i="clr-namespace:Avalonia.Xaml.Interactivity;assembly=Avalonia.Xaml.Interactivity" xmlns:i="clr-namespace:Avalonia.Xaml.Interactivity;assembly=Avalonia.Xaml.Interactivity"
xmlns:ia="clr-namespace:Avalonia.Xaml.Interactions.Core;assembly=Avalonia.Xaml.Interactions" xmlns:ia="clr-namespace:Avalonia.Xaml.Interactions.Core;assembly=Avalonia.Xaml.Interactions"
xmlns:interactions="using:FileTime.Core.Interactions" xmlns:interactions="using:FileTime.Core.Interactions"
xmlns:itemPreview="clr-namespace:FileTime.App.Core.ViewModels.ItemPreview;assembly=FileTime.App.Core"
xmlns:local="using:FileTime.GuiApp.Views" xmlns:local="using:FileTime.GuiApp.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sizePreview="clr-namespace:FileTime.App.ContainerSizeScanner;assembly=FileTime.App.ContainerSizeScanner"
xmlns:vm="using:FileTime.GuiApp.ViewModels" xmlns:vm="using:FileTime.GuiApp.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources> <Window.Resources>
@@ -45,8 +47,7 @@
</Window.Styles> </Window.Styles>
<Grid Background="{DynamicResource AppBackgroundBrush}"> <Grid Background="{DynamicResource AppBackgroundBrush}">
<Grid IsVisible="{Binding Loading, Converter={x:Static BoolConverters.Not}, FallbackValue=False}" <Grid IsVisible="{Binding Loading, Converter={x:Static BoolConverters.Not}, FallbackValue=False}" x:DataType="vm:IMainWindowViewModel">
x:DataType="vm:IMainWindowViewModel">
<Grid ColumnDefinitions="250,*" RowDefinitions="Auto,*"> <Grid ColumnDefinitions="250,*" RowDefinitions="Auto,*">
@@ -60,10 +61,8 @@
<Grid ColumnDefinitions="*, Auto"> <Grid ColumnDefinitions="*, Auto">
<StackPanel Margin="20,10" Orientation="Horizontal"> <StackPanel Margin="20,10" Orientation="Horizontal">
<local:PathPresenter <local:PathPresenter DataContext="{Binding AppState.SelectedTab^.CurrentLocation^.FullName.Path, Converter={StaticResource PathPreformatter}}" />
DataContext="{Binding AppState.SelectedTab^.CurrentLocation^.FullName.Path, Converter={StaticResource PathPreformatter}}" /> <TextBlock Foreground="{StaticResource AccentBrush}" Text="{Binding AppState.SelectedTab^.CurrentSelectedItem.Value.DisplayNameText}" />
<TextBlock Foreground="{StaticResource AccentBrush}"
Text="{Binding AppState.SelectedTab^.CurrentSelectedItem.Value.DisplayNameText}" />
</StackPanel> </StackPanel>
<StackPanel <StackPanel
Grid.Column="1" Grid.Column="1"
@@ -363,8 +362,7 @@
Grid.Column="0" Grid.Column="0"
Grid.Row="1" Grid.Row="1"
Margin="0,5,5,5"> Margin="0,5,5,5">
<TextBlock Text="{Binding DisplayDetailLabel^}" <TextBlock Text="{Binding DisplayDetailLabel^}" TextAlignment="Right" />
TextAlignment="Right" />
<ProgressBar <ProgressBar
Margin="0,5,0,0" Margin="0,5,0,0"
@@ -376,9 +374,7 @@
Grid.Column="1" Grid.Column="1"
Grid.Row="1" Grid.Row="1"
Margin="5,5,0,5"> Margin="5,5,0,5">
<TextBlock <TextBlock Text="{Binding TotalProgress^, StringFormat={}{0}%}" TextAlignment="Right" />
Text="{Binding TotalProgress^, StringFormat={}{0}%}"
TextAlignment="Right" />
<ProgressBar <ProgressBar
Margin="0,5,0,0" Margin="0,5,0,0"
@@ -417,8 +413,7 @@
<Grid RowDefinitions="Auto,1"> <Grid RowDefinitions="Auto,1">
<StackPanel Margin="20,0,20,0" Orientation="Horizontal"> <StackPanel Margin="20,0,20,0" Orientation="Horizontal">
<TextBlock Text="{Binding TabNumber, StringFormat=({0})}" <TextBlock Text="{Binding TabNumber, StringFormat=({0})}" VerticalAlignment="Center" />
VerticalAlignment="Center" />
<TextBlock <TextBlock
Margin="5,0,0,0" Margin="5,0,0,0"
@@ -464,8 +459,7 @@
Width="1" /> Width="1" />
<Grid Grid.Column="2" RowDefinitions="Auto,*"> <Grid Grid.Column="2" RowDefinitions="Auto,*">
<Grid <Grid IsVisible="{Binding AppState.SelectedTab^.CurrentLocation.Value.IsLoading^, FallbackValue=False}">
IsVisible="{Binding AppState.SelectedTab^.CurrentLocation.Value.IsLoading^, FallbackValue=False}">
<Image <Image
Classes="LoadingAnimation" Classes="LoadingAnimation"
Height="40" Height="40"
@@ -484,8 +478,7 @@
x:Name="CurrentItems"> x:Name="CurrentItems">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate x:DataType="corevm:IItemViewModel"> <DataTemplate x:DataType="corevm:IItemViewModel">
<local:ItemView HorizontalAlignment="Stretch" <local:ItemView HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" />
HorizontalContentAlignment="Stretch" />
</DataTemplate> </DataTemplate>
</ListBox.ItemTemplate> </ListBox.ItemTemplate>
</ListBox> </ListBox>
@@ -511,14 +504,11 @@
Width="1" /> Width="1" />
<Grid Grid.Column="4"> <Grid Grid.Column="4">
<Grid <Grid IsVisible="{Binding ItemPreviewService.ItemPreview^, Converter={x:Static ObjectConverters.IsNull}}">
IsVisible="{Binding ItemPreviewService.ItemPreview^, Converter={x:Static ObjectConverters.IsNull}}"> <Grid IsVisible="{Binding AppState.SelectedTab^.SelectedsChildren.Value, Converter={x:Static ObjectConverters.IsNotNull}, FallbackValue=False}">
<Grid
IsVisible="{Binding AppState.SelectedTab^.SelectedsChildren.Value, Converter={x:Static ObjectConverters.IsNotNull}, FallbackValue=False}">
<Grid RowDefinitions="Auto, Auto, *"> <Grid RowDefinitions="Auto, Auto, *">
<Grid <Grid IsVisible="{Binding AppState.SelectedTab^.CurrentSelectedItemAsContainer.Value.Container.IsLoading^, FallbackValue=False}">
IsVisible="{Binding AppState.SelectedTab^.CurrentSelectedItemAsContainer.Value.Container.IsLoading^, FallbackValue=False}">
<Image <Image
Classes="LoadingAnimation" Classes="LoadingAnimation"
Height="40" Height="40"
@@ -527,8 +517,7 @@
</Grid> </Grid>
<ItemsRepeater Grid.Row="1" <ItemsRepeater Grid.Row="1" ItemsSource="{Binding AppState.SelectedTab^.CurrentLocation.Value.Exceptions}">
ItemsSource="{Binding AppState.SelectedTab^.CurrentLocation.Value.Exceptions}">
<ItemsRepeater.ItemTemplate> <ItemsRepeater.ItemTemplate>
<DataTemplate> <DataTemplate>
<TextBlock <TextBlock
@@ -565,9 +554,7 @@
</TextBlock> </TextBlock>
</Grid> </Grid>
<Grid <Grid IsVisible="{Binding AppState.SelectedTab^.SelectedsChildren.Value, Converter={x:Static ObjectConverters.IsNull}, ConverterParameter=0, FallbackValue=False}" RowDefinitions="Auto, Auto">
IsVisible="{Binding AppState.SelectedTab^.SelectedsChildren.Value, Converter={x:Static ObjectConverters.IsNull}, ConverterParameter=0, FallbackValue=False}"
RowDefinitions="Auto, Auto">
<TextBlock <TextBlock
Foreground="{DynamicResource ErrorBrush}" Foreground="{DynamicResource ErrorBrush}"
HorizontalAlignment="Center" HorizontalAlignment="Center"
@@ -575,8 +562,7 @@
Text="There were some errors while opening container." Text="There were some errors while opening container."
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<ItemsRepeater Grid.Row="1" <ItemsRepeater Grid.Row="1" ItemsSource="{Binding AppState.SelectedTab^.CurrentSelectedItem.Value.BaseItem.Exceptions}">
ItemsSource="{Binding AppState.SelectedTab^.CurrentSelectedItem.Value.BaseItem.Exceptions}">
<ItemsRepeater.ItemTemplate> <ItemsRepeater.ItemTemplate>
<DataTemplate> <DataTemplate>
<TextBlock <TextBlock
@@ -588,30 +574,109 @@
</ItemsRepeater> </ItemsRepeater>
</Grid> </Grid>
</Grid> </Grid>
<Grid <Grid DataContext="{Binding ItemPreviewService.ItemPreview^}" IsVisible="{Binding Converter={x:Static ObjectConverters.IsNotNull}}">
IsVisible="{Binding ItemPreviewService.ItemPreview^, Converter={x:Static ObjectConverters.IsNotNull}}"> <Grid IsVisible="{Binding Name, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static itemPreview:ElementPreviewViewModel.PreviewName}}" x:DataType="itemPreview:ElementPreviewViewModel">
<TextBlock <TextBlock
HorizontalAlignment="Center" HorizontalAlignment="Center"
IsVisible="{Binding ItemPreviewService.ItemPreview^.Mode, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static appCoreModels:ItemPreviewMode.Unknown}, FallbackValue={x:Static appCoreModels:ItemPreviewMode.Unknown}}" IsVisible="{Binding Mode, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static appCoreModels:ItemPreviewMode.Unknown}, FallbackValue={x:Static appCoreModels:ItemPreviewMode.Unknown}}"
Text="Don't know how to preview this item." /> Text="Don't know how to preview this item." />
<TextBlock <TextBlock
HorizontalAlignment="Center" HorizontalAlignment="Center"
IsVisible="{Binding ItemPreviewService.ItemPreview^.Mode, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static appCoreModels:ItemPreviewMode.Empty}, FallbackValue={x:Static appCoreModels:ItemPreviewMode.Unknown}}" IsVisible="{Binding Mode, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static appCoreModels:ItemPreviewMode.Empty}, FallbackValue={x:Static appCoreModels:ItemPreviewMode.Unknown}}"
Text="Empty" /> Text="Empty" />
<Grid <Grid IsVisible="{Binding Mode, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static appCoreModels:ItemPreviewMode.Text}, FallbackValue={x:Static appCoreModels:ItemPreviewMode.Unknown}}" RowDefinitions="*, Auto">
IsVisible="{Binding ItemPreviewService.ItemPreview^.Mode, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static appCoreModels:ItemPreviewMode.Text}, FallbackValue={x:Static appCoreModels:ItemPreviewMode.Unknown}}"
RowDefinitions="*, Auto">
<ScrollViewer> <ScrollViewer>
<TextBox <TextBox IsReadOnly="True" Text="{Binding TextContent}" />
IsReadOnly="True"
Text="{Binding ItemPreviewService.ItemPreview^.TextContent}"
x:CompileBindings="False" />
</ScrollViewer> </ScrollViewer>
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
Margin="5" Margin="5"
Text="{Binding ItemPreviewService.ItemPreview^.TextEncoding, StringFormat=Encoding: {0}}" Text="{Binding TextEncoding, StringFormat=Encoding: {0}}" />
x:CompileBindings="False" /> </Grid>
</Grid>
<Grid
IsVisible="{Binding Name, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static sizePreview:ContainerSizeContainerPreview.PreviewName}}"
RowDefinitions="Auto, Auto"
x:DataType="sizePreview:ContainerSizeContainerPreview">
<ItemsControl Items="{Binding TopItems^}" Margin="0,0,0,30">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.Width>
<MultiBinding Converter="{StaticResource ItemSizeToSizeConverter}">
<MultiBinding.Bindings>
<Binding />
<Binding ElementName="SizeContainerPreview" Path="DataContext.TopItems^" />
<Binding ElementName="SizeContainerPreview" Path="Bounds.Width" />
</MultiBinding.Bindings>
</MultiBinding>
</Grid.Width>
<Rectangle HorizontalAlignment="Stretch">
<Rectangle.Fill>
<MultiBinding Converter="{StaticResource ItemSizeToBrushConverter}">
<MultiBinding.Bindings>
<Binding />
<Binding ElementName="SizeContainerPreview" Path="DataContext.Items^" />
</MultiBinding.Bindings>
</MultiBinding>
</Rectangle.Fill>
</Rectangle>
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Center">
<TextBlock
Classes="SmallText"
Margin="0,5,0,2"
Text="{Binding Name}">
<TextBlock.Foreground>
<MultiBinding Converter="{StaticResource ItemSizeToForegroundBrushConverter}">
<MultiBinding.Bindings>
<Binding />
<Binding ElementName="SizeContainerPreview" Path="DataContext.Items^" />
</MultiBinding.Bindings>
</MultiBinding>
</TextBlock.Foreground>
</TextBlock>
<TextBlock
Classes="SmallText"
Margin="0,2,0,5"
Text="{Binding Size, Converter={StaticResource FormatSizeConverter}, ConverterParameter=0}">
<TextBlock.Foreground>
<MultiBinding Converter="{StaticResource ItemSizeToForegroundBrushConverter}">
<MultiBinding.Bindings>
<Binding />
<Binding ElementName="SizeContainerPreview" Path="DataContext.Items^" />
</MultiBinding.Bindings>
</MultiBinding>
</TextBlock.Foreground>
</TextBlock>
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl
Grid.Row="1"
Items="{Binding Items^}"
x:CompileBindings="False">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="Auto,*" Margin="0,0,0,20">
<TextBlock Text="{Binding Name}" />
<TextBlock
Grid.Column="1"
HorizontalAlignment="Right"
Margin="0,0,20,0"
Text="{Binding Size, Converter={StaticResource FormatSizeConverter}, ConverterParameter=0}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid> </Grid>
</Grid> </Grid>
</Grid> </Grid>
@@ -654,9 +719,7 @@
</Grid> </Grid>
<Grid Grid.Row="3"> <Grid Grid.Row="3">
<Grid <Grid IsVisible="{Binding AppState.ViewMode^, Converter={StaticResource EqualsConverter}, ConverterParameter=RapidTravel}" RowDefinitions="1,Auto">
IsVisible="{Binding AppState.ViewMode^, Converter={StaticResource EqualsConverter}, ConverterParameter=RapidTravel}"
RowDefinitions="1,Auto">
<Rectangle <Rectangle
Fill="{DynamicResource ContentSeparatorBrush}" Fill="{DynamicResource ContentSeparatorBrush}"
@@ -678,8 +741,7 @@
</StackPanel> </StackPanel>
</Grid> </Grid>
<Grid <Grid IsVisible="{Binding AppState.PossibleCommands.Count, Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}">
IsVisible="{Binding AppState.PossibleCommands.Count, Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="1" /> <RowDefinition Height="1" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
@@ -702,8 +764,7 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBlock Text="{Binding KeysDisplayText}" /> <TextBlock Text="{Binding KeysDisplayText}" />
<TextBlock Grid.Column="1" <TextBlock Grid.Column="1" Text="{Binding Command, Converter={StaticResource CommandToCommandNameConverter}}" />
Text="{Binding Command, Converter={StaticResource CommandToCommandNameConverter}}" />
</Grid> </Grid>
</DataTemplate> </DataTemplate>
</ItemsRepeater.ItemTemplate> </ItemsRepeater.ItemTemplate>
@@ -772,10 +833,8 @@
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<Grid> <Grid>
<Grid <Grid IsVisible="{Binding PreviewType, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static appInteractions:PreviewType.DoubleTextList}}">
IsVisible="{Binding PreviewType, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static appInteractions:PreviewType.DoubleTextList}}"> <ItemsControl ItemsSource="{Binding Items}" x:DataType="appInteractions:DoubleTextListPreview">
<ItemsControl ItemsSource="{Binding Items}"
x:DataType="appInteractions:DoubleTextListPreview">
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<Grid ColumnDefinitions="*,*"> <Grid ColumnDefinitions="*,*">
@@ -787,13 +846,11 @@
</ItemsControl.ItemsPanel> </ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<TextBlock Text="{Binding Text}" <TextBlock Text="{Binding Text}" TextDecorations="{Binding IsSpecial, Converter={StaticResource TextDecorationConverter}}" />
TextDecorations="{Binding IsSpecial, Converter={StaticResource TextDecorationConverter}}" />
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</ItemsControl> </ItemsControl>
<ItemsControl Grid.Column="1" <ItemsControl Grid.Column="1" ItemsSource="{Binding Text2^}">
ItemsSource="{Binding Text2^}">
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" /> <StackPanel Orientation="Horizontal" />
@@ -801,8 +858,7 @@
</ItemsControl.ItemsPanel> </ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<TextBlock Text="{Binding Text}" <TextBlock Text="{Binding Text}" TextDecorations="{Binding IsSpecial, Converter={StaticResource TextDecorationConverter}}" />
TextDecorations="{Binding IsSpecial, Converter={StaticResource TextDecorationConverter}}" />
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</ItemsControl> </ItemsControl>
@@ -908,7 +964,7 @@
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
IsVisible="{Binding ShowWindow^, FallbackValue=False}" IsVisible="{Binding ShowWindow^, FallbackValue=False}"
VerticalAlignment="Stretch"> VerticalAlignment="Stretch">
<Grid Margin="100" Background="{DynamicResource ContainerBackgroundColor}"> <Grid Background="{DynamicResource ContainerBackgroundColor}" Margin="100">
<local:FrequencyNavigation IsVisible="{Binding ShowWindow^, FallbackValue=False}" /> <local:FrequencyNavigation IsVisible="{Binding ShowWindow^, FallbackValue=False}" />
</Grid> </Grid>
</Border> </Border>
@@ -919,7 +975,7 @@
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
IsVisible="{Binding ShowWindow^, FallbackValue=False}" IsVisible="{Binding ShowWindow^, FallbackValue=False}"
VerticalAlignment="Stretch"> VerticalAlignment="Stretch">
<Grid Margin="100" Background="{DynamicResource ContainerBackgroundColor}"> <Grid Background="{DynamicResource ContainerBackgroundColor}" Margin="100">
<local:CommandPalette IsVisible="{Binding ShowWindow^, FallbackValue=False}" /> <local:CommandPalette IsVisible="{Binding ShowWindow^, FallbackValue=False}" />
</Grid> </Grid>
</Border> </Border>

View File

@@ -10,7 +10,7 @@ public sealed class CombineProperty<TFrom, TTo> : DeclarativePropertyBase<TTo>
_combiner = combiner; _combiner = combiner;
} }
public async Task AddSource(IDeclarativeProperty<TFrom> source) public async Task AddSourceAsync(IDeclarativeProperty<TFrom> source)
{ {
if (_sourceProperties.Contains(source)) return; if (_sourceProperties.Contains(source)) return;
_sourceProperties.Add(source); _sourceProperties.Add(source);