diff --git a/src/AppCommon/FileTime.App.CommandPalette/ViewModels/CommandPaletteViewModel.cs b/src/AppCommon/FileTime.App.CommandPalette/ViewModels/CommandPaletteViewModel.cs index 967dce4..1cc8d50 100644 --- a/src/AppCommon/FileTime.App.CommandPalette/ViewModels/CommandPaletteViewModel.cs +++ b/src/AppCommon/FileTime.App.CommandPalette/ViewModels/CommandPaletteViewModel.cs @@ -56,6 +56,7 @@ public class CommandPaletteViewModel : FuzzyPanelViewModel, IDisposable IObservable> MarkedItems { get; } IDeclarativeProperty> SelectedsChildren { get; } IDeclarativeProperty> ParentsChildren { get; } + DeclarativeProperty Ordering { get; } void ClearMarkedItems(); void RemoveMarkedItem(FullName fullName); diff --git a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ToolUserCommandHandlerService.cs b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ToolUserCommandHandlerService.cs index a9561dd..2a1e92a 100644 --- a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ToolUserCommandHandlerService.cs +++ b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ToolUserCommandHandlerService.cs @@ -22,6 +22,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase private readonly IContentAccessorFactory _contentAccessorFactory; private IDeclarativeProperty? _currentLocation; private IDeclarativeProperty? _currentSelectedItem; + private ITabViewModel? _currentSelectedTab; public ToolUserCommandHandlerService( IAppState appState, @@ -42,6 +43,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase _contentAccessorFactory = contentAccessorFactory; SaveCurrentLocation(l => _currentLocation = l); SaveCurrentSelectedItem(i => _currentSelectedItem = i); + SaveSelectedTab(t => _currentSelectedTab = t); AddCommandHandlers(new IUserCommandHandler[] { @@ -49,9 +51,17 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase new TypeUserCommandHandler(CopyNativePath), new TypeUserCommandHandler(CopyBase64), new TypeUserCommandHandler(Search), + new TypeUserCommandHandler(SortItems), }); } + private async Task SortItems(SortItemsCommand sortItemsCommand) + { + if (_currentSelectedTab is null) return; + + await _currentSelectedTab.Ordering.SetValue(sortItemsCommand.Ordering); + } + private async Task CopyBase64() { var item = _currentSelectedItem?.Value?.BaseItem; diff --git a/src/AppCommon/FileTime.App.Core/StartupServices/DefaultIdentifiableCommandHandlerRegister.cs b/src/AppCommon/FileTime.App.Core/StartupServices/DefaultIdentifiableCommandHandlerRegister.cs index f7fd7c3..3e006bd 100644 --- a/src/AppCommon/FileTime.App.Core/StartupServices/DefaultIdentifiableCommandHandlerRegister.cs +++ b/src/AppCommon/FileTime.App.Core/StartupServices/DefaultIdentifiableCommandHandlerRegister.cs @@ -49,6 +49,10 @@ public class DefaultIdentifiableCommandHandlerRegister : IStartupHandler AddUserCommand(RenameCommand.Instance); AddUserCommand(RunOrOpenCommand.Instance); AddUserCommand(StartCommandSchedulerCommand.Instance); + AddUserCommand(SortItemsCommand.OrderByNameCommand); + AddUserCommand(SortItemsCommand.OrderByNameDescCommand); + AddUserCommand(SortItemsCommand.OrderByDateCommand); + AddUserCommand(SortItemsCommand.OrderByDateDescCommand); AddUserCommand(IdentifiableSearchCommand.SearchByNameContains); AddUserCommand(SwitchToTabCommand.SwitchToLastTab); AddUserCommand(SwitchToTabCommand.SwitchToTab1); diff --git a/src/AppCommon/FileTime.App.Core/ViewModels/TabViewModel.cs b/src/AppCommon/FileTime.App.Core/ViewModels/TabViewModel.cs index 0f5c0f7..a1647f4 100644 --- a/src/AppCommon/FileTime.App.Core/ViewModels/TabViewModel.cs +++ b/src/AppCommon/FileTime.App.Core/ViewModels/TabViewModel.cs @@ -1,9 +1,12 @@ using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq.Expressions; using System.Reactive.Linq; using DeclarativeProperty; using DynamicData; using DynamicData.Binding; using FileTime.App.Core.Extensions; +using FileTime.App.Core.Models; using FileTime.App.Core.Models.Enums; using FileTime.App.Core.Services; using FileTime.Core.Enums; @@ -45,6 +48,7 @@ public partial class TabViewModel : ITabViewModel public IObservable> MarkedItems { get; } public IDeclarativeProperty?> SelectedsChildren { get; private set; } public IDeclarativeProperty?> ParentsChildren { get; private set; } + public DeclarativeProperty Ordering { get; } = new(ItemOrdering.Name); public TabViewModel( @@ -71,14 +75,56 @@ public partial class TabViewModel : ITabViewModel CurrentLocation = tab.CurrentLocation; - CurrentItems = tab.CurrentItems - .Map((items, _) => - Task.FromResult?>( - items?.Selecting( - i => MapItemToViewModel(i, ItemViewModelType.Main) + CurrentItems = + tab.CurrentItems + .Map((items, _) => + Task.FromResult?>( + items?.Selecting( + i => MapItemToViewModel(i, ItemViewModelType.Main) + ) ) - ) - ); + ).CombineLatest( + Ordering, + (items, ordering) => + { + if (items is null) return Task.FromResult?>(null); + /*Expression> orderExpression = ordering switch + { + ItemOrdering.Name or ItemOrdering.NameDesc => i => i.DisplayNameText, + ItemOrdering.LastModifyDate or ItemOrdering.LastModifyDateDesc => i => i.CreatedAt, + _ => throw new NotImplementedException() + }; + Expression> direction = ordering switch + { + ItemOrdering.Name or ItemOrdering.LastModifyDate => () => ListSortDirection.Ascending, + ItemOrdering.NameDesc or ItemOrdering.LastModifyDateDesc => () => ListSortDirection.Descending, + _ => throw new NotImplementedException() + }; + return Task.FromResult((ObservableCollection?) items.Ordering(orderExpression, direction));*/ + + ObservableCollection? 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( () => CurrentItems.Subscribe(c => UpdateConsumer(c, ref _currentItemsConsumer)) ); @@ -127,6 +173,8 @@ public partial class TabViewModel : ITabViewModel var items = parent.Items .Selecting(i => MapItem(i)) + .Ordering(i => i.Type) + .ThenOrdering(i => i.Name) .Selecting(i => MapItemToViewModel(i, ItemViewModelType.SelectedChild)); return items; @@ -222,7 +270,7 @@ public partial class TabViewModel : ITabViewModel } } - public void ClearMarkedItems() + public void ClearMarkedItems() => _markedItems.Clear(); ~TabViewModel() => Dispose(false); diff --git a/src/AppCommon/FileTime.App.FrequencyNavigation/ViewModels/FrequencyNavigationViewModel.cs b/src/AppCommon/FileTime.App.FrequencyNavigation/ViewModels/FrequencyNavigationViewModel.cs index b40986b..da7111a 100644 --- a/src/AppCommon/FileTime.App.FrequencyNavigation/ViewModels/FrequencyNavigationViewModel.cs +++ b/src/AppCommon/FileTime.App.FrequencyNavigation/ViewModels/FrequencyNavigationViewModel.cs @@ -33,11 +33,12 @@ public class FrequencyNavigationViewModel : FuzzyPanelViewModel, IFreque public override async Task HandleKeyDown(KeyEventArgs keyEventArgs) { var handled = await base.HandleKeyDown(keyEventArgs); - + if (handled) return true; if (keyEventArgs.Key == Key.Enter) { + keyEventArgs.Handled = true; var targetContainer = await _timelessContentProvider.GetItemByFullNameAsync(new FullName(SelectedItem), PointInTime.Present); var openContainerCommand = new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, targetContainer)); await _userCommandHandlerService.HandleCommandAsync(openContainerCommand); diff --git a/src/AppCommon/FileTime.App.FuzzyPanel/FuzzyPanelViewModel.cs b/src/AppCommon/FileTime.App.FuzzyPanel/FuzzyPanelViewModel.cs index 74ca308..e80f447 100644 --- a/src/AppCommon/FileTime.App.FuzzyPanel/FuzzyPanelViewModel.cs +++ b/src/AppCommon/FileTime.App.FuzzyPanel/FuzzyPanelViewModel.cs @@ -46,6 +46,7 @@ public abstract partial class FuzzyPanelViewModel : IFuzzyPanelViewModel< if (nextItem is not null) { + keyEventArgs.Handled = true; SelectedItem = nextItem; } @@ -57,6 +58,7 @@ public abstract partial class FuzzyPanelViewModel : IFuzzyPanelViewModel< if (previousItem is not null) { + keyEventArgs.Handled = true; SelectedItem = previousItem; } diff --git a/src/Core/FileTime.Core.Services/Tab.cs b/src/Core/FileTime.Core.Services/Tab.cs index e158bd4..a405677 100644 --- a/src/Core/FileTime.Core.Services/Tab.cs +++ b/src/Core/FileTime.Core.Services/Tab.cs @@ -18,10 +18,10 @@ public class Tab : ITab private readonly DeclarativeProperty _currentLocation = new(null); private readonly BehaviorSubject _currentLocationForced = new(null); private readonly DeclarativeProperty _currentRequestItem = new(null); - private readonly SourceList _itemFilters = new(); + private readonly ObservableCollection _itemFilters = new(); + private readonly DeclarativeProperty?> _itemFiltersProperty; private AbsolutePath? _currentSelectedItemCached; private PointInTime _currentPointInTime; - private OcConsumer? _currentItemsConsumer; private CancellationTokenSource? _setCurrentLocationCancellationTokenSource; private CancellationTokenSource? _setCurrentItemCancellationTokenSource; @@ -38,6 +38,7 @@ public class Tab : ITab _timelessContentProvider = timelessContentProvider; _tabEvents = tabEvents; _currentPointInTime = null!; + _itemFiltersProperty = new(_itemFilters); _timelessContentProvider.CurrentPointInTime.Subscribe(p => _currentPointInTime = p); @@ -52,12 +53,25 @@ public class Tab : ITab return Task.CompletedTask; }); - CurrentItems = CurrentLocation - .Map((container, _) => + CurrentItems = DeclarativePropertyHelpers.CombineLatest( + CurrentLocation, + _itemFiltersProperty.Watch, ItemFilter>(), + (container, filters) => { - var items = container is null - ? (ObservableCollection?) null - : container.Items.Selecting(i => MapItem(i)); + ObservableCollection? items = null; + + if (container is not null) + { + items = container + .Items + .Selecting(i => MapItem(i)); + + if (filters is not null) + { + items = items.Filtering(i => filters.All(f => f.Filter(i))); + } + } + return Task.FromResult(items); } ); @@ -179,7 +193,7 @@ public class Tab : ITab 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); } @@ -194,8 +208,5 @@ public class Tab : ITab } public void Dispose() - { - _currentLocation.Dispose(); - _itemFilters.Dispose(); - } + => _currentLocation.Dispose(); } \ No newline at end of file diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Views/CommandPalette.axaml.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Views/CommandPalette.axaml.cs index 2a809d8..84409f9 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp/Views/CommandPalette.axaml.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Views/CommandPalette.axaml.cs @@ -30,6 +30,7 @@ public partial class CommandPalette : UserControl if (e.Key == Key.Escape) { + e.Handled = true; viewModel.Close(); } else @@ -37,9 +38,4 @@ public partial class CommandPalette : UserControl viewModel.HandleKeyDown(e); } } - - public void Test() - { - ; - } } \ No newline at end of file diff --git a/src/Library/DeclarativeProperty/DeclarativePropertyExtensions.cs b/src/Library/DeclarativeProperty/DeclarativePropertyExtensions.cs index 2dd08e2..99dc15a 100644 --- a/src/Library/DeclarativeProperty/DeclarativePropertyExtensions.cs +++ b/src/Library/DeclarativeProperty/DeclarativePropertyExtensions.cs @@ -69,4 +69,12 @@ public static class DeclarativePropertyExtensions this IDeclarativeProperty collection) where TCollection : IList, INotifyCollectionChanged => new CollectionRepeaterProperty(collection); + + + public static IDeclarativeProperty CombineLatest( + this IDeclarativeProperty prop1, + IDeclarativeProperty prop2, + Func> func, + Action? setValueHook = null) + => new CombineLatestProperty(prop1, prop2, func, setValueHook); } \ No newline at end of file