Skip to content

Commit e079671

Browse files
Implement ButtonDarkModeAdapter.
1 parent 097bd6a commit e079671

16 files changed

+1478
-48
lines changed

src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/Button.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ public AutoSizeMode AutoSizeMode
8181

8282
internal override ButtonBaseAdapter CreateStandardAdapter() => new ButtonStandardAdapter(this);
8383

84+
internal override ButtonBaseAdapter CreateDarkModeAdapter() => new ButtonDarkModeAdapter(this);
85+
8486
internal override Size GetPreferredSizeCore(Size proposedConstraints)
8587
{
8688
if (FlatStyle != FlatStyle.System)

src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonBase.cs

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -960,35 +960,44 @@ public override Size GetPreferredSize(Size proposedSize)
960960

961961
internal override Size GetPreferredSizeCore(Size proposedConstraints)
962962
{
963-
Size preferedSize = Adapter.GetPreferredSizeCore(proposedConstraints);
964-
return LayoutUtils.UnionSizes(preferedSize + Padding.Size, MinimumSize);
963+
Size preferredSize = Adapter.GetPreferredSizeCore(proposedConstraints);
964+
return LayoutUtils.UnionSizes(preferredSize + Padding.Size, MinimumSize);
965965
}
966966

967+
#pragma warning disable WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
967968
internal ButtonBaseAdapter Adapter
968969
{
969970
get
970971
{
971-
if (_adapter is null || FlatStyle != _cachedAdapterType)
972+
if (_adapter is null
973+
|| FlatStyle != _cachedAdapterType)
972974
{
973-
switch (FlatStyle)
975+
if (Application.IsDarkModeEnabled && this is Button)
974976
{
975-
case FlatStyle.Standard:
976-
_adapter = CreateStandardAdapter();
977-
break;
978-
case FlatStyle.Popup:
979-
_adapter = CreatePopupAdapter();
980-
break;
981-
case FlatStyle.Flat:
982-
_adapter = CreateFlatAdapter();
983-
;
984-
break;
985-
default:
986-
Debug.Fail($"Unsupported FlatStyle: \"{FlatStyle}\"");
987-
break;
977+
_adapter = CreateDarkModeAdapter();
988978
}
979+
else
980+
{
981+
switch (FlatStyle)
982+
{
983+
case FlatStyle.Standard:
984+
_adapter = CreateStandardAdapter();
985+
break;
986+
case FlatStyle.Popup:
987+
_adapter = CreatePopupAdapter();
988+
break;
989+
case FlatStyle.Flat:
990+
_adapter = CreateFlatAdapter();
991+
break;
992+
default:
993+
Debug.Fail($"Unsupported FlatStyle: \"{FlatStyle}\"");
994+
break;
995+
}
989996

990-
_cachedAdapterType = FlatStyle;
997+
_cachedAdapterType = FlatStyle;
998+
}
991999
}
1000+
#pragma warning restore WFO5001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
9921001

9931002
return _adapter;
9941003
}
@@ -1012,6 +1021,14 @@ internal virtual ButtonBaseAdapter CreateStandardAdapter()
10121021
return null;
10131022
}
10141023

1024+
internal virtual ButtonBaseAdapter CreateDarkModeAdapter()
1025+
{
1026+
// When a button-derived class does not have a dedicated DarkMode adapter implementation,
1027+
// we're falling back to the standard adapter, to not _force_ the derived class to implement
1028+
// a dark mode adapter.
1029+
return CreateStandardAdapter();
1030+
}
1031+
10151032
internal virtual StringFormat CreateStringFormat()
10161033
{
10171034
if (Adapter is null)

src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/ButtonBaseAdapter.cs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ internal abstract partial class ButtonBaseAdapter
2323
/// <summary>
2424
/// Returns a darkened color according to the required color contrast ratio.
2525
/// </summary>
26-
private protected static Color GetContrastingBorderColor(Color buttonBorderShadowColor) => Color.FromArgb(
27-
buttonBorderShadowColor.A,
28-
(int)(buttonBorderShadowColor.R * 0.8f),
29-
(int)(buttonBorderShadowColor.G * 0.8f),
30-
(int)(buttonBorderShadowColor.B * 0.8f));
26+
private protected static Color GetContrastingBorderColor(Color buttonBorderShadowColor) =>
27+
Color.FromArgb(
28+
buttonBorderShadowColor.A,
29+
(int)(buttonBorderShadowColor.R * 0.8f),
30+
(int)(buttonBorderShadowColor.G * 0.8f),
31+
(int)(buttonBorderShadowColor.B * 0.8f));
3132

3233
internal void Paint(PaintEventArgs e)
3334
{
@@ -48,6 +49,7 @@ internal void Paint(PaintEventArgs e)
4849
internal virtual Size GetPreferredSizeCore(Size proposedSize)
4950
{
5051
LayoutOptions? options = default;
52+
5153
using (var screen = GdiCache.GetScreenHdc())
5254
using (PaintEventArgs e = new(screen, default))
5355
{
@@ -277,11 +279,10 @@ private void Draw3DBorderRaised(IDeviceContext deviceContext, ref Rectangle boun
277279
hdc.DrawLine(topLeftInsetPen, p2, p3); // Left (up-down)
278280

279281
// Bottom + right inset
280-
281282
using CreatePenScope bottomRightInsetPen = new(
282283
disabledHighContrast
283-
? colors.WindowDisabled
284-
: stockColor ? SystemColors.ControlDark : colors.ButtonShadow);
284+
? colors.WindowDisabled
285+
: stockColor ? SystemColors.ControlDark : colors.ButtonShadow);
285286

286287
p1.Offset(0, -1); // Need to paint last pixel too.
287288
hdc.DrawLine(bottomRightInsetPen, p3, p4); // Bottom (left-right)
@@ -342,6 +343,7 @@ internal static void DrawFlatBorderWithSize(
342343
g.FillRectangle(brush, right);
343344
g.FillRectangle(brush, top);
344345
g.FillRectangle(brush, bottom);
346+
345347
return;
346348
}
347349

@@ -643,15 +645,18 @@ private ColorOptions CommonRender(IDeviceContext deviceContext) =>
643645
Enabled = Control.Enabled
644646
};
645647

646-
protected ColorOptions PaintRender(IDeviceContext deviceContext) => CommonRender(deviceContext);
648+
protected ColorOptions PaintRender(IDeviceContext deviceContext)
649+
=> CommonRender(deviceContext);
647650

648651
internal static ColorOptions PaintFlatRender(Graphics g, Color foreColor, Color backColor, bool enabled) =>
649652
CommonRender(g, foreColor, backColor, enabled);
650653

651-
protected ColorOptions PaintFlatRender(IDeviceContext deviceContext) => CommonRender(deviceContext);
654+
protected ColorOptions PaintFlatRender(IDeviceContext deviceContext)
655+
=> CommonRender(deviceContext);
652656

653657
internal static ColorOptions PaintPopupRender(Graphics g, Color foreColor, Color backColor, bool enabled) =>
654658
CommonRender(g, foreColor, backColor, enabled);
655659

656-
protected ColorOptions PaintPopupRender(IDeviceContext deviceContext) => CommonRender(deviceContext);
660+
protected ColorOptions PaintPopupRender(IDeviceContext deviceContext)
661+
=> CommonRender(deviceContext);
657662
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Drawing;
5+
using System.Windows.Forms.VisualStyles;
6+
7+
namespace System.Windows.Forms.ButtonInternal;
8+
9+
internal class ButtonDarkModeAdapter : ButtonBaseAdapter
10+
{
11+
internal ButtonDarkModeAdapter(ButtonBase control) : base(control) { }
12+
13+
internal override void PaintUp(PaintEventArgs e, CheckState state)
14+
{
15+
var smoothingMode = e.Graphics.SmoothingMode;
16+
e.Graphics.SmoothingMode = Drawing.Drawing2D.SmoothingMode.AntiAlias;
17+
18+
LayoutData layout = CommonLayout().Layout();
19+
20+
ButtonDarkModeRenderer.RenderButton(
21+
e.Graphics,
22+
Control.ClientRectangle,
23+
Control.FlatStyle,
24+
ToPushButtonState(state, Control.Enabled),
25+
Control.IsDefault,
26+
Control.Focused,
27+
Control.ShowFocusCues,
28+
Control.Parent?.BackColor ?? Control.BackColor,
29+
_ => PaintImage(e, layout),
30+
(_, textColor, drawFocus) => PaintField(
31+
e,
32+
layout,
33+
PaintDarkModeRender(e).Calculate(),
34+
textColor,
35+
drawFocus: false)
36+
);
37+
38+
e.Graphics.SmoothingMode = smoothingMode;
39+
}
40+
41+
internal override void PaintDown(PaintEventArgs e, CheckState state)
42+
{
43+
// Set the smoothing mode to AntiAlias for better rendering quality
44+
var smoothingMode = e.Graphics.SmoothingMode;
45+
e.Graphics.SmoothingMode = Drawing.Drawing2D.SmoothingMode.AntiAlias;
46+
47+
LayoutData layout = CommonLayout().Layout();
48+
ButtonDarkModeRenderer.RenderButton(
49+
e.Graphics,
50+
Control.ClientRectangle,
51+
Control.FlatStyle,
52+
PushButtonState.Pressed,
53+
Control.IsDefault,
54+
Control.Focused,
55+
Control.ShowFocusCues,
56+
Control.Parent?.BackColor ?? Control.BackColor,
57+
_ => PaintImage(e, layout),
58+
(_, textColor, drawFocus) => PaintField(
59+
e,
60+
layout,
61+
PaintDarkModeRender(e).Calculate(),
62+
textColor,
63+
drawFocus: false)
64+
);
65+
66+
// Restore the original smoothing mode
67+
e.Graphics.SmoothingMode = smoothingMode;
68+
}
69+
70+
internal override void PaintOver(PaintEventArgs e, CheckState state)
71+
{
72+
// Set the smoothing mode to AntiAlias for better rendering quality
73+
var smoothingMode = e.Graphics.SmoothingMode;
74+
e.Graphics.SmoothingMode = Drawing.Drawing2D.SmoothingMode.AntiAlias;
75+
76+
LayoutData layout = CommonLayout().Layout();
77+
ButtonDarkModeRenderer.RenderButton(
78+
e.Graphics,
79+
Control.ClientRectangle,
80+
Control.FlatStyle,
81+
PushButtonState.Hot,
82+
Control.IsDefault,
83+
Control.Focused,
84+
Control.ShowFocusCues,
85+
Control.Parent?.BackColor ?? Control.BackColor,
86+
_ => PaintImage(e, layout),
87+
(_, textColor, drawFocus) => PaintField(
88+
e,
89+
layout,
90+
PaintDarkModeRender(e).Calculate(),
91+
textColor,
92+
drawFocus: false)
93+
);
94+
95+
// Restore the original smoothing mode
96+
e.Graphics.SmoothingMode = smoothingMode;
97+
}
98+
99+
protected override LayoutOptions Layout(PaintEventArgs e) => CommonLayout();
100+
101+
private new LayoutOptions CommonLayout()
102+
{
103+
LayoutOptions layout = base.CommonLayout();
104+
layout.FocusOddEvenFixup = false;
105+
layout.ShadowedText = false;
106+
107+
return layout;
108+
}
109+
110+
private ColorOptions PaintDarkModeRender(IDeviceContext deviceContext) =>
111+
new(deviceContext, Control.ForeColor, Control.BackColor)
112+
{
113+
Enabled = Control.Enabled
114+
};
115+
116+
private static PushButtonState ToPushButtonState(CheckState state, bool enabled)
117+
{
118+
return !enabled
119+
? PushButtonState.Disabled
120+
: state switch
121+
{
122+
CheckState.Unchecked => PushButtonState.Normal,
123+
CheckState.Checked => PushButtonState.Pressed,
124+
CheckState.Indeterminate => PushButtonState.Hot,
125+
_ => PushButtonState.Normal
126+
};
127+
}
128+
}

src/System.Windows.Forms/System/Windows/Forms/Controls/Buttons/ButtonInternal/ButtonFlatAdapter.cs

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -223,23 +223,15 @@ internal override void PaintOver(PaintEventArgs e, CheckState state)
223223

224224
Rectangle r = Control.ClientRectangle;
225225

226-
Color backColor;
227-
if (!Control.FlatAppearance.MouseOverBackColor.IsEmpty)
228-
{
229-
backColor = Control.FlatAppearance.MouseOverBackColor;
230-
}
231-
else if (!Control.FlatAppearance.CheckedBackColor.IsEmpty)
232-
{
233-
backColor = state is CheckState.Checked or CheckState.Indeterminate
234-
? Control.FlatAppearance.CheckedBackColor.MixColor(colors.LowButtonFace)
235-
: colors.LowButtonFace;
236-
}
237-
else
238-
{
239-
backColor = state is CheckState.Indeterminate
240-
? colors.ButtonFace.MixColor(colors.LowButtonFace)
241-
: colors.LowButtonFace;
242-
}
226+
Color backColor = !Control.FlatAppearance.MouseOverBackColor.IsEmpty
227+
? Control.FlatAppearance.MouseOverBackColor
228+
: !Control.FlatAppearance.CheckedBackColor.IsEmpty
229+
? state is CheckState.Checked or CheckState.Indeterminate
230+
? Control.FlatAppearance.CheckedBackColor.MixColor(colors.LowButtonFace)
231+
: colors.LowButtonFace
232+
: state is CheckState.Indeterminate
233+
? colors.ButtonFace.MixColor(colors.LowButtonFace)
234+
: colors.LowButtonFace;
243235

244236
PaintBackground(e, r, IsHighContrastHighlighted() ? SystemColors.Highlight : backColor);
245237

@@ -249,11 +241,14 @@ internal override void PaintOver(PaintEventArgs e, CheckState state)
249241
}
250242

251243
PaintImage(e, layout);
244+
252245
PaintField(
253246
e,
254247
layout,
255248
colors,
256-
IsHighContrastHighlighted() ? SystemColors.HighlightText : colors.WindowText,
249+
IsHighContrastHighlighted()
250+
? SystemColors.HighlightText
251+
: colors.WindowText,
257252
drawFocus: false);
258253

259254
if (Control.Focused && Control.ShowFocusCues)

0 commit comments

Comments
 (0)