Skip to content

Commit 2530362

Browse files
Update rendering approaches.
1 parent b99afdb commit 2530362

File tree

8 files changed

+636
-912
lines changed

8 files changed

+636
-912
lines changed

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

Lines changed: 94 additions & 255 deletions
Original file line numberDiff line numberDiff line change
@@ -2,167 +2,113 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Drawing;
5-
using System.Drawing.Drawing2D;
5+
using System.Windows.Forms.VisualStyles;
66

77
namespace System.Windows.Forms.ButtonInternal;
88

99
internal class ButtonDarkModeAdapter : ButtonBaseAdapter
1010
{
11-
private const int BorderSize = 1;
12-
private const int CornerRadius = 5;
11+
// Magic numbers for PushButtonState mapping
12+
private const PushButtonState DisabledPushButtonState = PushButtonState.Disabled;
13+
private const PushButtonState NormalPushButtonState = PushButtonState.Normal;
14+
private const PushButtonState PressedPushButtonState = PushButtonState.Pressed;
15+
private const PushButtonState HotPushButtonState = PushButtonState.Hot;
1316

1417
internal ButtonDarkModeAdapter(ButtonBase control) : base(control) { }
1518

16-
private static void PaintBackground(PaintEventArgs e, Rectangle r, Color backColor)
17-
{
18-
// Save original smoothing mode and set to anti-alias for smooth corners
19-
SmoothingMode originalMode = e.Graphics.SmoothingMode;
20-
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
21-
22-
using GraphicsPath path = GetRoundedRectanglePath(r, CornerRadius);
23-
using SolidBrush brush = new(backColor);
24-
e.Graphics.FillPath(brush, path);
25-
26-
// Restore original smoothing mode
27-
e.Graphics.SmoothingMode = originalMode;
28-
}
29-
3019
internal override void PaintUp(PaintEventArgs e, CheckState state)
3120
{
32-
bool isDefault = Control.IsDefault;
33-
ColorData colors = PaintDarkModeRender(e).Calculate();
34-
LayoutData layout = PaintDarkModeLayout(
35-
up: true,
36-
check: state == CheckState.Checked,
37-
borderSize: BorderSize).Layout();
38-
39-
Rectangle r = Control.ClientRectangle;
40-
41-
// Determine background color based on button state
42-
Color backColor = GetBackgroundColor(state, isDefault, normal: true);
43-
44-
PaintBackground(e, r, backColor);
45-
46-
if (isDefault)
47-
{
48-
r.Inflate(-1, -1);
49-
}
50-
51-
PaintImage(e, layout);
52-
PaintField(
53-
e,
54-
layout,
55-
colors,
56-
GetTextColor(isDefault, Control.Enabled),
57-
drawFocus: false);
58-
59-
if (Control.Focused && Control.ShowFocusCues)
60-
{
61-
DrawDarkModeFocus(e, layout.Focus, isDefault);
62-
}
63-
64-
if (!(Control.IsDefault && Control.Focused && (BorderSize == 0)))
65-
{
66-
DrawDarkModeBorder(e, r, isDefault, pressed: false);
67-
}
21+
var smoothingMode = e.Graphics.SmoothingMode;
22+
e.Graphics.SmoothingMode = Drawing.Drawing2D.SmoothingMode.AntiAlias;
23+
24+
LayoutData layout = CommonLayout().Layout();
25+
26+
ButtonDarkModeRenderer.RenderButton(
27+
e.Graphics,
28+
Control.ClientRectangle,
29+
Control.FlatStyle,
30+
ToPushButtonState(state, Control.Enabled),
31+
Control.IsDefault,
32+
Control.Focused,
33+
Control.ShowFocusCues,
34+
Control.Parent?.BackColor ?? Control.BackColor,
35+
_ => PaintImage(e, layout),
36+
(_, textColor, drawFocus) => PaintField(
37+
e,
38+
layout,
39+
PaintDarkModeRender(e).Calculate(),
40+
textColor,
41+
drawFocus: false)
42+
);
43+
44+
e.Graphics.SmoothingMode = smoothingMode;
6845
}
6946

7047
internal override void PaintDown(PaintEventArgs e, CheckState state)
7148
{
72-
bool isDefault = Control.IsDefault;
73-
74-
ColorData colors = PaintDarkModeRender(e).Calculate();
75-
LayoutData layout = PaintDarkModeLayout(
76-
up: false,
77-
check: state == CheckState.Checked,
78-
borderSize: BorderSize).Layout();
79-
80-
Rectangle r = Control.ClientRectangle;
81-
82-
// Determine background color based on button state
83-
Color backColor = GetBackgroundColor(state, isDefault, normal: false);
84-
85-
PaintBackground(e, r, backColor);
86-
87-
if (isDefault)
88-
{
89-
r.Inflate(-1, -1);
90-
}
91-
92-
PaintImage(e, layout);
93-
94-
PaintField(
95-
e,
96-
layout,
97-
colors,
98-
GetTextColor(isDefault, Control.Enabled),
99-
drawFocus: false);
100-
101-
if (Control.Focused && Control.ShowFocusCues)
102-
{
103-
DrawDarkModeFocus(e, layout.Focus, isDefault);
104-
}
105-
106-
if (!(Control.IsDefault && Control.Focused && (BorderSize == 0)))
107-
{
108-
DrawDarkModeBorder(e, r, isDefault, pressed: true);
109-
}
49+
// Set the smoothing mode to AntiAlias for better rendering quality
50+
var smoothingMode = e.Graphics.SmoothingMode;
51+
e.Graphics.SmoothingMode = Drawing.Drawing2D.SmoothingMode.AntiAlias;
52+
53+
LayoutData layout = CommonLayout().Layout();
54+
ButtonDarkModeRenderer.RenderButton(
55+
e.Graphics,
56+
Control.ClientRectangle,
57+
Control.FlatStyle,
58+
PressedPushButtonState,
59+
Control.IsDefault,
60+
Control.Focused,
61+
Control.ShowFocusCues,
62+
Control.Parent?.BackColor ?? Control.BackColor,
63+
_ => PaintImage(e, layout),
64+
(_, textColor, drawFocus) => PaintField(
65+
e,
66+
layout,
67+
PaintDarkModeRender(e).Calculate(),
68+
textColor,
69+
drawFocus: false)
70+
);
71+
72+
// Restore the original smoothing mode
73+
e.Graphics.SmoothingMode = smoothingMode;
11074
}
11175

11276
internal override void PaintOver(PaintEventArgs e, CheckState state)
11377
{
114-
bool isDefault = Control.IsDefault;
115-
ColorData colors = PaintDarkModeRender(e).Calculate();
116-
117-
LayoutData layout = PaintDarkModeLayout(
118-
up: true,
119-
check: state == CheckState.Checked,
120-
borderSize: BorderSize).Layout();
121-
122-
Rectangle r = Control.ClientRectangle;
123-
124-
// Get hover background color
125-
Color backColor = isDefault
126-
? ButtonDarkModeRenderer.DarkModeButtonColors.DefaultHoverBackgroundColor
127-
: ButtonDarkModeRenderer.DarkModeButtonColors.HoverBackgroundColor;
128-
129-
PaintBackground(e, r, backColor);
130-
131-
if (isDefault)
132-
{
133-
r.Inflate(-1, -1);
134-
}
135-
136-
PaintImage(e, layout);
137-
PaintField(
138-
e,
139-
layout,
140-
colors,
141-
GetTextColor(isDefault, Control.Enabled),
142-
drawFocus: false);
143-
144-
if (Control.Focused && Control.ShowFocusCues)
145-
{
146-
DrawDarkModeFocus(e, layout.Focus, isDefault);
147-
}
148-
149-
if (!(Control.IsDefault && Control.Focused && (BorderSize == 0)))
150-
{
151-
DrawDarkModeBorder(e, r, isDefault, pressed: false);
152-
}
78+
// Set the smoothing mode to AntiAlias for better rendering quality
79+
var smoothingMode = e.Graphics.SmoothingMode;
80+
e.Graphics.SmoothingMode = Drawing.Drawing2D.SmoothingMode.AntiAlias;
81+
82+
LayoutData layout = CommonLayout().Layout();
83+
ButtonDarkModeRenderer.RenderButton(
84+
e.Graphics,
85+
Control.ClientRectangle,
86+
Control.FlatStyle,
87+
HotPushButtonState,
88+
Control.IsDefault,
89+
Control.Focused,
90+
Control.ShowFocusCues,
91+
Control.Parent?.BackColor ?? Control.BackColor,
92+
_ => PaintImage(e, layout),
93+
(_, textColor, drawFocus) => PaintField(
94+
e,
95+
layout,
96+
PaintDarkModeRender(e).Calculate(),
97+
textColor,
98+
drawFocus: false)
99+
);
100+
101+
// Restore the original smoothing mode
102+
e.Graphics.SmoothingMode = smoothingMode;
153103
}
154104

155-
protected override LayoutOptions Layout(PaintEventArgs e) =>
156-
PaintDarkModeLayout(up: true, check: false, BorderSize);
105+
protected override LayoutOptions Layout(PaintEventArgs e) => CommonLayout();
157106

158-
private LayoutOptions PaintDarkModeLayout(bool up, bool check, int borderSize)
107+
private new LayoutOptions CommonLayout()
159108
{
160-
LayoutOptions layout = CommonLayout();
161-
layout.BorderSize = borderSize + (check ? 1 : 0);
162-
layout.PaddingSize = check ? 1 : 2;
109+
LayoutOptions layout = base.CommonLayout();
163110
layout.FocusOddEvenFixup = false;
164-
layout.TextOffset = !up;
165-
layout.ShadowedText = false; // Dark mode doesn't use shadowed text
111+
layout.ShadowedText = false;
166112

167113
return layout;
168114
}
@@ -173,123 +119,16 @@ private ColorOptions PaintDarkModeRender(IDeviceContext deviceContext) =>
173119
Enabled = Control.Enabled
174120
};
175121

