Optimization, UI improvements, Run command

This commit is contained in:
2022-02-05 00:04:27 +01:00
parent bca940372a
commit c061f658aa
31 changed files with 579 additions and 212 deletions

View File

@@ -101,7 +101,7 @@ namespace FileTime.App.Core.Tab
: new List<AbsolutePath>().AsReadOnly();
}
public async Task MakrCurrentItem()
public async Task MarkCurrentItem()
{
var currentLocation = await Tab!.GetCurrentLocation();
if (currentLocation != null)

View File

@@ -67,7 +67,7 @@ namespace FileTime.ConsoleUI.App
{
if (_selectedTab != null)
{
await _tabStates[_selectedTab].MakrCurrentItem();
await _tabStates[_selectedTab].MarkCurrentItem();
}
}

View File

@@ -27,7 +27,7 @@ namespace FileTime.Core.Command
if (itemToRename != null)
{
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()!));
}
}

View File

@@ -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.
private string? _lastPath;
private readonly object _guardSetCurrentSelectedItemCTS = new object();
private bool _currentlySelecting = false;
private readonly object _guardSetCurrentSelectedItemCTS = new();
private CancellationTokenSource? _setCurrentSelectedItemCTS;
public int CurrentSelectedIndex { get; private set; }
@@ -54,6 +56,8 @@ namespace FileTime.Core.Components
public async Task SetCurrentSelectedItem(IItem? value, bool secondary = false)
{
if (_currentlySelecting) return;
if (_currentSelectedItem != value)
{
IItem? itemToSelect = null;
@@ -190,11 +194,13 @@ namespace FileTime.Core.Components
{
if (!currentPossibleItems.Any()) return;
var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
var currentLocation = await GetCurrentLocation();
var currentLocationItems = (await currentLocation.GetItems())!;
if (await GetCurrentSelectedItem() != null)
{
(await GetCurrentLocation())?.RefreshAsync();
_currentlySelecting = true;
currentLocation?.RefreshAsync();
IItem? newSelectedItem = null;
foreach (var item in currentPossibleItems)
@@ -212,6 +218,7 @@ namespace FileTime.Core.Components
newSelectedItem = (await (await GetCurrentLocation()).GetItems())?.FirstOrDefault(i => i.Name == newSelectedItem.Name);
}
_currentlySelecting = false;
await SetCurrentSelectedItem(newSelectedItem ?? (currentLocationItems.Count > 0 ? currentLocationItems[0] : null));
}
else
@@ -261,6 +268,11 @@ namespace FileTime.Core.Components
var newCurrentLocation = (await (await GetCurrentLocation()).GetItems())?.FirstOrDefault(i => i.Name == lastCurrentLocation.Name);
await SetCurrentSelectedItem(newCurrentLocation);
}
foreach(var lastLocationItem in currentLocationItems.OfType<IContainer>())
{
lastLocationItem.Dispose();
}
}
}

View File

@@ -4,5 +4,7 @@ namespace FileTime.Core.Models
{
bool IsSpecial { get; }
string GetPrimaryAttributeText();
Task<string> GetContent(CancellationToken token = default);
Task<long> GetElementSize(CancellationToken token = default);
}
}

View File

@@ -2,11 +2,12 @@ using FileTime.Core.Providers;
namespace FileTime.Core.Models
{
public interface IItem
public interface IItem : IDisposable
{
string Name { get; }
string? FullName { get; }
bool IsHidden { get; }
bool IsDisposed { get; }
SupportsDelete CanDelete { get; }
bool CanRename { get; }
IContentProvider Provider { get; }

View File

@@ -35,6 +35,8 @@ namespace FileTime.Core.Models
public AsyncEventHandler Refreshed { get; }
public bool IsDisposed => BaseContainer.IsDisposed;
private void RefreshAddBase(Func<object?, AsyncEventArgs, CancellationToken, Task> 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<bool> CanOpen() => await BaseContainer.CanOpen();
public void Dispose()
{
BaseContainer.Dispose();
}
}
}

View File

@@ -33,6 +33,8 @@ namespace FileTime.Core.Providers
public bool SupportsDirectoryLevelSoftDelete => false;
public bool IsDisposed => false;
public TopContainer(IEnumerable<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 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<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<bool> CanOpen() => Task.FromResult(true);
public void Dispose() { }
}
}

