diff --git a/src/Files.App/Actions/Navigation/DuplicateCurrentTabAction.cs b/src/Files.App/Actions/Navigation/DuplicateCurrentTabAction.cs index f18247b048d2..6c8984e8e110 100644 --- a/src/Files.App/Actions/Navigation/DuplicateCurrentTabAction.cs +++ b/src/Files.App/Actions/Navigation/DuplicateCurrentTabAction.cs @@ -7,8 +7,6 @@ internal class DuplicateCurrentTabAction : IAction { private readonly IMultitaskingContext context; - private readonly MainPageViewModel mainPageViewModel; - public string Label => "DuplicateTab".GetLocalizedResource(); @@ -18,16 +16,15 @@ public string Description public DuplicateCurrentTabAction() { context = Ioc.Default.GetRequiredService(); - mainPageViewModel = Ioc.Default.GetRequiredService(); } public async Task ExecuteAsync() { var arguments = context.CurrentTabItem.NavigationParameter; if (arguments is null) - await mainPageViewModel.AddNewTabByPathAsync(typeof(PaneHolderPage), "Home"); + await MultitaskingTabsHelpers.AddNewTabWithPathAsync(typeof(PaneHolderPage), "Home"); else - await mainPageViewModel.AddNewTabByParamAsync(arguments.InitialPageType, arguments.NavigationParameter, context.CurrentTabIndex + 1); + await MultitaskingTabsHelpers.AddNewTabWithParameterAsync(arguments.InitialPageType, arguments.NavigationParameter, context.CurrentTabIndex + 1); } } } diff --git a/src/Files.App/Actions/Navigation/DuplicateSelectedTabAction.cs b/src/Files.App/Actions/Navigation/DuplicateSelectedTabAction.cs index 10f22d9bc346..81d9afe15e11 100644 --- a/src/Files.App/Actions/Navigation/DuplicateSelectedTabAction.cs +++ b/src/Files.App/Actions/Navigation/DuplicateSelectedTabAction.cs @@ -7,8 +7,6 @@ internal class DuplicateSelectedTabAction : IAction { private readonly IMultitaskingContext context; - private readonly MainPageViewModel mainPageViewModel; - public string Label => "DuplicateTab".GetLocalizedResource(); @@ -21,16 +19,15 @@ public HotKey HotKey public DuplicateSelectedTabAction() { context = Ioc.Default.GetRequiredService(); - mainPageViewModel = Ioc.Default.GetRequiredService(); } public async Task ExecuteAsync() { var arguments = context.SelectedTabItem.NavigationParameter; if (arguments is null) - await mainPageViewModel.AddNewTabByPathAsync(typeof(PaneHolderPage), "Home"); + await MultitaskingTabsHelpers.AddNewTabWithPathAsync(typeof(PaneHolderPage), "Home"); else - await mainPageViewModel.AddNewTabByParamAsync(arguments.InitialPageType, arguments.NavigationParameter, context.SelectedTabIndex + 1); + await MultitaskingTabsHelpers.AddNewTabWithParameterAsync(arguments.InitialPageType, arguments.NavigationParameter, context.SelectedTabIndex + 1); } } } diff --git a/src/Files.App/Actions/Navigation/NewTabAction.cs b/src/Files.App/Actions/Navigation/NewTabAction.cs index 879cb369c997..2a530921a2ec 100644 --- a/src/Files.App/Actions/Navigation/NewTabAction.cs +++ b/src/Files.App/Actions/Navigation/NewTabAction.cs @@ -5,8 +5,6 @@ namespace Files.App.Actions { internal class NewTabAction : IAction { - private readonly MainPageViewModel mainPageViewModel; - public string Label => "NewTab".GetLocalizedResource(); @@ -18,12 +16,11 @@ public HotKey HotKey public NewTabAction() { - mainPageViewModel = Ioc.Default.GetRequiredService(); } public Task ExecuteAsync() { - return mainPageViewModel.AddNewTabAsync(); + return MultitaskingTabsHelpers.AddNewTabAsync(); } } } diff --git a/src/Files.App/Actions/Navigation/OpenDirectoryInNewTabAction.cs b/src/Files.App/Actions/Navigation/OpenDirectoryInNewTabAction.cs index be2e70b9ca5a..bed55ffe9b01 100644 --- a/src/Files.App/Actions/Navigation/OpenDirectoryInNewTabAction.cs +++ b/src/Files.App/Actions/Navigation/OpenDirectoryInNewTabAction.cs @@ -40,7 +40,7 @@ public async Task ExecuteAsync() { await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => { - await _mainPageViewModel.AddNewTabByPathAsync( + await MultitaskingTabsHelpers.AddNewTabWithPathAsync( typeof(PaneHolderPage), (listedItem as ShortcutItem)?.TargetPath ?? listedItem.ItemPath); }, diff --git a/src/Files.App/App.xaml.cs b/src/Files.App/App.xaml.cs index e7ec62d8ae24..6c2bbe24c1d1 100644 --- a/src/Files.App/App.xaml.cs +++ b/src/Files.App/App.xaml.cs @@ -323,8 +323,8 @@ private async void Window_ClosedAsync(object sender, WindowEventArgs args) // Save and close all tabs SaveSessionTabs(); - MainPageViewModel.AppInstances.ForEach(tabItem => tabItem.Unload()); - MainPageViewModel.AppInstances.Clear(); + MainPageViewModel.CurrentInstanceTabBarItems.ForEach(tabItem => tabItem.Unload()); + MainPageViewModel.CurrentInstanceTabBarItems.Clear(); // Wait for all properties windows to close await FilePropertiesHelpers.WaitClosingAll(); @@ -352,7 +352,7 @@ private async void Window_ClosedAsync(object sender, WindowEventArgs args) { await SafetyExtensions.IgnoreExceptions(async () => { - var instance = MainPageViewModel.AppInstances.FirstOrDefault(x => x.TabItemContent.IsCurrentInstance); + var instance = MainPageViewModel.CurrentInstanceTabBarItems.FirstOrDefault(x => x.TabItemContent.IsCurrentInstance); if (instance is null) return; @@ -392,7 +392,7 @@ public static void SaveSessionTabs() { IUserSettingsService userSettingsService = Ioc.Default.GetRequiredService(); - userSettingsService.GeneralSettingsService.LastSessionTabList = MainPageViewModel.AppInstances.DefaultIfEmpty().Select(tab => + userSettingsService.GeneralSettingsService.LastSessionTabList = MainPageViewModel.CurrentInstanceTabBarItems.DefaultIfEmpty().Select(tab => { if (tab is not null && tab.NavigationParameter is not null) { diff --git a/src/Files.App/Constants.cs b/src/Files.App/Constants.cs index 81c628b1299c..142347ffd115 100644 --- a/src/Files.App/Constants.cs +++ b/src/Files.App/Constants.cs @@ -5,6 +5,15 @@ namespace Files.App { public static class Constants { + public static class App + { + public const string AppName = "Files"; + + public const string AppLaunchAlias = "files-uwp:"; + + public const string AppStorePackageId = "49306atecsolution.FilesUWP"; + } + public static class AdaptiveLayout { public const float ExtraLargeThreshold = 85.0f; diff --git a/src/Files.App/Data/Contexts/Multitasking/MultitaskingContext.cs b/src/Files.App/Data/Contexts/Multitasking/MultitaskingContext.cs index d5536b223e43..d2a4978a5aa8 100644 --- a/src/Files.App/Data/Contexts/Multitasking/MultitaskingContext.cs +++ b/src/Files.App/Data/Contexts/Multitasking/MultitaskingContext.cs @@ -17,8 +17,8 @@ internal class MultitaskingContext : ObservableObject, IMultitaskingContext private ushort tabCount = 0; public ushort TabCount => tabCount; - public TabBarItem CurrentTabItem => MainPageViewModel.AppInstances[currentTabIndex]; - public TabBarItem SelectedTabItem => MainPageViewModel.AppInstances[selectedTabIndex]; + public TabBarItem CurrentTabItem => MainPageViewModel.CurrentInstanceTabBarItems[currentTabIndex]; + public TabBarItem SelectedTabItem => MainPageViewModel.CurrentInstanceTabBarItems[selectedTabIndex]; private ushort currentTabIndex = 0; public ushort CurrentTabIndex => currentTabIndex; @@ -28,7 +28,7 @@ internal class MultitaskingContext : ObservableObject, IMultitaskingContext public MultitaskingContext() { - MainPageViewModel.AppInstances.CollectionChanged += AppInstances_CollectionChanged; + MainPageViewModel.CurrentInstanceTabBarItems.CollectionChanged += AppInstances_CollectionChanged; App.AppModel.PropertyChanged += AppModel_PropertyChanged; BaseTabBar.OnLoaded += BaseMultitaskingControl_OnLoaded; TabBar.SelectedTabItemChanged += HorizontalMultitaskingControl_SelectedTabItemChanged; @@ -54,7 +54,7 @@ private void BaseMultitaskingControl_OnLoaded(object? sender, ITabBar control) private void HorizontalMultitaskingControl_SelectedTabItemChanged(object? sender, TabBarItem? e) { isPopupOpen = e is not null; - int newSelectedIndex = e is null ? currentTabIndex : MainPageViewModel.AppInstances.IndexOf(e); + int newSelectedIndex = e is null ? currentTabIndex : MainPageViewModel.CurrentInstanceTabBarItems.IndexOf(e); UpdateSelectedTabIndex(newSelectedIndex); } private void FocusManager_GotFocus(object? sender, FocusManagerGotFocusEventArgs e) @@ -64,7 +64,7 @@ private void FocusManager_GotFocus(object? sender, FocusManagerGotFocusEventArgs if (e.NewFocusedElement is FrameworkElement element && element.DataContext is TabBarItem tabItem) { - int newSelectedIndex = MainPageViewModel.AppInstances.IndexOf(tabItem); + int newSelectedIndex = MainPageViewModel.CurrentInstanceTabBarItems.IndexOf(tabItem); UpdateSelectedTabIndex(newSelectedIndex); } } @@ -81,7 +81,7 @@ private void FocusManager_LosingFocus(object? sender, LosingFocusEventArgs e) private void UpdateTabCount() { - SetProperty(ref tabCount, (ushort)MainPageViewModel.AppInstances.Count, nameof(TabCount)); + SetProperty(ref tabCount, (ushort)MainPageViewModel.CurrentInstanceTabBarItems.Count, nameof(TabCount)); } private void UpdateCurrentTabIndex() { diff --git a/src/Files.App/Data/Models/AppModel.cs b/src/Files.App/Data/Models/AppModel.cs index f41da4bf19b8..b7d1bfedb602 100644 --- a/src/Files.App/Data/Models/AppModel.cs +++ b/src/Files.App/Data/Models/AppModel.cs @@ -35,11 +35,10 @@ public int TabStripSelectedIndex { SetProperty(ref tabStripSelectedIndex, value); - if (value >= 0 && value < MainPageViewModel.AppInstances.Count) + if (value >= 0 && value < MainPageViewModel.CurrentInstanceTabBarItems.Count) { - Frame rootFrame = (Frame)MainWindow.Instance.Content; - var mainView = (MainPage)rootFrame.Content; - mainView.ViewModel.SelectedTabItem = MainPageViewModel.AppInstances[value]; + var mainPageViewModel = Ioc.Default.GetRequiredService(); + mainPageViewModel.SelectedTabBarItem = MainPageViewModel.CurrentInstanceTabBarItems[value]; } } } diff --git a/src/Files.App/Helpers/Navigation/MultitaskingTabsHelpers.cs b/src/Files.App/Helpers/Navigation/MultitaskingTabsHelpers.cs index f919c1c32ac5..78c5514fa658 100644 --- a/src/Files.App/Helpers/Navigation/MultitaskingTabsHelpers.cs +++ b/src/Files.App/Helpers/Navigation/MultitaskingTabsHelpers.cs @@ -1,20 +1,87 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.App.UserControls.TabBar; -using Files.App.ViewModels; -using System.Linq; -using System.Threading.Tasks; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media.Imaging; namespace Files.App.Helpers { + /// + /// Provides static helper for handling . + /// public static class MultitaskingTabsHelpers { + private static readonly DrivesViewModel _drivesViewModel = Ioc.Default.GetRequiredService(); + private static readonly NetworkDrivesViewModel _networkDrivesViewModel = Ioc.Default.GetRequiredService(); + private static readonly MainPageViewModel _mainPageViewModel = Ioc.Default.GetRequiredService(); + + public static async Task AddNewTabAsync() + { + await AddNewTabWithPathAsync(typeof(PaneHolderPage), "Home"); + } + + public static async Task AddNewTabWithPathAsync(Type type, string? path, int atIndex = -1) + { + if (string.IsNullOrEmpty(path)) + path = "Home"; + + // Support drives launched through jump list by stripping away the question mark at the end. + if (path.EndsWith("\\?")) + path = path.Remove(path.Length - 1); + + var tabItem = new TabBarItem + { + Header = null, + IconSource = null, + Description = null, + ToolTipText = null, + NavigationParameter = new CustomTabViewItemParameter() + { + InitialPageType = type, + NavigationParameter = path + } + }; + + tabItem.ContentChanged += TabViewItemContentFrame_ContentChanged; + + await UpdateTabInfoAsync(tabItem, path); + + var index = atIndex == -1 ? MainPageViewModel.CurrentInstanceTabBarItems.Count : atIndex; + + MainPageViewModel.CurrentInstanceTabBarItems.Insert(index, tabItem); + + App.AppModel.TabStripSelectedIndex = index; + } + + public static async Task AddNewTabWithParameterAsync(Type type, object tabViewItemArgs, int atIndex = -1) + { + var tabItem = new TabBarItem + { + Header = null, + IconSource = null, + Description = null, + ToolTipText = null, + NavigationParameter = new CustomTabViewItemParameter() + { + InitialPageType = type, + NavigationParameter = tabViewItemArgs + } + }; + + tabItem.ContentChanged += TabViewItemContentFrame_ContentChanged; + + await UpdateTabInfoAsync(tabItem, tabViewItemArgs); + + var index = atIndex == -1 ? MainPageViewModel.CurrentInstanceTabBarItems.Count : atIndex; + MainPageViewModel.CurrentInstanceTabBarItems.Insert(index, tabItem); + App.AppModel.TabStripSelectedIndex = index; + } + public static void CloseTabsToTheLeft(TabBarItem clickedTab, ITabBar multitaskingControl) { if (multitaskingControl is not null) { - var tabs = MainPageViewModel.AppInstances; + var tabs = MainPageViewModel.CurrentInstanceTabBarItems; var currentIndex = tabs.IndexOf(clickedTab); tabs.Take(currentIndex).ToList().ForEach(tab => multitaskingControl.CloseTab(tab)); @@ -25,7 +92,7 @@ public static void CloseTabsToTheRight(TabBarItem clickedTab, ITabBar multitaski { if (multitaskingControl is not null) { - var tabs = MainPageViewModel.AppInstances; + var tabs = MainPageViewModel.CurrentInstanceTabBarItems; var currentIndex = tabs.IndexOf(clickedTab); tabs.Skip(currentIndex + 1).ToList().ForEach(tab => multitaskingControl.CloseTab(tab)); @@ -36,21 +103,186 @@ public static void CloseOtherTabs(TabBarItem clickedTab, ITabBar multitaskingCon { if (multitaskingControl is not null) { - var tabs = MainPageViewModel.AppInstances; + var tabs = MainPageViewModel.CurrentInstanceTabBarItems; tabs.Where((t) => t != clickedTab).ToList().ForEach(tab => multitaskingControl.CloseTab(tab)); } } public static Task MoveTabToNewWindow(TabBarItem tab, ITabBar multitaskingControl) { - int index = MainPageViewModel.AppInstances.IndexOf(tab); - CustomTabViewItemParameter tabItemArguments = MainPageViewModel.AppInstances[index].NavigationParameter; + int index = MainPageViewModel.CurrentInstanceTabBarItems.IndexOf(tab); + CustomTabViewItemParameter tabItemArguments = MainPageViewModel.CurrentInstanceTabBarItems[index].NavigationParameter; - multitaskingControl?.CloseTab(MainPageViewModel.AppInstances[index]); + multitaskingControl?.CloseTab(MainPageViewModel.CurrentInstanceTabBarItems[index]); return tabItemArguments is not null ? NavigationHelpers.OpenTabInNewWindowAsync(tabItemArguments.Serialize()) : NavigationHelpers.OpenPathInNewWindowAsync("Home"); } + + public static async Task UpdateInstancePropertiesAsync(object navigationArg) + { + string windowTitle = string.Empty; + + if (navigationArg is PaneNavigationArguments paneArgs) + { + if (!string.IsNullOrEmpty(paneArgs.LeftPaneNavPathParam) && !string.IsNullOrEmpty(paneArgs.RightPaneNavPathParam)) + { + var leftTabInfo = await GetSelectedTabInfoAsync(paneArgs.LeftPaneNavPathParam); + var rightTabInfo = await GetSelectedTabInfoAsync(paneArgs.RightPaneNavPathParam); + + windowTitle = $"{leftTabInfo.tabLocationHeader} | {rightTabInfo.tabLocationHeader}"; + } + else + { + (windowTitle, _, _) = await GetSelectedTabInfoAsync(paneArgs.LeftPaneNavPathParam); + } + } + else if (navigationArg is string pathArgs) + { + (windowTitle, _, _) = await GetSelectedTabInfoAsync(pathArgs); + } + + if (MainPageViewModel.CurrentInstanceTabBarItems.Count > 1) + windowTitle = $"{windowTitle} ({MainPageViewModel.CurrentInstanceTabBarItems.Count})"; + + if (navigationArg == _mainPageViewModel.SelectedTabBarItem?.NavigationParameter?.NavigationParameter) + MainWindow.Instance.AppWindow.Title = $"{windowTitle} - Files"; + } + + public static async Task UpdateTabInfoAsync(TabBarItem tabItem, object navigationArg) + { + tabItem.AllowStorageItemDrop = true; + + (string, IconSource, string) result = (null, null, null); + + if (navigationArg is PaneNavigationArguments paneArgs) + { + if (!string.IsNullOrEmpty(paneArgs.LeftPaneNavPathParam) && !string.IsNullOrEmpty(paneArgs.RightPaneNavPathParam)) + { + var leftTabInfo = await GetSelectedTabInfoAsync(paneArgs.LeftPaneNavPathParam); + var rightTabInfo = await GetSelectedTabInfoAsync(paneArgs.RightPaneNavPathParam); + + result = ($"{leftTabInfo.tabLocationHeader} | {rightTabInfo.tabLocationHeader}", + leftTabInfo.tabIcon, + $"{leftTabInfo.toolTipText} | {rightTabInfo.toolTipText}"); + } + else + { + result = await GetSelectedTabInfoAsync(paneArgs.LeftPaneNavPathParam); + } + } + else if (navigationArg is string pathArgs) + { + result = await GetSelectedTabInfoAsync(pathArgs); + } + + // Don't update tabItem if the contents of the tab have already changed + if (result.Item1 is not null && navigationArg == tabItem.NavigationParameter.NavigationParameter) + (tabItem.Header, tabItem.IconSource, tabItem.ToolTipText) = result; + } + + public static async Task<(string tabLocationHeader, IconSource tabIcon, string toolTipText)> GetSelectedTabInfoAsync(string currentPath) + { + string? tabLocationHeader; + var iconSource = new ImageIconSource(); + string toolTipText = currentPath; + + if (string.IsNullOrEmpty(currentPath) || currentPath == "Home") + { + tabLocationHeader = "Home".GetLocalizedResource(); + + iconSource.ImageSource = new BitmapImage(new Uri(Constants.FluentIconsPaths.HomeIcon)); + } + else if (currentPath.Equals(Constants.UserEnvironmentPaths.DesktopPath, StringComparison.OrdinalIgnoreCase)) + { + tabLocationHeader = "Desktop".GetLocalizedResource(); + } + else if (currentPath.Equals(Constants.UserEnvironmentPaths.DownloadsPath, StringComparison.OrdinalIgnoreCase)) + { + tabLocationHeader = "Downloads".GetLocalizedResource(); + } + else if (currentPath.Equals(Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.OrdinalIgnoreCase)) + { + tabLocationHeader = "RecycleBin".GetLocalizedResource(); + + // Use 48 for higher resolution, the other items look fine with 16. + var iconData = await FileThumbnailHelper.LoadIconFromPathAsync(currentPath, 48u, Windows.Storage.FileProperties.ThumbnailMode.ListView, Windows.Storage.FileProperties.ThumbnailOptions.UseCurrentScale, true); + if (iconData is not null) + iconSource.ImageSource = await iconData.ToBitmapAsync(); + } + else if (currentPath.Equals(Constants.UserEnvironmentPaths.MyComputerPath, StringComparison.OrdinalIgnoreCase)) + { + tabLocationHeader = "ThisPC".GetLocalizedResource(); + } + else if (currentPath.Equals(Constants.UserEnvironmentPaths.NetworkFolderPath, StringComparison.OrdinalIgnoreCase)) + { + tabLocationHeader = "SidebarNetworkDrives".GetLocalizedResource(); + } + else if (App.LibraryManager.TryGetLibrary(currentPath, out LibraryLocationItem library)) + { + var libName = System.IO.Path.GetFileNameWithoutExtension(library.Path).GetLocalizedResource(); + // If localized string is empty use the library name. + tabLocationHeader = string.IsNullOrEmpty(libName) ? library.Text : libName; + } + else if (App.WSLDistroManager.TryGetDistro(currentPath, out WslDistroItem? wslDistro) && currentPath.Equals(wslDistro.Path)) + { + tabLocationHeader = wslDistro.Text; + iconSource.ImageSource = new BitmapImage(wslDistro.Icon); + } + else + { + var normalizedCurrentPath = PathNormalization.NormalizePath(currentPath); + + var matchingCloudDrive = App.CloudDrivesManager.Drives.FirstOrDefault(x => normalizedCurrentPath.Equals(PathNormalization.NormalizePath(x.Path), StringComparison.OrdinalIgnoreCase)); + if (matchingCloudDrive is not null) + { + iconSource.ImageSource = matchingCloudDrive.Icon; + tabLocationHeader = matchingCloudDrive.Text; + } + else if (PathNormalization.NormalizePath(PathNormalization.GetPathRoot(currentPath)) == normalizedCurrentPath) // If path is a drive's root + { + var matchingDrive = _networkDrivesViewModel.Drives.Cast().FirstOrDefault(netDrive => normalizedCurrentPath.Contains(PathNormalization.NormalizePath(netDrive.Path), StringComparison.OrdinalIgnoreCase)); + matchingDrive ??= _drivesViewModel.Drives.Cast().FirstOrDefault(drive => normalizedCurrentPath.Contains(PathNormalization.NormalizePath(drive.Path), StringComparison.OrdinalIgnoreCase)); + tabLocationHeader = matchingDrive is not null ? matchingDrive.Text : normalizedCurrentPath; + } + else + { + tabLocationHeader = currentPath.TrimEnd(System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar).Split('\\', StringSplitOptions.RemoveEmptyEntries).Last(); + + FilesystemResult rootItem = await FilesystemTasks.Wrap(() => DriveHelpers.GetRootFromPathAsync(currentPath)); + + if (rootItem) + { + BaseStorageFolder currentFolder = await FilesystemTasks.Wrap( + () => StorageFileExtensions.DangerousGetFolderFromPathAsync(currentPath, rootItem)); + + if (currentFolder is not null && !string.IsNullOrEmpty(currentFolder.DisplayName)) + tabLocationHeader = currentFolder.DisplayName; + } + } + } + + if (iconSource.ImageSource is null) + { + var iconData = await FileThumbnailHelper.LoadIconFromPathAsync(currentPath, 16u, Windows.Storage.FileProperties.ThumbnailMode.ListView, Windows.Storage.FileProperties.ThumbnailOptions.UseCurrentScale, true); + if (iconData is not null) + iconSource.ImageSource = await iconData.ToBitmapAsync(); + } + + return (tabLocationHeader, iconSource, toolTipText); + } + + public static async void TabViewItemContentFrame_ContentChanged(object? sender, CustomTabViewItemParameter e) + { + if (sender is null) + return; + + var matchingTabItem = MainPageViewModel.CurrentInstanceTabBarItems.SingleOrDefault(x => x == (TabBarItem)sender); + if (matchingTabItem is null) + return; + + await UpdateTabInfoAsync(matchingTabItem, e.NavigationParameter); + } } } diff --git a/src/Files.App/Helpers/Navigation/NavigationHelpers.cs b/src/Files.App/Helpers/Navigation/NavigationHelpers.cs index c004026e3cf6..88db45979716 100644 --- a/src/Files.App/Helpers/Navigation/NavigationHelpers.cs +++ b/src/Files.App/Helpers/Navigation/NavigationHelpers.cs @@ -11,10 +11,10 @@ namespace Files.App.Helpers { public static class NavigationHelpers { - private static readonly IUserSettingsService userSettingsService = Ioc.Default.GetRequiredService(); private static readonly MainPageViewModel mainPageViewModel = Ioc.Default.GetRequiredService(); + public static Task OpenPathInNewTab(string? path) - => mainPageViewModel.AddNewTabByPathAsync(typeof(PaneHolderPage), path); + => MultitaskingTabsHelpers.AddNewTabWithPathAsync(typeof(PaneHolderPage), path); public static Task OpenPathInNewWindowAsync(string? path) { diff --git a/src/Files.App/Helpers/UI/UIHelpers.cs b/src/Files.App/Helpers/UI/UIHelpers.cs index 83dce59f37f0..627b7c5e4456 100644 --- a/src/Files.App/Helpers/UI/UIHelpers.cs +++ b/src/Files.App/Helpers/UI/UIHelpers.cs @@ -108,13 +108,12 @@ public static async Task TryShowAsync(this IDialog 0)) + else if (!(string.IsNullOrEmpty(launchArgs.Arguments) && MainPageViewModel.CurrentInstanceTabBarItems.Count > 0)) { - await mainPageViewModel.AddNewTabByPathAsync(typeof(PaneHolderPage), launchArgs.Arguments); + await MultitaskingTabsHelpers.AddNewTabWithPathAsync(typeof(PaneHolderPage), launchArgs.Arguments); } else { @@ -166,7 +166,7 @@ public async Task InitializeApplicationAsync(object activatedEventArgs) case IFileActivatedEventArgs fileArgs: var index = 0; - if (rootFrame.Content is null || rootFrame.Content is SplashScreenPage || !MainPageViewModel.AppInstances.Any()) + if (rootFrame.Content is null || rootFrame.Content is SplashScreenPage || !MainPageViewModel.CurrentInstanceTabBarItems.Any()) { // When the navigation stack isn't restored navigate to the first page, // configuring the new page by passing required information as a navigation parameter @@ -175,7 +175,7 @@ public async Task InitializeApplicationAsync(object activatedEventArgs) } for (; index < fileArgs.Files.Count; index++) { - await mainPageViewModel.AddNewTabByPathAsync(typeof(PaneHolderPage), fileArgs.Files[index].Path); + await MultitaskingTabsHelpers.AddNewTabWithPathAsync(typeof(PaneHolderPage), fileArgs.Files[index].Path); } break; } @@ -234,8 +234,8 @@ async Task PerformNavigationAsync(string payload, string selectItem = null) RightPaneNavPathParam = Bounds.Width > PaneHolderPage.DualPaneWidthThreshold && (generalSettingsService?.AlwaysOpenDualPaneInNewTab ?? false) ? "Home" : null, }; - if (rootFrame.Content is MainPage && MainPageViewModel.AppInstances.Any()) - await mainPageViewModel.AddNewTabByParamAsync(typeof(PaneHolderPage), paneNavigationArgs); + if (rootFrame.Content is MainPage && MainPageViewModel.CurrentInstanceTabBarItems.Any()) + await MultitaskingTabsHelpers.AddNewTabWithParameterAsync(typeof(PaneHolderPage), paneNavigationArgs); else rootFrame.Navigate(typeof(MainPage), paneNavigationArgs, new SuppressNavigationTransitionInfo()); } diff --git a/src/Files.App/UserControls/TabBar/BaseTabBar.cs b/src/Files.App/UserControls/TabBar/BaseTabBar.cs index 26a693b70d7a..95f506ba7d15 100644 --- a/src/Files.App/UserControls/TabBar/BaseTabBar.cs +++ b/src/Files.App/UserControls/TabBar/BaseTabBar.cs @@ -11,8 +11,6 @@ namespace Files.App.UserControls.TabBar /// public abstract class BaseTabBar : UserControl, ITabBar { - protected readonly MainPageViewModel mainPageViewModel = Ioc.Default.GetRequiredService(); - protected ITabBarItemContent CurrentSelectedAppInstance; public static event EventHandler? OnLoaded; @@ -27,7 +25,7 @@ public abstract class BaseTabBar : UserControl, ITabBar public static Stack RecentlyClosedTabs { get; private set; } = new(); public ObservableCollection Items - => MainPageViewModel.AppInstances; + => MainPageViewModel.CurrentInstanceTabBarItems; public event EventHandler CurrentInstanceChanged; @@ -98,7 +96,7 @@ public void TabView_Loaded(object sender, RoutedEventArgs e) public ITabBarItemContent GetCurrentSelectedTabInstance() { - return MainPageViewModel.AppInstances[App.AppModel.TabStripSelectedIndex].TabItemContent; + return MainPageViewModel.CurrentInstanceTabBarItems[App.AppModel.TabStripSelectedIndex].TabItemContent; } public void SelectionChanged() @@ -114,7 +112,7 @@ public static void PushRecentTab(CustomTabViewItemParameter[] tab) public List GetAllTabInstances() { - return MainPageViewModel.AppInstances.Select(x => x.TabItemContent).ToList(); + return MainPageViewModel.CurrentInstanceTabBarItems.Select(x => x.TabItemContent).ToList(); } public async Task ReopenClosedTabAsync() @@ -124,7 +122,7 @@ public async Task ReopenClosedTabAsync() IsRestoringClosedTab = true; var lastTab = RecentlyClosedTabs.Pop(); foreach (var item in lastTab) - await mainPageViewModel.AddNewTabByParamAsync(item.InitialPageType, item.NavigationParameter); + await MultitaskingTabsHelpers.AddNewTabWithParameterAsync(item.InitialPageType, item.NavigationParameter); IsRestoringClosedTab = false; } @@ -155,7 +153,7 @@ public void SetLoadingIndicatorStatus(ITabBarItem item, bool loading) if (ContainerFromItem(item) is not Control tabItem) return; - var stateToGoName = (loading) ? "Loading" : "NotLoading"; + var stateToGoName = loading ? "Loading" : "NotLoading"; VisualStateManager.GoToState(tabItem, stateToGoName, false); } diff --git a/src/Files.App/UserControls/TabBar/TabBar.xaml.cs b/src/Files.App/UserControls/TabBar/TabBar.xaml.cs index 8deda089f782..30a6e5695128 100644 --- a/src/Files.App/UserControls/TabBar/TabBar.xaml.cs +++ b/src/Files.App/UserControls/TabBar/TabBar.xaml.cs @@ -188,7 +188,7 @@ private async void TabView_TabStripDropAsync(object sender, DragEventArgs e) var tabViewItemArgs = CustomTabViewItemParameter.Deserialize(tabViewItemString); ApplicationData.Current.LocalSettings.Values[TabDropHandledIdentifier] = true; - await mainPageViewModel.AddNewTabByParamAsync(tabViewItemArgs.InitialPageType, tabViewItemArgs.NavigationParameter, index); + await MultitaskingTabsHelpers.AddNewTabWithParameterAsync(tabViewItemArgs.InitialPageType, tabViewItemArgs.NavigationParameter, index); } private void TabView_TabDragCompleted(TabView sender, TabViewTabDragCompletedEventArgs args) diff --git a/src/Files.App/UserControls/TabBar/TabBarItem.cs b/src/Files.App/UserControls/TabBar/TabBarItem.cs index 625dd84b183c..21d4bc02d02c 100644 --- a/src/Files.App/UserControls/TabBar/TabBarItem.cs +++ b/src/Files.App/UserControls/TabBar/TabBarItem.cs @@ -86,9 +86,7 @@ public TabBarItem() public void Unload() { - MainPageViewModel mainPageViewModel = Ioc.Default.GetRequiredService(); - - ContentChanged -= mainPageViewModel.Control_ContentChangedAsync; + ContentChanged -= MultitaskingTabsHelpers.TabViewItemContentFrame_ContentChanged; Dispose(); } diff --git a/src/Files.App/ViewModels/MainPageViewModel.cs b/src/Files.App/ViewModels/MainPageViewModel.cs index 59ae6d10f4fc..527ab743f103 100644 --- a/src/Files.App/ViewModels/MainPageViewModel.cs +++ b/src/Files.App/ViewModels/MainPageViewModel.cs @@ -1,290 +1,85 @@ // Copyright (c) 2023 Files Community // Licensed under the MIT License. See the LICENSE. -using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Media.Imaging; using Microsoft.UI.Xaml.Navigation; using System.Windows.Input; using Windows.System; namespace Files.App.ViewModels { + /// + /// Represents the ViewModel for . + /// public class MainPageViewModel : ObservableObject { - private IUserSettingsService userSettingsService; - private IAppearanceSettingsService appearanceSettingsService; - private readonly DrivesViewModel drivesViewModel; - private readonly NetworkDrivesViewModel networkDrivesViewModel; - private IResourcesService resourcesService; + private readonly IUserSettingsService _userSettingsService = Ioc.Default.GetRequiredService(); + private readonly IAppearanceSettingsService _appearanceSettingsService = Ioc.Default.GetRequiredService(); + private readonly DrivesViewModel _drivesViewModel = Ioc.Default.GetRequiredService(); + private readonly NetworkDrivesViewModel _networkDrivesViewModel = Ioc.Default.GetRequiredService(); + private readonly IResourcesService _resourcesService = Ioc.Default.GetRequiredService(); + private SidebarViewModel SidebarAdaptiveViewModel { get; } = Ioc.Default.GetRequiredService(); - public ITabBar? MultitaskingControl { get; set; } + /// + /// Gets the tab items of the current instance. + /// + public static ObservableCollection CurrentInstanceTabBarItems { get; } = new(); - public List MultitaskingControls { get; } = new List(); + /// + /// Gets the TabBar control of the current instance. + /// + public ITabBar? CurrentInstanceTabBar { get; set; } - public static ObservableCollection AppInstances { get; private set; } = new ObservableCollection(); + // NOTE: This is useless because multi windowing is not supported for now + public List AllInstanceTabBars { get; private set; } = new(); - private Files.App.UserControls.TabBar.TabBarItem? selectedTabItem; - public Files.App.UserControls.TabBar.TabBarItem? SelectedTabItem - { - get => selectedTabItem; - set => SetProperty(ref selectedTabItem, value); - } + public double? ContentAreaActualWidth { get; set; } - public ICommand NavigateToNumberedTabKeyboardAcceleratorCommand { get; private set; } - public IAsyncRelayCommand OpenNewWindowAcceleratorCommand { get; private set; } + public bool ShouldViewControlBeDisplayed + => SidebarAdaptiveViewModel.PaneHolder?.ActivePane?.InstanceViewModel?.IsPageTypeNotHome ?? false; - public MainPageViewModel( - IUserSettingsService userSettings, - IAppearanceSettingsService appearanceSettings, - IResourcesService resources, - DrivesViewModel drivesViewModel, - NetworkDrivesViewModel networkDrivesViewModel) - { - userSettingsService = userSettings; - appearanceSettingsService = appearanceSettings; - this.drivesViewModel = drivesViewModel; - this.networkDrivesViewModel = networkDrivesViewModel; - resourcesService = resources; - // Create commands - NavigateToNumberedTabKeyboardAcceleratorCommand = new RelayCommand(NavigateToNumberedTabKeyboardAccelerator); - OpenNewWindowAcceleratorCommand = new AsyncRelayCommand(OpenNewWindowAcceleratorAsync); - } + public bool ShouldPreviewPaneBeActive + => _userSettingsService.PreviewPaneSettingsService.IsEnabled && ShouldPreviewPaneBeDisplayed; - private void NavigateToNumberedTabKeyboardAccelerator(KeyboardAcceleratorInvokedEventArgs? e) + public bool ShouldPreviewPaneBeDisplayed { - int indexToSelect = 0; - switch (e!.KeyboardAccelerator.Key) + get { - case VirtualKey.Number1: - indexToSelect = 0; - break; - - case VirtualKey.Number2: - indexToSelect = 1; - break; - - case VirtualKey.Number3: - indexToSelect = 2; - break; - - case VirtualKey.Number4: - indexToSelect = 3; - break; - - case VirtualKey.Number5: - indexToSelect = 4; - break; - - case VirtualKey.Number6: - indexToSelect = 5; - break; - - case VirtualKey.Number7: - indexToSelect = 6; - break; - - case VirtualKey.Number8: - indexToSelect = 7; - break; - - case VirtualKey.Number9: - // Select the last tab - indexToSelect = AppInstances.Count - 1; - break; - } - - // Only select the tab if it is in the list - if (indexToSelect < AppInstances.Count) - App.AppModel.TabStripSelectedIndex = indexToSelect; - e.Handled = true; - } + var isHomePage = !(SidebarAdaptiveViewModel.PaneHolder?.ActivePane?.InstanceViewModel?.IsPageTypeNotHome ?? false); - private async Task OpenNewWindowAcceleratorAsync(KeyboardAcceleratorInvokedEventArgs? e) - { - var filesUWPUri = new Uri("files-uwp:"); - await Launcher.LaunchUriAsync(filesUWPUri); - e!.Handled = true; - } + var isMultiPane = SidebarAdaptiveViewModel.PaneHolder?.IsMultiPaneActive ?? false; - public async Task AddNewTabByPathAsync(Type type, string? path, int atIndex = -1) - { - if (string.IsNullOrEmpty(path)) - path = "Home"; - else if (path.EndsWith("\\?")) // Support drives launched through jump list by stripping away the question mark at the end. - path = path.Remove(path.Length - 1); + var isBigEnough = MainWindow.Instance.Bounds.Width > 450 && MainWindow.Instance.Bounds.Height > 450 || + ContentAreaActualWidth > 700 && MainWindow.Instance.Bounds.Height > 360; - var tabItem = new Files.App.UserControls.TabBar.TabBarItem() - { - Header = null, - IconSource = null, - Description = null, - ToolTipText = null - }; - tabItem.NavigationParameter = new CustomTabViewItemParameter() - { - InitialPageType = type, - NavigationParameter = path - }; - tabItem.ContentChanged += Control_ContentChangedAsync; - await UpdateTabInfoAsync(tabItem, path); - var index = atIndex == -1 ? AppInstances.Count : atIndex; - AppInstances.Insert(index, tabItem); - App.AppModel.TabStripSelectedIndex = index; - } + var isEnabled = (!isHomePage || isMultiPane) && isBigEnough; - public async Task UpdateInstancePropertiesAsync(object navigationArg) - { - string windowTitle = string.Empty; - if (navigationArg is PaneNavigationArguments paneArgs) - { - if (!string.IsNullOrEmpty(paneArgs.LeftPaneNavPathParam) && !string.IsNullOrEmpty(paneArgs.RightPaneNavPathParam)) - { - var leftTabInfo = await GetSelectedTabInfoAsync(paneArgs.LeftPaneNavPathParam); - var rightTabInfo = await GetSelectedTabInfoAsync(paneArgs.RightPaneNavPathParam); - windowTitle = $"{leftTabInfo.tabLocationHeader} | {rightTabInfo.tabLocationHeader}"; - } - else - { - (windowTitle, _, _) = await GetSelectedTabInfoAsync(paneArgs.LeftPaneNavPathParam); - } + return isEnabled; } - else if (navigationArg is string pathArgs) - { - (windowTitle, _, _) = await GetSelectedTabInfoAsync(pathArgs); - } - - if (AppInstances.Count > 1) - windowTitle = $"{windowTitle} ({AppInstances.Count})"; - - if (navigationArg == SelectedTabItem?.NavigationParameter?.NavigationParameter) - MainWindow.Instance.AppWindow.Title = $"{windowTitle} - Files"; } - public async Task UpdateTabInfoAsync(Files.App.UserControls.TabBar.TabBarItem tabItem, object navigationArg) + private TabBarItem? _SelectedTabBarItem; + public TabBarItem? SelectedTabBarItem { - tabItem.AllowStorageItemDrop = true; - - (string, IconSource, string) result = (null, null, null); - if (navigationArg is PaneNavigationArguments paneArgs) - { - if (!string.IsNullOrEmpty(paneArgs.LeftPaneNavPathParam) && !string.IsNullOrEmpty(paneArgs.RightPaneNavPathParam)) - { - var leftTabInfo = await GetSelectedTabInfoAsync(paneArgs.LeftPaneNavPathParam); - var rightTabInfo = await GetSelectedTabInfoAsync(paneArgs.RightPaneNavPathParam); - result = ($"{leftTabInfo.tabLocationHeader} | {rightTabInfo.tabLocationHeader}", - leftTabInfo.tabIcon, - $"{leftTabInfo.toolTipText} | {rightTabInfo.toolTipText}"); - } - else - { - result = await GetSelectedTabInfoAsync(paneArgs.LeftPaneNavPathParam); - } - } - else if (navigationArg is string pathArgs) - { - result = await GetSelectedTabInfoAsync(pathArgs); - } - - // Don't update tabItem if the contents of the tab have already changed - if (result.Item1 is not null && navigationArg == tabItem.NavigationParameter.NavigationParameter) - (tabItem.Header, tabItem.IconSource, tabItem.ToolTipText) = result; + get => _SelectedTabBarItem; + set => SetProperty(ref _SelectedTabBarItem, value); } - public async Task<(string tabLocationHeader, IconSource tabIcon, string toolTipText)> GetSelectedTabInfoAsync(string currentPath) - { - string? tabLocationHeader; - var iconSource = new ImageIconSource(); - string toolTipText = currentPath; + public ICommand NavigateToNumberedTabKeyboardAcceleratorCommand; + public ICommand OpenNewWindowAcceleratorCommand; - if (string.IsNullOrEmpty(currentPath) || currentPath == "Home") - { - tabLocationHeader = "Home".GetLocalizedResource(); - iconSource.ImageSource = new BitmapImage(new Uri(Constants.FluentIconsPaths.HomeIcon)); - } - else if (currentPath.Equals(Constants.UserEnvironmentPaths.DesktopPath, StringComparison.OrdinalIgnoreCase)) - { - tabLocationHeader = "Desktop".GetLocalizedResource(); - } - else if (currentPath.Equals(Constants.UserEnvironmentPaths.DownloadsPath, StringComparison.OrdinalIgnoreCase)) - { - tabLocationHeader = "Downloads".GetLocalizedResource(); - } - else if (currentPath.Equals(Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.OrdinalIgnoreCase)) - { - tabLocationHeader = "RecycleBin".GetLocalizedResource(); - - // Use 48 for higher resolution, the other items look fine with 16. - var iconData = await FileThumbnailHelper.LoadIconFromPathAsync(currentPath, 48u, Windows.Storage.FileProperties.ThumbnailMode.ListView, Windows.Storage.FileProperties.ThumbnailOptions.UseCurrentScale, true); - if (iconData is not null) - iconSource.ImageSource = await iconData.ToBitmapAsync(); - } - else if (currentPath.Equals(Constants.UserEnvironmentPaths.MyComputerPath, StringComparison.OrdinalIgnoreCase)) - { - tabLocationHeader = "ThisPC".GetLocalizedResource(); - } - else if (currentPath.Equals(Constants.UserEnvironmentPaths.NetworkFolderPath, StringComparison.OrdinalIgnoreCase)) - { - tabLocationHeader = "SidebarNetworkDrives".GetLocalizedResource(); - } - else if (App.LibraryManager.TryGetLibrary(currentPath, out LibraryLocationItem library)) - { - var libName = System.IO.Path.GetFileNameWithoutExtension(library.Path).GetLocalizedResource(); - // If localized string is empty use the library name. - tabLocationHeader = string.IsNullOrEmpty(libName) ? library.Text : libName; - } - else if (App.WSLDistroManager.TryGetDistro(currentPath, out WslDistroItem? wslDistro) && currentPath.Equals(wslDistro.Path)) - { - tabLocationHeader = wslDistro.Text; - iconSource.ImageSource = new BitmapImage(wslDistro.Icon); - } - else - { - var normalizedCurrentPath = PathNormalization.NormalizePath(currentPath); - var matchingCloudDrive = App.CloudDrivesManager.Drives.FirstOrDefault(x => normalizedCurrentPath.Equals(PathNormalization.NormalizePath(x.Path), StringComparison.OrdinalIgnoreCase)); - if (matchingCloudDrive is not null) - { - iconSource.ImageSource = matchingCloudDrive.Icon; - tabLocationHeader = matchingCloudDrive.Text; - } - else if (PathNormalization.NormalizePath(PathNormalization.GetPathRoot(currentPath)) == normalizedCurrentPath) // If path is a drive's root - { - var matchingDrive = networkDrivesViewModel.Drives.Cast().FirstOrDefault(netDrive => normalizedCurrentPath.Contains(PathNormalization.NormalizePath(netDrive.Path), StringComparison.OrdinalIgnoreCase)); - matchingDrive ??= drivesViewModel.Drives.Cast().FirstOrDefault(drive => normalizedCurrentPath.Contains(PathNormalization.NormalizePath(drive.Path), StringComparison.OrdinalIgnoreCase)); - tabLocationHeader = matchingDrive is not null ? matchingDrive.Text : normalizedCurrentPath; - } - else - { - tabLocationHeader = currentPath.TrimEnd(System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar).Split('\\', StringSplitOptions.RemoveEmptyEntries).Last(); - - FilesystemResult rootItem = await FilesystemTasks.Wrap(() => DriveHelpers.GetRootFromPathAsync(currentPath)); - if (rootItem) - { - BaseStorageFolder currentFolder = await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFolderFromPathAsync(currentPath, rootItem)); - if (currentFolder is not null && !string.IsNullOrEmpty(currentFolder.DisplayName)) - tabLocationHeader = currentFolder.DisplayName; - } - } - } - - if (iconSource.ImageSource is null) - { - var iconData = await FileThumbnailHelper.LoadIconFromPathAsync(currentPath, 16u, Windows.Storage.FileProperties.ThumbnailMode.ListView, Windows.Storage.FileProperties.ThumbnailOptions.UseCurrentScale, true); - if (iconData is not null) - iconSource.ImageSource = await iconData.ToBitmapAsync(); - } - - return (tabLocationHeader, iconSource, toolTipText); + public MainPageViewModel() + { + // Create commands + NavigateToNumberedTabKeyboardAcceleratorCommand = new RelayCommand(NavigateToNumberedTabKeyboardAccelerator); + OpenNewWindowAcceleratorCommand = new AsyncRelayCommand(OpenNewWindowAcceleratorAsync); } - public async Task OnNavigatedToAsync(NavigationEventArgs e) + public async Task OnNavigatedTo(NavigationEventArgs e) { - if (e.NavigationMode == NavigationMode.Back) - return; - - //Initialize the static theme helper to capture a reference to this window - //to handle theme changes without restarting the app - var isInitialized = ThemeHelper.Initialize(); + // Initialize the static theme helper to capture a reference to this window + // to handle theme changes without restarting the app + var isThemeInitialized = ThemeHelper.Initialize(); var parameter = e.Parameter; var ignoreStartupSettings = false; @@ -294,62 +89,79 @@ public async Task OnNavigatedToAsync(NavigationEventArgs e) ignoreStartupSettings = mainPageNavigationArguments.IgnoreStartupSettings; } + // The navigation parameter is empty if (parameter is null || (parameter is string eventStr && string.IsNullOrEmpty(eventStr))) { try { - // add last session tabs to closed tabs stack if those tabs are not about to be opened - if (!userSettingsService.AppSettingsService.RestoreTabsOnStartup && !userSettingsService.GeneralSettingsService.ContinueLastSessionOnStartUp && userSettingsService.GeneralSettingsService.LastSessionTabList != null) + // Add last session tabs to closed tabs stack if those tabs are not about to be opened + if (!_userSettingsService.AppSettingsService.RestoreTabsOnStartup && + !_userSettingsService.GeneralSettingsService.ContinueLastSessionOnStartUp && + _userSettingsService.GeneralSettingsService.LastSessionTabList != null) { - var items = new CustomTabViewItemParameter[userSettingsService.GeneralSettingsService.LastSessionTabList.Count]; + var items = new CustomTabViewItemParameter[_userSettingsService.GeneralSettingsService.LastSessionTabList.Count]; + + // Get parameters of the last session tabs for (int i = 0; i < items.Length; i++) - items[i] = CustomTabViewItemParameter.Deserialize(userSettingsService.GeneralSettingsService.LastSessionTabList[i]); + items[i] = CustomTabViewItemParameter.Deserialize(_userSettingsService.GeneralSettingsService.LastSessionTabList[i]); + // Restore recent tabs BaseTabBar.PushRecentTab(items); } - if (userSettingsService.AppSettingsService.RestoreTabsOnStartup) + // Restore the tabs + if (_userSettingsService.AppSettingsService.RestoreTabsOnStartup) { - userSettingsService.AppSettingsService.RestoreTabsOnStartup = false; - if (userSettingsService.GeneralSettingsService.LastSessionTabList is not null) + _userSettingsService.AppSettingsService.RestoreTabsOnStartup = false; + + if (_userSettingsService.GeneralSettingsService.LastSessionTabList is not null) { - foreach (string tabArgsString in userSettingsService.GeneralSettingsService.LastSessionTabList) + foreach (string tabArgsString in _userSettingsService.GeneralSettingsService.LastSessionTabList) { var tabArgs = CustomTabViewItemParameter.Deserialize(tabArgsString); - await AddNewTabByParamAsync(tabArgs.InitialPageType, tabArgs.NavigationParameter); + + await MultitaskingTabsHelpers.AddNewTabWithParameterAsync(tabArgs.InitialPageType, tabArgs.NavigationParameter); } - if (!userSettingsService.GeneralSettingsService.ContinueLastSessionOnStartUp) - userSettingsService.GeneralSettingsService.LastSessionTabList = null; + if (!_userSettingsService.GeneralSettingsService.ContinueLastSessionOnStartUp) + _userSettingsService.GeneralSettingsService.LastSessionTabList = null; } } - else if (userSettingsService.GeneralSettingsService.OpenSpecificPageOnStartup && - userSettingsService.GeneralSettingsService.TabsOnStartupList is not null) + // Open specific path(s) stored in the list that can be modified from the Settings page + else if (_userSettingsService.GeneralSettingsService.OpenSpecificPageOnStartup && + _userSettingsService.GeneralSettingsService.TabsOnStartupList is not null) { - foreach (string path in userSettingsService.GeneralSettingsService.TabsOnStartupList) - await AddNewTabByPathAsync(typeof(PaneHolderPage), path); + foreach (string path in _userSettingsService.GeneralSettingsService.TabsOnStartupList) + await MultitaskingTabsHelpers.AddNewTabWithPathAsync(typeof(PaneHolderPage), path); } - else if (userSettingsService.GeneralSettingsService.ContinueLastSessionOnStartUp && - userSettingsService.GeneralSettingsService.LastSessionTabList is not null) + // Continue with last session tabs + else if (_userSettingsService.GeneralSettingsService.ContinueLastSessionOnStartUp && + _userSettingsService.GeneralSettingsService.LastSessionTabList is not null) { - foreach (string tabArgsString in userSettingsService.GeneralSettingsService.LastSessionTabList) + foreach (string tabArgsString in _userSettingsService.GeneralSettingsService.LastSessionTabList) { var tabArgs = CustomTabViewItemParameter.Deserialize(tabArgsString); - await AddNewTabByParamAsync(tabArgs.InitialPageType, tabArgs.NavigationParameter); + await MultitaskingTabsHelpers.AddNewTabWithParameterAsync(tabArgs.InitialPageType, tabArgs.NavigationParameter); } - var defaultArg = new CustomTabViewItemParameter() { InitialPageType = typeof(PaneHolderPage), NavigationParameter = "Home" }; + var defaultArg = new CustomTabViewItemParameter() + { + InitialPageType = typeof(PaneHolderPage), + NavigationParameter = "Home" + }; - userSettingsService.GeneralSettingsService.LastSessionTabList = new List { defaultArg.Serialize() }; + _userSettingsService.GeneralSettingsService.LastSessionTabList = new List { defaultArg.Serialize() }; } else { - await AddNewTabAsync(); + // Just add default page - Home + await MultitaskingTabsHelpers.AddNewTabAsync(); } } catch { - await AddNewTabAsync(); + // Just add default page - Home + await MultitaskingTabsHelpers.AddNewTabAsync(); } } else @@ -358,88 +170,98 @@ public async Task OnNavigatedToAsync(NavigationEventArgs e) { try { - if (userSettingsService.GeneralSettingsService.OpenSpecificPageOnStartup && - userSettingsService.GeneralSettingsService.TabsOnStartupList is not null) + // Open specific path(s) stored in the list that can be modified from the Settings page + if (_userSettingsService.GeneralSettingsService.OpenSpecificPageOnStartup && + _userSettingsService.GeneralSettingsService.TabsOnStartupList is not null) { - foreach (string path in userSettingsService.GeneralSettingsService.TabsOnStartupList) - await AddNewTabByPathAsync(typeof(PaneHolderPage), path); + foreach (string path in _userSettingsService.GeneralSettingsService.TabsOnStartupList) + await MultitaskingTabsHelpers.AddNewTabWithPathAsync(typeof(PaneHolderPage), path); } - else if (userSettingsService.GeneralSettingsService.ContinueLastSessionOnStartUp && - userSettingsService.GeneralSettingsService.LastSessionTabList is not null) + // Continue with last session tabs + else if (_userSettingsService.GeneralSettingsService.ContinueLastSessionOnStartUp && + _userSettingsService.GeneralSettingsService.LastSessionTabList is not null) { - foreach (string tabArgsString in userSettingsService.GeneralSettingsService.LastSessionTabList) + foreach (string tabArgsString in _userSettingsService.GeneralSettingsService.LastSessionTabList) { var tabArgs = CustomTabViewItemParameter.Deserialize(tabArgsString); - await AddNewTabByParamAsync(tabArgs.InitialPageType, tabArgs.NavigationParameter); + + await MultitaskingTabsHelpers.AddNewTabWithParameterAsync(tabArgs.InitialPageType, tabArgs.NavigationParameter); } - var defaultArg = new CustomTabViewItemParameter() { InitialPageType = typeof(PaneHolderPage), NavigationParameter = "Home" }; + var defaultArg = new CustomTabViewItemParameter() + { + InitialPageType = typeof(PaneHolderPage), + NavigationParameter = "Home" + }; - userSettingsService.GeneralSettingsService.LastSessionTabList = new List { defaultArg.Serialize() }; + // Change the list to have the one item that indicates Home page + _userSettingsService.GeneralSettingsService.LastSessionTabList = new List { defaultArg.Serialize() }; } } - catch { } + catch + { + } } + // The navigation parameter is string if (parameter is string navArgs) - await AddNewTabByPathAsync(typeof(PaneHolderPage), navArgs); + await MultitaskingTabsHelpers.AddNewTabWithPathAsync(typeof(PaneHolderPage), navArgs); + // The navigation parameter is for the pane folder page else if (parameter is PaneNavigationArguments paneArgs) - await AddNewTabByParamAsync(typeof(PaneHolderPage), paneArgs); + await MultitaskingTabsHelpers.AddNewTabWithParameterAsync(typeof(PaneHolderPage), paneArgs); + // The navigation parameter is for the custom page else if (parameter is CustomTabViewItemParameter tabArgs) - await AddNewTabByParamAsync(tabArgs.InitialPageType, tabArgs.NavigationParameter); + await MultitaskingTabsHelpers.AddNewTabWithParameterAsync(tabArgs.InitialPageType, tabArgs.NavigationParameter); } - if (isInitialized) + if (isThemeInitialized) { // Load the app theme resources - resourcesService.LoadAppResources(appearanceSettingsService); + _resourcesService.LoadAppResources(_appearanceSettingsService); + // Load the drives await Task.WhenAll( - drivesViewModel.UpdateDrivesAsync(), - networkDrivesViewModel.UpdateDrivesAsync()); + _drivesViewModel.UpdateDrivesAsync(), + _networkDrivesViewModel.UpdateDrivesAsync()); } } - public Task AddNewTabAsync() + public void NotifyChanges() { - return AddNewTabByPathAsync(typeof(PaneHolderPage), "Home"); + OnPropertyChanged(nameof(ShouldViewControlBeDisplayed)); + OnPropertyChanged(nameof(ShouldPreviewPaneBeActive)); + OnPropertyChanged(nameof(ShouldPreviewPaneBeDisplayed)); } - public async Task AddNewTabByParamAsync(Type type, object tabViewItemArgs, int atIndex = -1) + private void NavigateToNumberedTabKeyboardAccelerator(KeyboardAcceleratorInvokedEventArgs? e) { - var tabItem = new Files.App.UserControls.TabBar.TabBarItem() - { - Header = null, - IconSource = null, - Description = null, - ToolTipText = null - }; - - tabItem.NavigationParameter = new CustomTabViewItemParameter() + int indexToSelect = e!.KeyboardAccelerator.Key switch { - InitialPageType = type, - NavigationParameter = tabViewItemArgs + VirtualKey.Number1 => 0, + VirtualKey.Number2 => 1, + VirtualKey.Number3 => 2, + VirtualKey.Number4 => 3, + VirtualKey.Number5 => 4, + VirtualKey.Number6 => 5, + VirtualKey.Number7 => 6, + VirtualKey.Number8 => 7, + // Select the last tab + VirtualKey.Number9 => CurrentInstanceTabBarItems.Count - 1, + _ => CurrentInstanceTabBarItems.Count - 1, }; - tabItem.ContentChanged += Control_ContentChangedAsync; - - await UpdateTabInfoAsync(tabItem, tabViewItemArgs); + // Only select the tab if it is in the list + if (indexToSelect < CurrentInstanceTabBarItems.Count) + App.AppModel.TabStripSelectedIndex = indexToSelect; - var index = atIndex == -1 ? AppInstances.Count : atIndex; - AppInstances.Insert(index, tabItem); - App.AppModel.TabStripSelectedIndex = index; + e!.Handled = true; } - public async void Control_ContentChangedAsync(object? sender, CustomTabViewItemParameter e) + private async Task OpenNewWindowAcceleratorAsync(KeyboardAcceleratorInvokedEventArgs? e) { - if (sender is null) - return; - - var matchingTabItem = AppInstances.SingleOrDefault(x => x == (Files.App.UserControls.TabBar.TabBarItem)sender); - if (matchingTabItem is null) - return; + await Launcher.LaunchUriAsync(new Uri(Constants.App.AppLaunchAlias)); - await UpdateTabInfoAsync(matchingTabItem, e.NavigationParameter); + e!.Handled = true; } } } diff --git a/src/Files.App/ViewModels/UserControls/ToolbarViewModel.cs b/src/Files.App/ViewModels/UserControls/ToolbarViewModel.cs index 53abf125280d..c7529645d708 100644 --- a/src/Files.App/ViewModels/UserControls/ToolbarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/ToolbarViewModel.cs @@ -504,7 +504,7 @@ public async Task PathBoxItem_TappedAsync(object sender, TappedRoutedEventArgs e { await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(async () => { - await mainPageViewModel.AddNewTabByPathAsync(typeof(PaneHolderPage), itemTappedPath); + await MultitaskingTabsHelpers.AddNewTabWithPathAsync(typeof(PaneHolderPage), itemTappedPath); }, DispatcherQueuePriority.Low); e.Handled = true; pointerRoutedEventArgs = null; diff --git a/src/Files.App/Views/LayoutModes/BaseLayout.cs b/src/Files.App/Views/LayoutModes/BaseLayout.cs index 70ca3051c44f..7f7f25c5e04c 100644 --- a/src/Files.App/Views/LayoutModes/BaseLayout.cs +++ b/src/Files.App/Views/LayoutModes/BaseLayout.cs @@ -1445,10 +1445,12 @@ protected void UpdatePreviewPaneSelection(List? value) PreviewPaneViewModel.IsItemSelected = value?.Count > 0; PreviewPaneViewModel.SelectedItem = value?.Count == 1 ? value.First() : null; + var mainPageViewModel = Ioc.Default.GetRequiredService(); + // Check if the preview pane is open before updating the model if (PreviewPaneViewModel.IsEnabled) { - var isPaneEnabled = ((MainWindow.Instance.Content as Frame)?.Content as MainPage)?.ShouldPreviewPaneBeActive ?? false; + var isPaneEnabled = mainPageViewModel.ShouldPreviewPaneBeActive; if (isPaneEnabled) _ = PreviewPaneViewModel.UpdateSelectedItemPreviewAsync(); } diff --git a/src/Files.App/Views/MainPage.xaml b/src/Files.App/Views/MainPage.xaml index 77b851b26a13..d809fa63b5dc 100644 --- a/src/Files.App/Views/MainPage.xaml +++ b/src/Files.App/Views/MainPage.xaml @@ -1,4 +1,5 @@ - + + - @@ -48,6 +47,7 @@ IsChecked="{x:Bind SidebarAdaptiveViewModel.ShowFileTagsSection, Mode=TwoWay}" Text="{helpers:ResourceString Name=FileTags}" /> + @@ -130,6 +130,7 @@ + @@ -179,12 +180,12 @@ HorizontalContentAlignment="Stretch" x:Load="False" Loaded="NavToolbar_Loaded" - OngoingTasksViewModel="{x:Bind OngoingTasksViewModel}" ShowOngoingTasks="True" ShowSearchBox="True" ShowSettingsButton="{x:Bind WindowContext.IsCompactOverlay, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}" TabIndex="1" /> +