Item ordering

This commit is contained in:
2023-07-28 01:38:53 +02:00
parent 27ebb1efa8
commit 0db7949037
13 changed files with 155 additions and 28 deletions

View File

@@ -56,6 +56,7 @@ public class CommandPaletteViewModel : FuzzyPanelViewModel<ICommandPaletteEntryV
if (keyEventArgs.Key == Key.Escape) if (keyEventArgs.Key == Key.Escape)
{ {
keyEventArgs.Handled = true;
Close(); Close();
return true; return true;
} }
@@ -67,6 +68,9 @@ public class CommandPaletteViewModel : FuzzyPanelViewModel<ICommandPaletteEntryV
var command = _identifiableUserCommandService.GetCommand(SelectedItem.Identifier); var command = _identifiableUserCommandService.GetCommand(SelectedItem.Identifier);
if (command is null) return false; if (command is null) return false;
keyEventArgs.Handled = true;
Close();
try try
{ {
await _userCommandHandlerService.HandleCommandAsync(command); await _userCommandHandlerService.HandleCommandAsync(command);
@@ -76,7 +80,6 @@ public class CommandPaletteViewModel : FuzzyPanelViewModel<ICommandPaletteEntryV
_logger.LogError(e, "Unknown error while running command. {Command} {Error}", command.GetType().Name, e); _logger.LogError(e, "Unknown error while running command. {Command} {Error}", command.GetType().Name, e);
} }
Close();
return true; return true;
} }

View File

@@ -0,0 +1,9 @@
namespace FileTime.App.Core.Models;
public enum ItemOrdering
{
Name,
NameDesc,
LastModifyDate,
LastModifyDateDesc,
}

View File

@@ -25,7 +25,7 @@ public class IdentifiableSearchCommand : SearchCommand, IIdentifiableUserCommand
public static readonly IdentifiableSearchCommand SearchByNameContains = public static readonly IdentifiableSearchCommand SearchByNameContains =
new(null, SearchType.NameContains, SearchByNameContainsCommandName); new(null, SearchType.NameContains, SearchByNameContainsCommandName);
public IdentifiableSearchCommand( private IdentifiableSearchCommand(
string? searchText, string? searchText,
SearchType searchType, SearchType searchType,
string commandId) string commandId)

View File

@@ -0,0 +1,33 @@
using FileTime.App.Core.Models;
namespace FileTime.App.Core.UserCommand;
public class SortItemsCommand : IIdentifiableUserCommand
{
public const string OrderByNameCommandName = "order_by_name";
public const string OrderByNameDescCommandName = "order_by_name_desc";
public const string OrderByDateCommandName = "order_by_date";
public const string OrderByDateDescCommandName = "order_by_date_desc";
public static readonly SortItemsCommand OrderByNameCommand =
new(OrderByNameCommandName, ItemOrdering.Name);
public static readonly SortItemsCommand OrderByNameDescCommand =
new(OrderByNameDescCommandName, ItemOrdering.NameDesc);
public static readonly SortItemsCommand OrderByDateCommand =
new(OrderByDateCommandName, ItemOrdering.LastModifyDate);
public static readonly SortItemsCommand OrderByDateDescCommand =
new(OrderByDateDescCommandName, ItemOrdering.LastModifyDateDesc);
private SortItemsCommand(string userCommandId, ItemOrdering ordering)
{
UserCommandID = userCommandId;
Ordering = ordering;
}
public string UserCommandID { get; }
public ItemOrdering Ordering { get; }
}

View File

@@ -2,6 +2,7 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using DeclarativeProperty; using DeclarativeProperty;
using DynamicData; using DynamicData;
using FileTime.App.Core.Models;
using FileTime.Core.Models; using FileTime.Core.Models;
using FileTime.Core.Services; using FileTime.Core.Services;
using InitableService; using InitableService;
@@ -20,6 +21,7 @@ public interface ITabViewModel : IInitable<ITab, int>, IDisposable
IObservable<IChangeSet<FullName>> MarkedItems { get; } IObservable<IChangeSet<FullName>> MarkedItems { get; }
IDeclarativeProperty<ObservableCollection<IItemViewModel>> SelectedsChildren { get; } IDeclarativeProperty<ObservableCollection<IItemViewModel>> SelectedsChildren { get; }
IDeclarativeProperty<ObservableCollection<IItemViewModel>> ParentsChildren { get; } IDeclarativeProperty<ObservableCollection<IItemViewModel>> ParentsChildren { get; }
DeclarativeProperty<ItemOrdering?> Ordering { get; }
void ClearMarkedItems(); void ClearMarkedItems();
void RemoveMarkedItem(FullName fullName); void RemoveMarkedItem(FullName fullName);

View File