View File

@@ -29,6 +29,8 @@ namespace FileTime.Core.Timeline
public bool SupportsDirectoryLevelSoftDelete => false;
public bool IsDisposed { get; private set; }
public TimeContainer(string name, IContainer parent, IContentProvider contentProvider, IContentProvider virtualContentProvider, PointInTime pointInTime)
{
_parent = parent;
@@ -121,5 +123,7 @@ namespace FileTime.Core.Timeline
return new TimeElement(elementDiff.Name, this, Provider, elementDiff.AbsolutePath.VirtualContentProvider ?? elementDiff.AbsolutePath.ContentProvider);
}
public Task<bool> CanOpen() => Task.FromResult(true);
public void Dispose() => IsDisposed = true;
}
}

View File

@@ -31,6 +31,8 @@ namespace FileTime.Core.Timeline
public IContentProvider Provider { get; }
public IContentProvider VirtualProvider { get; }
public bool IsDisposed { get; private set; }
public Task Delete(bool hardDelete = false) => Task.CompletedTask;
public IContainer? GetParent() => _parent;
@@ -38,5 +40,10 @@ namespace FileTime.Core.Timeline
public string GetPrimaryAttributeText() => "";
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;
}
}

View File

@@ -28,6 +28,8 @@ namespace FileTime.Core.Timeline
public bool SupportsDirectoryLevelSoftDelete => false;
public bool IsDisposed => false;
public TimeProvider(PointInTime pointInTime)
{
_pointInTime = pointInTime;
@@ -90,5 +92,7 @@ namespace FileTime.Core.Timeline
public void SetParent(IContainer container) { }
public Task<bool> CanOpen() => Task.FromResult(true);
public void Dispose() { }
}
}

View File

@@ -12,6 +12,7 @@
<Color x:Key="AppBackgroundColor">#E7073642</Color>
<Color x:Key="ContainerBackgroundColor">#083e4c</Color>
<Color x:Key="TransparentContainerBackgroundColor">#D0083e4c</Color>
<Color x:Key="BarelyTransparentBackgroundColor">#80083e4c</Color>
<Color x:Key="ItemBackgroundColor">#00000000</Color>
<Color x:Key="AlternativeItemBackgroundColor">#10000000</Color>
@@ -128,6 +129,7 @@
<converters:IsEmptyConverter x:Key="IsNotEmptyConverter" Inverse="true"/>
<converters:ExceptionToStringConverter x:Key="ExceptionToStringConverter"/>
<converters:BoolInverter x:Key="BoolInverter"/>
<converters:IsElementConverter x:Key="IsElementConverter"/>
</ResourceDictionary>
</Application.Resources>
@@ -145,6 +147,10 @@
<Style Selector="TextBlock.ExtraSmallText">
<Setter Property="FontSize" Value="11"/>
</Style>
<Style Selector="TextBox">
<Setter Property="Foreground" Value="{DynamicResource ForegroundBrush}"/>
<Setter Property="Background" Value="{DynamicResource ContainerBackgroundBrush}"/>
</Style>
<Style Selector="ListBox.ContentListView">
<Setter Property="Background" Value="Transparent"/>
</Style>

View File

