From 3c7e70a1003a0c7e8dbd4bf3b89ec421e29aa231 Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Mon, 9 Oct 2023 00:42:27 +0900 Subject: [PATCH 1/3] Initial commit --- src/Files.App/App.xaml.cs | 1 - src/Files.App/Dialogs/AddBranchDialog.xaml | 2 +- src/Files.App/Dialogs/AddItemDialog.xaml | 2 +- .../Dialogs/CreateArchiveDialog.xaml | 2 +- .../Dialogs/CreateShortcutDialog.xaml | 2 +- src/Files.App/Dialogs/CredentialDialog.xaml | 2 +- .../Dialogs/DecompressArchiveDialog.xaml | 2 +- src/Files.App/Dialogs/DynamicDialog.xaml | 2 +- .../Dialogs/ElevateConfirmDialog.xaml | 2 +- src/Files.App/Dialogs/FileTooLargeDialog.xaml | 2 +- .../Dialogs/FilesystemOperationDialog.xaml | 2 +- src/Files.App/Dialogs/GitHubLoginDialog.xaml | 2 +- src/Files.App/Dialogs/PropertiesDialog.xaml | 2 +- src/Files.App/Dialogs/ReleaseNotesDialog.xaml | 2 +- .../Dialogs/ReorderSidebarItemsDialog.xaml | 2 +- src/Files.App/Helpers/UI/AppThemeHelper.cs | 143 ++++++++++++++++++ src/Files.App/Helpers/UI/ThemeHelper.cs | 133 ---------------- src/Files.App/Services/ResourcesService.cs | 2 +- .../Settings/AppearanceSettingsService.cs | 7 + .../Storage/Helpers/FilePropertiesHelpers.cs | 7 +- src/Files.App/ViewModels/MainPageViewModel.cs | 2 +- .../Settings/AppearanceViewModel.cs | 4 +- src/Files.App/ViewModels/SettingsViewModel.cs | 110 -------------- .../UserControls/SidebarViewModel.cs | 9 +- .../Properties/MainPropertiesPage.xaml.cs | 26 ++-- .../Settings/IAppearanceSettingsService.cs | 5 + 26 files changed, 199 insertions(+), 278 deletions(-) create mode 100644 src/Files.App/Helpers/UI/AppThemeHelper.cs delete mode 100644 src/Files.App/Helpers/UI/ThemeHelper.cs delete mode 100644 src/Files.App/ViewModels/SettingsViewModel.cs diff --git a/src/Files.App/App.xaml.cs b/src/Files.App/App.xaml.cs index 1ea38214f59a..f1c83124dd14 100644 --- a/src/Files.App/App.xaml.cs +++ b/src/Files.App/App.xaml.cs @@ -145,7 +145,6 @@ private IHost ConfigureHost() .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() diff --git a/src/Files.App/Dialogs/AddBranchDialog.xaml b/src/Files.App/Dialogs/AddBranchDialog.xaml index b79572979456..7e4664be45fb 100644 --- a/src/Files.App/Dialogs/AddBranchDialog.xaml +++ b/src/Files.App/Dialogs/AddBranchDialog.xaml @@ -14,7 +14,7 @@ IsPrimaryButtonEnabled="{x:Bind ViewModel.IsBranchValid, Mode=OneWay}" PrimaryButtonStyle="{StaticResource AccentButtonStyle}" PrimaryButtonText="{helpers:ResourceString Name=Create}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind helpers:AppThemeHelper.RootTheme}" SecondaryButtonText="{helpers:ResourceString Name=Cancel}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/AddItemDialog.xaml b/src/Files.App/Dialogs/AddItemDialog.xaml index 8f47a4ab9324..f36721dfc5c0 100644 --- a/src/Files.App/Dialogs/AddItemDialog.xaml +++ b/src/Files.App/Dialogs/AddItemDialog.xaml @@ -14,7 +14,7 @@ BorderThickness="0" CornerRadius="{StaticResource OverlayCornerRadius}" Loaded="AddItemDialog_Loaded" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind helpers:AppThemeHelper.RootTheme}" SecondaryButtonText="{helpers:ResourceString Name=Cancel}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/CreateArchiveDialog.xaml b/src/Files.App/Dialogs/CreateArchiveDialog.xaml index 4c0f998947de..07981b7c2994 100644 --- a/src/Files.App/Dialogs/CreateArchiveDialog.xaml +++ b/src/Files.App/Dialogs/CreateArchiveDialog.xaml @@ -17,7 +17,7 @@ IsPrimaryButtonEnabled="{x:Bind ViewModel.IsNameValid, Mode=OneWay}" Loaded="ContentDialog_Loaded" PrimaryButtonText="{helpers:ResourceString Name=Create}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind helpers:AppThemeHelper.RootTheme}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/CreateShortcutDialog.xaml b/src/Files.App/Dialogs/CreateShortcutDialog.xaml index 0a3752eabfd7..6dae7ebd2984 100644 --- a/src/Files.App/Dialogs/CreateShortcutDialog.xaml +++ b/src/Files.App/Dialogs/CreateShortcutDialog.xaml @@ -11,7 +11,7 @@ IsPrimaryButtonEnabled="{x:Bind ViewModel.IsLocationValid, Mode=OneWay}" PrimaryButtonCommand="{x:Bind ViewModel.PrimaryButtonCommand}" PrimaryButtonText="{helpers:ResourceString Name=Create}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind helpers:AppThemeHelper.RootTheme}" SecondaryButtonText="{helpers:ResourceString Name=Cancel}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/CredentialDialog.xaml b/src/Files.App/Dialogs/CredentialDialog.xaml index 1194fc4377a8..acd517f298ec 100644 --- a/src/Files.App/Dialogs/CredentialDialog.xaml +++ b/src/Files.App/Dialogs/CredentialDialog.xaml @@ -12,7 +12,7 @@ PrimaryButtonClick="ContentDialog_PrimaryButtonClick" PrimaryButtonStyle="{StaticResource AccentButtonStyle}" PrimaryButtonText="{helpers:ResourceString Name=OK}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind helpers:AppThemeHelper.RootTheme}" SecondaryButtonText="{helpers:ResourceString Name=Cancel}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/DecompressArchiveDialog.xaml b/src/Files.App/Dialogs/DecompressArchiveDialog.xaml index 0529351e14f5..f010250c2842 100644 --- a/src/Files.App/Dialogs/DecompressArchiveDialog.xaml +++ b/src/Files.App/Dialogs/DecompressArchiveDialog.xaml @@ -11,7 +11,7 @@ DefaultButton="Primary" PrimaryButtonClick="ContentDialog_PrimaryButtonClick" PrimaryButtonText="{helpers:ResourceString Name=Extract}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind helpers:AppThemeHelper.RootTheme}" SecondaryButtonText="{helpers:ResourceString Name=Cancel}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/DynamicDialog.xaml b/src/Files.App/Dialogs/DynamicDialog.xaml index bfa44938d45c..bd9fa3b87dab 100644 --- a/src/Files.App/Dialogs/DynamicDialog.xaml +++ b/src/Files.App/Dialogs/DynamicDialog.xaml @@ -18,7 +18,7 @@ KeyDown="ContentDialog_KeyDown" PrimaryButtonClick="ContentDialog_PrimaryButtonClick" PrimaryButtonText="{x:Bind ViewModel.PrimaryButtonText, Mode=OneWay}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind helpers:AppThemeHelper.RootTheme}" SecondaryButtonClick="ContentDialog_SecondaryButtonClick" SecondaryButtonText="{x:Bind ViewModel.SecondaryButtonText, Mode=OneWay}" Style="{StaticResource DefaultContentDialogStyle}" diff --git a/src/Files.App/Dialogs/ElevateConfirmDialog.xaml b/src/Files.App/Dialogs/ElevateConfirmDialog.xaml index 2178cb986609..246da6efcd51 100644 --- a/src/Files.App/Dialogs/ElevateConfirmDialog.xaml +++ b/src/Files.App/Dialogs/ElevateConfirmDialog.xaml @@ -13,7 +13,7 @@ CornerRadius="{StaticResource OverlayCornerRadius}" DefaultButton="None" PrimaryButtonText="{helpers:ResourceString Name=Yes}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind helpers:AppThemeHelper.RootTheme}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/FileTooLargeDialog.xaml b/src/Files.App/Dialogs/FileTooLargeDialog.xaml index 827187926bee..a3964d051008 100644 --- a/src/Files.App/Dialogs/FileTooLargeDialog.xaml +++ b/src/Files.App/Dialogs/FileTooLargeDialog.xaml @@ -10,7 +10,7 @@ CornerRadius="{StaticResource OverlayCornerRadius}" DefaultButton="Primary" PrimaryButtonText="{helpers:ResourceString Name=OK}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind helpers:AppThemeHelper.RootTheme}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/FilesystemOperationDialog.xaml b/src/Files.App/Dialogs/FilesystemOperationDialog.xaml index abae820b5fd1..ce63dec086e2 100644 --- a/src/Files.App/Dialogs/FilesystemOperationDialog.xaml +++ b/src/Files.App/Dialogs/FilesystemOperationDialog.xaml @@ -18,7 +18,7 @@ IsPrimaryButtonEnabled="{x:Bind ViewModel.PrimaryButtonEnabled, Mode=OneWay}" Opened="FilesystemOperationDialog_Opened" PrimaryButtonText="{x:Bind ViewModel.PrimaryButtonText, Mode=OneWay}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind helpers:AppThemeHelper.RootTheme}" SecondaryButtonCommand="{x:Bind ViewModel.SecondaryButtonClickCommand, Mode=OneWay}" SecondaryButtonText="{x:Bind ViewModel.SecondaryButtonText, Mode=OneWay}" Style="{StaticResource DefaultContentDialogStyle}" diff --git a/src/Files.App/Dialogs/GitHubLoginDialog.xaml b/src/Files.App/Dialogs/GitHubLoginDialog.xaml index 86c1c0716a38..a645b8e9ddd6 100644 --- a/src/Files.App/Dialogs/GitHubLoginDialog.xaml +++ b/src/Files.App/Dialogs/GitHubLoginDialog.xaml @@ -14,7 +14,7 @@ CornerRadius="{StaticResource OverlayCornerRadius}" PrimaryButtonClick="ContentDialog_PrimaryButtonClick" PrimaryButtonText="{helpers:ResourceString Name=OK}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind helpers:AppThemeHelper.RootTheme}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/PropertiesDialog.xaml b/src/Files.App/Dialogs/PropertiesDialog.xaml index 0967d1475cc0..6696abfffd90 100644 --- a/src/Files.App/Dialogs/PropertiesDialog.xaml +++ b/src/Files.App/Dialogs/PropertiesDialog.xaml @@ -7,7 +7,7 @@ xmlns:local2="using:Files.App.Helpers" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Name="PropertiesDialogMarkup" - RequestedTheme="{x:Bind local2:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind local2:AppThemeHelper.RootTheme}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/ReleaseNotesDialog.xaml b/src/Files.App/Dialogs/ReleaseNotesDialog.xaml index 0fadd28854c3..12404fd7f190 100644 --- a/src/Files.App/Dialogs/ReleaseNotesDialog.xaml +++ b/src/Files.App/Dialogs/ReleaseNotesDialog.xaml @@ -9,7 +9,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" CornerRadius="{StaticResource OverlayCornerRadius}" DefaultButton="None" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind helpers:AppThemeHelper.RootTheme}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml b/src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml index 61a6129be386..de075cef221b 100644 --- a/src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml +++ b/src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml @@ -12,7 +12,7 @@ IsPrimaryButtonEnabled="True" PrimaryButtonCommand="{x:Bind ViewModel.PrimaryButtonCommand}" PrimaryButtonText="{helpers:ResourceString Name=Save}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind helpers:AppThemeHelper.RootTheme}" SecondaryButtonText="{helpers:ResourceString Name=Cancel}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Helpers/UI/AppThemeHelper.cs b/src/Files.App/Helpers/UI/AppThemeHelper.cs new file mode 100644 index 000000000000..d6948ec7623a --- /dev/null +++ b/src/Files.App/Helpers/UI/AppThemeHelper.cs @@ -0,0 +1,143 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +using Microsoft.UI; +using Microsoft.UI.Windowing; +using Microsoft.UI.Xaml; +using System.Windows.Input; +using Windows.Storage; +using Windows.UI; +using Windows.UI.ViewManagement; + +namespace Files.App.Helpers +{ + /// + /// Provides static helper for switching and restoring app theme settings. + /// + public static class AppThemeHelper + { + private static readonly IUserSettingsService _userSettingsService = Ioc.Default.GetRequiredService(); + + private static Window? _currentApplicationWindow; + + private static AppWindowTitleBar? _titleBar; + + private static bool _isInitialized = false; + + // Keep reference so it does not get optimized/garbage collected + private static UISettings? UISettings; + + public static event EventHandler? ThemeModeChanged; + + public static ICommand? UpdateThemeElements; + + /// + /// Gets or sets (with LocalSettings persistence) the RequestedTheme of the root element. + /// + public static ElementTheme RootTheme + { + get + { + var savedTheme = _userSettingsService.AppearanceSettingsService.AppThemeMode; + + return !string.IsNullOrEmpty(savedTheme) + ? EnumExtensions.GetEnum(savedTheme) + : ElementTheme.Default; + } + set + { + _userSettingsService.AppearanceSettingsService.AppThemeMode = value.ToString(); + + ApplyTheme(); + } + } + + public static bool Initialize() + { + if (_isInitialized) + return false; + + _isInitialized = true; + + UpdateThemeElements = new RelayCommand(() => ThemeModeChanged?.Invoke(null, EventArgs.Empty)); + + // Save reference as this might be null when the user is in another app + _currentApplicationWindow = MainWindow.Instance; + + // Set TitleBar background color + if (_currentApplicationWindow is not null) + _titleBar = MainWindow.Instance.AppWindow.TitleBar; + + // Apply the desired theme based on what is set in the application settings + ApplyTheme(); + + // Registering to color changes, thus we notice when user changes theme system wide + UISettings = new UISettings(); + UISettings.ColorValuesChanged += UISettings_ColorValuesChanged; + + return true; + } + + public static void ApplyResources() + { + // Toggle between the themes to force reload the resource styles + ApplyTheme(ElementTheme.Dark); + ApplyTheme(ElementTheme.Light); + + // Restore the theme to the correct theme + ApplyTheme(); + } + + private static async void UISettings_ColorValuesChanged(UISettings sender, object args) + { + // Make sure we have a reference to our window so we dispatch a UI change + if (_currentApplicationWindow is null) + { + _currentApplicationWindow = MainWindow.Instance; + + if (_currentApplicationWindow is null) + return; + } + + _titleBar ??= MainWindow.Instance.AppWindow.TitleBar; + + // Dispatch on UI thread so that we have a current app bar to access and change + await _currentApplicationWindow.DispatcherQueue.EnqueueOrInvokeAsync(ApplyTheme); + } + + private static void ApplyTheme() + { + ApplyTheme(RootTheme); + } + + private static void ApplyTheme(ElementTheme rootTheme) + { + if (MainWindow.Instance.Content is FrameworkElement rootElement) + rootElement.RequestedTheme = rootTheme; + + if (_titleBar is not null) + { + _titleBar.ButtonBackgroundColor = Colors.Transparent; + _titleBar.ButtonInactiveBackgroundColor = Colors.Transparent; + + switch (rootTheme) + { + case ElementTheme.Default: + _titleBar.ButtonHoverBackgroundColor = (Color)Application.Current.Resources["SystemBaseLowColor"]; + _titleBar.ButtonForegroundColor = (Color)Application.Current.Resources["SystemBaseHighColor"]; + break; + case ElementTheme.Light: + _titleBar.ButtonHoverBackgroundColor = Color.FromArgb(51, 0, 0, 0); + _titleBar.ButtonForegroundColor = Colors.Black; + break; + case ElementTheme.Dark: + _titleBar.ButtonHoverBackgroundColor = Color.FromArgb(51, 255, 255, 255); + _titleBar.ButtonForegroundColor = Colors.White; + break; + } + } + + UpdateThemeElements?.Execute(null); + } + } +} diff --git a/src/Files.App/Helpers/UI/ThemeHelper.cs b/src/Files.App/Helpers/UI/ThemeHelper.cs deleted file mode 100644 index 646abd68098c..000000000000 --- a/src/Files.App/Helpers/UI/ThemeHelper.cs +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) 2023 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI; -using Microsoft.UI.Windowing; -using Microsoft.UI.Xaml; -using Windows.Storage; -using Windows.UI; -using Windows.UI.ViewManagement; - -namespace Files.App.Helpers -{ - /// - /// Class providing functionality around switching and restoring theme settings - /// - public static class ThemeHelper - { - private const string selectedAppThemeKey = "theme"; - private static Window? currentApplicationWindow; - private static AppWindowTitleBar? titleBar; - private static bool isInitialized = false; - - // Keep reference so it does not get optimized/garbage collected - public static UISettings UiSettings; - - /// - /// Gets or sets (with LocalSettings persistence) the RequestedTheme of the root element. - /// - public static ElementTheme RootTheme - { - get - { - var savedTheme = ApplicationData.Current.LocalSettings.Values[selectedAppThemeKey]?.ToString(); - - return !string.IsNullOrEmpty(savedTheme) - ? EnumExtensions.GetEnum(savedTheme) - : ElementTheme.Default; - } - set - { - ApplicationData.Current.LocalSettings.Values[selectedAppThemeKey] = value.ToString(); - ApplyTheme(); - } - } - - public static bool Initialize() - { - if (isInitialized) - return false; - - isInitialized = true; - - // Save reference as this might be null when the user is in another app - currentApplicationWindow = MainWindow.Instance; - - // Set TitleBar background color - if (currentApplicationWindow is not null) - titleBar = MainWindow.Instance.AppWindow.TitleBar; - - // Apply the desired theme based on what is set in the application settings - ApplyTheme(); - - // Registering to color changes, thus we notice when user changes theme system wide - UiSettings = new UISettings(); - UiSettings.ColorValuesChanged += UiSettings_ColorValuesChanged; - - return true; - } - - public static void ApplyResources() - { - // Toggle between the themes to force reload the resource styles - ApplyTheme(ElementTheme.Dark); - ApplyTheme(ElementTheme.Light); - - // Restore the theme to the correct theme - ApplyTheme(); - } - - private static async void UiSettings_ColorValuesChanged(UISettings sender, object args) - { - // Make sure we have a reference to our window so we dispatch a UI change - if (currentApplicationWindow is null) - { - currentApplicationWindow = MainWindow.Instance; - - if (currentApplicationWindow is null) - return; - } - - titleBar ??= MainWindow.Instance.AppWindow.TitleBar; - - // Dispatch on UI thread so that we have a current appbar to access and change - await currentApplicationWindow.DispatcherQueue.EnqueueOrInvokeAsync(ApplyTheme); - } - - private static void ApplyTheme() - { - ApplyTheme(RootTheme); - } - - private static void ApplyTheme(ElementTheme rootTheme) - { - if (MainWindow.Instance.Content is FrameworkElement rootElement) - rootElement.RequestedTheme = rootTheme; - - if (titleBar is not null) - { - titleBar.ButtonBackgroundColor = Colors.Transparent; - titleBar.ButtonInactiveBackgroundColor = Colors.Transparent; - - switch (rootTheme) - { - case ElementTheme.Default: - titleBar.ButtonHoverBackgroundColor = (Color)Application.Current.Resources["SystemBaseLowColor"]; - titleBar.ButtonForegroundColor = (Color)Application.Current.Resources["SystemBaseHighColor"]; - break; - - case ElementTheme.Light: - titleBar.ButtonHoverBackgroundColor = Color.FromArgb(51, 0, 0, 0); - titleBar.ButtonForegroundColor = Colors.Black; - break; - - case ElementTheme.Dark: - titleBar.ButtonHoverBackgroundColor = Color.FromArgb(51, 255, 255, 255); - titleBar.ButtonForegroundColor = Colors.White; - break; - } - } - Ioc.Default.GetRequiredService().UpdateThemeElements.Execute(null); - } - } -} \ No newline at end of file diff --git a/src/Files.App/Services/ResourcesService.cs b/src/Files.App/Services/ResourcesService.cs index 83eedca8e3c8..a99f8208174d 100644 --- a/src/Files.App/Services/ResourcesService.cs +++ b/src/Files.App/Services/ResourcesService.cs @@ -55,7 +55,7 @@ public void SetCompactSpacing(bool useCompactSpacing) /// public void ApplyResources() { - ThemeHelper.ApplyResources(); + AppThemeHelper.ApplyResources(); } } } \ No newline at end of file diff --git a/src/Files.App/Services/Settings/AppearanceSettingsService.cs b/src/Files.App/Services/Settings/AppearanceSettingsService.cs index 688f2223bb68..d734990cb142 100644 --- a/src/Files.App/Services/Settings/AppearanceSettingsService.cs +++ b/src/Files.App/Services/Settings/AppearanceSettingsService.cs @@ -35,6 +35,13 @@ public bool UseCompactStyles set => Set(value); } + /// + public String AppThemeMode + { + get => Get("Default"); + set => Set(value); + } + /// public String AppThemeBackgroundColor { diff --git a/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs b/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs index fa9915450952..b5f4d60a685c 100644 --- a/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs +++ b/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs @@ -93,7 +93,7 @@ public static void OpenPropertiesWindow(object item, IShellPage associatedInstan var frame = new Frame { - RequestedTheme = ThemeHelper.RootTheme + RequestedTheme = AppThemeHelper.RootTheme }; WinUIEx.WindowEx propertiesWindow; @@ -113,12 +113,13 @@ public static void OpenPropertiesWindow(object item, IShellPage associatedInstan propertiesWindow.SystemBackdrop = new AppSystemBackdrop(true); var appWindow = propertiesWindow.AppWindow; - appWindow.Title = "Properties".GetLocalizedResource(); + appWindow.Title = $"{"Properties".GetLocalizedResource()} - Files"; appWindow.TitleBar.ExtendsContentIntoTitleBar = true; appWindow.TitleBar.ButtonBackgroundColor = Colors.Transparent; appWindow.TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent; - appWindow.SetIcon(applicationService.AppIcoPath); + var ApplicationService = new ApplicationService(); + appWindow.SetIcon(SystemIO.Path.Combine(Package.Current.InstalledLocation.Path, ApplicationService.AppIcoPath)); frame.Navigate( typeof(Views.Properties.MainPropertiesPage), diff --git a/src/Files.App/ViewModels/MainPageViewModel.cs b/src/Files.App/ViewModels/MainPageViewModel.cs index bdb9aca8ed80..ebec9d43fa21 100644 --- a/src/Files.App/ViewModels/MainPageViewModel.cs +++ b/src/Files.App/ViewModels/MainPageViewModel.cs @@ -284,7 +284,7 @@ public async Task OnNavigatedTo(NavigationEventArgs e) //Initialize the static theme helper to capture a reference to this window //to handle theme changes without restarting the app - var isInitialized = ThemeHelper.Initialize(); + var isInitialized = AppThemeHelper.Initialize(); var parameter = e.Parameter; var ignoreStartupSettings = false; diff --git a/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs b/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs index 980db2d7708b..f0453f4f9e79 100644 --- a/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs +++ b/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs @@ -87,7 +87,7 @@ public AppThemeResourceItem SelectedAppThemeResources } } - private int selectedThemeIndex = (int)Enum.Parse(typeof(ElementTheme), ThemeHelper.RootTheme.ToString()); + private int selectedThemeIndex = (int)Enum.Parse(typeof(ElementTheme), AppThemeHelper.RootTheme.ToString()); public int SelectedThemeIndex { get => selectedThemeIndex; @@ -95,7 +95,7 @@ public int SelectedThemeIndex { if (SetProperty(ref selectedThemeIndex, value)) { - ThemeHelper.RootTheme = (ElementTheme)value; + AppThemeHelper.RootTheme = (ElementTheme)value; OnPropertyChanged(nameof(SelectedElementTheme)); } } diff --git a/src/Files.App/ViewModels/SettingsViewModel.cs b/src/Files.App/ViewModels/SettingsViewModel.cs deleted file mode 100644 index cf837503e801..000000000000 --- a/src/Files.App/ViewModels/SettingsViewModel.cs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) 2023 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Windows.Input; -using Windows.Storage; - -namespace Files.App.ViewModels -{ - [Obsolete("Do not use this class as Settings store anymore, settings have been merged to IUserSettingsService.")] - public class SettingsViewModel : ObservableObject - { - private readonly ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings; - - public SettingsViewModel() - { - UpdateThemeElements = new RelayCommand(() => ThemeModeChanged?.Invoke(this, EventArgs.Empty)); - } - - public event EventHandler ThemeModeChanged; - - public ICommand UpdateThemeElements { get; } - - #region ReadAndSaveSettings - - public bool Set(TValue value, [CallerMemberName] string propertyName = null) - { - propertyName = - propertyName is not null && propertyName.StartsWith("set_", StringComparison.OrdinalIgnoreCase) ? - propertyName.Substring(4) : - propertyName; - - TValue originalValue = default; - - if (localSettings.Values.ContainsKey(propertyName)) - { - originalValue = Get(originalValue, propertyName); - - localSettings.Values[propertyName] = value; - if (!SetProperty(ref originalValue, value, propertyName)) - { - return false; - } - } - else - { - localSettings.Values[propertyName] = value; - } - - return true; - } - - public TValue Get<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TValue>(TValue defaultValue, [CallerMemberName] string propertyName = null) - { - var name = propertyName ?? throw new ArgumentNullException(nameof(propertyName), "Cannot store property of unnamed."); - - name = - name.StartsWith("get_", StringComparison.OrdinalIgnoreCase) ? - propertyName.Substring(4) : - propertyName; - - if (localSettings.Values.ContainsKey(name)) - { - var value = localSettings.Values[name]; - - if (value is not TValue tValue) - { - if (value is IConvertible) - { - tValue = (TValue)Convert.ChangeType(value, typeof(TValue)); - } - else - { - var valueType = value.GetType(); - var tryParse = typeof(TValue).GetMethod("TryParse", BindingFlags.Instance | BindingFlags.Public); - - if (tryParse is null) - { - return default; - } - - var stringValue = value.ToString(); - tValue = default; - - var tryParseDelegate = - (TryParseDelegate)Delegate.CreateDelegate(valueType, tryParse, false); - - tValue = (tryParseDelegate?.Invoke(stringValue, out tValue) ?? false) ? tValue : default; - } - - // Put the corrected value in settings - Set(tValue, propertyName); - - return tValue; - } - return tValue; - } - - localSettings.Values[propertyName] = defaultValue; - - return defaultValue; - } - - private delegate bool TryParseDelegate(string inValue, out TValue parsedValue); - - #endregion ReadAndSaveSettings - } -} diff --git a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs index dd2315384b3d..37375417c627 100644 --- a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs @@ -399,7 +399,10 @@ await lib.CheckDefaultSaveFolderAccess() && } } - section.IsExpanded = Ioc.Default.GetRequiredService().Get(section.Text == "SidebarFavorites".GetLocalizedResource(), $"section:{section.Text.Replace('\\', '_')}"); + var key = $"section:{section.Text.Replace('\\', '_')}"; + + ApplicationData.Current.LocalSettings.Values.Get(key, section.Text == "SidebarFavorites".GetLocalizedResource()); + section.PropertyChanged += Section_PropertyChanged; } @@ -407,7 +410,9 @@ private void Section_PropertyChanged(object? sender, PropertyChangedEventArgs e) { if (sender is LocationItem section && e.PropertyName == nameof(section.IsExpanded)) { - Ioc.Default.GetRequiredService().Set(section.IsExpanded, $"section:{section.Text.Replace('\\', '_')}"); + var key = $"section:{section.Text.Replace('\\', '_')}"; + + ApplicationData.Current.LocalSettings.Values[key] = section.IsExpanded; } } diff --git a/src/Files.App/Views/Properties/MainPropertiesPage.xaml.cs b/src/Files.App/Views/Properties/MainPropertiesPage.xaml.cs index b8f2233ea386..8a0fc0563347 100644 --- a/src/Files.App/Views/Properties/MainPropertiesPage.xaml.cs +++ b/src/Files.App/Views/Properties/MainPropertiesPage.xaml.cs @@ -8,6 +8,7 @@ using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Navigation; +using Windows.ApplicationModel; using Windows.System; using Windows.UI; @@ -19,8 +20,6 @@ public sealed partial class MainPropertiesPage : BasePropertiesPage private Window Window; - private SettingsViewModel AppSettings { get; set; } - private MainPropertiesViewModel MainPropertiesViewModel { get; set; } public MainPropertiesPage() @@ -35,25 +34,27 @@ protected override void OnNavigatedTo(NavigationEventArgs e) { var parameter = (PropertiesPageNavigationParameter)e.Parameter; - AppWindow = parameter.AppWindow; Window = parameter.Window; + AppWindow = parameter.AppWindow; - base.OnNavigatedTo(e); + Window.Closed += Window_Closed; MainPropertiesViewModel = new(Window, AppWindow, MainContentFrame, BaseProperties, parameter); + + base.OnNavigatedTo(e); } private void Page_Loaded(object sender, RoutedEventArgs e) { - AppSettings = Ioc.Default.GetRequiredService(); - AppSettings.ThemeModeChanged += AppSettings_ThemeModeChanged; - Window.Closed += Window_Closed; + AppSettings_ThemeModeChanged(null, null); UpdatePageLayout(); } private void Page_SizeChanged(object sender, SizeChangedEventArgs e) - => UpdatePageLayout(); + { + UpdatePageLayout(); + } private void Page_KeyDown(object sender, KeyRoutedEventArgs e) { @@ -86,9 +87,12 @@ private async void AppSettings_ThemeModeChanged(object? sender, EventArgs e) await DispatcherQueue.EnqueueOrInvokeAsync(() => { - ((Frame)Parent).RequestedTheme = ThemeHelper.RootTheme; + ((Frame)Parent).RequestedTheme = AppThemeHelper.RootTheme; + + AppWindow.TitleBar.ButtonBackgroundColor = Colors.Transparent; + AppWindow.TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent; - switch (ThemeHelper.RootTheme) + switch (AppThemeHelper.RootTheme) { case ElementTheme.Default: AppWindow.TitleBar.ButtonHoverBackgroundColor = (Color)Application.Current.Resources["SystemBaseLowColor"]; @@ -108,7 +112,7 @@ await DispatcherQueue.EnqueueOrInvokeAsync(() => private void Window_Closed(object sender, WindowEventArgs args) { - AppSettings.ThemeModeChanged -= AppSettings_ThemeModeChanged; + AppThemeHelper.ThemeModeChanged -= AppSettings_ThemeModeChanged; Window.Closed -= Window_Closed; if (MainPropertiesViewModel.ChangedPropertiesCancellationTokenSource is not null && diff --git a/src/Files.Core/Services/Settings/IAppearanceSettingsService.cs b/src/Files.Core/Services/Settings/IAppearanceSettingsService.cs index 00da16c03643..7a64227c742c 100644 --- a/src/Files.Core/Services/Settings/IAppearanceSettingsService.cs +++ b/src/Files.Core/Services/Settings/IAppearanceSettingsService.cs @@ -24,6 +24,11 @@ public interface IAppearanceSettingsService : IBaseSettingsService, INotifyPrope /// bool UseCompactStyles { get; set; } + /// + /// Gets or sets a value for the app theme mode. + /// + String AppThemeMode { get; set; } + /// /// Gets or sets a value for the app theme background color. /// From 4e6fbb5aecfea7b6ced2ba9a2aff444db72bbaf9 Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Mon, 9 Oct 2023 01:30:54 +0900 Subject: [PATCH 2/3] Update ApplyTheme to be generic method --- src/Files.App/Helpers/UI/AppThemeHelper.cs | 72 +++++++------------ src/Files.App/Services/ResourcesService.cs | 2 +- .../Storage/Helpers/FilePropertiesHelpers.cs | 1 + .../Properties/MainPropertiesPage.xaml.cs | 30 +------- 4 files changed, 31 insertions(+), 74 deletions(-) diff --git a/src/Files.App/Helpers/UI/AppThemeHelper.cs b/src/Files.App/Helpers/UI/AppThemeHelper.cs index d6948ec7623a..0df5138c756d 100644 --- a/src/Files.App/Helpers/UI/AppThemeHelper.cs +++ b/src/Files.App/Helpers/UI/AppThemeHelper.cs @@ -18,14 +18,10 @@ public static class AppThemeHelper { private static readonly IUserSettingsService _userSettingsService = Ioc.Default.GetRequiredService(); - private static Window? _currentApplicationWindow; - - private static AppWindowTitleBar? _titleBar; - private static bool _isInitialized = false; // Keep reference so it does not get optimized/garbage collected - private static UISettings? UISettings; + private static UISettings? uiSettings; public static event EventHandler? ThemeModeChanged; @@ -61,28 +57,21 @@ public static bool Initialize() UpdateThemeElements = new RelayCommand(() => ThemeModeChanged?.Invoke(null, EventArgs.Empty)); - // Save reference as this might be null when the user is in another app - _currentApplicationWindow = MainWindow.Instance; - - // Set TitleBar background color - if (_currentApplicationWindow is not null) - _titleBar = MainWindow.Instance.AppWindow.TitleBar; - // Apply the desired theme based on what is set in the application settings ApplyTheme(); // Registering to color changes, thus we notice when user changes theme system wide - UISettings = new UISettings(); - UISettings.ColorValuesChanged += UISettings_ColorValuesChanged; + uiSettings = new UISettings(); + uiSettings.ColorValuesChanged += UISettings_ColorValuesChanged; return true; } - public static void ApplyResources() + public static void RefreshThemeMode() { // Toggle between the themes to force reload the resource styles - ApplyTheme(ElementTheme.Dark); - ApplyTheme(ElementTheme.Light); + ApplyTheme(MainWindow.Instance, ElementTheme.Dark); + ApplyTheme(MainWindow.Instance, ElementTheme.Light); // Restore the theme to the correct theme ApplyTheme(); @@ -90,54 +79,45 @@ public static void ApplyResources() private static async void UISettings_ColorValuesChanged(UISettings sender, object args) { - // Make sure we have a reference to our window so we dispatch a UI change - if (_currentApplicationWindow is null) - { - _currentApplicationWindow = MainWindow.Instance; - - if (_currentApplicationWindow is null) - return; - } - - _titleBar ??= MainWindow.Instance.AppWindow.TitleBar; - // Dispatch on UI thread so that we have a current app bar to access and change - await _currentApplicationWindow.DispatcherQueue.EnqueueOrInvokeAsync(ApplyTheme); + await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => ApplyTheme()); } - private static void ApplyTheme() + public static void ApplyTheme(Window? window = null, ElementTheme? rootTheme = null, bool executeChangedEvent = true) { - ApplyTheme(RootTheme); - } + window ??= MainWindow.Instance; - private static void ApplyTheme(ElementTheme rootTheme) - { - if (MainWindow.Instance.Content is FrameworkElement rootElement) - rootElement.RequestedTheme = rootTheme; + rootTheme ??= RootTheme; + + if (window.Content is FrameworkElement rootElement) + rootElement.RequestedTheme = (ElementTheme)rootTheme; + + var titleBar = window.AppWindow.TitleBar; - if (_titleBar is not null) + if (titleBar is not null) { - _titleBar.ButtonBackgroundColor = Colors.Transparent; - _titleBar.ButtonInactiveBackgroundColor = Colors.Transparent; + titleBar.ButtonBackgroundColor = Colors.Transparent; + titleBar.ButtonInactiveBackgroundColor = Colors.Transparent; switch (rootTheme) { case ElementTheme.Default: - _titleBar.ButtonHoverBackgroundColor = (Color)Application.Current.Resources["SystemBaseLowColor"]; - _titleBar.ButtonForegroundColor = (Color)Application.Current.Resources["SystemBaseHighColor"]; + titleBar.ButtonHoverBackgroundColor = (Color)Application.Current.Resources["SystemBaseLowColor"]; + titleBar.ButtonForegroundColor = (Color)Application.Current.Resources["SystemBaseHighColor"]; break; case ElementTheme.Light: - _titleBar.ButtonHoverBackgroundColor = Color.FromArgb(51, 0, 0, 0); - _titleBar.ButtonForegroundColor = Colors.Black; + titleBar.ButtonHoverBackgroundColor = Color.FromArgb(51, 0, 0, 0); + titleBar.ButtonForegroundColor = Colors.Black; break; case ElementTheme.Dark: - _titleBar.ButtonHoverBackgroundColor = Color.FromArgb(51, 255, 255, 255); - _titleBar.ButtonForegroundColor = Colors.White; + titleBar.ButtonHoverBackgroundColor = Color.FromArgb(51, 255, 255, 255); + titleBar.ButtonForegroundColor = Colors.White; break; } } - UpdateThemeElements?.Execute(null); + if (executeChangedEvent) + UpdateThemeElements?.Execute(null); } } } diff --git a/src/Files.App/Services/ResourcesService.cs b/src/Files.App/Services/ResourcesService.cs index a99f8208174d..7fb63472511c 100644 --- a/src/Files.App/Services/ResourcesService.cs +++ b/src/Files.App/Services/ResourcesService.cs @@ -55,7 +55,7 @@ public void SetCompactSpacing(bool useCompactSpacing) /// public void ApplyResources() { - AppThemeHelper.ApplyResources(); + AppThemeHelper.RefreshThemeMode(); } } } \ No newline at end of file diff --git a/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs b/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs index b5f4d60a685c..b80a8beb3d6b 100644 --- a/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs +++ b/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs @@ -8,6 +8,7 @@ using Microsoft.UI.Xaml.Media.Animation; using Microsoft.Windows.ApplicationModel.Resources; using System.Collections.Concurrent; +using Windows.ApplicationModel; using Windows.Graphics; namespace Files.App.Utils.Storage diff --git a/src/Files.App/Views/Properties/MainPropertiesPage.xaml.cs b/src/Files.App/Views/Properties/MainPropertiesPage.xaml.cs index 8a0fc0563347..1de7dc3708cc 100644 --- a/src/Files.App/Views/Properties/MainPropertiesPage.xaml.cs +++ b/src/Files.App/Views/Properties/MainPropertiesPage.xaml.cs @@ -8,7 +8,6 @@ using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Navigation; -using Windows.ApplicationModel; using Windows.System; using Windows.UI; @@ -39,6 +38,8 @@ protected override void OnNavigatedTo(NavigationEventArgs e) Window.Closed += Window_Closed; + AppThemeHelper.ThemeModeChanged += AppSettings_ThemeModeChanged; + MainPropertiesViewModel = new(Window, AppWindow, MainContentFrame, BaseProperties, parameter); base.OnNavigatedTo(e); @@ -82,32 +83,7 @@ private void UpdatePageLayout() private async void AppSettings_ThemeModeChanged(object? sender, EventArgs e) { - if (Parent is null) - return; - - await DispatcherQueue.EnqueueOrInvokeAsync(() => - { - ((Frame)Parent).RequestedTheme = AppThemeHelper.RootTheme; - - AppWindow.TitleBar.ButtonBackgroundColor = Colors.Transparent; - AppWindow.TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent; - - switch (AppThemeHelper.RootTheme) - { - case ElementTheme.Default: - AppWindow.TitleBar.ButtonHoverBackgroundColor = (Color)Application.Current.Resources["SystemBaseLowColor"]; - AppWindow.TitleBar.ButtonForegroundColor = (Color)Application.Current.Resources["SystemBaseHighColor"]; - break; - case ElementTheme.Light: - AppWindow.TitleBar.ButtonHoverBackgroundColor = Color.FromArgb(0x33, 0, 0, 0); - AppWindow.TitleBar.ButtonForegroundColor = Colors.Black; - break; - case ElementTheme.Dark: - AppWindow.TitleBar.ButtonHoverBackgroundColor = Color.FromArgb(0x33, 0xFF, 0xFF, 0xFF); - AppWindow.TitleBar.ButtonForegroundColor = Colors.White; - break; - } - }); + AppThemeHelper.ApplyTheme(Window, AppThemeHelper.RootTheme, false); } private void Window_Closed(object sender, WindowEventArgs args) From ad362fd4cc468196eeec984dd711f71642a124c6 Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Sat, 14 Oct 2023 21:57:58 +0900 Subject: [PATCH 3/3] Update --- src/Files.App/Helpers/UI/AppThemeHelper.cs | 111 ++++++++++-------- .../Settings/AppearanceViewModel.cs | 2 +- .../UserControls/SidebarViewModel.cs | 2 +- 3 files changed, 61 insertions(+), 54 deletions(-) diff --git a/src/Files.App/Helpers/UI/AppThemeHelper.cs b/src/Files.App/Helpers/UI/AppThemeHelper.cs index 0df5138c756d..c15f724c174e 100644 --- a/src/Files.App/Helpers/UI/AppThemeHelper.cs +++ b/src/Files.App/Helpers/UI/AppThemeHelper.cs @@ -2,10 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using Microsoft.UI; -using Microsoft.UI.Windowing; using Microsoft.UI.Xaml; -using System.Windows.Input; -using Windows.Storage; using Windows.UI; using Windows.UI.ViewManagement; @@ -20,42 +17,34 @@ public static class AppThemeHelper private static bool _isInitialized = false; - // Keep reference so it does not get optimized/garbage collected private static UISettings? uiSettings; - public static event EventHandler? ThemeModeChanged; - - public static ICommand? UpdateThemeElements; - /// - /// Gets or sets (with LocalSettings persistence) the RequestedTheme of the root element. + /// Gets the UI theme mode that is used by the root window content for resource determination. /// - public static ElementTheme RootTheme - { - get - { - var savedTheme = _userSettingsService.AppearanceSettingsService.AppThemeMode; + /// + /// The UI theme mode specified with RequestedTheme can override the app-level RequestedTheme. + /// + public static ElementTheme RootTheme => + !string.IsNullOrEmpty(_userSettingsService.AppearanceSettingsService.AppThemeMode) + ? EnumExtensions.GetEnum(_userSettingsService.AppearanceSettingsService.AppThemeMode) + : ElementTheme.Default; - return !string.IsNullOrEmpty(savedTheme) - ? EnumExtensions.GetEnum(savedTheme) - : ElementTheme.Default; - } - set - { - _userSettingsService.AppearanceSettingsService.AppThemeMode = value.ToString(); - - ApplyTheme(); - } - } + public static event EventHandler? ThemeModeChanged; + /// + /// Initializes a new instance. + /// + /// + /// Once this class was initialized, this class doesn't need to be initialized again in somewhere else. + /// + /// Returns true if initialization succeed; otherwise false. public static bool Initialize() { if (_isInitialized) return false; - - _isInitialized = true; - - UpdateThemeElements = new RelayCommand(() => ThemeModeChanged?.Invoke(null, EventArgs.Empty)); + else + _isInitialized = true; // Apply the desired theme based on what is set in the application settings ApplyTheme(); @@ -67,6 +56,12 @@ public static bool Initialize() return true; } + /// + /// Refreshes theme mode. + /// + /// + /// To reload the app resources, the theme will be toggled between dark and light, and the correct theme will be applied. + /// public static void RefreshThemeMode() { // Toggle between the themes to force reload the resource styles @@ -77,47 +72,59 @@ public static void RefreshThemeMode() ApplyTheme(); } - private static async void UISettings_ColorValuesChanged(UISettings sender, object args) - { - // Dispatch on UI thread so that we have a current app bar to access and change - await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => ApplyTheme()); - } - + /// + /// Applies theme mode to the requested theme of a specific window. + /// + /// A window whose requested theme will be changed. + /// Requested theme mode + /// Determines if the event should be triggered. if the main window's requested theme will be changed, the event should be called public static void ApplyTheme(Window? window = null, ElementTheme? rootTheme = null, bool executeChangedEvent = true) { + // Validate variables window ??= MainWindow.Instance; - rootTheme ??= RootTheme; + // Set theme mode on the window if (window.Content is FrameworkElement rootElement) rootElement.RequestedTheme = (ElementTheme)rootTheme; - var titleBar = window.AppWindow.TitleBar; + // Store theme mode setting + _userSettingsService.AppearanceSettingsService.AppThemeMode = rootTheme.ToString()!; + // Update title bar buttons color + var titleBar = window.AppWindow.TitleBar; if (titleBar is not null) { + // Remove normal state background titleBar.ButtonBackgroundColor = Colors.Transparent; titleBar.ButtonInactiveBackgroundColor = Colors.Transparent; - switch (rootTheme) + // Pointer over state background + titleBar.ButtonHoverBackgroundColor = rootTheme switch + { + ElementTheme.Light => titleBar.ButtonHoverBackgroundColor = Color.FromArgb(0x33, 0x00, 0x00, 0x00), + ElementTheme.Dark => titleBar.ButtonHoverBackgroundColor = Color.FromArgb(0x33, 0xFF, 0xFF, 0xFF), + _ => titleBar.ButtonHoverBackgroundColor = (Color)Application.Current.Resources["SystemBaseLowColor"], + }; + + // Pointer over state foreground + titleBar.ButtonForegroundColor = rootTheme switch { - case ElementTheme.Default: - titleBar.ButtonHoverBackgroundColor = (Color)Application.Current.Resources["SystemBaseLowColor"]; - titleBar.ButtonForegroundColor = (Color)Application.Current.Resources["SystemBaseHighColor"]; - break; - case ElementTheme.Light: - titleBar.ButtonHoverBackgroundColor = Color.FromArgb(51, 0, 0, 0); - titleBar.ButtonForegroundColor = Colors.Black; - break; - case ElementTheme.Dark: - titleBar.ButtonHoverBackgroundColor = Color.FromArgb(51, 255, 255, 255); - titleBar.ButtonForegroundColor = Colors.White; - break; - } + ElementTheme.Light => titleBar.ButtonHoverBackgroundColor = Colors.Black, + ElementTheme.Dark => titleBar.ButtonHoverBackgroundColor = Colors.White, + _ => titleBar.ButtonHoverBackgroundColor = (Color)Application.Current.Resources["SystemBaseHighColor"], + }; } + // Trigger theme mode changed event if (executeChangedEvent) - UpdateThemeElements?.Execute(null); + ThemeModeChanged?.Invoke(null, EventArgs.Empty); + } + + private static async void UISettings_ColorValuesChanged(UISettings sender, object args) + { + // Dispatch on UI thread so that we have a current app bar to access and change + await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => ApplyTheme()); } } } diff --git a/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs b/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs index f0453f4f9e79..1177ce5f25e8 100644 --- a/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs +++ b/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs @@ -95,7 +95,7 @@ public int SelectedThemeIndex { if (SetProperty(ref selectedThemeIndex, value)) { - AppThemeHelper.RootTheme = (ElementTheme)value; + AppThemeHelper.ApplyTheme(MainWindow.Instance, (ElementTheme)value); OnPropertyChanged(nameof(SelectedElementTheme)); } } diff --git a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs index 37375417c627..7dff54df60ed 100644 --- a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs @@ -401,7 +401,7 @@ await lib.CheckDefaultSaveFolderAccess() && var key = $"section:{section.Text.Replace('\\', '_')}"; - ApplicationData.Current.LocalSettings.Values.Get(key, section.Text == "SidebarFavorites".GetLocalizedResource()); + section.IsExpanded = ApplicationData.Current.LocalSettings.Values.Get(key, section.Text == "SidebarFavorites".GetLocalizedResource()); section.PropertyChanged += Section_PropertyChanged; }