Color picker - Lab format: use roundoff optional #13603 (#42986)

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
The default CIELab format rounds the values to two decimal places, which
is a degree of precision that isn't always needed. This PR adds an
optional formatting character (i) to the three CIELab format parameters,
which rounds the value to the nearest integer.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #13603/#14863. Note that in the discussion for #13603,
there are some additional suggestions that this PR doesn't address.
- [ ] **Communication:** Haven't gotten the green light for this
approach with the core contributors yet. Happy to pivot to a different
approach if needed.
- [ ] **Tests:** Added/updated and all pass
- [x] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

In the case where a or b get rounded to -0 (e.g. -0.0001 rounds to -0),
the negative sign gets removed. However, I noticed during testing that
the default format (rounding to two decimal places) retains the negative
sign in these situations (see third screenshot). I can a) revert to
keeping the -0 for the new rounding behavior, b) change -0 to 0 for
other rounded values, or c) leave it as-is. Also open to suggestions.

I can update the docs as well, if we're happy with the approach.

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
- Settings: Can change the default CIELab format to display rounded
values
- Settings: Can create a custom format with rounded CIELab values
- Color Picker: Rounded values are displayed when hovering and as a
saved color
<img width="1076" height="1520" alt="powertoys-cielab"
src="https://github.com/user-attachments/assets/8a67142d-d7f4-49bc-b1ba-ad9304235218"
/>
<img width="447" height="390" alt="powertoys-cielab-selected"
src="https://github.com/user-attachments/assets/c96d3bc9-cac7-4470-af3f-b2bce78d0915"
/>
<img width="445" height="389" alt="powertoys-cielab-selected-0"
src="https://github.com/user-attachments/assets/c329bc5b-c18a-4f61-a808-0fa5050e09ed"
/>

---------

Co-authored-by: vanzue <vanzue@outlook.com>
This commit is contained in:
Ruthie Sun
2026-02-27 14:12:58 -08:00
committed by GitHub
parent 96e6542cf1
commit 3e1b07f52c
4 changed files with 47 additions and 9 deletions

View File