@@ -20,6 +20,7 @@ namespace FileTime.Avalonia.Application
[Inject(typeof(Tab))]
public partial class TabContainer : INewItemProcessor
{
private bool _updateFromCode;
[Property]
private TabState _tabState;
@@ -46,11 +47,13 @@ namespace FileTime.Avalonia.Application
get => _selectedItem;
set
{
if (value != null)
if (!_updateFromCode && value != null)
{
try
{
SetSelectedItemAsync(value, true).Wait();
/*var task = SetSelectedItemAsync(value, true);
Task.WaitAll(new Task[] { task }, 100);*/
SetSelectedItemAsync(value, true);
}
catch
{
@@ -60,14 +63,28 @@ namespace FileTime.Avalonia.Application
}
}
[Property]
private ElementPreviewViewModel? _elementPreview;
public async Task SetSelectedItemAsync(IItemViewModel? value, bool fromDataBinding = false)
{
if (_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);
OnPropertyChanged(nameof(SelectedItem));
}
}
@@ -174,7 +191,7 @@ namespace FileTime.Avalonia.Application
if (token.IsCancellationRequested) return;
var items = await _currentLocation.GetItems();
var items = await _currentLocation.GetItems(token);
if (items?.Count > 0)
{
foreach (var item in items)
@@ -198,7 +215,7 @@ namespace FileTime.Avalonia.Application
{
if (token.IsCancellationRequested) return;
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)
{
child.IsSelected = true;
@@ -239,59 +256,71 @@ namespace FileTime.Avalonia.Application
catch { }
}
private async Task RunFromCode(Func<Task> task)
{
_updateFromCode = true;
try
{
await task();
}
catch
{
_updateFromCode = false;
throw;
}
}
public async Task Open()
{
if (ChildContainer != null)
{
await Tab.Open();
await UpdateCurrentSelectedItem();
await RunFromCode(Tab.Open);
}
}
public async Task GoUp()
{
await Tab.GoUp();
await UpdateCurrentSelectedItem();
await RunFromCode(Tab.GoUp);
}
public async Task MoveCursorDown()
{
await Tab.SelectNextItem();
await RunFromCode(async () => await Tab.SelectNextItem());
}
public async Task MoveCursorDownPage()
{
await Tab.SelectNextItem(10);
await RunFromCode(async () => await Tab.SelectNextItem(10));
}
public async Task MoveCursorUp()
{
await Tab.SelectPreviousItem();
await RunFromCode(async () => await Tab.SelectPreviousItem());
}
public async Task MoveCursorUpPage()
{
await Tab.SelectPreviousItem(10);
await RunFromCode(async () => await Tab.SelectPreviousItem(10));
}
public async Task MoveCursorToFirst()
{
await Tab.SelectFirstItem();
await RunFromCode(Tab.SelectFirstItem);
}
public async Task MoveCursorToLast()
{
await Tab.SelectLastItem();
await RunFromCode(Tab.SelectLastItem);
}
public async Task GotToProvider()
{
await Tab.GoToProvider();
await RunFromCode(Tab.GoToProvider);
}
public async Task GotToRoot()
{
await Tab.GoToRoot();
await RunFromCode(Tab.GoToRoot);
}
public async Task GotToHome()
@@ -300,28 +329,42 @@ namespace FileTime.Avalonia.Application
var resolvedPath = await LocalContentProvider.GetByPath(path);
if (resolvedPath is IContainer homeFolder)
{
await Tab.OpenContainer(homeFolder);
await OpenContainer(homeFolder);
}
}
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)
{
(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)
{
await Tab.OpenContainer(container);
await RunFromCode(async () => await Tab.OpenContainer(container));
}
public async Task MarkCurrentItem()
{
await _tabState.MakrCurrentItem();
await _tabState.MarkCurrentItem();
}
public async Task UpdateMarkedItems(ContainerViewModel containerViewModel, CancellationToken token = default)

View File

@@ -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;
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;
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;

View File

@@ -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();
}
}
}

View File

@@ -1,21 +1,20 @@
using FileTime.Core.Interactions;
using System;
using System.Collections.Generic;
using System.Text;
namespace FileTime.Avalonia.Misc
{
public class InputElementWrapper
{
public InputElement InputElement { get; }
public string Value { get; set; }
public char? PasswordChar { get; set; }
public InputElementWrapper(InputElement inputElement, string? defaultValue = null)
{
InputElement = inputElement;
Value = defaultValue ?? "";
PasswordChar = inputElement.InputType == InputType.Password ? '*' : null;
}
}
}

View File

@@ -0,0 +1,9 @@
namespace FileTime.Avalonia.Models
{
public enum ElementPreviewMode
{
Unknown,
Text,
Empty
}
}

View File

@@ -60,6 +60,12 @@ namespace FileTime.Avalonia
{
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
}
#if DEBUG
catch
{
throw;
}
#else
catch (Exception e)
{
var message = $"Ciritcal error cought in {nameof(Program)}";
@@ -79,6 +85,7 @@ namespace FileTime.Avalonia
using var streamWriter = new StreamWriter(fileWriter);
streamWriter.WriteLine(DateTime.Now.ToString() + ": " + message + "\n" + e.ToString() + "\n\n");
}
#endif
}
// Avalonia configuration, don't remove; also used by visual designer.

View File

