Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Added an option to keep Files running in the background for quicker startup time #13236

Merged
merged 15 commits into from
Aug 24, 2023
31 changes: 30 additions & 1 deletion src/Files.App/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
using Microsoft.Extensions.Logging;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media.Animation;
using Microsoft.Windows.AppLifecycle;
using System.IO;
using System.Text;
Expand All @@ -40,7 +41,7 @@ public partial class App : Application
private static bool ShowErrorNotification = false;
public static string OutputPath { get; set; }
public static CommandBarFlyout? LastOpenedFlyout { get; set; }
public static TaskCompletionSource? SplashScreenLoadingTCS { get; set; }
public static TaskCompletionSource? SplashScreenLoadingTCS { get; private set; }

public static StorageHistoryWrapper HistoryWrapper { get; } = new();
public static AppModel AppModel { get; private set; }
Expand Down Expand Up @@ -300,6 +301,34 @@ private async void Window_Closed(object sender, WindowEventArgs args)
return;
}

if (Ioc.Default.GetRequiredService<IUserSettingsService>().GeneralSettingsService.LeaveAppRunning)
{
// Cache the window istead of closing it
hishitetsu marked this conversation as resolved.
Show resolved Hide resolved
MainWindow.Instance.AppWindow.Hide();
args.Handled = true;

// Save and close all tabs
SaveSessionTabs();
MainPageViewModel.AppInstances.ForEach(tabItem => tabItem.Unload());
MainPageViewModel.AppInstances.Clear();
await Task.Delay(100);

// Wait for all properties windows to close
hishitetsu marked this conversation as resolved.
Show resolved Hide resolved
await FilePropertiesHelpers.WaitClosingAll();

Program.Pool = new(0, 1, "Files-Instance");
Thread.Yield();
if (Program.Pool.WaitOne())
{
Program.Pool.Dispose();
MainWindow.Instance.AppWindow.Show();
MainWindow.Instance.Activate();
MainWindow.Instance.EnsureWindowIsInitialized().Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo());
}

return;
}

// Method can take a long time, make sure the window is hidden
await Task.Yield();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
// Copyright (c) 2023 Files Community
// Licensed under the MIT License. See the LICENSE.

using CommunityToolkit.Mvvm.ComponentModel;
using Files.App.UserControls.MultitaskingControl;
using Files.App.ViewModels;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Input;
using System.Collections.Specialized;
using System.ComponentModel;

