diff --git a/src/modules/imageresizer/ui/App.xaml b/src/modules/imageresizer/ui/App.xaml index 30dd38a48fe9..a25c2c7a51b4 100644 --- a/src/modules/imageresizer/ui/App.xaml +++ b/src/modules/imageresizer/ui/App.xaml @@ -20,6 +20,8 @@ + + \ No newline at end of file diff --git a/src/modules/imageresizer/ui/Views/AutoDoubleConverter.cs b/src/modules/imageresizer/ui/Views/AutoDoubleConverter.cs index 69f886036b1e..631416a7c0ba 100644 --- a/src/modules/imageresizer/ui/Views/AutoDoubleConverter.cs +++ b/src/modules/imageresizer/ui/Views/AutoDoubleConverter.cs @@ -10,29 +10,55 @@ using ImageResizer.Properties; -namespace ImageResizer.Views +namespace ImageResizer.Views; + +/// +/// Converts between double and string for text-based controls bound to Width or Height fields. +/// Optionally returns localized "Auto" text when the underlying value is 0, letting the UI show, +/// for example "(auto) x 1024 pixels". +/// +[ValueConversion(typeof(double), typeof(string))] +internal class AutoDoubleConverter : IValueConverter { - [ValueConversion(typeof(double), typeof(string))] - internal class AutoDoubleConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + /// + /// Converts a double to a string, optionally showing "Auto" for 0 values. NaN values are + /// converted to empty strings. + /// + /// The value to convert from to + /// . + /// The conversion target type. here. + /// Set to "Auto" to return the localized "Auto" string if the + /// value is 0. + /// The to use for the number formatting. + /// + /// The string representation of the passed-in value. + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => + value switch { - var d = (double)value; + double d => d switch + { + double.NaN => "0", + 0 => (string)parameter == "Auto" ? Resources.Input_Auto : "0", + _ => d.ToString(culture), + }, - return d != 0 - ? d.ToString(culture) - : (string)parameter == "Auto" - ? Resources.Input_Auto - : string.Empty; - } + _ => "0", + }; - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + /// + /// Converts the string representation back to a double, returning 0 if the string is empty, + /// null or not a valid number in the specified culture. + /// + /// The string value to convert. + /// The conversion target type. here. + /// Converter parameter. Unused. + /// The to use for the text parsing. + /// The corresponding double value. + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => + value switch { - var text = (string)value; - - return !string.IsNullOrEmpty(text) - ? double.Parse(text, culture) - : 0; - } - } + null or "" => 0, + string text when double.TryParse(text, NumberStyles.Any, culture, out double result) => result, + _ => 0, + }; } diff --git a/src/modules/imageresizer/ui/Views/InputPage.xaml b/src/modules/imageresizer/ui/Views/InputPage.xaml index f1e997022b09..8249aef00dd1 100644 --- a/src/modules/imageresizer/ui/Views/InputPage.xaml +++ b/src/modules/imageresizer/ui/Views/InputPage.xaml @@ -4,7 +4,8 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:m="clr-namespace:ImageResizer.Models" xmlns:p="clr-namespace:ImageResizer.Properties" - xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"> + xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml" + xmlns:v="clr-namespace:ImageResizer.Views"> @@ -107,8 +108,12 @@ KeyDown="Button_KeyDown" Minimum="0" SpinButtonPlacementMode="Inline"> + + + + + + + /// Converts the underlying double value to a display-friendly format. Ensures that NaN values + /// are not propagated to the UI. + /// + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => + value is double d && double.IsNaN(d) ? 0 : value; + + /// + /// Converts the user input back to the underlying double value. If the input is not a valid + /// number, 0 is returned. + /// + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => + value switch + { + null => 0, + double d when double.IsNaN(d) => 0, + string str when !double.TryParse(str, out _) => 0, + _ => value, + }; +} diff --git a/src/modules/imageresizer/ui/Views/ZeroToEmptyStringNumberFormatter.cs b/src/modules/imageresizer/ui/Views/ZeroToEmptyStringNumberFormatter.cs new file mode 100644 index 000000000000..a753895e58bf --- /dev/null +++ b/src/modules/imageresizer/ui/Views/ZeroToEmptyStringNumberFormatter.cs @@ -0,0 +1,37 @@ +// 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.Globalization; +using Wpf.Ui.Controls; + +namespace ImageResizer.Views; + +public class ZeroToEmptyStringNumberFormatter : INumberFormatter, INumberParser +{ + public string FormatDouble(double? value) => value switch + { + null => string.Empty, + 0 => string.Empty, + _ => value.Value.ToString(CultureInfo.CurrentCulture), + }; + + public double? ParseDouble(string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + return 0; + } + + return double.TryParse(value, NumberStyles.Any, CultureInfo.CurrentCulture, out double result) ? result : 0; + } + + public string FormatInt(int? value) => throw new NotImplementedException(); + + public string FormatUInt(uint? value) => throw new NotImplementedException(); + + public int? ParseInt(string value) => throw new NotImplementedException(); + + public uint? ParseUInt(string value) => throw new NotImplementedException(); +} diff --git a/src/settings-ui/Settings.UI/Converters/ImageResizerDoubleToAutoConverter.cs b/src/settings-ui/Settings.UI/Converters/ImageResizerDoubleToAutoConverter.cs new file mode 100644 index 000000000000..63ba8cde9278 --- /dev/null +++ b/src/settings-ui/Settings.UI/Converters/ImageResizerDoubleToAutoConverter.cs @@ -0,0 +1,61 @@ +// 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.Globalization; +using Microsoft.UI.Xaml.Data; + +namespace Microsoft.PowerToys.Settings.UI.Converters; + +/// +/// Converts between double and string for text-based controls bound to Width or Height fields. +/// Optionally returns localized "Auto" text when the underlying value is 0, letting the UI show, +/// for example "(auto) x 1024 pixels". +/// +public sealed partial class ImageResizerDoubleToAutoConverter : IValueConverter +{ + private static readonly string AutoText = + Helpers.ResourceLoaderInstance.ResourceLoader.GetString("ImageResizer_AutoText"); + + /// + /// Converts a double to a string, optionally showing "Auto" for 0 values. NaN values are + /// converted to empty strings. + /// + /// The value to convert from to + /// . + /// The conversion target type. here. + /// Set to "Auto" to return the localized "Auto" string if the + /// value is 0. + /// Ignored. + /// The string representation of the passed-in value. + public object Convert(object value, Type targetType, object parameter, string language) => + value switch + { + double d => d switch + { + double.NaN => "0", + 0 => (string)parameter == "Auto" ? AutoText : "0", + _ => d.ToString(CultureInfo.CurrentCulture), + }, + + _ => "0", + }; + + /// + /// Converts the string representation back to a double, returning 0 if the string is empty, + /// null or not a valid number in the specified culture. + /// + /// The string value to convert. + /// The conversion target type. here. + /// Converter parameter. Unused. + /// Ignored. + /// The corresponding double value. + public object ConvertBack(object value, Type targetType, object parameter, string language) => + value switch + { + null or "" => 0.0, + string text when double.TryParse(text, NumberStyles.Any, CultureInfo.CurrentCulture, out double result) => result, + _ => 0.0, + }; +} diff --git a/src/settings-ui/Settings.UI/Converters/ImageResizerNumberBoxValueConverter.cs b/src/settings-ui/Settings.UI/Converters/ImageResizerNumberBoxValueConverter.cs new file mode 100644 index 000000000000..777657186df2 --- /dev/null +++ b/src/settings-ui/Settings.UI/Converters/ImageResizerNumberBoxValueConverter.cs @@ -0,0 +1,31 @@ +// 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 Microsoft.UI.Xaml.Data; + +namespace Microsoft.PowerToys.Settings.UI.Converters; + +public partial class ImageResizerNumberBoxValueConverter : IValueConverter +{ + /// + /// Converts the underlying double value to a display-friendly format. Ensures that NaN values + /// are not propagated to the UI. + /// + public object Convert(object value, Type targetType, object parameter, string language) => + value is double d && double.IsNaN(d) ? 0.0 : value; + + /// + /// Converts the user input back to the underlying double value. If the input is not a valid + /// number, a double with value 0 is returned. + /// + public object ConvertBack(object value, Type targetType, object parameter, string language) => + value switch + { + null => 0.0, + double d when double.IsNaN(d) => 0.0, + string str when !double.TryParse(str, out _) => 0.0, + _ => value, + }; +} diff --git a/src/settings-ui/Settings.UI/Converters/ImageResizerZeroToEmptyStringNumberFormatter.cs b/src/settings-ui/Settings.UI/Converters/ImageResizerZeroToEmptyStringNumberFormatter.cs new file mode 100644 index 000000000000..241210f6c056 --- /dev/null +++ b/src/settings-ui/Settings.UI/Converters/ImageResizerZeroToEmptyStringNumberFormatter.cs @@ -0,0 +1,39 @@ +// 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.Globalization; +using Microsoft.UI.Xaml.Controls; + +namespace Microsoft.PowerToys.Settings.UI.Converters; + +public partial class ImageResizerZeroToEmptyStringNumberFormatter +{ + public string Format(long value) => throw new NotImplementedException(); + + public string Format(ulong value) => throw new NotImplementedException(); + + public string Format(double value) => throw new NotImplementedException(); + + public string FormatDouble(double? value) => value switch + { + null => string.Empty, + 0 => string.Empty, + _ => value.Value.ToString(CultureInfo.CurrentCulture), + }; + + public double? ParseDouble(string text) + { + if (string.IsNullOrWhiteSpace(text)) + { + return 0.0; + } + + return double.TryParse(text, NumberStyles.Any, CultureInfo.CurrentCulture, out double result) ? result : 0.0; + } + + public long? ParseInt(string text) => throw new NotImplementedException(); + + public ulong? ParseUInt(string text) => throw new NotImplementedException(); +} diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/ImageResizerDimensionsNumberBox.cs b/src/settings-ui/Settings.UI/SettingsXAML/Controls/ImageResizerDimensionsNumberBox.cs new file mode 100644 index 000000000000..a310f2d675c1 --- /dev/null +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/ImageResizerDimensionsNumberBox.cs @@ -0,0 +1,38 @@ +// 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 Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Microsoft.PowerToys.Settings.UI.Controls; + +public partial class ImageResizerDimensionsNumberBox : NumberBox +{ + public ImageResizerDimensionsNumberBox() + { + this.Loaded += (_, _) => UpdateDisplayText(); + + this.ValueChanged += (_, _) => UpdateDisplayText(); + + this.GotFocus += (s, e) => + { + // Show "0" in the UI when focused on the empty value. This ensures that the spinbutton + // controls are usable. + if (Value is double.NaN) + { + Value = 0.0; + } + }; + + this.LostFocus += (_, _) => UpdateDisplayText(); + } + + private void UpdateDisplayText() + { + if (FocusState == FocusState.Unfocused && Value == 0) + { + Text = string.Empty; + } + } +} diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/ImageResizerPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/ImageResizerPage.xaml index 7e7d956ce91b..2db21e91adfb 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/ImageResizerPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/ImageResizerPage.xaml @@ -19,6 +19,9 @@ + + + + Text="{x:Bind Width, Mode=OneWay, Converter={StaticResource ImageResizerDoubleToAutoConverter}, ConverterParameter=Auto}" /> - + Value="{x:Bind Width, Mode=TwoWay, Converter={StaticResource ImageResizerNumberBoxValueConverter}}" /> - + Value="{x:Bind Height, Mode=TwoWay, Converter={StaticResource ImageResizerNumberBoxValueConverter}}" /> TIFF compression {Locked="TIFF"} + + (auto) + Displayed on the preset card when the Width or Height property is zero. The same as "Input_Auto" in the ImageResizerUI project's resources. + File as in a computer file