@@ -133,12 +133,13 @@ namespace FileTime.Avalonia.ViewModels
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)
{
await Refresh(false, token);
await Refresh(false, false, token);
}
[Obsolete($"Use the parametrizable version of {nameof(Refresh)}.")]
@@ -146,7 +147,7 @@ namespace FileTime.Avalonia.ViewModels
{
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;
@@ -157,18 +158,13 @@ namespace FileTime.Avalonia.ViewModels
{
_isRefreshing = true;
var containers = (await _container.GetContainers())!.Select(c => AdoptOrReuseOrCreateItem(c, (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 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, alloweReuse , (e2) => new ElementViewModel(e2, this, ItemNameConverterService))).ToList();
if (token.IsCancellationRequested) return;
Exceptions = new List<Exception>(_container.Exceptions);
foreach (var containerToRemove in _containers.Except(containers))
{
containerToRemove?.Dispose();
}
if (initializeChildren)
{
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);
Elements = new ObservableCollection<ElementViewModel>(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)
{
@@ -197,13 +232,31 @@ namespace FileTime.Avalonia.ViewModels
_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);
if (itemToAdopt is TResult itemViewModel) return itemViewModel;
if (allowResuse)
{
var existingViewModel = _items?.FirstOrDefault(i => i.Item == item);
if (existingViewModel is TResult itemViewModelToReuse) return itemViewModelToReuse;
}
return generator(item);
}
@@ -221,26 +274,26 @@ namespace FileTime.Avalonia.ViewModels
}
}
_containers = new ObservableCollection<ContainerViewModel>();
_elements = new ObservableCollection<ElementViewModel>();
_items = new ObservableCollection<IItemViewModel>();
_containers.Clear();
_elements.Clear();
_items.Clear();
}
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;
}
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;
}
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;
}

View File

@@ -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;
}
}
}
}

View File

