Optimalization, file name&extension
This commit is contained in:
@@ -18,6 +18,8 @@ namespace FileTime.Core.Components
|
|||||||
|
|
||||||
public int CurrentSelectedIndex { get; private set; }
|
public int CurrentSelectedIndex { get; private set; }
|
||||||
|
|
||||||
|
public bool AutoRefresh { get; set; }
|
||||||
|
|
||||||
public AsyncEventHandler CurrentLocationChanged = new();
|
public AsyncEventHandler CurrentLocationChanged = new();
|
||||||
public AsyncEventHandler CurrentSelectedItemChanged = new();
|
public AsyncEventHandler CurrentSelectedItemChanged = new();
|
||||||
|
|
||||||
@@ -54,16 +56,15 @@ namespace FileTime.Core.Components
|
|||||||
return Task.FromResult(_currentSelectedItem);
|
return Task.FromResult(_currentSelectedItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SetCurrentSelectedItem(IItem? value, bool secondary = false)
|
public async Task<bool> SetCurrentSelectedItem(IItem? value, bool secondary = false, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
if (_currentlySelecting) return;
|
if (_currentlySelecting) return false;
|
||||||
|
|
||||||
if (_currentSelectedItem != value)
|
if (_currentSelectedItem == value) return false;
|
||||||
{
|
|
||||||
IItem? itemToSelect = null;
|
IItem? itemToSelect = null;
|
||||||
if (value != null)
|
if (value != null)
|
||||||
{
|
{
|
||||||
itemToSelect = (await _currentLocation.GetItems())?.FirstOrDefault(i =>
|
itemToSelect = (await _currentLocation.GetItems(token))?.FirstOrDefault(i =>
|
||||||
i.FullName == null && value?.FullName == null
|
i.FullName == null && value?.FullName == null
|
||||||
? i.Name == value?.Name
|
? i.Name == value?.Name
|
||||||
: i.FullName == value?.FullName);
|
: i.FullName == value?.FullName);
|
||||||
@@ -73,20 +74,29 @@ namespace FileTime.Core.Components
|
|||||||
CancellationToken newToken;
|
CancellationToken newToken;
|
||||||
lock (_guardSetCurrentSelectedItemCTS)
|
lock (_guardSetCurrentSelectedItemCTS)
|
||||||
{
|
{
|
||||||
|
if (token.IsCancellationRequested) return false;
|
||||||
_setCurrentSelectedItemCTS?.Cancel();
|
_setCurrentSelectedItemCTS?.Cancel();
|
||||||
|
if (token.IsCancellationRequested)
|
||||||
|
{
|
||||||
_setCurrentSelectedItemCTS = new CancellationTokenSource();
|
_setCurrentSelectedItemCTS = new CancellationTokenSource();
|
||||||
newToken = _setCurrentSelectedItemCTS.Token;
|
newToken = _setCurrentSelectedItemCTS.Token;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_setCurrentSelectedItemCTS = new CancellationTokenSource();
|
||||||
|
newToken = CancellationTokenSource.CreateLinkedTokenSource(_setCurrentSelectedItemCTS.Token, token).Token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_currentSelectedItem = itemToSelect;
|
_currentSelectedItem = itemToSelect;
|
||||||
_lastPath = GetCommonPath(_lastPath, itemToSelect?.FullName);
|
_lastPath = GetCommonPath(_lastPath, itemToSelect?.FullName);
|
||||||
|
|
||||||
var newCurrentSelectedIndex = await GetItemIndex(itemToSelect, newToken);
|
var newCurrentSelectedIndex = await GetItemIndex(itemToSelect, CancellationToken.None);
|
||||||
if (newToken.IsCancellationRequested) return;
|
|
||||||
CurrentSelectedIndex = newCurrentSelectedIndex;
|
CurrentSelectedIndex = newCurrentSelectedIndex;
|
||||||
|
|
||||||
await CurrentSelectedItemChanged.InvokeAsync(this, AsyncEventArgs.Empty, newToken);
|
await CurrentSelectedItemChanged.InvokeAsync(this, AsyncEventArgs.Empty, newToken);
|
||||||
}
|
|
||||||
|
return !newToken.IsCancellationRequested;
|
||||||
}
|
}
|
||||||
public async Task<IItem?> GetItemByLastPath(IContainer? container = null)
|
public async Task<IItem?> GetItemByLastPath(IContainer? container = null)
|
||||||
{
|
{
|
||||||
@@ -94,7 +104,6 @@ namespace FileTime.Core.Components
|
|||||||
var containerFullName = container.FullName;
|
var containerFullName = container.FullName;
|
||||||
|
|
||||||
if (_lastPath == null
|
if (_lastPath == null
|
||||||
|| !container.IsLoaded
|
|
||||||
|| (containerFullName != null && !_lastPath.StartsWith(containerFullName))
|
|| (containerFullName != null && !_lastPath.StartsWith(containerFullName))
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@@ -154,53 +163,60 @@ namespace FileTime.Core.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SelectFirstItem()
|
public async Task SelectFirstItem(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
|
var currentLocationItems = (await (await GetCurrentLocation(token)).GetItems(token))!;
|
||||||
if (currentLocationItems.Count > 0)
|
if (currentLocationItems.Count > 0)
|
||||||
{
|
{
|
||||||
await SetCurrentSelectedItem(currentLocationItems[0]);
|
await SetCurrentSelectedItem(currentLocationItems[0], token: token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SelectLastItem()
|
public async Task SelectLastItem(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
|
var currentLocationItems = (await (await GetCurrentLocation(token)).GetItems(token))!;
|
||||||
if (currentLocationItems.Count > 0)
|
if (currentLocationItems.Count > 0)
|
||||||
{
|
{
|
||||||
await SetCurrentSelectedItem(currentLocationItems[currentLocationItems.Count - 1]);
|
await SetCurrentSelectedItem(currentLocationItems[currentLocationItems.Count - 1], token: token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SelectPreviousItem(int skip = 0)
|
public async Task SelectPreviousItem(int skip = 0, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
|
var currentLocationItems = (await (await GetCurrentLocation(token)).GetItems(token))!;
|
||||||
var possibleItemsToSelect = currentLocationItems.Take(CurrentSelectedIndex).Reverse().Skip(skip).ToList();
|
var possibleItemsToSelect = currentLocationItems.Take(CurrentSelectedIndex).Reverse().Skip(skip).ToList();
|
||||||
|
|
||||||
if (possibleItemsToSelect.Count == 0) possibleItemsToSelect = currentLocationItems.ToList();
|
if (possibleItemsToSelect.Count == 0) possibleItemsToSelect = currentLocationItems.ToList();
|
||||||
await SelectItem(possibleItemsToSelect);
|
await SelectItem(possibleItemsToSelect, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SelectNextItem(int skip = 0)
|
public async Task SelectNextItem(int skip = 0, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
|
var currentLocationItems = (await (await GetCurrentLocation(token)).GetItems(token))!;
|
||||||
var possibleItemsToSelect = currentLocationItems.Skip(CurrentSelectedIndex + 1 + skip).ToList();
|
var possibleItemsToSelect = currentLocationItems.Skip(CurrentSelectedIndex + 1 + skip).ToList();
|
||||||
|
|
||||||
if (possibleItemsToSelect.Count == 0) possibleItemsToSelect = currentLocationItems.Reverse().ToList();
|
if (possibleItemsToSelect.Count == 0) possibleItemsToSelect = currentLocationItems.Reverse().ToList();
|
||||||
await SelectItem(possibleItemsToSelect);
|
await SelectItem(possibleItemsToSelect, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SelectItem(IEnumerable<IItem> currentPossibleItems)
|
private async Task SelectItem(IEnumerable<IItem> currentPossibleItems, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
if (!currentPossibleItems.Any()) return;
|
if (!currentPossibleItems.Any()) return;
|
||||||
|
|
||||||
var currentLocation = await GetCurrentLocation();
|
if (token.IsCancellationRequested) return;
|
||||||
var currentLocationItems = (await currentLocation.GetItems())!;
|
var currentLocation = await GetCurrentLocation(token);
|
||||||
|
var currentLocationItems = (await currentLocation.GetItems(token))!;
|
||||||
|
|
||||||
if (await GetCurrentSelectedItem() != null)
|
if (await GetCurrentSelectedItem() != null)
|
||||||
{
|
{
|
||||||
|
if (token.IsCancellationRequested) return;
|
||||||
|
|
||||||
_currentlySelecting = true;
|
_currentlySelecting = true;
|
||||||
currentLocation?.RefreshAsync();
|
if (AutoRefresh && currentLocation != null)
|
||||||
|
{
|
||||||
|
await currentLocation.RefreshAsync(token);
|
||||||
|
if (token.IsCancellationRequested) return;
|
||||||
|
}
|
||||||
|
|
||||||
IItem? newSelectedItem = null;
|
IItem? newSelectedItem = null;
|
||||||
foreach (var item in currentPossibleItems)
|
foreach (var item in currentPossibleItems)
|
||||||
@@ -215,15 +231,15 @@ namespace FileTime.Core.Components
|
|||||||
|
|
||||||
if (newSelectedItem != null)
|
if (newSelectedItem != null)
|
||||||
{
|
{
|
||||||
newSelectedItem = (await (await GetCurrentLocation()).GetItems())?.FirstOrDefault(i => i.Name == newSelectedItem.Name);
|
newSelectedItem = (await (await GetCurrentLocation(token)).GetItems(token))?.FirstOrDefault(i => i.Name == newSelectedItem.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
_currentlySelecting = false;
|
_currentlySelecting = false;
|
||||||
await SetCurrentSelectedItem(newSelectedItem ?? (currentLocationItems.Count > 0 ? currentLocationItems[0] : null));
|
await SetCurrentSelectedItem(newSelectedItem ?? (currentLocationItems.Count > 0 ? currentLocationItems[0] : null), token: token);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await SetCurrentSelectedItem(currentLocationItems.Count > 0 ? currentLocationItems[0] : null);
|
await SetCurrentSelectedItem(currentLocationItems.Count > 0 ? currentLocationItems[0] : null, token: token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,7 +285,7 @@ namespace FileTime.Core.Components
|
|||||||
await SetCurrentSelectedItem(newCurrentLocation);
|
await SetCurrentSelectedItem(newCurrentLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(var lastLocationItem in currentLocationItems.OfType<IContainer>())
|
foreach (var lastLocationItem in currentLocationItems.OfType<IContainer>())
|
||||||
{
|
{
|
||||||
lastLocationItem.Dispose();
|
lastLocationItem.Dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ namespace FileTime.Core.Models
|
|||||||
|
|
||||||
Task<IContainer> Clone();
|
Task<IContainer> Clone();
|
||||||
Task<bool> CanOpen();
|
Task<bool> CanOpen();
|
||||||
|
void Unload();
|
||||||
|
|
||||||
bool IsLoaded { get; }
|
bool IsLoaded { get; }
|
||||||
bool SupportsDirectoryLevelSoftDelete { get; }
|
bool SupportsDirectoryLevelSoftDelete { get; }
|
||||||
|
|||||||
@@ -175,5 +175,10 @@ namespace FileTime.Core.Models
|
|||||||
{
|
{
|
||||||
BaseContainer.Dispose();
|
BaseContainer.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Unload()
|
||||||
|
{
|
||||||
|
BaseContainer.Unload();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,5 +72,7 @@ namespace FileTime.Core.Providers
|
|||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpen() => Task.FromResult(true);
|
||||||
|
|
||||||
public void Dispose() { }
|
public void Dispose() { }
|
||||||
|
|
||||||
|
public void Unload() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,5 +125,6 @@ namespace FileTime.Core.Timeline
|
|||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpen() => Task.FromResult(true);
|
||||||
|
|
||||||
public void Dispose() => IsDisposed = true;
|
public void Dispose() => IsDisposed = true;
|
||||||
|
public void Unload() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,5 +94,7 @@ namespace FileTime.Core.Timeline
|
|||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpen() => Task.FromResult(true);
|
||||||
|
|
||||||
public void Dispose() { }
|
public void Dispose() { }
|
||||||
|
|
||||||
|
public void Unload() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,6 +132,8 @@
|
|||||||
<converters:DateTimeConverter x:Key="DateTimeConverter"/>
|
<converters:DateTimeConverter x:Key="DateTimeConverter"/>
|
||||||
<converters:IsTypeConverter x:Key="IsTypeConverter"/>
|
<converters:IsTypeConverter x:Key="IsTypeConverter"/>
|
||||||
<converters:ItemViewModelIsAttibuteTypeConverter x:Key="ItemViewModelIsAttibuteTypeConverter"/>
|
<converters:ItemViewModelIsAttibuteTypeConverter x:Key="ItemViewModelIsAttibuteTypeConverter"/>
|
||||||
|
<converters:ItemViewModelIsAttibuteTypeConverter x:Key="ItemViewModelIsNotAttibuteTypeConverter" Invert="true"/>
|
||||||
|
<converters:GetFileExtensionConverter x:Key="GetFileExtensionConverter"/>
|
||||||
|
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ namespace FileTime.Avalonia.Application
|
|||||||
public partial class TabContainer : INewItemProcessor
|
public partial class TabContainer : INewItemProcessor
|
||||||
{
|
{
|
||||||
private bool _updateFromCode;
|
private bool _updateFromCode;
|
||||||
|
private CancellationTokenSource? _moveCancellationTokenSource;
|
||||||
|
|
||||||
[Property]
|
[Property]
|
||||||
private TabState _tabState;
|
private TabState _tabState;
|
||||||
|
|
||||||
@@ -70,6 +72,10 @@ namespace FileTime.Avalonia.Application
|
|||||||
{
|
{
|
||||||
if (_selectedItem != value)
|
if (_selectedItem != value)
|
||||||
{
|
{
|
||||||
|
if(_selectedItem is ContainerViewModel containerVM)
|
||||||
|
{
|
||||||
|
containerVM.Unload(unloadParent: false);
|
||||||
|
}
|
||||||
_selectedItem = value;
|
_selectedItem = value;
|
||||||
|
|
||||||
if (value is ElementViewModel elementViewModel)
|
if (value is ElementViewModel elementViewModel)
|
||||||
@@ -83,10 +89,12 @@ namespace FileTime.Avalonia.Application
|
|||||||
ElementPreview = null;
|
ElementPreview = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Tab.SetCurrentSelectedItem(SelectedItem?.Item, fromDataBinding);
|
if (await Tab.SetCurrentSelectedItem(SelectedItem?.Item, fromDataBinding))
|
||||||
|
{
|
||||||
OnPropertyChanged(nameof(SelectedItem));
|
OnPropertyChanged(nameof(SelectedItem));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
partial void OnInitialize()
|
partial void OnInitialize()
|
||||||
{
|
{
|
||||||
@@ -131,6 +139,7 @@ namespace FileTime.Avalonia.Application
|
|||||||
|
|
||||||
private async Task Tab_CurrentLocationChanged(object? sender, AsyncEventArgs e, CancellationToken token = default)
|
private async Task Tab_CurrentLocationChanged(object? sender, AsyncEventArgs e, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
|
CurrentLocation.Unload(true);
|
||||||
var currentLocation = await Tab.GetCurrentLocation(token);
|
var currentLocation = await Tab.GetCurrentLocation(token);
|
||||||
var parent = GenerateParent(currentLocation);
|
var parent = GenerateParent(currentLocation);
|
||||||
CurrentLocation = new ContainerViewModel(this, parent, currentLocation, ItemNameConverterService);
|
CurrentLocation = new ContainerViewModel(this, parent, currentLocation, ItemNameConverterService);
|
||||||
@@ -270,6 +279,13 @@ namespace FileTime.Avalonia.Application
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CancellationToken CancelAndGenerateNextMovementToken()
|
||||||
|
{
|
||||||
|
if(_moveCancellationTokenSource != null) _moveCancellationTokenSource.Cancel();
|
||||||
|
_moveCancellationTokenSource = new CancellationTokenSource();
|
||||||
|
return _moveCancellationTokenSource.Token;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task Open()
|
public async Task Open()
|
||||||
{
|
{
|
||||||
if (ChildContainer != null)
|
if (ChildContainer != null)
|
||||||
@@ -285,32 +301,32 @@ namespace FileTime.Avalonia.Application
|
|||||||
|
|
||||||
public async Task MoveCursorDown()
|
public async Task MoveCursorDown()
|
||||||
{
|
{
|
||||||
await RunFromCode(async () => await Tab.SelectNextItem());
|
await RunFromCode(async () => await Tab.SelectNextItem(token: CancelAndGenerateNextMovementToken()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task MoveCursorDownPage()
|
public async Task MoveCursorDownPage()
|
||||||
{
|
{
|
||||||
await RunFromCode(async () => await Tab.SelectNextItem(10));
|
await RunFromCode(async () => await Tab.SelectNextItem(10, token: CancelAndGenerateNextMovementToken()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task MoveCursorUp()
|
public async Task MoveCursorUp()
|
||||||
{
|
{
|
||||||
await RunFromCode(async () => await Tab.SelectPreviousItem());
|
await RunFromCode(async () => await Tab.SelectPreviousItem(token: CancelAndGenerateNextMovementToken()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task MoveCursorUpPage()
|
public async Task MoveCursorUpPage()
|
||||||
{
|
{
|
||||||
await RunFromCode(async () => await Tab.SelectPreviousItem(10));
|
await RunFromCode(async () => await Tab.SelectPreviousItem(10, token: CancelAndGenerateNextMovementToken()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task MoveCursorToFirst()
|
public async Task MoveCursorToFirst()
|
||||||
{
|
{
|
||||||
await RunFromCode(Tab.SelectFirstItem);
|
await RunFromCode(async () => await Tab.SelectFirstItem(token: CancelAndGenerateNextMovementToken()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task MoveCursorToLast()
|
public async Task MoveCursorToLast()
|
||||||
{
|
{
|
||||||
await RunFromCode(Tab.SelectLastItem);
|
await RunFromCode(async () => await Tab.SelectLastItem(token: CancelAndGenerateNextMovementToken()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task GotToProvider()
|
public async Task GotToProvider()
|
||||||
|
|||||||
@@ -10,14 +10,12 @@ namespace FileTime.Avalonia.Converters
|
|||||||
{
|
{
|
||||||
public class ContextMenuGenerator : IValueConverter
|
public class ContextMenuGenerator : IValueConverter
|
||||||
{
|
{
|
||||||
private readonly IContextMenuProvider _contextMenuProvider;
|
private IContextMenuProvider? _contextMenuProvider;
|
||||||
|
|
||||||
public ContextMenuGenerator()
|
|
||||||
{
|
|
||||||
_contextMenuProvider = App.ServiceProvider.GetService<IContextMenuProvider>() ?? throw new Exception($"No {nameof(IContextMenuProvider)} is registered.");
|
|
||||||
}
|
|
||||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
|
_contextMenuProvider ??= App.ServiceProvider.GetService<IContextMenuProvider>() ?? throw new Exception($"No {nameof(IContextMenuProvider)} is registered.");
|
||||||
|
|
||||||
if (value is ContainerViewModel containerViewModel)
|
if (value is ContainerViewModel containerViewModel)
|
||||||
{
|
{
|
||||||
return _contextMenuProvider.GetContextMenuForFolder(containerViewModel.Container);
|
return _contextMenuProvider.GetContextMenuForFolder(containerViewModel.Container);
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using FileTime.Avalonia.Services;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace FileTime.Avalonia.Converters
|
||||||
|
{
|
||||||
|
public class GetFileExtensionConverter : IValueConverter
|
||||||
|
{
|
||||||
|
private ItemNameConverterService? _itemNameConverterService;
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is not string fullName) return value;
|
||||||
|
_itemNameConverterService ??= App.ServiceProvider.GetService<ItemNameConverterService>() ?? throw new Exception($"No {nameof(ItemNameConverterService)} is registered.");;
|
||||||
|
|
||||||
|
return _itemNameConverterService.GetFileExtension(fullName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,9 +9,12 @@ namespace FileTime.Avalonia.Converters
|
|||||||
{
|
{
|
||||||
public class ItemViewModelIsAttibuteTypeConverter : IValueConverter
|
public class ItemViewModelIsAttibuteTypeConverter : IValueConverter
|
||||||
{
|
{
|
||||||
|
public bool Invert { get; set; }
|
||||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
return parameter is AttibuteType targetAttribute && GetAttibuteType(value) == targetAttribute;
|
var result = parameter is AttibuteType targetAttribute && GetAttibuteType(value) == targetAttribute;
|
||||||
|
if (Invert && parameter is AttibuteType) result = !result;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AttibuteType? GetAttibuteType(object? value)
|
private static AttibuteType? GetAttibuteType(object? value)
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
using FileTime.Avalonia.Application;
|
using FileTime.Avalonia.Application;
|
||||||
using FileTime.Avalonia.Models;
|
using FileTime.Avalonia.Models;
|
||||||
using FileTime.Avalonia.ViewModels;
|
using FileTime.Avalonia.ViewModels;
|
||||||
|
using FileTime.Core.Models;
|
||||||
using MvvmGen;
|
using MvvmGen;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -17,9 +18,10 @@ namespace FileTime.Avalonia.Services
|
|||||||
var nameParts = new List<ItemNamePart>();
|
var nameParts = new List<ItemNamePart>();
|
||||||
var rapidTravelText = AppState.RapidTravelText.ToLower();
|
var rapidTravelText = AppState.RapidTravelText.ToLower();
|
||||||
|
|
||||||
|
var name = itemViewModel.Item is IElement ? GetFileName(itemViewModel.Item.Name) : itemViewModel.Item.Name;
|
||||||
if (AppState.ViewMode == ViewMode.RapidTravel && rapidTravelText.Length > 0)
|
if (AppState.ViewMode == ViewMode.RapidTravel && rapidTravelText.Length > 0)
|
||||||
{
|
{
|
||||||
var nameLeft = itemViewModel.Item.Name;
|
var nameLeft = name;
|
||||||
|
|
||||||
while (nameLeft.ToLower().IndexOf(rapidTravelText, StringComparison.Ordinal) is int rapidTextStart && rapidTextStart != -1)
|
while (nameLeft.ToLower().IndexOf(rapidTravelText, StringComparison.Ordinal) is int rapidTextStart && rapidTextStart != -1)
|
||||||
{
|
{
|
||||||
@@ -43,9 +45,22 @@ namespace FileTime.Avalonia.Services
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
nameParts.Add(new ItemNamePart(itemViewModel.Item.Name));
|
nameParts.Add(new ItemNamePart(name));
|
||||||
}
|
}
|
||||||
return nameParts;
|
return nameParts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GetFileName(string fullName)
|
||||||
|
{
|
||||||
|
var parts = fullName.Split('.');
|
||||||
|
var fileName = string.Join('.', parts[..^1]);
|
||||||
|
return fileName == "." ? fullName : fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetFileExtension(string fullName)
|
||||||
|
{
|
||||||
|
var parts = fullName.Split('.');
|
||||||
|
return parts.Length > 1 ? parts[^1] : "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!_isInitialized) Task.Run(Refresh);
|
if (!_isInitialized) Task.Run(Refresh).Wait();
|
||||||
return _containers;
|
return _containers;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
@@ -90,7 +90,7 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!_isInitialized) Task.Run(Refresh);
|
if (!_isInitialized) Task.Run(Refresh).Wait();
|
||||||
return _elements;
|
return _elements;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
@@ -107,7 +107,7 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!_isInitialized) Task.Run(Refresh);
|
if (!_isInitialized) Task.Run(Refresh).Wait();
|
||||||
return _items;
|
return _items;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
@@ -139,15 +139,15 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
|
|
||||||
private async Task Container_Refreshed(object? sender, AsyncEventArgs e, CancellationToken token = default)
|
private async Task Container_Refreshed(object? sender, AsyncEventArgs e, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
await Refresh(false, false, token);
|
await Refresh(false, false, token: token);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete($"Use the parametrizable version of {nameof(Refresh)}.")]
|
[Obsolete($"Use the parametrizable version of {nameof(Refresh)}.")]
|
||||||
private async Task Refresh()
|
private async Task Refresh()
|
||||||
{
|
{
|
||||||
await Refresh(true);
|
await Refresh(true, silent: true);
|
||||||
}
|
}
|
||||||
private async Task Refresh(bool initializeChildren, bool alloweReuse = true, CancellationToken token = default)
|
private async Task Refresh(bool initializeChildren, bool alloweReuse = true, bool silent = false, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
if (_isRefreshing) return;
|
if (_isRefreshing) return;
|
||||||
|
|
||||||
@@ -169,9 +169,9 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(await _container.GetElements() is IReadOnlyList<IElement> elements)
|
if (await _container.GetElements() is IReadOnlyList<IElement> elements)
|
||||||
{
|
{
|
||||||
foreach(var element in elements)
|
foreach (var element in elements)
|
||||||
{
|
{
|
||||||
var generator = async (IElement e) =>
|
var generator = async (IElement e) =>
|
||||||
{
|
{
|
||||||
@@ -203,9 +203,18 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
containerToRemove?.Dispose();
|
containerToRemove?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (silent)
|
||||||
|
{
|
||||||
|
_containers = new ObservableCollection<ContainerViewModel>(newContainers);
|
||||||
|
_elements = new ObservableCollection<ElementViewModel>(newElements);
|
||||||
|
_items = new ObservableCollection<IItemViewModel>(newContainers.Cast<IItemViewModel>().Concat(newElements));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
Containers = new ObservableCollection<ContainerViewModel>(newContainers);
|
Containers = new ObservableCollection<ContainerViewModel>(newContainers);
|
||||||
Elements = new ObservableCollection<ElementViewModel>(newElements);
|
Elements = new ObservableCollection<ElementViewModel>(newElements);
|
||||||
Items = new ObservableCollection<IItemViewModel>(newContainers.Cast<IItemViewModel>().Concat(newElements));
|
Items = new ObservableCollection<IItemViewModel>(newContainers.Cast<IItemViewModel>().Concat(newElements));
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < Items.Count; i++)
|
for (var i = 0; i < Items.Count; i++)
|
||||||
{
|
{
|
||||||
@@ -260,19 +269,35 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
return await generator(item);
|
return await generator(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Unload(bool recursive = true)
|
public void Unload(bool recursive = true, bool unloadParent = true, bool unloadEvents = false)
|
||||||
{
|
{
|
||||||
_isInitialized = false;
|
_isInitialized = false;
|
||||||
if (recursive)
|
if (recursive)
|
||||||
{
|
{
|
||||||
foreach (var container in _containers)
|
foreach (var container in _containers)
|
||||||
{
|
{
|
||||||
container.Unload(true);
|
container.Unload(true, false, true);
|
||||||
container.Dispose();
|
container.Dispose();
|
||||||
container.ChildrenToAdopt.Clear();
|
container.ChildrenToAdopt.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (unloadParent)
|
||||||
|
{
|
||||||
|
var parent = Parent;
|
||||||
|
while (parent != null)
|
||||||
|
{
|
||||||
|
var lastParent = parent;
|
||||||
|
parent = parent.Parent;
|
||||||
|
lastParent.Unload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(unloadEvents)
|
||||||
|
{
|
||||||
|
Container.Refreshed.Remove(Container_Refreshed);
|
||||||
|
}
|
||||||
|
|
||||||
_containers.Clear();
|
_containers.Clear();
|
||||||
_elements.Clear();
|
_elements.Clear();
|
||||||
_items.Clear();
|
_items.Clear();
|
||||||
|
|||||||
@@ -33,8 +33,15 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
Mode = ElementPreviewMode.Empty;
|
Mode = ElementPreviewMode.Empty;
|
||||||
}
|
}
|
||||||
else if (elementSize < MAXTEXTPREVIEWSIZE)
|
else if (elementSize < MAXTEXTPREVIEWSIZE)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
TextContent = await element.GetContent();
|
TextContent = await element.GetContent();
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
TextContent = $"Error while getting content of {element.FullName}. " + e.ToString();
|
||||||
|
}
|
||||||
Mode = ElementPreviewMode.Text;
|
Mode = ElementPreviewMode.Text;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -202,7 +202,9 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
}
|
}
|
||||||
places.Add(new PlaceInfo(name, container));
|
places.Add(new PlaceInfo(name, container));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
LocalContentProvider.Unload();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -972,6 +974,22 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Task ToggleAutoRefresh()
|
||||||
|
{
|
||||||
|
var tab = AppState.SelectedTab.TabState.Tab;
|
||||||
|
tab.AutoRefresh = !tab.AutoRefresh;
|
||||||
|
|
||||||
|
var text = "Auto refresh is: " + (tab.AutoRefresh ? "ON" : "OFF");
|
||||||
|
_popupTexts.Add(text);
|
||||||
|
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(5000);
|
||||||
|
await Dispatcher.UIThread.InvokeAsync(() => _popupTexts.Remove(text));
|
||||||
|
});
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
[Command]
|
[Command]
|
||||||
public async void ProcessInputs()
|
public async void ProcessInputs()
|
||||||
{
|
{
|
||||||
@@ -1438,6 +1456,11 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
FileTime.App.Core.Command.Commands.Dummy,
|
FileTime.App.Core.Command.Commands.Dummy,
|
||||||
new KeyWithModifiers[] { new KeyWithModifiers(Key.T), new KeyWithModifiers(Key.M) },
|
new KeyWithModifiers[] { new KeyWithModifiers(Key.T), new KeyWithModifiers(Key.M) },
|
||||||
ChangeTimelineMode),
|
ChangeTimelineMode),
|
||||||
|
new CommandBinding(
|
||||||
|
"toggle auto refresh",
|
||||||
|
FileTime.App.Core.Command.Commands.Dummy,
|
||||||
|
new KeyWithModifiers[] { new KeyWithModifiers(Key.R, shift: true) },
|
||||||
|
ToggleAutoRefresh),
|
||||||
//TODO REMOVE
|
//TODO REMOVE
|
||||||
new CommandBinding(
|
new CommandBinding(
|
||||||
"open in default file browser",
|
"open in default file browser",
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
Source="{Binding Converter={StaticResource ItemToImageConverter}}" />
|
Source="{Binding Converter={StaticResource ItemToImageConverter}}" />
|
||||||
|
|
||||||
<ItemsControl
|
<ItemsControl
|
||||||
|
Margin="5,0,0,0"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Items="{Binding DisplayName}">
|
Items="{Binding DisplayName}">
|
||||||
@@ -41,13 +42,14 @@
|
|||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
|
|
||||||
<Grid Grid.Column="2" IsVisible="{Binding ShowAttributes,ElementName=ItemRoot}">
|
<Grid Grid.Column="2" IsVisible="{Binding ShowAttributes,ElementName=ItemRoot}">
|
||||||
<Grid ColumnDefinitions="90,90,40,45"
|
<Grid ColumnDefinitions="30,50,90,40,45"
|
||||||
IsVisible="{Binding Converter={StaticResource ItemViewModelIsAttibuteTypeConverter},ConverterParameter={x:Static models:AttibuteType.LocalFile}}">
|
IsVisible="{Binding Converter={StaticResource ItemViewModelIsAttibuteTypeConverter},ConverterParameter={x:Static models:AttibuteType.LocalFile}}">
|
||||||
|
|
||||||
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Text="{Binding Size, Converter={StaticResource FormatSizeConverter}, ConverterParameter=0}"/>
|
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Text="{Binding Item.Name, Converter={StaticResource GetFileExtensionConverter}}"/>
|
||||||
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="1" Text="{Binding Item.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=yyyy-MM-dd}"/>
|
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="1" Text="{Binding Size, Converter={StaticResource FormatSizeConverter}, ConverterParameter=0}"/>
|
||||||
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="2" Text="{Binding Item.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=hh:mm}"/>
|
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="2" Text="{Binding Item.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=yyyy-MM-dd}"/>
|
||||||
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="3" Text="{Binding Item.Attributes}"/>
|
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="3" Text="{Binding Item.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter=hh:mm}"/>
|
||||||
|
<TextBlock HorizontalAlignment="Right" Classes="SmallText" Grid.Column="4" Text="{Binding Item.Attributes}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid ColumnDefinitions="90,40,45"
|
<Grid ColumnDefinitions="90,40,45"
|
||||||
IsVisible="{Binding Converter={StaticResource ItemViewModelIsAttibuteTypeConverter},ConverterParameter={x:Static models:AttibuteType.Container}}">
|
IsVisible="{Binding Converter={StaticResource ItemViewModelIsAttibuteTypeConverter},ConverterParameter={x:Static models:AttibuteType.Container}}">
|
||||||
|
|||||||
@@ -103,6 +103,20 @@ namespace FileTime.Providers.Local
|
|||||||
public Task Rename(string newName) => throw new NotSupportedException();
|
public Task Rename(string newName) => throw new NotSupportedException();
|
||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpen() => Task.FromResult(true);
|
||||||
|
|
||||||
public void Dispose() { }
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (var c in _rootContainers)
|
||||||
|
{
|
||||||
|
c.Unload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unload()
|
||||||
|
{
|
||||||
|
foreach (var c in _rootContainers)
|
||||||
|
{
|
||||||
|
c.Unload();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ namespace FileTime.Providers.Local
|
|||||||
public SupportsDelete CanDelete { get; }
|
public SupportsDelete CanDelete { get; }
|
||||||
public bool CanRename => true;
|
public bool CanRename => true;
|
||||||
|
|
||||||
public AsyncEventHandler Refreshed { get; } = new();
|
public AsyncEventHandler Refreshed { get; private set; } = new();
|
||||||
|
|
||||||
public string Attributes => GetAttributes();
|
public string Attributes => GetAttributes();
|
||||||
|
|
||||||
@@ -191,6 +191,14 @@ namespace FileTime.Providers.Local
|
|||||||
_containers = null;
|
_containers = null;
|
||||||
_elements = null;
|
_elements = null;
|
||||||
IsDisposed = true;
|
IsDisposed = true;
|
||||||
|
Refreshed = new AsyncEventHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unload()
|
||||||
|
{
|
||||||
|
_items = null;
|
||||||
|
_containers = null;
|
||||||
|
_elements = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,5 +106,7 @@ namespace FileTime.Providers.Smb
|
|||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpen() => Task.FromResult(true);
|
||||||
|
|
||||||
public void Dispose() { }
|
public void Dispose() { }
|
||||||
|
|
||||||
|
public void Unload() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,21 +119,28 @@ namespace FileTime.Providers.Smb
|
|||||||
|
|
||||||
public async Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
|
public async Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
if (_items == null) await RefreshAsync();
|
if (_items == null) await RefreshAsync(token);
|
||||||
return _items;
|
return _items;
|
||||||
}
|
}
|
||||||
public async Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default)
|
public async Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
if (_containers == null) await RefreshAsync();
|
if (_containers == null) await RefreshAsync(token);
|
||||||
return _containers;
|
return _containers;
|
||||||
}
|
}
|
||||||
public async Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default)
|
public async Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
if (_elements == null) await RefreshAsync();
|
if (_elements == null) await RefreshAsync(token);
|
||||||
return _elements;
|
return _elements;
|
||||||
}
|
}
|
||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpen() => Task.FromResult(true);
|
||||||
|
|
||||||
public void Dispose() => IsDisposed = true;
|
public void Dispose() => IsDisposed = true;
|
||||||
|
|
||||||
|
public void Unload()
|
||||||
|
{
|
||||||
|
_items = null;
|
||||||
|
_containers = null;
|
||||||
|
_elements = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,5 +194,7 @@ namespace FileTime.Providers.Smb
|
|||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpen() => Task.FromResult(true);
|
||||||
|
|
||||||
public void Dispose() { }
|
public void Dispose() { }
|
||||||
|
|
||||||
|
public void Unload() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,5 +172,12 @@ namespace FileTime.Providers.Smb
|
|||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpen() => Task.FromResult(true);
|
||||||
|
|
||||||
public void Dispose() { }
|
public void Dispose() { }
|
||||||
|
|
||||||
|
public void Unload()
|
||||||
|
{
|
||||||
|
_items = null;
|
||||||
|
_containers = null;
|
||||||
|
_elements = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user