22using System . Collections . Generic ;
33using System . ComponentModel ;
44using System . Drawing ;
5+ using System . Drawing . Drawing2D ;
56using System . Linq ;
67using System . Reflection ;
78using System . Text ;
@@ -12,6 +13,9 @@ namespace CapsLockIndicatorV3
1213{
1314 public class BetterCheckBox : CheckBox
1415 {
16+ const int WIN11_CORNER_RADIUS = 2 ;
17+
18+ private bool isWindows11 = Versions . IsWindows11 ;
1519 private bool _darkMode ;
1620
1721 [ DesignerSerializationVisibility ( DesignerSerializationVisibility . Hidden ) , Browsable ( false ) ]
@@ -40,6 +44,7 @@ private TextFormatFlags TextRenderingFlags
4044 }
4145 }
4246
47+ // Win10 style colors
4348 private static readonly Color N_Border = Color . FromArgb ( 137 , 137 , 137 ) ;
4449 private static readonly Color N_Background = Color . FromArgb ( 0 , 0 , 0 ) ;
4550 private static readonly Color N_Check = Color . FromArgb ( 222 , 222 , 222 ) ;
@@ -50,6 +55,18 @@ private TextFormatFlags TextRenderingFlags
5055 private static readonly Color A_Background = Color . FromArgb ( 0 , 9 , 16 ) ;
5156 private static readonly Color A_Check = Color . FromArgb ( 120 , 130 , 187 ) ;
5257
58+ // Win11 style colors
59+ private static readonly Color N_U_Background_11 = Color . FromArgb ( unchecked ( ( int ) 0xff383838 ) ) ;
60+ private static readonly Color H_U_Background_11 = Color . FromArgb ( unchecked ( ( int ) 0xff424242 ) ) ;
61+ private static readonly Color A_U_Background_11 = Color . FromArgb ( unchecked ( ( int ) 0xff202020 ) ) ;
62+ private static readonly Color N_C_Background_11 = Color . FromArgb ( unchecked ( ( int ) 0xff8FCBFF ) ) ;
63+ private static readonly Color H_C_Background_11 = Color . FromArgb ( unchecked ( ( int ) 0xffBCE0FF ) ) ;
64+ private static readonly Color A_C_Background_11 = Color . FromArgb ( unchecked ( ( int ) 0xff74ABDC ) ) ;
65+ private static readonly Color N_C_Check_11 = Color . FromArgb ( unchecked ( ( int ) 0xff191919 ) ) ;
66+ private static readonly Color H_C_Check_11 = Color . FromArgb ( unchecked ( ( int ) 0xff202020 ) ) ;
67+ private static readonly Color A_C_Check_11 = Color . FromArgb ( unchecked ( ( int ) 0xff101010 ) ) ;
68+ private static readonly Color Border_11 = Color . FromArgb ( unchecked ( ( int ) 0xff8b8b8b ) ) ;
69+
5370 public BetterCheckBox ( ) : base ( )
5471 {
5572 DoubleBuffered = true ;
@@ -90,37 +107,14 @@ protected override void OnPaint(PaintEventArgs e)
90107 }
91108 else
92109 {
93- var scaling = DPIHelper . GetScalingFactorPercent ( Handle ) ;
94- var border = N_Border ;
95- var background = N_Background ;
96- var check = N_Check ;
97- var fg = ForeColor ;
98-
99- if ( ! Enabled )
100- {
101- border = N_Border . Blend ( bgColor , 0.5d ) ;
102- background = N_Background . Blend ( bgColor , 0.5d ) ;
103- check = N_Check . Blend ( bgColor , 0.5d ) ;
104- fg = ForeColor . Blend ( bgColor , 0.5d ) ;
105- }
106- else if ( Native . GetButtonFlag ( this , Native . ButtonFlags . MouseDown ) )
107- {
108- border = A_Border ;
109- background = A_Background ;
110- check = A_Check ;
111- }
112- else if ( Native . GetButtonFlag ( this , Native . ButtonFlags . MouseOver ) )
113- {
114- border = H_Border ;
115- background = H_Background ;
116- check = H_Check ;
117- }
110+ Color border , background , check ;
118111
112+ var fg = ForeColor ;
113+ var scaling = DPIHelper . GetScalingFactorPercent ( Handle ) ;
119114 int boxSize = ( int ) ( 13 * scaling ) ;
120115 int boxRectInnerMargin = ( int ) ( 2 * scaling ) ;
121116 int checkPenWidth = ( int ) ( 2 * scaling ) ;
122117 int textMargin = ( int ) ( 3 * scaling ) ;
123-
124118 var boxY = Height / 2 - boxSize / 2 ;
125119
126120 var boxRect = new Rectangle ( 0 , boxY , boxSize , boxSize ) ;
@@ -133,33 +127,144 @@ protected override void OnPaint(PaintEventArgs e)
133127 boxRectInner . Width -= 2 * boxRectInnerMargin ;
134128 boxRectInner . Height -= 2 * boxRectInnerMargin ;
135129
136- var checkPoints = new Point [ ]
130+ var isChecked = CheckState != CheckState . Unchecked ;
131+ var radius = ( int ) ( WIN11_CORNER_RADIUS * scaling ) ;
132+
133+ if ( isWindows11 )
137134 {
138- new Point ( boxRectInner . X , boxRectInner . Y + boxRectInner . Height / 2 ) ,
139- new Point ( boxRectInner . X + boxRectInner . Width / 3 , boxRectInner . Bottom - checkPenWidth ) ,
140- new Point ( boxRectInner . Right - ( checkPenWidth / 2 ) , boxRectInner . Y )
141- } ;
135+ e . Graphics . InterpolationMode = InterpolationMode . HighQualityBilinear ;
136+ e . Graphics . CompositingQuality = CompositingQuality . HighQuality ;
137+ e . Graphics . PixelOffsetMode = PixelOffsetMode . HighQuality ;
138+ e . Graphics . SmoothingMode = SmoothingMode . AntiAlias ;
142139
143- e . Graphics . Clear ( bgColor ) ;
140+ background = isChecked ? N_C_Background_11 : N_U_Background_11 ;
141+ check = N_C_Check_11 ;
142+ border = Border_11 ;
144143
145- using ( var b = new SolidBrush ( background ) )
146- e . Graphics . FillRectangle ( b , boxRect ) ;
144+ if ( ! Enabled )
145+ {
146+ border = border . Blend ( bgColor , 0.5d ) ;
147+ background = background . Blend ( bgColor , 0.5d ) ;
148+ check = check . Blend ( bgColor , 0.5d ) ;
149+ fg = ForeColor . Blend ( bgColor , 0.5d ) ;
150+ }
151+ else if ( Native . GetButtonFlag ( this , Native . ButtonFlags . MouseDown ) )
152+ {
153+ background = isChecked ? A_C_Background_11 : A_U_Background_11 ;
154+ check = A_C_Check_11 ;
155+ }
156+ else if ( Native . GetButtonFlag ( this , Native . ButtonFlags . MouseOver ) )
157+ {
158+ background = isChecked ? H_C_Background_11 : H_U_Background_11 ;
159+ check = H_C_Check_11 ;
160+ }
147161
148- using ( var p = new Pen ( border ) )
149- e . Graphics . DrawRectangle ( p , boxRectPen ) ;
162+ var innerBoxSize = ( int ) Math . Ceiling ( boxRect . Width / 2d ) ;
150163
151- if ( CheckState == CheckState . Indeterminate )
152- {
153- using ( var b = new SolidBrush ( check ) )
154- e . Graphics . FillRectangle ( b , boxRectInner ) ;
164+ boxRectInner = new Rectangle (
165+ boxRect . X + innerBoxSize / 2 ,
166+ boxRect . Y + innerBoxSize / 2 ,
167+ innerBoxSize ,
168+ innerBoxSize
169+ ) ;
170+ var boxRectFPen = new RectangleF (
171+ boxRect . X + 0.5f ,
172+ boxRect . Y + 0.5f ,
173+ boxRect . Width - 1.0f ,
174+ boxRect . Height - 1.0f
175+ ) ;
176+ //checkPenWidth = (int)scaling / 2;
177+
178+ var checkPoints = new Point [ ]
179+ {
180+ new Point ( boxRectInner . X , boxRectInner . Y + boxRectInner . Height / 2 ) ,
181+ new Point ( boxRectInner . X + boxRectInner . Width / 3 , boxRectInner . Bottom - checkPenWidth ) ,
182+ new Point ( boxRectInner . Right - ( checkPenWidth / 2 ) , boxRectInner . Y + ( boxRectInner . Height / 4 ) )
183+ } ;
184+
185+ e . Graphics . Clear ( bgColor ) ;
186+
187+ using ( var b = new SolidBrush ( background ) )
188+ FillRoundedRectangle ( e . Graphics , b , boxRect , radius ) ;
189+
190+ if ( ! isChecked )
191+ using ( var p = new Pen ( border ) { Alignment = PenAlignment . Center } )
192+ DrawRoundedRectangle ( e . Graphics , p , boxRectFPen , radius ) ;
193+
194+ if ( CheckState == CheckState . Indeterminate )
195+ {
196+ //using (var b = new SolidBrush(check))
197+ // FillRoundedRectangle(e.Graphics, b, boxRectInner, WIN11_CORNER_RADIUS);
198+ using ( var b = new SolidBrush ( check ) )
199+ e . Graphics . FillRectangle ( b , new Rectangle (
200+ boxRectInner . X ,
201+ boxRectInner . Y + ( boxRectInner . Height - checkPenWidth ) / 2 ,
202+ boxRectInner . Width ,
203+ checkPenWidth
204+ ) ) ;
205+ }
206+ else if ( CheckState == CheckState . Checked )
207+ {
208+ e . Graphics . SmoothingMode = SmoothingMode . HighQuality ;
209+ e . Graphics . PixelOffsetMode = PixelOffsetMode . HighQuality ;
210+ using ( var p = new Pen ( check , checkPenWidth ) )
211+ e . Graphics . DrawLines ( p , checkPoints ) ;
212+ }
155213 }
156- else if ( CheckState == CheckState . Checked )
214+ else
157215 {
158- var _m = e . Graphics . SmoothingMode ;
159- e . Graphics . SmoothingMode = System . Drawing . Drawing2D . SmoothingMode . AntiAlias ;
160- using ( var p = new Pen ( check , checkPenWidth ) )
161- e . Graphics . DrawLines ( p , checkPoints ) ;
162- e . Graphics . SmoothingMode = _m ;
216+ border = N_Border ;
217+ background = isWindows11 ? N_U_Background_11 : N_Background ;
218+ check = isWindows11 ? N_C_Check_11 : N_Check ;
219+
220+ if ( ! Enabled )
221+ {
222+ border = background . Blend ( N_Border , 0.5d ) ;
223+ background = background . Blend ( N_Background , 0.5d ) ;
224+ check = check . Blend ( N_Check , 0.5d ) ;
225+ fg = ForeColor . Blend ( bgColor , 0.5d ) ;
226+ }
227+ else if ( Native . GetButtonFlag ( this , Native . ButtonFlags . MouseDown ) )
228+ {
229+ border = A_Border ;
230+ background = A_Background ;
231+ check = A_Check ;
232+ }
233+ else if ( Native . GetButtonFlag ( this , Native . ButtonFlags . MouseOver ) )
234+ {
235+ border = H_Border ;
236+ background = H_Background ;
237+ check = H_Check ;
238+ }
239+
240+ var checkPoints = new Point [ ]
241+ {
242+ new Point ( boxRectInner . X , boxRectInner . Y + boxRectInner . Height / 2 ) ,
243+ new Point ( boxRectInner . X + boxRectInner . Width / 3 , boxRectInner . Bottom - checkPenWidth ) ,
244+ new Point ( boxRectInner . Right - ( checkPenWidth / 2 ) , boxRectInner . Y )
245+ } ;
246+
247+ e . Graphics . Clear ( bgColor ) ;
248+
249+ using ( var b = new SolidBrush ( background ) )
250+ e . Graphics . FillRectangle ( b , boxRect ) ;
251+
252+ using ( var p = new Pen ( border ) )
253+ e . Graphics . DrawRectangle ( p , boxRectPen ) ;
254+
255+ if ( CheckState == CheckState . Indeterminate )
256+ {
257+ using ( var b = new SolidBrush ( check ) )
258+ e . Graphics . FillRectangle ( b , boxRectInner ) ;
259+ }
260+ else if ( CheckState == CheckState . Checked )
261+ {
262+ var _m = e . Graphics . SmoothingMode ;
263+ e . Graphics . SmoothingMode = SmoothingMode . AntiAlias ;
264+ using ( var p = new Pen ( check , checkPenWidth ) )
265+ e . Graphics . DrawLines ( p , checkPoints ) ;
266+ e . Graphics . SmoothingMode = _m ;
267+ }
163268 }
164269
165270 var textSz = TextRenderer . MeasureText ( e . Graphics , Text , Font ) ;
@@ -176,5 +281,106 @@ protected override void OnPaint(PaintEventArgs e)
176281 }
177282 }
178283 }
284+ public static GraphicsPath RoundedRect ( Rectangle bounds , int radius )
285+ {
286+ int diameter = radius * 2 ;
287+ Size size = new Size ( diameter , diameter ) ;
288+ Rectangle arc = new Rectangle ( bounds . Location , size ) ;
289+ GraphicsPath path = new GraphicsPath ( ) ;
290+
291+ if ( radius == 0 )
292+ {
293+ path . AddRectangle ( bounds ) ;
294+ return path ;
295+ }
296+
297+ // top left arc
298+ path . AddArc ( arc , 180 , 90 ) ;
299+
300+ // top right arc
301+ arc . X = bounds . Right - diameter ;
302+ path . AddArc ( arc , 270 , 90 ) ;
303+
304+ // bottom right arc
305+ arc . Y = bounds . Bottom - diameter ;
306+ path . AddArc ( arc , 0 , 90 ) ;
307+
308+ // bottom left arc
309+ arc . X = bounds . Left ;
310+ path . AddArc ( arc , 90 , 90 ) ;
311+
312+ path . CloseFigure ( ) ;
313+ return path ;
314+ }
315+ public static GraphicsPath RoundedRect ( RectangleF bounds , int radius )
316+ {
317+ int diameter = radius * 2 ;
318+ Size size = new Size ( diameter , diameter ) ;
319+ RectangleF arc = new RectangleF ( bounds . Location , size ) ;
320+ GraphicsPath path = new GraphicsPath ( ) ;
321+
322+ if ( radius == 0 )
323+ {
324+ path . AddRectangle ( bounds ) ;
325+ return path ;
326+ }
327+
328+ // top left arc
329+ path . AddArc ( arc , 180 , 90 ) ;
330+
331+ // top right arc
332+ arc . X = bounds . Right - diameter ;
333+ path . AddArc ( arc , 270 , 90 ) ;
334+
335+ // bottom right arc
336+ arc . Y = bounds . Bottom - diameter ;
337+ path . AddArc ( arc , 0 , 90 ) ;
338+
339+ // bottom left arc
340+ arc . X = bounds . Left ;
341+ path . AddArc ( arc , 90 , 90 ) ;
342+
343+ path . CloseFigure ( ) ;
344+ return path ;
345+ }
346+
347+ public static void DrawRoundedRectangle ( Graphics graphics , Pen pen , Rectangle bounds , int cornerRadius )
348+ {
349+ if ( graphics == null )
350+ throw new ArgumentNullException ( "graphics" ) ;
351+ if ( pen == null )
352+ throw new ArgumentNullException ( "pen" ) ;
353+
354+ using ( GraphicsPath path = RoundedRect ( bounds , cornerRadius ) )
355+ {
356+ graphics . DrawPath ( pen , path ) ;
357+ }
358+ }
359+
360+ public static void DrawRoundedRectangle ( Graphics graphics , Pen pen , RectangleF bounds , int cornerRadius )
361+ {
362+ if ( graphics == null )
363+ throw new ArgumentNullException ( "graphics" ) ;
364+ if ( pen == null )
365+ throw new ArgumentNullException ( "pen" ) ;
366+
367+ using ( GraphicsPath path = RoundedRect ( bounds , cornerRadius ) )
368+ {
369+ graphics . DrawPath ( pen , path ) ;
370+ }
371+ }
372+
373+ public static void FillRoundedRectangle ( Graphics graphics , Brush brush , Rectangle bounds , int cornerRadius )
374+ {
375+ if ( graphics == null )
376+ throw new ArgumentNullException ( "graphics" ) ;
377+ if ( brush == null )
378+ throw new ArgumentNullException ( "brush" ) ;
379+
380+ using ( GraphicsPath path = RoundedRect ( bounds , cornerRadius ) )
381+ {
382+ graphics . FillPath ( brush , path ) ;
383+ }
384+ }
179385 }
180386}
0 commit comments