@@ -74,10 +74,10 @@ namespace FileTime.Avalonia.ViewModels
private string _messageBoxText;
[Property]
private ObservableCollection<string> _popupTexts = new ObservableCollection<string>();
private ObservableCollection<string> _popupTexts = new();
[Property]
private bool _showAllShortcut;
private bool _isAllShortcutVisible;
[Property]
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;
}
@@ -757,14 +806,14 @@ namespace FileTime.Avalonia.ViewModels
_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
|| key == Key.RightAlt
|| key == Key.LeftShift
|| key == Key.RightShift
|| key == Key.LeftCtrl
|| key == Key.RightCtrl) return false;
|| key == Key.RightCtrl) return;
NoCommandFound = false;
@@ -782,12 +831,14 @@ namespace FileTime.Avalonia.ViewModels
if (key == Key.Escape)
{
ShowAllShortcut = false;
IsAllShortcutVisible = false;
_previousKeys.Clear();
PossibleCommands = new();
setHandled(true);
}
else if (selectedCommandBinding != null)
{
setHandled(true);
await selectedCommandBinding.InvokeAsync();
_previousKeys.Clear();
PossibleCommands = new();
@@ -796,10 +847,11 @@ namespace FileTime.Avalonia.ViewModels
{
_previousKeys.Clear();
PossibleCommands = new();
return false;
return;
}
else if (_previousKeys.Count == 2)
{
setHandled(true);
NoCommandFound = true;
_previousKeys.Clear();
PossibleCommands = new();
@@ -817,6 +869,7 @@ namespace FileTime.Avalonia.ViewModels
{
PossibleCommands = possibleCommands;
}
setHandled(true);
}
}
else
@@ -826,9 +879,10 @@ namespace FileTime.Avalonia.ViewModels
if (key == Key.Escape)
{
if (ShowAllShortcut)
setHandled(true);
if (IsAllShortcutVisible)
{
ShowAllShortcut = false;
IsAllShortcutVisible = false;
}
else
{
@@ -839,12 +893,14 @@ namespace FileTime.Avalonia.ViewModels
{
if (AppState.RapidTravelText.Length > 0)
{
setHandled(true);
AppState.RapidTravelText = AppState.RapidTravelText.Substring(0, AppState.RapidTravelText.Length - 1);
updateRapidTravelFilter = true;
}
}
else if (keyString.Length == 1)
{
setHandled(true);
AppState.RapidTravelText += keyString.ToString().ToLower();
updateRapidTravelFilter = true;
}
@@ -854,12 +910,8 @@ namespace FileTime.Avalonia.ViewModels
var selectedCommandBinding = _universalCommandBindings.Find(c => AreKeysEqual(c.Keys, currentKeyAsList));
if (selectedCommandBinding != null)
{
setHandled(true);
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)
@@ -1144,7 +1189,12 @@ namespace FileTime.Avalonia.ViewModels
"show all shortcut",
FileTime.App.Core.Command.Commands.Dummy,
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
new CommandBinding(
"open in default file browser",

View File

@@ -6,12 +6,12 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:FileTime.Avalonia.ViewModels"
xmlns:local="using:FileTime.Avalonia.Views"
xmlns:models="using:FileTime.Avalonia.Models"
Title="FileTime"
d:DesignHeight="450"
d:DesignWidth="800"
Icon="/Assets/filetime.ico"
InputElement.KeyDown="OnKeyDown"
InputElement.KeyUp="OnKeyUp"
TransparencyLevelHint="Blur"
Background="Transparent"
ExtendClientAreaToDecorationsHint="True"
@@ -269,123 +269,65 @@
VerticalAlignment="Stretch"
Fill="{DynamicResource ContentSeparatorBrush}" />
<Grid Grid.Column="4" IsVisible="{Binding AppState.SelectedTab.ChildContainer,Converter={StaticResource IsNotNullConverter}}">
<ListBox
Classes="ContentListView"
IsEnabled="False"
x:Name="ChildItems"
Items="{Binding AppState.SelectedTab.ChildContainer.Items}"
IsVisible="{Binding AppState.SelectedTab.ChildContainer.Items.Count, Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}">
<ListBox.ItemTemplate>
<DataTemplate>
<local:ItemView/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Grid Grid.Column="4">
<Grid IsVisible="{Binding AppState.SelectedTab.ChildContainer,Converter={StaticResource IsNotNullConverter}}">
<ListBox
Classes="ContentListView"
IsEnabled="False"
x:Name="ChildItems"
Items="{Binding AppState.SelectedTab.ChildContainer.Items}"
IsVisible="{Binding AppState.SelectedTab.ChildContainer.Items.Count, Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}">
<ListBox.ItemTemplate>
<DataTemplate>
<local:ItemView/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Grid
IsVisible="{Binding AppState.SelectedTab.ChildContainer.Items.Count, Converter={StaticResource EqualityConverter}, ConverterParameter=0}">
<TextBlock
x:Name="ChildEmpty"
Margin="10"
HorizontalAlignment="Center"
FontWeight="Bold"
Foreground="{DynamicResource ErrorBrush}"
IsVisible="{Binding AppState.SelectedTab.ChildContainer.Exceptions.Count, Converter={StaticResource EqualityConverter}, ConverterParameter=0}">
Empty
</TextBlock>
<Grid
RowDefinitions="Auto, Auto"
IsVisible="{Binding AppState.SelectedTab.ChildContainer.Exceptions.Count, Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}">
<Grid IsVisible="{Binding AppState.SelectedTab.ChildContainer.Items.Count, Converter={StaticResource EqualityConverter}, ConverterParameter=0}">
<TextBlock
Margin="0,0,0,10"
x:Name="ChildEmpty"
Margin="10"
HorizontalAlignment="Center"
TextWrapping="Wrap"
Text="There were some errors while opening container."
Foreground="{DynamicResource ErrorBrush}" />
FontWeight="Bold"
Foreground="{DynamicResource ErrorBrush}"
IsVisible="{Binding AppState.SelectedTab.ChildContainer.Exceptions.Count, Converter={StaticResource EqualityConverter}, ConverterParameter=0}">
Empty
</TextBlock>
<ItemsRepeater Grid.Row="1" Items="{Binding AppState.SelectedTab.ChildContainer.Exceptions}">
<ItemsRepeater.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding, Converter={StaticResource ExceptionToStringConverter}}"/>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsVisible="{Binding Inputs, Converter={StaticResource IsNotNullConverter}}">
<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>
<Grid
RowDefinitions="Auto, Auto"
IsVisible="{Binding AppState.SelectedTab.ChildContainer.Exceptions.Count, Converter={StaticResource NotEqualsConverter}, ConverterParameter=0}">
<TextBlock
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding InputElement.Text}" />
<TextBox
AttachedToVisualTree="InputText_AttachedToVisualTree"
Grid.Column="1"
GotFocus="InputText_GotFocus"
LostFocus="InputText_LostFocus"
KeyDown="InputText_KeyDown"
Text="{Binding Value, Mode=TwoWay}" />
Margin="0,0,0,10"
HorizontalAlignment="Center"
TextWrapping="Wrap"
Text="There were some errors while opening container."
Foreground="{DynamicResource ErrorBrush}" />
<ItemsRepeater Grid.Row="1" Items="{Binding AppState.SelectedTab.ChildContainer.Exceptions}">
<ItemsRepeater.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding, Converter={StaticResource ExceptionToStringConverter}}"/>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
</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>
<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 IsVisible="{Binding AppState.SelectedTab.ElementPreview, Converter={StaticResource IsNotNullConverter}}">
<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}}"/>
<TextBlock HorizontalAlignment="Center" Text="Empty" IsVisible="{Binding AppState.SelectedTab.ElementPreview.Mode, Converter={StaticResource EqualityConverter}, ConverterParameter={x:Static models:ElementPreviewMode.Empty}}"/>
<ScrollViewer IsVisible="{Binding AppState.SelectedTab.ElementPreview.Mode, Converter={StaticResource EqualityConverter}, ConverterParameter={x:Static models:ElementPreviewMode.Text}}">
<TextBox
IsReadOnly="True"
Text="{Binding AppState.SelectedTab.ElementPreview.TextContent}" />
</ScrollViewer>
</Grid>
</Grid>
</Grid>
<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>
<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">
<TextBlock Text="Shortcuts" Margin="0,0,0,20"/>
<ScrollViewer

