New reactive core WIP
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
|
||||
<PackageReference Include="morelinq" Version="3.4.2" />
|
||||
<PackageReference Include="MvvmGen" Version="1.2.1" />
|
||||
<PackageReference Include="ObservableComputations" Version="2.3.0" />
|
||||
<PackageReference Include="System.Interactive.Async" Version="6.0.1" />
|
||||
<PackageReference Include="System.Reactive" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
@@ -21,6 +22,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Core\FileTime.Core.Abstraction\FileTime.Core.Abstraction.csproj" />
|
||||
<ProjectReference Include="..\..\Core\FileTime.Core.Models\FileTime.Core.Models.csproj" />
|
||||
<ProjectReference Include="..\..\Library\Defer\Defer.csproj" />
|
||||
<ProjectReference Include="..\..\Providers\FileTime.Providers.Local.Abstractions\FileTime.Providers.Local.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\..\Tools\FileTime.Tools\FileTime.Tools.csproj" />
|
||||
<ProjectReference Include="..\FileTime.App.CommandPalette.Abstractions\FileTime.App.CommandPalette.Abstractions.csproj" />
|
||||
|
||||
@@ -81,7 +81,7 @@ public class TabPersistenceService : ITabPersistenceService
|
||||
{
|
||||
if (!File.Exists(_settingsPath))
|
||||
{
|
||||
CreateEmptyTab();
|
||||
await CreateEmptyTab();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -99,12 +99,12 @@ public class TabPersistenceService : ITabPersistenceService
|
||||
_logger.LogError(e, "Unknown exception while restoring app state");
|
||||
}
|
||||
|
||||
CreateEmptyTab();
|
||||
await CreateEmptyTab();
|
||||
|
||||
void CreateEmptyTab()
|
||||
async Task CreateEmptyTab()
|
||||
{
|
||||
var tab = _serviceProvider.GetInitableResolver<IContainer>(_localContentProvider)
|
||||
.GetRequiredService<ITab>();
|
||||
var tab = await _serviceProvider.GetAsyncInitableResolver<IContainer>(_localContentProvider)
|
||||
.GetRequiredServiceAsync<ITab>();
|
||||
var tabViewModel = _serviceProvider.GetInitableResolver(tab, 1).GetRequiredService<ITabViewModel>();
|
||||
|
||||
_appState.AddTab(tabViewModel);
|
||||
@@ -159,8 +159,8 @@ public class TabPersistenceService : ITabPersistenceService
|
||||
|
||||
if (container == null) continue;
|
||||
|
||||
var tabToLoad = _serviceProvider.GetInitableResolver(container)
|
||||
.GetRequiredService<ITab>();
|
||||
var tabToLoad = await _serviceProvider.GetAsyncInitableResolver(container)
|
||||
.GetRequiredServiceAsync<ITab>();
|
||||
var tabViewModel = _serviceProvider.GetInitableResolver(tabToLoad, tab.Number)
|
||||
.GetRequiredService<ITabViewModel>();
|
||||
|
||||
@@ -205,7 +205,7 @@ public class TabPersistenceService : ITabPersistenceService
|
||||
var tabStates = new List<TabState>();
|
||||
foreach (var tab in _appState.Tabs)
|
||||
{
|
||||
var currentLocation = tab.CachedCurrentLocation;
|
||||
var currentLocation = tab.CurrentLocation.Value;
|
||||
if (currentLocation is null) continue;
|
||||
tabStates.Add(new TabState(currentLocation.FullName!, tab.TabNumber));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace FileTime.App.Core.Services;
|
||||
|
||||
public sealed class RefreshSmoothnessCalculator : IRefreshSmoothnessCalculator, INotifyPropertyChanged
|
||||
{
|
||||
private const int MaxSampleTimeInSeconds = 10;
|
||||
private const int MaxDelayBetweenRefreshes = 400;
|
||||
private readonly TimeSpan _maxDelay = TimeSpan.FromSeconds(MaxSampleTimeInSeconds);
|
||||
private readonly TimeSpan _defaultRefreshDelay = TimeSpan.FromMilliseconds(200);
|
||||
private readonly Queue<DateTime> _changeTimes = new();
|
||||
private readonly object _lock = new();
|
||||
private TimeSpan _refreshDelay;
|
||||
|
||||
public TimeSpan RefreshDelay
|
||||
{
|
||||
get => _refreshDelay;
|
||||
private set
|
||||
{
|
||||
if (value.Equals(_refreshDelay)) return;
|
||||
_refreshDelay = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public RefreshSmoothnessCalculator()
|
||||
{
|
||||
_refreshDelay = _defaultRefreshDelay;
|
||||
}
|
||||
|
||||
public void RegisterChange() => RegisterChange(DateTime.Now);
|
||||
|
||||
public void RegisterChange(DateTime changeTime)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_changeTimes.Count > 0 && Math.Abs((_changeTimes.Last() - changeTime).TotalMilliseconds) < 5) return;
|
||||
CleanList(DateTime.Now);
|
||||
_changeTimes.Enqueue(changeTime);
|
||||
}
|
||||
}
|
||||
|
||||
private void CleanList(DateTime now)
|
||||
{
|
||||
while (_changeTimes.Count > 0)
|
||||
{
|
||||
var item = _changeTimes.Peek();
|
||||
if (now - item < _maxDelay) return;
|
||||
_changeTimes.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
public void RecalculateSmoothness()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_changeTimes.Count < 2)
|
||||
{
|
||||
RefreshDelay = _defaultRefreshDelay;
|
||||
return;
|
||||
}
|
||||
|
||||
var now = DateTime.Now;
|
||||
CleanList(now);
|
||||
|
||||
var queue = new Queue<DateTime>(_changeTimes);
|
||||
var values = new List<(double score, double weight)>(queue.Count - 1);
|
||||
|
||||
var previousChangeTime = queue.Dequeue();
|
||||
var biggestDelay = now - previousChangeTime;
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
var changeTime = queue.Dequeue();
|
||||
|
||||
var (score, weight) = CalculateScoreAndWeight(changeTime, previousChangeTime, biggestDelay);
|
||||
|
||||
values.Add((score, weight));
|
||||
|
||||
previousChangeTime = changeTime;
|
||||
}
|
||||
|
||||
var combinedScore = values.Sum(i => i.weight * i.score) / values.Sum(i => i.weight);
|
||||
|
||||
var normalizedCombinedScore = (combinedScore * 1.2 - 0.1);
|
||||
|
||||
if (normalizedCombinedScore < 0) normalizedCombinedScore = 0;
|
||||
else if (normalizedCombinedScore > 1) normalizedCombinedScore = 1;
|
||||
|
||||
var finalDelay = normalizedCombinedScore * MaxDelayBetweenRefreshes;
|
||||
|
||||
RefreshDelay = TimeSpan.FromMilliseconds(finalDelay);
|
||||
|
||||
(double score, double weight) CalculateScoreAndWeight(DateTime changeTime, DateTime previousChangeTime, TimeSpan biggestDelay)
|
||||
{
|
||||
var delayToPrevious = changeTime - previousChangeTime;
|
||||
var delayToNow = now - changeTime;
|
||||
|
||||
var toNowRatio = (delayToNow.TotalMilliseconds / biggestDelay.TotalMilliseconds);
|
||||
var score = 1 - (delayToPrevious.TotalMilliseconds / biggestDelay.TotalMilliseconds);
|
||||
var weight = 1 - toNowRatio;
|
||||
|
||||
if (score < 0) score = 0;
|
||||
else if (score > 1) score = 1;
|
||||
|
||||
return (score, weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||||
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Text.RegularExpressions;
|
||||
using DeclarativeProperty;
|
||||
using DynamicData;
|
||||
using FileTime.App.Core.Interactions;
|
||||
using FileTime.App.Core.Models;
|
||||
@@ -25,7 +26,7 @@ namespace FileTime.App.Core.Services.UserCommandHandler;
|
||||
public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
{
|
||||
private ITabViewModel? _selectedTab;
|
||||
private IItemViewModel? _currentSelectedItem;
|
||||
private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem;
|
||||
private readonly IUserCommandHandlerService _userCommandHandlerService;
|
||||
private readonly IClipboardService _clipboardService;
|
||||
private readonly IUserCommunicationService _userCommunicationService;
|
||||
@@ -35,7 +36,7 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ISystemClipboardService _systemClipboardService;
|
||||
private readonly BindedCollection<FullName>? _markedItems;
|
||||
private IContainer? _currentLocation;
|
||||
private IDeclarativeProperty<IContainer?>? _currentLocation;
|
||||
|
||||
public ItemManipulationUserCommandHandlerService(
|
||||
IAppState appState,
|
||||
@@ -84,7 +85,7 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
|
||||
{
|
||||
list.AddRange(_markedItems!.Collection!);
|
||||
}
|
||||
else if(_currentSelectedItem?.BaseItem?.FullName is { } selectedItemName)
|
||||
else if(_currentSelectedItem?.Value?.BaseItem?.FullName is { } selectedItemName)
|
||||
{
|
||||
list.Add(selectedItemName);
|
||||
}
|
||||
@@ -106,20 +107,20 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
|
||||
|
||||
private async Task PasteFilesFromClipboardAsync(TransportMode mode)
|
||||
{
|
||||
if (_currentLocation?.FullName is not { }) return;
|
||||
if (_currentLocation?.Value?.FullName is not { }) return;
|
||||
|
||||
var files = (await _systemClipboardService.GetFilesAsync()).ToList();
|
||||
var copyCommandFactory = _serviceProvider.GetRequiredService<FileTime.Core.Command.Copy.CopyCommandFactory>();
|
||||
var copyCommand = copyCommandFactory.GenerateCommand(files, mode, _currentLocation.FullName);
|
||||
var copyCommand = copyCommandFactory.GenerateCommand(files, mode, _currentLocation.Value.FullName);
|
||||
|
||||
await AddCommandAsync(copyCommand);
|
||||
}
|
||||
|
||||
private async Task MarkItemAsync()
|
||||
{
|
||||
if (_selectedTab == null || _currentSelectedItem?.BaseItem?.FullName == null) return;
|
||||
if (_selectedTab == null || _currentSelectedItem?.Value?.BaseItem?.FullName == null) return;
|
||||
|
||||
_selectedTab.ToggleMarkedItem(_currentSelectedItem.BaseItem.FullName);
|
||||
_selectedTab.ToggleMarkedItem(_currentSelectedItem.Value.BaseItem.FullName);
|
||||
await _userCommandHandlerService.HandleCommandAsync(MoveCursorDownCommand.Instance);
|
||||
}
|
||||
|
||||
@@ -137,9 +138,9 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
|
||||
|
||||
_selectedTab?.ClearMarkedItems();
|
||||
}
|
||||
else if (_currentSelectedItem?.BaseItem != null)
|
||||
else if (_currentSelectedItem?.Value?.BaseItem != null)
|
||||
{
|
||||
var item = _currentSelectedItem.BaseItem;
|
||||
var item = _currentSelectedItem.Value.BaseItem;
|
||||
_clipboardService.AddContent(
|
||||
item.FullName
|
||||
?? throw new ArgumentException($"{nameof(item.FullName)} can not be null.", nameof(item))
|
||||
@@ -168,7 +169,7 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
|
||||
|
||||
//TODO: check _currentLocation?.FullName
|
||||
var commandFactory = (ITransportationCommandFactory) _serviceProvider.GetRequiredService(_clipboardService.CommandFactoryType);
|
||||
var command = commandFactory.GenerateCommand(_clipboardService.Content, mode, _currentLocation?.FullName);
|
||||
var command = commandFactory.GenerateCommand(_clipboardService.Content, mode, _currentLocation?.Value?.FullName);
|
||||
|
||||
_clipboardService.Clear();
|
||||
|
||||
@@ -186,10 +187,10 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
|
||||
//TODO: message on empty result
|
||||
var newContainerName = containerNameInput.Value;
|
||||
|
||||
if (_currentLocation?.FullName is null || newContainerName is null) return;
|
||||
if (_currentLocation?.Value?.FullName is null || newContainerName is null) return;
|
||||
|
||||
var command = _serviceProvider
|
||||
.GetInitableResolver(_currentLocation.FullName, newContainerName)
|
||||
.GetInitableResolver(_currentLocation.Value.FullName, newContainerName)
|
||||
.GetRequiredService<CreateContainerCommand>();
|
||||
await AddCommandAsync(command);
|
||||
}
|
||||
@@ -203,10 +204,10 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
|
||||
//TODO: message on empty result
|
||||
var newContainerName = containerNameInput.Value;
|
||||
|
||||
if (_currentLocation?.FullName is null || newContainerName is null) return;
|
||||
if (_currentLocation?.Value?.FullName is null || newContainerName is null) return;
|
||||
|
||||
var command = _serviceProvider
|
||||
.GetInitableResolver(_currentLocation.FullName, newContainerName)
|
||||
.GetInitableResolver(_currentLocation.Value.FullName, newContainerName)
|
||||
.GetRequiredService<FileTime.Core.Command.CreateElement.CreateElementCommand>();
|
||||
await AddCommandAsync(command);
|
||||
}
|
||||
@@ -342,9 +343,9 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_currentSelectedItem?.BaseItem?.FullName is null) return;
|
||||
if (_currentSelectedItem?.Value?.BaseItem?.FullName is null) return;
|
||||
|
||||
var item = await _timelessContentProvider.GetItemByFullNameAsync(_currentSelectedItem.BaseItem.FullName, PointInTime.Present);
|
||||
var item = await _timelessContentProvider.GetItemByFullNameAsync(_currentSelectedItem.Value.BaseItem.FullName, PointInTime.Present);
|
||||
|
||||
if (item is null) return;
|
||||
|
||||
@@ -410,11 +411,11 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
|
||||
{
|
||||
itemsToDelete = new List<FullName>(_markedItems!.Collection!);
|
||||
}
|
||||
else if (_currentSelectedItem?.BaseItem?.FullName is not null)
|
||||
else if (_currentSelectedItem?.Value?.BaseItem?.FullName is not null)
|
||||
{
|
||||
itemsToDelete = new List<FullName>()
|
||||
{
|
||||
_currentSelectedItem.BaseItem.FullName
|
||||
_currentSelectedItem.Value.BaseItem.FullName
|
||||
};
|
||||
}
|
||||
|
||||
@@ -425,7 +426,7 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
|
||||
var resolvedOnlyItem = await _timelessContentProvider.GetItemByFullNameAsync(itemsToDelete[0], PointInTime.Present);
|
||||
|
||||
if (resolvedOnlyItem is IContainer {AllowRecursiveDeletion: true} onlyContainer
|
||||
&& onlyContainer.ItemsCollection.Any())
|
||||
&& onlyContainer.Items.Count > 0)
|
||||
{
|
||||
questionText = $"The container '{onlyContainer.DisplayName}' is not empty. Proceed with delete?";
|
||||
}
|
||||
@@ -461,8 +462,6 @@ public class ItemManipulationUserCommandHandlerService : UserCommandHandlerServi
|
||||
_selectedTab?.ClearMarkedItems();
|
||||
}
|
||||
|
||||
private async Task AddCommandAsync(ICommand command)
|
||||
{
|
||||
await _commandScheduler.AddCommand(command);
|
||||
}
|
||||
private async Task AddCommandAsync(ICommand command)
|
||||
=> await _commandScheduler.AddCommand(command);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using DeclarativeProperty;
|
||||
using FileTime.App.CommandPalette.Services;
|
||||
using FileTime.App.Core.Extensions;
|
||||
using FileTime.App.Core.Models.Enums;
|
||||
@@ -25,9 +27,9 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
private readonly IFrequencyNavigationService _frequencyNavigationService;
|
||||
private readonly ICommandPaletteService _commandPaletteService;
|
||||
private ITabViewModel? _selectedTab;
|
||||
private IContainer? _currentLocation;
|
||||
private IItemViewModel? _currentSelectedItem;
|
||||
private IEnumerable<IItemViewModel> _currentItems = Enumerable.Empty<IItemViewModel>();
|
||||
private IDeclarativeProperty<IContainer?>? _currentLocation;
|
||||
private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem;
|
||||
private IDeclarativeProperty<ObservableCollection<IItemViewModel>?>? _currentItems;
|
||||
private ViewMode _viewMode;
|
||||
|
||||
public NavigationUserCommandHandlerService(
|
||||
@@ -121,7 +123,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
|
||||
private async Task GoToRoot()
|
||||
{
|
||||
var root = _currentLocation;
|
||||
var root = _currentLocation?.Value;
|
||||
if (root is null) return;
|
||||
|
||||
while (true)
|
||||
@@ -138,21 +140,25 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
|
||||
private async Task GoToProvider()
|
||||
{
|
||||
if (_currentLocation is null) return;
|
||||
if (_currentLocation?.Value is null) return;
|
||||
|
||||
await _userCommandHandlerService.HandleCommandAsync(
|
||||
new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, _currentLocation.Provider)));
|
||||
new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, _currentLocation.Value.Provider)));
|
||||
}
|
||||
|
||||
private async Task Refresh()
|
||||
{
|
||||
if (_currentLocation?.FullName is null) return;
|
||||
if (_currentLocation?.Value?.FullName is null) return;
|
||||
var refreshedItem =
|
||||
await _timelessContentProvider.GetItemByFullNameAsync(_currentLocation.FullName, PointInTime.Present);
|
||||
await _timelessContentProvider.GetItemByFullNameAsync(_currentLocation.Value.FullName, PointInTime.Present);
|
||||
|
||||
if (refreshedItem is not IContainer refreshedContainer) return;
|
||||
|
||||
_selectedTab?.Tab?.ForceSetCurrentLocation(refreshedContainer);
|
||||
|
||||
if (_selectedTab?.Tab is { } tab)
|
||||
{
|
||||
await tab.ForceSetCurrentLocation(refreshedContainer);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OpenContainer(OpenContainerCommand command)
|
||||
@@ -160,91 +166,100 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
var resolvedPath = await command.Path.ResolveAsync();
|
||||
if (resolvedPath is not IContainer resolvedContainer) return;
|
||||
|
||||
_selectedTab?.Tab?.SetCurrentLocation(resolvedContainer);
|
||||
if (_selectedTab?.Tab is { } tab)
|
||||
{
|
||||
await tab.SetCurrentLocation(resolvedContainer);
|
||||
}
|
||||
}
|
||||
|
||||
private Task OpenSelected()
|
||||
private async Task OpenSelected()
|
||||
{
|
||||
if (_currentSelectedItem is not IContainerViewModel containerViewModel || containerViewModel.Container is null)
|
||||
return Task.CompletedTask;
|
||||
if (_currentSelectedItem?.Value is not IContainerViewModel containerViewModel || containerViewModel.Container is null)
|
||||
return;
|
||||
|
||||
_appState.RapidTravelText = "";
|
||||
_selectedTab?.Tab?.SetCurrentLocation(containerViewModel.Container);
|
||||
return Task.CompletedTask;
|
||||
if (_selectedTab?.Tab is { } tab)
|
||||
{
|
||||
await tab.SetCurrentLocation(containerViewModel.Container);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GoUp()
|
||||
{
|
||||
if (_currentLocation?.Parent is not AbsolutePath parentPath ||
|
||||
if (_currentLocation?.Value?.Parent is not AbsolutePath parentPath ||
|
||||
await parentPath.ResolveAsyncSafe() is not IContainer newContainer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_appState.RapidTravelText = "";
|
||||
_selectedTab?.Tab?.SetCurrentLocation(newContainer);
|
||||
}
|
||||
|
||||
private Task MoveCursorDown()
|
||||
{
|
||||
SelectNewSelectedItem(items =>
|
||||
items.SkipWhile(i => !i.EqualsTo(_currentSelectedItem)).Skip(1).FirstOrDefault());
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task MoveCursorUp()
|
||||
{
|
||||
SelectNewSelectedItem(items => items.TakeWhile(i => !i.EqualsTo(_currentSelectedItem)).LastOrDefault());
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task MoveCursorDownPage()
|
||||
{
|
||||
SelectNewSelectedItem(items =>
|
||||
if (_selectedTab?.Tab is { } tab)
|
||||
{
|
||||
var relevantItems = items.SkipWhile(i => !i.EqualsTo(_currentSelectedItem)).ToList();
|
||||
await tab.SetCurrentLocation(newContainer);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task MoveCursorDown()
|
||||
=> await SelectNewSelectedItem(items =>
|
||||
{
|
||||
if (_currentSelectedItem?.Value == null) return items.FirstOrDefault();
|
||||
return items.SkipWhile(i => !i.EqualsTo(_currentSelectedItem?.Value)).Skip(1).FirstOrDefault();
|
||||
});
|
||||
|
||||
private async Task MoveCursorUp()
|
||||
=> await SelectNewSelectedItem(items =>
|
||||
{
|
||||
if (_currentSelectedItem?.Value == null) return items.LastOrDefault();
|
||||
return items.TakeWhile(i => !i.EqualsTo(_currentSelectedItem?.Value)).LastOrDefault();
|
||||
});
|
||||
|
||||
private async Task MoveCursorDownPage()
|
||||
=> await SelectNewSelectedItem(items =>
|
||||
{
|
||||
var relevantItems = _currentSelectedItem?.Value is null
|
||||
? items.ToList()
|
||||
: items.SkipWhile(i => !i.EqualsTo(_currentSelectedItem.Value)).ToList();
|
||||
|
||||
var fallBackItems = relevantItems.Take(PageSize + 1).Reverse();
|
||||
var preferredItems = relevantItems.Skip(PageSize + 1);
|
||||
|
||||
return preferredItems.Concat(fallBackItems).FirstOrDefault();
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task MoveCursorUpPage()
|
||||
{
|
||||
SelectNewSelectedItem(items =>
|
||||
private async Task MoveCursorUpPage()
|
||||
=> await SelectNewSelectedItem(items =>
|
||||
{
|
||||
var relevantItems = items.TakeWhile(i => !i.EqualsTo(_currentSelectedItem)).Reverse().ToList();
|
||||
var relevantItems = _currentSelectedItem?.Value is null
|
||||
? items.Reverse().ToList()
|
||||
: items.TakeWhile(i => !i.EqualsTo(_currentSelectedItem?.Value)).Reverse().ToList();
|
||||
|
||||
var fallBackItems = relevantItems.Take(PageSize).Reverse();
|
||||
var preferredItems = relevantItems.Skip(PageSize);
|
||||
|
||||
return preferredItems.Concat(fallBackItems).FirstOrDefault();
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task MoveCursorToFirst()
|
||||
private async Task MoveCursorToFirst()
|
||||
=> await SelectNewSelectedItem(items => items.FirstOrDefault());
|
||||
|
||||
private async Task MoveCursorToLast()
|
||||
=> await SelectNewSelectedItem(items => items.LastOrDefault());
|
||||
|
||||
private Task SelectNewSelectedItem(Func<IEnumerable<IItemViewModel>, IItemViewModel?> getNewSelected)
|
||||
{
|
||||
SelectNewSelectedItem(items => items.FirstOrDefault());
|
||||
if (_selectedTab is null || _currentItems?.Value is null) return Task.CompletedTask;
|
||||
|
||||
var newSelectedItem = getNewSelected(_currentItems.Value);
|
||||
if (newSelectedItem == null) return Task.CompletedTask;
|
||||
|
||||
if (_selectedTab.Tab is { } tab)
|
||||
{
|
||||
tab.SetSelectedItem(newSelectedItem.ToAbsolutePath(_timelessContentProvider));
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task MoveCursorToLast()
|
||||
{
|
||||
SelectNewSelectedItem(items => items.LastOrDefault());
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void SelectNewSelectedItem(Func<IEnumerable<IItemViewModel>, IItemViewModel?> getNewSelected)
|
||||
{
|
||||
if (_selectedTab is null || _currentLocation is null) return;
|
||||
|
||||
var newSelectedItem = getNewSelected(_currentItems);
|
||||
if (newSelectedItem == null) return;
|
||||
|
||||
_selectedTab.Tab?.SetSelectedItem(newSelectedItem.ToAbsolutePath(_timelessContentProvider));
|
||||
}
|
||||
|
||||
private Task EnterRapidTravel()
|
||||
{
|
||||
_appState.SwitchViewMode(ViewMode.RapidTravel);
|
||||
@@ -257,7 +272,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task SwitchToTab(SwitchToTabCommand command)
|
||||
private async Task SwitchToTab(SwitchToTabCommand command)
|
||||
{
|
||||
var number = command.TabNumber;
|
||||
var tabViewModel = _appState.Tabs.FirstOrDefault(t => t.TabNumber == number);
|
||||
@@ -269,8 +284,8 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
}
|
||||
else if (tabViewModel == null)
|
||||
{
|
||||
var tab = _serviceProvider.GetInitableResolver<IContainer>(_currentLocation ?? _localContentProvider)
|
||||
.GetRequiredService<ITab>();
|
||||
var tab = await _serviceProvider.GetAsyncInitableResolver<IContainer>(_currentLocation?.Value ?? _localContentProvider)
|
||||
.GetRequiredServiceAsync<ITab>();
|
||||
var newTabViewModel = _serviceProvider.GetInitableResolver(tab, number).GetRequiredService<ITabViewModel>();
|
||||
|
||||
_appState.AddTab(newTabViewModel);
|
||||
@@ -279,12 +294,10 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
|
||||
if (_viewMode == ViewMode.RapidTravel)
|
||||
{
|
||||
_userCommandHandlerService.HandleCommandAsync(ExitRapidTravelCommand.Instance);
|
||||
await _userCommandHandlerService.HandleCommandAsync(ExitRapidTravelCommand.Instance);
|
||||
}
|
||||
|
||||
_appState.SetSelectedTab(tabViewModel!);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task CloseTab()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Diagnostics;
|
||||
using DeclarativeProperty;
|
||||
using FileTime.App.Core.UserCommand;
|
||||
using FileTime.App.Core.ViewModels;
|
||||
using FileTime.App.Search;
|
||||
@@ -19,8 +20,8 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||
private readonly IUserCommandHandlerService _userCommandHandlerService;
|
||||
private readonly IContentAccessorFactory _contentAccessorFactory;
|
||||
private IContainer? _currentLocation;
|
||||
private IItemViewModel? _currentSelectedItem;
|
||||
private IDeclarativeProperty<IContainer?>? _currentLocation;
|
||||
private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem;
|
||||
|
||||
public ToolUserCommandHandlerService(
|
||||
IAppState appState,
|
||||
@@ -53,7 +54,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
|
||||
private async Task CopyBase64()
|
||||
{
|
||||
var item = _currentSelectedItem?.BaseItem;
|
||||
var item = _currentSelectedItem?.Value?.BaseItem;
|
||||
if (item?.Type != AbsolutePathType.Element || item is not IElement element) return;
|
||||
|
||||
var contentReader = await _contentAccessorFactory.GetContentReaderFactory(element.Provider).CreateContentReaderAsync(element);
|
||||
@@ -72,7 +73,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
|
||||
private async Task Search(SearchCommand searchCommand)
|
||||
{
|
||||
if (_currentLocation is null) return;
|
||||
if (_currentLocation?.Value is null) return;
|
||||
|
||||
var searchQuery = searchCommand.SearchText;
|
||||
if (string.IsNullOrEmpty(searchQuery))
|
||||
@@ -102,21 +103,21 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
var searchTask = await _searchManager.StartSearchAsync(searchMatcher, _currentLocation);
|
||||
var searchTask = await _searchManager.StartSearchAsync(searchMatcher, _currentLocation.Value);
|
||||
var openContainerCommand = new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, searchTask.SearchContainer));
|
||||
await _userCommandHandlerService.HandleCommandAsync(openContainerCommand);
|
||||
}
|
||||
|
||||
private async Task CopyNativePath()
|
||||
{
|
||||
if (_currentSelectedItem?.BaseItem?.NativePath is null) return;
|
||||
await _systemClipboardService.CopyToClipboardAsync(_currentSelectedItem.BaseItem.NativePath.Path);
|
||||
if (_currentSelectedItem?.Value?.BaseItem?.NativePath is null) return;
|
||||
await _systemClipboardService.CopyToClipboardAsync(_currentSelectedItem.Value.BaseItem.NativePath.Path);
|
||||
}
|
||||
|
||||
private Task OpenInDefaultFileExplorer()
|
||||
{
|
||||
if (_currentLocation?.NativePath is null) return Task.CompletedTask;
|
||||
Process.Start("explorer.exe", "\"" + _currentLocation.NativePath.Path + "\"");
|
||||
if (_currentLocation?.Value?.NativePath is null) return Task.CompletedTask;
|
||||
Process.Start("explorer.exe", "\"" + _currentLocation.Value.NativePath.Path + "\"");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reactive.Linq;
|
||||
using DeclarativeProperty;
|
||||
using DynamicData;
|
||||
using FileTime.App.Core.ViewModels;
|
||||
using FileTime.Core.Models;
|
||||
@@ -44,14 +46,14 @@ public abstract class UserCommandHandlerServiceBase : IUserCommandHandler
|
||||
|
||||
protected IDisposable SaveSelectedTab(Action<ITabViewModel?> handler) => RunWithAppState(appState => appState.SelectedTab.Subscribe(handler));
|
||||
|
||||
protected IDisposable SaveCurrentSelectedItem(Action<IItemViewModel?> handler)
|
||||
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t == null ? Observable.Return<IItemViewModel?>(null) : t.CurrentSelectedItem).Switch().Subscribe(handler));
|
||||
protected IDisposable SaveCurrentSelectedItem(Action<IDeclarativeProperty<IItemViewModel?>?> handler)
|
||||
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t?.CurrentSelectedItem).Subscribe(handler));
|
||||
|
||||
protected IDisposable SaveCurrentLocation(Action<IContainer?> handler)
|
||||
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t == null ? Observable.Return<IContainer?>(null) : t.CurrentLocation).Switch().Subscribe(handler));
|
||||
protected IDisposable SaveCurrentLocation(Action<IDeclarativeProperty<IContainer?>?> handler)
|
||||
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t?.CurrentLocation).Subscribe(handler));
|
||||
|
||||
protected IDisposable SaveCurrentItems(Action<IEnumerable<IItemViewModel>> handler)
|
||||
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t?.CurrentItemsCollectionObservable ?? Observable.Return((IEnumerable<IItemViewModel>?) Enumerable.Empty<IItemViewModel>())).Switch().Subscribe(i => handler(i ?? Enumerable.Empty<IItemViewModel>())));
|
||||
protected IDisposable SaveCurrentItems(Action<IDeclarativeProperty<ObservableCollection<IItemViewModel>?>?> handler)
|
||||
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t?.CurrentItems).Subscribe(handler));
|
||||
|
||||
protected IDisposable SaveMarkedItems(Action<IChangeSet<FullName>> handler)
|
||||
=> RunWithAppState(appState => appState.SelectedTab.Select(t => t == null ? Observable.Empty<IChangeSet<FullName>>() : t.MarkedItems).Switch().Subscribe(handler));
|
||||
|
||||
@@ -26,6 +26,7 @@ public static class Startup
|
||||
serviceCollection.TryAddSingleton<IIdentifiableUserCommandService, IdentifiableUserCommandService>();
|
||||
serviceCollection.TryAddSingleton<IItemPreviewService, ItemPreviewService>();
|
||||
serviceCollection.TryAddSingleton<ITimelineViewModel, TimelineViewModel>();
|
||||
serviceCollection.TryAddSingleton<IRefreshSmoothnessCalculator, RefreshSmoothnessCalculator>();
|
||||
|
||||
return serviceCollection
|
||||
.AddCommandHandlers()
|
||||
|
||||
@@ -10,12 +10,11 @@ public partial class ContainerViewModel : ItemViewModel, IContainerViewModel
|
||||
{
|
||||
public IContainer? Container => BaseItem as IContainer;
|
||||
|
||||
public ContainerViewModel(IItemNameConverterService itemNameConverterService, IAppState appState) : base(itemNameConverterService, appState)
|
||||
public ContainerViewModel(IItemNameConverterService itemNameConverterService, IAppState appState)
|
||||
: base(itemNameConverterService, appState)
|
||||
{
|
||||
}
|
||||
|
||||
public void Init(IContainer item, ITabViewModel parentTab, ItemViewModelType itemViewModelType)
|
||||
{
|
||||
Init((IItem)item, parentTab, itemViewModelType);
|
||||
}
|
||||
public void Init(IContainer item, ITabViewModel parentTab, ItemViewModelType itemViewModelType)
|
||||
=> Init((IItem)item, parentTab, itemViewModelType);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.ComponentModel;
|
||||
using System.Reactive.Linq;
|
||||
using DeclarativeProperty;
|
||||
using DynamicData;
|
||||
using FileTime.App.Core.Models;
|
||||
using FileTime.App.Core.Models.Enums;
|
||||
@@ -18,32 +19,23 @@ public abstract partial class ItemViewModel : IItemViewModel
|
||||
{
|
||||
private ITabViewModel? _parentTab;
|
||||
|
||||
[Property]
|
||||
private IItem? _baseItem;
|
||||
[Property] private IItem? _baseItem;
|
||||
|
||||
[Property]
|
||||
private IObservable<IReadOnlyList<ItemNamePart>>? _displayName;
|
||||
[Property] private IObservable<IReadOnlyList<ItemNamePart>>? _displayName;
|
||||
|
||||
[Property]
|
||||
private string? _displayNameText;
|
||||
[Property] private string? _displayNameText;
|
||||
|
||||
[Property]
|
||||
private IObservable<bool>? _isSelected;
|
||||
[Property] private IDeclarativeProperty<bool> _isSelected;
|
||||
|
||||
[Property]
|
||||
private IObservable<bool>? _isMarked;
|
||||
[Property] private IObservable<bool>? _isMarked;
|
||||
|
||||
[Property]
|
||||
private IObservable<ItemViewMode> _viewMode;
|
||||
[Property] private IObservable<ItemViewMode> _viewMode;
|
||||
|
||||
[Property]
|
||||
private DateTime? _createdAt;
|
||||
[Property] private DateTime? _createdAt;
|
||||
|
||||
[Property]
|
||||
private string? _attributes;
|
||||
[Property] private string? _attributes;
|
||||
|
||||
[Property]
|
||||
private IObservable<bool> _isAlternative;
|
||||
[Property] private IDeclarativeProperty<bool> _isAlternative;
|
||||
|
||||
public void Init(IItem item, ITabViewModel parentTab, ItemViewModelType itemViewModelType)
|
||||
{
|
||||
@@ -51,9 +43,9 @@ public abstract partial class ItemViewModel : IItemViewModel
|
||||
|
||||
var sourceCollection = itemViewModelType switch
|
||||
{
|
||||
ItemViewModelType.Main => parentTab.CurrentItemsCollectionObservable,
|
||||
ItemViewModelType.Parent => parentTab.ParentsChildrenCollectionObservable,
|
||||
ItemViewModelType.SelectedChild => parentTab.SelectedsChildrenCollectionObservable,
|
||||
ItemViewModelType.Main => parentTab.CurrentItems,
|
||||
ItemViewModelType.Parent => parentTab.ParentsChildren,
|
||||
ItemViewModelType.SelectedChild => parentTab.SelectedsChildren,
|
||||
_ => throw new InvalidEnumArgumentException()
|
||||
};
|
||||
|
||||
@@ -66,10 +58,10 @@ public abstract partial class ItemViewModel : IItemViewModel
|
||||
: Observable.Return(false);
|
||||
|
||||
IsSelected = itemViewModelType is ItemViewModelType.Main
|
||||
? parentTab.CurrentSelectedItem.Select(EqualsTo)
|
||||
: Observable.Return(IsInDeepestPath());
|
||||
? parentTab.CurrentSelectedItem.Map(EqualsTo)
|
||||
: new DeclarativeProperty<bool>(IsInDeepestPath());
|
||||
|
||||
IsAlternative = sourceCollection.Select(c => c?.Index().FirstOrDefault(i => EqualsTo(i.Value)).Key % 2 == 0);
|
||||
IsAlternative = sourceCollection.Map(c => c?.Index().FirstOrDefault(i => EqualsTo(i.Value)).Key % 2 == 0);
|
||||
|
||||
ViewMode = Observable.CombineLatest(IsMarked, IsSelected, IsAlternative, GenerateViewMode).Throttle(TimeSpan.FromMilliseconds(10));
|
||||
Attributes = item.Attributes;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Reactive.Linq;
|
||||
using DeclarativeProperty;
|
||||
using DynamicData;
|
||||
using DynamicData.Binding;
|
||||
using FileTime.App.Core.Extensions;
|
||||
@@ -10,6 +13,9 @@ using FileTime.Core.Models.Extensions;
|
||||
using FileTime.Core.Services;
|
||||
using InitableService;
|
||||
using MvvmGen;
|
||||
using ObservableComputations;
|
||||
using IContainer = FileTime.Core.Models.IContainer;
|
||||
using static System.DeferTools;
|
||||
|
||||
namespace FileTime.App.Core.ViewModels;
|
||||
|
||||
@@ -23,36 +29,23 @@ public partial class TabViewModel : ITabViewModel
|
||||
private readonly SourceList<FullName> _markedItems = new();
|
||||
private readonly List<IDisposable> _disposables = new();
|
||||
private bool _disposed;
|
||||
private OcConsumer? _currentItemsConsumer;
|
||||
private OcConsumer? _selectedsChildrenConsumer;
|
||||
private OcConsumer? _parentsChildrenConsumer;
|
||||
|
||||
public ITab? Tab { get; private set; }
|
||||
public int TabNumber { get; private set; }
|
||||
|
||||
public IObservable<bool> IsSelected { get; }
|
||||
|
||||
public IObservable<IContainer?> CurrentLocation { get; private set; } = null!;
|
||||
public IObservable<IItemViewModel?> CurrentSelectedItem { get; private set; } = null!;
|
||||
public IObservable<IObservable<IChangeSet<IItemViewModel, string>>?> CurrentItems { get; private set; } = null!;
|
||||
public IDeclarativeProperty<IContainer?> CurrentLocation { get; private set; }
|
||||
public IDeclarativeProperty<IItemViewModel?> CurrentSelectedItem { get; private set; }
|
||||
public IDeclarativeProperty<IContainerViewModel?> CurrentSelectedItemAsContainer { get; private set; }
|
||||
public IDeclarativeProperty<ObservableCollection<IItemViewModel>?> CurrentItems { get; private set; }
|
||||
public IObservable<IChangeSet<FullName>> MarkedItems { get; }
|
||||
public IObservable<IObservable<IChangeSet<IItemViewModel, string>>?> SelectedsChildren { get; private set; } = null!;
|
||||
public IObservable<IObservable<IChangeSet<IItemViewModel, string>>?> ParentsChildren { get; private set; } = null!;
|
||||
public IDeclarativeProperty<ObservableCollection<IItemViewModel>?> SelectedsChildren { get; private set; }
|
||||
public IDeclarativeProperty<ObservableCollection<IItemViewModel>?> ParentsChildren { get; private set; }
|
||||
|
||||
public IObservable<IReadOnlyCollection<IItemViewModel>?> CurrentItemsCollectionObservable { get; private set; } =
|
||||
null!;
|
||||
|
||||
public IObservable<IReadOnlyCollection<IItemViewModel>?> ParentsChildrenCollectionObservable { get; private set; } =
|
||||
null!;
|
||||
|
||||
public IObservable<IReadOnlyCollection<IItemViewModel>?>
|
||||
SelectedsChildrenCollectionObservable
|
||||
{ get; private set; } = null!;
|
||||
|
||||
[Property] private BindedCollection<IItemViewModel, string>? _currentItemsCollection;
|
||||
|
||||
[Property] private BindedCollection<IItemViewModel, string>? _parentsChildrenCollection;
|
||||
|
||||
[Property] private BindedCollection<IItemViewModel, string>? _selectedsChildrenCollection;
|
||||
|
||||
public IContainer? CachedCurrentLocation { get; private set; }
|
||||
|
||||
public TabViewModel(
|
||||
IServiceProvider serviceProvider,
|
||||
@@ -76,13 +69,19 @@ public partial class TabViewModel : ITabViewModel
|
||||
|
||||
tab.AddToDisposables(_disposables);
|
||||
|
||||
CurrentLocation = tab.CurrentLocation.AsObservable();
|
||||
CurrentLocation.Subscribe(l => CachedCurrentLocation = l).AddToDisposables(_disposables);
|
||||
CurrentLocation = tab.CurrentLocation;
|
||||
|
||||
CurrentItems = tab.CurrentItems
|
||||
.Select(items => items?.Transform(i => MapItemToViewModel(i, ItemViewModelType.Main)))
|
||||
.Publish(null)
|
||||
.RefCount();
|
||||
.Map((items, _) =>
|
||||
Task.FromResult<ObservableCollection<IItemViewModel>?>(
|
||||
items?.Selecting<IItem, IItemViewModel>(
|
||||
i => MapItemToViewModel(i, ItemViewModelType.Main)
|
||||
)
|
||||
)
|
||||
);
|
||||
using var _ = Defer(
|
||||
() => CurrentItems.Subscribe(c => UpdateConsumer(c, ref _currentItemsConsumer))
|
||||
);
|
||||
|
||||
/*CurrentSelectedItem =
|
||||
Observable.CombineLatest(
|
||||
@@ -100,30 +99,65 @@ public partial class TabViewModel : ITabViewModel
|
||||
.Publish(null)
|
||||
.RefCount();*/
|
||||
|
||||
CurrentSelectedItem =
|
||||
CurrentSelectedItem = DeclarativePropertyHelpers.CombineLatest(
|
||||
tab.CurrentSelectedItem,
|
||||
CurrentItems.Watch<ObservableCollection<IItemViewModel>, IItemViewModel>(),
|
||||
(currentSelectedItem, currentItems) =>
|
||||
Task.FromResult(currentItems?.FirstOrDefault(i => i.BaseItem?.FullName?.Path == currentSelectedItem?.Path.Path))
|
||||
);
|
||||
|
||||
CurrentSelectedItemAsContainer = CurrentSelectedItem.Map(i => i as IContainerViewModel);
|
||||
//CurrentSelectedItem = tab.CurrentSelectedItem.Map((item, _) => Task.FromResult(CurrentItems.Value?.FirstOrDefault(i => i.BaseItem?.FullName?.Path == item?.Path.Path)));
|
||||
|
||||
/*CurrentSelectedItem =
|
||||
Observable.CombineLatest(
|
||||
CurrentItems,
|
||||
tab.CurrentSelectedItem,
|
||||
(currentItems, currentSelectedItemPath) =>
|
||||
CurrentItemsCollection?.Collection?.FirstOrDefault(i => i.BaseItem?.FullName?.Path == currentSelectedItemPath?.Path.Path)
|
||||
)
|
||||
)
|
||||
.Publish(null)
|
||||
.RefCount();
|
||||
.RefCount();*/
|
||||
|
||||
SelectedsChildren = InitSelectedsChildren();
|
||||
ParentsChildren = InitParentsChildren();
|
||||
SelectedsChildren = CurrentSelectedItem
|
||||
.Debounce(TimeSpan.FromMilliseconds(200), resetTimer: true)
|
||||
.DistinctUntilChanged()
|
||||
.Map(item =>
|
||||
{
|
||||
if (item is not IContainerViewModel {Container: { } container})
|
||||
return (ObservableCollection<IItemViewModel>?) null;
|
||||
|
||||
CurrentItemsCollectionObservable = InitCollection(CurrentItems);
|
||||
SelectedsChildrenCollectionObservable = InitCollection(SelectedsChildren);
|
||||
ParentsChildrenCollectionObservable = InitCollection(ParentsChildren);
|
||||
var items = container
|
||||
.Items
|
||||
.Selecting(i => MapItem(i))
|
||||
.Ordering(i => i.Type)
|
||||
.ThenOrdering(i => i.Name)
|
||||
.Selecting(i => MapItemToViewModel(i, ItemViewModelType.SelectedChild));
|
||||
|
||||
CurrentItemsCollection = new(CurrentItems);
|
||||
ParentsChildrenCollection = new(ParentsChildren);
|
||||
SelectedsChildrenCollection = new(SelectedsChildren);
|
||||
return items;
|
||||
});
|
||||
using var __ = Defer(() =>
|
||||
SelectedsChildren.Subscribe(c => UpdateConsumer(c, ref _selectedsChildrenConsumer))
|
||||
);
|
||||
|
||||
tab.CurrentLocation.Subscribe((_) => _markedItems.Clear()).AddToDisposables(_disposables);
|
||||
ParentsChildren = CurrentLocation.Map(async (item, _) =>
|
||||
{
|
||||
if (item is null || item.Parent is null) return (ObservableCollection<IItemViewModel>?) null;
|
||||
var parent = (IContainer) await item.Parent.ResolveAsync(itemInitializationSettings: new ItemInitializationSettings(true));
|
||||
|
||||
IObservable<IObservable<IChangeSet<IItemViewModel, string>>?> InitSelectedsChildren()
|
||||
var items = parent.Items
|
||||
.Selecting<AbsolutePath, IItem>(i => MapItem(i))
|
||||
.Selecting(i => MapItemToViewModel(i, ItemViewModelType.SelectedChild));
|
||||
|
||||
return items;
|
||||
});
|
||||
using var ___ = Defer(() =>
|
||||
ParentsChildren.Subscribe(c => UpdateConsumer(c, ref _parentsChildrenConsumer))
|
||||
);
|
||||
|
||||
tab.CurrentLocation.Subscribe(_ => _markedItems.Clear()).AddToDisposables(_disposables);
|
||||
|
||||
/*IObservable<IObservable<IChangeSet<IItemViewModel, string>>?> InitSelectedsChildren()
|
||||
{
|
||||
var currentSelectedItemThrottled =
|
||||
CurrentSelectedItem.Throttle(TimeSpan.FromMilliseconds(250)).Publish(null).RefCount();
|
||||
@@ -135,16 +169,16 @@ public partial class TabViewModel : ITabViewModel
|
||||
.Select(c => c.Container!.Items)
|
||||
.Select(i =>
|
||||
i
|
||||
?.TransformAsync(MapItem)
|
||||
?.TransformAsync(MapItemAsync)
|
||||
.Transform(i => MapItemToViewModel(i, ItemViewModelType.SelectedChild))
|
||||
.Sort(SortItems())
|
||||
),
|
||||
currentSelectedItemThrottled
|
||||
.Where(c => c is null or not IContainerViewModel)
|
||||
.Select(_ => (IObservable<IChangeSet<IItemViewModel, string>>?)null)
|
||||
.Select(_ => (IObservable<IChangeSet<IItemViewModel, string>>?) null)
|
||||
)
|
||||
/*.ObserveOn(_rxSchedulerService.GetWorkerScheduler())
|
||||
.SubscribeOn(_rxSchedulerService.GetUIScheduler())*/
|
||||
.SubscribeOn(_rxSchedulerService.GetUIScheduler())#1#
|
||||
.Publish(null)
|
||||
.RefCount();
|
||||
}
|
||||
@@ -160,35 +194,34 @@ public partial class TabViewModel : ITabViewModel
|
||||
return Observable.Merge(
|
||||
parentThrottled
|
||||
.Where(p => p is not null)
|
||||
.Select(p => Observable.FromAsync(async () => (IContainer)await p!.ResolveAsync()))
|
||||
.Select(p => Observable.FromAsync(async () => (IContainer) await p!.ResolveAsync()))
|
||||
.Switch()
|
||||
.Select(p => p.Items)
|
||||
.Select(items =>
|
||||
items
|
||||
?.TransformAsync(MapItem)
|
||||
?.TransformAsync(MapItemAsync)
|
||||
.Transform(i => MapItemToViewModel(i, ItemViewModelType.Parent))
|
||||
.Sort(SortItems())
|
||||
),
|
||||
parentThrottled
|
||||
.Where(p => p is null)
|
||||
.Select(_ => (IObservable<IChangeSet<IItemViewModel, string>>?)null)
|
||||
.Select(_ => (IObservable<IChangeSet<IItemViewModel, string>>?) null)
|
||||
)
|
||||
/*.ObserveOn(_rxSchedulerService.GetWorkerScheduler())
|
||||
.SubscribeOn(_rxSchedulerService.GetUIScheduler())*/
|
||||
.SubscribeOn(_rxSchedulerService.GetUIScheduler())#1#
|
||||
.Publish(null)
|
||||
.RefCount();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
IObservable<IReadOnlyCollection<IItemViewModel>?> InitCollection(
|
||||
IObservable<IObservable<IChangeSet<IItemViewModel, string>>?> source)
|
||||
{
|
||||
return source
|
||||
.Select(c =>
|
||||
c != null ? c.ToCollection() : Observable.Return((IReadOnlyCollection<IItemViewModel>?)null))
|
||||
.Switch()
|
||||
.Publish(null)
|
||||
.RefCount();
|
||||
}
|
||||
|
||||
static void UpdateConsumer<T>(ObservableCollection<T>? collection, ref OcConsumer? consumer)
|
||||
{
|
||||
if (collection is not IComputing computing) return;
|
||||
|
||||
consumer?.Dispose();
|
||||
consumer = new OcConsumer();
|
||||
computing.For(consumer);
|
||||
}
|
||||
|
||||
private static SortExpressionComparer<IItemViewModel> SortItems()
|
||||
@@ -197,7 +230,14 @@ public partial class TabViewModel : ITabViewModel
|
||||
.Ascending(i => i.BaseItem?.Type ?? AbsolutePathType.Unknown)
|
||||
.ThenByAscending(i => i.DisplayNameText?.ToLower() ?? "");
|
||||
|
||||
private static async Task<IItem> MapItem(AbsolutePath item)
|
||||
private static IItem MapItem(AbsolutePath item)
|
||||
{
|
||||
var t = Task.Run(async () => await MapItemAsync(item));
|
||||
t.Wait();
|
||||
return t.Result;
|
||||
}
|
||||
|
||||
private static async Task<IItem> MapItemAsync(AbsolutePath item)
|
||||
=> await item.ResolveAsync(forceResolve: true,
|
||||
itemInitializationSettings: new ItemInitializationSettings(true));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user