@@ -515,8 +515,7 @@ namespace ManagedCommon
return lightnessL.ToString(CultureInfo.InvariantCulture); return lightnessL.ToString(CultureInfo.InvariantCulture);
case "Lc": case "Lc":
var (lightnessC, _, _) = ConvertToCIELABColor(color); var (lightnessC, _, _) = ConvertToCIELABColor(color);
lightnessC = Math.Round(lightnessC, 2); return ColorPercentFormatted(lightnessC, paramFormat, 2);
return lightnessC.ToString(CultureInfo.InvariantCulture);
case "Lo": case "Lo":
var (lightnessO, _, _) = ConvertToOklabColor(color); var (lightnessO, _, _) = ConvertToOklabColor(color);
lightnessO = Math.Round(lightnessO, 2); lightnessO = Math.Round(lightnessO, 2);
@@ -531,12 +530,10 @@ namespace ManagedCommon
return blackness.ToString(CultureInfo.InvariantCulture); return blackness.ToString(CultureInfo.InvariantCulture);
case "Ca": case "Ca":
var (_, chromaticityA, _) = ConvertToCIELABColor(color); var (_, chromaticityA, _) = ConvertToCIELABColor(color);
chromaticityA = Math.Round(chromaticityA, 2); return ColorPercentFormatted(chromaticityA, paramFormat, 2);
return chromaticityA.ToString(CultureInfo.InvariantCulture);
case "Cb": case "Cb":
var (_, _, chromaticityB) = ConvertToCIELABColor(color); var (_, _, chromaticityB) = ConvertToCIELABColor(color);
chromaticityB = Math.Round(chromaticityB, 2); return ColorPercentFormatted(chromaticityB, paramFormat, 2);
return chromaticityB.ToString(CultureInfo.InvariantCulture);
case "Oa": case "Oa":
var (_, chromaticityAOklab, _) = ConvertToOklabColor(color); var (_, chromaticityAOklab, _) = ConvertToOklabColor(color);
chromaticityAOklab = Math.Round(chromaticityAOklab, 2); chromaticityAOklab = Math.Round(chromaticityAOklab, 2);
@@ -595,6 +592,24 @@ namespace ManagedCommon
} }
} }
private static string ColorPercentFormatted(double colorPercentValue, char paramFormat, int defaultDecimalDigits)
{
switch (paramFormat)
{
case 'i':
double roundedColorPercentValue = Math.Round(colorPercentValue);
if (roundedColorPercentValue == 0)
{
// convert -0 to 0
roundedColorPercentValue = 0.0;
}
return roundedColorPercentValue.ToString(CultureInfo.InvariantCulture);
default:
return Math.Round(colorPercentValue, defaultDecimalDigits).ToString(CultureInfo.InvariantCulture);
}
}
public static string GetDefaultFormat(string formatName) public static string GetDefaultFormat(string formatName)
{ {
switch (formatName) switch (formatName)

View File

@@ -108,7 +108,7 @@
Style="{StaticResource CaptionTextBlockStyle}" /> Style="{StaticResource CaptionTextBlockStyle}" />
<ItemsControl <ItemsControl
x:Name="ColorParametersItemsControl" x:Name="RGBAColorParametersItemsControl"
IsTabStop="False" IsTabStop="False"
ItemTemplate="{StaticResource ColorParameterTemplate}" ItemTemplate="{StaticResource ColorParameterTemplate}"
ItemsPanel="{StaticResource ItemPanelTemplate}" /> ItemsPanel="{StaticResource ItemPanelTemplate}" />
@@ -117,6 +117,18 @@
x:Uid="ColorFormatEditorHelpline3" x:Uid="ColorFormatEditorHelpline3"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
Style="{StaticResource CaptionTextBlockStyle}" /> Style="{StaticResource CaptionTextBlockStyle}" />
<ItemsControl
x:Name="CIELabColorParametersItemsControl"
IsTabStop="False"
ItemTemplate="{StaticResource ColorParameterTemplate}"
ItemsPanel="{StaticResource ItemPanelTemplate}" />
<TextBlock
x:Uid="ColorFormatEditorHelpline4"
VerticalAlignment="Bottom"
Style="{StaticResource CaptionTextBlockStyle}" />
</StackPanel> </StackPanel>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@@ -66,7 +66,7 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
new ColorFormatParameter() { Parameter = "%Na", Description = resourceLoader.GetString("Help_color_name") }, new ColorFormatParameter() { Parameter = "%Na", Description = resourceLoader.GetString("Help_color_name") },
}; };
ColorParametersItemsControl.ItemsSource = new List<ColorFormatParameter> RGBAColorParametersItemsControl.ItemsSource = new List<ColorFormatParameter>
{ {
new ColorFormatParameter() { Parameter = "b", Description = resourceLoader.GetString("Help_byte") }, new ColorFormatParameter() { Parameter = "b", Description = resourceLoader.GetString("Help_byte") },
new ColorFormatParameter() { Parameter = "h", Description = resourceLoader.GetString("Help_hexL1") }, new ColorFormatParameter() { Parameter = "h", Description = resourceLoader.GetString("Help_hexL1") },
@@ -76,6 +76,11 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
new ColorFormatParameter() { Parameter = "f", Description = resourceLoader.GetString("Help_floatWith") }, new ColorFormatParameter() { Parameter = "f", Description = resourceLoader.GetString("Help_floatWith") },
new ColorFormatParameter() { Parameter = "F", Description = resourceLoader.GetString("Help_floatWithout") }, new ColorFormatParameter() { Parameter = "F", Description = resourceLoader.GetString("Help_floatWithout") },
}; };
CIELabColorParametersItemsControl.ItemsSource = new List<ColorFormatParameter>
{
new ColorFormatParameter() { Parameter = "i", Description = resourceLoader.GetString("Help_integer") },
};
} }
private void NewColorName_TextChanged(object sender, TextChangedEventArgs e) private void NewColorName_TextChanged(object sender, TextChangedEventArgs e)

View File

@@ -1728,6 +1728,12 @@ Made with 💗 by Microsoft and the PowerToys community.</value>
<value>float without leading zero</value> <value>float without leading zero</value>
</data> </data>
<data name="ColorFormatEditorHelpline3.Text" xml:space="preserve"> <data name="ColorFormatEditorHelpline3.Text" xml:space="preserve">
<value>The lightness (CIE), chromaticity A (CIE Lab) and chromaticity B (CIE Lab) values can be formatted to the following formats:</value>
</data>
<data name="Help_integer" xml:space="preserve">
<value>rounded to the nearest integer</value>
</data>
<data name="ColorFormatEditorHelpline4.Text" xml:space="preserve">
<value>Example: %ReX means red value in hex uppercase two digits format.</value> <value>Example: %ReX means red value in hex uppercase two digits format.</value>
</data> </data>
<data name="ColorPicker_ShowColorName.Header" xml:space="preserve"> <data name="ColorPicker_ShowColorName.Header" xml:space="preserve">