diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 0f2d1b0f7b..4431adcaba 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -295,6 +295,7 @@ codeofconduct codereview COINIT Colorbrush +colorconv colorpicker colorpickerref COLORREF @@ -903,6 +904,8 @@ hresult hrgn HRSRC HSCROLL +hsb +hsi hsl hstring hsv @@ -913,6 +916,7 @@ html htt http hu +hwb HWINEVENTHOOK hwnd HWNDFIRST @@ -1087,6 +1091,7 @@ IPrincipal IProgram IPublic IQuery +IRead IReflect IRegistered IRegistration @@ -1439,6 +1444,7 @@ NCMBUTTONDOWN NCMBUTTONUP NCMOUSELEAVE NCMOUSEMOVE +NCol NCPAINT NCRBUTTONDBLCLK NCRBUTTONDOWN diff --git a/.github/actions/spell-check/patterns.txt b/.github/actions/spell-check/patterns.txt index 60513959e0..e5192cd0c3 100644 --- a/.github/actions/spell-check/patterns.txt +++ b/.github/actions/spell-check/patterns.txt @@ -10,6 +10,11 @@ data:[a-zA-Z=;,/0-9+-]+ "Lorem[^"]+?\." TestCase\("[^"]+" +# Test line with hexadecimal colors +\[DataRow\("[0-9A-F]{6}", \d{3}, \d{3}, \d{3}\)\] +\[DataRow\("[0-9A-F]{6}", \d{3}.\d{1}, \d{3}.\d{1}, \d{3}.\d{1}\)\] +\[DataRow\("[0-9A-F]{6}", "[BCGMRY]\d\d?", \d{3}, \d{3}\)\] + # Windows paths \\native \\notifications diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Library/ColorPickerProperties.cs b/src/core/Microsoft.PowerToys.Settings.UI.Library/ColorPickerProperties.cs index 7f65789992..f5305f5960 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Library/ColorPickerProperties.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Library/ColorPickerProperties.cs @@ -4,37 +4,10 @@ using System.Text.Json; using System.Text.Json.Serialization; +using Microsoft.PowerToys.Settings.UI.Library.Enumerations; namespace Microsoft.PowerToys.Settings.UI.Library { - public enum ColorRepresentationType - { - /// - /// Color presentation as hexadecimal color value without the alpha-value (e.g. #0055FF) - /// - HEX = 0, - - /// - /// Color presentation as RGB color value (red[0..255], green[0..255], blue[0..255]) - /// - RGB = 1, - - /// - /// Color presentation as CMYK color value (cyan[0%..100%], magenta[0%..100%], yellow[0%..100%], black key[0%..100%]) - /// - CMYK = 2, - - /// - /// Color presentation as HSL color value (hue[0°..360°], saturation[0..100%], lightness[0%..100%]) - /// - HSL = 3, - - /// - /// Color presentation as HSV color value (hue[0°..360°], saturation[0%..100%], value[0%..100%]) - /// - HSV = 4, - } - public class ColorPickerProperties { public ColorPickerProperties() diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Library/ColorPickerSettings.cs b/src/core/Microsoft.PowerToys.Settings.UI.Library/ColorPickerSettings.cs index 72ebb295c9..a27a1076a3 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Library/ColorPickerSettings.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Library/ColorPickerSettings.cs @@ -40,14 +40,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library } public string GetModuleName() - { - return Name; - } + => Name; // This can be utilized in the future if the settings.json file is to be modified/deleted. public bool UpgradeSettingsConfiguration() - { - return false; - } + => false; } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Library/Enumerations/ColorRepresentationType.cs b/src/core/Microsoft.PowerToys.Settings.UI.Library/Enumerations/ColorRepresentationType.cs new file mode 100644 index 0000000000..9e456dfef6 --- /dev/null +++ b/src/core/Microsoft.PowerToys.Settings.UI.Library/Enumerations/ColorRepresentationType.cs @@ -0,0 +1,59 @@ +// 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. + +namespace Microsoft.PowerToys.Settings.UI.Library.Enumerations +{ + // NOTE: don't change the order (numbers) of the enumeration entires + + /// + /// The type of the color representation + /// + public enum ColorRepresentationType + { + /// + /// Color presentation as hexadecimal color value without the alpha-value (e.g. #0055FF) + /// + HEX = 0, + + /// + /// Color presentation as RGB color value (red[0..255], green[0..255], blue[0..255]) + /// + RGB = 1, + + /// + /// Color presentation as CMYK color value (cyan[0%..100%], magenta[0%..100%], yellow[0%..100%], black key[0%..100%]) + /// + CMYK = 2, + + /// + /// Color presentation as HSL color value (hue[0°..360°], saturation[0..100%], lightness[0%..100%]) + /// + HSL = 3, + + /// + /// Color presentation as HSV color value (hue[0°..360°], saturation[0%..100%], value[0%..100%]) + /// + HSV = 4, + + /// + /// Color presentation as HSB color value (hue[0°..360°], saturation[0%..100%], brightness[0%..100%]) + /// + HSB = 5, + + /// + /// Color presentation as HSI color value (hue[0°..360°], saturation[0%..100%], intensity[0%..100%]) + /// + HSI = 6, + + /// + /// Color presentation as HWB color value (hue[0°..360°], whiteness[0%..100%], blackness[0%..100%]) + /// + HWB = 7, + + /// + /// Color presentation as natural color (hue, whiteness[0%..100%], blackness[0%..100%]) + /// + NCol = 8, + } +} diff --git a/src/core/Microsoft.PowerToys.Settings.UI.Library/ViewModels/ColorPickerViewModel.cs b/src/core/Microsoft.PowerToys.Settings.UI.Library/ViewModels/ColorPickerViewModel.cs index ff4f37f612..9d6dce41d6 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI.Library/ViewModels/ColorPickerViewModel.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI.Library/ViewModels/ColorPickerViewModel.cs @@ -3,8 +3,10 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.Json; +using Microsoft.PowerToys.Settings.UI.Library.Enumerations; using Microsoft.PowerToys.Settings.UI.Library.Helpers; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; @@ -16,7 +18,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels private readonly ISettingsUtils _settingsUtils; - private ColorPickerSettings _colorPickerSettings; + private readonly ColorPickerSettings _colorPickerSettings; private bool _isEnabled; @@ -30,6 +32,19 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels throw new ArgumentNullException(nameof(settingsRepository)); } + SelectableColorRepresentations = new Dictionary + { + { ColorRepresentationType.CMYK, "CMYK - cmyk(100%, 50%, 75%, 0%)" }, + { ColorRepresentationType.HEX, "HEX - #FFAA00" }, + { ColorRepresentationType.HSB, "HSB - hsb(100, 50%, 75%)" }, + { ColorRepresentationType.HSI, "HSI - hsi(100, 50%, 75%)" }, + { ColorRepresentationType.HSL, "HSL - hsl(100, 50%, 75%)" }, + { ColorRepresentationType.HSV, "HSV - hsv(100, 50%, 75%)" }, + { ColorRepresentationType.HWB, "HWB - hwb(100, 50%, 75%)" }, + { ColorRepresentationType.NCol, "NCol - R10, 50%, 75%" }, + { ColorRepresentationType.RGB, "RGB - rgb(100, 50, 75)" }, + }; + GeneralSettingsConfig = settingsRepository.SettingsConfig; _settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils)); @@ -48,13 +63,14 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels SendConfigMSG = ipcMSGCallBackFunc; } + /// + /// Gets a list with all selectable s + /// + public IReadOnlyDictionary SelectableColorRepresentations { get; } + public bool IsEnabled { - get - { - return _isEnabled; - } - + get => _isEnabled; set { if (_isEnabled != value) @@ -64,7 +80,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels // Set the status of ColorPicker in the general settings GeneralSettingsConfig.Enabled.ColorPicker = value; - OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig); + var outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig); SendConfigMSG(outgoing.ToString()); } @@ -73,11 +89,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels public bool ChangeCursor { - get - { - return _colorPickerSettings.Properties.ChangeCursor; - } - + get => _colorPickerSettings.Properties.ChangeCursor; set { if (_colorPickerSettings.Properties.ChangeCursor != value) @@ -91,11 +103,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels public HotkeySettings ActivationShortcut { - get - { - return _colorPickerSettings.Properties.ActivationShortcut; - } - + get => _colorPickerSettings.Properties.ActivationShortcut; set { if (_colorPickerSettings.Properties.ActivationShortcut != value) @@ -107,19 +115,15 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels } } - public int CopiedColorRepresentationIndex + public ColorRepresentationType SelectedColorRepresentationValue { - get - { - return (int)_colorPickerSettings.Properties.CopiedColorRepresentation; - } - + get => _colorPickerSettings.Properties.CopiedColorRepresentation; set { - if (_colorPickerSettings.Properties.CopiedColorRepresentation != (ColorRepresentationType)value) + if (_colorPickerSettings.Properties.CopiedColorRepresentation != value) { - _colorPickerSettings.Properties.CopiedColorRepresentation = (ColorRepresentationType)value; - OnPropertyChanged(nameof(CopiedColorRepresentationIndex)); + _colorPickerSettings.Properties.CopiedColorRepresentation = value; + OnPropertyChanged(nameof(SelectedColorRepresentationValue)); NotifySettingsChanged(); } } diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml b/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml index 7fb9e40c5d..1c9b6333b7 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml @@ -13,6 +13,95 @@ AutomationProperties.LandmarkType="Main"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -26,109 +115,16 @@ - - + + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml.cs b/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml.cs index 6d994eedc2..c7a17a3335 100644 --- a/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml.cs +++ b/src/core/Microsoft.PowerToys.Settings.UI/Views/ColorPickerPage.xaml.cs @@ -21,5 +21,33 @@ namespace Microsoft.PowerToys.Settings.UI.Views DataContext = ViewModel; InitializeComponent(); } + + /// + /// Event is called when the is completely loaded, inclusive the ItemSource + /// + /// The sender of this event + /// The arguments of this event + private void ColorPicker_ComboBox_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e) + { + /** + * UWP hack + * because UWP load the bound ItemSource of the ComboBox asynchronous, + * so after InitializeComponent() the ItemSource is still empty and can't automatically select a entry. + * Selection via SelectedItem and SelectedValue is still not working too + */ + var index = 0; + + foreach (var item in ViewModel.SelectableColorRepresentations) + { + if (item.Key == ViewModel.SelectedColorRepresentationValue) + { + break; + } + + index++; + } + + ColorPicker_ComboBox.SelectedIndex = index; + } } } diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/ColorHelper.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/ColorHelper.cs index 44f547fc5b..edb2f6d1e0 100644 --- a/src/modules/colorPicker/ColorPickerUI/Helpers/ColorHelper.cs +++ b/src/modules/colorPicker/ColorPickerUI/Helpers/ColorHelper.cs @@ -13,44 +13,7 @@ namespace ColorPicker.Helpers internal static class ColorHelper { /// - /// Convert a given color to a HSL color (hue, saturation, lightness) - /// - /// The to convert - /// The hue [0°..360°], saturation [0..1] and lightness [0..1] values of the converted color - internal static (double hue, double saturation, double lightness) ConvertToHSLColor(Color color) - { - var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d; - var max = Math.Max(Math.Max(color.R, color.G), color.B) / 255d; - - var lightness = (max + min) / 2d; - - if (lightness == 0d || min == max) - { - return (color.GetHue(), 0d, lightness); - } - else if (lightness > 0d && lightness <= 0.5d) - { - return (color.GetHue(), (max - min) / (max + min), lightness); - } - - return (color.GetHue(), (max - min) / (2d - (max + min)), lightness); - } - - /// - /// Convert a given color to a HSV color (hue, saturation, value) - /// - /// The to convert - /// The hue [0°..360°], saturation [0..1] and value [0..1] of the converted color - internal static (double hue, double saturation, double value) ConvertToHSVColor(Color color) - { - var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d; - var max = Math.Max(Math.Max(color.R, color.G), color.B) / 255d; - - return (color.GetHue(), max == 0d ? 0d : (max - min) / max, max); - } - - /// - /// Convert a given color to a CYMK color (cyan, magenta, yellow, black key) + /// Convert a given to a CYMK color (cyan, magenta, yellow, black key) /// /// The to convert /// The cyan[0..1], magenta[0..1], yellow[0..1] and black key[0..1] of the converted color @@ -80,5 +43,135 @@ namespace ColorPicker.Helpers return (cyan, magenta, yellow, blackKey); } + + /// + /// Convert a given to a HSB color (hue, saturation, brightness) + /// + /// The to convert + /// The hue [0°..360°], saturation [0..1] and brightness [0..1] of the converted color + internal static (double hue, double saturation, double brightness) ConvertToHSBColor(Color color) + => (color.GetHue(), color.GetSaturation(), color.GetBrightness()); + + /// + /// Convert a given to a HSI color (hue, saturation, intensity) + /// + /// The to convert + /// The hue [0°..360°], saturation [0..1] and intensity [0..1] of the converted color + internal static (double hue, double saturation, double intensity) ConvertToHSIColor(Color color) + { + // special case for black + if (color.R == 0 && color.G == 0 && color.B == 0) + { + return (0d, 0d, 0d); + } + + var red = color.R / 255d; + var green = color.G / 255d; + var blue = color.B / 255d; + + var intensity = (red + green + blue) / 3d; + + var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d; + + return (color.GetHue(), 1d - (min / intensity), intensity); + } + + /// + /// Convert a given to a HSL color (hue, saturation, lightness) + /// + /// The to convert + /// The hue [0°..360°], saturation [0..1] and lightness [0..1] values of the converted color + internal static (double hue, double saturation, double lightness) ConvertToHSLColor(Color color) + { + var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d; + var max = Math.Max(Math.Max(color.R, color.G), color.B) / 255d; + + var lightness = (max + min) / 2d; + + if (lightness == 0d || min == max) + { + return (color.GetHue(), 0d, lightness); + } + else if (lightness > 0d && lightness <= 0.5d) + { + return (color.GetHue(), (max - min) / (max + min), lightness); + } + + return (color.GetHue(), (max - min) / (2d - (max + min)), lightness); + } + + /// + /// Convert a given to a HSV color (hue, saturation, value) + /// + /// The to convert + /// The hue [0°..360°], saturation [0..1] and value [0..1] of the converted color + internal static (double hue, double saturation, double value) ConvertToHSVColor(Color color) + { + var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d; + var max = Math.Max(Math.Max(color.R, color.G), color.B) / 255d; + + return (color.GetHue(), max == 0d ? 0d : (max - min) / max, max); + } + + /// + /// Convert a given to a HWB color (hue, whiteness, blackness) + /// + /// The to convert + /// The hue [0°..360°], whiteness [0..1] and blackness [0..1] of the converted color + internal static (double hue, double whiteness, double blackness) ConvertToHWBColor(Color color) + { + var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d; + var max = Math.Max(Math.Max(color.R, color.G), color.B) / 255d; + + return (color.GetHue(), min, 1 - max); + } + + /// + /// Convert a given to a natural color (hue, whiteness, blackness) + /// + /// The to convert + /// The hue, whiteness [0..1] and blackness [0..1] of the converted color + internal static (string hue, double whiteness, double blackness) ConvertToNaturalColor(Color color) + { + var min = Math.Min(Math.Min(color.R, color.G), color.B) / 255d; + var max = Math.Max(Math.Max(color.R, color.G), color.B) / 255d; + + return (GetNaturalColorFromHue(color.GetHue()), min, 1 - max); + } + + /// + /// Return the natural color for the given hue value + /// + /// The hue value to convert + /// A natural color + private static string GetNaturalColorFromHue(double hue) + { + if (hue < 60d) + { + return $"R{Math.Round(hue / 0.6d, 0)}"; + } + + if (hue < 120d) + { + return $"Y{Math.Round((hue - 60d) / 0.6d, 0)}"; + } + + if (hue < 180d) + { + return $"G{Math.Round((hue - 120d) / 0.6d, 0)}"; + } + + if (hue < 240d) + { + return $"C{Math.Round((hue - 180d) / 0.6d, 0)}"; + } + + if (hue < 300d) + { + return $"B{Math.Round((hue - 240d) / 0.6d, 0)}"; + } + + return $"M{Math.Round((hue - 300d) / 0.6d, 0)}"; + } } } diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/ColorRepresentationHelper.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/ColorRepresentationHelper.cs index 1b2294d915..b21304a9ab 100644 --- a/src/modules/colorPicker/ColorPickerUI/Helpers/ColorRepresentationHelper.cs +++ b/src/modules/colorPicker/ColorPickerUI/Helpers/ColorRepresentationHelper.cs @@ -5,7 +5,7 @@ using System; using System.Drawing; using System.Globalization; -using Microsoft.PowerToys.Settings.UI.Library; +using Microsoft.PowerToys.Settings.UI.Library.Enumerations; namespace ColorPicker.Helpers { @@ -25,14 +25,38 @@ namespace ColorPicker.Helpers { ColorRepresentationType.CMYK => ColorToCYMK(color), ColorRepresentationType.HEX => ColorToHex(color), + ColorRepresentationType.HSB => ColorToHSB(color), + ColorRepresentationType.HSI => ColorToHSI(color), ColorRepresentationType.HSL => ColorToHSL(color), ColorRepresentationType.HSV => ColorToHSV(color), + ColorRepresentationType.HWB => ColorToHWB(color), + ColorRepresentationType.NCol => ColorToNCol(color), ColorRepresentationType.RGB => ColorToRGB(color), // Fall-back value, when "_userSettings.CopiedColorRepresentation.Value" is incorrect _ => ColorToHex(color), }; + /// + /// Return a representation of a CYMK color + /// + /// The for the CYMK color presentation + /// A representation of a CYMK color + private static string ColorToCYMK(Color color) + { + var (cyan, magenta, yellow, blackKey) = ColorHelper.ConvertToCMYKColor(color); + + cyan = Math.Round(cyan * 100); + magenta = Math.Round(magenta * 100); + yellow = Math.Round(yellow * 100); + blackKey = Math.Round(blackKey * 100); + + return $"cmyk({cyan.ToString(CultureInfo.InvariantCulture)}%" + + $", {magenta.ToString(CultureInfo.InvariantCulture)}%" + + $", {yellow.ToString(CultureInfo.InvariantCulture)}%" + + $", {blackKey.ToString(CultureInfo.InvariantCulture)}%)"; + } + /// /// Return a hexadecimal representation of a RGB color /// @@ -44,19 +68,45 @@ namespace ColorPicker.Helpers + $"{color.B.ToString("X2", CultureInfo.InvariantCulture)}"; /// - /// Return a representation of a RGB color + /// Return a representation of a HSB color /// - /// The see cref="Color"/> for the RGB color presentation - /// A representation of a RGB color - private static string ColorToRGB(Color color) - => $"rgb({color.R.ToString(CultureInfo.InvariantCulture)}" - + $", {color.G.ToString(CultureInfo.InvariantCulture)}" - + $", {color.B.ToString(CultureInfo.InvariantCulture)})"; + /// The for the HSB color presentation + /// A representation of a HSB color + private static string ColorToHSB(Color color) + { + var (hue, saturation, brightness) = ColorHelper.ConvertToHSBColor(color); + + hue = Math.Round(hue); + saturation = Math.Round(saturation * 100); + brightness = Math.Round(brightness * 100); + + return $"hsb({hue.ToString(CultureInfo.InvariantCulture)}" + + $", {saturation.ToString(CultureInfo.InvariantCulture)}%" + + $", {brightness.ToString(CultureInfo.InvariantCulture)}%)"; + } + + /// + /// Return a representation of a HSI color + /// + /// The for the HSI color presentation + /// A representation of a HSI color + private static string ColorToHSI(Color color) + { + var (hue, saturation, intensity) = ColorHelper.ConvertToHSIColor(color); + + hue = Math.Round(hue); + saturation = Math.Round(saturation * 100); + intensity = Math.Round(intensity * 100); + + return $"hsi({hue.ToString(CultureInfo.InvariantCulture)}" + + $", {saturation.ToString(CultureInfo.InvariantCulture)}%" + + $", {intensity.ToString(CultureInfo.InvariantCulture)}%)"; + } /// /// Return a representation of a HSL color /// - /// The see cref="Color"/> for the HSL color presentation + /// The for the HSL color presentation /// A representation of a HSL color private static string ColorToHSL(Color color) { @@ -75,7 +125,7 @@ namespace ColorPicker.Helpers /// /// Return a representation of a HSV color /// - /// The see cref="Color"/> for the HSV color presentation + /// The for the HSV color presentation /// A representation of a HSV color private static string ColorToHSV(Color color) { @@ -92,24 +142,48 @@ namespace ColorPicker.Helpers } /// - /// Return a representation of a HSV color + /// Return a representation of a HWB color /// - /// The see cref="Color"/> for the HSV color presentation - /// A representation of a HSV color - private static string ColorToCYMK(Color color) + /// The for the HWB color presentation + /// A representation of a HWB color + private static string ColorToHWB(Color color) { - var (cyan, magenta, yellow, blackKey) = ColorHelper.ConvertToCMYKColor(color); + var (hue, whiteness, blackness) = ColorHelper.ConvertToHWBColor(color); - cyan = Math.Round(cyan * 100); - magenta = Math.Round(magenta * 100); - yellow = Math.Round(yellow * 100); - blackKey = Math.Round(blackKey * 100); + hue = Math.Round(hue); + whiteness = Math.Round(whiteness * 100); + blackness = Math.Round(blackness * 100); - // Using InvariantCulture since this is used for color representation - return $"cmyk({cyan.ToString(CultureInfo.InvariantCulture)}%" - + $", {magenta.ToString(CultureInfo.InvariantCulture)}%" - + $", {yellow.ToString(CultureInfo.InvariantCulture)}%" - + $", {blackKey.ToString(CultureInfo.InvariantCulture)}%)"; + return $"hwb({hue.ToString(CultureInfo.InvariantCulture)}" + + $", {whiteness.ToString(CultureInfo.InvariantCulture)}%" + + $", {blackness.ToString(CultureInfo.InvariantCulture)}%)"; } + + /// + /// Return a representation of a natural color + /// + /// The for the natural color presentation + /// A representation of a natural color + private static string ColorToNCol(Color color) + { + var (hue, whiteness, blackness) = ColorHelper.ConvertToNaturalColor(color); + + whiteness = Math.Round(whiteness * 100); + blackness = Math.Round(blackness * 100); + + return $"{hue}" + + $", {whiteness.ToString(CultureInfo.InvariantCulture)}%" + + $", {blackness.ToString(CultureInfo.InvariantCulture)}%"; + } + + /// + /// Return a representation of a RGB color + /// + /// The see cref="Color"/> for the RGB color presentation + /// A representation of a RGB color + private static string ColorToRGB(Color color) + => $"rgb({color.R.ToString(CultureInfo.InvariantCulture)}" + + $", {color.G.ToString(CultureInfo.InvariantCulture)}" + + $", {color.B.ToString(CultureInfo.InvariantCulture)})"; } } diff --git a/src/modules/colorPicker/ColorPickerUI/Settings/IUserSettings.cs b/src/modules/colorPicker/ColorPickerUI/Settings/IUserSettings.cs index b05be4d12e..585b441279 100644 --- a/src/modules/colorPicker/ColorPickerUI/Settings/IUserSettings.cs +++ b/src/modules/colorPicker/ColorPickerUI/Settings/IUserSettings.cs @@ -2,7 +2,7 @@ // 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.PowerToys.Settings.UI.Library; +using Microsoft.PowerToys.Settings.UI.Library.Enumerations; namespace ColorPicker.Settings { diff --git a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs index 3ccea1f1e4..852278f90b 100644 --- a/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs +++ b/src/modules/colorPicker/ColorPickerUI/Settings/UserSettings.cs @@ -8,6 +8,7 @@ using System.IO; using System.IO.Abstractions; using System.Threading; using Microsoft.PowerToys.Settings.UI.Library; +using Microsoft.PowerToys.Settings.UI.Library.Enumerations; using Microsoft.PowerToys.Settings.UI.Library.Utilities; namespace ColorPicker.Settings diff --git a/src/modules/colorPicker/ColorPickerUI/ViewModels/MainViewModel.cs b/src/modules/colorPicker/ColorPickerUI/ViewModels/MainViewModel.cs index a600471f61..d747eae0a2 100644 --- a/src/modules/colorPicker/ColorPickerUI/ViewModels/MainViewModel.cs +++ b/src/modules/colorPicker/ColorPickerUI/ViewModels/MainViewModel.cs @@ -15,7 +15,6 @@ using ColorPicker.Mouse; using ColorPicker.Settings; using ColorPicker.Telemetry; using ColorPicker.ViewModelContracts; -using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Telemetry; namespace ColorPicker.ViewModels diff --git a/src/modules/colorPicker/UnitTest-ColorPickerUI/Helpers/ColorHelperTest.cs b/src/modules/colorPicker/UnitTest-ColorPickerUI/Helpers/ColorHelperTest.cs index aae56ac7c2..804ecddd7a 100644 --- a/src/modules/colorPicker/UnitTest-ColorPickerUI/Helpers/ColorHelperTest.cs +++ b/src/modules/colorPicker/UnitTest-ColorPickerUI/Helpers/ColorHelperTest.cs @@ -1,5 +1,6 @@ using System; using System.Drawing; +using System.Globalization; using ColorPicker.Helpers; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -41,7 +42,7 @@ namespace UnitTest_ColorPickerUI.Helpers [DataRow(315, 100, 050, 100, 000, 075)] // Red-magenta [DataRow(330, 100, 050, 100, 000, 050)] // Blue-red [DataRow(345, 100, 050, 100, 000, 025)] // Light blue-red - public void ColorRGBtoHSL(double hue, double saturation, double lightness, int red, int green, int blue) + public void ColorRGBtoHSLTest(double hue, double saturation, double lightness, int red, int green, int blue) { red = Convert.ToInt32(Math.Round(255d / 100d * red)); // [0%..100%] to [0..255] green = Convert.ToInt32(Math.Round(255d / 100d * green)); // [0%..100%] to [0..255] @@ -90,7 +91,7 @@ namespace UnitTest_ColorPickerUI.Helpers [DataRow(315, 100, 100, 100, 000, 075)] // Red-magenta [DataRow(330, 100, 100, 100, 000, 050)] // Blue-red [DataRow(345, 100, 100, 100, 000, 025)] // Light blue-red - public void ColorRGBtoHSV(double hue, double saturation, double value, int red, int green, int blue) + public void ColorRGBtoHSVTest(double hue, double saturation, double value, int red, int green, int blue) { red = Convert.ToInt32(Math.Round(255d / 100d * red)); // [0%..100%] to [0..255] green = Convert.ToInt32(Math.Round(255d / 100d * green)); // [0%..100%] to [0..255] @@ -138,7 +139,7 @@ namespace UnitTest_ColorPickerUI.Helpers [DataRow(000, 100, 025, 000, 255, 000, 192)] // Red-magenta [DataRow(000, 100, 050, 000, 255, 000, 128)] // Blue-red [DataRow(000, 100, 075, 000, 255, 000, 064)] // Light blue-red - public void ColorRGBtoCMYK(int cyan, int magenta, int yellow, int blackKey, int red, int green, int blue) + public void ColorRGBtoCMYKTest(int cyan, int magenta, int yellow, int blackKey, int red, int green, int blue) { var color = Color.FromArgb(255, red, green, blue); var result = ColorHelper.ConvertToCMYKColor(color); @@ -156,8 +157,130 @@ namespace UnitTest_ColorPickerUI.Helpers Assert.AreEqual(result.blackKey * 100d, blackKey, 0.5d); } + // values taken from https://en.wikipedia.org/wiki/HSL_and_HSV#Examples [TestMethod] - public void ColorRGBtoCMYKZeroDiv() + [DataRow("FFFFFF", 000.0, 000.0, 100.0)] // white + [DataRow("808080", 000.0, 000.0, 050.0)] // gray + [DataRow("000000", 000.0, 000.0, 000.0)] // black + [DataRow("FF0000", 000.0, 100.0, 033.3)] // red + [DataRow("BFBF00", 060.0, 100.0, 050.0)] // yellow + [DataRow("008000", 120.0, 100.0, 016.7)] // green + [DataRow("80FFFF", 180.0, 040.0, 083.3)] // cyan + [DataRow("8080FF", 240.0, 025.0, 066.7)] // blue + [DataRow("BF40BF", 300.0, 057.1, 058.3)] // magenta + [DataRow("A0A424", 061.8, 069.9, 047.1)] + [DataRow("411BEA", 251.1, 075.6, 042.6)] + [DataRow("1EAC41", 134.9, 066.7, 034.9)] + [DataRow("F0C80E", 049.5, 091.1, 059.3)] + [DataRow("B430E5", 283.7, 068.6, 059.6)] + [DataRow("ED7651", 014.3, 044.6, 057.0)] + [DataRow("FEF888", 056.9, 036.3, 083.5)] + [DataRow("19CB97", 162.4, 080.0, 049.5)] + [DataRow("362698", 248.3, 053.3, 031.9)] + [DataRow("7E7EB8", 240.5, 013.5, 057.0)] + public void ColorRGBtoHSITest(string hexValue, double hue, double saturation, double intensity) + { + var red = int.Parse(hexValue.Substring(0, 2), NumberStyles.HexNumber); + var green = int.Parse(hexValue.Substring(2, 2), NumberStyles.HexNumber); + var blue = int.Parse(hexValue.Substring(4, 2), NumberStyles.HexNumber); + + var color = Color.FromArgb(255, red, green, blue); + var result = ColorHelper.ConvertToHSIColor(color); + + // hue[0°..360°] + Assert.AreEqual(result.hue, hue, 0.5d); + + // saturation[0..1] + Assert.AreEqual(result.saturation * 100d, saturation, 0.5d); + + // intensity[0..1] + Assert.AreEqual(result.intensity * 100d, intensity, 0.5d); + } + + // values taken from https://en.wikipedia.org/wiki/HSL_and_HSV#Examples + // and manual convert via https://colorconv.com/hwb + [TestMethod] + [DataRow("FFFFFF", 000, 100, 000)] // white + [DataRow("808080", 000, 050, 050)] // gray + [DataRow("000000", 000, 000, 100)] // black + [DataRow("FF0000", 000, 000, 000)] // red + [DataRow("BFBF00", 060, 000, 025)] // yellow + [DataRow("008000", 120, 000, 050)] // green + [DataRow("80FFFF", 180, 050, 000)] // cyan + [DataRow("8080FF", 240, 050, 000)] // blue + [DataRow("BF40BF", 300, 025, 025)] // magenta + [DataRow("A0A424", 062, 014, 036)] + [DataRow("411BEA", 251, 011, 008)] + [DataRow("1EAC41", 135, 012, 033)] + [DataRow("F0C80E", 049, 005, 006)] + [DataRow("B430E5", 284, 019, 010)] + [DataRow("ED7651", 014, 032, 007)] + [DataRow("FEF888", 057, 053, 000)] + [DataRow("19CB97", 162, 010, 020)] + [DataRow("362698", 248, 015, 040)] + [DataRow("7E7EB8", 240, 049, 028)] + public void ColorRGBtoHWBTest(string hexValue, double hue, double whiteness, double blackness) + { + var red = int.Parse(hexValue.Substring(0, 2), NumberStyles.HexNumber); + var green = int.Parse(hexValue.Substring(2, 2), NumberStyles.HexNumber); + var blue = int.Parse(hexValue.Substring(4, 2), NumberStyles.HexNumber); + + var color = Color.FromArgb(255, red, green, blue); + var result = ColorHelper.ConvertToHWBColor(color); + + // hue[0°..360°] + Assert.AreEqual(result.hue, hue, 0.5d); + + // whiteness[0..1] + Assert.AreEqual(result.whiteness * 100d, whiteness, 0.5d); + + // blackness[0..1] + Assert.AreEqual(result.blackness * 100d, blackness, 0.5d); + } + + // values taken from https://en.wikipedia.org/wiki/HSL_and_HSV#Examples + // and manual convert via https://colorconv.com/hwb + [TestMethod] + [DataRow("FFFFFF", "R0", 100, 000)] // white + [DataRow("808080", "R0", 050, 050)] // gray + [DataRow("000000", "R0", 000, 100)] // black + [DataRow("FF0000", "R0", 000, 000)] // red + [DataRow("BFBF00", "Y0", 000, 025)] // yellow + [DataRow("008000", "G0", 000, 050)] // green + [DataRow("80FFFF", "C0", 050, 000)] // cyan + [DataRow("8080FF", "B0", 050, 000)] // blue + [DataRow("BF40BF", "M0", 025, 025)] // magenta + [DataRow("A0A424", "Y3", 014, 036)] + [DataRow("411BEA", "B18", 011, 008)] + [DataRow("1EAC41", "G25", 012, 033)] + [DataRow("F0C80E", "R82", 005, 006)] + [DataRow("B430E5", "B73", 019, 010)] + [DataRow("ED7651", "R24", 032, 007)] + [DataRow("FEF888", "R95", 053, 000)] + [DataRow("19CB97", "G71", 010, 020)] + [DataRow("362698", "B14", 015, 040)] + [DataRow("7E7EB8", "B0", 049, 028)] + public void ColorRGBtoNColTest(string hexValue, string hue, double whiteness, double blackness) + { + var red = int.Parse(hexValue.Substring(0, 2), NumberStyles.HexNumber); + var green = int.Parse(hexValue.Substring(2, 2), NumberStyles.HexNumber); + var blue = int.Parse(hexValue.Substring(4, 2), NumberStyles.HexNumber); + + var color = Color.FromArgb(255, red, green, blue); + var result = ColorHelper.ConvertToNaturalColor(color); + + // hue + Assert.AreEqual(result.hue, hue); + + // whiteness[0..1] + Assert.AreEqual(result.whiteness * 100d, whiteness, 0.5d); + + // blackness[0..1] + Assert.AreEqual(result.blackness * 100d, blackness, 0.5d); + } + + [TestMethod] + public void ColorRGBtoCMYKZeroDivTest() { for(var red = 0; red < 256; red++) { diff --git a/src/modules/colorPicker/UnitTest-ColorPickerUI/Helpers/ColorRepresentationHelperTest.cs b/src/modules/colorPicker/UnitTest-ColorPickerUI/Helpers/ColorRepresentationHelperTest.cs index fe6fddf015..d9ce148756 100644 --- a/src/modules/colorPicker/UnitTest-ColorPickerUI/Helpers/ColorRepresentationHelperTest.cs +++ b/src/modules/colorPicker/UnitTest-ColorPickerUI/Helpers/ColorRepresentationHelperTest.cs @@ -1,5 +1,5 @@ using ColorPicker.Helpers; -using Microsoft.PowerToys.Settings.UI.Library; +using Microsoft.PowerToys.Settings.UI.Library.Enumerations; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Drawing; @@ -11,11 +11,15 @@ namespace UnitTest_ColorPickerUI.Helpers [TestMethod] [DataRow(ColorRepresentationType.CMYK, "cmyk(0%, 0%, 0%, 100%)")] [DataRow(ColorRepresentationType.HEX, "#000000")] + [DataRow(ColorRepresentationType.NCol, "R0, 0%, 100%")] + [DataRow(ColorRepresentationType.HSB, "hsb(0, 0%, 0%)")] + [DataRow(ColorRepresentationType.HSI, "hsi(0, 0%, 0%)")] [DataRow(ColorRepresentationType.HSL, "hsl(0, 0%, 0%)")] [DataRow(ColorRepresentationType.HSV, "hsv(0, 0%, 0%)")] + [DataRow(ColorRepresentationType.HWB, "hwb(0, 0%, 100%)")] [DataRow(ColorRepresentationType.RGB, "rgb(0, 0, 0)")] - public void ColorRGBtoCMYKZeroDiv(ColorRepresentationType type, string expected) + public void GetStringRepresentationTest(ColorRepresentationType type, string expected) { var result = ColorRepresentationHelper.GetStringRepresentation(Color.Black, type); Assert.AreEqual(result, expected);