@@ -22,6 +22,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
private readonly IContentAccessorFactory _contentAccessorFactory; private readonly IContentAccessorFactory _contentAccessorFactory;
private IDeclarativeProperty<IContainer?>? _currentLocation; private IDeclarativeProperty<IContainer?>? _currentLocation;
private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem; private IDeclarativeProperty<IItemViewModel?>? _currentSelectedItem;
private ITabViewModel? _currentSelectedTab;
public ToolUserCommandHandlerService( public ToolUserCommandHandlerService(
IAppState appState, IAppState appState,
@@ -42,6 +43,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
_contentAccessorFactory = contentAccessorFactory; _contentAccessorFactory = contentAccessorFactory;
SaveCurrentLocation(l => _currentLocation = l); SaveCurrentLocation(l => _currentLocation = l);
SaveCurrentSelectedItem(i => _currentSelectedItem = i); SaveCurrentSelectedItem(i => _currentSelectedItem = i);
SaveSelectedTab(t => _currentSelectedTab = t);
AddCommandHandlers(new IUserCommandHandler[] AddCommandHandlers(new IUserCommandHandler[]
{ {
@@ -49,9 +51,17 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase
new TypeUserCommandHandler<CopyNativePathCommand>(CopyNativePath), new TypeUserCommandHandler<CopyNativePathCommand>(CopyNativePath),
new TypeUserCommandHandler<CopyBase64Command>(CopyBase64), new TypeUserCommandHandler<CopyBase64Command>(CopyBase64),
new TypeUserCommandHandler<SearchCommand>(Search), new TypeUserCommandHandler<SearchCommand>(Search),
new TypeUserCommandHandler<SortItemsCommand>(SortItems),
}); });
} }
private async Task SortItems(SortItemsCommand sortItemsCommand)
{
if (_currentSelectedTab is null) return;
await _currentSelectedTab.Ordering.SetValue(sortItemsCommand.Ordering);
}
private async Task CopyBase64() private async Task CopyBase64()
{ {
var item = _currentSelectedItem?.Value?.BaseItem; var item = _currentSelectedItem?.Value?.BaseItem;

View File

@@ -49,6 +49,10 @@ public class DefaultIdentifiableCommandHandlerRegister : IStartupHandler
AddUserCommand(RenameCommand.Instance); AddUserCommand(RenameCommand.Instance);
AddUserCommand(RunOrOpenCommand.Instance); AddUserCommand(RunOrOpenCommand.Instance);
AddUserCommand(StartCommandSchedulerCommand.Instance); AddUserCommand(StartCommandSchedulerCommand.Instance);
AddUserCommand(SortItemsCommand.OrderByNameCommand);
AddUserCommand(SortItemsCommand.OrderByNameDescCommand);
AddUserCommand(SortItemsCommand.OrderByDateCommand);
AddUserCommand(SortItemsCommand.OrderByDateDescCommand);
AddUserCommand(IdentifiableSearchCommand.SearchByNameContains); AddUserCommand(IdentifiableSearchCommand.SearchByNameContains);
AddUserCommand(SwitchToTabCommand.SwitchToLastTab); AddUserCommand(SwitchToTabCommand.SwitchToLastTab);
AddUserCommand(SwitchToTabCommand.SwitchToTab1); AddUserCommand(SwitchToTabCommand.SwitchToTab1);

View File

@@ -1,9 +1,12 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reactive.Linq; using System.Reactive.Linq;
using DeclarativeProperty; using DeclarativeProperty;
using DynamicData; using DynamicData;
using DynamicData.Binding; using DynamicData.Binding;
using FileTime.App.Core.Extensions; using FileTime.App.Core.Extensions;
using FileTime.App.Core.Models;
using FileTime.App.Core.Models.Enums; using FileTime.App.Core.Models.Enums;
using FileTime.App.Core.Services; using FileTime.App.Core.Services;
using FileTime.Core.Enums; using FileTime.Core.Enums;
@@ -45,6 +48,7 @@ public partial class TabViewModel : ITabViewModel
public IObservable<IChangeSet<FullName>> MarkedItems { get; } public IObservable<IChangeSet<FullName>> MarkedItems { get; }
public IDeclarativeProperty<ObservableCollection<IItemViewModel>?> SelectedsChildren { get; private set; } public IDeclarativeProperty<ObservableCollection<IItemViewModel>?> SelectedsChildren { get; private set; }
public IDeclarativeProperty<ObservableCollection<IItemViewModel>?> ParentsChildren { get; private set; } public IDeclarativeProperty<ObservableCollection<IItemViewModel>?> ParentsChildren { get; private set; }
public DeclarativeProperty<ItemOrdering?> Ordering { get; } = new(ItemOrdering.Name);
public TabViewModel( public TabViewModel(
@@ -71,14 +75,56 @@ public partial class TabViewModel : ITabViewModel
CurrentLocation = tab.CurrentLocation; CurrentLocation = tab.CurrentLocation;
CurrentItems = tab.CurrentItems CurrentItems =
tab.CurrentItems
.Map((items, _) => .Map((items, _) =>
Task.FromResult<ObservableCollection<IItemViewModel>?>( Task.FromResult<ObservableCollection<IItemViewModel>?>(
items?.Selecting<IItem, IItemViewModel>( items?.Selecting<IItem, IItemViewModel>(
i => MapItemToViewModel(i, ItemViewModelType.Main) i => MapItemToViewModel(i, ItemViewModelType.Main)
) )
) )
).CombineLatest(
Ordering,
(items, ordering) =>
{
if (items is null) return Task.FromResult<ObservableCollection<IItemViewModel>?>(null);
/*Expression<Func<IItemViewModel, object?>> orderExpression = ordering switch
{
ItemOrdering.Name or ItemOrdering.NameDesc => i => i.DisplayNameText,
ItemOrdering.LastModifyDate or ItemOrdering.LastModifyDateDesc => i => i.CreatedAt,
_ => throw new NotImplementedException()
};
Expression<Func<ListSortDirection>> direction = ordering switch
{
ItemOrdering.Name or ItemOrdering.LastModifyDate => () => ListSortDirection.Ascending,
ItemOrdering.NameDesc or ItemOrdering.LastModifyDateDesc => () => ListSortDirection.Descending,
_ => throw new NotImplementedException()
};
return Task.FromResult((ObservableCollection<IItemViewModel>?) items.Ordering(orderExpression, direction));*/
ObservableCollection<IItemViewModel>? orderedItems = ordering switch
{
ItemOrdering.Name =>
items
.Ordering(i => i.BaseItem.Type)
.ThenOrdering(i => i.DisplayNameText),
ItemOrdering.NameDesc =>
items
.Ordering(i => i.BaseItem.Type)
.ThenOrdering(i => i.DisplayNameText, ListSortDirection.Descending),
ItemOrdering.LastModifyDate =>
items
.Ordering(i => i.CreatedAt),
ItemOrdering.LastModifyDateDesc =>
items
.Ordering(i => i.CreatedAt, ListSortDirection.Descending),
_ => throw new NotImplementedException()
};
return Task.FromResult(orderedItems);
}
); );
using var _ = Defer( using var _ = Defer(
() => CurrentItems.Subscribe(c => UpdateConsumer(c, ref _currentItemsConsumer)) () => CurrentItems.Subscribe(c => UpdateConsumer(c, ref _currentItemsConsumer))
); );
@@ -127,6 +173,8 @@ public partial class TabViewModel : ITabViewModel
var items = parent.Items var items = parent.Items
.Selecting(i => MapItem(i)) .Selecting(i => MapItem(i))
.Ordering(i => i.Type)
.ThenOrdering(i => i.Name)
.Selecting(i => MapItemToViewModel(i, ItemViewModelType.SelectedChild)); .Selecting(i => MapItemToViewModel(i, ItemViewModelType.SelectedChild));
return items; return items;

View File

@@ -38,6 +38,7 @@ public class FrequencyNavigationViewModel : FuzzyPanelViewModel<string>, IFreque
if (keyEventArgs.Key == Key.Enter) if (keyEventArgs.Key == Key.Enter)
{ {
keyEventArgs.Handled = true;
var targetContainer = await _timelessContentProvider.GetItemByFullNameAsync(new FullName(SelectedItem), PointInTime.Present); var targetContainer = await _timelessContentProvider.GetItemByFullNameAsync(new FullName(SelectedItem), PointInTime.Present);
var openContainerCommand = new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, targetContainer)); var openContainerCommand = new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, targetContainer));
await _userCommandHandlerService.HandleCommandAsync(openContainerCommand); await _userCommandHandlerService.HandleCommandAsync(openContainerCommand);

View File

@@ -46,6 +46,7 @@ public abstract partial class FuzzyPanelViewModel<TItem> : IFuzzyPanelViewModel<
if (nextItem is not null) if (nextItem is not null)
{ {
keyEventArgs.Handled = true;
SelectedItem = nextItem; SelectedItem = nextItem;
} }
@@ -57,6 +58,7 @@ public abstract partial class FuzzyPanelViewModel<TItem> : IFuzzyPanelViewModel<
if (previousItem is not null) if (previousItem is not null)
{ {
keyEventArgs.Handled = true;
SelectedItem = previousItem; SelectedItem = previousItem;
} }

View File

@@ -18,10 +18,10 @@ public class Tab : ITab
private readonly DeclarativeProperty<IContainer?> _currentLocation = new(null); private readonly DeclarativeProperty<IContainer?> _currentLocation = new(null);
private readonly BehaviorSubject<IContainer?> _currentLocationForced = new(null); private readonly BehaviorSubject<IContainer?> _currentLocationForced = new(null);
private readonly DeclarativeProperty<AbsolutePath?> _currentRequestItem = new(null); private readonly DeclarativeProperty<AbsolutePath?> _currentRequestItem = new(null);
private readonly SourceList<ItemFilter> _itemFilters = new(); private readonly ObservableCollection<ItemFilter> _itemFilters = new();
private readonly DeclarativeProperty<ObservableCollection<ItemFilter>?> _itemFiltersProperty;
private AbsolutePath? _currentSelectedItemCached; private AbsolutePath? _currentSelectedItemCached;
private PointInTime _currentPointInTime; private PointInTime _currentPointInTime;
private OcConsumer? _currentItemsConsumer;
private CancellationTokenSource? _setCurrentLocationCancellationTokenSource; private CancellationTokenSource? _setCurrentLocationCancellationTokenSource;
private CancellationTokenSource? _setCurrentItemCancellationTokenSource; private CancellationTokenSource? _setCurrentItemCancellationTokenSource;
@@ -38,6 +38,7 @@ public class Tab : ITab
_timelessContentProvider = timelessContentProvider; _timelessContentProvider = timelessContentProvider;
_tabEvents = tabEvents; _tabEvents = tabEvents;
_currentPointInTime = null!; _currentPointInTime = null!;
_itemFiltersProperty = new(_itemFilters);
_timelessContentProvider.CurrentPointInTime.Subscribe(p => _currentPointInTime = p); _timelessContentProvider.CurrentPointInTime.Subscribe(p => _currentPointInTime = p);
@@ -52,12 +53,25 @@ public class Tab : ITab
return Task.CompletedTask; return Task.CompletedTask;
}); });
CurrentItems = CurrentLocation CurrentItems = DeclarativePropertyHelpers.CombineLatest(
.Map((container, _) => CurrentLocation,
_itemFiltersProperty.Watch<ObservableCollection<ItemFilter>, ItemFilter>(),
(container, filters) =>
{ {
var items = container is null ObservableCollection<IItem>? items = null;
? (ObservableCollection<IItem>?) null
: container.Items.Selecting<AbsolutePath, IItem>(i => MapItem(i)); if (container is not null)
{
items = container
.Items
.Selecting<AbsolutePath, IItem>(i => MapItem(i));
if (filters is not null)
{
items = items.Filtering(i => filters.All(f => f.Filter(i)));
}
}
return Task.FromResult(items); return Task.FromResult(items);
} }
); );
@@ -179,7 +193,7 @@ public class Tab : ITab
public void RemoveItemFilter(string name) public void RemoveItemFilter(string name)
{ {
var itemsToRemove = _itemFilters.Items.Where(t => t.Name == name).ToList(); var itemsToRemove = _itemFilters.Where(t => t.Name == name).ToList();
_itemFilters.RemoveMany(itemsToRemove); _itemFilters.RemoveMany(itemsToRemove);
} }
@@ -194,8 +208,5 @@ public class Tab : ITab
} }
public void Dispose() public void Dispose()
{ => _currentLocation.Dispose();
_currentLocation.Dispose();
_itemFilters.Dispose();
}
} }