namespace Files.App.Data.Contexts
{
Expand Down Expand Up @@ -46,7 +43,8 @@ private void AppInstances_CollectionChanged(object? sender, NotifyCollectionChan
}
private void AppModel_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
UpdateCurrentTabIndex();
if (e.PropertyName is nameof(AppModel.TabStripSelectedIndex))
UpdateCurrentTabIndex();
}
private void BaseMultitaskingControl_OnLoaded(object? sender, IMultitaskingControl control)
{
Expand Down
18 changes: 6 additions & 12 deletions src/Files.App/Data/Models/AppModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,13 @@ public int TabStripSelectedIndex
get => tabStripSelectedIndex;
set
{
if (value >= 0)
{
if (tabStripSelectedIndex != value)
{
SetProperty(ref tabStripSelectedIndex, value);
}
SetProperty(ref tabStripSelectedIndex, value);

if (value < MainPageViewModel.AppInstances.Count)
{
Frame rootFrame = (Frame)MainWindow.Instance.Content;
var mainView = (MainPage)rootFrame.Content;
mainView.ViewModel.SelectedTabItem = MainPageViewModel.AppInstances[value];
}
if (value >= 0 && value < MainPageViewModel.AppInstances.Count)
{
Frame rootFrame = (Frame)MainWindow.Instance.Content;
var mainView = (MainPage)rootFrame.Content;
mainView.ViewModel.SelectedTabItem = MainPageViewModel.AppInstances[value];
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/Files.App/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
using Files.App.UserControls.MultitaskingControl;
using Microsoft.UI;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media.Animation;
using Microsoft.UI.Xaml.Navigation;
using System.IO;
using System.Runtime.InteropServices;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Storage;
Expand Down Expand Up @@ -176,7 +176,7 @@ public async Task InitializeApplication(object activatedEventArgs)
rootFrame.Navigate(typeof(MainPage), null, new SuppressNavigationTransitionInfo());
}

private Frame EnsureWindowIsInitialized()
public Frame EnsureWindowIsInitialized()
{
// NOTE:
// Do not repeat app initialization when the Window already has content,
Expand Down
13 changes: 13 additions & 0 deletions src/Files.App/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@ namespace Files.App
{
internal class Program
{
public static Semaphore Pool;

static Program()
{
Pool = new(0, 1, "Files-Instance", out var isNew);
if (!isNew)
{
Pool.Release();
Environment.Exit(0);
}
Pool.Dispose();
}

// Note:
// We can't declare Main to be async because in a WinUI app
// This prevents Narrator from reading XAML elements
Expand Down
6 changes: 6 additions & 0 deletions src/Files.App/Services/Settings/GeneralSettingsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,12 @@ public bool ShowOpenInNewPane
set => Set(value);
}

public bool LeaveAppRunning
{
get => Get(false);
set => Set(value);
}

public FileNameConflictResolveOptionType ConflictsResolveOption
{
get => (FileNameConflictResolveOptionType)Get((long)FileNameConflictResolveOptionType.GenerateNewName);
Expand Down
3 changes: 3 additions & 0 deletions src/Files.App/Strings/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -3447,4 +3447,7 @@
<data name="FilesRunningAsAdminContent" xml:space="preserve">
<value>Due to platform limitations, drag and drop isn't available when running Files as administrator.</value>
</data>
<data name="SettingsLeaveAppRunning" xml:space="preserve">
<value>Leave app running in the background when the window is closed</value>
</data>
</root>
22 changes: 21 additions & 1 deletion src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public static class FilePropertiesHelpers
public static nint GetWindowHandle(Window w)
=> WinRT.Interop.WindowNative.GetWindowHandle(w);

private static int WindowCount = 0;
private static TaskCompletionSource? PropertiesWindowsClosingTCS;
private static BlockingCollection<WinUIEx.WindowEx> WindowCache = new();

/// <summary>
Expand Down Expand Up @@ -140,8 +142,10 @@ public static void OpenPropertiesWindow(object item, IShellPage associatedInstan
+ Math.Max(0, Math.Min(displayArea.WorkArea.Height - appWindow.Size.Height, pointerPosition.Y - displayArea.WorkArea.Y)),
};

appWindow.Move(appWindowPos);
if (Interlocked.Increment(ref WindowCount) == 1)
PropertiesWindowsClosingTCS = new();

appWindow.Move(appWindowPos);
appWindow.Show();
}

Expand All @@ -156,9 +160,19 @@ private static void PropertiesWindow_Closed(object sender, WindowEventArgs args)
window.AppWindow.Hide();
window.Content = null;
WindowCache.Add(window);

if (Interlocked.Decrement(ref WindowCount) == 0)
{
PropertiesWindowsClosingTCS!.TrySetResult();
PropertiesWindowsClosingTCS = null;
}
}
}

/// <summary>
/// Destroy all cached properties windows
/// </summary>
/// <returns></returns>
public static void DestroyCachedWindows()
{
while (WindowCache.TryTake(out var window))
Expand All @@ -167,5 +181,11 @@ public static void DestroyCachedWindows()
window.Close();
}
}

/// <summary>
/// Returns task to wait for all properties windows to close
/// </summary>
/// <returns>Task to wait</returns>
public static Task WaitClosingAll() => PropertiesWindowsClosingTCS?.Task ?? Task.CompletedTask;
}
}
14 changes: 14 additions & 0 deletions src/Files.App/ViewModels/Settings/AdvancedViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,20 @@ public bool CanOpenOnWindowsStartup
set => SetProperty(ref canOpenOnWindowsStartup, value);
}

