1 // =================================
  2 // Copyright (c) 2024 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 using System;
  7 
  8 namespace System.Windows
  9 {
 10     public class delegate void CheckedChangedEventHandler();
 11 
 12     public ControlCreateParams& CheckBoxControlCreateParams(ControlCreateParams& controlCreateParamsButtonStyle buttonStyle)
 13     {
 14         controlCreateParams.SetWindowClassName("BUTTON");
 15         controlCreateParams.SetWindowStyle(cast<WindowStyle>(DefaultChildWindowStyle() | WindowStyle.WS_TABSTOP | buttonStyle));
 16         controlCreateParams.SetBackgroundColor(DefaultControlBackgroundColor());
 17         return controlCreateParams;
 18     }
 19 
 20     public ControlCreateParams& CheckBoxControlCreateParams(ControlCreateParams& controlCreateParams)
 21     {
 22         return CheckBoxControlCreateParams(controlCreateParamscast<ButtonStyle>(ButtonStyle.BS_CHECKBOX | ButtonStyle.BS_NOTIFY));
 23     }
 24 
 25     public class CheckBoxCreateParams
 26     {
 27         public CheckBoxCreateParams(ControlCreateParams& controlCreateParams_) : controlCreateParams(controlCreateParams_)autoSize(true)
 28         {
 29         }
 30         public CheckBoxCreateParams& Defaults()
 31         {
 32             return *this;
 33         }
 34         public CheckBoxCreateParams& SetAutoSize(bool autoSize_)
 35         {
 36             autoSize = autoSize_;
 37             return *this;
 38         }
 39         public ControlCreateParams& controlCreateParams;
 40         public bool autoSize;
 41     }
 42 
 43     public class CheckBox : ButtonBase
 44     {
 45         private enum Flags : sbyte
 46         {
 47             none = 0checked = 1 << 0autoSize = 1 << 1autoSized = 1 << 2
 48         }
 49         public CheckBox(const Color& backgroundColorconst string& textconst Point& locationconst Size& sizeDock dockAnchors anchorsbool autoSize) : 
 50             base("BUTTON"DefaultWindowClassStyle()
 51             cast<WindowStyle>(DefaultChildWindowStyle() | WindowStyle.WS_TABSTOP | ButtonStyle.BS_CHECKBOX | ButtonStyle.BS_NOTIFY)
 52             DefaultExtendedWindowStyle()backgroundColortextlocationsizedockanchors)flags(Flags.none)
 53         {
 54             if (autoSize)
 55             {
 56                 SetAutoSizeFlag();
 57             }
 58             else
 59             {
 60                 ResetAutoSizeFlag();
 61             }
 62         }
 63         public CheckBox(const string& textconst Point& locationconst Size& sizeDock dockAnchors anchorsbool autoSize) : 
 64             this(DefaultControlBackgroundColor()textlocationsizedockanchorsautoSize)
 65         {
 66         }
 67         public CheckBox(CheckBoxCreateParams& createParams) : base(createParams.controlCreateParams)
 68         {
 69             if (createParams.autoSize)
 70             {
 71                 SetAutoSizeFlag();
 72             }
 73             else
 74             {
 75                 ResetAutoSizeFlag();
 76             }
 77         }
 78         public bool Checked()
 79         {
 80             return GetCheckedFlag();
 81         }
 82         public void SetChecked(bool checked)
 83         {
 84             if (checked)
 85             {
 86                 SetCheckedFlag();
 87                 if (Handle() != null)
 88                 {
 89                     WinSendMessage(Handle()BM_SETCHECKBST_CHECKED0);
 90                 }
 91             }
 92             else
 93             {
 94                 ResetCheckedFlag();
 95                 if (Handle() != null)
 96                 {
 97                     WinSendMessage(Handle()BM_SETCHECKBST_UNCHECKED0);
 98                 }
 99             }
100         }
101         protected override Result<bool> OnCreated()
102         {
103             auto baseResult = base->OnCreated();
104             if (baseResult.Error())
105             {
106                 return Result<bool>(ErrorId(baseResult.GetErrorId()));
107             }
108             auto graphicsResult = Graphics.FromWindowHandle(Handle());
109             if (graphicsResult.Error())
110             {
111                 return Result<bool>(ErrorId(graphicsResult.GetErrorId()));
112             }
113             Graphics& graphics = graphicsResult.Value();
114             auto fontHandleResult = GetFontHandle(graphics);
115             if (fontHandleResult.Error())
116             {
117                 return Result<bool>(ErrorId(fontHandleResult.GetErrorId()));
118             }
119             FontHandle* fontHandle = fontHandleResult.Value();
120             if (!fontHandle->IsNull())
121             {
122                 SendSetFontMessage(*fontHandle);
123             }
124             if (GetCheckedFlag())
125             {
126                 SetChecked(true);
127             }
128             else
129             {
130                 SetChecked(false);
131             }
132             if (GetAutoSizeFlag())
133             {
134                 auto result = DoAutoSize();
135                 if (result.Error())
136                 {
137                     return Result<bool>(ErrorId(result.GetErrorId()));
138                 }
139             }
140             return Result<bool>(true);
141         }
142         [nodiscard]
143         protected override Result<bool> OnClick(ClickEventArgs& args)
144         {
145             auto result = base->OnClick(args);
146             if (result.Error()) return result;
147             SetChecked(!GetCheckedFlag());
148             return Result<bool>(true);
149         }
150         [nodiscard]
151         protected override Result<bool> OnTextChanged()
152         {
153             auto result = base->OnTextChanged();
154             if (result.Error())
155             {
156                 return Result<bool>(ErrorId(result.GetErrorId()));
157             }
158             if (GetAutoSizeFlag())
159             {
160                 ResetAutoSized();
161                 auto result = DoAutoSize();
162                 if (result.Error())
163                 {
164                     return Result<bool>(ErrorId(result.GetErrorId()));
165                 }
166             }
167             return Result<bool>(true);
168         }
169         protected virtual void OnCheckedChanged()
170         {
171             checkedChangedEvent.Fire();
172         }
173         public Event<CheckedChangedEventHandler>& CheckedChangedEvent() const
174         {
175             return checkedChangedEvent;
176         }
177         private void RetrieveCheckedState()
178         {
179             long result = WinSendMessage(Handle()BM_GETCHECK0u0);
180             if (result == BST_CHECKED)
181             {
182                 SetCheckedFlag();
183             }
184             else
185             {
186                 ResetCheckedFlag();
187             }
188         }
189         private Result<bool> DoAutoSize()
190         {
191             if (AutoSized()) return Result<bool>(true);
192             if (Handle() == null) return Result<bool>(false);
193             auto graphicsResult = Graphics.FromWindowHandle(Handle());
194             if (graphicsResult.Error())
195             {
196                 return Result<bool>(ErrorId(graphicsResult.GetErrorId()));
197             }
198             Graphics& graphics = graphicsResult.Value();
199             const Font& font = GetFont();
200             StringFormat stringFormat;
201             if (stringFormat.Error())
202             {
203                 return Result<bool>(ErrorId(stringFormat.GetErrorId()));
204             }
205             auto measureResult = graphics.MeasureStringRectF(Text()fontPointF(00)stringFormat);
206             if (measureResult.Error())
207             {
208                 return Result<bool>(ErrorId(measureResult.GetErrorId()));
209             }
210             RectF r = measureResult.Value();
211             Size checkSize = GetCheckSize();
212             Size borderSize = GetBorderSize();
213             r.size.w = r.size.w + checkSize.w + borderSize.w;
214             r.size.h = Max(r.size.hcheckSize.h + borderSize.h);
215             auto result = SetSize(Size(cast<int>(r.size.w)cast<int>(r.size.h)));
216             if (result.Error()) return result;
217             SetAutoSized();
218             return Result<bool>(true);
219         }
220         private Size GetCheckSize()
221         {
222             int x = GetSystemMetrics(SystemMetricsId.SM_CXMENUCHECK);
223             int y = GetSystemMetrics(SystemMetricsId.SM_CYMENUCHECK);
224             return Size(xy);
225         }
226         private Size GetBorderSize()
227         {
228             int x = GetSystemMetrics(SystemMetricsId.SM_CXBORDER);
229             int y = GetSystemMetrics(SystemMetricsId.SM_CYBORDER);
230             return Size(xy);
231         }
232         private inline bool GetCheckedFlag() const
233         {
234             return (flags & Flags.checked) != Flags.none;
235         }
236         private void SetCheckedFlag()
237         {
238             if (!GetCheckedFlag())
239             {
240                 flags = cast<Flags>(flags | Flags.checked);
241                 OnCheckedChanged();
242             }
243         }
244         private void ResetCheckedFlag()
245         {
246             if (GetCheckedFlag())
247             {
248                 flags = cast<Flags>(flags & ~Flags.checked);
249                 OnCheckedChanged();
250             }
251         }
252         private inline bool GetAutoSizeFlag() const
253         {
254             return (flags & Flags.autoSize) != Flags.none;
255         }
256         private inline void SetAutoSizeFlag()
257         {
258             flags = cast<Flags>(flags | Flags.autoSize);
259         }
260         private inline void ResetAutoSizeFlag()
261         {
262             flags = cast<Flags>(flags & ~Flags.autoSize);
263         }
264         private inline bool AutoSized() const
265         {
266             return (flags & Flags.autoSized) != Flags.none;
267         }
268         private inline void SetAutoSized()
269         {
270             flags = cast<Flags>(flags | Flags.autoSized);
271         }
272         private inline void ResetAutoSized()
273         {
274             flags = cast<Flags>(flags & ~Flags.autoSized);
275         }
276         private Flags flags;
277         private Event<CheckedChangedEventHandler> checkedChangedEvent;
278     }