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

Code Quality: Improved app theme mode handler #13490

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/Files.App/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ private IHost ConfigureHost()
.AddSingleton<MainPageViewModel>()
.AddSingleton<PreviewPaneViewModel>()
.AddSingleton<SidebarViewModel>()
.AddSingleton<SettingsViewModel>()
.AddSingleton<DrivesViewModel>()
.AddSingleton<NetworkDrivesViewModel>()
.AddSingleton<StatusCenterViewModel>()
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Dialogs/AddBranchDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Dialogs/AddItemDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Dialogs/CreateArchiveDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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">

Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Dialogs/CreateShortcutDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Dialogs/CredentialDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Dialogs/DecompressArchiveDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Dialogs/DynamicDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Dialogs/ElevateConfirmDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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">

Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Dialogs/FileTooLargeDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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">

Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Dialogs/FilesystemOperationDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Dialogs/GitHubLoginDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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">

Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Dialogs/PropertiesDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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">

<ContentDialog.Resources>
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Dialogs/ReleaseNotesDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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">
<ContentDialog.Resources>
Expand Down
2 changes: 1 addition & 1 deletion src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down
130 changes: 130 additions & 0 deletions src/Files.App/Helpers/UI/AppThemeHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright (c) 2023 Files Community
// Licensed under the MIT License. See the LICENSE.

using Microsoft.UI;
using Microsoft.UI.Xaml;
using Windows.UI;
using Windows.UI.ViewManagement;

namespace Files.App.Helpers
{
/// <summary>
/// Provides static helper for switching and restoring app theme settings.
/// </summary>
public static class AppThemeHelper
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be simplified to ThemeHelper?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, looks like that was already the name.

{
private static readonly IUserSettingsService _userSettingsService = Ioc.Default.GetRequiredService<IUserSettingsService>();

private static bool _isInitialized = false;

private static UISettings? uiSettings;

/// <summary>
/// Gets the UI theme mode that is used by the root window content for resource determination.
/// </summary>
/// <remarks>
/// The UI theme mode specified with RequestedTheme can override the app-level RequestedTheme.
/// </remarks>
public static ElementTheme RootTheme =>
!string.IsNullOrEmpty(_userSettingsService.AppearanceSettingsService.AppThemeMode)
? EnumExtensions.GetEnum<ElementTheme>(_userSettingsService.AppearanceSettingsService.AppThemeMode)
: ElementTheme.Default;

public static event EventHandler? ThemeModeChanged;

/// <summary>
/// Initializes a new <see cref="AppThemeHelper"/> instance.
/// </summary>
/// <remarks>
/// Once this class was initialized, this class doesn't need to be initialized again in somewhere else.
/// </remarks>
/// <returns>Returns true if initialization succeed; otherwise false.</returns>
public static bool Initialize()
{
if (_isInitialized)
return false;
else
_isInitialized = true;

// 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;
}

/// <summary>
/// Refreshes theme mode.
/// </summary>
/// <remarks>
/// To reload the app resources, the theme will be toggled between dark and light, and the correct theme will be applied.
/// </remarks>
public static void RefreshThemeMode()
{
// Toggle between the themes to force reload the resource styles
ApplyTheme(MainWindow.Instance, ElementTheme.Dark);
ApplyTheme(MainWindow.Instance, ElementTheme.Light);

// Restore the theme to the correct theme
ApplyTheme();
}

/// <summary>
/// Applies theme mode to the requested theme of a specific window.
/// </summary>
/// <param name="window">A window whose requested theme will be changed.</param>
/// <param name="rootTheme">Requested theme mode</param>
/// <param name="executeChangedEvent">Determines if the event should be triggered. if the main window's requested theme will be changed, the event should be called</param>
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;

// 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;

// 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
{
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)
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());
}
}
}
133 changes: 0 additions & 133 deletions src/Files.App/Helpers/UI/ThemeHelper.cs

This file was deleted.

Loading