1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-08 03:27:04 +09:00

Add alternate system color experimental feature (#105525)

Allows overriding KnownColor system values with an alternate set, which in the initial iteration is "dark mode" colors. Enables "dark mode" features in Windows Forms.

This is from the approved part of https://github.com/dotnet/winforms/issues/7641 with further naming iteration done offline with API review.
This commit is contained in:
Jeremy Kuhne 2024-07-29 13:30:04 -07:00 committed by GitHub
parent 4c58b5a513
commit 63cb7ec3af
Signed by: github
GPG key ID: B5690EEEBB952194
10 changed files with 230 additions and 88 deletions

View file

@ -293,3 +293,4 @@ Diagnostic id values for experimental APIs must not be recycled, as that could s
| Diagnostic ID | Introduced | Removed | Description |
| :---------------- | ---------: | ------: | :---------- |
| __`SYSLIB5001`__ | .NET 9 | TBD | `Tensor<T>` and related APIs in System.Numerics.Tensors are experimental in .NET 9 |
| __`SYSLIB5002`__ | .NET 9 | TBD | `SystemColors` alternate colors are experimental in .NET 9 |

View file

@ -0,0 +1,32 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Runtime.InteropServices;
internal static partial class Interop
{
internal static partial class User32
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public unsafe partial struct HIGHCONTRASTW
{
internal uint cbSize;
internal HIGHCONTRASTW_FLAGS dwFlags;
internal void* lpszDefaultScheme;
}
[Flags]
public enum HIGHCONTRASTW_FLAGS : uint
{
HCF_HIGHCONTRASTON = 0x00000001,
HCF_AVAILABLE = 0x00000002,
HCF_HOTKEYACTIVE = 0x00000004,
HCF_CONFIRMHOTKEY = 0x00000008,
HCF_HOTKEYSOUND = 0x00000010,
HCF_INDICATOR = 0x00000020,
HCF_HOTKEYAVAILABLE = 0x00000040,
HCF_OPTION_NOTHEMECHANGE = 0x00001000
}
}
}

View file

@ -11,7 +11,8 @@ internal static partial class Interop
public enum SystemParametersAction : uint
{
SPI_GETICONTITLELOGFONT = 0x1F,
SPI_GETNONCLIENTMETRICS = 0x29
SPI_GETNONCLIENTMETRICS = 0x29,
SPI_GETHIGHCONTRAST = 0x42
}
[LibraryImport(Libraries.User32)]

View file

@ -19,6 +19,9 @@ namespace System
// Tensor<T> and related APIs are marked as [Experimental] in .NET 9
internal const string TensorTDiagId = "SYSLIB5001";
// SystemColors alternate colors are marked as [Experimental] in .NET 9
internal const string SystemColorsDiagId = "SYSLIB5002";
// When adding a new diagnostic ID, add it to the table in docs\project\list-of-diagnostics.md as well.
// Keep new const identifiers above this comment.
}

View file

