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