diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml new file mode 100644 index 000000000000..0d6d3272285d --- /dev/null +++ b/.github/workflows/dotnet.yml @@ -0,0 +1,26 @@ +# This workflow will build a C++ project using g++ +name: C++ Build with g++ + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: windows-latest + + steps: + - uses: actions/checkout@v4 + - name: Install MinGW (for g++) + run: | + choco install mingw + echo "C:\tools\mingw64\bin" >> $env:GITHUB_PATH + - name: Build C++ Project + run: | + $cppFiles = Get-ChildItem -Path ../../../src -Filter *.cs | ForEach-Object { $_.FullName } + g++ -o output.exe -Wall -Wextra -O2 $cppFiles + - name: Run Executable + run: ./output.exe diff --git a/global.json b/global.json index 62a1920fd79d..a0699d2f1ac4 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "8.0.303" + "version": "8.0.403" } } diff --git a/src/Files.App/MainWindow.xaml.cs b/src/Files.App/MainWindow.xaml.cs index e80e55247649..34c56753f42c 100644 --- a/src/Files.App/MainWindow.xaml.cs +++ b/src/Files.App/MainWindow.xaml.cs @@ -1,343 +1,228 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - using Microsoft.Extensions.Logging; using Microsoft.UI; using Microsoft.UI.Windowing; +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Animation; +using System.Globalization; using System.Runtime.InteropServices; using Windows.ApplicationModel.Activation; +using Windows.Globalization; using Windows.Storage; -using IO = System.IO; +using System; +using System.Linq; +using System.Threading.Tasks; +using System.IO; namespace Files.App { - public sealed partial class MainWindow : WinUIEx.WindowEx - { - private static MainWindow? _Instance; - public static MainWindow Instance => _Instance ??= new(); - - public nint WindowHandle { get; } - - public MainWindow() - { - InitializeComponent(); - - WindowHandle = WinUIEx.WindowExtensions.GetWindowHandle(this); - MinHeight = 316; - MinWidth = 416; - ExtendsContentIntoTitleBar = true; - Title = "Files"; - PersistenceId = "FilesMainWindow"; - AppWindow.TitleBar.ButtonBackgroundColor = Colors.Transparent; - AppWindow.TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent; - AppWindow.TitleBar.ButtonPressedBackgroundColor = Colors.Transparent; - AppWindow.TitleBar.ButtonHoverBackgroundColor = Colors.Transparent; - AppWindow.SetIcon(AppLifecycleHelper.AppIconPath); - } - - public void ShowSplashScreen() - { - var rootFrame = EnsureWindowIsInitialized(); - - rootFrame?.Navigate(typeof(SplashScreenPage)); - } - - public async Task InitializeApplicationAsync(object activatedEventArgs) - { - var rootFrame = EnsureWindowIsInitialized(); - - if (rootFrame is null) - return; - - // Set system backdrop - SystemBackdrop = new AppSystemBackdrop(); - - switch (activatedEventArgs) - { - case ILaunchActivatedEventArgs launchArgs: - if (launchArgs.Arguments is not null && - (CommandLineParser.SplitArguments(launchArgs.Arguments, true)[0].EndsWith($"files.exe", StringComparison.OrdinalIgnoreCase) - || CommandLineParser.SplitArguments(launchArgs.Arguments, true)[0].EndsWith($"files", StringComparison.OrdinalIgnoreCase))) - { - // WINUI3: When launching from commandline the argument is not ICommandLineActivatedEventArgs (#10370) - var ppm = CommandLineParser.ParseUntrustedCommands(launchArgs.Arguments); - if (ppm.IsEmpty()) - rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo()); - else - await InitializeFromCmdLineArgsAsync(rootFrame, ppm); - } - else if (rootFrame.Content is null || rootFrame.Content is SplashScreenPage || !MainPageViewModel.AppInstances.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 - rootFrame.Navigate(typeof(MainPage), launchArgs.Arguments, new SuppressNavigationTransitionInfo()); - } - else if (!(string.IsNullOrEmpty(launchArgs.Arguments) && MainPageViewModel.AppInstances.Count > 0)) - { - // Bring to foreground (#14730) - Win32Helper.BringToForegroundEx(new(WindowHandle)); - - await NavigationHelpers.AddNewTabByPathAsync(typeof(ShellPanesPage), launchArgs.Arguments, true); - } - else - { - rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo()); - } - break; - - case IProtocolActivatedEventArgs eventArgs: - if (eventArgs.Uri.AbsoluteUri == "files-uwp:") - { - rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo()); - - if (MainPageViewModel.AppInstances.Count > 0) - { - // Bring to foreground (#14730) - Win32Helper.BringToForegroundEx(new(WindowHandle)); - } - } - else - { - var parsedArgs = eventArgs.Uri.Query.TrimStart('?').Split('='); - var unescapedValue = Uri.UnescapeDataString(parsedArgs[1]); - var folder = (StorageFolder)await FilesystemTasks.Wrap(() => StorageFolder.GetFolderFromPathAsync(unescapedValue).AsTask()); - if (folder is not null && !string.IsNullOrEmpty(folder.Path)) - { - // Convert short name to long name (#6190) - unescapedValue = folder.Path; - } - switch (parsedArgs[0]) - { - case "tab": - rootFrame.Navigate(typeof(MainPage), - new MainPageNavigationArguments() { Parameter = TabBarItemParameter.Deserialize(unescapedValue), IgnoreStartupSettings = true }, - new SuppressNavigationTransitionInfo()); - break; - - case "folder": - rootFrame.Navigate(typeof(MainPage), - new MainPageNavigationArguments() { Parameter = unescapedValue, IgnoreStartupSettings = true }, - new SuppressNavigationTransitionInfo()); - break; - - case "cmd": - var ppm = CommandLineParser.ParseUntrustedCommands(unescapedValue); - if (ppm.IsEmpty()) - rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo()); - else - await InitializeFromCmdLineArgsAsync(rootFrame, ppm); - break; - default: - rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo()); - break; - } - } - break; - - case ICommandLineActivatedEventArgs cmdLineArgs: - var operation = cmdLineArgs.Operation; - var cmdLineString = operation.Arguments; - var activationPath = operation.CurrentDirectoryPath; - - var parsedCommands = CommandLineParser.ParseUntrustedCommands(cmdLineString); - if (parsedCommands is not null && parsedCommands.Count > 0) - { - await InitializeFromCmdLineArgsAsync(rootFrame, parsedCommands, activationPath); - } - else - { - rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo()); - } - break; - - case IFileActivatedEventArgs fileArgs: - var index = 0; - if (rootFrame.Content is null || rootFrame.Content is SplashScreenPage || !MainPageViewModel.AppInstances.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 - rootFrame.Navigate(typeof(MainPage), fileArgs.Files.First().Path, new SuppressNavigationTransitionInfo()); - index = 1; - } - else - { - // Bring to foreground (#14730) - Win32Helper.BringToForegroundEx(new(WindowHandle)); - } - - for (; index < fileArgs.Files.Count; index++) - { - await NavigationHelpers.AddNewTabByPathAsync(typeof(ShellPanesPage), fileArgs.Files[index].Path, true); - } - break; - - case IStartupTaskActivatedEventArgs startupArgs: - // Just launch the app with no arguments - rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo()); - break; - - default: - // Just launch the app with no arguments - rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo()); - break; - } - - if (!AppWindow.IsVisible) - { - // When resuming the cached instance - AppWindow.Show(); - Activate(); - - // Bring to foreground (#14730) in case Activate() doesn't - Win32Helper.BringToForegroundEx(new(WindowHandle)); - } - - if (Windows.Win32.PInvoke.IsIconic(new(WindowHandle))) - WinUIEx.WindowExtensions.Restore(Instance); // Restore window if minimized - } - - private Frame? EnsureWindowIsInitialized() - { - try - { - // NOTE: - // Do not repeat app initialization when the Window already has content, - // just ensure that the window is active - if (Instance.Content is not Frame rootFrame) - { - // Create a Frame to act as the navigation context and navigate to the first page - rootFrame = new() { CacheSize = 1 }; - rootFrame.NavigationFailed += (s, e) => - { - throw new Exception("Failed to load Page " + e.SourcePageType.FullName); - }; - - // Place the frame in the current Window - Instance.Content = rootFrame; - } - - return rootFrame; - } - catch (COMException) - { - return null; - } - } - - private async Task InitializeFromCmdLineArgsAsync(Frame rootFrame, ParsedCommands parsedCommands, string activationPath = "") - { - async Task PerformNavigationAsync(string payload, string selectItem = null) - { - if (!string.IsNullOrEmpty(payload)) - { - payload = Constants.UserEnvironmentPaths.ShellPlaces.Get(payload.ToUpperInvariant(), payload); - var folder = (StorageFolder)await FilesystemTasks.Wrap(() => StorageFolder.GetFolderFromPathAsync(payload).AsTask()); - if (folder is not null && !string.IsNullOrEmpty(folder.Path)) - payload = folder.Path; // Convert short name to long name (#6190) - } - - var generalSettingsService = Ioc.Default.GetService(); - - double boundsWidth = 0; - try - { - boundsWidth = Bounds.Width; - } - catch (Exception ex) - { - // Handle exception in case WinUI Windows is closed - // (see https://github.com/files-community/Files/issues/15599) - - App.Logger.LogWarning(ex, ex.Message); - return; - } - - var paneNavigationArgs = new PaneNavigationArguments - { - LeftPaneNavPathParam = payload, - LeftPaneSelectItemParam = selectItem, - RightPaneNavPathParam = boundsWidth > Constants.UI.MultiplePaneWidthThreshold && (generalSettingsService?.AlwaysOpenDualPaneInNewTab ?? false) ? "Home" : null, - }; - - if (rootFrame.Content is MainPage && MainPageViewModel.AppInstances.Any()) - { - // Bring to foreground (#14730) - Win32Helper.BringToForegroundEx(new(WindowHandle)); - - var existingTabIndex = MainPageViewModel.AppInstances - .Select((tabItem, idx) => new { tabItem, idx }) - .FirstOrDefault(x => - x.tabItem.NavigationParameter.NavigationParameter is PaneNavigationArguments paneArgs && - (paneNavigationArgs.LeftPaneNavPathParam == paneArgs.LeftPaneNavPathParam || - paneNavigationArgs.LeftPaneNavPathParam == paneArgs.RightPaneNavPathParam))?.idx ?? -1; - - if (existingTabIndex >= 0) - App.AppModel.TabStripSelectedIndex = existingTabIndex; - else - await NavigationHelpers.AddNewTabByParamAsync(typeof(ShellPanesPage), paneNavigationArgs); - } - else - rootFrame.Navigate(typeof(MainPage), paneNavigationArgs, new SuppressNavigationTransitionInfo()); - } - foreach (var command in parsedCommands) - { - switch (command.Type) - { - case ParsedCommandType.OpenDirectory: - case ParsedCommandType.OpenPath: - case ParsedCommandType.ExplorerShellCommand: - var selectItemCommand = parsedCommands.FirstOrDefault(x => x.Type == ParsedCommandType.SelectItem); - await PerformNavigationAsync(command.Payload, selectItemCommand?.Payload); - break; - - case ParsedCommandType.SelectItem: - if (IO.Path.IsPathRooted(command.Payload)) - await PerformNavigationAsync(IO.Path.GetDirectoryName(command.Payload), IO.Path.GetFileName(command.Payload)); - break; - - case ParsedCommandType.TagFiles: - var tagService = Ioc.Default.GetService(); - var tag = tagService.GetTagsByName(command.Payload).FirstOrDefault(); - foreach (var file in command.Args.Skip(1)) - { - var fileFRN = await FilesystemTasks.Wrap(() => StorageHelpers.ToStorageItem(file)) - .OnSuccess(item => FileTagsHelper.GetFileFRN(item)); - if (fileFRN is not null) - { - var tagUid = tag is not null ? new[] { tag.Uid } : []; - var dbInstance = FileTagsHelper.GetDbInstance(); - dbInstance.SetTags(file, fileFRN, tagUid); - FileTagsHelper.WriteFileTag(file, tagUid); - } - } - break; - - case ParsedCommandType.Unknown: - if (command.Payload.Equals(".")) - { - await PerformNavigationAsync(activationPath); - } - else - { - if (!string.IsNullOrEmpty(command.Payload)) - { - var target = IO.Path.GetFullPath(IO.Path.Combine(activationPath, command.Payload)); - await PerformNavigationAsync(target); - } - else - { - await PerformNavigationAsync(null); - } - } - break; - - case ParsedCommandType.OutputPath: - App.OutputPath = command.Payload; - break; - } - } - } - } + public sealed partial class MainWindow : WinUIEx.WindowEx + { + private static readonly Lazy _Instance = new(() => new MainWindow()); + public static MainWindow Instance => _Instance.Value; + + public nint WindowHandle { get; } + + public MainWindow() + { + InitializeComponent(); + + // Force the app to use the correct culture for UI elements + SetAppCulture(); + + WindowHandle = WinUIEx.WindowExtensions.GetWindowHandle(this); + MinHeight = 316; + MinWidth = 416; + ExtendsContentIntoTitleBar = true; + Title = "Files"; + PersistenceId = "FilesMainWindow"; + AppWindow.TitleBar.ButtonBackgroundColor = Colors.Transparent; + AppWindow.TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent; + AppWindow.TitleBar.ButtonPressedBackgroundColor = Colors.Transparent; + AppWindow.TitleBar.ButtonHoverBackgroundColor = Colors.Transparent; + AppWindow.SetIcon(AppLifecycleHelper.AppIconPath); + } + + private void SetAppCulture() + { + var culture = CultureInfo.CurrentUICulture; + CultureInfo.DefaultThreadCurrentUICulture = culture; + CultureInfo.DefaultThreadCurrentCulture = culture; + ApplicationLanguages.PrimaryLanguageOverride = culture.Name; + } + + public void ShowSplashScreen() + { + var rootFrame = EnsureWindowIsInitialized(); + + if (rootFrame != null) + { + rootFrame.Navigate(typeof(SplashScreenPage)); + } + } + + public async Task InitializeApplicationAsync(object activatedEventArgs) + { + var rootFrame = EnsureWindowIsInitialized(); + + if (rootFrame == null) + return; + + // Set system backdrop + SystemBackdrop = new AppSystemBackdrop(); + + try + { + switch (activatedEventArgs) + { + case ILaunchActivatedEventArgs launchArgs: + await HandleLaunchArgsAsync(rootFrame, launchArgs); + break; + + case IProtocolActivatedEventArgs protocolArgs: + await HandleProtocolArgsAsync(rootFrame, protocolArgs); + break; + + case IFileActivatedEventArgs fileArgs: + HandleFileArgs(rootFrame, fileArgs); + break; + + default: + rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo()); + break; + } + } + catch (Exception ex) + { + // Log or handle exceptions accordingly + Console.WriteLine($"Error during initialization: {ex.Message}"); + } + } + + private async Task HandleLaunchArgsAsync(Frame rootFrame, ILaunchActivatedEventArgs launchArgs) + { + var args = CommandLineParser.SplitArguments(launchArgs.Arguments, true); + if (args.Length > 0 && + (args[0].EndsWith("files.exe", StringComparison.OrdinalIgnoreCase) || + args[0].EndsWith("files", StringComparison.OrdinalIgnoreCase))) + { + var ppm = CommandLineParser.ParseUntrustedCommands(launchArgs.Arguments); + if (ppm.IsEmpty()) + { + rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo()); + } + else + { + await InitializeFromCmdLineArgsAsync(rootFrame, ppm); + } + } + else if (rootFrame.Content is null || rootFrame.Content is SplashScreenPage || !MainPageViewModel.AppInstances.Any()) + { + rootFrame.Navigate(typeof(MainPage), launchArgs.Arguments, new SuppressNavigationTransitionInfo()); + } + else + { + Win32Helper.BringToForegroundEx(new(WindowHandle)); + await NavigationHelpers.AddNewTabByPathAsync(typeof(ShellPanesPage), launchArgs.Arguments, true); + } + } + + private async Task HandleProtocolArgsAsync(Frame rootFrame, IProtocolActivatedEventArgs eventArgs) + { + if (eventArgs.Uri.AbsoluteUri == "files-uwp:") + { + rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo()); + if (MainPageViewModel.AppInstances.Count > 0) + { + Win32Helper.BringToForegroundEx(new(WindowHandle)); + } + } + else + { + var parsedArgs = eventArgs.Uri.Query.TrimStart('?').Split('='); + if (parsedArgs.Length == 2) + { + var unescapedValue = Uri.UnescapeDataString(parsedArgs[1]); + var folder = (StorageFolder)await FilesystemTasks.Wrap(() => StorageFolder.GetFolderFromPathAsync(unescapedValue).AsTask()); + if (folder != null && !string.IsNullOrEmpty(folder.Path)) + { + unescapedValue = folder.Path; + } + + switch (parsedArgs[0]) + { + case "tab": + rootFrame.Navigate(typeof(MainPage), new MainPageNavigationArguments() { Parameter = TabBarItemParameter.Deserialize(unescapedValue), IgnoreStartupSettings = true }, new SuppressNavigationTransitionInfo()); + break; + + case "folder": + rootFrame.Navigate(typeof(MainPage), new MainPageNavigationArguments() { Parameter = unescapedValue, IgnoreStartupSettings = true }, new SuppressNavigationTransitionInfo()); + break; + + case "cmd": + var ppm = CommandLineParser.ParseUntrustedCommands(unescapedValue); + if (ppm.IsEmpty()) + rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo()); + else + await InitializeFromCmdLineArgsAsync(rootFrame, ppm); + break; + + default: + rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo()); + break; + } + } + else + { + rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo()); + } + } + } + + private void HandleFileArgs(Frame rootFrame, IFileActivatedEventArgs fileArgs) + { + if (fileArgs.Files.Count > 0) + { + rootFrame.Navigate(typeof(MainPage), fileArgs.Files, new SuppressNavigationTransitionInfo()); + } + } + + private Frame? EnsureWindowIsInitialized() + { + try + { + if (Instance.Content is not Frame rootFrame) + { + rootFrame = new() { CacheSize = 1 }; + rootFrame.NavigationFailed += (s, e) => + { + throw new Exception("Failed to load Page " + e.SourcePageType.FullName); + }; + + Instance.Content = rootFrame; + } + + return rootFrame; + } + catch (COMException) + { + return null; + } + } + + private async Task InitializeFromCmdLineArgsAsync(Frame rootFrame, CommandLineParameterModel ppm) + { + if (ppm.Commands?.FirstOrDefault() is string cmd) + { + if (cmd.Equals("open", StringComparison.OrdinalIgnoreCase) && ppm.Parameters?.FirstOrDefault() is string path) + { + await NavigationHelpers.AddNewTabByPathAsync(typeof(ShellPanesPage), path, true); + } + else if (cmd.Equals("new", StringComparison.OrdinalIgnoreCase)) + { + rootFrame.Navigate(typeof(MainPage), new MainPageNavigationArguments() { Parameter = null }, new SuppressNavigationTransitionInfo()); + } + } + } + } } diff --git a/src/Files.App/Views/MainPage.xaml.cs b/src/Files.App/Views/MainPage.xaml.cs index 55b9a425aaa8..389ac33460b4 100644 --- a/src/Files.App/Views/MainPage.xaml.cs +++ b/src/Files.App/Views/MainPage.xaml.cs @@ -21,470 +21,242 @@ namespace Files.App.Views { - public sealed partial class MainPage : Page - { - private IGeneralSettingsService generalSettingsService { get; } = Ioc.Default.GetRequiredService(); - public IUserSettingsService UserSettingsService { get; } - private readonly IWindowContext WindowContext = Ioc.Default.GetRequiredService(); - public ICommandManager Commands { get; } - public SidebarViewModel SidebarAdaptiveViewModel { get; } - public MainPageViewModel ViewModel { get; } - public StatusCenterViewModel OngoingTasksViewModel { get; } - - private bool keyReleased = true; - - private DispatcherQueueTimer _updateDateDisplayTimer; - - public MainPage() - { - InitializeComponent(); - - // Dependency Injection - UserSettingsService = Ioc.Default.GetRequiredService(); - Commands = Ioc.Default.GetRequiredService(); - SidebarAdaptiveViewModel = Ioc.Default.GetRequiredService(); - SidebarAdaptiveViewModel.PaneFlyout = (MenuFlyout)Resources["SidebarContextMenu"]; - ViewModel = Ioc.Default.GetRequiredService(); - OngoingTasksViewModel = Ioc.Default.GetRequiredService(); - - if (FilePropertiesHelpers.FlowDirectionSettingIsRightToLeft) - FlowDirection = FlowDirection.RightToLeft; - - ViewModel.PropertyChanged += ViewModel_PropertyChanged; - UserSettingsService.OnSettingChangedEvent += UserSettingsService_OnSettingChangedEvent; - - _updateDateDisplayTimer = DispatcherQueue.CreateTimer(); - _updateDateDisplayTimer.Interval = TimeSpan.FromSeconds(1); - _updateDateDisplayTimer.Tick += UpdateDateDisplayTimer_Tick; - } - - private async Task PromptForReviewAsync() - { - var promptForReviewDialog = new ContentDialog - { - Title = "ReviewFiles".ToLocalized(), - Content = "ReviewFilesContent".ToLocalized(), - PrimaryButtonText = "Yes".ToLocalized(), - SecondaryButtonText = "No".ToLocalized() - }; - - if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - promptForReviewDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; - - var result = await promptForReviewDialog.TryShowAsync(); - - if (result == ContentDialogResult.Primary) - { - try - { - var storeContext = StoreContext.GetDefault(); - InitializeWithWindow.Initialize(storeContext, MainWindow.Instance.WindowHandle); - var storeRateAndReviewResult = await storeContext.RequestRateAndReviewAppAsync(); - - App.Logger.LogInformation($"STORE: review request status: {storeRateAndReviewResult.Status}"); - - UserSettingsService.ApplicationSettingsService.ClickedToReviewApp = true; - } - catch (Exception) { } - } - } - - private async Task AppRunningAsAdminPromptAsync() - { - var runningAsAdminPrompt = new ContentDialog - { - Title = "FilesRunningAsAdmin".ToLocalized(), - Content = "FilesRunningAsAdminContent".ToLocalized(), - PrimaryButtonText = "Ok".ToLocalized(), - SecondaryButtonText = "DontShowAgain".ToLocalized() - }; - - var result = await SetContentDialogRoot(runningAsAdminPrompt).TryShowAsync(); - - if (result == ContentDialogResult.Secondary) - UserSettingsService.ApplicationSettingsService.ShowRunningAsAdminPrompt = false; - } - - // WINUI3 - private ContentDialog SetContentDialogRoot(ContentDialog contentDialog) - { - if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - contentDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; - - return contentDialog; - } - - private void UserSettingsService_OnSettingChangedEvent(object? sender, SettingChangedEventArgs e) - { - switch (e.SettingName) - { - case nameof(IInfoPaneSettingsService.IsInfoPaneEnabled): - LoadPaneChanged(); - break; - } - } - - private void HorizontalMultitaskingControl_Loaded(object sender, RoutedEventArgs e) - { - TabControl.DragArea.SizeChanged += (_, _) => MainWindow.Instance.RaiseSetTitleBarDragRegion(SetTitleBarDragRegion); - if (ViewModel.MultitaskingControl is not TabBar) - { - ViewModel.MultitaskingControl = TabControl; - ViewModel.MultitaskingControls.Add(TabControl); - ViewModel.MultitaskingControl.CurrentInstanceChanged += MultitaskingControl_CurrentInstanceChanged; - } - } - - private int SetTitleBarDragRegion(InputNonClientPointerSource source, SizeInt32 size, double scaleFactor, Func getScaledRect) - { - var height = (int)TabControl.ActualHeight; - source.SetRegionRects(NonClientRegionKind.Passthrough, [getScaledRect(this, new RectInt32(0, 0, (int)(TabControl.ActualWidth + TabControl.Margin.Left - TabControl.DragArea.ActualWidth), height))]); - return height; - } - - public async void TabItemContent_ContentChanged(object? sender, TabBarItemParameter e) - { - if (SidebarAdaptiveViewModel.PaneHolder is null) - return; - - var paneArgs = e.NavigationParameter as PaneNavigationArguments; - SidebarAdaptiveViewModel.UpdateSidebarSelectedItemFromArgs(SidebarAdaptiveViewModel.PaneHolder.IsLeftPaneActive ? - paneArgs?.LeftPaneNavPathParam : paneArgs?.RightPaneNavPathParam); - - UpdateStatusBarProperties(); - LoadPaneChanged(); - UpdateNavToolbarProperties(); - await NavigationHelpers.UpdateInstancePropertiesAsync(paneArgs); - - // Save the updated tab list - AppLifecycleHelper.SaveSessionTabs(); - } - - public async void MultitaskingControl_CurrentInstanceChanged(object? sender, CurrentInstanceChangedEventArgs e) - { - if (SidebarAdaptiveViewModel.PaneHolder is not null) - SidebarAdaptiveViewModel.PaneHolder.PropertyChanged -= PaneHolder_PropertyChanged; - - var navArgs = e.CurrentInstance.TabBarItemParameter?.NavigationParameter; - if (e.CurrentInstance is IShellPanesPage currentInstance) - { - SidebarAdaptiveViewModel.PaneHolder = currentInstance; - SidebarAdaptiveViewModel.PaneHolder.PropertyChanged += PaneHolder_PropertyChanged; - } - SidebarAdaptiveViewModel.NotifyInstanceRelatedPropertiesChanged((navArgs as PaneNavigationArguments)?.LeftPaneNavPathParam); - - if (SidebarAdaptiveViewModel.PaneHolder?.ActivePaneOrColumn.SlimContentPage?.StatusBarViewModel is not null) - SidebarAdaptiveViewModel.PaneHolder.ActivePaneOrColumn.SlimContentPage.StatusBarViewModel.ShowLocals = true; - - UpdateStatusBarProperties(); - UpdateNavToolbarProperties(); - LoadPaneChanged(); - - e.CurrentInstance.ContentChanged -= TabItemContent_ContentChanged; - e.CurrentInstance.ContentChanged += TabItemContent_ContentChanged; - - await NavigationHelpers.UpdateInstancePropertiesAsync(navArgs); - } - - private void PaneHolder_PropertyChanged(object? sender, PropertyChangedEventArgs e) - { - SidebarAdaptiveViewModel.NotifyInstanceRelatedPropertiesChanged(SidebarAdaptiveViewModel.PaneHolder.ActivePane?.TabBarItemParameter?.NavigationParameter?.ToString()); - UpdateStatusBarProperties(); - UpdateNavToolbarProperties(); - LoadPaneChanged(); - } - - private void UpdateStatusBarProperties() - { - if (StatusBar is not null) - { - StatusBar.StatusBarViewModel = SidebarAdaptiveViewModel.PaneHolder?.ActivePaneOrColumn.SlimContentPage?.StatusBarViewModel; - StatusBar.SelectedItemsPropertiesViewModel = SidebarAdaptiveViewModel.PaneHolder?.ActivePaneOrColumn.SlimContentPage?.SelectedItemsPropertiesViewModel; - } - } - - private void UpdateNavToolbarProperties() - { - if (NavToolbar is not null) - NavToolbar.ViewModel = SidebarAdaptiveViewModel.PaneHolder?.ActivePaneOrColumn.ToolbarViewModel; - - if (InnerNavigationToolbar is not null) - InnerNavigationToolbar.ViewModel = SidebarAdaptiveViewModel.PaneHolder?.ActivePaneOrColumn.ToolbarViewModel; - } - - protected override void OnNavigatedTo(NavigationEventArgs e) - { - ViewModel.OnNavigatedToAsync(e); - } - - protected override async void OnPreviewKeyDown(KeyRoutedEventArgs e) => await OnPreviewKeyDownAsync(e); - - private async Task OnPreviewKeyDownAsync(KeyRoutedEventArgs e) - { - base.OnPreviewKeyDown(e); - - switch (e.Key) - { - case VirtualKey.Menu: - case VirtualKey.Control: - case VirtualKey.Shift: - case VirtualKey.LeftWindows: - case VirtualKey.RightWindows: - break; - default: - var currentModifiers = HotKeyHelpers.GetCurrentKeyModifiers(); - HotKey hotKey = new((Keys)e.Key, currentModifiers); - - // A textbox takes precedence over certain hotkeys. - if (e.OriginalSource is DependencyObject source && source.FindAscendantOrSelf() is not null) - break; - - // Execute command for hotkey - var command = Commands[hotKey]; - if (command.Code is not CommandCodes.None && keyReleased) - { - keyReleased = false; - e.Handled = command.IsExecutable; - await command.ExecuteAsync(); - } - break; - } - } - - protected override void OnPreviewKeyUp(KeyRoutedEventArgs e) - { - base.OnPreviewKeyUp(e); - - switch (e.Key) - { - case VirtualKey.Menu: - case VirtualKey.Control: - case VirtualKey.Shift: - case VirtualKey.LeftWindows: - case VirtualKey.RightWindows: - break; - default: - keyReleased = true; - break; - } - } - - // A workaround for issue with OnPreviewKeyUp not being called when the hotkey displays a dialog - protected override void OnLostFocus(RoutedEventArgs e) - { - base.OnLostFocus(e); - - keyReleased = true; - } - - private void Page_Loaded(object sender, RoutedEventArgs e) - { - MainWindow.Instance.AppWindow.Changed += (_, _) => MainWindow.Instance.RaiseSetTitleBarDragRegion(SetTitleBarDragRegion); - - // Defers the status bar loading until after the page has loaded to improve startup perf - FindName(nameof(StatusBar)); - FindName(nameof(InnerNavigationToolbar)); - FindName(nameof(TabControl)); - FindName(nameof(NavToolbar)); - - // Notify user that drag and drop is disabled - // Prompt is disabled in the dev environment to prevent issues with the automation testing - // ToDo put this in a StartupPromptService - if - ( - AppLifecycleHelper.AppEnvironment is not AppEnvironment.Dev && - WindowContext.IsRunningAsAdmin && - UserSettingsService.ApplicationSettingsService.ShowRunningAsAdminPrompt - ) - { - DispatcherQueue.TryEnqueue(async () => await AppRunningAsAdminPromptAsync()); - } - - // ToDo put this in a StartupPromptService - if (Package.Current.Id.Name != "49306atecsolution.FilesUWP" || UserSettingsService.ApplicationSettingsService.ClickedToReviewApp) - return; - - var totalLaunchCount = SystemInformation.Instance.TotalLaunchCount; - if (totalLaunchCount is 15 or 30 or 60) - { - // Prompt user to review app in the Store - DispatcherQueue.TryEnqueue(async () => await PromptForReviewAsync()); - } - } - - private void PreviewPane_Loaded(object sender, RoutedEventArgs e) - { - _updateDateDisplayTimer.Start(); - } - - private void PreviewPane_Unloaded(object sender, RoutedEventArgs e) - { - _updateDateDisplayTimer.Stop(); - } - - private void UpdateDateDisplayTimer_Tick(object sender, object e) - { - if (!App.AppModel.IsMainWindowClosed) - InfoPane?.ViewModel.UpdateDateDisplay(); - } - - private void Page_SizeChanged(object sender, SizeChangedEventArgs e) - { - switch (InfoPane?.Position) - { - case PreviewPanePositions.Right when ContentColumn.ActualWidth == ContentColumn.MinWidth: - UserSettingsService.InfoPaneSettingsService.VerticalSizePx += e.NewSize.Width - e.PreviousSize.Width; - UpdatePositioning(); - break; - case PreviewPanePositions.Bottom when ContentRow.ActualHeight == ContentRow.MinHeight: - UserSettingsService.InfoPaneSettingsService.HorizontalSizePx += e.NewSize.Height - e.PreviousSize.Height; - UpdatePositioning(); - break; - } - } - - private void SidebarControl_Loaded(object sender, RoutedEventArgs e) - { - // Set the correct tab margin on startup - SidebarAdaptiveViewModel.UpdateTabControlMargin(); - } - - private void RootGrid_SizeChanged(object sender, SizeChangedEventArgs e) => LoadPaneChanged(); - - /// - /// Call this function to update the positioning of the preview pane. - /// This is a workaround as the VisualStateManager causes problems. - /// - private void UpdatePositioning() - { - if (InfoPane is null || !ViewModel.ShouldPreviewPaneBeActive) - { - PaneRow.MinHeight = 0; - PaneRow.MaxHeight = double.MaxValue; - PaneRow.Height = new GridLength(0); - PaneColumn.MinWidth = 0; - PaneColumn.MaxWidth = double.MaxValue; - PaneColumn.Width = new GridLength(0); - } - else - { - InfoPane.UpdatePosition(RootGrid.ActualWidth, RootGrid.ActualHeight); - switch (InfoPane.Position) - { - case PreviewPanePositions.None: - PaneRow.MinHeight = 0; - PaneRow.Height = new GridLength(0); - PaneColumn.MinWidth = 0; - PaneColumn.Width = new GridLength(0); - break; - case PreviewPanePositions.Right: - InfoPane.SetValue(Grid.RowProperty, 1); - InfoPane.SetValue(Grid.ColumnProperty, 2); - PaneSplitter.SetValue(Grid.RowProperty, 1); - PaneSplitter.SetValue(Grid.ColumnProperty, 1); - PaneSplitter.Width = 2; - PaneSplitter.Height = RootGrid.ActualHeight; - PaneSplitter.GripperCursor = GridSplitter.GripperCursorType.SizeWestEast; - PaneSplitter.ChangeCursor(InputSystemCursor.Create(InputSystemCursorShape.SizeWestEast)); - PaneColumn.MinWidth = InfoPane.MinWidth; - PaneColumn.MaxWidth = InfoPane.MaxWidth; - PaneColumn.Width = new GridLength(UserSettingsService.InfoPaneSettingsService.VerticalSizePx, GridUnitType.Pixel); - PaneRow.MinHeight = 0; - PaneRow.MaxHeight = double.MaxValue; - PaneRow.Height = new GridLength(0); - break; - case PreviewPanePositions.Bottom: - InfoPane.SetValue(Grid.RowProperty, 3); - InfoPane.SetValue(Grid.ColumnProperty, 0); - PaneSplitter.SetValue(Grid.RowProperty, 2); - PaneSplitter.SetValue(Grid.ColumnProperty, 0); - PaneSplitter.Height = 2; - PaneSplitter.Width = RootGrid.ActualWidth; - PaneSplitter.GripperCursor = GridSplitter.GripperCursorType.SizeNorthSouth; - PaneSplitter.ChangeCursor(InputSystemCursor.Create(InputSystemCursorShape.SizeNorthSouth)); - PaneColumn.MinWidth = 0; - PaneColumn.MaxWidth = double.MaxValue; - PaneColumn.Width = new GridLength(0); - PaneRow.MinHeight = InfoPane.MinHeight; - PaneRow.MaxHeight = InfoPane.MaxHeight; - PaneRow.Height = new GridLength(UserSettingsService.InfoPaneSettingsService.HorizontalSizePx, GridUnitType.Pixel); - break; - } - } - } - - private void PaneSplitter_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e) - { - switch (InfoPane?.Position) - { - case PreviewPanePositions.Right: - UserSettingsService.InfoPaneSettingsService.VerticalSizePx = InfoPane.ActualWidth; - break; - case PreviewPanePositions.Bottom: - UserSettingsService.InfoPaneSettingsService.HorizontalSizePx = InfoPane.ActualHeight; - break; - } - - this.ChangeCursor(InputSystemCursor.Create(InputSystemCursorShape.Arrow)); - } - - private void LoadPaneChanged() - { - try - { - var isHomePage = !(SidebarAdaptiveViewModel.PaneHolder?.ActivePane?.InstanceViewModel?.IsPageTypeNotHome ?? false); - var isMultiPane = SidebarAdaptiveViewModel.PaneHolder?.IsMultiPaneActive ?? false; - var isBigEnough = !App.AppModel.IsMainWindowClosed && - (MainWindow.Instance.Bounds.Width > 450 && MainWindow.Instance.Bounds.Height > 450 || RootGrid.ActualWidth > 700 && MainWindow.Instance.Bounds.Height > 360); - - ViewModel.ShouldPreviewPaneBeDisplayed = (!isHomePage || isMultiPane) && isBigEnough; - ViewModel.ShouldPreviewPaneBeActive = UserSettingsService.InfoPaneSettingsService.IsInfoPaneEnabled && ViewModel.ShouldPreviewPaneBeDisplayed; - ViewModel.ShouldViewControlBeDisplayed = SidebarAdaptiveViewModel.PaneHolder?.ActivePane?.InstanceViewModel?.IsPageTypeNotHome ?? false; - - UpdatePositioning(); - } - catch (Exception ex) - { - // Handle exception in case WinUI Windows is closed - // (see https://github.com/files-community/Files/issues/15599) - - App.Logger.LogWarning(ex, ex.Message); - } - } - - private async void ViewModel_PropertyChanged(object? sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == nameof(ViewModel.ShouldPreviewPaneBeActive) && ViewModel.ShouldPreviewPaneBeActive) - await Ioc.Default.GetRequiredService().UpdateSelectedItemPreviewAsync(); - } - - private void RootGrid_PreviewKeyDown(object sender, KeyRoutedEventArgs e) - { - switch (e.Key) - { - case VirtualKey.Menu: - case VirtualKey.Control: - case VirtualKey.Shift: - case VirtualKey.LeftWindows: - case VirtualKey.RightWindows: - break; - default: - var currentModifiers = HotKeyHelpers.GetCurrentKeyModifiers(); - HotKey hotKey = new((Keys)e.Key, currentModifiers); - - // Prevents the arrow key events from navigating the list instead of switching compact overlay - if (Commands[hotKey].Code is CommandCodes.EnterCompactOverlay or CommandCodes.ExitCompactOverlay) - Focus(FocusState.Keyboard); - break; - } - } - - private void NavToolbar_Loaded(object sender, RoutedEventArgs e) => UpdateNavToolbarProperties(); - - private void PaneSplitter_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e) - { - this.ChangeCursor(InputSystemCursor.Create(PaneSplitter.GripperCursor == GridSplitter.GripperCursorType.SizeWestEast ? - InputSystemCursorShape.SizeWestEast : InputSystemCursorShape.SizeNorthSouth)); - } - } + public sealed partial class MainPage : Page + { + private IGeneralSettingsService generalSettingsService = Ioc.Default.GetRequiredService(); + public IUserSettingsService UserSettingsService { get; } + private readonly IWindowContext WindowContext = Ioc.Default.GetRequiredService(); + public ICommandManager Commands { get; } + public SidebarViewModel SidebarAdaptiveViewModel { get; } + public MainPageViewModel ViewModel { get; } + public StatusCenterViewModel OngoingTasksViewModel { get; } + + private bool keyReleased = true; + private DispatcherQueueTimer _updateDateDisplayTimer; + + public MainPage() + { + InitializeComponent(); + + UserSettingsService = Ioc.Default.GetRequiredService(); + Commands = Ioc.Default.GetRequiredService(); + SidebarAdaptiveViewModel = Ioc.Default.GetRequiredService(); + SidebarAdaptiveViewModel.PaneFlyout = (MenuFlyout)Resources["SidebarContextMenu"]; + ViewModel = Ioc.Default.GetRequiredService(); + OngoingTasksViewModel = Ioc.Default.GetRequiredService(); + + if (FilePropertiesHelpers.FlowDirectionSettingIsRightToLeft) + FlowDirection = FlowDirection.RightToLeft; + + ViewModel.PropertyChanged += ViewModel_PropertyChanged; + UserSettingsService.OnSettingChangedEvent += UserSettingsService_OnSettingChangedEvent; + + _updateDateDisplayTimer = DispatcherQueue.CreateTimer(); + _updateDateDisplayTimer.Interval = TimeSpan.FromSeconds(1); + _updateDateDisplayTimer.Tick += UpdateDateDisplayTimer_Tick; + } + + private void LoadPaneChanged() + { + try + { + if (SidebarAdaptiveViewModel.PaneHolder != null) + { + bool isHomePage = !(SidebarAdaptiveViewModel.PaneHolder.ActivePane.InstanceViewModel is not IShellPanesPage); + bool isMultiPane = SidebarAdaptiveViewModel.PaneHolder.IsMultiPaneActive; + bool isBigEnough = !App.AppModel.IsMainWindowClosed && + (MainWindow.Instance.Bounds.Width > 450 && MainWindow.Instance.Bounds.Height > 450 || + RootGrid.ActualWidth > 700 && MainWindow.Instance.Bounds.Height > 360); + + ViewModel.ShouldPreviewPaneBeDisplayed = (!isHomePage || isMultiPane) && isBigEnough; + ViewModel.ShouldPreviewPaneBeActive = UserSettingsService.InfoPaneSettingsService.IsInfoPaneEnabled && ViewModel.ShouldPreviewPaneBeDisplayed; + + UpdateStatusBarProperties(); + UpdateNavToolbarProperties(); + UpdatePositioning(); + } + } + catch (Exception ex) + { + App.Logger.LogWarning(ex, "Error while loading pane changes: {Message}", ex.Message); + } + } + + private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + switch (e.PropertyName) + { + case nameof(ViewModel.ShouldPreviewPaneBeActive): + if (ViewModel.ShouldPreviewPaneBeActive) + { + var infoPaneViewModel = Ioc.Default.GetRequiredService(); + infoPaneViewModel.UpdateSelectedItemPreviewAsync(); + } + break; + + case nameof(ViewModel.ShouldViewControlBeDisplayed): + ViewControl.Visibility = ViewModel.ShouldViewControlBeDisplayed ? Visibility.Visible : Visibility.Collapsed; + break; + + case nameof(ViewModel.MultitaskingControl): + UpdateMultitaskingControl(ViewModel.MultitaskingControl); + break; + + case nameof(ViewModel.SelectedTabIndex): + UpdateTabSelection(ViewModel.SelectedTabIndex); + break; + } + } + + private void UpdateMultitaskingControl(object multitaskingControl) + { + if (multitaskingControl is TabBar tabBar) + { + TabControl.ItemsSource = tabBar.Items; + } + } + + private void UpdateTabSelection(int selectedIndex) + { + TabControl.SelectedIndex = selectedIndex; + } + + private async Task PromptForReviewAsync() + { + var promptForReviewDialog = new ContentDialog + { + Title = "ReviewFiles", + Content = "ReviewFilesContent", + PrimaryButtonText = "Yes", + SecondaryButtonText = "No" + }; + + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + promptForReviewDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; + + var result = await promptForReviewDialog.TryShowAsync(); + + if (result == ContentDialogResult.Primary) + { + try + { + var storeContext = StoreContext.GetDefault(); + InitializeWithWindow.Initialize(storeContext, MainWindow.Instance.WindowHandle); + var storeRateAndReviewResult = await storeContext.RequestRateAndReviewAppAsync(); + + App.Logger.LogInformation($"STORE: review request status: {storeRateAndReviewResult.Status}"); + + UserSettingsService.ApplicationSettingsService.ClickedToReviewApp = true; + } + catch (Exception) { } + } + } + + private async Task AppRunningAsAdminPromptAsync() + { + var runningAsAdminPrompt = new ContentDialog + { + Title = "FilesRunningAsAdmin", + Content = "FilesRunningAsAdminContent", + PrimaryButtonText = "Ok", + SecondaryButtonText = "DontShowAgain" + }; + + var result = await SetContentDialogRoot(runningAsAdminPrompt).TryShowAsync(); + + if (result == ContentDialogResult.Secondary) + UserSettingsService.ApplicationSettingsService.ShowRunningAsAdminPrompt = false; + } + + private ContentDialog SetContentDialogRoot(ContentDialog contentDialog) + { + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + contentDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; + + return contentDialog; + } + + private void UserSettingsService_OnSettingChangedEvent(object? sender, SettingChangedEventArgs e) + { + if (e.SettingName == nameof(IInfoPaneSettingsService.IsInfoPaneEnabled)) + { + LoadPaneChanged(); + } + } + + private void HorizontalMultitaskingControl_Loaded(object sender, RoutedEventArgs e) + { + TabControl.DragArea.SizeChanged += (_, _) => MainWindow.Instance.RaiseSetTitleBarDragRegion(SetTitleBarDragRegion); + if (ViewModel.MultitaskingControl is not TabBar) + { + ViewModel.MultitaskingControl = TabControl; + ViewModel.MultitaskingControls.Add(TabControl); + ViewModel.MultitaskingControl.CurrentInstanceChanged += MultitaskingControl_CurrentInstanceChanged; + } + } + + private int SetTitleBarDragRegion(InputNonClientPointerSource source, SizeInt32 size, double scaleFactor, Func getScaledRect) + { + var height = (int)TabControl.ActualHeight; + source.SetRegionRects(NonClientRegionKind.Passthrough, [getScaledRect(this, new RectInt32(0, 0, (int)(TabControl.ActualWidth + TabControl.Margin.Left - TabControl.DragArea.ActualWidth), height))]); + return height; + } + + public async void TabItemContent_ContentChanged(object? sender, TabBarItemParameter e) + { + if (SidebarAdaptiveViewModel.PaneHolder is null) + return; + + var paneArgs = e.NavigationParameter as PaneNavigationArguments; + SidebarAdaptiveViewModel.UpdateSidebarSelectedItemFromArgs(SidebarAdaptiveViewModel.PaneHolder.IsLeftPaneActive ? + paneArgs?.LeftPaneNavPathParam : paneArgs?.RightPaneNavPathParam); + + UpdateStatusBarProperties(); + LoadPaneChanged(); + UpdateNavToolbarProperties(); + await NavigationHelpers.UpdateInstancePropertiesAsync(paneArgs); + + AppLifecycleHelper.SaveSessionTabs(); + LoadPaneChanged(); + } + + public async void MultitaskingControl_CurrentInstanceChanged(object? sender, CurrentInstanceChangedEventArgs e) + { + if (SidebarAdaptiveViewModel.PaneHolder is not null) + SidebarAdaptiveViewModel.PaneHolder.PropertyChanged -= PaneHolder_PropertyChanged; + + var navArgs = e.CurrentInstance.TabBarItemParameter?.NavigationParameter; + if (e.CurrentInstance is IShellPanesPage currentInstance) + { + SidebarAdaptiveViewModel.PaneHolder = currentInstance; + SidebarAdaptiveViewModel.PaneHolder.PropertyChanged += PaneHolder_PropertyChanged; + } + + SidebarAdaptiveViewModel.NotifyInstanceRelatedPropertiesChanged((navArgs as PaneNavigationArguments)?.LeftPaneNavPathParam); + + if (SidebarAdaptiveViewModel.PaneHolder?.ActivePaneOrColumn.SlimContentPage?.StatusBarViewModel is not null) + SidebarAdaptiveViewModel.PaneHolder.ActivePaneOrColumn.SlimContentPage.StatusBarViewModel.ShowLocals = true; + + UpdateStatusBarProperties(); + UpdateNavToolbarProperties(); + LoadPaneChanged(); + + e.CurrentInstance.ContentChanged -= TabItemContent_ContentChanged; + e.CurrentInstance.ContentChanged += TabItemContent_ContentChanged; + + await NavigationHelpers.UpdateInstancePropertiesAsync(navArgs); + } + + private void PaneHolder_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + SidebarAdaptiveViewModel.NotifyInstanceRelatedPropertiesChanged(SidebarAdaptiveViewModel.PaneHolder.ActivePane?.TabBarItemParameter?.NavigationParameter); + UpdateStatusBarProperties(); + UpdateNavToolbarProperties(); + } + + private void UpdateDateDisplayTimer_Tick(object? sender, object e) + { + ViewModel.UpdateDateDisplay(); + } + } }