Optimization, UI improvements, Run command
This commit is contained in:
@@ -101,7 +101,7 @@ namespace FileTime.App.Core.Tab
|
|||||||
: new List<AbsolutePath>().AsReadOnly();
|
: new List<AbsolutePath>().AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task MakrCurrentItem()
|
public async Task MarkCurrentItem()
|
||||||
{
|
{
|
||||||
var currentLocation = await Tab!.GetCurrentLocation();
|
var currentLocation = await Tab!.GetCurrentLocation();
|
||||||
if (currentLocation != null)
|
if (currentLocation != null)
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ namespace FileTime.ConsoleUI.App
|
|||||||
{
|
{
|
||||||
if (_selectedTab != null)
|
if (_selectedTab != null)
|
||||||
{
|
{
|
||||||
await _tabStates[_selectedTab].MakrCurrentItem();
|
await _tabStates[_selectedTab].MarkCurrentItem();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace FileTime.Core.Command
|
|||||||
if (itemToRename != null)
|
if (itemToRename != null)
|
||||||
{
|
{
|
||||||
await itemToRename.Rename(Target);
|
await itemToRename.Rename(Target);
|
||||||
timeRunner.RefreshContainer?.InvokeAsync(this, new AbsolutePath(itemToRename.GetParent()!));
|
if(timeRunner.RefreshContainer != null) await timeRunner.RefreshContainer.InvokeAsync(this, new AbsolutePath(itemToRename.GetParent()!));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ namespace FileTime.Core.Components
|
|||||||
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||||
private string? _lastPath;
|
private string? _lastPath;
|
||||||
|
|
||||||
private readonly object _guardSetCurrentSelectedItemCTS = new object();
|
private bool _currentlySelecting = false;
|
||||||
|
|
||||||
|
private readonly object _guardSetCurrentSelectedItemCTS = new();
|
||||||
private CancellationTokenSource? _setCurrentSelectedItemCTS;
|
private CancellationTokenSource? _setCurrentSelectedItemCTS;
|
||||||
|
|
||||||
public int CurrentSelectedIndex { get; private set; }
|
public int CurrentSelectedIndex { get; private set; }
|
||||||
@@ -54,6 +56,8 @@ namespace FileTime.Core.Components
|
|||||||
|
|
||||||
public async Task SetCurrentSelectedItem(IItem? value, bool secondary = false)
|
public async Task SetCurrentSelectedItem(IItem? value, bool secondary = false)
|
||||||
{
|
{
|
||||||
|
if (_currentlySelecting) return;
|
||||||
|
|
||||||
if (_currentSelectedItem != value)
|
if (_currentSelectedItem != value)
|
||||||
{
|
{
|
||||||
IItem? itemToSelect = null;
|
IItem? itemToSelect = null;
|
||||||
@@ -190,11 +194,13 @@ namespace FileTime.Core.Components
|
|||||||
{
|
{
|
||||||
if (!currentPossibleItems.Any()) return;
|
if (!currentPossibleItems.Any()) return;
|
||||||
|
|
||||||
var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
|
var currentLocation = await GetCurrentLocation();
|
||||||
|
var currentLocationItems = (await currentLocation.GetItems())!;
|
||||||
|
|
||||||
if (await GetCurrentSelectedItem() != null)
|
if (await GetCurrentSelectedItem() != null)
|
||||||
{
|
{
|
||||||
(await GetCurrentLocation())?.RefreshAsync();
|
_currentlySelecting = true;
|
||||||
|
currentLocation?.RefreshAsync();
|
||||||
|
|
||||||
IItem? newSelectedItem = null;
|
IItem? newSelectedItem = null;
|
||||||
foreach (var item in currentPossibleItems)
|
foreach (var item in currentPossibleItems)
|
||||||
@@ -212,6 +218,7 @@ namespace FileTime.Core.Components
|
|||||||
newSelectedItem = (await (await GetCurrentLocation()).GetItems())?.FirstOrDefault(i => i.Name == newSelectedItem.Name);
|
newSelectedItem = (await (await GetCurrentLocation()).GetItems())?.FirstOrDefault(i => i.Name == newSelectedItem.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_currentlySelecting = false;
|
||||||
await SetCurrentSelectedItem(newSelectedItem ?? (currentLocationItems.Count > 0 ? currentLocationItems[0] : null));
|
await SetCurrentSelectedItem(newSelectedItem ?? (currentLocationItems.Count > 0 ? currentLocationItems[0] : null));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -261,6 +268,11 @@ namespace FileTime.Core.Components
|
|||||||
var newCurrentLocation = (await (await GetCurrentLocation()).GetItems())?.FirstOrDefault(i => i.Name == lastCurrentLocation.Name);
|
var newCurrentLocation = (await (await GetCurrentLocation()).GetItems())?.FirstOrDefault(i => i.Name == lastCurrentLocation.Name);
|
||||||
await SetCurrentSelectedItem(newCurrentLocation);
|
await SetCurrentSelectedItem(newCurrentLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach(var lastLocationItem in currentLocationItems.OfType<IContainer>())
|
||||||
|
{
|
||||||
|
lastLocationItem.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,5 +4,7 @@ namespace FileTime.Core.Models
|
|||||||
{
|
{
|
||||||
bool IsSpecial { get; }
|
bool IsSpecial { get; }
|
||||||
string GetPrimaryAttributeText();
|
string GetPrimaryAttributeText();
|
||||||
|
Task<string> GetContent(CancellationToken token = default);
|
||||||
|
Task<long> GetElementSize(CancellationToken token = default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,11 +2,12 @@ using FileTime.Core.Providers;
|
|||||||
|
|
||||||
namespace FileTime.Core.Models
|
namespace FileTime.Core.Models
|
||||||
{
|
{
|
||||||
public interface IItem
|
public interface IItem : IDisposable
|
||||||
{
|
{
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
string? FullName { get; }
|
string? FullName { get; }
|
||||||
bool IsHidden { get; }
|
bool IsHidden { get; }
|
||||||
|
bool IsDisposed { get; }
|
||||||
SupportsDelete CanDelete { get; }
|
SupportsDelete CanDelete { get; }
|
||||||
bool CanRename { get; }
|
bool CanRename { get; }
|
||||||
IContentProvider Provider { get; }
|
IContentProvider Provider { get; }
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ namespace FileTime.Core.Models
|
|||||||
|
|
||||||
public AsyncEventHandler Refreshed { get; }
|
public AsyncEventHandler Refreshed { get; }
|
||||||
|
|
||||||
|
public bool IsDisposed => BaseContainer.IsDisposed;
|
||||||
|
|
||||||
private void RefreshAddBase(Func<object?, AsyncEventArgs, CancellationToken, Task> handler)
|
private void RefreshAddBase(Func<object?, AsyncEventArgs, CancellationToken, Task> handler)
|
||||||
{
|
{
|
||||||
BaseContainer.Refreshed.Add(handler);
|
BaseContainer.Refreshed.Add(handler);
|
||||||
@@ -168,5 +170,10 @@ namespace FileTime.Core.Models
|
|||||||
|
|
||||||
public async Task Rename(string newName) => await BaseContainer.Rename(newName);
|
public async Task Rename(string newName) => await BaseContainer.Rename(newName);
|
||||||
public async Task<bool> CanOpen() => await BaseContainer.CanOpen();
|
public async Task<bool> CanOpen() => await BaseContainer.CanOpen();
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
BaseContainer.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,6 +33,8 @@ namespace FileTime.Core.Providers
|
|||||||
|
|
||||||
public bool SupportsDirectoryLevelSoftDelete => false;
|
public bool SupportsDirectoryLevelSoftDelete => false;
|
||||||
|
|
||||||
|
public bool IsDisposed => false;
|
||||||
|
|
||||||
public TopContainer(IEnumerable<IContentProvider> contentProviders)
|
public TopContainer(IEnumerable<IContentProvider> contentProviders)
|
||||||
{
|
{
|
||||||
_contentProviders = new List<IContentProvider>(contentProviders);
|
_contentProviders = new List<IContentProvider>(contentProviders);
|
||||||
@@ -57,7 +59,7 @@ namespace FileTime.Core.Providers
|
|||||||
|
|
||||||
public Task<bool> IsExists(string name) => throw new NotImplementedException();
|
public Task<bool> IsExists(string name) => throw new NotImplementedException();
|
||||||
|
|
||||||
public async Task RefreshAsync(CancellationToken token = default) => await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty);
|
public async Task RefreshAsync(CancellationToken token = default) => await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
|
||||||
|
|
||||||
public Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default) => Task.FromResult(_items);
|
public Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default) => Task.FromResult(_items);
|
||||||
public Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default) => Task.FromResult(_containers);
|
public Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default) => Task.FromResult(_containers);
|
||||||
@@ -68,5 +70,7 @@ namespace FileTime.Core.Providers
|
|||||||
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() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,6 +29,8 @@ namespace FileTime.Core.Timeline
|
|||||||
|
|
||||||
public bool SupportsDirectoryLevelSoftDelete => false;
|
public bool SupportsDirectoryLevelSoftDelete => false;
|
||||||
|
|
||||||
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
public TimeContainer(string name, IContainer parent, IContentProvider contentProvider, IContentProvider virtualContentProvider, PointInTime pointInTime)
|
public TimeContainer(string name, IContainer parent, IContentProvider contentProvider, IContentProvider virtualContentProvider, PointInTime pointInTime)
|
||||||
{
|
{
|
||||||
_parent = parent;
|
_parent = parent;
|
||||||
@@ -121,5 +123,7 @@ namespace FileTime.Core.Timeline
|
|||||||
return new TimeElement(elementDiff.Name, this, Provider, elementDiff.AbsolutePath.VirtualContentProvider ?? elementDiff.AbsolutePath.ContentProvider);
|
return new TimeElement(elementDiff.Name, this, Provider, elementDiff.AbsolutePath.VirtualContentProvider ?? elementDiff.AbsolutePath.ContentProvider);
|
||||||
}
|
}
|
||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpen() => Task.FromResult(true);
|
||||||
|
|
||||||
|
public void Dispose() => IsDisposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,6 +31,8 @@ namespace FileTime.Core.Timeline
|
|||||||
public IContentProvider Provider { get; }
|
public IContentProvider Provider { get; }
|
||||||
public IContentProvider VirtualProvider { get; }
|
public IContentProvider VirtualProvider { get; }
|
||||||
|
|
||||||
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
public Task Delete(bool hardDelete = false) => Task.CompletedTask;
|
public Task Delete(bool hardDelete = false) => Task.CompletedTask;
|
||||||
|
|
||||||
public IContainer? GetParent() => _parent;
|
public IContainer? GetParent() => _parent;
|
||||||
@@ -38,5 +40,10 @@ namespace FileTime.Core.Timeline
|
|||||||
public string GetPrimaryAttributeText() => "";
|
public string GetPrimaryAttributeText() => "";
|
||||||
|
|
||||||
public Task Rename(string newName) => Task.CompletedTask;
|
public Task Rename(string newName) => Task.CompletedTask;
|
||||||
|
|
||||||
|
public Task<string> GetContent(CancellationToken token = default) => Task.FromResult("");
|
||||||
|
public Task<long> GetElementSize(CancellationToken token = default) => Task.FromResult(-1L);
|
||||||
|
|
||||||
|
public void Dispose() => IsDisposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,6 +28,8 @@ namespace FileTime.Core.Timeline
|
|||||||
|
|
||||||
public bool SupportsDirectoryLevelSoftDelete => false;
|
public bool SupportsDirectoryLevelSoftDelete => false;
|
||||||
|
|
||||||
|
public bool IsDisposed => false;
|
||||||
|
|
||||||
public TimeProvider(PointInTime pointInTime)
|
public TimeProvider(PointInTime pointInTime)
|
||||||
{
|
{
|
||||||
_pointInTime = pointInTime;
|
_pointInTime = pointInTime;
|
||||||
@@ -90,5 +92,7 @@ namespace FileTime.Core.Timeline
|
|||||||
|
|
||||||
public void SetParent(IContainer container) { }
|
public void SetParent(IContainer container) { }
|
||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpen() => Task.FromResult(true);
|
||||||
|
|
||||||
|
public void Dispose() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
<Color x:Key="AppBackgroundColor">#E7073642</Color>
|
<Color x:Key="AppBackgroundColor">#E7073642</Color>
|
||||||
<Color x:Key="ContainerBackgroundColor">#083e4c</Color>
|
<Color x:Key="ContainerBackgroundColor">#083e4c</Color>
|
||||||
<Color x:Key="TransparentContainerBackgroundColor">#D0083e4c</Color>
|
<Color x:Key="TransparentContainerBackgroundColor">#D0083e4c</Color>
|
||||||
|
<Color x:Key="BarelyTransparentBackgroundColor">#80083e4c</Color>
|
||||||
|
|
||||||
<Color x:Key="ItemBackgroundColor">#00000000</Color>
|
<Color x:Key="ItemBackgroundColor">#00000000</Color>
|
||||||
<Color x:Key="AlternativeItemBackgroundColor">#10000000</Color>
|
<Color x:Key="AlternativeItemBackgroundColor">#10000000</Color>
|
||||||
@@ -128,6 +129,7 @@
|
|||||||
<converters:IsEmptyConverter x:Key="IsNotEmptyConverter" Inverse="true"/>
|
<converters:IsEmptyConverter x:Key="IsNotEmptyConverter" Inverse="true"/>
|
||||||
<converters:ExceptionToStringConverter x:Key="ExceptionToStringConverter"/>
|
<converters:ExceptionToStringConverter x:Key="ExceptionToStringConverter"/>
|
||||||
<converters:BoolInverter x:Key="BoolInverter"/>
|
<converters:BoolInverter x:Key="BoolInverter"/>
|
||||||
|
<converters:IsElementConverter x:Key="IsElementConverter"/>
|
||||||
|
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
@@ -145,6 +147,10 @@
|
|||||||
<Style Selector="TextBlock.ExtraSmallText">
|
<Style Selector="TextBlock.ExtraSmallText">
|
||||||
<Setter Property="FontSize" Value="11"/>
|
<Setter Property="FontSize" Value="11"/>
|
||||||
</Style>
|
</Style>
|
||||||
|
<Style Selector="TextBox">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource ForegroundBrush}"/>
|
||||||
|
<Setter Property="Background" Value="{DynamicResource ContainerBackgroundBrush}"/>
|
||||||
|
</Style>
|
||||||
<Style Selector="ListBox.ContentListView">
|
<Style Selector="ListBox.ContentListView">
|
||||||
<Setter Property="Background" Value="Transparent"/>
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
</Style>
|
</Style>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ namespace FileTime.Avalonia.Application
|
|||||||
[Inject(typeof(Tab))]
|
[Inject(typeof(Tab))]
|
||||||
public partial class TabContainer : INewItemProcessor
|
public partial class TabContainer : INewItemProcessor
|
||||||
{
|
{
|
||||||
|
private bool _updateFromCode;
|
||||||
[Property]
|
[Property]
|
||||||
private TabState _tabState;
|
private TabState _tabState;
|
||||||
|
|
||||||
@@ -46,11 +47,13 @@ namespace FileTime.Avalonia.Application
|
|||||||
get => _selectedItem;
|
get => _selectedItem;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value != null)
|
if (!_updateFromCode && value != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
SetSelectedItemAsync(value, true).Wait();
|
/*var task = SetSelectedItemAsync(value, true);
|
||||||
|
Task.WaitAll(new Task[] { task }, 100);*/
|
||||||
|
SetSelectedItemAsync(value, true);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -60,14 +63,28 @@ namespace FileTime.Avalonia.Application
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Property]
|
||||||
|
private ElementPreviewViewModel? _elementPreview;
|
||||||
|
|
||||||
public async Task SetSelectedItemAsync(IItemViewModel? value, bool fromDataBinding = false)
|
public async Task SetSelectedItemAsync(IItemViewModel? value, bool fromDataBinding = false)
|
||||||
{
|
{
|
||||||
if (_selectedItem != value)
|
if (_selectedItem != value)
|
||||||
{
|
{
|
||||||
_selectedItem = value;
|
_selectedItem = value;
|
||||||
|
|
||||||
OnPropertyChanged(nameof(SelectedItem));
|
if (value is ElementViewModel elementViewModel)
|
||||||
|
{
|
||||||
|
var elementPreview = new ElementPreviewViewModel();
|
||||||
|
await elementPreview.Init(elementViewModel.Element);
|
||||||
|
ElementPreview = elementPreview;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ElementPreview = null;
|
||||||
|
}
|
||||||
|
|
||||||
await Tab.SetCurrentSelectedItem(SelectedItem?.Item, fromDataBinding);
|
await Tab.SetCurrentSelectedItem(SelectedItem?.Item, fromDataBinding);
|
||||||
|
OnPropertyChanged(nameof(SelectedItem));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,7 +191,7 @@ namespace FileTime.Avalonia.Application
|
|||||||
|
|
||||||
if (token.IsCancellationRequested) return;
|
if (token.IsCancellationRequested) return;
|
||||||
|
|
||||||
var items = await _currentLocation.GetItems();
|
var items = await _currentLocation.GetItems(token);
|
||||||
if (items?.Count > 0)
|
if (items?.Count > 0)
|
||||||
{
|
{
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
@@ -198,7 +215,7 @@ namespace FileTime.Avalonia.Application
|
|||||||
{
|
{
|
||||||
if (token.IsCancellationRequested) return;
|
if (token.IsCancellationRequested) return;
|
||||||
var activeChildItem = await Tab.GetItemByLastPath(containerViewModel.Container);
|
var activeChildItem = await Tab.GetItemByLastPath(containerViewModel.Container);
|
||||||
child = (await containerViewModel.GetItems()).FirstOrDefault(i => i.Item == activeChildItem);
|
child = (await containerViewModel.GetItems(token)).FirstOrDefault(i => i.Item == activeChildItem);
|
||||||
if (child != null)
|
if (child != null)
|
||||||
{
|
{
|
||||||
child.IsSelected = true;
|
child.IsSelected = true;
|
||||||
@@ -239,59 +256,71 @@ namespace FileTime.Avalonia.Application
|
|||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task RunFromCode(Func<Task> task)
|
||||||
|
{
|
||||||
|
_updateFromCode = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await task();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_updateFromCode = false;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task Open()
|
public async Task Open()
|
||||||
{
|
{
|
||||||
if (ChildContainer != null)
|
if (ChildContainer != null)
|
||||||
{
|
{
|
||||||
await Tab.Open();
|
await RunFromCode(Tab.Open);
|
||||||
await UpdateCurrentSelectedItem();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task GoUp()
|
public async Task GoUp()
|
||||||
{
|
{
|
||||||
await Tab.GoUp();
|
await RunFromCode(Tab.GoUp);
|
||||||
await UpdateCurrentSelectedItem();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task MoveCursorDown()
|
public async Task MoveCursorDown()
|
||||||
{
|
{
|
||||||
await Tab.SelectNextItem();
|
await RunFromCode(async () => await Tab.SelectNextItem());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task MoveCursorDownPage()
|
public async Task MoveCursorDownPage()
|
||||||
{
|
{
|
||||||
await Tab.SelectNextItem(10);
|
await RunFromCode(async () => await Tab.SelectNextItem(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task MoveCursorUp()
|
public async Task MoveCursorUp()
|
||||||
{
|
{
|
||||||
await Tab.SelectPreviousItem();
|
await RunFromCode(async () => await Tab.SelectPreviousItem());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task MoveCursorUpPage()
|
public async Task MoveCursorUpPage()
|
||||||
{
|
{
|
||||||
await Tab.SelectPreviousItem(10);
|
await RunFromCode(async () => await Tab.SelectPreviousItem(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task MoveCursorToFirst()
|
public async Task MoveCursorToFirst()
|
||||||
{
|
{
|
||||||
await Tab.SelectFirstItem();
|
await RunFromCode(Tab.SelectFirstItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task MoveCursorToLast()
|
public async Task MoveCursorToLast()
|
||||||
{
|
{
|
||||||
await Tab.SelectLastItem();
|
await RunFromCode(Tab.SelectLastItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task GotToProvider()
|
public async Task GotToProvider()
|
||||||
{
|
{
|
||||||
await Tab.GoToProvider();
|
await RunFromCode(Tab.GoToProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task GotToRoot()
|
public async Task GotToRoot()
|
||||||
{
|
{
|
||||||
await Tab.GoToRoot();
|
await RunFromCode(Tab.GoToRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task GotToHome()
|
public async Task GotToHome()
|
||||||
@@ -300,28 +329,42 @@ namespace FileTime.Avalonia.Application
|
|||||||
var resolvedPath = await LocalContentProvider.GetByPath(path);
|
var resolvedPath = await LocalContentProvider.GetByPath(path);
|
||||||
if (resolvedPath is IContainer homeFolder)
|
if (resolvedPath is IContainer homeFolder)
|
||||||
{
|
{
|
||||||
await Tab.OpenContainer(homeFolder);
|
await OpenContainer(homeFolder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateContainer(string name)
|
public async Task CreateContainer(string name)
|
||||||
{
|
{
|
||||||
(await Tab.GetCurrentLocation())?.CreateContainer(name);
|
await RunFromCode(async () =>
|
||||||
|
{
|
||||||
|
var currentLocation = await Tab.GetCurrentLocation();
|
||||||
|
if (currentLocation != null)
|
||||||
|
{
|
||||||
|
await currentLocation.CreateContainer(name);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateElement(string name)
|
public async Task CreateElement(string name)
|
||||||
{
|
{
|
||||||
(await Tab.GetCurrentLocation())?.CreateElement(name);
|
await RunFromCode(async () =>
|
||||||
|
{
|
||||||
|
var currentLocation = await Tab.GetCurrentLocation();
|
||||||
|
if (currentLocation != null)
|
||||||
|
{
|
||||||
|
await currentLocation.CreateElement(name);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OpenContainer(IContainer container)
|
public async Task OpenContainer(IContainer container)
|
||||||
{
|
{
|
||||||
await Tab.OpenContainer(container);
|
await RunFromCode(async () => await Tab.OpenContainer(container));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task MarkCurrentItem()
|
public async Task MarkCurrentItem()
|
||||||
{
|
{
|
||||||
await _tabState.MakrCurrentItem();
|
await _tabState.MarkCurrentItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateMarkedItems(ContainerViewModel containerViewModel, CancellationToken token = default)
|
public async Task UpdateMarkedItems(ContainerViewModel containerViewModel, CancellationToken token = default)
|
||||||
|
|||||||
@@ -42,11 +42,16 @@ namespace FileTime.Avalonia.Converters
|
|||||||
else if (value is double valueDouble && (parameter is double parameterDouble || double.TryParse(parameter?.ToString(), out parameterDouble))) return valueDouble != parameterDouble;
|
else if (value is double valueDouble && (parameter is double parameterDouble || double.TryParse(parameter?.ToString(), out parameterDouble))) return valueDouble != parameterDouble;
|
||||||
return value != parameter;
|
return value != parameter;
|
||||||
}
|
}
|
||||||
if (ComparisonCondition == ComparisonCondition.Equal)
|
else if (ComparisonCondition == ComparisonCondition.Equal)
|
||||||
{
|
{
|
||||||
if (value is int valueInt && (parameter is int parameterInt || int.TryParse(parameter?.ToString(), out parameterInt))) return valueInt == parameterInt;
|
if (value is int valueInt && (parameter is int parameterInt || int.TryParse(parameter?.ToString(), out parameterInt))) return valueInt == parameterInt;
|
||||||
else if (value is double valueDouble && (parameter is double parameterDouble || double.TryParse(parameter?.ToString(), out parameterDouble))) return valueDouble == parameterDouble;
|
else if (value is double valueDouble && (parameter is double parameterDouble || double.TryParse(parameter?.ToString(), out parameterDouble))) return valueDouble == parameterDouble;
|
||||||
else if (value?.GetType().IsEnum ?? false && Enum.TryParse(value.GetType(), parameter?.ToString(), out var _)) return value.ToString() == parameter?.ToString();
|
else if (value?.GetType().IsEnum ?? false && Enum.TryParse(value.GetType(), parameter?.ToString(), out var _))
|
||||||
|
{
|
||||||
|
var s1 = value.ToString();
|
||||||
|
var s2 = parameter?.ToString();
|
||||||
|
return s1 == s2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return value == parameter;
|
return value == parameter;
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
|
||||||
|
namespace FileTime.Avalonia.Converters
|
||||||
|
{
|
||||||
|
public class IsElementConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) => value is IElement;
|
||||||
|
|
||||||
|
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,21 +1,20 @@
|
|||||||
using FileTime.Core.Interactions;
|
using FileTime.Core.Interactions;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace FileTime.Avalonia.Misc
|
namespace FileTime.Avalonia.Misc
|
||||||
{
|
{
|
||||||
public class InputElementWrapper
|
public class InputElementWrapper
|
||||||
{
|
{
|
||||||
|
|
||||||
public InputElement InputElement { get; }
|
public InputElement InputElement { get; }
|
||||||
|
|
||||||
public string Value { get; set; }
|
public string Value { get; set; }
|
||||||
|
|
||||||
|
public char? PasswordChar { get; set; }
|
||||||
|
|
||||||
public InputElementWrapper(InputElement inputElement, string? defaultValue = null)
|
public InputElementWrapper(InputElement inputElement, string? defaultValue = null)
|
||||||
{
|
{
|
||||||
InputElement = inputElement;
|
InputElement = inputElement;
|
||||||
Value = defaultValue ?? "";
|
Value = defaultValue ?? "";
|
||||||
|
PasswordChar = inputElement.InputType == InputType.Password ? '*' : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace FileTime.Avalonia.Models
|
||||||
|
{
|
||||||
|
public enum ElementPreviewMode
|
||||||
|
{
|
||||||
|
Unknown,
|
||||||
|
Text,
|
||||||
|
Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -60,6 +60,12 @@ namespace FileTime.Avalonia
|
|||||||
{
|
{
|
||||||
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||||
}
|
}
|
||||||
|
#if DEBUG
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
#else
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
var message = $"Ciritcal error cought in {nameof(Program)}";
|
var message = $"Ciritcal error cought in {nameof(Program)}";
|
||||||
@@ -79,6 +85,7 @@ namespace FileTime.Avalonia
|
|||||||
using var streamWriter = new StreamWriter(fileWriter);
|
using var streamWriter = new StreamWriter(fileWriter);
|
||||||
streamWriter.WriteLine(DateTime.Now.ToString() + ": " + message + "\n" + e.ToString() + "\n\n");
|
streamWriter.WriteLine(DateTime.Now.ToString() + ": " + message + "\n" + e.ToString() + "\n\n");
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avalonia configuration, don't remove; also used by visual designer.
|
// Avalonia configuration, don't remove; also used by visual designer.
|
||||||
|
|||||||
@@ -133,12 +133,13 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
|
|
||||||
public async Task Init(bool initializeChildren = true, CancellationToken token = default)
|
public async Task Init(bool initializeChildren = true, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
await Refresh(initializeChildren, token);
|
if (_isInitialized) return;
|
||||||
|
await Refresh(initializeChildren, token: token);
|
||||||
}
|
}
|
||||||
|
|
||||||
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, token);
|
await Refresh(false, false, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete($"Use the parametrizable version of {nameof(Refresh)}.")]
|
[Obsolete($"Use the parametrizable version of {nameof(Refresh)}.")]
|
||||||
@@ -146,7 +147,7 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
{
|
{
|
||||||
await Refresh(true);
|
await Refresh(true);
|
||||||
}
|
}
|
||||||
private async Task Refresh(bool initializeChildren, CancellationToken token = default)
|
private async Task Refresh(bool initializeChildren, bool alloweReuse = true, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
if (_isRefreshing) return;
|
if (_isRefreshing) return;
|
||||||
|
|
||||||
@@ -157,18 +158,13 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
{
|
{
|
||||||
_isRefreshing = true;
|
_isRefreshing = true;
|
||||||
|
|
||||||
var containers = (await _container.GetContainers())!.Select(c => AdoptOrReuseOrCreateItem(c, (c2) => new ContainerViewModel(_newItemProcessor, this, c2, ItemNameConverterService))).ToList();
|
var containers = (await _container.GetContainers())!.Select(c => AdoptOrReuseOrCreateItem(c, alloweReuse , (c2) => new ContainerViewModel(_newItemProcessor, this, c2, ItemNameConverterService))).ToList();
|
||||||
var elements = (await _container.GetElements())!.Select(e => AdoptOrReuseOrCreateItem(e, (e2) => new ElementViewModel(e2, this, ItemNameConverterService))).ToList();
|
var elements = (await _container.GetElements())!.Select(e => AdoptOrReuseOrCreateItem(e, alloweReuse , (e2) => new ElementViewModel(e2, this, ItemNameConverterService))).ToList();
|
||||||
|
|
||||||
if (token.IsCancellationRequested) return;
|
if (token.IsCancellationRequested) return;
|
||||||
|
|
||||||
Exceptions = new List<Exception>(_container.Exceptions);
|
Exceptions = new List<Exception>(_container.Exceptions);
|
||||||
|
|
||||||
foreach (var containerToRemove in _containers.Except(containers))
|
|
||||||
{
|
|
||||||
containerToRemove?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (initializeChildren)
|
if (initializeChildren)
|
||||||
{
|
{
|
||||||
foreach (var container in containers)
|
foreach (var container in containers)
|
||||||
@@ -178,14 +174,53 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < _items.Count; i++)
|
/*var containersToAdd = containers.Except(_containers).ToList();
|
||||||
|
var containersToRemove = _containers.Except(containers).ToList();
|
||||||
|
|
||||||
|
var elementsToAdd = elements.Except(_elements).ToList();
|
||||||
|
var elementsToRemove = _elements.Except(elements).ToList();
|
||||||
|
|
||||||
|
foreach (var containerToRemove in containersToRemove)
|
||||||
{
|
{
|
||||||
_items[i].IsAlternative = i % 2 == 1;
|
Containers.Remove(containerToRemove);
|
||||||
|
Items.Remove(containerToRemove);
|
||||||
|
containerToRemove?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var elementToRemove in elementsToRemove)
|
||||||
|
{
|
||||||
|
Elements.Remove(elementToRemove);
|
||||||
|
Items.Remove(elementToRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var containerToAdd in containersToAdd)
|
||||||
|
{
|
||||||
|
Containers.Insert(GetNewItemPosition(containerToAdd, Containers), containerToAdd);
|
||||||
|
Items.Insert(GetNewItemPosition(containerToAdd, Items), containerToAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var elementToAdd in elementsToAdd)
|
||||||
|
{
|
||||||
|
Elements.Insert(GetNewItemPosition(elementToAdd, Elements), elementToAdd);
|
||||||
|
Items.Insert(GetNewItemPosition(elementToAdd, Items), elementToAdd);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var containersToRemove = _containers.Except(containers).ToList();
|
||||||
|
foreach (var containerToRemove in containersToRemove)
|
||||||
|
{
|
||||||
|
containerToRemove?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Containers = new ObservableCollection<ContainerViewModel>(containers);
|
Containers = new ObservableCollection<ContainerViewModel>(containers);
|
||||||
Elements = new ObservableCollection<ElementViewModel>(elements);
|
Elements = new ObservableCollection<ElementViewModel>(elements);
|
||||||
Items = new ObservableCollection<IItemViewModel>(containers.Cast<IItemViewModel>().Concat(elements));
|
Items = new ObservableCollection<IItemViewModel>(containers.Cast<IItemViewModel>().Concat(elements));
|
||||||
|
|
||||||
|
for (var i = 0; i < Items.Count; i++)
|
||||||
|
{
|
||||||
|
Items[i].IsAlternative = i % 2 == 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -197,13 +232,31 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
_isRefreshing = false;
|
_isRefreshing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TResult AdoptOrReuseOrCreateItem<T, TResult>(T item, Func<T, TResult> generator) where T : class, IItem
|
private int GetNewItemPosition<TItem, T>(TItem itemToAdd, IList<T> items) where TItem : IItemViewModel where T : IItemViewModel
|
||||||
|
{
|
||||||
|
var i = 0;
|
||||||
|
for (; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
var item = items[i];
|
||||||
|
if (item is TItem && itemToAdd.Item.Name.CompareTo(item.Item.Name) < 0)
|
||||||
|
{
|
||||||
|
return i - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TResult AdoptOrReuseOrCreateItem<T, TResult>(T item, bool allowResuse, Func<T, TResult> generator) where T : class, IItem
|
||||||
{
|
{
|
||||||
var itemToAdopt = ChildrenToAdopt.Find(i => i.Item == item);
|
var itemToAdopt = ChildrenToAdopt.Find(i => i.Item == item);
|
||||||
if (itemToAdopt is TResult itemViewModel) return itemViewModel;
|
if (itemToAdopt is TResult itemViewModel) return itemViewModel;
|
||||||
|
|
||||||
|
if (allowResuse)
|
||||||
|
{
|
||||||
var existingViewModel = _items?.FirstOrDefault(i => i.Item == item);
|
var existingViewModel = _items?.FirstOrDefault(i => i.Item == item);
|
||||||
if (existingViewModel is TResult itemViewModelToReuse) return itemViewModelToReuse;
|
if (existingViewModel is TResult itemViewModelToReuse) return itemViewModelToReuse;
|
||||||
|
}
|
||||||
|
|
||||||
return generator(item);
|
return generator(item);
|
||||||
}
|
}
|
||||||
@@ -221,26 +274,26 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_containers = new ObservableCollection<ContainerViewModel>();
|
_containers.Clear();
|
||||||
_elements = new ObservableCollection<ElementViewModel>();
|
_elements.Clear();
|
||||||
_items = new ObservableCollection<IItemViewModel>();
|
_items.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ObservableCollection<ContainerViewModel>> GetContainers(CancellationToken token = default)
|
public async Task<ObservableCollection<ContainerViewModel>> GetContainers(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
if (!_isInitialized) await Task.Run(async () => await Refresh(false, token), token);
|
if (!_isInitialized) await Task.Run(async () => await Refresh(false, token: token), token);
|
||||||
return _containers;
|
return _containers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ObservableCollection<ElementViewModel>> GetElements(CancellationToken token = default)
|
public async Task<ObservableCollection<ElementViewModel>> GetElements(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
if (!_isInitialized) await Task.Run(async () => await Refresh(false, token), token);
|
if (!_isInitialized) await Task.Run(async () => await Refresh(false, token: token), token);
|
||||||
return _elements;
|
return _elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ObservableCollection<IItemViewModel>> GetItems(CancellationToken token = default)
|
public async Task<ObservableCollection<IItemViewModel>> GetItems(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
if (!_isInitialized) await Task.Run(async () => await Refresh(false, token), token);
|
if (!_isInitialized) await Task.Run(async () => await Refresh(false, token: token), token);
|
||||||
return _items;
|
return _items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
using FileTime.Avalonia.Models;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
using MvvmGen;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FileTime.Avalonia.ViewModels
|
||||||
|
{
|
||||||
|
[ViewModel]
|
||||||
|
public partial class ElementPreviewViewModel
|
||||||
|
{
|
||||||
|
private const int MAXTEXTPREVIEWSIZE = 1024 * 1024;
|
||||||
|
[Property]
|
||||||
|
private IElement? _element;
|
||||||
|
|
||||||
|
[Property]
|
||||||
|
private string? _textContent;
|
||||||
|
|
||||||
|
[Property]
|
||||||
|
private ElementPreviewMode? _mode;
|
||||||
|
|
||||||
|
public async Task Init(IElement element, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
Element = element;
|
||||||
|
|
||||||
|
var elementSize = await element.GetElementSize(token);
|
||||||
|
if (elementSize == 0)
|
||||||
|
{
|
||||||
|
Mode = ElementPreviewMode.Empty;
|
||||||
|
}
|
||||||
|
else if (elementSize < MAXTEXTPREVIEWSIZE)
|
||||||
|
{
|
||||||
|
TextContent = await element.GetContent();
|
||||||
|
Mode = ElementPreviewMode.Text;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Mode = ElementPreviewMode.Unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -74,10 +74,10 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
private string _messageBoxText;
|
private string _messageBoxText;
|
||||||
|
|
||||||
[Property]
|
[Property]
|
||||||
private ObservableCollection<string> _popupTexts = new ObservableCollection<string>();
|
private ObservableCollection<string> _popupTexts = new();
|
||||||
|
|
||||||
[Property]
|
[Property]
|
||||||
private bool _showAllShortcut;
|
private bool _isAllShortcutVisible;
|
||||||
|
|
||||||
[Property]
|
[Property]
|
||||||
private List<CommandBinding> _allShortcut;
|
private List<CommandBinding> _allShortcut;
|
||||||
@@ -716,9 +716,58 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task ShowAllShortcut2()
|
private Task ShowAllShortcut()
|
||||||
{
|
{
|
||||||
ShowAllShortcut = true;
|
IsAllShortcutVisible = true;
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task RunCommandInContainer()
|
||||||
|
{
|
||||||
|
var handler = () =>
|
||||||
|
{
|
||||||
|
if (Inputs != null)
|
||||||
|
{
|
||||||
|
var input = Inputs[0].Value;
|
||||||
|
string? path = null;
|
||||||
|
string? arguments = null;
|
||||||
|
|
||||||
|
if (input.StartsWith("\""))
|
||||||
|
{
|
||||||
|
var pathEnd = input.IndexOf('\"', 1);
|
||||||
|
|
||||||
|
path = input.Substring(1, pathEnd);
|
||||||
|
arguments = input.Substring(pathEnd + 1).Trim();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var inputParts = input.Split(' ');
|
||||||
|
path = inputParts[0];
|
||||||
|
arguments = inputParts.Length > 1 ? string.Join(' ', inputParts[1..]).Trim() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(path))
|
||||||
|
{
|
||||||
|
var process = new Process();
|
||||||
|
process.StartInfo.FileName = path;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(arguments))
|
||||||
|
{
|
||||||
|
process.StartInfo.Arguments = arguments;
|
||||||
|
}
|
||||||
|
if (AppState.SelectedTab.CurrentLocation.Container is LocalFolder localFolder)
|
||||||
|
{
|
||||||
|
process.StartInfo.WorkingDirectory = localFolder.Directory.FullName;
|
||||||
|
}
|
||||||
|
process.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
};
|
||||||
|
|
||||||
|
ReadInputs(new List<Core.Interactions.InputElement>() { new Core.Interactions.InputElement("Command", InputType.Text) }, handler);
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,14 +806,14 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
_inputHandler = null;
|
_inputHandler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> ProcessKeyDown(Key key, KeyModifiers keyModifiers)
|
public async void ProcessKeyDown(Key key, KeyModifiers keyModifiers, Action<bool> setHandled)
|
||||||
{
|
{
|
||||||
if (key == Key.LeftAlt
|
if (key == Key.LeftAlt
|
||||||
|| key == Key.RightAlt
|
|| key == Key.RightAlt
|
||||||
|| key == Key.LeftShift
|
|| key == Key.LeftShift
|
||||||
|| key == Key.RightShift
|
|| key == Key.RightShift
|
||||||
|| key == Key.LeftCtrl
|
|| key == Key.LeftCtrl
|
||||||
|| key == Key.RightCtrl) return false;
|
|| key == Key.RightCtrl) return;
|
||||||
|
|
||||||
NoCommandFound = false;
|
NoCommandFound = false;
|
||||||
|
|
||||||
@@ -782,12 +831,14 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
|
|
||||||
if (key == Key.Escape)
|
if (key == Key.Escape)
|
||||||
{
|
{
|
||||||
ShowAllShortcut = false;
|
IsAllShortcutVisible = false;
|
||||||
_previousKeys.Clear();
|
_previousKeys.Clear();
|
||||||
PossibleCommands = new();
|
PossibleCommands = new();
|
||||||
|
setHandled(true);
|
||||||
}
|
}
|
||||||
else if (selectedCommandBinding != null)
|
else if (selectedCommandBinding != null)
|
||||||
{
|
{
|
||||||
|
setHandled(true);
|
||||||
await selectedCommandBinding.InvokeAsync();
|
await selectedCommandBinding.InvokeAsync();
|
||||||
_previousKeys.Clear();
|
_previousKeys.Clear();
|
||||||
PossibleCommands = new();
|
PossibleCommands = new();
|
||||||
@@ -796,10 +847,11 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
{
|
{
|
||||||
_previousKeys.Clear();
|
_previousKeys.Clear();
|
||||||
PossibleCommands = new();
|
PossibleCommands = new();
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
else if (_previousKeys.Count == 2)
|
else if (_previousKeys.Count == 2)
|
||||||
{
|
{
|
||||||
|
setHandled(true);
|
||||||
NoCommandFound = true;
|
NoCommandFound = true;
|
||||||
_previousKeys.Clear();
|
_previousKeys.Clear();
|
||||||
PossibleCommands = new();
|
PossibleCommands = new();
|
||||||
@@ -817,6 +869,7 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
{
|
{
|
||||||
PossibleCommands = possibleCommands;
|
PossibleCommands = possibleCommands;
|
||||||
}
|
}
|
||||||
|
setHandled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -826,9 +879,10 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
|
|
||||||
if (key == Key.Escape)
|
if (key == Key.Escape)
|
||||||
{
|
{
|
||||||
if (ShowAllShortcut)
|
setHandled(true);
|
||||||
|
if (IsAllShortcutVisible)
|
||||||
{
|
{
|
||||||
ShowAllShortcut = false;
|
IsAllShortcutVisible = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -839,12 +893,14 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
{
|
{
|
||||||
if (AppState.RapidTravelText.Length > 0)
|
if (AppState.RapidTravelText.Length > 0)
|
||||||
{
|
{
|
||||||
|
setHandled(true);
|
||||||
AppState.RapidTravelText = AppState.RapidTravelText.Substring(0, AppState.RapidTravelText.Length - 1);
|
AppState.RapidTravelText = AppState.RapidTravelText.Substring(0, AppState.RapidTravelText.Length - 1);
|
||||||
updateRapidTravelFilter = true;
|
updateRapidTravelFilter = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (keyString.Length == 1)
|
else if (keyString.Length == 1)
|
||||||
{
|
{
|
||||||
|
setHandled(true);
|
||||||
AppState.RapidTravelText += keyString.ToString().ToLower();
|
AppState.RapidTravelText += keyString.ToString().ToLower();
|
||||||
updateRapidTravelFilter = true;
|
updateRapidTravelFilter = true;
|
||||||
}
|
}
|
||||||
@@ -854,12 +910,8 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
var selectedCommandBinding = _universalCommandBindings.Find(c => AreKeysEqual(c.Keys, currentKeyAsList));
|
var selectedCommandBinding = _universalCommandBindings.Find(c => AreKeysEqual(c.Keys, currentKeyAsList));
|
||||||
if (selectedCommandBinding != null)
|
if (selectedCommandBinding != null)
|
||||||
{
|
{
|
||||||
|
setHandled(true);
|
||||||
await selectedCommandBinding.InvokeAsync();
|
await selectedCommandBinding.InvokeAsync();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -895,13 +947,6 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<bool> ProcessKeyUp(Key key, KeyModifiers keyModifiers)
|
|
||||||
{
|
|
||||||
return Task.FromResult(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReadInputs(List<Core.Interactions.InputElement> inputs, Action inputHandler)
|
private void ReadInputs(List<Core.Interactions.InputElement> inputs, Action inputHandler)
|
||||||
@@ -1144,7 +1189,12 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
"show all shortcut",
|
"show all shortcut",
|
||||||
FileTime.App.Core.Command.Commands.Dummy,
|
FileTime.App.Core.Command.Commands.Dummy,
|
||||||
new KeyWithModifiers[] { new KeyWithModifiers(Key.F1) },
|
new KeyWithModifiers[] { new KeyWithModifiers(Key.F1) },
|
||||||
ShowAllShortcut2),
|
ShowAllShortcut),
|
||||||
|
new CommandBinding(
|
||||||
|
"run command",
|
||||||
|
FileTime.App.Core.Command.Commands.Dummy,
|
||||||
|
new KeyWithModifiers[] { new KeyWithModifiers(Key.D4, shift: true) },
|
||||||
|
RunCommandInContainer),
|
||||||
//TODO REMOVE
|
//TODO REMOVE
|
||||||
new CommandBinding(
|
new CommandBinding(
|
||||||
"open in default file browser",
|
"open in default file browser",
|
||||||
|
|||||||
@@ -6,12 +6,12 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:vm="using:FileTime.Avalonia.ViewModels"
|
xmlns:vm="using:FileTime.Avalonia.ViewModels"
|
||||||
xmlns:local="using:FileTime.Avalonia.Views"
|
xmlns:local="using:FileTime.Avalonia.Views"
|
||||||
|
xmlns:models="using:FileTime.Avalonia.Models"
|
||||||
Title="FileTime"
|
Title="FileTime"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
Icon="/Assets/filetime.ico"
|
Icon="/Assets/filetime.ico"
|
||||||
InputElement.KeyDown="OnKeyDown"
|
InputElement.KeyDown="OnKeyDown"
|
||||||
InputElement.KeyUp="OnKeyUp"
|
|
||||||
TransparencyLevelHint="Blur"
|
TransparencyLevelHint="Blur"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
ExtendClientAreaToDecorationsHint="True"
|
ExtendClientAreaToDecorationsHint="True"
|
||||||
@@ -269,7 +269,8 @@
|
|||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
Fill="{DynamicResource ContentSeparatorBrush}" />
|
Fill="{DynamicResource ContentSeparatorBrush}" />
|
||||||
|
|
||||||
<Grid Grid.Column="4" IsVisible="{Binding AppState.SelectedTab.ChildContainer,Converter={StaticResource IsNotNullConverter}}">
|
<Grid Grid.Column="4">
|
||||||
|
<Grid IsVisible="{Binding AppState.SelectedTab.ChildContainer,Converter={StaticResource IsNotNullConverter}}">
|
||||||
<ListBox
|
<ListBox
|
||||||
Classes="ContentListView"
|
Classes="ContentListView"
|
||||||
IsEnabled="False"
|
IsEnabled="False"
|
||||||
@@ -283,8 +284,7 @@
|
|||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
</ListBox>
|
</ListBox>
|
||||||
|
|
||||||
<Grid
|
<Grid IsVisible="{Binding AppState.SelectedTab.ChildContainer.Items.Count, Converter={StaticResource EqualityConverter}, ConverterParameter=0}">
|
||||||
IsVisible="{Binding AppState.SelectedTab.ChildContainer.Items.Count, Converter={StaticResource EqualityConverter}, ConverterParameter=0}">
|
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
x:Name="ChildEmpty"
|
x:Name="ChildEmpty"
|
||||||
@@ -317,75 +317,17 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid
|
<Grid IsVisible="{Binding AppState.SelectedTab.ElementPreview, Converter={StaticResource IsNotNullConverter}}">
|
||||||
HorizontalAlignment="Center"
|
<TextBlock HorizontalAlignment="Center" Text="Don't know how to preview this file" IsVisible="{Binding AppState.SelectedTab.ElementPreview.Mode, Converter={StaticResource EqualityConverter}, ConverterParameter={x:Static models:ElementPreviewMode.Unknown}}"/>
|
||||||
VerticalAlignment="Center"
|
<TextBlock HorizontalAlignment="Center" Text="Empty" IsVisible="{Binding AppState.SelectedTab.ElementPreview.Mode, Converter={StaticResource EqualityConverter}, ConverterParameter={x:Static models:ElementPreviewMode.Empty}}"/>
|
||||||
IsVisible="{Binding Inputs, Converter={StaticResource IsNotNullConverter}}">
|
<ScrollViewer IsVisible="{Binding AppState.SelectedTab.ElementPreview.Mode, Converter={StaticResource EqualityConverter}, ConverterParameter={x:Static models:ElementPreviewMode.Text}}">
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<ItemsControl
|
|
||||||
x:Name="InputList"
|
|
||||||
Items="{Binding Inputs}">
|
|
||||||
<ItemsControl.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<Grid MinWidth="400">
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition MinWidth="200" />
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<TextBlock
|
|
||||||
HorizontalAlignment="Left"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Text="{Binding InputElement.Text}" />
|
|
||||||
<TextBox
|
<TextBox
|
||||||
AttachedToVisualTree="InputText_AttachedToVisualTree"
|
IsReadOnly="True"
|
||||||
Grid.Column="1"
|
Text="{Binding AppState.SelectedTab.ElementPreview.TextContent}" />
|
||||||
GotFocus="InputText_GotFocus"
|
</ScrollViewer>
|
||||||
LostFocus="InputText_LostFocus"
|
|
||||||
KeyDown="InputText_KeyDown"
|
|
||||||
Text="{Binding Value, Mode=TwoWay}" />
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
</ItemsControl>
|
|
||||||
<StackPanel
|
|
||||||
Grid.Row="1"
|
|
||||||
Orientation="Horizontal">
|
|
||||||
<Button
|
|
||||||
Command="{Binding ProcessInputsCommand}"
|
|
||||||
Content="Ok" />
|
|
||||||
<Button
|
|
||||||
Command="{Binding CancelInputsCommand}"
|
|
||||||
Content="Cancel" />
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
IsVisible="{Binding MessageBoxText, Converter={StaticResource IsNotNullConverter}}">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<TextBlock Text="{Binding MessageBoxText}"/>
|
|
||||||
<StackPanel
|
|
||||||
Grid.Row="1"
|
|
||||||
Orientation="Horizontal">
|
|
||||||
<Button
|
|
||||||
Command="{Binding ProcessMessageBoxCommand}"
|
|
||||||
Content="Yes" />
|
|
||||||
<Button
|
|
||||||
Command="{Binding CancelMessageBoxCommand}"
|
|
||||||
Content="No" />
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<ItemsRepeater Items="{Binding PopupTexts}" Margin="0,0,0,20" HorizontalAlignment="Center" VerticalAlignment="Bottom" IsVisible="{Binding PopupTexts.Count,Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}">
|
<ItemsRepeater Items="{Binding PopupTexts}" Margin="0,0,0,20" HorizontalAlignment="Center" VerticalAlignment="Bottom" IsVisible="{Binding PopupTexts.Count,Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}">
|
||||||
@@ -496,7 +438,99 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Border Background="{DynamicResource TransparentContainerBackgroundBrush}" Margin="20" HorizontalAlignment="Center" IsVisible="{Binding ShowAllShortcut}">
|
<Border
|
||||||
|
Background="{DynamicResource BarelyTransparentBackgroundColor}"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
IsVisible="{Binding Inputs, Converter={StaticResource IsNotNullConverter}}">
|
||||||
|
<Border
|
||||||
|
Background="{DynamicResource ContainerBackgroundBrush}"
|
||||||
|
Padding="20"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<Grid RowDefinitions="Auto,Auto">
|
||||||
|
|
||||||
|
<ItemsControl
|
||||||
|
x:Name="InputList"
|
||||||
|
Items="{Binding Inputs}">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Grid MinWidth="500" ColumnDefinitions="250,*" Margin="10,5">
|
||||||
|
<TextBlock
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{Binding InputElement.Text}" />
|
||||||
|
<TextBox
|
||||||
|
PasswordChar="{Binding PasswordChar}"
|
||||||
|
AttachedToVisualTree="InputText_AttachedToVisualTree"
|
||||||
|
IsTabStop="True"
|
||||||
|
Grid.Column="1"
|
||||||
|
GotFocus="InputText_GotFocus"
|
||||||
|
LostFocus="InputText_LostFocus"
|
||||||
|
KeyDown="InputText_KeyDown"
|
||||||
|
Text="{Binding Value, Mode=TwoWay}" />
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
<StackPanel
|
||||||
|
Grid.Row="1"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
Margin="0,10,0,0">
|
||||||
|
<Button
|
||||||
|
HorizontalContentAlignment="Center"
|
||||||
|
Width="80"
|
||||||
|
Command="{Binding ProcessInputsCommand}"
|
||||||
|
Content="Ok" />
|
||||||
|
<Button
|
||||||
|
HorizontalContentAlignment="Center"
|
||||||
|
Width="80"
|
||||||
|
Margin="10,0,0,0"
|
||||||
|
Command="{Binding CancelInputsCommand}"
|
||||||
|
Content="Cancel" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border
|
||||||
|
Background="{DynamicResource BarelyTransparentBackgroundColor}"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
IsVisible="{Binding MessageBoxText, Converter={StaticResource IsNotNullConverter}}">
|
||||||
|
<Border
|
||||||
|
Background="{DynamicResource ContainerBackgroundBrush}"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Padding="20">
|
||||||
|
<Grid RowDefinitions="Auto,Auto">
|
||||||
|
|
||||||
|
<TextBlock Text="{Binding MessageBoxText}"/>
|
||||||
|
<StackPanel
|
||||||
|
Grid.Row="1"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
Margin="0,10,0,0">
|
||||||
|
<Button
|
||||||
|
HorizontalContentAlignment="Center"
|
||||||
|
Width="80"
|
||||||
|
Command="{Binding ProcessMessageBoxCommand}"
|
||||||
|
Content="Yes" />
|
||||||
|
<Button
|
||||||
|
HorizontalContentAlignment="Center"
|
||||||
|
Width="80"
|
||||||
|
Margin="10,0,0,0"
|
||||||
|
Command="{Binding CancelMessageBoxCommand}"
|
||||||
|
Content="No" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border
|
||||||
|
Background="{DynamicResource TransparentContainerBackgroundBrush}"
|
||||||
|
Margin="20"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
IsVisible="{Binding IsAllShortcutVisible}">
|
||||||
<Grid RowDefinitions="Auto, *" Margin="30,10">
|
<Grid RowDefinitions="Auto, *" Margin="30,10">
|
||||||
<TextBlock Text="Shortcuts" Margin="0,0,0,20"/>
|
<TextBlock Text="Shortcuts" Margin="0,0,0,20"/>
|
||||||
<ScrollViewer
|
<ScrollViewer
|
||||||
|
|||||||
@@ -45,19 +45,11 @@ namespace FileTime.Avalonia.Views
|
|||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void OnKeyDown(object sender, KeyEventArgs e)
|
public void OnKeyDown(object sender, KeyEventArgs e)
|
||||||
{
|
{
|
||||||
if (_inputElementWrapper == null)
|
if (_inputElementWrapper == null)
|
||||||
{
|
{
|
||||||
e.Handled = e.Handled || await ViewModel!.ProcessKeyDown(e.Key, e.KeyModifiers);
|
ViewModel!.ProcessKeyDown(e.Key, e.KeyModifiers, h => e.Handled = h);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void OnKeyUp(object sender, KeyEventArgs e)
|
|
||||||
{
|
|
||||||
if (_inputElementWrapper == null)
|
|
||||||
{
|
|
||||||
e.Handled = e.Handled || await ViewModel!.ProcessKeyUp(e.Key, e.KeyModifiers);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +79,7 @@ namespace FileTime.Avalonia.Views
|
|||||||
|
|
||||||
private void InputText_LostFocus(object sender, RoutedEventArgs e)
|
private void InputText_LostFocus(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is TextBox inputText && inputText.DataContext is InputElementWrapper inputElementWrapper)
|
if (sender is TextBox inputText && inputText.DataContext is InputElementWrapper)
|
||||||
{
|
{
|
||||||
_inputElementWrapper = null;
|
_inputElementWrapper = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ namespace FileTime.Providers.Local
|
|||||||
|
|
||||||
public bool SupportsDirectoryLevelSoftDelete => false;
|
public bool SupportsDirectoryLevelSoftDelete => false;
|
||||||
|
|
||||||
|
public bool IsDisposed => false;
|
||||||
|
|
||||||
public LocalContentProvider(ILogger<LocalContentProvider> logger)
|
public LocalContentProvider(ILogger<LocalContentProvider> logger)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -100,5 +102,7 @@ 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() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,6 +30,8 @@ namespace FileTime.Providers.Local
|
|||||||
|
|
||||||
private readonly LocalFolder _parent;
|
private readonly LocalFolder _parent;
|
||||||
|
|
||||||
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
public LocalFile(FileInfo file, LocalFolder parent, IContentProvider contentProvider)
|
public LocalFile(FileInfo file, LocalFolder parent, IContentProvider contentProvider)
|
||||||
{
|
{
|
||||||
_parent = parent;
|
_parent = parent;
|
||||||
@@ -80,5 +82,10 @@ namespace FileTime.Providers.Local
|
|||||||
}
|
}
|
||||||
|
|
||||||
public IContainer? GetParent() => _parent;
|
public IContainer? GetParent() => _parent;
|
||||||
|
|
||||||
|
public async Task<string> GetContent(CancellationToken token = default) => await System.IO.File.ReadAllTextAsync(File.FullName, token);
|
||||||
|
public Task<long> GetElementSize(CancellationToken token = default) => Task.FromResult(File.Length);
|
||||||
|
|
||||||
|
public void Dispose() => IsDisposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@ namespace FileTime.Providers.Local
|
|||||||
private IReadOnlyList<IItem>? _items;
|
private IReadOnlyList<IItem>? _items;
|
||||||
private IReadOnlyList<IContainer>? _containers;
|
private IReadOnlyList<IContainer>? _containers;
|
||||||
private IReadOnlyList<IElement>? _elements;
|
private IReadOnlyList<IElement>? _elements;
|
||||||
private List<Exception> _exceptions;
|
private readonly List<Exception> _exceptions;
|
||||||
private readonly IContainer? _parent;
|
private readonly IContainer? _parent;
|
||||||
|
|
||||||
public bool IsHidden => (Directory.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
|
public bool IsHidden => (Directory.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
|
||||||
@@ -34,6 +34,8 @@ namespace FileTime.Providers.Local
|
|||||||
public DateTime CreatedAt => Directory.CreationTime;
|
public DateTime CreatedAt => Directory.CreationTime;
|
||||||
public IReadOnlyList<Exception> Exceptions { get; }
|
public IReadOnlyList<Exception> Exceptions { get; }
|
||||||
|
|
||||||
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
public bool SupportsDirectoryLevelSoftDelete { get; }
|
public bool SupportsDirectoryLevelSoftDelete { get; }
|
||||||
|
|
||||||
public LocalFolder(DirectoryInfo directory, LocalContentProvider contentProvider, IContainer? parent)
|
public LocalFolder(DirectoryInfo directory, LocalContentProvider contentProvider, IContainer? parent)
|
||||||
@@ -59,8 +61,16 @@ namespace FileTime.Providers.Local
|
|||||||
|
|
||||||
public Task<IContainer> Clone() => Task.FromResult((IContainer)new LocalFolder(Directory, Provider, _parent));
|
public Task<IContainer> Clone() => Task.FromResult((IContainer)new LocalFolder(Directory, Provider, _parent));
|
||||||
|
|
||||||
public Task RefreshAsync(CancellationToken token = default)
|
public async Task RefreshAsync(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
|
if (_items != null)
|
||||||
|
{
|
||||||
|
foreach (var item in _items)
|
||||||
|
{
|
||||||
|
item.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_containers = new List<IContainer>();
|
_containers = new List<IContainer>();
|
||||||
_elements = new List<IElement>();
|
_elements = new List<IElement>();
|
||||||
_items = new List<IItem>();
|
_items = new List<IItem>();
|
||||||
@@ -68,13 +78,13 @@ namespace FileTime.Providers.Local
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (token.IsCancellationRequested) return Task.CompletedTask;
|
if (token.IsCancellationRequested) return;
|
||||||
_containers = Directory.GetDirectories().Select(d => new LocalFolder(d, Provider, this)).OrderBy(d => d.Name).ToList().AsReadOnly();
|
_containers = Directory.GetDirectories().Select(d => new LocalFolder(d, Provider, this)).OrderBy(d => d.Name).ToList().AsReadOnly();
|
||||||
|
|
||||||
if (token.IsCancellationRequested) return Task.CompletedTask;
|
if (token.IsCancellationRequested) return;
|
||||||
_elements = Directory.GetFiles().Select(f => new LocalFile(f, this, Provider)).OrderBy(f => f.Name).ToList().AsReadOnly();
|
_elements = Directory.GetFiles().Select(f => new LocalFile(f, this, Provider)).OrderBy(f => f.Name).ToList().AsReadOnly();
|
||||||
|
|
||||||
if (token.IsCancellationRequested) return Task.CompletedTask;
|
if (token.IsCancellationRequested) return;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -82,9 +92,7 @@ namespace FileTime.Providers.Local
|
|||||||
}
|
}
|
||||||
|
|
||||||
_items = _containers.Cast<IItem>().Concat(_elements).ToList().AsReadOnly();
|
_items = _containers.Cast<IItem>().Concat(_elements).ToList().AsReadOnly();
|
||||||
Refreshed?.InvokeAsync(this, AsyncEventArgs.Empty, token);
|
if (Refreshed != null) await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
|
public async Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
|
||||||
@@ -176,5 +184,13 @@ namespace FileTime.Providers.Local
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpen() => Task.FromResult(true);
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_items = null;
|
||||||
|
_containers = null;
|
||||||
|
_elements = null;
|
||||||
|
IsDisposed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,6 +31,8 @@ namespace FileTime.Providers.Smb
|
|||||||
|
|
||||||
public bool SupportsDirectoryLevelSoftDelete => false;
|
public bool SupportsDirectoryLevelSoftDelete => false;
|
||||||
|
|
||||||
|
public bool IsDisposed => false;
|
||||||
|
|
||||||
public SmbContentProvider(IInputInterface inputInterface)
|
public SmbContentProvider(IInputInterface inputInterface)
|
||||||
{
|
{
|
||||||
_rootContainers = new List<IContainer>();
|
_rootContainers = new List<IContainer>();
|
||||||
@@ -102,5 +104,7 @@ namespace FileTime.Providers.Smb
|
|||||||
|
|
||||||
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() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,6 +19,8 @@ namespace FileTime.Providers.Smb
|
|||||||
public IContentProvider Provider { get; }
|
public IContentProvider Provider { get; }
|
||||||
private IContainer _parent;
|
private IContainer _parent;
|
||||||
|
|
||||||
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
public SmbFile(string name, SmbContentProvider provider, IContainer parent)
|
public SmbFile(string name, SmbContentProvider provider, IContainer parent)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
@@ -43,5 +45,9 @@ namespace FileTime.Providers.Smb
|
|||||||
}
|
}
|
||||||
|
|
||||||
public IContainer? GetParent() => _parent;
|
public IContainer? GetParent() => _parent;
|
||||||
|
public Task<string> GetContent(CancellationToken token = default) => Task.FromResult("NotImplemented");
|
||||||
|
public Task<long> GetElementSize(CancellationToken token = default) => Task.FromResult(-1L);
|
||||||
|
|
||||||
|
public void Dispose() => IsDisposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,6 +29,8 @@ namespace FileTime.Providers.Smb
|
|||||||
public AsyncEventHandler Refreshed { get; } = new();
|
public AsyncEventHandler Refreshed { get; } = new();
|
||||||
public IReadOnlyList<Exception> Exceptions { get; } = new List<Exception>().AsReadOnly();
|
public IReadOnlyList<Exception> Exceptions { get; } = new List<Exception>().AsReadOnly();
|
||||||
|
|
||||||
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
public bool SupportsDirectoryLevelSoftDelete => false;
|
public bool SupportsDirectoryLevelSoftDelete => false;
|
||||||
|
|
||||||
public SmbFolder(string name, SmbContentProvider contentProvider, SmbShare smbShare, IContainer parent)
|
public SmbFolder(string name, SmbContentProvider contentProvider, SmbShare smbShare, IContainer parent)
|
||||||
@@ -103,6 +105,14 @@ namespace FileTime.Providers.Smb
|
|||||||
_containers = containers.AsReadOnly();
|
_containers = containers.AsReadOnly();
|
||||||
_elements = elements.AsReadOnly();
|
_elements = elements.AsReadOnly();
|
||||||
|
|
||||||
|
if (_items != null)
|
||||||
|
{
|
||||||
|
foreach (var item in _items)
|
||||||
|
{
|
||||||
|
item.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_items = _containers.Cast<IItem>().Concat(_elements).ToList().AsReadOnly();
|
_items = _containers.Cast<IItem>().Concat(_elements).ToList().AsReadOnly();
|
||||||
await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
|
await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
|
||||||
}
|
}
|
||||||
@@ -123,5 +133,7 @@ namespace FileTime.Providers.Smb
|
|||||||
return _elements;
|
return _elements;
|
||||||
}
|
}
|
||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpen() => Task.FromResult(true);
|
||||||
|
|
||||||
|
public void Dispose() => IsDisposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,6 +40,8 @@ namespace FileTime.Providers.Smb
|
|||||||
|
|
||||||
public bool SupportsDirectoryLevelSoftDelete => false;
|
public bool SupportsDirectoryLevelSoftDelete => false;
|
||||||
|
|
||||||
|
public bool IsDisposed => false;
|
||||||
|
|
||||||
public SmbServer(string path, SmbContentProvider contentProvider, IInputInterface inputInterface)
|
public SmbServer(string path, SmbContentProvider contentProvider, IInputInterface inputInterface)
|
||||||
{
|
{
|
||||||
_inputInterface = inputInterface;
|
_inputInterface = inputInterface;
|
||||||
@@ -97,7 +99,7 @@ namespace FileTime.Providers.Smb
|
|||||||
|
|
||||||
_shares = shares.ConvertAll(s => new SmbShare(s, Provider, this, _smbClientContext)).AsReadOnly();
|
_shares = shares.ConvertAll(s => new SmbShare(s, Provider, this, _smbClientContext)).AsReadOnly();
|
||||||
_items = _shares.Cast<IItem>().ToList().AsReadOnly();
|
_items = _shares.Cast<IItem>().ToList().AsReadOnly();
|
||||||
await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty);
|
await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IContainer> Clone() => Task.FromResult((IContainer)this);
|
public Task<IContainer> Clone() => Task.FromResult((IContainer)this);
|
||||||
@@ -190,5 +192,7 @@ namespace FileTime.Providers.Smb
|
|||||||
|
|
||||||
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() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,6 +29,8 @@ namespace FileTime.Providers.Smb
|
|||||||
public AsyncEventHandler Refreshed { get; } = new();
|
public AsyncEventHandler Refreshed { get; } = new();
|
||||||
public IReadOnlyList<Exception> Exceptions { get; } = new List<Exception>().AsReadOnly();
|
public IReadOnlyList<Exception> Exceptions { get; } = new List<Exception>().AsReadOnly();
|
||||||
|
|
||||||
|
public bool IsDisposed => false;
|
||||||
|
|
||||||
public bool SupportsDirectoryLevelSoftDelete => false;
|
public bool SupportsDirectoryLevelSoftDelete => false;
|
||||||
|
|
||||||
public SmbShare(string name, SmbContentProvider contentProvider, IContainer parent, SmbClientContext smbClientContext)
|
public SmbShare(string name, SmbContentProvider contentProvider, IContainer parent, SmbClientContext smbClientContext)
|
||||||
@@ -111,6 +113,14 @@ namespace FileTime.Providers.Smb
|
|||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
|
if (_items != null)
|
||||||
|
{
|
||||||
|
foreach (var item in _items)
|
||||||
|
{
|
||||||
|
item.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_containers = containers.AsReadOnly();
|
_containers = containers.AsReadOnly();
|
||||||
_elements = elements.AsReadOnly();
|
_elements = elements.AsReadOnly();
|
||||||
|
|
||||||
@@ -160,5 +170,7 @@ namespace FileTime.Providers.Smb
|
|||||||
|
|
||||||
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() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user