diff --git a/Files/App.xaml b/Files/App.xaml
index 2bbee6f285a5..0de9ba45c940 100644
--- a/Files/App.xaml
+++ b/Files/App.xaml
@@ -1,8 +1,8 @@
-
+ xmlns:converters="using:Files.Helpers">
@@ -10,44 +10,19 @@
-
-
-
-
-
-
-
-
+
+
-
@@ -62,6 +37,7 @@
+
@@ -69,6 +45,7 @@
+
\ No newline at end of file
diff --git a/Files/App.xaml.cs b/Files/App.xaml.cs
index 1fe3131d6633..59cd239c6cc7 100644
--- a/Files/App.xaml.cs
+++ b/Files/App.xaml.cs
@@ -1,6 +1,7 @@
-using Files.CommandLine;
+using Files.CommandLine;
using Files.Controls;
using Files.Filesystem;
+using Files.Helpers;
using Files.Interacts;
using Files.View_Models;
using Microsoft.AppCenter;
@@ -11,7 +12,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
-using System.Linq;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.ApplicationModel.DataTransfer;
@@ -19,8 +19,6 @@
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
-using Windows.UI.Xaml.Controls.Primitives;
-using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Navigation;
@@ -202,6 +200,7 @@ protected override void OnLaunched(LaunchActivatedEventArgs e)
rootFrame.Navigate(typeof(InstanceTabsView), e.Arguments, new SuppressNavigationTransitionInfo());
}
+ ThemeHelper.Initialize();
// Ensure the current window is active
Window.Current.Activate();
Window.Current.CoreWindow.PointerPressed += CoreWindow_PointerPressed;
diff --git a/Files/Dialogs/ConfirmDeleteDialog.xaml b/Files/Dialogs/ConfirmDeleteDialog.xaml
index 1b08953c86fb..8530c5990390 100644
--- a/Files/Dialogs/ConfirmDeleteDialog.xaml
+++ b/Files/Dialogs/ConfirmDeleteDialog.xaml
@@ -2,15 +2,15 @@
x:Class="Files.Dialogs.ConfirmDeleteDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:Windows10version1903="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 8)"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- xmlns:local="using:Files.Dialogs"
xmlns:local1="using:Files"
+ xmlns:local2="using:Files.Helpers"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Uid="ConfirmDeleteDialog"
Title="Delete Item(s)"
CornerRadius="4"
- mc:Ignorable="d">
+ mc:Ignorable="d"
+ RequestedTheme="{x:Bind local2:ThemeHelper.RootTheme}">
diff --git a/Files/Dialogs/ConsentDialog.xaml b/Files/Dialogs/ConsentDialog.xaml
index 85cc21bc99e4..a4ac23d8050c 100644
--- a/Files/Dialogs/ConsentDialog.xaml
+++ b/Files/Dialogs/ConsentDialog.xaml
@@ -2,14 +2,23 @@
x:Class="Files.Dialogs.ConsentDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:local="using:Files.Dialogs"
+ xmlns:local2="using:Files.Helpers"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
- xmlns:Windows10version1903="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 8)"
- CornerRadius="4" Grid.RowSpan="4" DefaultButton="Primary" PrimaryButtonClick="PermissionDialog_PrimaryButtonClick" PrimaryButtonText="Grant Permission" x:Name="PermissionDialog" Title="Welcome to Files" x:Uid="WelcomeDialog">
+ CornerRadius="4"
+ Grid.RowSpan="4"
+ DefaultButton="Primary"
+ PrimaryButtonClick="PermissionDialog_PrimaryButtonClick"
+ PrimaryButtonText="Grant Permission"
+ x:Name="PermissionDialog"
+ Title="Welcome to Files"
+ x:Uid="WelcomeDialog"
+ RequestedTheme="{x:Bind local2:ThemeHelper.RootTheme}">
-
+
\ No newline at end of file
diff --git a/Files/Dialogs/ExceptionDialog.xaml b/Files/Dialogs/ExceptionDialog.xaml
index 704f5ed0c816..c70e41dee94b 100644
--- a/Files/Dialogs/ExceptionDialog.xaml
+++ b/Files/Dialogs/ExceptionDialog.xaml
@@ -2,7 +2,7 @@
x:Class="Files.Dialogs.ExceptionDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:local="using:Files.Dialogs"
+ xmlns:local2="using:Files.Helpers"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
@@ -13,7 +13,9 @@
CloseButtonText="Ignore"
DefaultButton="Primary"
PrimaryButtonClick="ContentDialog_PrimaryButtonClick"
- SecondaryButtonClick="ContentDialog_SecondaryButtonClick" Loaded="ContentDialog_Loaded">
+ SecondaryButtonClick="ContentDialog_SecondaryButtonClick"
+ Loaded="ContentDialog_Loaded"
+ RequestedTheme="{x:Bind local2:ThemeHelper.RootTheme}">
diff --git a/Files/Dialogs/ExtractFilesDialog.xaml b/Files/Dialogs/ExtractFilesDialog.xaml
index 41b9c492dd0a..ceb2e8d069e1 100644
--- a/Files/Dialogs/ExtractFilesDialog.xaml
+++ b/Files/Dialogs/ExtractFilesDialog.xaml
@@ -3,7 +3,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- xmlns:local="using:Files.Dialogs"
+ xmlns:local2="using:Files.Helpers"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Uid="ExtractFilesDialog"
Title="Extract Compressed Archive"
@@ -13,7 +13,8 @@
DefaultButton="Primary"
PrimaryButtonClick="ContentDialog_PrimaryButtonClick"
PrimaryButtonText="Extract"
- mc:Ignorable="d">
+ mc:Ignorable="d"
+ RequestedTheme="{x:Bind local2:ThemeHelper.RootTheme}">
diff --git a/Files/Dialogs/PropertiesDialog.xaml b/Files/Dialogs/PropertiesDialog.xaml
index e92eefaac60d..8c8ef0fe0789 100644
--- a/Files/Dialogs/PropertiesDialog.xaml
+++ b/Files/Dialogs/PropertiesDialog.xaml
@@ -2,14 +2,14 @@
x:Class="Files.Dialogs.PropertiesDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:Windows10version1903="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 8)"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- xmlns:local="using:Files.Dialogs"
+ xmlns:local2="using:Files.Helpers"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="PropertiesDialogMarkup"
Title="Properties"
CornerRadius="4"
- mc:Ignorable="d">
+ mc:Ignorable="d"
+ RequestedTheme="{x:Bind local2:ThemeHelper.RootTheme}">
+ CornerRadius="4"
+ Grid.RowSpan="4"
+ DefaultButton="Primary"
+ Title="Enter an item name"
+ BorderThickness="0"
+ PrimaryButtonClick="NameDialog_PrimaryButtonClick"
+ x:Name="NameDialog"
+ PrimaryButtonText="Set Name"
+ SecondaryButtonText="Cancel"
+ x:Uid="RenameDialog"
+ RequestedTheme="{x:Bind local2:ThemeHelper.RootTheme}">
-
+
\ No newline at end of file
diff --git a/Files/Enums/ThemeStyle.cs b/Files/Enums/ThemeStyle.cs
deleted file mode 100644
index bbeaea8320d7..000000000000
--- a/Files/Enums/ThemeStyle.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Files.Enums
-{
- public enum ThemeStyle
- {
- System = 0,
- Light = 1,
- Dark = 2
- }
-}
\ No newline at end of file
diff --git a/Files/Files.csproj b/Files/Files.csproj
index 85f7a0a69a53..90697e87e194 100644
--- a/Files/Files.csproj
+++ b/Files/Files.csproj
@@ -149,8 +149,11 @@
ConfirmDeleteDialog.xaml
+
+
+
@@ -185,7 +188,6 @@
-
diff --git a/Files/Helpers/AppTheme.cs b/Files/Helpers/AppTheme.cs
new file mode 100644
index 000000000000..ce2e17657777
--- /dev/null
+++ b/Files/Helpers/AppTheme.cs
@@ -0,0 +1,90 @@
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using Windows.UI;
+using Windows.UI.Xaml;
+
+namespace Files.Helpers
+{
+ public class AppTheme : INotifyPropertyChanged
+ {
+ private double? _TintLuminosityOpacity;
+ private Color _FallbackColor;
+ private Color _TintColor;
+ private double _TintOpacity;
+
+ public double? TintLuminosityOpacity
+ {
+ get { return _TintLuminosityOpacity; }
+ set
+ {
+ _TintLuminosityOpacity = value;
+ NotifyPropertyChanged("TintLuminosityOpacity");
+ }
+ }
+
+ public Color FallbackColor
+ {
+ get { return _FallbackColor; }
+ set
+ {
+ _FallbackColor = value;
+ NotifyPropertyChanged("FallbackColor");
+ }
+ }
+
+ public Color TintColor
+ {
+ get { return _TintColor; }
+ set
+ {
+ _TintColor = value;
+ NotifyPropertyChanged("TintColor");
+ }
+ }
+
+ public double TintOpacity
+ {
+ get { return _TintOpacity; }
+ set
+ {
+ _TintOpacity = value;
+ NotifyPropertyChanged("TintOpacity");
+ }
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ public AppTheme()
+ {
+ }
+
+ public void SetDefaultTheme()
+ {
+ TintLuminosityOpacity = 0.9;
+ FallbackColor = (Color)Application.Current.Resources["SystemChromeMediumLowColor"];
+ TintColor = (Color)Application.Current.Resources["SystemAltHighColor"];
+ TintOpacity = 0.9;
+ }
+
+ public void SetLightTheme()
+ {
+ TintLuminosityOpacity = 0.9;
+ FallbackColor = Color.FromArgb(255, 242, 242, 242);
+ TintColor = Colors.White;
+ TintOpacity = 0.9;
+ }
+
+ public void SetDarkTheme()
+ {
+ TintLuminosityOpacity = 0.9;
+ FallbackColor = Color.FromArgb(255, 43, 43, 43);
+ TintColor = Colors.Black;
+ TintOpacity = 0.7;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Files/Helpers/NegateConverter.cs b/Files/Helpers/NegateConverter.cs
new file mode 100644
index 000000000000..806b56704017
--- /dev/null
+++ b/Files/Helpers/NegateConverter.cs
@@ -0,0 +1,28 @@
+using System;
+using Windows.UI.Xaml.Data;
+
+namespace Files.Helpers
+{
+ public class NegateConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ if (targetType != typeof(bool))
+ {
+ throw new InvalidOperationException("The target must be a boolean");
+ }
+
+ return !(bool)value;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ if (targetType != typeof(bool))
+ {
+ throw new InvalidOperationException("The target must be a boolean");
+ }
+
+ return !(bool)value;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Files/Helpers/ThemeHelper.cs b/Files/Helpers/ThemeHelper.cs
new file mode 100644
index 000000000000..b77bce47803e
--- /dev/null
+++ b/Files/Helpers/ThemeHelper.cs
@@ -0,0 +1,133 @@
+using System.Collections.Generic;
+using System.Linq;
+using Windows.Storage;
+using Windows.UI;
+using Windows.UI.ViewManagement;
+using Windows.UI.Xaml;
+
+namespace Files.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 ApplicationViewTitleBar _TitleBar;
+
+ // Keep reference so it does not get optimized/garbage collected
+ public static UISettings UiSettings;
+
+ ///
+ /// Gets the current actual theme of the app based on the requested theme of the
+ /// root element, or if that value is Default, the requested theme of the Application.
+ ///
+ public static ElementTheme ActualTheme
+ {
+ get
+ {
+ if (Window.Current.Content is FrameworkElement rootElement)
+ {
+ if (rootElement.RequestedTheme != ElementTheme.Default)
+ {
+ return rootElement.RequestedTheme;
+ }
+ }
+
+ return Interacts.Interaction.GetEnum(Application.Current.RequestedTheme.ToString());
+ }
+ }
+
+ ///
+ /// Gets or sets (with LocalSettings persistence) the RequestedTheme of the root element.
+ ///
+ public static ElementTheme RootTheme
+ {
+ get
+ {
+ if (Window.Current.Content is FrameworkElement rootElement)
+ {
+ return rootElement.RequestedTheme;
+ }
+
+ return ElementTheme.Default;
+ }
+ set
+ {
+ if (Window.Current.Content is FrameworkElement rootElement)
+ {
+ rootElement.RequestedTheme = value;
+ }
+
+ ApplicationData.Current.LocalSettings.Values[_SelectedAppThemeKey] = value.ToString();
+ UpdateTheme();
+ }
+ }
+
+ public static void Initialize()
+ {
+ App.AppSettings.AppTheme = new AppTheme();
+
+ // Set TitleBar background color
+ _TitleBar = ApplicationView.GetForCurrentView().TitleBar;
+ _TitleBar.ButtonBackgroundColor = Colors.Transparent;
+ _TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
+
+ // Save reference as this might be null when the user is in another app
+ _CurrentApplicationWindow = Window.Current;
+ string savedTheme = ApplicationData.Current.LocalSettings.Values[_SelectedAppThemeKey]?.ToString();
+
+ if (!string.IsNullOrEmpty(savedTheme))
+ {
+ RootTheme = Interacts.Interaction.GetEnum(savedTheme);
+ }
+ else
+ {
+ RootTheme = ElementTheme.Default;
+ }
+
+ // Registering to color changes, thus we notice when user changes theme system wide
+ UiSettings = new UISettings();
+ UiSettings.ColorValuesChanged += UiSettings_ColorValuesChanged;
+ }
+
+ private static void UiSettings_ColorValuesChanged(UISettings sender, object args)
+ {
+ // Make sure we have a reference to our window so we dispatch a UI change
+ if (_CurrentApplicationWindow != null)
+ {
+ // Dispatch on UI thread so that we have a current appbar to access and change
+ _CurrentApplicationWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
+ {
+ UpdateTheme();
+ });
+ }
+ }
+
+ public static void UpdateTheme()
+ {
+ switch (RootTheme)
+ {
+ case ElementTheme.Default:
+ App.AppSettings.AppTheme.SetDefaultTheme();
+ _TitleBar.ButtonHoverBackgroundColor = (Color)Application.Current.Resources["SystemBaseLowColor"];
+ _TitleBar.ButtonForegroundColor = (Color)Application.Current.Resources["SystemBaseHighColor"];
+ break;
+
+ case ElementTheme.Light:
+ App.AppSettings.AppTheme.SetLightTheme();
+ _TitleBar.ButtonHoverBackgroundColor = Color.FromArgb(51, 0, 0, 0);
+ _TitleBar.ButtonForegroundColor = Colors.Black;
+ break;
+
+ case ElementTheme.Dark:
+ App.AppSettings.AppTheme.SetDarkTheme();
+ _TitleBar.ButtonHoverBackgroundColor = Color.FromArgb(51, 255, 255, 255);
+ _TitleBar.ButtonForegroundColor = Colors.White;
+ break;
+ }
+ App.AppSettings.UpdateThemeElements.Execute(null);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Files/Interacts/Interaction.cs b/Files/Interacts/Interaction.cs
index c30d6d68ca67..6b43a784322c 100644
--- a/Files/Interacts/Interaction.cs
+++ b/Files/Interacts/Interaction.cs
@@ -36,6 +36,8 @@
using Windows.Security.Cryptography;
using Windows.Storage.Streams;
using GalaSoft.MvvmLight.Command;
+using Files.Helpers;
+using Windows.UI.Xaml.Data;
namespace Files.Interacts
{
@@ -113,7 +115,7 @@ await CoreWindow.GetForCurrentThread().Dispatcher.RunAsync(CoreDispatcherPriorit
instanceTabsView.AddNewTab(typeof(ModernShellPage), listedItem.ItemPath);
});
}
- }
+ }
public void OpenPathInNewTab(string path)
{
@@ -314,6 +316,15 @@ public static T FindParent(DependencyObject child) where T : DependencyObject
return parent;
}
+ public static TEnum GetEnum(string text) where TEnum : struct
+ {
+ if (!typeof(TEnum).GetTypeInfo().IsEnum)
+ {
+ throw new InvalidOperationException("Generic parameter 'TEnum' must be an enum.");
+ }
+ return (TEnum)Enum.Parse(typeof(TEnum), text);
+ }
+
public void OpenItem_Click(object sender, RoutedEventArgs e)
{
OpenSelectedItems(false);
@@ -440,19 +451,6 @@ public async void ShowPropertiesButton_Click(object sender, RoutedEventArgs e)
AppWindow appWindow = await AppWindow.TryCreateAsync();
Frame frame = new Frame();
appWindow.TitleBar.ExtendsContentIntoTitleBar = true;
- var titleBar = appWindow.TitleBar;
- titleBar.ButtonBackgroundColor = Colors.Transparent;
- titleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
- var selectedTheme = Application.Current.RequestedTheme;
- if (selectedTheme == ApplicationTheme.Light)
- {
- titleBar.ButtonForegroundColor = Color.FromArgb(255, 0, 0, 0);
- titleBar.ButtonHoverBackgroundColor = Color.FromArgb(20, 0, 0, 0);
- }
- else if (selectedTheme == ApplicationTheme.Dark)
- {
- titleBar.ButtonHoverBackgroundColor = Color.FromArgb(40, 255, 255, 255);
- }
frame.Navigate(typeof(Properties), null, new SuppressNavigationTransitionInfo());
WindowManagementPreview.SetPreferredMinSize(appWindow, new Size(400, 475));
@@ -907,6 +905,9 @@ public async Task PasteItems(DataPackageView packageView, string destinationPath
if (destinationPath.Contains(item.Path, StringComparison.OrdinalIgnoreCase))
{
ImpossibleActionResponseTypes responseType = ImpossibleActionResponseTypes.Abort;
+ Binding themeBind = new Binding();
+ themeBind.Source = ThemeHelper.RootTheme;
+
ContentDialog dialog = new ContentDialog()
{
Title = ResourceController.GetTranslation("ErrorDialogThisActionCannotBeDone"),
@@ -916,6 +917,8 @@ public async Task PasteItems(DataPackageView packageView, string destinationPath
PrimaryButtonCommand = new RelayCommand(() => { responseType = ImpossibleActionResponseTypes.Skip; }),
CloseButtonCommand = new RelayCommand(() => { responseType = ImpossibleActionResponseTypes.Abort; })
};
+ BindingOperations.SetBinding(dialog, FrameworkElement.RequestedThemeProperty, themeBind);
+
await dialog.ShowAsync();
if (responseType == ImpossibleActionResponseTypes.Skip)
{
diff --git a/Files/MultilingualResources/Files.es-ES.xlf b/Files/MultilingualResources/Files.es-ES.xlf
index 9de65ccc0968..fcd1fe72e50c 100644
--- a/Files/MultilingualResources/Files.es-ES.xlf
+++ b/Files/MultilingualResources/Files.es-ES.xlf
@@ -617,4 +617,4 @@