View File

@@ -45,19 +45,11 @@ namespace FileTime.Avalonia.Views
AvaloniaXamlLoader.Load(this);
}
public async void OnKeyDown(object sender, KeyEventArgs e)
public void OnKeyDown(object sender, KeyEventArgs e)
{
if (_inputElementWrapper == null)
{
e.Handled = e.Handled || await ViewModel!.ProcessKeyDown(e.Key, e.KeyModifiers);
}
}
public async void OnKeyUp(object sender, KeyEventArgs e)
{
if (_inputElementWrapper == null)
{
e.Handled = e.Handled || await ViewModel!.ProcessKeyUp(e.Key, e.KeyModifiers);
ViewModel!.ProcessKeyDown(e.Key, e.KeyModifiers, h => e.Handled = h);
}
}
@@ -87,7 +79,7 @@ namespace FileTime.Avalonia.Views
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;
}

View File

@@ -33,6 +33,8 @@ namespace FileTime.Providers.Local
public bool SupportsDirectoryLevelSoftDelete => false;
public bool IsDisposed => false;
public LocalContentProvider(ILogger<LocalContentProvider> logger)
{
_logger = logger;
@@ -100,5 +102,7 @@ namespace FileTime.Providers.Local
public Task Rename(string newName) => throw new NotSupportedException();
public Task<bool> CanOpen() => Task.FromResult(true);
public void Dispose() { }
}
}

View File

@@ -30,6 +30,8 @@ namespace FileTime.Providers.Local
private readonly LocalFolder _parent;
public bool IsDisposed { get; private set; }
public LocalFile(FileInfo file, LocalFolder parent, IContentProvider contentProvider)
{
_parent = parent;
@@ -80,5 +82,10 @@ namespace FileTime.Providers.Local
}
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;
}
}

View File

@@ -11,7 +11,7 @@ namespace FileTime.Providers.Local
private IReadOnlyList<IItem>? _items;
private IReadOnlyList<IContainer>? _containers;
private IReadOnlyList<IElement>? _elements;
private List<Exception> _exceptions;
private readonly List<Exception> _exceptions;
private readonly IContainer? _parent;
public bool IsHidden => (Directory.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
@@ -34,6 +34,8 @@ namespace FileTime.Providers.Local
public DateTime CreatedAt => Directory.CreationTime;
public IReadOnlyList<Exception> Exceptions { get; }
public bool IsDisposed { get; private set; }
public bool SupportsDirectoryLevelSoftDelete { get; }
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 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>();
_elements = new List<IElement>();
_items = new List<IItem>();
@@ -68,13 +78,13 @@ namespace FileTime.Providers.Local
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();
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();
if (token.IsCancellationRequested) return Task.CompletedTask;
if (token.IsCancellationRequested) return;
}
catch (Exception e)
{
@@ -82,9 +92,7 @@ namespace FileTime.Providers.Local
}
_items = _containers.Cast<IItem>().Concat(_elements).ToList().AsReadOnly();
Refreshed?.InvokeAsync(this, AsyncEventArgs.Empty, token);
return Task.CompletedTask;
if (Refreshed != null) await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
}
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 void Dispose()
{
_items = null;
_containers = null;
_elements = null;
IsDisposed = true;
}
}
}