public bool LeaveAppRunning
{
get => UserSettingsService.GeneralSettingsService.LeaveAppRunning;
set
{
if (value != UserSettingsService.GeneralSettingsService.LeaveAppRunning)
{
UserSettingsService.GeneralSettingsService.LeaveAppRunning = value;

OnPropertyChanged();
}
}
}

public async Task OpenFilesOnWindowsStartup()
{
var stateMode = await ReadState();
Expand Down
15 changes: 5 additions & 10 deletions src/Files.App/Views/Properties/MainPropertiesPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
// Copyright (c) 2023 Files Community
// Licensed under the MIT License. See the LICENSE.

using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.WinUI;
using Files.App.Data.Parameters;
using Files.App.Helpers;
using Files.App.ViewModels;
using Files.App.ViewModels.Properties;
using Microsoft.UI;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Threading.Tasks;
using Windows.System;
using Windows.UI;

Expand Down Expand Up @@ -45,16 +38,15 @@ protected override void OnNavigatedTo(NavigationEventArgs e)
AppWindow = parameter.AppWindow;
Window = parameter.Window;

AppSettings = Ioc.Default.GetRequiredService<SettingsViewModel>();
AppSettings.ThemeModeChanged += AppSettings_ThemeModeChanged;

base.OnNavigatedTo(e);

MainPropertiesViewModel = new(Window, AppWindow, MainContentFrame, BaseProperties, parameter);
}

private void Page_Loaded(object sender, RoutedEventArgs e)
{
AppSettings = Ioc.Default.GetRequiredService<SettingsViewModel>();
AppSettings.ThemeModeChanged += AppSettings_ThemeModeChanged;
Window.Closed += Window_Closed;

UpdatePageLayout();
Expand Down Expand Up @@ -89,6 +81,9 @@ private void UpdatePageLayout()

private async void AppSettings_ThemeModeChanged(object? sender, EventArgs e)
{
if (Parent is null)
return;

await DispatcherQueue.EnqueueOrInvokeAsync(() =>
{
((Frame)Parent).RequestedTheme = ThemeHelper.RootTheme;
Expand Down
11 changes: 11 additions & 0 deletions src/Files.App/Views/Settings/AdvancedPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,17 @@
</ToggleSwitch>
</local:SettingsBlockControl>

<!-- Leave App Running -->
<local:SettingsBlockControl Title="{helpers:ResourceString Name=SettingsLeaveAppRunning}" HorizontalAlignment="Stretch">
yaira2 marked this conversation as resolved.
Show resolved Hide resolved
<local:SettingsBlockControl.Icon>
<FontIcon Glyph="&#xE708;" />
yaira2 marked this conversation as resolved.
Show resolved Hide resolved
</local:SettingsBlockControl.Icon>
<ToggleSwitch
AutomationProperties.Name="{helpers:ResourceString Name=SettingsLeaveAppRunning}"
IsOn="{x:Bind ViewModel.LeaveAppRunning, Mode=TwoWay}"
Style="{StaticResource RightAlignedToggleSwitchStyle}" />
</local:SettingsBlockControl>

<!-- Experimental Settings -->
<TextBlock
Padding="0,16,0,4"
Expand Down
5 changes: 5 additions & 0 deletions src/Files.Core/Services/Settings/IGeneralSettingsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@ public interface IGeneralSettingsService : IBaseSettingsService, INotifyProperty
/// </summary>
bool ShowSendToMenu { get; set; }

/// <summary>
/// Gets or sets a value indicating whether or not to leave app running in the background.
/// </summary>
bool LeaveAppRunning { get; set; }
yaira2 marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Gets or sets a value indicating the default option to resolve conflicts.
/// </summary>
Expand Down