diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/Command/Command.cs b/src/AppCommon/FileTime.App.Core.Abstraction/Command/Command.cs index 80e8b55..d0b093a 100644 --- a/src/AppCommon/FileTime.App.Core.Abstraction/Command/Command.cs +++ b/src/AppCommon/FileTime.App.Core.Abstraction/Command/Command.cs @@ -16,6 +16,7 @@ public enum Command Cut, Edit, EnterRapidTravel, + ExitRapidTravel, FindByName, FindByNameRegex, GoToHome, diff --git a/src/AppCommon/FileTime.App.Core.Abstraction/ViewModels/IAppState.cs b/src/AppCommon/FileTime.App.Core.Abstraction/ViewModels/IAppState.cs index 939a68f..a284a32 100644 --- a/src/AppCommon/FileTime.App.Core.Abstraction/ViewModels/IAppState.cs +++ b/src/AppCommon/FileTime.App.Core.Abstraction/ViewModels/IAppState.cs @@ -5,14 +5,16 @@ namespace FileTime.App.Core.ViewModels; public interface IAppState { - ObservableCollection Tabs { get; } + ReadOnlyObservableCollection Tabs { get; } IObservable SelectedTab { get; } IObservable SearchText { get; } IObservable ViewMode { get; } string RapidTravelText { get; set; } + ITabViewModel? CurrentSelectedTab { get; } void AddTab(ITabViewModel tabViewModel); void RemoveTab(ITabViewModel tabViewModel); void SetSearchText(string? searchText); void SwitchViewMode(ViewMode newViewMode); + void SetSelectedTab(ITabViewModel tabToSelect); } \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core/FileTime.App.Core.csproj b/src/AppCommon/FileTime.App.Core/FileTime.App.Core.csproj index 228348f..90a0c45 100644 --- a/src/AppCommon/FileTime.App.Core/FileTime.App.Core.csproj +++ b/src/AppCommon/FileTime.App.Core/FileTime.App.Core.csproj @@ -20,6 +20,7 @@ + diff --git a/src/AppCommon/FileTime.App.Core/Services/CommandHandler/NavigationCommandHandler.cs b/src/AppCommon/FileTime.App.Core/Services/CommandHandler/NavigationCommandHandler.cs index 595a4a1..cade34c 100644 --- a/src/AppCommon/FileTime.App.Core/Services/CommandHandler/NavigationCommandHandler.cs +++ b/src/AppCommon/FileTime.App.Core/Services/CommandHandler/NavigationCommandHandler.cs @@ -4,33 +4,60 @@ using FileTime.App.Core.Extensions; using FileTime.App.Core.Models.Enums; using FileTime.App.Core.ViewModels; using FileTime.Core.Models; +using FileTime.Core.Services; +using FileTime.Providers.Local; +using Microsoft.Extensions.DependencyInjection; namespace FileTime.App.Core.Services.CommandHandler; public class NavigationCommandHandler : CommandHandlerBase { private readonly IAppState _appState; + private readonly IServiceProvider _serviceProvider; + private readonly ILocalContentProvider _localContentProvider; + private readonly ICommandHandlerService _commandHandlerService; private ITabViewModel? _selectedTab; private IContainer? _currentLocation; private IItemViewModel? _currentSelectedItem; private IEnumerable _currentItems = Enumerable.Empty(); + private ViewMode _viewMode; - public NavigationCommandHandler(IAppState appState) : base(appState) + public NavigationCommandHandler( + IAppState appState, + IServiceProvider serviceProvider, + ILocalContentProvider localContentProvider, + ICommandHandlerService commandHandlerService) : base(appState) { _appState = appState; + _serviceProvider = serviceProvider; + _localContentProvider = localContentProvider; + _commandHandlerService = commandHandlerService; SaveSelectedTab(t => _selectedTab = t); SaveCurrentSelectedItem(i => _currentSelectedItem = i); SaveCurrentLocation(l => _currentLocation = l); SaveCurrentItems(i => _currentItems = i); + appState.ViewMode.Subscribe(v => _viewMode = v); + AddCommandHandlers(new (Command.Command, Func)[] { + (Command.Command.CloseTab, CloseTab), (Command.Command.EnterRapidTravel, EnterRapidTravel), + (Command.Command.ExitRapidTravel, ExitRapidTravel), (Command.Command.GoUp, GoUp), (Command.Command.MoveCursorDown, MoveCursorDown), (Command.Command.MoveCursorUp, MoveCursorUp), (Command.Command.Open, OpenContainer), + (Command.Command.SwitchToLastTab, async () => await SwitchToTab(-1)), + (Command.Command.SwitchToTab1, async () => await SwitchToTab(1)), + (Command.Command.SwitchToTab2, async () => await SwitchToTab(2)), + (Command.Command.SwitchToTab3, async () => await SwitchToTab(3)), + (Command.Command.SwitchToTab4, async () => await SwitchToTab(4)), + (Command.Command.SwitchToTab5, async () => await SwitchToTab(5)), + (Command.Command.SwitchToTab6, async () => await SwitchToTab(6)), + (Command.Command.SwitchToTab7, async () => await SwitchToTab(7)), + (Command.Command.SwitchToTab8, async () => await SwitchToTab(8)), }); } @@ -75,4 +102,47 @@ public class NavigationCommandHandler : CommandHandlerBase _appState.SwitchViewMode(ViewMode.RapidTravel); return Task.CompletedTask; } + + private Task ExitRapidTravel() + { + _appState.SwitchViewMode(ViewMode.Default); + return Task.CompletedTask; + } + + private Task SwitchToTab(int number) + { + var tabViewModel = _appState.Tabs.FirstOrDefault(t => t.TabNumber == number); + + if (number == -1) + { + var greatestNumber = _appState.Tabs.Max(t => t.TabNumber); + tabViewModel = _appState.Tabs.FirstOrDefault(t => t.TabNumber == greatestNumber); + } + else if (tabViewModel == null) + { + var tab = _serviceProvider.GetInitableResolver(_currentLocation ?? _localContentProvider).GetRequiredService(); + var newTabViewModel = _serviceProvider.GetInitableResolver(tab, number).GetRequiredService(); + + _appState.AddTab(newTabViewModel); + tabViewModel = newTabViewModel; + } + + if (_viewMode == ViewMode.RapidTravel) + { + _commandHandlerService.HandleCommandAsync(Command.Command.ExitRapidTravel); + } + + _appState.SetSelectedTab(tabViewModel!); + + return Task.CompletedTask; + } + + private Task CloseTab() + { + if (_appState.Tabs.Count < 2) return Task.CompletedTask; + + _appState.RemoveTab(_selectedTab!); + + return Task.CompletedTask; + } } \ No newline at end of file diff --git a/src/AppCommon/FileTime.App.Core/ViewModels/AppStateBase.cs b/src/AppCommon/FileTime.App.Core/ViewModels/AppStateBase.cs index 531e5a0..9e815a6 100644 --- a/src/AppCommon/FileTime.App.Core/ViewModels/AppStateBase.cs +++ b/src/AppCommon/FileTime.App.Core/ViewModels/AppStateBase.cs @@ -1,6 +1,7 @@ using System.Collections.ObjectModel; using System.Reactive.Linq; using System.Reactive.Subjects; +using DynamicData; using FileTime.App.Core.Models.Enums; using MvvmGen; using MoreLinq; @@ -12,15 +13,16 @@ public abstract partial class AppStateBase : IAppState { private readonly BehaviorSubject _searchText = new(null); private readonly BehaviorSubject _selectedTab = new(null); - private readonly BehaviorSubject> _tabs = new(Enumerable.Empty()); private readonly BehaviorSubject _viewMode = new(Models.Enums.ViewMode.Default); + private readonly SourceList _tabs = new(); public IObservable ViewMode { get; private set; } - public ObservableCollection Tabs { get; } = new(); + public ReadOnlyObservableCollection Tabs { get; private set; } public IObservable SearchText { get; private set; } public IObservable SelectedTab { get; private set; } + public ITabViewModel? CurrentSelectedTab { get; private set; } [Property] private string _rapidTravelText = ""; @@ -28,21 +30,39 @@ public abstract partial class AppStateBase : IAppState { ViewMode = _viewMode.AsObservable(); - Tabs.CollectionChanged += (_, _) => _tabs.OnNext(Tabs); + var tabsObservable = _tabs.Connect(); + SearchText = _searchText.AsObservable(); - SelectedTab = Observable.CombineLatest(_tabs, _selectedTab, GetSelectedTab); + SelectedTab = Observable.CombineLatest(tabsObservable.ToCollection(), _selectedTab.DistinctUntilChanged(), GetSelectedTab); + + SelectedTab.Subscribe(t => + { + _selectedTab.OnNext(t); + CurrentSelectedTab = t; + }); + + tabsObservable + .Bind(out var collection) + .DisposeMany() + .Subscribe(); + + Tabs = collection; } public void AddTab(ITabViewModel tabViewModel) { - Tabs.Add(tabViewModel); + if (_tabs.Items.Any(t => t.TabNumber == tabViewModel.TabNumber)) + throw new ArgumentException($"There is a tab with the same tab number {tabViewModel.TabNumber}.", nameof(tabViewModel)); + + var index = _tabs.Items.Count(t => t.TabNumber < tabViewModel.TabNumber); + _tabs.Insert(index, tabViewModel); } public void RemoveTab(ITabViewModel tabViewModel) { - if (!Tabs.Contains(tabViewModel)) return; + if (!_tabs.Items.Contains(tabViewModel)) return; - Tabs.Remove(tabViewModel); + _tabs.Remove(tabViewModel); } public void SetSearchText(string? searchText) => _searchText.OnNext(searchText); @@ -56,7 +76,7 @@ public abstract partial class AppStateBase : IAppState private ITabViewModel? GetSelectedTab(IEnumerable tabs, ITabViewModel? expectedSelectedTab) { - var (prefered, others) = tabs.OrderBy(t => t.TabNumber).Partition(t => t.TabNumber >= (expectedSelectedTab?.TabNumber ?? 0)); - return prefered.Concat(others).FirstOrDefault(); + var (preferred, others) = tabs.OrderBy(t => t.TabNumber).Partition(t => t.TabNumber >= (expectedSelectedTab?.TabNumber ?? 0)); + return preferred.Concat(others).FirstOrDefault(); } } \ No newline at end of file diff --git a/src/GuiApp/Avalonia/FileTime.GuiApp/Services/RapidTravelModeKeyInputHandler.cs b/src/GuiApp/Avalonia/FileTime.GuiApp/Services/RapidTravelModeKeyInputHandler.cs index 901f393..b3aa70c 100644 --- a/src/GuiApp/Avalonia/FileTime.GuiApp/Services/RapidTravelModeKeyInputHandler.cs +++ b/src/GuiApp/Avalonia/FileTime.GuiApp/Services/RapidTravelModeKeyInputHandler.cs @@ -56,7 +56,7 @@ public class RapidTravelModeKeyInputHandler : IRapidTravelModeKeyInputHandler } else { - _appState.SwitchViewMode(ViewMode.Default); + await CallCommandAsync(Command.ExitRapidTravel); } } else if (key == Key.Back)