@ -1,4 +1,8 @@
Microsoft Visual Studio Solution File, Format Version 12.00

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.35111.106
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{39205290-06C5-468E-B5C9-D9C5737909EE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Collections.NonGeneric", "..\System.Collections.NonGeneric\ref\System.Collections.NonGeneric.csproj", "{4B06D595-C5B5-49E3-BC5D-7CA55B52D91B}"
@ -45,11 +49,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{722DCDC1-751
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{8349AD04-5979-4347-A869-7F76B043453E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "tools\gen", "{1ACFEEFC-7E61-483E-9CAF-1EB2DFC11558}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{1ACFEEFC-7E61-483E-9CAF-1EB2DFC11558}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "tools\src", "{D99A011B-F48D-4E22-9C5B-050294758522}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D99A011B-F48D-4E22-9C5B-050294758522}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "tools\ref", "{5CB40A8A-59D5-4E57-8F9A-D716C5BDFDC7}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{5CB40A8A-59D5-4E57-8F9A-D716C5BDFDC7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{0205F064-E9AB-408E-BC47-D1EB62C753C7}"
EndProject
@ -141,29 +145,33 @@ Global
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{39205290-06C5-468E-B5C9-D9C5737909EE} = {B004DC3D-DA89-4C76-8D15-327CCDB6D7C0}
{515B6C1E-757F-497E-9707-37B5822FFC9A} = {B004DC3D-DA89-4C76-8D15-327CCDB6D7C0}
{4B06D595-C5B5-49E3-BC5D-7CA55B52D91B} = {F2D0660B-B4A3-4039-A47D-63F9D1CE19B6}
{6ED31F56-EBDB-4E4D-A6D5-8F6078B1A241} = {F2D0660B-B4A3-4039-A47D-63F9D1CE19B6}
{9D080C1F-6334-4C14-BABF-D0D9132C0D83} = {F2D0660B-B4A3-4039-A47D-63F9D1CE19B6}
{F92BFBC7-A148-44D5-A977-01926068615D} = {F2D0660B-B4A3-4039-A47D-63F9D1CE19B6}
{D2E753F4-34A3-4641-9C0F-53539147CCF2} = {F2D0660B-B4A3-4039-A47D-63F9D1CE19B6}
{80A68643-0E37-4525-BF06-F50C3BF7B867} = {722DCDC1-7510-4B50-93F4-51E5FF833B2A}
{515B6C1E-757F-497E-9707-37B5822FFC9A} = {B004DC3D-DA89-4C76-8D15-327CCDB6D7C0}
{731DA2F5-004D-46F0-8751-4945163E8DF4} = {F2D0660B-B4A3-4039-A47D-63F9D1CE19B6}
{D8C6E8A8-4E73-42CD-A310-C63B64844A4C} = {F2D0660B-B4A3-4039-A47D-63F9D1CE19B6}
{FD462F99-C9F6-4D3E-B080-F5E337E8F2F8} = {F2D0660B-B4A3-4039-A47D-63F9D1CE19B6}
{80A68643-0E37-4525-BF06-F50C3BF7B867} = {722DCDC1-7510-4B50-93F4-51E5FF833B2A}
{F06CCE8D-0066-4B17-8EF0-162AE711F6D8} = {8349AD04-5979-4347-A869-7F76B043453E}
{315E574D-6A26-4A89-83B5-1C948F3C83D2} = {8349AD04-5979-4347-A869-7F76B043453E}
{9ECCC771-064F-403E-8E0E-7B049AAFAD36} = {8349AD04-5979-4347-A869-7F76B043453E}
{FD462F99-C9F6-4D3E-B080-F5E337E8F2F8} = {F2D0660B-B4A3-4039-A47D-63F9D1CE19B6}
{D5E974B9-EB58-4D32-A4F4-C31559436DEA} = {1ACFEEFC-7E61-483E-9CAF-1EB2DFC11558}
{D2D8DF0A-836A-4521-A9F5-349F91E87046} = {1ACFEEFC-7E61-483E-9CAF-1EB2DFC11558}
{1ACFEEFC-7E61-483E-9CAF-1EB2DFC11558} = {0205F064-E9AB-408E-BC47-D1EB62C753C7}
{198C17DB-F65F-4165-996B-128E3123A6CF} = {D99A011B-F48D-4E22-9C5B-050294758522}
{A92D7FC7-E3A9-4260-8F25-7FCF3AA900DC} = {D99A011B-F48D-4E22-9C5B-050294758522}
{D99A011B-F48D-4E22-9C5B-050294758522} = {0205F064-E9AB-408E-BC47-D1EB62C753C7}
{0A6457CF-5932-44F6-9983-978FA163B3A5} = {5CB40A8A-59D5-4E57-8F9A-D716C5BDFDC7}
{1ACFEEFC-7E61-483E-9CAF-1EB2DFC11558} = {0205F064-E9AB-408E-BC47-D1EB62C753C7}
{D99A011B-F48D-4E22-9C5B-050294758522} = {0205F064-E9AB-408E-BC47-D1EB62C753C7}
{5CB40A8A-59D5-4E57-8F9A-D716C5BDFDC7} = {0205F064-E9AB-408E-BC47-D1EB62C753C7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E2DD25F1-FA29-41D5-AB37-65DDC6A49304}
EndGlobalSection
GlobalSection(SharedMSBuildProjectFiles) = preSolution
..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{a92d7fc7-e3a9-4260-8f25-7fcf3aa900dc}*SharedItemsImports = 5
..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{d2d8df0a-836a-4521-a9f5-349f91e87046}*SharedItemsImports = 5
EndGlobalSection
EndGlobal

View file

@ -625,5 +625,7 @@ namespace System.Drawing
public static System.Drawing.Color Window { get { throw null; } }
public static System.Drawing.Color WindowFrame { get { throw null; } }
public static System.Drawing.Color WindowText { get { throw null; } }
[System.Diagnostics.CodeAnalysis.Experimental("SYSLIB5002", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
public static bool UseAlternativeColorSet { get { throw null; } set { } }
}
}

View file

@ -29,6 +29,7 @@
Link="System\Drawing\ColorConverterCommon.cs" />
<Compile Include="$(CommonPath)System\Drawing\ColorTable.cs"
Link="System\Drawing\ColorTable.cs" />
<Compile Include="$(CommonPath)System\Experimentals.cs" Link="Common\System\Experimentals.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'windows'">
@ -36,8 +37,12 @@
Link="Common\Interop\Windows\Interop.Libraries.cs" />
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.GetSysColor.cs"
Link="Common\Interop\Windows\User32\Interop.GetSysColor.cs" />
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.HIGHCONTRASTW.cs"
Link="Common\Interop\Windows\User32\Interop.HIGHCONTRASTW.cs" />
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.Win32SystemColors.cs"
Link="Common\Interop\Windows\User32\Interop.Win32SystemColors.cs" />
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.SystemParametersInfo.cs"
Link="Common\Interop\Windows\User32\Interop.SystemParametersInfo.cs" />
</ItemGroup>
<ItemGroup>

View file

@ -18,32 +18,32 @@ namespace System.Drawing
0,
// "System" colors, Part 1
#if FEATURE_WINDOWS_SYSTEM_COLORS
(uint)(byte)Interop.User32.Win32SystemColors.ActiveBorder,
(uint)(byte)Interop.User32.Win32SystemColors.ActiveCaption,
(uint)(byte)Interop.User32.Win32SystemColors.ActiveCaptionText,
(uint)(byte)Interop.User32.Win32SystemColors.AppWorkspace,
(uint)(byte)Interop.User32.Win32SystemColors.Control,
(uint)(byte)Interop.User32.Win32SystemColors.ControlDark,
(uint)(byte)Interop.User32.Win32SystemColors.ControlDarkDark,
(uint)(byte)Interop.User32.Win32SystemColors.ControlLight,
(uint)(byte)Interop.User32.Win32SystemColors.ControlLightLight,
(uint)(byte)Interop.User32.Win32SystemColors.ControlText,
(uint)(byte)Interop.User32.Win32SystemColors.Desktop,
(uint)(byte)Interop.User32.Win32SystemColors.GrayText,
(uint)(byte)Interop.User32.Win32SystemColors.Highlight,
(uint)(byte)Interop.User32.Win32SystemColors.HighlightText,
(uint)(byte)Interop.User32.Win32SystemColors.HotTrack,
(uint)(byte)Interop.User32.Win32SystemColors.InactiveBorder,
(uint)(byte)Interop.User32.Win32SystemColors.InactiveCaption,
(uint)(byte)Interop.User32.Win32SystemColors.InactiveCaptionText,
(uint)(byte)Interop.User32.Win32SystemColors.Info,
(uint)(byte)Interop.User32.Win32SystemColors.InfoText,
(uint)(byte)Interop.User32.Win32SystemColors.Menu,
(uint)(byte)Interop.User32.Win32SystemColors.MenuText,
(uint)(byte)Interop.User32.Win32SystemColors.ScrollBar,
(uint)(byte)Interop.User32.Win32SystemColors.Window,
(uint)(byte)Interop.User32.Win32SystemColors.WindowFrame,
(uint)(byte)Interop.User32.Win32SystemColors.WindowText,
(byte)Interop.User32.Win32SystemColors.ActiveBorder,
(byte)Interop.User32.Win32SystemColors.ActiveCaption,
(byte)Interop.User32.Win32SystemColors.ActiveCaptionText,
(byte)Interop.User32.Win32SystemColors.AppWorkspace,
(byte)Interop.User32.Win32SystemColors.Control,
(byte)Interop.User32.Win32SystemColors.ControlDark,
(byte)Interop.User32.Win32SystemColors.ControlDarkDark,
(byte)Interop.User32.Win32SystemColors.ControlLight,
(byte)Interop.User32.Win32SystemColors.ControlLightLight,
(byte)Interop.User32.Win32SystemColors.ControlText,
(byte)Interop.User32.Win32SystemColors.Desktop,
(byte)Interop.User32.Win32SystemColors.GrayText,
(byte)Interop.User32.Win32SystemColors.Highlight,
(byte)Interop.User32.Win32SystemColors.HighlightText,
(byte)Interop.User32.Win32SystemColors.HotTrack,
(byte)Interop.User32.Win32SystemColors.InactiveBorder,
(byte)Interop.User32.Win32SystemColors.InactiveCaption,
(byte)Interop.User32.Win32SystemColors.InactiveCaptionText,
(byte)Interop.User32.Win32SystemColors.Info,
(byte)Interop.User32.Win32SystemColors.InfoText,
(byte)Interop.User32.Win32SystemColors.Menu,
(byte)Interop.User32.Win32SystemColors.MenuText,
(byte)Interop.User32.Win32SystemColors.ScrollBar,
(byte)Interop.User32.Win32SystemColors.Window,
(byte)Interop.User32.Win32SystemColors.WindowFrame,
(byte)Interop.User32.Win32SystemColors.WindowText,
#else
// Hard-coded constants, based on default Windows settings.
0xFFD4D0C8, // ActiveBorder
@ -217,13 +217,13 @@ namespace System.Drawing
0xFF9ACD32, // YellowGreen
#if FEATURE_WINDOWS_SYSTEM_COLORS
// "System" colors, Part 2
(uint)(byte)Interop.User32.Win32SystemColors.ButtonFace,
(uint)(byte)Interop.User32.Win32SystemColors.ButtonHighlight,
(uint)(byte)Interop.User32.Win32SystemColors.ButtonShadow,
(uint)(byte)Interop.User32.Win32SystemColors.GradientActiveCaption,
(uint)(byte)Interop.User32.Win32SystemColors.GradientInactiveCaption,
(uint)(byte)Interop.User32.Win32SystemColors.MenuBar,
(uint)(byte)Interop.User32.Win32SystemColors.MenuHighlight,
(byte)Interop.User32.Win32SystemColors.ButtonFace,
(byte)Interop.User32.Win32SystemColors.ButtonHighlight,
(byte)Interop.User32.Win32SystemColors.ButtonShadow,
(byte)Interop.User32.Win32SystemColors.GradientActiveCaption,
(byte)Interop.User32.Win32SystemColors.GradientInactiveCaption,
(byte)Interop.User32.Win32SystemColors.MenuBar,
(byte)Interop.User32.Win32SystemColors.MenuHighlight,
#else
0xFFF0F0F0, // ButtonFace
0xFFFFFFFF, // ButtonHighlight
@ -242,8 +242,8 @@ namespace System.Drawing
[
// "not a known color"
KnownColorKindUnknown,
// "System" colors, Part 1
#if FEATURE_WINDOWS_SYSTEM_COLORS
KnownColorKindSystem, // ActiveBorder
KnownColorKindSystem, // ActiveCaption
KnownColorKindSystem, // ActiveCaptionText
@ -270,35 +270,7 @@ namespace System.Drawing
KnownColorKindSystem, // Window
KnownColorKindSystem, // WindowFrame
KnownColorKindSystem, // WindowText
#else
// Hard-coded constants, based on default Windows settings.
KnownColorKindSystem, // ActiveBorder
KnownColorKindSystem, // ActiveCaption
KnownColorKindSystem, // ActiveCaptionText
KnownColorKindSystem, // AppWorkspace
KnownColorKindSystem, // Control
KnownColorKindSystem, // ControlDark
KnownColorKindSystem, // ControlDarkDark
KnownColorKindSystem, // ControlLight
KnownColorKindSystem, // ControlLightLight
KnownColorKindSystem, // ControlText
KnownColorKindSystem, // Desktop
KnownColorKindSystem, // GrayText
KnownColorKindSystem, // Highlight
KnownColorKindSystem, // HighlightText
KnownColorKindSystem, // HotTrack
KnownColorKindSystem, // InactiveBorder
KnownColorKindSystem, // InactiveCaption
KnownColorKindSystem, // InactiveCaptionText
KnownColorKindSystem, // Info
KnownColorKindSystem, // InfoText
KnownColorKindSystem, // Menu
KnownColorKindSystem, // MenuText
KnownColorKindSystem, // ScrollBar
KnownColorKindSystem, // Window
KnownColorKindSystem, // WindowFrame
KnownColorKindSystem, // WindowText
#endif
// "Web" Colors, Part 1
KnownColorKindWeb, // Transparent
KnownColorKindWeb, // AliceBlue
@ -441,8 +413,8 @@ namespace System.Drawing
KnownColorKindWeb, // WhiteSmoke
KnownColorKindWeb, // Yellow
KnownColorKindWeb, // YellowGreen
#if FEATURE_WINDOWS_SYSTEM_COLORS
// "System" colors, Part 2
// "System" colors, Part 1
KnownColorKindSystem, // ButtonFace
KnownColorKindSystem, // ButtonHighlight
KnownColorKindSystem, // ButtonShadow
@ -450,19 +422,56 @@ namespace System.Drawing
KnownColorKindSystem, // GradientInactiveCaption
KnownColorKindSystem, // MenuBar
KnownColorKindSystem, // MenuHighlight
#else
KnownColorKindSystem, // ButtonFace
KnownColorKindSystem, // ButtonHighlight
KnownColorKindSystem, // ButtonShadow
KnownColorKindSystem, // GradientActiveCaption
KnownColorKindSystem, // GradientInactiveCaption
KnownColorKindSystem, // MenuBar
KnownColorKindSystem, // MenuHighlight
#endif
// "Web" colors, Part 2
KnownColorKindWeb, // RebeccaPurple
];
// These values were based on manual investigation of dark mode themes in the
// Win32 Common Controls and WinUI. There aren't direct mappings published by
// Windows, these may change slightly when this feature is finalized to make
// sure we have the best experience in hybrid dark mode scenarios (mixing
// WPF, WinForms, and WinUI).
private static ReadOnlySpan<uint> AlternateSystemColors =>
[
0, // To align with KnownColor.ActiveBorder = 1
// Existing New
0xFF464646, // FFB4B4B4 - FF464646: ActiveBorder - Dark gray
0xFF3C5F78, // FF99B4D1 - FF3C5F78: ActiveCaption - Highlighted Text Background
0xFFFFFFFF, // FF000000 - FFBEBEBE: ActiveCaptionText - White
0xFF3C3C3C, // FFABABAB - FF3C3C3C: AppWorkspace - Panel Background
0xFF202020, // FFF0F0F0 - FF373737: Control - Normal Panel/Windows Background
0xFF4A4A4A, // FFA0A0A0 - FF464646: ControlDark - A lighter gray for dark mode
0xFF5A5A5A, // FF696969 - FF5A5A5A: ControlDarkDark - An even lighter gray for dark mode
0xFF2E2E2E, // FFE3E3E3 - FF2E2E2E: ControlLight - Unfocused Textbox Background
0xFF1F1F1F, // FFFFFFFF - FF1F1F1F: ControlLightLight - Focused Textbox Background
0xFFFFFFFF, // FF000000 - FFFFFFFF: ControlText - Control Forecolor and Text Color
0xFF101010, // FF000000 - FF101010: Desktop - Black
0xFF969696, // FF6D6D6D - FF969696: GrayText - Prompt Text Focused TextBox
0xFF2864B4, // FF0078D7 - FF2864B4: Highlight - Highlighted Panel in DarkMode
0xFF000000, // FFFFFFFF - FF000000: HighlightText - White
0xFF2D5FAF, // FF0066CC - FF2D5FAF: HotTrack - Background of the ToggleSwitch
0xFF3C3F41, // FFF4F7FC - FF3C3F41: InactiveBorder - Dark gray
0xFF374B5A, // FFBFCBDD - FF374B5A: InactiveCaption - Highlighted Panel in DarkMode
0xFFBEBEBE, // FF000000 - FFBEBEBE: InactiveCaptionText - Middle Dark Panel
0xFF50503C, // FFFFFFE1 - FF50503C: Info - Link Label
0xFFBEBEBE, // FF000000 - FFBEBEBE: InfoText - Prompt Text Color
0xFF373737, // FFF0F0F0 - FF373737: Menu - Normal Menu Background
0xFFF0F0F0, // FF000000 - FFF0F0F0: MenuText - White
0xFF505050, // FFC8C8C8 - FF505050: ScrollBar - Scrollbars and Scrollbar Arrows
0xFF323232, // FFFFFFFF - FF323232: Window - Window Background
0xFF282828, // FF646464 - FF282828: WindowFrame - White
0xFFF0F0F0, // FF000000 - FFF0F0F0: WindowText - White
0xFF202020, // FFF0F0F0 - FF373737: ButtonFace - Same as Window Background
0xFF101010, // FFFFFFFF - FF101010: ButtonHighlight - White
0xFF464646, // FFA0A0A0 - FF464646: ButtonShadow - Same as Scrollbar Elements
0XFF416482, // FFB9D1EA - FF416482: GradientActiveCaption - Same as Highlighted Text Background
0xFF557396, // FFD7E4F2 - FF557396: GradientInactiveCaption - Same as Highlighted Panel in DarkMode
0xFF373737, // FFF0F0F0 - FF373737: MenuBar - Same as Normal Menu Background
0xFF2A80D2 // FF3399FF - FF2A80D2: MenuHighlight - Same as Highlighted Menu Background
];
internal static Color ArgbToKnownColor(uint argb)
{
Debug.Assert((argb & Color.ARGBAlphaMask) == Color.ARGBAlphaMask);
@ -490,19 +499,50 @@ namespace System.Drawing
: ColorValueTable[(int)color];
}
private static uint GetAlternateSystemColorArgb(KnownColor color)
{
// Shift the original (split) index to fit the alternate color map.
int index = color <= KnownColor.WindowText
? (int)color
: (int)color - (int)KnownColor.ButtonFace + (int)KnownColor.WindowText + 1;
return AlternateSystemColors[index];
}
#if FEATURE_WINDOWS_SYSTEM_COLORS
public static uint GetSystemColorArgb(KnownColor color)
{
Debug.Assert(Color.IsKnownColorSystem(color));
return ColorTranslator.COLORREFToARGB(Interop.User32.GetSysColor((byte)ColorValueTable[(int)color]));
return !SystemColors.s_useAlternativeColorSet || HighContrastEnabled()
? ColorTranslator.COLORREFToARGB(Interop.User32.GetSysColor((byte)ColorValueTable[(int)color]))
: GetAlternateSystemColorArgb(color);
}
private static unsafe bool HighContrastEnabled()
{
Interop.User32.HIGHCONTRASTW highContrast = default;
// Note that the documentation for HIGHCONTRASTW says that the lpszDefaultScheme member needs to be
// freed, but this is incorrect. No internal users ever free the pointer and the pointer never changes.
highContrast.cbSize = (uint)sizeof(Interop.User32.HIGHCONTRASTW);
bool success = Interop.User32.SystemParametersInfoW(
Interop.User32.SystemParametersAction.SPI_GETHIGHCONTRAST,
highContrast.cbSize,
&highContrast,
0); // This has no meaning when getting values
return success && highContrast.dwFlags.HasFlag(Interop.User32.HIGHCONTRASTW_FLAGS.HCF_HIGHCONTRASTON);
}
#else
public static uint GetSystemColorArgb(KnownColor color)
{
Debug.Assert(Color.IsKnownColorSystem(color));
return ColorValueTable[(int)color];
return (!SystemColors.s_useAlternativeColorSet)
? ColorValueTable[(int)color]
: GetAlternateSystemColorArgb(color);
}
#endif
}

View file

@ -1,10 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics.CodeAnalysis;
namespace System.Drawing
{
public static class SystemColors
{
internal static bool s_useAlternativeColorSet;
public static Color ActiveBorder => Color.FromKnownColor(KnownColor.ActiveBorder);
public static Color ActiveCaption => Color.FromKnownColor(KnownColor.ActiveCaption);
public static Color ActiveCaptionText => Color.FromKnownColor(KnownColor.ActiveCaptionText);
@ -47,5 +51,29 @@ namespace System.Drawing
public static Color Window => Color.FromKnownColor(KnownColor.Window);
public static Color WindowFrame => Color.FromKnownColor(KnownColor.WindowFrame);
public static Color WindowText => Color.FromKnownColor(KnownColor.WindowText);
/// <summary>
/// When <see langword="true"/>, system <see cref="KnownColor"/> values will return
/// the alternative color set (as returned by <see cref="SystemColors"/> statics or
/// <see cref="Color.FromKnownColor(KnownColor)"/>). This is currently "dark mode"
/// variants of the system colors.
/// </summary>
/// <remarks>
/// <para>
/// <see cref="KnownColor"/> <see cref="Color"/> values are always looked up every
/// time you use them and do not retain any other context. As such, existing
/// <see cref="Color"/> values will change when this property is set.
/// </para>
/// <para>
/// On Windows, system <see cref="KnownColor"/> values will always return the current
/// Windows color when the OS has a high contrast theme enabled.
/// </para>
/// </remarks>
[Experimental(Experimentals.SystemColorsDiagId, UrlFormat = Experimentals.SharedUrlFormat)]
public static bool UseAlternativeColorSet
{
get => s_useAlternativeColorSet;
set => s_useAlternativeColorSet = value;
}
}
}

View file

@ -448,7 +448,7 @@ namespace System.Drawing.Primitives.Tests
public static IEnumerable<object[]> Equality_MemberData()
{
yield return new object[] { Color.AliceBlue, Color.AliceBlue, true };
yield return new object[] { Color.AliceBlue, Color.White, false};
yield return new object[] { Color.AliceBlue, Color.White, false };
yield return new object[] { Color.AliceBlue, Color.Black, false };
yield return new object[] { Color.FromArgb(255, 1, 2, 3), Color.FromArgb(255, 1, 2, 3), true };
@ -466,7 +466,7 @@ namespace System.Drawing.Primitives.Tests
string someNameConstructed = string.Join("", "Some", "Name");
Assert.NotSame("SomeName", someNameConstructed); // If this fails the above must be changed so this test is correct.
yield return new object[] {Color.FromName("SomeName"), Color.FromName(someNameConstructed), true};
yield return new object[] { Color.FromName("SomeName"), Color.FromName(someNameConstructed), true };
}
[Theory]
@ -889,5 +889,27 @@ namespace System.Drawing.Primitives.Tests
// The COLORREF value has the following hexadecimal form: 0x00bbggrr.
return color.B << 16 | color.G << 8 | color.R;
}
[Fact]
public void SystemColor_AlternativeColors()
{
try
{
#pragma warning disable SYSLIB5002 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
Drawing.SystemColors.UseAlternativeColorSet = true;
#pragma warning restore SYSLIB5002
Assert.Equal(0xFF464646, (uint)Drawing.SystemColors.ActiveBorder.ToArgb());
Assert.Equal(0xFFF0F0F0, (uint)Drawing.SystemColors.WindowText.ToArgb());
Assert.Equal(0xFF202020, (uint)Drawing.SystemColors.ButtonFace.ToArgb());
Assert.Equal(0xFF2A80D2, (uint)Drawing.SystemColors.MenuHighlight.ToArgb());
}
finally
{
#pragma warning disable SYSLIB5002 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
Drawing.SystemColors.UseAlternativeColorSet = false;
#pragma warning restore SYSLIB5002
}
}
}
}