2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
4
using System . Drawing ;
5
- using System . Drawing . Drawing2D ;
5
+ using System . Windows . Forms . VisualStyles ;
6
6
7
7
namespace System . Windows . Forms . ButtonInternal ;
8
8
9
9
internal class ButtonDarkModeAdapter : ButtonBaseAdapter
10
10
{
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 ;
13
16
14
17
internal ButtonDarkModeAdapter ( ButtonBase control ) : base ( control ) { }
15
18
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
-
30
19
internal override void PaintUp ( PaintEventArgs e , CheckState state )
31
20
{
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 ;
68
45
}
69
46
70
47
internal override void PaintDown ( PaintEventArgs e , CheckState state )
71
48
{
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 ;
110
74
}
111
75
112
76
internal override void PaintOver ( PaintEventArgs e , CheckState state )
113
77
{
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 ;
153
103
}
154
104
155
- protected override LayoutOptions Layout ( PaintEventArgs e ) =>
156
- PaintDarkModeLayout ( up : true , check : false , BorderSize ) ;
105
+ protected override LayoutOptions Layout ( PaintEventArgs e ) => CommonLayout ( ) ;
157
106
158
- private LayoutOptions PaintDarkModeLayout ( bool up , bool check , int borderSize )
107
+ private new LayoutOptions CommonLayout ( )
159
108
{
160
- LayoutOptions layout = CommonLayout ( ) ;
161
- layout . BorderSize = borderSize + ( check ? 1 : 0 ) ;
162
- layout . PaddingSize = check ? 1 : 2 ;
109
+ LayoutOptions layout = base . CommonLayout ( ) ;
163
110
layout . FocusOddEvenFixup = false ;
164
- layout . TextOffset = ! up ;
165
- layout . ShadowedText = false ; // Dark mode doesn't use shadowed text
111
+ layout . ShadowedText = false ;
166
112
167
113
return layout ;
168
114
}
@@ -173,123 +119,16 @@ private ColorOptions PaintDarkModeRender(IDeviceContext deviceContext) =>
173
119
Enabled = Control . Enabled
174
120
} ;
175
121
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 )
223
123
{
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
+ } ;
294
133
}
295
134
}
0 commit comments