176-
private Color GetBackgroundColor(CheckState state, bool isDefault, bool normal)
177-
{
178-
if (!Control.Enabled)
179-
{
180-
return isDefault
181-
? ButtonDarkModeRenderer.DarkModeButtonColors.DefaultDisabledBackgroundColor
182-
: ButtonDarkModeRenderer.DarkModeButtonColors.DisabledBackgroundColor;
183-
}
184-
185-
if (!normal) // Pressed state
186-
{
187-
return isDefault
188-
? ButtonDarkModeRenderer.DarkModeButtonColors.DefaultPressedBackgroundColor
189-
: ButtonDarkModeRenderer.DarkModeButtonColors.PressedBackgroundColor;
190-
}
191-
192-
// Normal state
193-
switch (state)
194-
{
195-
case CheckState.Checked:
196-
return isDefault
197-
? ButtonDarkModeRenderer.DarkModeButtonColors.DefaultBackgroundColor
198-
: ButtonDarkModeRenderer.DarkModeButtonColors.NormalBackgroundColor;
199-
case CheckState.Indeterminate:
200-
Color baseColor = isDefault
201-
? ButtonDarkModeRenderer.DarkModeButtonColors.DefaultBackgroundColor
202-
: ButtonDarkModeRenderer.DarkModeButtonColors.NormalBackgroundColor;
203-
204-
return Color.FromArgb(
205-
baseColor.R + 10,
206-
baseColor.G + 10,
207-
baseColor.B + 10);
208-
default:
209-
return isDefault
210-
? ButtonDarkModeRenderer.DarkModeButtonColors.DefaultBackgroundColor
211-
: ButtonDarkModeRenderer.DarkModeButtonColors.NormalBackgroundColor;
212-
}
213-
}
214-
215-
private static Color GetTextColor(bool isDefault, bool enabled) =>
216-
!enabled
217-
? ButtonDarkModeRenderer.DarkModeButtonColors.DisabledTextColor
218-
: isDefault
219-
? ButtonDarkModeRenderer.DarkModeButtonColors.DefaultTextColor
220-
: ButtonDarkModeRenderer.DarkModeButtonColors.NormalTextColor;
221-
222-
private static void DrawDarkModeBorder(PaintEventArgs e, Rectangle r, bool isDefault, bool pressed)
122+
private static PushButtonState ToPushButtonState(CheckState state, bool enabled)
223123
{
224-
SmoothingMode originalMode = e.Graphics.SmoothingMode;
225-
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
226-
227-
using GraphicsPath path = GetRoundedRectanglePath(r, CornerRadius);
228-
229-
Color borderColor = isDefault
230-
? Color.FromArgb(
231-
red: ButtonDarkModeRenderer.DarkModeButtonColors.DefaultBackgroundColor.R - 20,
232-
green: ButtonDarkModeRenderer.DarkModeButtonColors.DefaultBackgroundColor.G - 10,
233-
blue: ButtonDarkModeRenderer.DarkModeButtonColors.DefaultBackgroundColor.B - 30)
234-
: pressed
235-
? ButtonDarkModeRenderer.DarkModeButtonColors.BottomRightBorderColor
236-
: ButtonDarkModeRenderer.DarkModeButtonColors.SingleBorderColor;
237-
238-
using Pen borderPen = new(borderColor);
239-
e.Graphics.DrawPath(borderPen, path);
240-
241-
// Restore original smoothing mode
242-
e.Graphics.SmoothingMode = originalMode;
243-
}
244-
245-
private static void DrawDarkModeFocus(PaintEventArgs e, Rectangle focusRect, bool isDefault)
246-
{
247-
SmoothingMode originalMode = e.Graphics.SmoothingMode;
248-
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
249-
250-
// Create a slightly smaller rectangle for the focus indicator (2px inside)
251-
Rectangle innerRect = Rectangle.Inflate(focusRect, -2, -2);
252-
253-
// Get appropriate focus color
254-
Color focusColor = isDefault
255-
? ButtonDarkModeRenderer.DarkModeButtonColors.DefaultFocusIndicatorColor
256-
: ButtonDarkModeRenderer.DarkModeButtonColors.FocusIndicatorColor;
257-
258-
using Pen focusPen = new(focusColor)
259-
{
260-
DashStyle = DashStyle.Dot
261-
};
262-
263-
// Draw the focus rectangle with rounded corners
264-
using GraphicsPath focusPath = GetRoundedRectanglePath(innerRect, 3);
265-
e.Graphics.DrawPath(focusPen, focusPath);
266-
267-
// Restore original smoothing mode
268-
e.Graphics.SmoothingMode = originalMode;
269-
}
270-
271-
private static GraphicsPath GetRoundedRectanglePath(Rectangle bounds, int radius)
272-
{
273-
GraphicsPath path = new();
274-
int diameter = radius * 2;
275-
Rectangle arcRect = new(bounds.Location, new Size(diameter, diameter));
276-
277-
// Top left corner
278-
path.AddArc(arcRect, 180, 90);
279-
280-
// Top right corner
281-
arcRect.X = bounds.Right - diameter;
282-
path.AddArc(arcRect, 270, 90);
283-
284-
// Bottom right corner
285-
arcRect.Y = bounds.Bottom - diameter;
286-
path.AddArc(arcRect, 0, 90);
287-
288-
// Bottom left corner
289-
arcRect.X = bounds.Left;
290-
path.AddArc(arcRect, 90, 90);
291-
292-
path.CloseFigure();
293-
return path;
124+
return !enabled
125+
? DisabledPushButtonState
126+
: state switch
127+
{
128+
CheckState.Unchecked => NormalPushButtonState,
129+
CheckState.Checked => PressedPushButtonState,
130+
CheckState.Indeterminate => HotPushButtonState,
131+
_ => NormalPushButtonState
132+
};
294133
}
295134
}

0 commit comments

Comments
 (0)