From c20c31cd2fb710a6cd4594c9842b11a58beddd18 Mon Sep 17 00:00:00 2001 From: Klaus Loeffelmann Date: Wed, 31 Jul 2024 17:58:46 -0700 Subject: [PATCH] Fix issues with SystemColors and UseAlternativeColorSet. --- .../src/System/Drawing/SystemBrushes.cs | 20 +++++++++++++++++- .../src/System/Drawing/SystemPens.cs | 21 ++++++++++++++++++- .../Windows/Forms/DeviceContextExtensions.cs | 20 ++++++++++++++++-- .../src/Windows/Win32/CreateBrushScope.cs | 5 ++++- .../src/System/Windows/Forms/Control.cs | 4 +++- .../Controls/ListBoxes/CheckedListBox.cs | 2 -- 6 files changed, 64 insertions(+), 8 deletions(-) diff --git a/src/System.Drawing.Common/src/System/Drawing/SystemBrushes.cs b/src/System.Drawing.Common/src/System/Drawing/SystemBrushes.cs index a2fd62f88e1..518b019f8c3 100644 --- a/src/System.Drawing.Common/src/System/Drawing/SystemBrushes.cs +++ b/src/System.Drawing.Common/src/System/Drawing/SystemBrushes.cs @@ -5,6 +5,11 @@ namespace System.Drawing; public static class SystemBrushes { +#if NET9_0_OR_GREATER +#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. + private static bool s_colorSetOnLastAccess = SystemColors.UseAlternativeColorSet; +#pragma warning restore SYSLIB5002 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +#endif private static readonly object s_systemBrushesKey = new(); public static Brush ActiveBorder => FromSystemColor(SystemColors.ActiveBorder); @@ -57,7 +62,20 @@ public static Brush FromSystemColor(Color c) throw new ArgumentException(SR.Format(SR.ColorNotSystemColor, c.ToString())); } - if (!Gdip.ThreadData.TryGetValue(s_systemBrushesKey, out object? tempSystemBrushes) || tempSystemBrushes is not Brush[] systemBrushes) +#if NET9_0_OR_GREATER +#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. + if (s_colorSetOnLastAccess != SystemColors.UseAlternativeColorSet) + { + s_colorSetOnLastAccess = SystemColors.UseAlternativeColorSet; + + // We need to clear the SystemBrushes cache, when the ColorMode had changed. + Gdip.ThreadData.Remove(s_systemBrushesKey); + } +#pragma warning restore SYSLIB5002 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +#endif + + if (!Gdip.ThreadData.TryGetValue(s_systemBrushesKey, out object? tempSystemBrushes) + || tempSystemBrushes is not Brush[] systemBrushes) { systemBrushes = new Brush[(int)KnownColor.WindowText + (int)KnownColor.MenuHighlight - (int)KnownColor.YellowGreen]; Gdip.ThreadData[s_systemBrushesKey] = systemBrushes; diff --git a/src/System.Drawing.Common/src/System/Drawing/SystemPens.cs b/src/System.Drawing.Common/src/System/Drawing/SystemPens.cs index 8e1786d5fac..a104c452b12 100644 --- a/src/System.Drawing.Common/src/System/Drawing/SystemPens.cs +++ b/src/System.Drawing.Common/src/System/Drawing/SystemPens.cs @@ -5,6 +5,12 @@ namespace System.Drawing; public static class SystemPens { +#if NET9_0_OR_GREATER +#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. + private static bool s_colorSetOnLastAccess = SystemColors.UseAlternativeColorSet; +#pragma warning restore SYSLIB5002 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +#endif + private static readonly object s_systemPensKey = new(); public static Pen ActiveBorder => FromSystemColor(SystemColors.ActiveBorder); @@ -58,7 +64,20 @@ public static Pen FromSystemColor(Color c) throw new ArgumentException(SR.Format(SR.ColorNotSystemColor, c.ToString())); } - if (!Gdip.ThreadData.TryGetValue(s_systemPensKey, out object? tempSystemPens) || tempSystemPens is not Pen[] systemPens) +#if NET9_0_OR_GREATER +#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. + if (s_colorSetOnLastAccess != SystemColors.UseAlternativeColorSet) + { + s_colorSetOnLastAccess = SystemColors.UseAlternativeColorSet; + + // We need to clear the SystemBrushes cache, when the ColorMode had changed. + Gdip.ThreadData.Remove(s_systemPensKey); + } +#pragma warning restore SYSLIB5002 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +#endif + + if (!Gdip.ThreadData.TryGetValue(s_systemPensKey, out object? tempSystemPens) + || tempSystemPens is not Pen[] systemPens) { systemPens = new Pen[(int)KnownColor.WindowText + (int)KnownColor.MenuHighlight - (int)KnownColor.YellowGreen]; Gdip.ThreadData[s_systemPensKey] = systemPens; diff --git a/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/DeviceContextExtensions.cs b/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/DeviceContextExtensions.cs index 5e8d271c476..b1d9f49d0a7 100644 --- a/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/DeviceContextExtensions.cs +++ b/src/System.Windows.Forms.Primitives/src/System/Windows/Forms/DeviceContextExtensions.cs @@ -95,7 +95,8 @@ internal static unsafe void DrawLines(this HDC hdc, HPEN hpen, ReadOnlySpan } } - internal static Color FindNearestColor(this DeviceContextHdcScope hdc, Color color) => FindNearestColor(hdc.HDC, color); + internal static Color FindNearestColor(this DeviceContextHdcScope hdc, Color color) => + FindNearestColor(hdc.HDC, color); /// /// Calls to get the nearest color for the given @@ -116,7 +117,22 @@ internal static unsafe void DrawLines(this HDC hdc, HPEN hpen, ReadOnlySpan /// internal static Color FindNearestColor(this HDC hdc, Color color) { - Color newColor = ColorTranslator.FromWin32((int)PInvoke.GetNearestColor(hdc, (COLORREF)(uint)ColorTranslator.ToWin32(color)).Value); +#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. + if (color.IsSystemColor && SystemColors.UseAlternativeColorSet) + { + // We need to "un-system" the color for nearest color to work correctly in dark mode, + // since GetNearestColor doesn't understand dark mode system colors and internally works + // with the light mode colors. + color = Color.FromArgb(color.ToArgb()); + Debug.Assert(!color.IsSystemColor, "Color should not be a system color."); + } +#pragma warning restore SYSLIB5002 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + + Color newColor = ColorTranslator.FromWin32( + (int)PInvoke.GetNearestColor( + hdc: hdc, + color: (COLORREF)(uint)ColorTranslator.ToWin32(color)).Value); + return newColor.ToArgb() == color.ToArgb() ? color : newColor; } diff --git a/src/System.Windows.Forms.Primitives/src/Windows/Win32/CreateBrushScope.cs b/src/System.Windows.Forms.Primitives/src/Windows/Win32/CreateBrushScope.cs index cd09e6fc84c..2988ae0c6ee 100644 --- a/src/System.Windows.Forms.Primitives/src/Windows/Win32/CreateBrushScope.cs +++ b/src/System.Windows.Forms.Primitives/src/Windows/Win32/CreateBrushScope.cs @@ -28,9 +28,12 @@ internal readonly ref struct CreateBrushScope /// public CreateBrushScope(Color color) { - HBRUSH = color.IsSystemColor +#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. + HBRUSH = color.IsSystemColor && !SystemColors.UseAlternativeColorSet ? PInvoke.GetSysColorBrush(color) : PInvoke.CreateSolidBrush(color); +#pragma warning restore SYSLIB5002 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + ValidateBrushHandle(); } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs index 8e36a2237f0..21909ddb411 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs @@ -718,7 +718,8 @@ internal HBRUSH BackColorBrush Color color = BackColor; HBRUSH backBrush; - if (color.IsSystemColor) +#pragma warning disable WFO9001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + if (color.IsSystemColor && !Application.IsDarkModeEnabled) { backBrush = PInvoke.GetSysColorBrush(color); SetState(States.OwnCtlBrush, false); @@ -728,6 +729,7 @@ internal HBRUSH BackColorBrush backBrush = PInvoke.CreateSolidBrush((COLORREF)(uint)ColorTranslator.ToWin32(color)); SetState(States.OwnCtlBrush, true); } +#pragma warning restore WFO9001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. Debug.Assert(!backBrush.IsNull, "Failed to create brushHandle"); Properties.SetObject(s_backBrushProperty, backBrush); diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ListBoxes/CheckedListBox.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ListBoxes/CheckedListBox.cs index 5cd2e4ebc4b..a67e9bcfbc8 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ListBoxes/CheckedListBox.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/ListBoxes/CheckedListBox.cs @@ -578,7 +578,6 @@ protected override void OnDrawItem(DrawItemEventArgs e) textBounds.X = bounds.X; } -#pragma warning disable WFO9001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. Color backColor = (SelectionMode != SelectionMode.None) ? e.BackColor : BackColor; Color foreColor = (SelectionMode != SelectionMode.None) ? e.ForeColor : ForeColor; if (!Enabled) @@ -605,7 +604,6 @@ protected override void OnDrawItem(DrawItemEventArgs e) foreColor = SystemColors.GrayText; } } -#pragma warning restore WFO9001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. // Draw the text