Container size scan, improvements
This commit is contained in:
@@ -1,13 +0,0 @@
|
|||||||
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; }
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using FileTime.Core.Models;
|
using FileTime.App.Core.Models.Traits;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
|
||||||
namespace FileTime.App.ContainerSizeScanner;
|
namespace FileTime.App.ContainerSizeScanner;
|
||||||
|
|
||||||
public interface ISizeItem : IItem
|
public interface ISizeItem : IItem, ISizeProvider
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
using FileTime.App.Core.ViewModels.ItemPreview;
|
using DeclarativeProperty;
|
||||||
|
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||||
|
|
||||||
namespace FileTime.App.ContainerSizeScanner;
|
namespace FileTime.App.ContainerSizeScanner;
|
||||||
|
|
||||||
public interface ISizePreviewItem : IItemPreviewViewModel
|
public interface ISizePreviewItem
|
||||||
{
|
{
|
||||||
|
IDeclarativeProperty<long> Size { get; }
|
||||||
|
string Name { get; }
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using DeclarativeProperty;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Core.Models.ContainerTraits;
|
||||||
|
|
||||||
|
namespace FileTime.App.ContainerSizeScanner;
|
||||||
|
|
||||||
|
public interface ISizeScanContainer : ISizeItem, IContainer, IStatusProviderContainer
|
||||||
|
{
|
||||||
|
public Task AddSizeSourceAsync(IDeclarativeProperty<long> sizeElement);
|
||||||
|
ObservableCollection<ISizeScanContainer> ChildContainers { get; }
|
||||||
|
ObservableCollection<ISizeScanElement> ChildElements { get; }
|
||||||
|
ObservableCollection<ISizeItem> SizeItems { get; }
|
||||||
|
IContainer RealContainer { get; init; }
|
||||||
|
Task StartLoadingAsync();
|
||||||
|
Task StopLoadingAsync();
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using FileTime.Core.Models;
|
||||||
|
|
||||||
|
namespace FileTime.App.ContainerSizeScanner;
|
||||||
|
|
||||||
|
public interface ISizeScanElement : ISizeItem, IElement
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@ namespace FileTime.App.ContainerSizeScanner;
|
|||||||
|
|
||||||
public interface ISizeScanTask : IInitable<IContainer>
|
public interface ISizeScanTask : IInitable<IContainer>
|
||||||
{
|
{
|
||||||
IContainerSizeScanContainer SizeContainer { get; }
|
ISizeScanContainer SizeSizeScanContainer { get; }
|
||||||
|
bool IsRunning { get; }
|
||||||
void Start();
|
void Start();
|
||||||
|
void Stop();
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using DeclarativeProperty;
|
||||||
|
|
||||||
|
namespace FileTime.App.ContainerSizeScanner;
|
||||||
|
|
||||||
|
public class ChildContainerPreview : ISizePreviewItem
|
||||||
|
{
|
||||||
|
public ChildContainerPreview(ISizeScanContainer container)
|
||||||
|
{
|
||||||
|
Name = container.Name;
|
||||||
|
Size = container.Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
public IDeclarativeProperty<long> Size { get; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using DeclarativeProperty;
|
||||||
|
|
||||||
|
namespace FileTime.App.ContainerSizeScanner;
|
||||||
|
|
||||||
|
public class ChildElementPreview : ISizePreviewItem
|
||||||
|
{
|
||||||
|
public ChildElementPreview(ISizeScanElement element)
|
||||||
|
{
|
||||||
|
Name = element.Name;
|
||||||
|
Size = element.Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
public IDeclarativeProperty<long> Size { get; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||||
|
using ObservableComputations;
|
||||||
|
|
||||||
|
namespace FileTime.App.ContainerSizeScanner;
|
||||||
|
|
||||||
|
public class ContainerPreview : IItemPreviewViewModel, IDisposable
|
||||||
|
{
|
||||||
|
private readonly OcConsumer _topItemsConsumer = new();
|
||||||
|
public const string PreviewName = "SizePreviewContainer";
|
||||||
|
public string Name => PreviewName;
|
||||||
|
|
||||||
|
public ObservableCollection<ISizePreviewItem> TopItems { get; }
|
||||||
|
|
||||||
|
public ContainerPreview(ISizeScanContainer sizeScanContainer)
|
||||||
|
{
|
||||||
|
TopItems = sizeScanContainer
|
||||||
|
.SizeItems
|
||||||
|
.Ordering(c => c.Size.Value, ListSortDirection.Descending)
|
||||||
|
.Taking(0, 10)
|
||||||
|
.Selecting(i => CreatePreviewItem(i))
|
||||||
|
.For(_topItemsConsumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ISizePreviewItem CreatePreviewItem(ISizeItem sizeItem)
|
||||||
|
=> sizeItem switch
|
||||||
|
{
|
||||||
|
ISizeScanContainer container => new ChildContainerPreview(container),
|
||||||
|
ISizeScanElement element => new ChildElementPreview(element),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(sizeItem))
|
||||||
|
};
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
=> _topItemsConsumer.Dispose();
|
||||||
|
}
|
||||||
@@ -7,14 +7,14 @@ using InitableService;
|
|||||||
|
|
||||||
namespace FileTime.App.ContainerSizeScanner;
|
namespace FileTime.App.ContainerSizeScanner;
|
||||||
|
|
||||||
public class ContainerScanSnapshotProvider : ContentProviderBase, IContainerScanSnapshotProvider
|
public class ContainerScanProvider : ContentProviderBase, IContainerScanSnapshotProvider
|
||||||
{
|
{
|
||||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly List<ISizeScanTask> _sizeScanTasks = new();
|
private readonly List<ISizeScanTask> _sizeScanTasks = new();
|
||||||
internal const string ContentProviderName = "container-size-scan";
|
internal const string ContentProviderName = "container-size-scan";
|
||||||
|
|
||||||
public ContainerScanSnapshotProvider(
|
public ContainerScanProvider(
|
||||||
ITimelessContentProvider timelessContentProvider,
|
ITimelessContentProvider timelessContentProvider,
|
||||||
IServiceProvider serviceProvider)
|
IServiceProvider serviceProvider)
|
||||||
: base(ContentProviderName, timelessContentProvider)
|
: base(ContentProviderName, timelessContentProvider)
|
||||||
@@ -36,7 +36,7 @@ public class ContainerScanSnapshotProvider : ContentProviderBase, IContainerScan
|
|||||||
|
|
||||||
var pathParts = fullName.Path.Split(Constants.SeparatorChar);
|
var pathParts = fullName.Path.Split(Constants.SeparatorChar);
|
||||||
|
|
||||||
var item = _sizeScanTasks.FirstOrDefault(t => t.SizeContainer.Name == pathParts[1])?.SizeContainer;
|
var item = _sizeScanTasks.FirstOrDefault(t => t.SizeSizeScanContainer.Name == pathParts[1])?.SizeSizeScanContainer;
|
||||||
|
|
||||||
if (pathParts.Length == 2)
|
if (pathParts.Length == 2)
|
||||||
return item ?? throw new ItemNotFoundException(fullName);
|
return item ?? throw new ItemNotFoundException(fullName);
|
||||||
@@ -49,10 +49,10 @@ public class ContainerScanSnapshotProvider : ContentProviderBase, IContainerScan
|
|||||||
|
|
||||||
if (item is not null)
|
if (item is not null)
|
||||||
{
|
{
|
||||||
var container = item.ChildContainers.FirstOrDefault(c => c.Name == pathParts[^1]);
|
var childItem = item.SizeItems.FirstOrDefault(c => c.Name == pathParts[^1]);
|
||||||
if (container is not null) return container;
|
if (childItem is not null) return childItem;
|
||||||
|
|
||||||
var childName = item.RealContainer.FullName?.GetChild(pathParts[^1]);
|
/*var childName = item.RealContainer.FullName?.GetChild(pathParts[^1]);
|
||||||
if (childName is null) throw new ItemNotFoundException(fullName);
|
if (childName is null) throw new ItemNotFoundException(fullName);
|
||||||
|
|
||||||
return await _timelessContentProvider.GetItemByFullNameAsync(
|
return await _timelessContentProvider.GetItemByFullNameAsync(
|
||||||
@@ -61,7 +61,7 @@ public class ContainerScanSnapshotProvider : ContentProviderBase, IContainerScan
|
|||||||
forceResolve,
|
forceResolve,
|
||||||
forceResolvePathType,
|
forceResolvePathType,
|
||||||
itemInitializationSettings
|
itemInitializationSettings
|
||||||
);
|
);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ItemNotFoundException(fullName);
|
throw new ItemNotFoundException(fullName);
|
||||||
@@ -106,7 +106,7 @@ public class ContainerScanSnapshotProvider : ContentProviderBase, IContainerScan
|
|||||||
|
|
||||||
_sizeScanTasks.Add(searchTask);
|
_sizeScanTasks.Add(searchTask);
|
||||||
searchTask.Start();
|
searchTask.Start();
|
||||||
Items.Add(new AbsolutePath(_timelessContentProvider, searchTask.SizeContainer));
|
Items.Add(new AbsolutePath(_timelessContentProvider, searchTask.SizeSizeScanContainer));
|
||||||
|
|
||||||
return searchTask;
|
return searchTask;
|
||||||
}
|
}
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
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 };
|
|
||||||
}
|
|
||||||
@@ -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 PreviewProvider : IItemPreviewProvider
|
||||||
|
{
|
||||||
|
public bool CanHandle(IItem item) => item is SizeScanContainer;
|
||||||
|
|
||||||
|
public Task<IItemPreviewViewModel> CreatePreviewAsync(IItem item)
|
||||||
|
{
|
||||||
|
if (item is not SizeScanContainer container) throw new NotSupportedException();
|
||||||
|
|
||||||
|
return Task.FromResult((IItemPreviewViewModel) new ContainerPreview(container));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,12 +5,13 @@ using DeclarativeProperty;
|
|||||||
using FileTime.Core.ContentAccess;
|
using FileTime.Core.ContentAccess;
|
||||||
using FileTime.Core.Enums;
|
using FileTime.Core.Enums;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Core.Models.ContainerTraits;
|
||||||
using FileTime.Core.Timeline;
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
namespace FileTime.App.ContainerSizeScanner;
|
namespace FileTime.App.ContainerSizeScanner;
|
||||||
|
|
||||||
//TODO: create readonly version
|
//TODO: create readonly version
|
||||||
public class ContainerSizeScanContainer : IContainerSizeScanContainer
|
public record SizeScanContainer : ISizeScanContainer, IEscHandlerContainer
|
||||||
{
|
{
|
||||||
private readonly ReadOnlyExtensionCollection _readOnlyExtensions;
|
private readonly ReadOnlyExtensionCollection _readOnlyExtensions;
|
||||||
private readonly BehaviorSubject<bool> _isLoading = new(false);
|
private readonly BehaviorSubject<bool> _isLoading = new(false);
|
||||||
@@ -19,20 +20,20 @@ public class ContainerSizeScanContainer : IContainerSizeScanContainer
|
|||||||
public required string DisplayName { get; init; }
|
public required string DisplayName { get; init; }
|
||||||
public required FullName? FullName { get; init; }
|
public required FullName? FullName { get; init; }
|
||||||
public required NativePath? NativePath { get; init; }
|
public required NativePath? NativePath { get; init; }
|
||||||
public AbsolutePath? Parent { get; init; }
|
public required AbsolutePath? Parent { get; init; }
|
||||||
|
public required IContentProvider Provider { get; init; }
|
||||||
public bool IsHidden => false;
|
public bool IsHidden => false;
|
||||||
public bool IsExists => true;
|
public bool IsExists => true;
|
||||||
public DateTime? CreatedAt { get; }
|
public DateTime? CreatedAt { get; } = DateTime.Now;
|
||||||
public SupportsDelete CanDelete => SupportsDelete.True;
|
public SupportsDelete CanDelete => SupportsDelete.True;
|
||||||
public bool CanRename => false;
|
public bool CanRename => false;
|
||||||
public IContentProvider Provider { get; }
|
|
||||||
public string? Attributes => null;
|
public string? Attributes => null;
|
||||||
public AbsolutePathType Type => AbsolutePathType.Container;
|
public AbsolutePathType Type => AbsolutePathType.Container;
|
||||||
public PointInTime PointInTime => PointInTime.Present;
|
public PointInTime PointInTime => PointInTime.Present;
|
||||||
public ObservableCollection<Exception> Exceptions { get; } = new();
|
public ObservableCollection<Exception> Exceptions { get; } = new();
|
||||||
public ExtensionCollection Extensions { get; } = new();
|
public ExtensionCollection Extensions { get; } = new();
|
||||||
ReadOnlyExtensionCollection IItem.Extensions => _readOnlyExtensions;
|
ReadOnlyExtensionCollection IItem.Extensions => _readOnlyExtensions;
|
||||||
public IItem WithParent(AbsolutePath parent) => throw new NotImplementedException();
|
public IItem WithParent(AbsolutePath parent) => this with {Parent = parent};
|
||||||
|
|
||||||
public ObservableCollection<AbsolutePath> Items { get; } = new();
|
public ObservableCollection<AbsolutePath> Items { get; } = new();
|
||||||
public IObservable<bool> IsLoading { get; }
|
public IObservable<bool> IsLoading { get; }
|
||||||
@@ -41,18 +42,21 @@ public class ContainerSizeScanContainer : IContainerSizeScanContainer
|
|||||||
|
|
||||||
public bool AllowRecursiveDeletion => false;
|
public bool AllowRecursiveDeletion => false;
|
||||||
|
|
||||||
public IDeclarativeProperty<long> Size => _size;
|
public IDeclarativeProperty<long> Size { get; }
|
||||||
public ObservableCollection<IContainerSizeScanContainer> ChildContainers { get; } = new();
|
|
||||||
|
public ObservableCollection<ISizeScanContainer> ChildContainers { get; } = new();
|
||||||
|
public ObservableCollection<ISizeScanElement> ChildElements { get; } = new();
|
||||||
|
public ObservableCollection<ISizeItem> SizeItems { get; } = new();
|
||||||
public required IContainer RealContainer { get; init; }
|
public required IContainer RealContainer { get; init; }
|
||||||
|
|
||||||
public ContainerSizeScanContainer(IContainerScanSnapshotProvider provider)
|
internal SizeScanContainer()
|
||||||
{
|
{
|
||||||
_readOnlyExtensions = new ReadOnlyExtensionCollection(Extensions);
|
_readOnlyExtensions = new ReadOnlyExtensionCollection(Extensions);
|
||||||
IsLoading = _isLoading.AsObservable();
|
IsLoading = _isLoading.AsObservable();
|
||||||
|
|
||||||
_size = new(childContainerSizes => Task.FromResult(childContainerSizes.Sum()));
|
_size = new(childContainerSizes => Task.FromResult(childContainerSizes.Sum()));
|
||||||
|
Size = _size.Debounce(TimeSpan.FromSeconds(1));
|
||||||
CreatedAt = DateTime.Now;
|
CreatedAt = DateTime.Now;
|
||||||
Provider = provider;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AddSizeSourceAsync(IDeclarativeProperty<long> sizeElement)
|
public async Task AddSizeSourceAsync(IDeclarativeProperty<long> sizeElement)
|
||||||
@@ -71,4 +75,19 @@ public class ContainerSizeScanContainer : IContainerSizeScanContainer
|
|||||||
IsLoaded = true;
|
IsLoaded = true;
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public required IDeclarativeProperty<string> Status { get; init; } = new DeclarativeProperty<string>();
|
||||||
|
|
||||||
|
public required SizeScanTask SizeScanTask { get; init; }
|
||||||
|
|
||||||
|
public Task<ContainerEscapeResult> HandleEsc()
|
||||||
|
{
|
||||||
|
if (!SizeScanTask.IsRunning)
|
||||||
|
{
|
||||||
|
return Task.FromResult(new ContainerEscapeResult(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
SizeScanTask.Stop();
|
||||||
|
return Task.FromResult(new ContainerEscapeResult(true));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using DeclarativeProperty;
|
||||||
|
using FileTime.Core.ContentAccess;
|
||||||
|
using FileTime.Core.Enums;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
|
namespace FileTime.App.ContainerSizeScanner;
|
||||||
|
|
||||||
|
public record SizeScanElement : ISizeScanElement
|
||||||
|
{
|
||||||
|
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 required IDeclarativeProperty<long> Size { get; init; }
|
||||||
|
public bool IsHidden => false;
|
||||||
|
public bool IsExists => true;
|
||||||
|
public DateTime? CreatedAt { get; } = DateTime.Now;
|
||||||
|
public SupportsDelete CanDelete => SupportsDelete.False;
|
||||||
|
public bool CanRename => false;
|
||||||
|
public string? Attributes => "";
|
||||||
|
public required IContentProvider Provider { get; init; }
|
||||||
|
public PointInTime PointInTime { get; } = PointInTime.Present;
|
||||||
|
public ObservableCollection<Exception> Exceptions { get; } = new();
|
||||||
|
public ReadOnlyExtensionCollection Extensions { get; } = new();
|
||||||
|
public AbsolutePathType Type => AbsolutePathType.Element;
|
||||||
|
|
||||||
|
public IItem WithParent(AbsolutePath parent) => this with {Parent = parent};
|
||||||
|
}
|
||||||
@@ -9,13 +9,19 @@ namespace FileTime.App.ContainerSizeScanner;
|
|||||||
|
|
||||||
public class SizeScanTask : ISizeScanTask
|
public class SizeScanTask : ISizeScanTask
|
||||||
{
|
{
|
||||||
|
private bool _cancelled;
|
||||||
|
private int _processedItems;
|
||||||
|
private ulong _processedItemsTotal;
|
||||||
private IContainer _scanSizeOf = null!;
|
private IContainer _scanSizeOf = null!;
|
||||||
private readonly IContainerScanSnapshotProvider _containerScanSnapshotProvider;
|
private readonly IContainerScanSnapshotProvider _containerScanSnapshotProvider;
|
||||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||||
private readonly ILogger<SizeScanTask> _logger;
|
private readonly ILogger<SizeScanTask> _logger;
|
||||||
private Thread? _sizeScanThread;
|
private Thread? _sizeScanThread;
|
||||||
private static int _searchId = 1;
|
private static int _searchId = 1;
|
||||||
public IContainerSizeScanContainer SizeContainer { get; private set; } = null!;
|
private readonly DeclarativeProperty<string> _containerStatus = new();
|
||||||
|
private readonly IDeclarativeProperty<string> _containerStatusDebounced;
|
||||||
|
public ISizeScanContainer SizeSizeScanContainer { get; private set; } = null!;
|
||||||
|
public bool IsRunning { get; private set; }
|
||||||
|
|
||||||
public SizeScanTask(
|
public SizeScanTask(
|
||||||
IContainerScanSnapshotProvider containerScanSnapshotProvider,
|
IContainerScanSnapshotProvider containerScanSnapshotProvider,
|
||||||
@@ -25,21 +31,25 @@ public class SizeScanTask : ISizeScanTask
|
|||||||
_containerScanSnapshotProvider = containerScanSnapshotProvider;
|
_containerScanSnapshotProvider = containerScanSnapshotProvider;
|
||||||
_timelessContentProvider = timelessContentProvider;
|
_timelessContentProvider = timelessContentProvider;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_containerStatusDebounced = _containerStatus.Debounce(TimeSpan.FromMilliseconds(250));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Init(IContainer scanSizeOf)
|
public void Init(IContainer scanSizeOf)
|
||||||
{
|
{
|
||||||
_scanSizeOf = scanSizeOf;
|
_scanSizeOf = scanSizeOf;
|
||||||
var name = $"{_searchId++}_{scanSizeOf.Name}";
|
var name = $"{_searchId++}_{scanSizeOf.Name}";
|
||||||
var randomId = ContainerScanSnapshotProvider.ContentProviderName + Constants.SeparatorChar + name;
|
var randomId = ContainerScanProvider.ContentProviderName + Constants.SeparatorChar + name;
|
||||||
SizeContainer = new ContainerSizeScanContainer(_containerScanSnapshotProvider)
|
SizeSizeScanContainer = new SizeScanContainer
|
||||||
{
|
{
|
||||||
Name = name,
|
Name = name,
|
||||||
DisplayName = scanSizeOf.DisplayName,
|
DisplayName = scanSizeOf.DisplayName,
|
||||||
FullName = new FullName(randomId),
|
FullName = new FullName(randomId),
|
||||||
NativePath = new NativePath(randomId),
|
NativePath = new NativePath(randomId),
|
||||||
Parent = new AbsolutePath(_timelessContentProvider, _containerScanSnapshotProvider),
|
Parent = new AbsolutePath(_timelessContentProvider, _containerScanSnapshotProvider),
|
||||||
RealContainer = scanSizeOf
|
RealContainer = scanSizeOf,
|
||||||
|
Provider = _containerScanSnapshotProvider,
|
||||||
|
Status = _containerStatusDebounced,
|
||||||
|
SizeScanTask = this
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,23 +62,34 @@ public class SizeScanTask : ISizeScanTask
|
|||||||
_sizeScanThread = sizeScanThread;
|
_sizeScanThread = sizeScanThread;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Stop() => _cancelled = true;
|
||||||
|
|
||||||
private async void Run()
|
private async void Run()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await TraverseTree(_scanSizeOf, SizeContainer);
|
IsRunning = true;
|
||||||
|
await SizeSizeScanContainer.StartLoadingAsync();
|
||||||
|
await TraverseTree(_scanSizeOf, SizeSizeScanContainer);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error while scanning container {ContainerName}", _scanSizeOf.Name);
|
_logger.LogError(ex, "Error while scanning container {ContainerName}", _scanSizeOf.Name);
|
||||||
|
SizeSizeScanContainer.Exceptions.Add(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IsRunning = false;
|
||||||
|
await SizeSizeScanContainer.StopLoadingAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: make static
|
//TODO: make static
|
||||||
private async Task TraverseTree(
|
private async Task TraverseTree(
|
||||||
IContainer realContainer,
|
IContainer realContainer,
|
||||||
IContainerSizeScanContainer container)
|
ISizeScanContainer sizeScanContainer)
|
||||||
{
|
{
|
||||||
|
if(_cancelled) return;
|
||||||
|
|
||||||
|
await realContainer.WaitForLoaded();
|
||||||
var resolvedItems = new List<IItem>(realContainer.Items.Count);
|
var resolvedItems = new List<IItem>(realContainer.Items.Count);
|
||||||
foreach (var item in realContainer.Items)
|
foreach (var item in realContainer.Items)
|
||||||
{
|
{
|
||||||
@@ -78,34 +99,59 @@ public class SizeScanTask : ISizeScanTask
|
|||||||
|
|
||||||
foreach (var element in resolvedItems.OfType<IElement>())
|
foreach (var element in resolvedItems.OfType<IElement>())
|
||||||
{
|
{
|
||||||
|
if(_cancelled) return;
|
||||||
|
|
||||||
var fileExtension = element.GetExtension<FileExtension>();
|
var fileExtension = element.GetExtension<FileExtension>();
|
||||||
if (fileExtension?.Size is not { } size) continue;
|
if (fileExtension?.Size is not { } size) continue;
|
||||||
|
|
||||||
var childName = container.FullName!.GetChild(element.Name).Path;
|
var sizeProperty = new DeclarativeProperty<long>(size);
|
||||||
await container.AddSizeSourceAsync(new DeclarativeProperty<long>(size));
|
|
||||||
container.Items.Add(new AbsolutePath(
|
var childName = sizeScanContainer.FullName!.GetChild(element.Name).Path;
|
||||||
|
await sizeScanContainer.AddSizeSourceAsync(sizeProperty);
|
||||||
|
sizeScanContainer.Items.Add(new AbsolutePath(
|
||||||
_timelessContentProvider,
|
_timelessContentProvider,
|
||||||
PointInTime.Present,
|
PointInTime.Present,
|
||||||
new FullName(childName),
|
new FullName(childName),
|
||||||
AbsolutePathType.Element));
|
AbsolutePathType.Element));
|
||||||
|
|
||||||
|
|
||||||
|
var childSearchContainer = new SizeScanElement
|
||||||
|
{
|
||||||
|
Name = element.Name,
|
||||||
|
DisplayName = element.DisplayName,
|
||||||
|
FullName = new FullName(childName),
|
||||||
|
NativePath = new NativePath(childName),
|
||||||
|
Parent = new AbsolutePath(_timelessContentProvider, sizeScanContainer),
|
||||||
|
Provider = _containerScanSnapshotProvider,
|
||||||
|
Size = sizeProperty
|
||||||
|
};
|
||||||
|
sizeScanContainer.SizeItems.Add(childSearchContainer);
|
||||||
|
_processedItems++;
|
||||||
|
_processedItemsTotal++;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var childContainer in resolvedItems.OfType<IContainer>())
|
foreach (var childContainer in resolvedItems.OfType<IContainer>())
|
||||||
{
|
{
|
||||||
var childName = container.FullName!.GetChild(childContainer.Name).Path;
|
if(_cancelled) return;
|
||||||
var childSearchContainer = new ContainerSizeScanContainer(_containerScanSnapshotProvider)
|
|
||||||
|
var childName = sizeScanContainer.FullName!.GetChild(childContainer.Name).Path;
|
||||||
|
var childSearchContainer = new SizeScanContainer
|
||||||
{
|
{
|
||||||
Name = childContainer.Name,
|
Name = childContainer.Name,
|
||||||
DisplayName = childContainer.DisplayName,
|
DisplayName = childContainer.DisplayName,
|
||||||
FullName = new FullName(childName),
|
FullName = new FullName(childName),
|
||||||
NativePath = new NativePath(childName),
|
NativePath = new NativePath(childName),
|
||||||
Parent = new AbsolutePath(_timelessContentProvider, container),
|
Parent = new AbsolutePath(_timelessContentProvider, sizeScanContainer),
|
||||||
RealContainer = childContainer
|
RealContainer = childContainer,
|
||||||
|
Provider = _containerScanSnapshotProvider,
|
||||||
|
Status = _containerStatusDebounced,
|
||||||
|
SizeScanTask = this
|
||||||
};
|
};
|
||||||
|
|
||||||
container.ChildContainers.Add(childSearchContainer);
|
sizeScanContainer.ChildContainers.Add(childSearchContainer);
|
||||||
await container.AddSizeSourceAsync(childSearchContainer.Size);
|
sizeScanContainer.SizeItems.Add(childSearchContainer);
|
||||||
container.Items.Add(new AbsolutePath(
|
await sizeScanContainer.AddSizeSourceAsync(childSearchContainer.Size);
|
||||||
|
sizeScanContainer.Items.Add(new AbsolutePath(
|
||||||
_timelessContentProvider,
|
_timelessContentProvider,
|
||||||
PointInTime.Present,
|
PointInTime.Present,
|
||||||
new FullName(childName),
|
new FullName(childName),
|
||||||
@@ -113,5 +159,16 @@ public class SizeScanTask : ISizeScanTask
|
|||||||
|
|
||||||
await TraverseTree(childContainer, childSearchContainer);
|
await TraverseTree(childContainer, childSearchContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_processedItems++;
|
||||||
|
_processedItemsTotal++;
|
||||||
|
await _containerStatus.SetValue("Items processed: " + _processedItemsTotal);
|
||||||
|
|
||||||
|
if (_processedItems > 1000)
|
||||||
|
{
|
||||||
|
_processedItems = 0;
|
||||||
|
//Let some time for the UI to refresh
|
||||||
|
await Task.Delay(1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,10 +9,10 @@ public static class Startup
|
|||||||
{
|
{
|
||||||
public static IServiceCollection AddContainerSizeScanner(this IServiceCollection services)
|
public static IServiceCollection AddContainerSizeScanner(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.TryAddSingleton<IContainerScanSnapshotProvider, ContainerScanSnapshotProvider>();
|
services.TryAddSingleton<IContainerScanSnapshotProvider, ContainerScanProvider>();
|
||||||
services.AddSingleton<IContentProvider>(sp => sp.GetRequiredService<IContainerScanSnapshotProvider>());
|
services.AddSingleton<IContentProvider>(sp => sp.GetRequiredService<IContainerScanSnapshotProvider>());
|
||||||
services.AddTransient<ISizeScanTask, SizeScanTask>();
|
services.AddTransient<ISizeScanTask, SizeScanTask>();
|
||||||
services.AddTransient<IItemPreviewProvider, ContainerSizePreviewProvider>();
|
services.AddTransient<IItemPreviewProvider, PreviewProvider>();
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,6 @@ public class DoubleTextPreview : IPreviewElement
|
|||||||
public IObservable<List<ItemNamePart>> Text1 { get; init; } = new BehaviorSubject<List<ItemNamePart>>(new());
|
public IObservable<List<ItemNamePart>> Text1 { get; init; } = new BehaviorSubject<List<ItemNamePart>>(new());
|
||||||
public IObservable<List<ItemNamePart>> Text2 { get; init; } = new BehaviorSubject<List<ItemNamePart>>(new());
|
public IObservable<List<ItemNamePart>> Text2 { get; init; } = new BehaviorSubject<List<ItemNamePart>>(new());
|
||||||
|
|
||||||
public PreviewType PreviewType { get; } = PreviewType.DoubleTextList;
|
public PreviewType PreviewType => PreviewType.DoubleTextList;
|
||||||
object IPreviewElement.PreviewType => PreviewType;
|
object IPreviewElement.PreviewType => PreviewType;
|
||||||
}
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace FileTime.App.Core.Models.Enums;
|
|
||||||
|
|
||||||
public enum ItemAttributeType
|
|
||||||
{
|
|
||||||
File,
|
|
||||||
Element,
|
|
||||||
Container,
|
|
||||||
SizeContainer
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using DeclarativeProperty;
|
||||||
|
|
||||||
|
namespace FileTime.App.Core.Models.Traits;
|
||||||
|
|
||||||
|
public interface ISizeProvider
|
||||||
|
{
|
||||||
|
IDeclarativeProperty<long> Size { get; }
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
|
using DeclarativeProperty;
|
||||||
using FileTime.App.Core.ViewModels.ItemPreview;
|
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||||
|
|
||||||
namespace FileTime.App.Core.Services;
|
namespace FileTime.App.Core.Services;
|
||||||
|
|
||||||
public interface IItemPreviewService
|
public interface IItemPreviewService
|
||||||
{
|
{
|
||||||
IObservable<IItemPreviewViewModel?> ItemPreview { get; }
|
IDeclarativeProperty<IItemPreviewViewModel?> ItemPreview { get; }
|
||||||
}
|
}
|
||||||
@@ -13,6 +13,7 @@ public interface IAppState
|
|||||||
IDeclarativeProperty<ViewMode> ViewMode { get; }
|
IDeclarativeProperty<ViewMode> ViewMode { get; }
|
||||||
DeclarativeProperty<string?> RapidTravelText { get; }
|
DeclarativeProperty<string?> RapidTravelText { get; }
|
||||||
ITimelineViewModel TimelineViewModel { get; }
|
ITimelineViewModel TimelineViewModel { get; }
|
||||||
|
IDeclarativeProperty<string?> ContainerStatus { get; }
|
||||||
|
|
||||||
void AddTab(ITabViewModel tabViewModel);
|
void AddTab(ITabViewModel tabViewModel);
|
||||||
void RemoveTab(ITabViewModel tabViewModel);
|
void RemoveTab(ITabViewModel tabViewModel);
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
using FileTime.App.Core.Models.Enums;
|
|
||||||
using FileTime.Core.Models;
|
|
||||||
using InitableService;
|
|
||||||
|
|
||||||
namespace FileTime.App.Core.ViewModels;
|
|
||||||
|
|
||||||
public interface IContainerSizeContainerViewModel : IItemViewModel, IInitable<IContainer, ITabViewModel, ItemViewModelType>
|
|
||||||
{
|
|
||||||
long Size { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
using FileTime.App.Core.Models.Enums;
|
using FileTime.App.Core.Models.Enums;
|
||||||
|
using FileTime.App.Core.Models.Traits;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
using InitableService;
|
using InitableService;
|
||||||
|
|
||||||
namespace FileTime.App.Core.ViewModels;
|
namespace FileTime.App.Core.ViewModels;
|
||||||
|
|
||||||
public interface IElementViewModel : IItemViewModel, IInitable<IElement, ITabViewModel, ItemViewModelType>
|
public interface IElementViewModel :
|
||||||
|
IItemViewModel,
|
||||||
|
ISizeProvider,
|
||||||
|
IInitable<IElement, ITabViewModel, ItemViewModelType>
|
||||||
{
|
{
|
||||||
IElement? Element { get; }
|
IElement? Element { get; }
|
||||||
long? Size { get; set; }
|
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ public interface IItemViewModel : IInitable<IItem, ITabViewModel, ItemViewModelT
|
|||||||
IDeclarativeProperty<bool> IsSelected { get; set; }
|
IDeclarativeProperty<bool> IsSelected { get; set; }
|
||||||
IDeclarativeProperty<bool>? IsMarked { get; set; }
|
IDeclarativeProperty<bool>? IsMarked { get; set; }
|
||||||
IDeclarativeProperty<bool> IsAlternative { get; }
|
IDeclarativeProperty<bool> IsAlternative { get; }
|
||||||
IObservable<ItemViewMode> ViewMode { get; set; }
|
IDeclarativeProperty<ItemViewMode> ViewMode { get; set; }
|
||||||
DateTime? CreatedAt { get; set; }
|
DateTime? CreatedAt { get; set; }
|
||||||
string? Attributes { get; set; }
|
string? Attributes { get; set; }
|
||||||
bool EqualsTo(IItemViewModel? itemViewModel);
|
bool EqualsTo(IItemViewModel? itemViewModel);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
|
using DeclarativeProperty;
|
||||||
using FileTime.App.Core.ViewModels;
|
using FileTime.App.Core.ViewModels;
|
||||||
using FileTime.App.Core.ViewModels.ItemPreview;
|
using FileTime.App.Core.ViewModels.ItemPreview;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
@@ -10,7 +11,7 @@ public class ItemPreviewService : IItemPreviewService
|
|||||||
{
|
{
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly IEnumerable<IItemPreviewProvider> _itemPreviewProviders;
|
private readonly IEnumerable<IItemPreviewProvider> _itemPreviewProviders;
|
||||||
public IObservable<IItemPreviewViewModel?> ItemPreview { get; }
|
public IDeclarativeProperty<IItemPreviewViewModel?> ItemPreview { get; }
|
||||||
|
|
||||||
public ItemPreviewService(
|
public ItemPreviewService(
|
||||||
IAppState appState,
|
IAppState appState,
|
||||||
@@ -21,18 +22,15 @@ public class ItemPreviewService : IItemPreviewService
|
|||||||
_itemPreviewProviders = itemPreviewProviders;
|
_itemPreviewProviders = itemPreviewProviders;
|
||||||
ItemPreview = appState
|
ItemPreview = appState
|
||||||
.SelectedTab
|
.SelectedTab
|
||||||
.Select(t =>
|
.Map(t => t.CurrentSelectedItem)
|
||||||
t?.CurrentSelectedItem.Throttle(TimeSpan.FromMilliseconds(250))
|
|
||||||
?? Observable.Return<IItemViewModel?>(null))
|
|
||||||
.Switch()
|
.Switch()
|
||||||
.Select(item =>
|
.Debounce(TimeSpan.FromMilliseconds(250))
|
||||||
|
.Map(async (item, _) =>
|
||||||
item == null
|
item == null
|
||||||
? Observable.Return<IItemPreviewViewModel?>(null)
|
? null
|
||||||
: Observable.FromAsync(async () => await Map(item))
|
: await Map(item)
|
||||||
)
|
)
|
||||||
.Switch()
|
.DistinctUntilChanged();
|
||||||
.Publish(null)
|
|
||||||
.RefCount();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IItemPreviewViewModel?> Map(IItemViewModel itemViewModel)
|
private async Task<IItemPreviewViewModel?> Map(IItemViewModel itemViewModel)
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ public class TabPersistenceService : ITabPersistenceService
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (tab.Path == null) continue;
|
if (tab.Path == null) continue;
|
||||||
|
if (_contentProvidersNotToRestore.Any(p => tab.Path.StartsWith(p))) continue;
|
||||||
|
|
||||||
IContainer? container = null;
|
IContainer? container = null;
|
||||||
var path = FullName.CreateSafe(tab.Path);
|
var path = FullName.CreateSafe(tab.Path);
|
||||||
|
|||||||
@@ -332,7 +332,11 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
}
|
}
|
||||||
else if (tabViewModel == null)
|
else if (tabViewModel == null)
|
||||||
{
|
{
|
||||||
var tab = await _serviceProvider.GetAsyncInitableResolver<IContainer>(_currentLocation?.Value ?? _localContentProvider)
|
var newLocation = _currentLocation?.Value?.FullName is { } fullName
|
||||||
|
? (IContainer) await _timelessContentProvider.GetItemByFullNameAsync(fullName, PointInTime.Present)
|
||||||
|
: _localContentProvider;
|
||||||
|
|
||||||
|
var tab = await _serviceProvider.GetAsyncInitableResolver<IContainer>(newLocation)
|
||||||
.GetRequiredServiceAsync<ITab>();
|
.GetRequiredServiceAsync<ITab>();
|
||||||
var newTabViewModel = _serviceProvider.GetInitableResolver(tab, number).GetRequiredService<ITabViewModel>();
|
var newTabViewModel = _serviceProvider.GetInitableResolver(tab, number).GetRequiredService<ITabViewModel>();
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
|
|||||||
if (_currentLocation?.Value is null) return;
|
if (_currentLocation?.Value is null) return;
|
||||||
|
|
||||||
var searchTask = _containerScanSnapshotProvider.StartSizeScan(_currentLocation.Value);
|
var searchTask = _containerScanSnapshotProvider.StartSizeScan(_currentLocation.Value);
|
||||||
var openContainerCommand = new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, searchTask.SizeContainer));
|
var openContainerCommand = new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, searchTask.SizeSizeScanContainer));
|
||||||
await _userCommandHandlerService.HandleCommandAsync(openContainerCommand);
|
await _userCommandHandlerService.HandleCommandAsync(openContainerCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ public static class Startup
|
|||||||
serviceCollection.TryAddTransient<IContainerViewModel, ContainerViewModel>();
|
serviceCollection.TryAddTransient<IContainerViewModel, ContainerViewModel>();
|
||||||
serviceCollection.TryAddTransient<IElementViewModel, ElementViewModel>();
|
serviceCollection.TryAddTransient<IElementViewModel, ElementViewModel>();
|
||||||
serviceCollection.TryAddTransient<IFileViewModel, FileViewModel>();
|
serviceCollection.TryAddTransient<IFileViewModel, FileViewModel>();
|
||||||
serviceCollection.TryAddTransient<IContainerSizeContainerViewModel, ContainerSizeContainerViewModel>();
|
|
||||||
serviceCollection.TryAddTransient<IItemNameConverterService, ItemNameConverterService>();
|
serviceCollection.TryAddTransient<IItemNameConverterService, ItemNameConverterService>();
|
||||||
serviceCollection.TryAddTransient<ElementPreviewViewModel>();
|
serviceCollection.TryAddTransient<ElementPreviewViewModel>();
|
||||||
serviceCollection.TryAddSingleton<IUserCommandHandlerService, UserCommandHandlerService>();
|
serviceCollection.TryAddSingleton<IUserCommandHandlerService, UserCommandHandlerService>();
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ using DeclarativeProperty;
|
|||||||
using DynamicData;
|
using DynamicData;
|
||||||
using FileTime.App.Core.Models.Enums;
|
using FileTime.App.Core.Models.Enums;
|
||||||
using FileTime.App.Core.ViewModels.Timeline;
|
using FileTime.App.Core.ViewModels.Timeline;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Core.Models.ContainerTraits;
|
||||||
using MvvmGen;
|
using MvvmGen;
|
||||||
using MoreLinq;
|
using MoreLinq;
|
||||||
|
|
||||||
@@ -27,6 +29,8 @@ public abstract partial class AppStateBase : IAppState
|
|||||||
public IDeclarativeProperty<ITabViewModel?> SelectedTab { get; private set; }
|
public IDeclarativeProperty<ITabViewModel?> SelectedTab { get; private set; }
|
||||||
public DeclarativeProperty<string?> RapidTravelText { get; private set; }
|
public DeclarativeProperty<string?> RapidTravelText { get; private set; }
|
||||||
|
|
||||||
|
public IDeclarativeProperty<string?> ContainerStatus { get; private set; }
|
||||||
|
|
||||||
partial void OnInitialize()
|
partial void OnInitialize()
|
||||||
{
|
{
|
||||||
RapidTravelText = new("");
|
RapidTravelText = new("");
|
||||||
@@ -40,6 +44,12 @@ public abstract partial class AppStateBase : IAppState
|
|||||||
);
|
);
|
||||||
|
|
||||||
Tabs = new ReadOnlyObservableCollection<ITabViewModel>(_tabs);
|
Tabs = new ReadOnlyObservableCollection<ITabViewModel>(_tabs);
|
||||||
|
|
||||||
|
ContainerStatus = SelectedTab
|
||||||
|
.Map(t => t?.CurrentLocation)
|
||||||
|
.Switch()
|
||||||
|
.Map(c => c is IStatusProviderContainer statusProvider ? statusProvider.Status : null)
|
||||||
|
.Switch();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddTab(ITabViewModel tabViewModel)
|
public void AddTab(ITabViewModel tabViewModel)
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
using FileTime.App.Core.Models.Enums;
|
|
||||||
using FileTime.App.Core.Services;
|
|
||||||
using FileTime.Core.Models;
|
|
||||||
using MvvmGen;
|
|
||||||
|
|
||||||
namespace FileTime.App.Core.ViewModels;
|
|
||||||
|
|
||||||
[ViewModel(GenerateConstructor = false)]
|
|
||||||
public partial class ContainerSizeContainerViewModel : ItemViewModel, IContainerSizeContainerViewModel
|
|
||||||
{
|
|
||||||
[Property]
|
|
||||||
private long _size;
|
|
||||||
|
|
||||||
public ContainerSizeContainerViewModel(IItemNameConverterService itemNameConverterService, IAppState appState) : base(itemNameConverterService, appState)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Init(IContainer item, ITabViewModel parentTab, ItemViewModelType itemViewModelType)
|
|
||||||
=> Init((IItem)item, parentTab, itemViewModelType);
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using DeclarativeProperty;
|
||||||
using FileTime.App.Core.Models.Enums;
|
using FileTime.App.Core.Models.Enums;
|
||||||
using FileTime.App.Core.Services;
|
using FileTime.App.Core.Services;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
@@ -10,13 +11,12 @@ public partial class ElementViewModel : ItemViewModel, IElementViewModel
|
|||||||
{
|
{
|
||||||
public IElement? Element => BaseItem as Element;
|
public IElement? Element => BaseItem as Element;
|
||||||
|
|
||||||
[Property]
|
|
||||||
private long? _size;
|
|
||||||
|
|
||||||
public ElementViewModel(IItemNameConverterService itemNameConverterService, IAppState appState) : base(itemNameConverterService, appState)
|
public ElementViewModel(IItemNameConverterService itemNameConverterService, IAppState appState) : base(itemNameConverterService, appState)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Init(IElement item, ITabViewModel parentTab, ItemViewModelType itemViewModelType)
|
public void Init(IElement item, ITabViewModel parentTab, ItemViewModelType itemViewModelType)
|
||||||
=> Init((IItem)item, parentTab, itemViewModelType);
|
=> Init((IItem)item, parentTab, itemViewModelType);
|
||||||
|
|
||||||
|
public IDeclarativeProperty<long> Size { get; protected set; } = new DeclarativeProperty<long>(0);
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using DeclarativeProperty;
|
||||||
using FileTime.App.Core.Models.Enums;
|
using FileTime.App.Core.Models.Enums;
|
||||||
using FileTime.App.Core.Services;
|
using FileTime.App.Core.Services;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
@@ -15,7 +16,7 @@ public partial class FileViewModel : ElementViewModel, IFileViewModel
|
|||||||
|
|
||||||
public void Init(IElement item, FileExtension fileExtension, ITabViewModel parentTab, ItemViewModelType itemViewModelType)
|
public void Init(IElement item, FileExtension fileExtension, ITabViewModel parentTab, ItemViewModelType itemViewModelType)
|
||||||
{
|
{
|
||||||
Init((IElement)item, parentTab, itemViewModelType);
|
Init(item, parentTab, itemViewModelType);
|
||||||
Size = fileExtension.Size;
|
Size = new DeclarativeProperty<long>(fileExtension.Size ?? 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using DeclarativeProperty;
|
using DeclarativeProperty;
|
||||||
@@ -27,7 +28,7 @@ public abstract partial class ItemViewModel : IItemViewModel
|
|||||||
|
|
||||||
[Property] private IDeclarativeProperty<bool>? _isMarked;
|
[Property] private IDeclarativeProperty<bool>? _isMarked;
|
||||||
|
|
||||||
[Property] private IObservable<ItemViewMode> _viewMode;
|
[Property] private IDeclarativeProperty<ItemViewMode> _viewMode;
|
||||||
|
|
||||||
[Property] private DateTime? _createdAt;
|
[Property] private DateTime? _createdAt;
|
||||||
|
|
||||||
@@ -75,15 +76,23 @@ public abstract partial class ItemViewModel : IItemViewModel
|
|||||||
? parentTab.CurrentSelectedItem.Map(EqualsTo)
|
? parentTab.CurrentSelectedItem.Map(EqualsTo)
|
||||||
: new DeclarativeProperty<bool>(IsInDeepestPath());
|
: new DeclarativeProperty<bool>(IsInDeepestPath());
|
||||||
|
|
||||||
IsAlternative = sourceCollection.Map(c => c?.Index().FirstOrDefault(i => EqualsTo(i.Value)).Key % 2 == 0);
|
IsAlternative = sourceCollection
|
||||||
|
.Debounce(TimeSpan.FromMilliseconds(100))
|
||||||
|
.Map(c =>
|
||||||
|
c?.Index().FirstOrDefault(i => EqualsTo(i.Value)).Key % 2 == 1
|
||||||
|
);
|
||||||
|
|
||||||
ViewMode = Observable.CombineLatest(IsMarked, IsSelected, IsAlternative, GenerateViewMode).Throttle(TimeSpan.FromMilliseconds(10));
|
ViewMode = DeclarativePropertyHelpers
|
||||||
|
.CombineLatest(IsMarked, IsSelected, IsAlternative, GenerateViewMode)
|
||||||
|
.DistinctUntilChanged()
|
||||||
|
.Debounce(TimeSpan.FromMilliseconds(100));
|
||||||
Attributes = item.Attributes;
|
Attributes = item.Attributes;
|
||||||
CreatedAt = item.CreatedAt;
|
CreatedAt = item.CreatedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ItemViewMode GenerateViewMode(bool isMarked, bool isSelected, bool isAlternative)
|
private Task<ItemViewMode> GenerateViewMode(bool isMarked, bool isSelected, bool isAlternative)
|
||||||
=> (isMarked, isSelected, isAlternative) switch
|
{
|
||||||
|
var result = (isMarked, isSelected, isAlternative) switch
|
||||||
{
|
{
|
||||||
(true, true, _) => ItemViewMode.MarkedSelected,
|
(true, true, _) => ItemViewMode.MarkedSelected,
|
||||||
(true, false, true) => ItemViewMode.MarkedAlternative,
|
(true, false, true) => ItemViewMode.MarkedAlternative,
|
||||||
@@ -93,11 +102,12 @@ public abstract partial class ItemViewModel : IItemViewModel
|
|||||||
_ => ItemViewMode.Default
|
_ => ItemViewMode.Default
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return Task.FromResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public bool EqualsTo(IItemViewModel? itemViewModel)
|
public bool EqualsTo(IItemViewModel? itemViewModel)
|
||||||
{
|
=> BaseItem?.FullName?.Path is { } path && path == itemViewModel?.BaseItem?.FullName?.Path;
|
||||||
return BaseItem?.FullName?.Path is string path && path == itemViewModel?.BaseItem?.FullName?.Path;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsInDeepestPath()
|
private bool IsInDeepestPath()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ public partial class TabViewModel : ITabViewModel
|
|||||||
.Selecting(i => MapItem(i))
|
.Selecting(i => MapItem(i))
|
||||||
.Ordering(i => i.Type)
|
.Ordering(i => i.Type)
|
||||||
.ThenOrdering(i => i.Name)
|
.ThenOrdering(i => i.Name)
|
||||||
.Selecting(i => MapItemToViewModel(i, ItemViewModelType.SelectedChild));
|
.Selecting(i => MapItemToViewModel(i, ItemViewModelType.Parent));
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ public partial class FrequencyNavigationService : IFrequencyNavigationService, I
|
|||||||
{
|
{
|
||||||
return _containerScores
|
return _containerScores
|
||||||
.Where(c => c.Key.Contains(searchText, StringComparison.OrdinalIgnoreCase))
|
.Where(c => c.Key.Contains(searchText, StringComparison.OrdinalIgnoreCase))
|
||||||
.OrderBy(c => GetWeightedScore(c.Value.Score, c.Value.LastAccessed))
|
.OrderByDescending(c => GetWeightedScore(c.Value.Score, c.Value.LastAccessed))
|
||||||
.Select(c => c.Key)
|
.Select(c => c.Key)
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
namespace FileTime.Core.Models;
|
||||||
|
|
||||||
|
public class ContainerEscapeResult
|
||||||
|
{
|
||||||
|
public bool Handled { get; }
|
||||||
|
public IContainer? NavigateTo { get; }
|
||||||
|
|
||||||
|
public ContainerEscapeResult(bool handled)
|
||||||
|
{
|
||||||
|
Handled = handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContainerEscapeResult(IContainer navigateTo)
|
||||||
|
{
|
||||||
|
NavigateTo = navigateTo;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace FileTime.Core.Models.ContainerTraits;
|
||||||
|
|
||||||
|
public interface IEscHandlerContainer
|
||||||
|
{
|
||||||
|
Task<ContainerEscapeResult> HandleEsc();
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using DeclarativeProperty;
|
||||||
|
|
||||||
|
namespace FileTime.Core.Models.ContainerTraits;
|
||||||
|
|
||||||
|
public interface IStatusProviderContainer
|
||||||
|
{
|
||||||
|
IDeclarativeProperty<string> Status { get; }
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ public class ReadOnlyExtensionCollection : IEnumerable<object>
|
|||||||
{
|
{
|
||||||
private readonly ExtensionCollection _collection;
|
private readonly ExtensionCollection _collection;
|
||||||
|
|
||||||
|
public ReadOnlyExtensionCollection()
|
||||||
|
{
|
||||||
|
_collection = new ExtensionCollection();
|
||||||
|
}
|
||||||
|
|
||||||
public ReadOnlyExtensionCollection(ExtensionCollection collection)
|
public ReadOnlyExtensionCollection(ExtensionCollection collection)
|
||||||
{
|
{
|
||||||
_collection = collection;
|
_collection = collection;
|
||||||
|
|||||||
@@ -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.ContentAccess;
|
using FileTime.Core.ContentAccess;
|
||||||
using FileTime.Core.Enums;
|
using FileTime.Core.Enums;
|
||||||
using FileTime.Core.Timeline;
|
using FileTime.Core.Timeline;
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
|
||||||
|
namespace FileTime.GuiApp.Converters;
|
||||||
|
|
||||||
|
public class DebugConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) => value;
|
||||||
|
|
||||||
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) => value;
|
||||||
|
}
|
||||||
@@ -1,46 +1,29 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Avalonia.Data.Converters;
|
using Avalonia.Data.Converters;
|
||||||
|
using ByteSizeLib;
|
||||||
|
|
||||||
namespace FileTime.GuiApp.Converters;
|
namespace FileTime.GuiApp.Converters;
|
||||||
|
|
||||||
public class FormatSizeConverter : IValueConverter
|
public class FormatSizeConverter : IValueConverter
|
||||||
{
|
{
|
||||||
private const long OneKiloByte = 1024;
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) =>
|
||||||
private const long OneMegaByte = OneKiloByte * 1024;
|
(value, int.TryParse(parameter?.ToString(), out var prec)) switch
|
||||||
private const long OneGigaByte = OneMegaByte * 1024;
|
|
||||||
private const long OneTerraByte = OneGigaByte * 1024;
|
|
||||||
|
|
||||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
||||||
{
|
|
||||||
return (value, int.TryParse(parameter?.ToString(), out var prec)) switch
|
|
||||||
{
|
{
|
||||||
(long size, true) => ToSizeString(size, prec),
|
(long size, true) => ToSizeString(size, prec),
|
||||||
(long size, false) => ToSizeString(size),
|
(long size, false) => ToSizeString(size),
|
||||||
(null, _) => "...",
|
(null, _) => "...",
|
||||||
_ => value
|
_ => value
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ToSizeString(long fileSize, int precision = 1)
|
public static string ToSizeString(long fileSize, int? precision = null)
|
||||||
{
|
{
|
||||||
var fileSizeD = (decimal)fileSize;
|
var size = new ByteSize(fileSize);
|
||||||
var (size, suffix) = fileSize switch
|
return precision == null? size.ToString()
|
||||||
{
|
: size.ToString("0." + new string('#', precision.Value));
|
||||||
> OneTerraByte => (fileSizeD / OneTerraByte, "T"),
|
|
||||||
> OneGigaByte => (fileSizeD / OneGigaByte, "G"),
|
|
||||||
> OneMegaByte => (fileSizeD / OneMegaByte, "M"),
|
|
||||||
> OneKiloByte => (fileSizeD / OneKiloByte, "K"),
|
|
||||||
_ => (fileSizeD, "B")
|
|
||||||
};
|
|
||||||
|
|
||||||
var result = string.Format("{0:N" + precision + "}", size).Replace(',', '.');
|
|
||||||
|
|
||||||
if (result.Contains('.')) result = result.TrimEnd('0').TrimEnd('.');
|
|
||||||
return result + " " + suffix;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
|
||||||
|
namespace FileTime.GuiApp.Converters;
|
||||||
|
|
||||||
|
public class IsTypeConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public bool Invert { get; set; }
|
||||||
|
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (parameter is Type parameterType)
|
||||||
|
{
|
||||||
|
var result = parameterType.IsInstanceOfType(value);
|
||||||
|
|
||||||
|
if(Invert) result = !result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
=> throw new NotImplementedException();
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
using FileTime.App.ContainerSizeScanner;
|
||||||
|
using FileTime.GuiApp.Helper;
|
||||||
|
|
||||||
|
namespace FileTime.GuiApp.Converters;
|
||||||
|
|
||||||
|
public class ItemSizeToBrushConverter : IMultiValueConverter
|
||||||
|
{
|
||||||
|
public double HueDiff { get; set; }
|
||||||
|
public double Lightness { get; set; } = 0.75;
|
||||||
|
|
||||||
|
public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (values is [ISizePreviewItem previewItem, ContainerPreview sizeContainerViewModel])
|
||||||
|
{
|
||||||
|
var items = sizeContainerViewModel.TopItems;
|
||||||
|
var i = 0;
|
||||||
|
for (; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
if (items[i].Name == previewItem.Name) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hue = (360d * i / (items.Count < 1 ? 1 : items.Count)) + HueDiff;
|
||||||
|
if (hue > 360) hue -= 360;
|
||||||
|
if (hue < 0) hue += 360;
|
||||||
|
|
||||||
|
var (r, g, b) = ColorHelper.HlsToRgb(hue, Lightness, 1);
|
||||||
|
|
||||||
|
var task = Dispatcher.UIThread.InvokeAsync(() => new SolidColorBrush(Color.FromRgb(r, g, b)));
|
||||||
|
task.Wait();
|
||||||
|
return task.Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte Normalize(byte b) => (byte) (255 - (255 - b) / 2);
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using FileTime.App.ContainerSizeScanner;
|
||||||
|
|
||||||
|
namespace FileTime.GuiApp.Converters;
|
||||||
|
|
||||||
|
public class ItemSizeToSizeConverter : IMultiValueConverter
|
||||||
|
{
|
||||||
|
public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (values is [ISizePreviewItem previewItem, ContainerPreview sizeContainerViewModel, double width and > 0])
|
||||||
|
{
|
||||||
|
var cumulativeSize = sizeContainerViewModel.TopItems.Select(i => i.Size.Value).Sum();
|
||||||
|
return width * previewItem.Size.Value / cumulativeSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
using Avalonia.Data.Converters;
|
|
||||||
using FileTime.App.Core.Models.Enums;
|
|
||||||
using FileTime.App.Core.ViewModels;
|
|
||||||
|
|
||||||
namespace FileTime.GuiApp.Converters;
|
|
||||||
|
|
||||||
public class ItemViewModelIsAttributeTypeConverter : IValueConverter
|
|
||||||
{
|
|
||||||
public bool Invert { get; set; }
|
|
||||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
||||||
{
|
|
||||||
var attributeType = GetAttributeType(value);
|
|
||||||
if (parameter == null) return attributeType;
|
|
||||||
var result = parameter is ItemAttributeType targetAttribute && attributeType == targetAttribute;
|
|
||||||
if (Invert && parameter is ItemAttributeType) result = !result;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ItemAttributeType? GetAttributeType(object? value)
|
|
||||||
{
|
|
||||||
return value switch
|
|
||||||
{
|
|
||||||
IFileViewModel => ItemAttributeType.File,
|
|
||||||
IContainerSizeContainerViewModel => ItemAttributeType.SizeContainer,
|
|
||||||
IElementViewModel => ItemAttributeType.Element,
|
|
||||||
IContainerViewModel => ItemAttributeType.Container,
|
|
||||||
_ => null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
43
src/GuiApp/Avalonia/FileTime.GuiApp/Helper/ColorHelper.cs
Normal file
43
src/GuiApp/Avalonia/FileTime.GuiApp/Helper/ColorHelper.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
namespace FileTime.GuiApp.Helper;
|
||||||
|
|
||||||
|
public static class ColorHelper
|
||||||
|
{
|
||||||
|
// Convert an HLS value into an RGB value.
|
||||||
|
public static (byte r, byte g, byte b) HlsToRgb(double h, double l, double s)
|
||||||
|
{
|
||||||
|
double p2;
|
||||||
|
if (l <= 0.5) p2 = l * (1 + s);
|
||||||
|
else p2 = l + s - l * s;
|
||||||
|
|
||||||
|
var p1 = 2 * l - p2;
|
||||||
|
double doubleR, doubleG, doubleB;
|
||||||
|
if (s == 0)
|
||||||
|
{
|
||||||
|
doubleR = l;
|
||||||
|
doubleG = l;
|
||||||
|
doubleB = l;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
doubleR = QqhToRgb(p1, p2, h + 120);
|
||||||
|
doubleG = QqhToRgb(p1, p2, h);
|
||||||
|
doubleB = QqhToRgb(p1, p2, h - 120);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert RGB to the 0 to 255 range.
|
||||||
|
return ((byte) (doubleR * 255.0),
|
||||||
|
(byte) (doubleG * 255.0),
|
||||||
|
(byte) (doubleB * 255.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double QqhToRgb(double q1, double q2, double hue)
|
||||||
|
{
|
||||||
|
if (hue > 360) hue -= 360;
|
||||||
|
else if (hue < 0) hue += 360;
|
||||||
|
|
||||||
|
if (hue < 60) return q1 + (q2 - q1) * hue / 60;
|
||||||
|
if (hue < 180) return q2;
|
||||||
|
if (hue < 240) return q1 + (q2 - q1) * (240 - hue) / 60;
|
||||||
|
return q1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,18 +26,13 @@
|
|||||||
SelectedBrush="{StaticResource SelectedItemBackgroundBrush}"
|
SelectedBrush="{StaticResource SelectedItemBackgroundBrush}"
|
||||||
x:Key="ItemViewModeToBackgroundConverter" />
|
x:Key="ItemViewModeToBackgroundConverter" />
|
||||||
<converters:NamePartShrinkerConverter x:Key="NamePartShrinkerConverter" />
|
<converters:NamePartShrinkerConverter x:Key="NamePartShrinkerConverter" />
|
||||||
<converters:ItemViewModelIsAttributeTypeConverter x:Key="ItemViewModelIsAttributeTypeConverter" />
|
|
||||||
<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}"
|
<converters:CompareConverter ComparisonCondition="{x:Static converters:ComparisonCondition.NotEqual}" x:Key="NotEqualsConverter" />
|
||||||
x:Key="NotEqualsConverter" />
|
<converters:CompareConverter ComparisonCondition="{x:Static converters:ComparisonCondition.GreaterThan}" x:Key="GreaterThanConverter" />
|
||||||
<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" />
|
||||||
@@ -47,6 +42,16 @@
|
|||||||
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" />
|
||||||
|
<converters:ItemSizeToSizeConverter x:Key="ItemSizeToSizeConverter" />
|
||||||
|
<converters:ItemSizeToBrushConverter x:Key="ItemSizeToBrushConverter" />
|
||||||
|
<converters:ItemSizeToBrushConverter
|
||||||
|
HueDiff="90"
|
||||||
|
Lightness="0.25"
|
||||||
|
x:Key="ItemSizeToForegroundBrushConverter" />
|
||||||
|
<converters:IsTypeConverter x:Key="IsTypeConverter" />
|
||||||
|
<converters:IsTypeConverter Invert="True" x:Key="IsNotTypeConverter" />
|
||||||
|
|
||||||
|
<converters:DebugConverter x:Key="DebugConverter" />
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
@@ -5,6 +5,7 @@ using FileTime.App.Core.UserCommand;
|
|||||||
using FileTime.App.Core.ViewModels;
|
using FileTime.App.Core.ViewModels;
|
||||||
using FileTime.Core.Extensions;
|
using FileTime.Core.Extensions;
|
||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Core.Models.ContainerTraits;
|
||||||
using FileTime.GuiApp.Configuration;
|
using FileTime.GuiApp.Configuration;
|
||||||
using FileTime.GuiApp.Extensions;
|
using FileTime.GuiApp.Extensions;
|
||||||
using FileTime.GuiApp.Models;
|
using FileTime.GuiApp.Models;
|
||||||
@@ -66,20 +67,23 @@ public class DefaultModeKeyInputHandler : IDefaultModeKeyInputHandler
|
|||||||
|
|
||||||
if (key == Key.Escape)
|
if (key == Key.Escape)
|
||||||
{
|
{
|
||||||
bool doGeneralReset = _appState.PreviousKeys.Count > 1 || _appState.IsAllShortcutVisible;
|
var doGeneralReset = _appState.PreviousKeys.Count > 1 || _appState.IsAllShortcutVisible;
|
||||||
|
|
||||||
if ((_openModals.Collection?.Count ?? 0) > 0)
|
if ((_openModals.Collection?.Count ?? 0) > 0)
|
||||||
{
|
{
|
||||||
_modalService.CloseModal(_openModals.Collection!.Last());
|
_modalService.CloseModal(_openModals.Collection!.Last());
|
||||||
}
|
}
|
||||||
/*else if (_currentLocation.Container.CanHandleEscape)
|
else if (_currentLocation is IEscHandlerContainer escHandler)
|
||||||
{
|
{
|
||||||
var escapeResult = await _currentLocation.Container.HandleEscape();
|
var escapeResult = await escHandler.HandleEsc();
|
||||||
if (escapeResult.NavigateTo != null)
|
if (escapeResult.NavigateTo != null)
|
||||||
{
|
{
|
||||||
setHandled(true);
|
setHandled(true);
|
||||||
_appState.PreviousKeys.Clear();
|
_appState.PreviousKeys.Clear();
|
||||||
await _appState.SelectedTab.OpenContainer(escapeResult.NavigateTo);
|
if (_appState.SelectedTab.Value?.Tab is { } selectedTab)
|
||||||
|
{
|
||||||
|
await selectedTab.SetCurrentLocation(escapeResult.NavigateTo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -92,7 +96,7 @@ public class DefaultModeKeyInputHandler : IDefaultModeKeyInputHandler
|
|||||||
doGeneralReset = true;
|
doGeneralReset = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
if (doGeneralReset)
|
if (doGeneralReset)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,52 +1,49 @@
|
|||||||
<UserControl
|
<UserControl
|
||||||
|
Background="{Binding ViewMode.Value, Converter={StaticResource ItemViewModeToBackgroundConverter}}"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
HorizontalContentAlignment="Stretch"
|
||||||
|
d:DesignHeight="450"
|
||||||
|
d:DesignWidth="800"
|
||||||
|
mc:Ignorable="d"
|
||||||
x:Class="FileTime.GuiApp.Views.ItemView"
|
x:Class="FileTime.GuiApp.Views.ItemView"
|
||||||
|
x:CompileBindings="True"
|
||||||
|
x:DataType="appcore:IItemViewModel"
|
||||||
|
x:Name="ItemRoot"
|
||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:appcore="using:FileTime.App.Core.ViewModels"
|
xmlns:appcore="using:FileTime.App.Core.ViewModels"
|
||||||
xmlns:appcoreenums="using:FileTime.App.Core.Models.Enums"
|
xmlns:appcoreenums="using:FileTime.App.Core.Models.Enums"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:guiappvm="using:FileTime.GuiApp.ViewModels"
|
xmlns:guiappvm="using:FileTime.GuiApp.ViewModels"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
x:Name="ItemRoot"
|
xmlns:traits="clr-namespace:FileTime.App.Core.Models.Traits;assembly=FileTime.App.Core.Abstraction"
|
||||||
HorizontalAlignment="Stretch"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
HorizontalContentAlignment="Stretch"
|
|
||||||
d:DesignHeight="450"
|
|
||||||
d:DesignWidth="800"
|
|
||||||
x:CompileBindings="True"
|
|
||||||
x:DataType="appcore:IItemViewModel"
|
|
||||||
Background="{Binding ViewMode^, Converter={StaticResource ItemViewModeToBackgroundConverter}}"
|
|
||||||
mc:Ignorable="d">
|
|
||||||
<Grid
|
<Grid
|
||||||
x:Name="RootGrid"
|
ColumnDefinitions="20,*,Auto"
|
||||||
Margin="3"
|
Margin="3"
|
||||||
ColumnDefinitions="20,*,Auto">
|
x:Name="RootGrid">
|
||||||
<Grid.Styles>
|
<Grid.Styles>
|
||||||
<Style Selector="TextBlock">
|
<Style Selector="TextBlock">
|
||||||
<Setter x:CompileBindings="False" Property="Foreground" Value="{Binding DataContext.ViewMode^, Converter={StaticResource ItemViewModeToForegroundConverter}, ElementName=ItemRoot}" />
|
<Setter Property="Foreground" Value="{Binding DataContext.ViewMode.Value, Converter={StaticResource ItemViewModeToForegroundConverter}, ElementName=ItemRoot}" x:CompileBindings="False" />
|
||||||
</Style>
|
</Style>
|
||||||
</Grid.Styles>
|
</Grid.Styles>
|
||||||
<Image
|
<Image
|
||||||
Width="18"
|
|
||||||
Height="18"
|
Height="18"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
|
Source="{Binding Converter={StaticResource ItemToImageConverter}}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Source="{Binding Converter={StaticResource ItemToImageConverter}}" />
|
Width="18" />
|
||||||
|
|
||||||
<ItemsControl
|
<ItemsControl
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="5,0,0,0"
|
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
|
Margin="5,0,0,0"
|
||||||
VerticalAlignment="Center">
|
VerticalAlignment="Center">
|
||||||
<ItemsControl.ItemsSource>
|
<ItemsControl.ItemsSource>
|
||||||
<MultiBinding Converter="{StaticResource NamePartShrinkerConverter}">
|
<MultiBinding Converter="{StaticResource NamePartShrinkerConverter}">
|
||||||
<MultiBinding.Bindings>
|
<MultiBinding.Bindings>
|
||||||
<Binding Path="DisplayName^" />
|
<Binding Path="DisplayName^" />
|
||||||
<Binding
|
<Binding ElementName="RootGrid" Path="Bounds.Width" />
|
||||||
ElementName="RootGrid"
|
<Binding ElementName="ItemRoot" Path="ShowAttributes" />
|
||||||
Path="Bounds.Width" />
|
|
||||||
<Binding
|
|
||||||
ElementName="ItemRoot"
|
|
||||||
Path="ShowAttributes" />
|
|
||||||
</MultiBinding.Bindings>
|
</MultiBinding.Bindings>
|
||||||
</MultiBinding>
|
</MultiBinding>
|
||||||
</ItemsControl.ItemsSource>
|
</ItemsControl.ItemsSource>
|
||||||
@@ -58,96 +55,55 @@
|
|||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate x:DataType="guiappvm:ItemNamePartViewModel">
|
<DataTemplate x:DataType="guiappvm:ItemNamePartViewModel">
|
||||||
<Grid>
|
<Grid>
|
||||||
<TextBlock
|
<TextBlock Text="{Binding Text}" TextDecorations="{Binding TextDecorations}" />
|
||||||
Text="{Binding Text}"
|
|
||||||
TextDecorations="{Binding TextDecorations}" />
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
|
|
||||||
<Grid
|
<Grid Grid.Column="2" IsVisible="{Binding ShowAttributes, ElementName=ItemRoot}">
|
||||||
Grid.Column="2"
|
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
|
||||||
IsVisible="{Binding ShowAttributes, ElementName=ItemRoot}">
|
<TextBlock
|
||||||
<Grid
|
Classes="SmallText"
|
||||||
HorizontalAlignment="Right"
|
IsVisible="{Binding Converter={StaticResource IsTypeConverter}, ConverterParameter={x:Type appcore:IElementViewModel}}"
|
||||||
x:DataType="appcore:IFileViewModel"
|
Text="{Binding BaseItem.DisplayName, Converter={StaticResource GetFileExtensionConverter}}"
|
||||||
ColumnDefinitions="50,50,90,40,45"
|
Width="50" />
|
||||||
IsVisible="{Binding Converter={StaticResource ItemViewModelIsAttributeTypeConverter}, ConverterParameter={x:Static appcoreenums:ItemAttributeType.File}}">
|
<Grid DataContext="{Binding BaseItem}" IsVisible="{Binding Converter={StaticResource IsTypeConverter}, ConverterParameter={x:Type traits:ISizeProvider}}">
|
||||||
|
<TextBlock
|
||||||
|
Classes="SmallText"
|
||||||
|
Text="{Binding Size.Value, Converter={StaticResource FormatSizeConverter}}"
|
||||||
|
TextAlignment="Right"
|
||||||
|
Width="60"
|
||||||
|
x:DataType="traits:ISizeProvider" />
|
||||||
|
</Grid>
|
||||||
|
<Grid IsVisible="{Binding BaseItem, Converter={StaticResource IsNotTypeConverter}, ConverterParameter={x:Type traits:ISizeProvider}}">
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Classes="SmallText"
|
||||||
|
IsVisible="{Binding Converter={StaticResource IsTypeConverter}, ConverterParameter={x:Type traits:ISizeProvider}}"
|
||||||
|
Text="{Binding Size.Value, Converter={StaticResource FormatSizeConverter}}"
|
||||||
|
TextAlignment="Right"
|
||||||
|
Width="60"
|
||||||
|
x:DataType="traits:ISizeProvider" />
|
||||||
|
</Grid>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Classes="SmallText"
|
Classes="SmallText"
|
||||||
Text="{Binding BaseItem.DisplayName, Converter={StaticResource GetFileExtensionConverter}}" />
|
Text="{Binding CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=yyyy-MM-dd}"
|
||||||
|
TextAlignment="Right"
|
||||||
|
Width="95" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Column="1"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Classes="SmallText"
|
Classes="SmallText"
|
||||||
Text="{Binding Size, Converter={StaticResource FormatSizeConverter}, ConverterParameter=0}" />
|
Text="{Binding CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=hh:mm}"
|
||||||
|
TextAlignment="Right"
|
||||||
|
Width="35" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Column="2"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Classes="SmallText"
|
Classes="SmallText"
|
||||||
Text="{Binding BaseItem.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=yyyy-MM-dd}" />
|
Text="{Binding BaseItem.Attributes}"
|
||||||
<TextBlock
|
TextAlignment="Right"
|
||||||
Grid.Column="3"
|
Width="45" />
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Classes="SmallText"
|
|
||||||
Text="{Binding BaseItem.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=hh:mm}" />
|
|
||||||
<TextBlock
|
|
||||||
Grid.Column="4"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Classes="SmallText"
|
|
||||||
Text="{Binding BaseItem.Attributes}" />
|
|
||||||
</Grid>
|
|
||||||
<Grid
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
x:DataType="appcore:IContainerViewModel"
|
|
||||||
ColumnDefinitions="90,40,45"
|
|
||||||
IsVisible="{Binding Converter={StaticResource ItemViewModelIsAttributeTypeConverter}, ConverterParameter={x:Static appcoreenums:ItemAttributeType.Container}}">
|
|
||||||
|
|
||||||
<TextBlock
|
</StackPanel>
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Classes="SmallText"
|
|
||||||
Text="{Binding CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=yyyy-MM-dd}" />
|
|
||||||
<TextBlock
|
|
||||||
Grid.Column="1"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Classes="SmallText"
|
|
||||||
Text="{Binding BaseItem.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=hh:mm}" />
|
|
||||||
<TextBlock
|
|
||||||
Grid.Column="2"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Classes="SmallText"
|
|
||||||
Text="{Binding BaseItem.Attributes}" />
|
|
||||||
</Grid>
|
|
||||||
<Grid
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
x:DataType="appcore:IContainerSizeContainerViewModel"
|
|
||||||
ColumnDefinitions="50,90,40,45"
|
|
||||||
IsVisible="{Binding Converter={StaticResource ItemViewModelIsAttributeTypeConverter}, ConverterParameter={x:Static appcoreenums:ItemAttributeType.SizeContainer}}">
|
|
||||||
|
|
||||||
<TextBlock
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Classes="SmallText"
|
|
||||||
Text="{Binding Size, Converter={StaticResource FormatSizeConverter}, ConverterParameter=0}" />
|
|
||||||
</Grid>
|
|
||||||
<Grid
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
x:DataType="appcore:IElementViewModel"
|
|
||||||
ColumnDefinitions="50,50"
|
|
||||||
IsVisible="{Binding Converter={StaticResource ItemViewModelIsAttributeTypeConverter}, ConverterParameter={x:Static appcoreenums:ItemAttributeType.Element}}">
|
|
||||||
|
|
||||||
<TextBlock
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Classes="SmallText"
|
|
||||||
Text="{Binding BaseItem.DisplayName, Converter={StaticResource GetFileExtensionConverter}}" />
|
|
||||||
<TextBlock
|
|
||||||
Grid.Column="1"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Classes="SmallText"
|
|
||||||
Text="{Binding Size, Converter={StaticResource FormatSizeConverter}, ConverterParameter=0}" />
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@@ -61,8 +61,8 @@
|
|||||||
|
|
||||||
<Grid ColumnDefinitions="*, Auto">
|
<Grid ColumnDefinitions="*, Auto">
|
||||||
<StackPanel Margin="20,10" Orientation="Horizontal">
|
<StackPanel Margin="20,10" Orientation="Horizontal">
|
||||||
<local:PathPresenter DataContext="{Binding AppState.SelectedTab^.CurrentLocation^.FullName.Path, Converter={StaticResource PathPreformatter}}" />
|
<local:PathPresenter DataContext="{Binding AppState.SelectedTab.Value.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.Value.CurrentSelectedItem.Value.DisplayNameText}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
@@ -153,7 +153,7 @@
|
|||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Classes="SmallText"
|
Classes="SmallText"
|
||||||
Text="{Binding Free, Converter={StaticResource FormatSizeConverter}, ConverterParameter=0}"
|
Text="{Binding Free, Converter={StaticResource FormatSizeConverter}}"
|
||||||
VerticalAlignment="Center" />
|
VerticalAlignment="Center" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -163,7 +163,7 @@
|
|||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Classes="SmallText"
|
Classes="SmallText"
|
||||||
Text="{Binding Size, Converter={StaticResource FormatSizeConverter}, ConverterParameter=0}"
|
Text="{Binding Size, Converter={StaticResource FormatSizeConverter}}"
|
||||||
VerticalAlignment="Center" />
|
VerticalAlignment="Center" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
@@ -437,7 +437,7 @@
|
|||||||
AutoScrollToSelectedItem="True"
|
AutoScrollToSelectedItem="True"
|
||||||
Classes="ContentListView"
|
Classes="ContentListView"
|
||||||
IsTabStop="True"
|
IsTabStop="True"
|
||||||
ItemsSource="{Binding AppState.SelectedTab^.ParentsChildren.Value}"
|
ItemsSource="{Binding AppState.SelectedTab.Value.ParentsChildren.Value}"
|
||||||
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
|
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
|
||||||
ScrollViewer.VerticalScrollBarVisibility="Visible">
|
ScrollViewer.VerticalScrollBarVisibility="Visible">
|
||||||
<ListBox.ItemTemplate>
|
<ListBox.ItemTemplate>
|
||||||
@@ -459,7 +459,7 @@
|
|||||||
Width="1" />
|
Width="1" />
|
||||||
|
|
||||||
<Grid Grid.Column="2" RowDefinitions="Auto,*">
|
<Grid Grid.Column="2" RowDefinitions="Auto,*">
|
||||||
<Grid IsVisible="{Binding AppState.SelectedTab^.CurrentLocation.Value.IsLoading^, FallbackValue=False}">
|
<Grid IsVisible="{Binding AppState.SelectedTab.Value.CurrentLocation.Value.IsLoading^, FallbackValue=False}">
|
||||||
<Image
|
<Image
|
||||||
Classes="LoadingAnimation"
|
Classes="LoadingAnimation"
|
||||||
Height="40"
|
Height="40"
|
||||||
@@ -471,10 +471,10 @@
|
|||||||
Classes="ContentListView"
|
Classes="ContentListView"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
IsTabStop="True"
|
IsTabStop="True"
|
||||||
ItemsSource="{Binding AppState.SelectedTab^.CurrentItems.Value}"
|
ItemsSource="{Binding AppState.SelectedTab.Value.CurrentItems.Value}"
|
||||||
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
|
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
|
||||||
ScrollViewer.VerticalScrollBarVisibility="Visible"
|
ScrollViewer.VerticalScrollBarVisibility="Visible"
|
||||||
SelectedItem="{Binding AppState.SelectedTab^.CurrentSelectedItem.Value}"
|
SelectedItem="{Binding AppState.SelectedTab.Value.CurrentSelectedItem.Value}"
|
||||||
x:Name="CurrentItems">
|
x:Name="CurrentItems">
|
||||||
<ListBox.ItemTemplate>
|
<ListBox.ItemTemplate>
|
||||||
<DataTemplate x:DataType="corevm:IItemViewModel">
|
<DataTemplate x:DataType="corevm:IItemViewModel">
|
||||||
@@ -488,7 +488,7 @@
|
|||||||
Foreground="{DynamicResource ErrorBrush}"
|
Foreground="{DynamicResource ErrorBrush}"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
IsVisible="{Binding AppState.SelectedTab^.CurrentItems.Value.Count, Converter={StaticResource EqualsConverter}, ConverterParameter=0}"
|
IsVisible="{Binding AppState.SelectedTab.Value.CurrentItems.Value.Count, Converter={StaticResource EqualsConverter}, ConverterParameter=0}"
|
||||||
Margin="10"
|
Margin="10"
|
||||||
x:Name="CurrentEmpty">
|
x:Name="CurrentEmpty">
|
||||||
Empty
|
Empty
|
||||||
@@ -504,11 +504,11 @@
|
|||||||
Width="1" />
|
Width="1" />
|
||||||
|
|
||||||
<Grid Grid.Column="4">
|
<Grid Grid.Column="4">
|
||||||
<Grid IsVisible="{Binding ItemPreviewService.ItemPreview^, Converter={x:Static ObjectConverters.IsNull}}">
|
<Grid IsVisible="{Binding ItemPreviewService.ItemPreview.Value, Converter={x:Static ObjectConverters.IsNull}}">
|
||||||
<Grid IsVisible="{Binding AppState.SelectedTab^.SelectedsChildren.Value, Converter={x:Static ObjectConverters.IsNotNull}, FallbackValue=False}">
|
<Grid IsVisible="{Binding AppState.SelectedTab.Value.SelectedsChildren.Value, Converter={x:Static ObjectConverters.IsNotNull}, FallbackValue=False}">
|
||||||
<Grid RowDefinitions="Auto, Auto, *">
|
<Grid RowDefinitions="Auto, Auto, *">
|
||||||
|
|
||||||
<Grid IsVisible="{Binding AppState.SelectedTab^.CurrentSelectedItemAsContainer.Value.Container.IsLoading^, FallbackValue=False}">
|
<Grid IsVisible="{Binding AppState.SelectedTab.Value.CurrentSelectedItemAsContainer.Value.Container.IsLoading^, FallbackValue=False}">
|
||||||
<Image
|
<Image
|
||||||
Classes="LoadingAnimation"
|
Classes="LoadingAnimation"
|
||||||
Height="40"
|
Height="40"
|
||||||
@@ -517,7 +517,7 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
<ItemsRepeater Grid.Row="1" ItemsSource="{Binding AppState.SelectedTab^.CurrentLocation.Value.Exceptions}">
|
<ItemsRepeater Grid.Row="1" ItemsSource="{Binding AppState.SelectedTab.Value.CurrentLocation.Value.Exceptions}">
|
||||||
<ItemsRepeater.ItemTemplate>
|
<ItemsRepeater.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -532,8 +532,8 @@
|
|||||||
AutoScrollToSelectedItem="True"
|
AutoScrollToSelectedItem="True"
|
||||||
Classes="ContentListView"
|
Classes="ContentListView"
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
IsVisible="{Binding AppState.SelectedTab^.SelectedsChildren.Value.Count, Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}"
|
IsVisible="{Binding AppState.SelectedTab.Value.SelectedsChildren.Value.Count, Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}"
|
||||||
ItemsSource="{Binding AppState.SelectedTab^.SelectedsChildren.Value}"
|
ItemsSource="{Binding AppState.SelectedTab.Value.SelectedsChildren.Value}"
|
||||||
x:Name="ChildItems">
|
x:Name="ChildItems">
|
||||||
<ListBox.ItemTemplate>
|
<ListBox.ItemTemplate>
|
||||||
<DataTemplate x:DataType="corevm:IItemViewModel">
|
<DataTemplate x:DataType="corevm:IItemViewModel">
|
||||||
@@ -547,14 +547,14 @@
|
|||||||
FontWeight="Bold"
|
FontWeight="Bold"
|
||||||
Foreground="{DynamicResource ErrorBrush}"
|
Foreground="{DynamicResource ErrorBrush}"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
IsVisible="{Binding AppState.SelectedTab^.SelectedsChildren.Value.Count, Converter={StaticResource EqualsConverter}, ConverterParameter=0}"
|
IsVisible="{Binding AppState.SelectedTab.Value.SelectedsChildren.Value.Count, Converter={StaticResource EqualsConverter}, ConverterParameter=0}"
|
||||||
Margin="10"
|
Margin="10"
|
||||||
x:Name="ChildEmpty">
|
x:Name="ChildEmpty">
|
||||||
Empty
|
Empty
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid IsVisible="{Binding AppState.SelectedTab^.SelectedsChildren.Value, Converter={x:Static ObjectConverters.IsNull}, ConverterParameter=0, FallbackValue=False}" RowDefinitions="Auto, Auto">
|
<Grid IsVisible="{Binding AppState.SelectedTab.Value.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"
|
||||||
@@ -562,7 +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" ItemsSource="{Binding AppState.SelectedTab^.CurrentSelectedItem.Value.BaseItem.Exceptions}">
|
<ItemsRepeater Grid.Row="1" ItemsSource="{Binding AppState.SelectedTab.Value.CurrentSelectedItem.Value.BaseItem.Exceptions}">
|
||||||
<ItemsRepeater.ItemTemplate>
|
<ItemsRepeater.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -574,109 +574,109 @@
|
|||||||
</ItemsRepeater>
|
</ItemsRepeater>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid DataContext="{Binding ItemPreviewService.ItemPreview^}" IsVisible="{Binding Converter={x:Static ObjectConverters.IsNotNull}}">
|
<Grid DataContext="{Binding ItemPreviewService.ItemPreview.Value}" IsVisible="{Binding FallbackValue=false, Converter={x:Static ObjectConverters.IsNotNull}}">
|
||||||
<Grid IsVisible="{Binding Name, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static itemPreview:ElementPreviewViewModel.PreviewName}}" x:DataType="itemPreview:ElementPreviewViewModel">
|
<Grid IsVisible="{Binding Name, FallbackValue=false, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static itemPreview:ElementPreviewViewModel.PreviewName}}">
|
||||||
<TextBlock
|
<Grid x:DataType="itemPreview:ElementPreviewViewModel">
|
||||||
HorizontalAlignment="Center"
|
|
||||||
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." />
|
|
||||||
<TextBlock
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
IsVisible="{Binding Mode, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static appCoreModels:ItemPreviewMode.Empty}, FallbackValue={x:Static appCoreModels:ItemPreviewMode.Unknown}}"
|
|
||||||
Text="Empty" />
|
|
||||||
<Grid IsVisible="{Binding Mode, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static appCoreModels:ItemPreviewMode.Text}, FallbackValue={x:Static appCoreModels:ItemPreviewMode.Unknown}}" RowDefinitions="*, Auto">
|
|
||||||
<ScrollViewer>
|
|
||||||
<TextBox IsReadOnly="True" Text="{Binding TextContent}" />
|
|
||||||
</ScrollViewer>
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="1"
|
HorizontalAlignment="Center"
|
||||||
Margin="5"
|
IsVisible="{Binding Mode, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static appCoreModels:ItemPreviewMode.Unknown}, FallbackValue={x:Static appCoreModels:ItemPreviewMode.Unknown}}"
|
||||||
Text="{Binding TextEncoding, StringFormat=Encoding: {0}}" />
|
Text="Don't know how to preview this item." />
|
||||||
|
<TextBlock
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
IsVisible="{Binding Mode, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static appCoreModels:ItemPreviewMode.Empty}, FallbackValue={x:Static appCoreModels:ItemPreviewMode.Unknown}}"
|
||||||
|
Text="Empty" />
|
||||||
|
<Grid IsVisible="{Binding Mode, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static appCoreModels:ItemPreviewMode.Text}, FallbackValue={x:Static appCoreModels:ItemPreviewMode.Unknown}}" RowDefinitions="*, Auto">
|
||||||
|
<ScrollViewer>
|
||||||
|
<TextBox IsReadOnly="True" Text="{Binding TextContent}" />
|
||||||
|
</ScrollViewer>
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="1"
|
||||||
|
Margin="5"
|
||||||
|
Text="{Binding TextEncoding, StringFormat=Encoding: {0}}" />
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid
|
<Grid IsVisible="{Binding Name, FallbackValue=false, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static sizePreview:ContainerPreview.PreviewName}}" x:Name="SizeContainerPreview">
|
||||||
IsVisible="{Binding Name, Converter={StaticResource EqualsConverter}, ConverterParameter={x:Static sizePreview:ContainerSizeContainerPreview.PreviewName}}"
|
<Grid RowDefinitions="Auto, Auto" x:DataType="sizePreview:ContainerPreview">
|
||||||
RowDefinitions="Auto, Auto"
|
<ItemsControl ItemsSource="{Binding TopItems}" Margin="0,0,0,30">
|
||||||
x:DataType="sizePreview:ContainerSizeContainerPreview">
|
<ItemsControl.ItemsPanel>
|
||||||
<ItemsControl Items="{Binding TopItems^}" Margin="0,0,0,30">
|
<ItemsPanelTemplate>
|
||||||
<ItemsControl.ItemsPanel>
|
<StackPanel Orientation="Horizontal" />
|
||||||
<ItemsPanelTemplate>
|
</ItemsPanelTemplate>
|
||||||
<StackPanel Orientation="Horizontal" />
|
</ItemsControl.ItemsPanel>
|
||||||
</ItemsPanelTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
</ItemsControl.ItemsPanel>
|
<DataTemplate>
|
||||||
<ItemsControl.ItemTemplate>
|
<Grid>
|
||||||
<DataTemplate>
|
<Grid.Width>
|
||||||
<Grid>
|
<MultiBinding Converter="{StaticResource ItemSizeToSizeConverter}">
|
||||||
<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>
|
<MultiBinding.Bindings>
|
||||||
<Binding />
|
<Binding />
|
||||||
<Binding ElementName="SizeContainerPreview" Path="DataContext.Items^" />
|
<Binding ElementName="SizeContainerPreview" Path="DataContext" />
|
||||||
|
<Binding ElementName="SizeContainerPreview" Path="Bounds.Width" />
|
||||||
</MultiBinding.Bindings>
|
</MultiBinding.Bindings>
|
||||||
</MultiBinding>
|
</MultiBinding>
|
||||||
</Rectangle.Fill>
|
</Grid.Width>
|
||||||
</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
|
<Rectangle HorizontalAlignment="Stretch">
|
||||||
Grid.Row="1"
|
<Rectangle.Fill>
|
||||||
Items="{Binding Items^}"
|
<MultiBinding Converter="{StaticResource ItemSizeToBrushConverter}">
|
||||||
x:CompileBindings="False">
|
<MultiBinding.Bindings>
|
||||||
<ItemsControl.ItemTemplate>
|
<Binding />
|
||||||
<DataTemplate>
|
<Binding ElementName="SizeContainerPreview" Path="DataContext" />
|
||||||
<Grid ColumnDefinitions="Auto,*" Margin="0,0,0,20">
|
</MultiBinding.Bindings>
|
||||||
<TextBlock Text="{Binding Name}" />
|
</MultiBinding>
|
||||||
<TextBlock
|
</Rectangle.Fill>
|
||||||
Grid.Column="1"
|
</Rectangle>
|
||||||
HorizontalAlignment="Right"
|
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Center">
|
||||||
Margin="0,0,20,0"
|
<TextBlock
|
||||||
Text="{Binding Size, Converter={StaticResource FormatSizeConverter}, ConverterParameter=0}" />
|
Classes="SmallText"
|
||||||
</Grid>
|
FontWeight="Bold"
|
||||||
</DataTemplate>
|
Margin="5,5,5,2"
|
||||||
</ItemsControl.ItemTemplate>
|
Text="{Binding Name}">
|
||||||
</ItemsControl>
|
<TextBlock.Foreground>
|
||||||
|
<MultiBinding Converter="{StaticResource ItemSizeToForegroundBrushConverter}">
|
||||||
|
<MultiBinding.Bindings>
|
||||||
|
<Binding />
|
||||||
|
<Binding ElementName="SizeContainerPreview" Path="DataContext" />
|
||||||
|
</MultiBinding.Bindings>
|
||||||
|
</MultiBinding>
|
||||||
|
</TextBlock.Foreground>
|
||||||
|
</TextBlock>
|
||||||
|
<TextBlock
|
||||||
|
Classes="SmallText"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Margin="5,2,5,5"
|
||||||
|
Text="{Binding Size.Value, Converter={StaticResource FormatSizeConverter}}">
|
||||||
|
<TextBlock.Foreground>
|
||||||
|
<MultiBinding Converter="{StaticResource ItemSizeToForegroundBrushConverter}">
|
||||||
|
<MultiBinding.Bindings>
|
||||||
|
<Binding />
|
||||||
|
<Binding ElementName="SizeContainerPreview" Path="DataContext" />
|
||||||
|
</MultiBinding.Bindings>
|
||||||
|
</MultiBinding>
|
||||||
|
</TextBlock.Foreground>
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
|
||||||
|
<ItemsControl Grid.Row="1" ItemsSource="{Binding TopItems}">
|
||||||
|
<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.Value, Converter={StaticResource FormatSizeConverter}}" />
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -684,7 +684,7 @@
|
|||||||
|
|
||||||
<ItemsRepeater
|
<ItemsRepeater
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
IsVisible="{Binding AppState.PopupTexts.Count, Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}"
|
IsVisible="{Binding AppState.PopupTexts.Count, Converter={StaticResource NotEqualsConverter}}"
|
||||||
ItemsSource="{Binding AppState.PopupTexts}"
|
ItemsSource="{Binding AppState.PopupTexts}"
|
||||||
Margin="0,0,0,20"
|
Margin="0,0,0,20"
|
||||||
VerticalAlignment="Top">
|
VerticalAlignment="Top">
|
||||||
@@ -718,57 +718,62 @@
|
|||||||
</ItemsRepeater>
|
</ItemsRepeater>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid Grid.Row="3">
|
<Grid Grid.Row="3" RowDefinitions="Auto, Auto">
|
||||||
<Grid IsVisible="{Binding AppState.ViewMode^, Converter={StaticResource EqualsConverter}, ConverterParameter=RapidTravel}" RowDefinitions="1,Auto">
|
<Grid>
|
||||||
|
<TextBlock IsVisible="{Binding AppState.ContainerStatus.Value, Converter={x:Static ObjectConverters.IsNotNull}}" Text="{Binding AppState.ContainerStatus.Value}" />
|
||||||
<Rectangle
|
|
||||||
Fill="{DynamicResource ContentSeparatorBrush}"
|
|
||||||
Height="1"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
Margin="10,0"
|
|
||||||
VerticalAlignment="Center" />
|
|
||||||
|
|
||||||
<StackPanel
|
|
||||||
Grid.Row="1"
|
|
||||||
Margin="30,10,10,10"
|
|
||||||
Orientation="Horizontal">
|
|
||||||
|
|
||||||
<TextBlock Margin="0,0,30,0" Text="Rapid travel mode" />
|
|
||||||
|
|
||||||
<TextBlock Text="Filter " />
|
|
||||||
|
|
||||||
<TextBlock Text="{Binding AppState.RapidTravelText.Value}" />
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid Grid.Row="1">
|
||||||
|
<Grid IsVisible="{Binding AppState.ViewMode^, Converter={StaticResource EqualsConverter}, ConverterParameter=RapidTravel}" RowDefinitions="1,Auto">
|
||||||
|
|
||||||
<Grid IsVisible="{Binding AppState.PossibleCommands.Count, Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}">
|
<Rectangle
|
||||||
<Grid.RowDefinitions>
|
Fill="{DynamicResource ContentSeparatorBrush}"
|
||||||
<RowDefinition Height="1" />
|
Height="1"
|
||||||
<RowDefinition Height="Auto" />
|
HorizontalAlignment="Stretch"
|
||||||
</Grid.RowDefinitions>
|
Margin="10,0"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
|
||||||
<Rectangle
|
<StackPanel
|
||||||
Fill="{DynamicResource ContentSeparatorBrush}"
|
Grid.Row="1"
|
||||||
Height="1"
|
Margin="30,10,10,10"
|
||||||
HorizontalAlignment="Stretch"
|
Orientation="Horizontal">
|
||||||
Margin="10,0"
|
|
||||||
VerticalAlignment="Center" />
|
|
||||||
|
|
||||||
<ItemsRepeater Grid.Row="1" ItemsSource="{Binding AppState.PossibleCommands}">
|
<TextBlock Margin="0,0,30,0" Text="Rapid travel mode" />
|
||||||
<ItemsRepeater.ItemTemplate>
|
|
||||||
<DataTemplate x:DataType="config:CommandBindingConfiguration">
|
|
||||||
<Grid>
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="200" />
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<TextBlock Text="{Binding KeysDisplayText}" />
|
<TextBlock Text="Filter " />
|
||||||
<TextBlock Grid.Column="1" Text="{Binding Command, Converter={StaticResource CommandToCommandNameConverter}}" />
|
|
||||||
</Grid>
|
<TextBlock Text="{Binding AppState.RapidTravelText.Value}" />
|
||||||
</DataTemplate>
|
</StackPanel>
|
||||||
</ItemsRepeater.ItemTemplate>
|
</Grid>
|
||||||
</ItemsRepeater>
|
|
||||||
|
<Grid IsVisible="{Binding AppState.PossibleCommands.Count, Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="1" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<Rectangle
|
||||||
|
Fill="{DynamicResource ContentSeparatorBrush}"
|
||||||
|
Height="1"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Margin="10,0"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
|
||||||
|
<ItemsRepeater Grid.Row="1" ItemsSource="{Binding AppState.PossibleCommands}">
|
||||||
|
<ItemsRepeater.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="config:CommandBindingConfiguration">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="200" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Text="{Binding KeysDisplayText}" />
|
||||||
|
<TextBlock Grid.Column="1" Text="{Binding Command, Converter={StaticResource CommandToCommandNameConverter}}" />
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsRepeater.ItemTemplate>
|
||||||
|
</ItemsRepeater>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -38,3 +38,51 @@ public sealed class CombineLatestProperty<T1, T2, TResult> : DeclarativeProperty
|
|||||||
await SetNewValueAsync(result, cancellationToken);
|
await SetNewValueAsync(result, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class CombineLatestProperty<T1, T2, T3, TResult> : DeclarativePropertyBase<TResult>
|
||||||
|
{
|
||||||
|
private readonly Func<T1?, T2?,T3?, Task<TResult>> _func;
|
||||||
|
private T1? _value1;
|
||||||
|
private T2? _value2;
|
||||||
|
private T3? _value3;
|
||||||
|
|
||||||
|
public CombineLatestProperty(
|
||||||
|
IDeclarativeProperty<T1> prop1,
|
||||||
|
IDeclarativeProperty<T2> prop2,
|
||||||
|
IDeclarativeProperty<T3> prop3,
|
||||||
|
Func<T1?, T2?, T3?,Task<TResult>> func,
|
||||||
|
Action<TResult?>? setValueHook = null) : base(setValueHook)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(prop1);
|
||||||
|
ArgumentNullException.ThrowIfNull(prop2);
|
||||||
|
ArgumentNullException.ThrowIfNull(prop3);
|
||||||
|
|
||||||
|
_func = func;
|
||||||
|
|
||||||
|
_value1 = prop1.Value is null ? default : prop1.Value;
|
||||||
|
_value2 = prop2.Value is null ? default : prop2.Value;
|
||||||
|
_value3 = prop3.Value is null ? default : prop3.Value;
|
||||||
|
|
||||||
|
prop1.Subscribe(async (value1, token) =>
|
||||||
|
{
|
||||||
|
_value1 = value1;
|
||||||
|
await UpdateAsync(token);
|
||||||
|
});
|
||||||
|
prop2.Subscribe(async (value2, token) =>
|
||||||
|
{
|
||||||
|
_value2 = value2;
|
||||||
|
await UpdateAsync(token);
|
||||||
|
});
|
||||||
|
prop3.Subscribe(async (value3, token) =>
|
||||||
|
{
|
||||||
|
_value3 = value3;
|
||||||
|
await UpdateAsync(token);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UpdateAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var result = await _func(_value1, _value2, _value3);
|
||||||
|
await SetNewValueAsync(result, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,14 @@ public static class DeclarativePropertyHelpers
|
|||||||
Func<T1, T2, Task<TResult>> func,
|
Func<T1, T2, Task<TResult>> func,
|
||||||
Action<TResult?>? setValueHook = null)
|
Action<TResult?>? setValueHook = null)
|
||||||
=> new(prop1, prop2, func, setValueHook);
|
=> new(prop1, prop2, func, setValueHook);
|
||||||
|
|
||||||
|
public static CombineLatestProperty<T1, T2, T3, TResult> CombineLatest<T1, T2, T3, TResult>(
|
||||||
|
IDeclarativeProperty<T1> prop1,
|
||||||
|
IDeclarativeProperty<T2> prop2,
|
||||||
|
IDeclarativeProperty<T3> prop3,
|
||||||
|
Func<T1, T2, T3, Task<TResult>> func,
|
||||||
|
Action<TResult?>? setValueHook = null)
|
||||||
|
=> new(prop1, prop2, prop3, func, setValueHook);
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class DeclarativeProperty<T> : DeclarativePropertyBase<T>
|
public sealed class DeclarativeProperty<T> : DeclarativePropertyBase<T>
|
||||||
|
|||||||
Reference in New Issue
Block a user