diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index ba6c659ccf..ac50aa86bd 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1364,6 +1364,7 @@ MMI mockapi MODECHANGE moderncop +modernwpf modulekey MONITORINFO MONITORINFOEX diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index 0b38064128..2b2f96488b 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -485,12 +485,16 @@ + + + + + - diff --git a/src/modules/imageresizer/tests/Properties/AppFixture.cs b/src/modules/imageresizer/tests/Properties/AppFixture.cs index 2361b76af4..433a869cb1 100644 --- a/src/modules/imageresizer/tests/Properties/AppFixture.cs +++ b/src/modules/imageresizer/tests/Properties/AppFixture.cs @@ -26,6 +26,7 @@ namespace ImageResizer.Properties { if (disposing) { + _imageResizerApp.Dispose(); _imageResizerApp = null; } diff --git a/src/modules/imageresizer/ui/App.xaml b/src/modules/imageresizer/ui/App.xaml index 8b64a919da..f29e4a54a2 100644 --- a/src/modules/imageresizer/ui/App.xaml +++ b/src/modules/imageresizer/ui/App.xaml @@ -3,74 +3,36 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:m="clr-namespace:ImageResizer.Models" xmlns:sys="clr-namespace:System;assembly=System.Runtime" - xmlns:v="clr-namespace:ImageResizer.Views"> + xmlns:v="clr-namespace:ImageResizer.Views" + xmlns:ui="http://schemas.modernwpf.com/2019"> - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/imageresizer/ui/App.xaml.cs b/src/modules/imageresizer/ui/App.xaml.cs index 1d6fba2cd5..ccd0993557 100644 --- a/src/modules/imageresizer/ui/App.xaml.cs +++ b/src/modules/imageresizer/ui/App.xaml.cs @@ -13,8 +13,11 @@ using ImageResizer.Views; namespace ImageResizer { - public partial class App : Application + public partial class App : Application, IDisposable { + private ThemeManager _themeManager; + private bool _isDisposed; + static App() { Console.InputEncoding = Encoding.Unicode; @@ -28,6 +31,8 @@ namespace ImageResizer var mainWindow = new MainWindow(new MainViewModel(batch, Settings.Default)); mainWindow.Show(); + _themeManager = new ThemeManager(this); + // Temporary workaround for issue #1273 BecomeForegroundWindow(new System.Windows.Interop.WindowInteropHelper(mainWindow).Handle); } @@ -39,5 +44,27 @@ namespace ImageResizer _ = NativeMethods.SendInput(1, inputs, NativeMethods.INPUT.Size); NativeMethods.SetForegroundWindow(hWnd); } + + protected virtual void Dispose(bool disposing) + { + if (!_isDisposed) + { + if (disposing) + { + _themeManager?.Dispose(); + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + _isDisposed = true; + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } } } diff --git a/src/modules/imageresizer/ui/ImageResizerUI.csproj b/src/modules/imageresizer/ui/ImageResizerUI.csproj index e7a580b509..2d95951209 100644 --- a/src/modules/imageresizer/ui/ImageResizerUI.csproj +++ b/src/modules/imageresizer/ui/ImageResizerUI.csproj @@ -61,10 +61,12 @@ + all + all diff --git a/src/modules/imageresizer/ui/Properties/Resources.Designer.cs b/src/modules/imageresizer/ui/Properties/Resources.Designer.cs index 500cac4970..8ac729b141 100644 --- a/src/modules/imageresizer/ui/Properties/Resources.Designer.cs +++ b/src/modules/imageresizer/ui/Properties/Resources.Designer.cs @@ -105,6 +105,15 @@ namespace ImageResizer.Properties { } } + /// + /// Looks up a localized string similar to Fallback encoder. + /// + public static string Advanced_FallbackEncoder_Name { + get { + return ResourceManager.GetString("Advanced_FallbackEncoder_Name", resourceCulture); + } + } + /// /// Looks up a localized string similar to File. /// @@ -123,6 +132,15 @@ namespace ImageResizer.Properties { } } + /// + /// Looks up a localized string similar to Filename. + /// + public static string Advanced_FileName_Name { + get { + return ResourceManager.GetString("Advanced_FileName_Name", resourceCulture); + } + } + /// /// Looks up a localized string similar to Original filename. /// @@ -195,6 +213,15 @@ namespace ImageResizer.Properties { } } + /// + /// Looks up a localized string similar to JPEG quality level. + /// + public static string Advanced_JpegQualityLevel_Name { + get { + return ResourceManager.GetString("Advanced_JpegQualityLevel_Name", resourceCulture); + } + } + /// /// Looks up a localized string similar to _Use original date modified. /// @@ -213,6 +240,15 @@ namespace ImageResizer.Properties { } } + /// + /// Looks up a localized string similar to PNG interlacing. + /// + public static string Advanced_PngInterlaceOption_Name { + get { + return ResourceManager.GetString("Advanced_PngInterlaceOption_Name", resourceCulture); + } + } + /// /// Looks up a localized string similar to Sizes. /// @@ -232,7 +268,16 @@ namespace ImageResizer.Properties { } /// - /// Looks up a localized string similar to Advanced Options. + /// Looks up a localized string similar to TIFF compression. + /// + public static string Advanced_TiffCompressOption_Name { + get { + return ResourceManager.GetString("Advanced_TiffCompressOption_Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Settings. /// public static string Advanced_Title { get { @@ -259,7 +304,7 @@ namespace ImageResizer.Properties { } /// - /// Looks up a localized string similar to © 2019 Brice Lambson. All rights reserved.. + /// Looks up a localized string similar to © 2020 Brice Lambson. All rights reserved.. /// public static string Copyright { get { @@ -268,7 +313,16 @@ namespace ImageResizer.Properties { } /// - /// Looks up a localized string similar to Image Resizer for Windows. + /// Looks up a localized string similar to Height. + /// + public static string Height { + get { + return ResourceManager.GetString("Height", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Image Resizer. /// public static string ImageResizer { get { @@ -286,7 +340,7 @@ namespace ImageResizer.Properties { } /// - /// Looks up a localized string similar to _Select a size.. + /// Looks up a localized string similar to _Select a size. /// public static string Input_Content { get { @@ -312,15 +366,6 @@ namespace ImageResizer.Properties { } } - /// - /// Looks up a localized string similar to Resize your pictures. - /// - public static string Input_MainInstruction { - get { - return ResourceManager.GetString("Input_MainInstruction", resourceCulture); - } - } - /// /// Looks up a localized string similar to R_esize the original pictures (don't create copies). /// @@ -340,7 +385,7 @@ namespace ImageResizer.Properties { } /// - /// Looks up a localized string similar to Advanced options.... + /// Looks up a localized string similar to Settings. /// public static string Input_ShowAdvanced { get { @@ -384,6 +429,15 @@ namespace ImageResizer.Properties { } } + /// + /// Looks up a localized string similar to Apply settings. + /// + public static string OK_Tooltip { + get { + return ResourceManager.GetString("OK_Tooltip", resourceCulture); + } + } + /// /// Looks up a localized string similar to Phone. /// @@ -537,6 +591,24 @@ namespace ImageResizer.Properties { } } + /// + /// Looks up a localized string similar to Resize pictures. + /// + public static string Resize_Tooltip { + get { + return ResourceManager.GetString("Resize_Tooltip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Resize type. + /// + public static string Resize_Type { + get { + return ResourceManager.GetString("Resize_Type", resourceCulture); + } + } + /// /// Looks up a localized string similar to Fill. /// @@ -717,6 +789,24 @@ namespace ImageResizer.Properties { } } + /// + /// Looks up a localized string similar to Times Symbol. + /// + public static string Times_Symbol { + get { + return ResourceManager.GetString("Times_Symbol", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unit. + /// + public static string Unit { + get { + return ResourceManager.GetString("Unit", resourceCulture); + } + } + /// /// Looks up a localized string similar to Value must be between '{0}' and '{1}'.. /// @@ -734,5 +824,14 @@ namespace ImageResizer.Properties { return ResourceManager.GetString("Version", resourceCulture); } } + + /// + /// Looks up a localized string similar to Width. + /// + public static string Width { + get { + return ResourceManager.GetString("Width", resourceCulture); + } + } } } diff --git a/src/modules/imageresizer/ui/Properties/Resources.resx b/src/modules/imageresizer/ui/Properties/Resources.resx index 63f04aaf6f..48d127c9f6 100644 --- a/src/modules/imageresizer/ui/Properties/Resources.resx +++ b/src/modules/imageresizer/ui/Properties/Resources.resx @@ -125,15 +125,21 @@ Delete + remove a file Encoding + encoding a file to a different format _Fallback encoder: + + Fallback encoder + File + as in file name _Filename: @@ -159,21 +165,33 @@ The following parameters can be used. + + Filename + _JPEG quality level: + + JPEG quality level + _Use original date modified _PNG interlacing: + + PNG interlacing + Sizes _TIFF compression: + + TIFF compression + Settings @@ -184,16 +202,20 @@ Cancel - © 2019 Brice Lambson. All rights reserved. + © 2020 Brice Lambson. All rights reserved. + + + Height Image Resizer + Product name, do not loc (auto) - _Select a size. + _Select a size Custom @@ -201,9 +223,6 @@ Ign_ore the orientation of pictures - - Resize your pictures - R_esize the original pictures (don't create copies) @@ -225,6 +244,9 @@ OK + + Apply settings + Phone @@ -306,6 +328,12 @@ Pixels + + Resize pictures + + + Resize type + Close @@ -336,10 +364,19 @@ Zip + + Times Symbol + + + Unit + Value must be between '{0}' and '{1}'. Version + + Width + \ No newline at end of file diff --git a/src/modules/imageresizer/ui/Themes/Dark.xaml b/src/modules/imageresizer/ui/Themes/Dark.xaml new file mode 100644 index 0000000000..f0d0cb1339 --- /dev/null +++ b/src/modules/imageresizer/ui/Themes/Dark.xaml @@ -0,0 +1,18 @@ + + + + Dark.Accent1 + PowerToysImageResizer + Accent1 (Dark) + Dark + Accent1 + Black + + + + + + + \ No newline at end of file diff --git a/src/modules/imageresizer/ui/Themes/HighContrast1.xaml b/src/modules/imageresizer/ui/Themes/HighContrast1.xaml new file mode 100644 index 0000000000..7bfaf2e19d --- /dev/null +++ b/src/modules/imageresizer/ui/Themes/HighContrast1.xaml @@ -0,0 +1,18 @@ + + + + HighContrast.Accent2 + PowerToysImageResizer + Accent2 (HighContrast) + HighContrast + Accent2 + White + + + + + + + \ No newline at end of file diff --git a/src/modules/imageresizer/ui/Themes/HighContrast2.xaml b/src/modules/imageresizer/ui/Themes/HighContrast2.xaml new file mode 100644 index 0000000000..15f7f1b639 --- /dev/null +++ b/src/modules/imageresizer/ui/Themes/HighContrast2.xaml @@ -0,0 +1,19 @@ + + + + HighContrast.Accent3 + PowerToysImageResizer + Accent3 (HighContrast) + HighContrast + Accent3 + White + + + + + + + + \ No newline at end of file diff --git a/src/modules/imageresizer/ui/Themes/HighContrastBlack.xaml b/src/modules/imageresizer/ui/Themes/HighContrastBlack.xaml new file mode 100644 index 0000000000..3f5458dabb --- /dev/null +++ b/src/modules/imageresizer/ui/Themes/HighContrastBlack.xaml @@ -0,0 +1,18 @@ + + + + HighContrast.Accent4 + PowerToysImageResizer + Accent4 (HighContrast) + HighContrast + Accent4 + White + + + + + + + \ No newline at end of file diff --git a/src/modules/imageresizer/ui/Themes/HighContrastWhite.xaml b/src/modules/imageresizer/ui/Themes/HighContrastWhite.xaml new file mode 100644 index 0000000000..b68137fc4b --- /dev/null +++ b/src/modules/imageresizer/ui/Themes/HighContrastWhite.xaml @@ -0,0 +1,18 @@ + + + + HighContrast.Accent5 + PowerToysImageResizer + Accent5 (HighContrast) + HighContrast + Accent5 + White + + + + + + + \ No newline at end of file diff --git a/src/modules/imageresizer/ui/Themes/Light.xaml b/src/modules/imageresizer/ui/Themes/Light.xaml new file mode 100644 index 0000000000..3f9323e3a0 --- /dev/null +++ b/src/modules/imageresizer/ui/Themes/Light.xaml @@ -0,0 +1,18 @@ + + + + Light.Accent1 + PowerToysImageResizer + Accent1 (Light) + Light + Accent1 + White + + + + + + + \ No newline at end of file diff --git a/src/modules/imageresizer/ui/Utilities/CustomLibraryThemeProvider.cs b/src/modules/imageresizer/ui/Utilities/CustomLibraryThemeProvider.cs new file mode 100644 index 0000000000..ab2fc3a7f5 --- /dev/null +++ b/src/modules/imageresizer/ui/Utilities/CustomLibraryThemeProvider.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using ControlzEx.Theming; + +namespace ImageResizer.Utilities +{ + public class CustomLibraryThemeProvider : LibraryThemeProvider + { + public static readonly CustomLibraryThemeProvider DefaultInstance = new CustomLibraryThemeProvider(); + + public CustomLibraryThemeProvider() + : base(true) + { + } + + /// + public override void FillColorSchemeValues(Dictionary values, RuntimeThemeColorValues colorValues) + { + } + } +} diff --git a/src/modules/imageresizer/ui/Utilities/ThemeManager.cs b/src/modules/imageresizer/ui/Utilities/ThemeManager.cs new file mode 100644 index 0000000000..03efb9688a --- /dev/null +++ b/src/modules/imageresizer/ui/Utilities/ThemeManager.cs @@ -0,0 +1,201 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Linq; +using System.Windows; +using ControlzEx.Theming; +using Microsoft.Win32; + +namespace ImageResizer.Utilities +{ + public class ThemeManager : IDisposable + { + private readonly Application _app; + private const string LightTheme = "Light.Accent1"; + private const string DarkTheme = "Dark.Accent1"; + private const string HighContrastOneTheme = "HighContrast.Accent2"; + private const string HighContrastTwoTheme = "HighContrast.Accent3"; + private const string HighContrastBlackTheme = "HighContrast.Accent4"; + private const string HighContrastWhiteTheme = "HighContrast.Accent5"; + + private Theme currentTheme; + private bool _disposed; + + public event ThemeChangedHandler ThemeChanged; + + public ThemeManager(Application app) + { + _app = app; + + Uri highContrastOneThemeUri = new Uri("pack://application:,,,/Themes/HighContrast1.xaml"); + Uri highContrastTwoThemeUri = new Uri("pack://application:,,,/Themes/HighContrast2.xaml"); + Uri highContrastBlackThemeUri = new Uri("pack://application:,,,/Themes/HighContrastWhite.xaml"); + Uri highContrastWhiteThemeUri = new Uri("pack://application:,,,/Themes/HighContrastBlack.xaml"); + Uri lightThemeUri = new Uri("pack://application:,,,/Themes/Light.xaml"); + Uri darkThemeUri = new Uri("pack://application:,,,/Themes/Dark.xaml"); + + ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme( + new LibraryTheme( + highContrastOneThemeUri, + CustomLibraryThemeProvider.DefaultInstance)); + ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme( + new LibraryTheme( + highContrastTwoThemeUri, + CustomLibraryThemeProvider.DefaultInstance)); + ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme( + new LibraryTheme( + highContrastBlackThemeUri, + CustomLibraryThemeProvider.DefaultInstance)); + ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme( + new LibraryTheme( + highContrastWhiteThemeUri, + CustomLibraryThemeProvider.DefaultInstance)); + ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme( + new LibraryTheme( + lightThemeUri, + CustomLibraryThemeProvider.DefaultInstance)); + ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme( + new LibraryTheme( + darkThemeUri, + CustomLibraryThemeProvider.DefaultInstance)); + + ResetTheme(); + ControlzEx.Theming.ThemeManager.Current.ThemeSyncMode = ThemeSyncMode.SyncWithAppMode; + ControlzEx.Theming.ThemeManager.Current.ThemeChanged += Current_ThemeChanged; + SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged; + } + + private void SystemParameters_StaticPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(SystemParameters.HighContrast)) + { + ResetTheme(); + } + } + + public Theme GetCurrentTheme() + { + return currentTheme; + } + + private static Theme GetHighContrastBaseType() + { + string registryKey = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes"; + string theme = (string)Registry.GetValue(registryKey, "CurrentTheme", string.Empty); + theme = theme.Split('\\').Last().Split('.').First().ToString(); + + switch (theme) + { + case "hc1": + return Theme.HighContrastOne; + case "hc2": + return Theme.HighContrastTwo; + case "hcwhite": + return Theme.HighContrastWhite; + case "hcblack": + return Theme.HighContrastBlack; + default: + return Theme.None; + } + } + + private void ResetTheme() + { + if (SystemParameters.HighContrast) + { + Theme highContrastBaseType = GetHighContrastBaseType(); + ChangeTheme(highContrastBaseType); + } + else + { + string baseColor = WindowsThemeHelper.GetWindowsBaseColor(); + ChangeTheme((Theme)Enum.Parse(typeof(Theme), baseColor)); + } + } + + private void ChangeTheme(Theme theme) + { + Theme oldTheme = currentTheme; + if (theme == currentTheme) + { + return; + } + + if (theme == Theme.HighContrastOne) + { + ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, HighContrastOneTheme); + currentTheme = Theme.HighContrastOne; + } + else if (theme == Theme.HighContrastTwo) + { + ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, HighContrastTwoTheme); + currentTheme = Theme.HighContrastTwo; + } + else if (theme == Theme.HighContrastWhite) + { + ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, HighContrastWhiteTheme); + currentTheme = Theme.HighContrastWhite; + } + else if (theme == Theme.HighContrastBlack) + { + ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, HighContrastBlackTheme); + currentTheme = Theme.HighContrastBlack; + } + else if (theme == Theme.Light) + { + ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, LightTheme); + currentTheme = Theme.Light; + } + else if (theme == Theme.Dark) + { + ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, DarkTheme); + currentTheme = Theme.Dark; + } + else + { + currentTheme = Theme.None; + } + + ThemeChanged?.Invoke(oldTheme, currentTheme); + } + + private void Current_ThemeChanged(object sender, ThemeChangedEventArgs e) + { + ResetTheme(); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + ControlzEx.Theming.ThemeManager.Current.ThemeChanged -= Current_ThemeChanged; + SystemParameters.StaticPropertyChanged -= SystemParameters_StaticPropertyChanged; + _disposed = true; + } + } + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } + + public delegate void ThemeChangedHandler(Theme oldTheme, Theme newTheme); + + public enum Theme + { + None, + Light, + Dark, + HighContrastOne, + HighContrastTwo, + HighContrastBlack, + HighContrastWhite, + } +} diff --git a/src/modules/imageresizer/ui/Views/AdvancedWindow.xaml b/src/modules/imageresizer/ui/Views/AdvancedWindow.xaml index 7abef9b325..d43c40b1e7 100644 --- a/src/modules/imageresizer/ui/Views/AdvancedWindow.xaml +++ b/src/modules/imageresizer/ui/Views/AdvancedWindow.xaml @@ -5,9 +5,13 @@ xmlns:m="clr-namespace:ImageResizer.Models" xmlns:p="clr-namespace:ImageResizer.Properties" xmlns:sys="clr-namespace:System;assembly=mscorlib" - MinWidth="390" + xmlns:ui="http://schemas.modernwpf.com/2019" + ContentRendered="WindowContentRendered" + MinWidth="560" MinHeight="340" - Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" + ui:WindowHelper.UseModernWindowStyle="True" + ui:TitleBar.IsIconVisible="True" + Background="{DynamicResource PrimaryBackgroundBrush}" Name="_this" ResizeMode="NoResize" SizeToContent="WidthAndHeight" @@ -33,19 +37,19 @@ - + - + - + - + - + @@ -55,11 +59,16 @@ - + @@ -68,9 +77,11 @@ + Width="56" + MaxWidth="56" + TextWrapping="Wrap" + AutomationProperties.Name="{x:Static p:Resources.Width}" + Margin="8,0,0,0"> - × - + Text="" + FontFamily="Segoe MDL2 Assets" + Width="25" + TextAlignment="Center" + Visibility="{Binding ShowHeight,Converter={StaticResource BoolValueConverter}}"/> @@ -113,24 +130,27 @@ - - - - - +