View File

@@ -31,6 +31,8 @@ namespace FileTime.Providers.Smb
public bool SupportsDirectoryLevelSoftDelete => false;
public bool IsDisposed => false;
public SmbContentProvider(IInputInterface inputInterface)
{
_rootContainers = new List<IContainer>();
@@ -102,5 +104,7 @@ namespace FileTime.Providers.Smb
public Task Rename(string newName) => throw new NotSupportedException();
public Task<bool> CanOpen() => Task.FromResult(true);
public void Dispose() { }
}
}

View File

@@ -19,6 +19,8 @@ namespace FileTime.Providers.Smb
public IContentProvider Provider { get; }
private IContainer _parent;
public bool IsDisposed { get; private set; }
public SmbFile(string name, SmbContentProvider provider, IContainer parent)
{
Name = name;
@@ -43,5 +45,9 @@ namespace FileTime.Providers.Smb
}
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;
}
}

View File

@@ -29,6 +29,8 @@ namespace FileTime.Providers.Smb
public AsyncEventHandler Refreshed { get; } = new();
public IReadOnlyList<Exception> Exceptions { get; } = new List<Exception>().AsReadOnly();
public bool IsDisposed { get; private set; }
public bool SupportsDirectoryLevelSoftDelete => false;
public SmbFolder(string name, SmbContentProvider contentProvider, SmbShare smbShare, IContainer parent)
@@ -103,6 +105,14 @@ namespace FileTime.Providers.Smb
_containers = containers.AsReadOnly();
_elements = elements.AsReadOnly();
if (_items != null)
{
foreach (var item in _items)
{
item.Dispose();
}
}
_items = _containers.Cast<IItem>().Concat(_elements).ToList().AsReadOnly();
await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
}
@@ -123,5 +133,7 @@ namespace FileTime.Providers.Smb
return _elements;
}
public Task<bool> CanOpen() => Task.FromResult(true);
public void Dispose() => IsDisposed = true;
}
}

View File

@@ -40,6 +40,8 @@ namespace FileTime.Providers.Smb
public bool SupportsDirectoryLevelSoftDelete => false;
public bool IsDisposed => false;
public SmbServer(string path, SmbContentProvider contentProvider, IInputInterface inputInterface)
{
_inputInterface = inputInterface;
@@ -97,7 +99,7 @@ namespace FileTime.Providers.Smb
_shares = shares.ConvertAll(s => new SmbShare(s, Provider, this, _smbClientContext)).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);
@@ -190,5 +192,7 @@ namespace FileTime.Providers.Smb
public Task Rename(string newName) => throw new NotSupportedException();
public Task<bool> CanOpen() => Task.FromResult(true);
public void Dispose() { }
}
}

View File

@@ -29,6 +29,8 @@ namespace FileTime.Providers.Smb
public AsyncEventHandler Refreshed { get; } = new();
public IReadOnlyList<Exception> Exceptions { get; } = new List<Exception>().AsReadOnly();
public bool IsDisposed => false;
public bool SupportsDirectoryLevelSoftDelete => false;
public SmbShare(string name, SmbContentProvider contentProvider, IContainer parent, SmbClientContext smbClientContext)
@@ -111,6 +113,14 @@ namespace FileTime.Providers.Smb
}
catch { }
if (_items != null)
{
foreach (var item in _items)
{
item.Dispose();
}
}
_containers = containers.AsReadOnly();
_elements = elements.AsReadOnly();
@@ -160,5 +170,7 @@ namespace FileTime.Providers.Smb
public Task Rename(string newName) => throw new NotSupportedException();
public Task<bool> CanOpen() => Task.FromResult(true);
public void Dispose() { }
}
}