View File

@@ -30,6 +30,7 @@ public partial class CommandPalette : UserControl
if (e.Key == Key.Escape) if (e.Key == Key.Escape)
{ {
e.Handled = true;
viewModel.Close(); viewModel.Close();
} }
else else
@@ -37,9 +38,4 @@ public partial class CommandPalette : UserControl
viewModel.HandleKeyDown(e); viewModel.HandleKeyDown(e);
} }
} }
public void Test()
{
;
}
} }

View File

@@ -69,4 +69,12 @@ public static class DeclarativePropertyExtensions
this IDeclarativeProperty<TCollection?> collection) this IDeclarativeProperty<TCollection?> collection)
where TCollection : IList<TItem>, INotifyCollectionChanged where TCollection : IList<TItem>, INotifyCollectionChanged
=> new CollectionRepeaterProperty<TCollection?, TItem>(collection); => new CollectionRepeaterProperty<TCollection?, TItem>(collection);
public static IDeclarativeProperty<TResult?> CombineLatest<T1, T2, TResult>(
this IDeclarativeProperty<T1> prop1,
IDeclarativeProperty<T2> prop2,
Func<T1, T2, Task<TResult>> func,
Action<TResult?>? setValueHook = null)
=> new CombineLatestProperty<T1,T2,TResult?>(prop1, prop2, func, setValueHook);
} }