1 // =================================
  2 // Copyright (c) 2024 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 using System;
  7 using System.Collections;
  8 
  9 namespace System.Windows
 10 {
 11     public enum ButtonStyle : long
 12     {
 13         BS_PUSHBUTTON = 0
 14         BS_DEFPUSHBUTTON = 1
 15         BS_3STATE = 5
 16         BS_AUTO3STATE = 6
 17         BS_AUTOCHECKBOX = 3
 18         BS_AUTORADIOBUTTON = 9
 19         BS_BITMAP = 128u
 20         BS_BOTTOM = 2048
 21         BS_CENTER = 768
 22         BS_CHECKBOX = 2
 23         BS_GROUPBOX = 7
 24         BS_ICON = 64
 25         BS_FLAT = 32768u
 26         BS_LEFT = 256
 27         BS_LEFTTEXT = 32
 28         BS_MULTILINE = 8192
 29         BS_NOTIFY = 16384
 30         BS_OWNERDRAW = 11
 31         BS_PUSHLIKE = 4096
 32         BS_RADIOBUTTON = 4
 33         BS_RIGHT = 512
 34         BS_TOP = 1024
 35         BS_TYPEMASK = 15
 36         BS_VCENTER = 3072
 37     }
 38 
 39     public abstract class ButtonBase : Control
 40     {
 41         public ButtonBase(const string& windowClassNameWindowClassStyle windowClassStyleWindowStyle styleExtendedWindowStyle exStyle
 42             const Color& backgroundColorconst string& textconst Point& locationconst Size& sizeDock dockAnchors anchors) : 
 43             base(windowClassNamewindowClassStylestyleexStylebackgroundColortextlocationsizedockanchors)
 44         {
 45         }
 46         public ButtonBase(ControlCreateParams& controlCreateParams) : base(controlCreateParams)
 47         {
 48         }
 49         [nodiscard]
 50         internal Result<bool> OnClickInternal()
 51         {
 52             ClickEventArgs args;
 53             auto result = OnClick(args);
 54             if (result.Error()) return result;
 55             if (args.errorId != 0)
 56             {
 57                 return Result<bool>(ErrorId(args.errorId));
 58             }
 59             return Result<bool>(true);
 60         }
 61         [nodiscard]
 62         public Result<bool> DoClick()
 63         {
 64             ClickEventArgs args;
 65             auto result = OnClick(args);
 66             if (result.Error()) return result;
 67             if (args.errorId != 0)
 68             {
 69                 return Result<bool>(ErrorId(args.errorId));
 70             }
 71             return Result<bool>(true);
 72         }
 73     }
 74 
 75     public ControlCreateParams& ButtonControlCreateParams(ControlCreateParams& controlCreateParamsButtonStyle buttonStyle)
 76     {
 77         controlCreateParams.SetWindowClassName("BUTTON");
 78         controlCreateParams.SetWindowStyle(cast<WindowStyle>(DefaultChildWindowStyle() | WindowStyle.WS_TABSTOP | buttonStyle));
 79         controlCreateParams.SetWindowClassBackgroundColor(SystemColor.COLOR_BTNFACE);
 80         controlCreateParams.SetBackgroundColor(GetSystemColor(SystemColor.COLOR_BTNFACE));
 81         return controlCreateParams;
 82     }
 83 
 84     public ControlCreateParams& ButtonControlCreateParams(ControlCreateParams& controlCreateParamsbool setDefault)
 85     {
 86         ButtonStyle buttonStyle = ButtonStyle();
 87         if (setDefault)
 88         {
 89             buttonStyle = cast<ButtonStyle>(buttonStyle | ButtonStyle.BS_DEFPUSHBUTTON | ButtonStyle.BS_NOTIFY);
 90         }
 91         else
 92         {
 93             buttonStyle = cast<ButtonStyle>(buttonStyle | ButtonStyle.BS_PUSHBUTTON | ButtonStyle.BS_NOTIFY);
 94         }
 95         return ButtonControlCreateParams(controlCreateParamsbuttonStyle);
 96     }
 97 
 98     public ControlCreateParams& ButtonControlCreateParams(ControlCreateParams& controlCreateParams)
 99     {
100         return ButtonControlCreateParams(controlCreateParamsfalse);
101     }
102 
103     public class ButtonCreateParams
104     {
105         public ButtonCreateParams(ControlCreateParams& controlCreateParams_) : controlCreateParams(controlCreateParams_)
106         {
107         }
108         public ButtonCreateParams& Defaults()
109         {
110             return *this;
111         }
112         public ControlCreateParams& controlCreateParams;
113     }
114 
115     public class Button : ButtonBase
116     {
117         private enum Flags : sbyte
118         {
119             none = 0defaultButton = 1 << 0
120         }
121         public Button(ButtonStyle buttonStyleconst Color& backgroundColorconst string& textconst Point& locationconst Size& size
122             Dock dockAnchors anchors) : 
123             base("BUTTON"DefaultWindowClassStyle()cast<WindowStyle>(DefaultChildWindowStyle() | WindowStyle.WS_TABSTOP | buttonStyle)
124             DefaultExtendedWindowStyle()
125             backgroundColortextlocationsizedockanchors)flags(Flags.none)dialogResult(DialogResult.none)
126         {
127             SetFlagsFromButtonStyle(buttonStyle);
128         }
129         public Button(const string& textconst Point& locationconst Size& sizeDock dockAnchors anchors) : 
130             this(cast<ButtonStyle>(ButtonStyle.BS_PUSHBUTTON | ButtonStyle.BS_NOTIFY)GetSystemColor(SystemColor.COLOR_BTNFACE)textlocationsizedockanchors)
131         {
132         }
133         public Button(ButtonStyle buttonStyleconst string& textconst Point& locationconst Size& size) : 
134             this(buttonStyleGetSystemColor(SystemColor.COLOR_BTNFACE)textlocationsizeDock.nonecast<Anchors>(Anchors.top | Anchors.left))
135         {
136         }
137         public Button(const string& textconst Point& locationconst Size& size) : 
138             this(cast<ButtonStyle>(ButtonStyle.BS_PUSHBUTTON | ButtonStyle.BS_NOTIFY)textlocationsize)
139         {
140         }
141         public Button(ButtonCreateParams& createParams) : 
142             base(createParams.controlCreateParams)flags(Flags.none)dialogResult(DialogResult.none)
143         {
144             SetFlagsFromButtonStyle(cast<ButtonStyle>(cast<long>(createParams.controlCreateParams.windowStyle)));
145         }
146         protected override Result<bool> OnCreated()
147         {
148             auto result = base->OnCreated();
149             if (result.Error())
150             {
151                 return Result<bool>(ErrorId(result.GetErrorId()));
152             }
153             auto graphicsResult = Graphics.FromWindowHandle(Handle());
154             if (graphicsResult.Error())
155             {
156                 return Result<bool>(ErrorId(graphicsResult.GetErrorId()));
157             }
158             else
159             {
160                 Graphics& graphics = graphicsResult.Value();
161                 Result<FontHandle*> fontHandleResult = GetFontHandle(graphics);
162                 if (fontHandleResult.Error())
163                 {
164                     return Result<bool>(ErrorId(fontHandleResult.GetErrorId()));
165                 }
166                 else
167                 {
168                     FontHandle* fontHandle = fontHandleResult.Value();
169                     if (!fontHandle->IsNull())
170                     {
171                         SendSetFontMessage(*fontHandle);
172                     }
173                 }
174             }
175             return Result<bool>(true);
176         }
177         [nodiscard]
178         protected override Result<bool> OnGotFocus()
179         {
180             auto result = base->OnGotFocus();
181             if (result.Error()) return result;
182             if (!IsDefault())
183             {
184                 Window* window = GetWindow();
185                 if (window != null)
186                 {
187                     Button* defaultButton = window->DefaultButton();
188                     if (defaultButton != null)
189                     {
190                         defaultButton->ResetDefaultButtonStyle();
191                     }
192                 }
193                 SetDefaultButtonStyle();
194             }
195             return Result<bool>(true);
196         }
197         [nodiscard]
198         protected override Result<bool> OnLostFocus()
199         {
200             auto result = base->OnLostFocus();
201             if (result.Error()) return result;
202             if (!IsDefault())
203             {
204                 ResetDefaultButtonStyle();
205                 Window* window = GetWindow();
206                 if (window != null)
207                 {
208                     Button* defaultButton = window->DefaultButton();
209                     if (defaultButton != null)
210                     {
211                         defaultButton->SetDefaultButtonStyle();
212                     }
213                 }
214             }
215             return Result<bool>(true);
216         }
217         [nodiscard]
218         protected override Result<bool> OnClick(ClickEventArgs& args)
219         {
220             auto result = base->OnClick(args);
221             if (result.Error()) return result;
222             if (dialogResult != DialogResult.none)
223             {
224                 Window* window = GetWindow();
225                 if (window != null)
226                 {
227                     window->SetDialogResult(dialogResult);
228                 }
229             }
230             return Result<bool>(true);
231         }
232         [nodiscard]
233         protected override Result<bool> OnKeyDown(KeyEventArgs& args)
234         {
235             auto result = base->OnKeyDown(args);
236             if (result.Error()) return result;
237             if (!args.handled)
238             {
239                 switch (args.key)
240                 {
241                     case Keys.enter:
242                     {
243                         ClickEventArgs clickEventArgs;
244                         result = OnClick(clickEventArgs);
245                         if (result.Error()) return result;
246                         if (clickEventArgs.errorId != 0)
247                         {
248                             return Result<bool>(ErrorId(clickEventArgs.errorId));
249                         }
250                         break;
251                     }
252                 }
253             }
254             return Result<bool>(true);
255         }
256         [nodiscard]
257         public override Result<bool> PrintWindowTree(int level)
258         {
259             LogView* log = Application.GetLogView();
260             if (log != null)
261             {
262                 auto handleResult = ToHexString(cast<ulong>(Handle()));
263                 if (handleResult.Error())
264                 {
265                     return Result<bool>(ErrorId(handleResult.GetErrorId()));
266                 }
267                 const string& handleStr = handleResult.Value();
268                 auto parentTextResult = ParentText();
269                 if (parentTextResult.Error())
270                 {
271                     return Result<bool>(ErrorId(parentTextResult.GetErrorId()));
272                 }
273                 const string& parentText = parentTextResult.Value();
274                 auto result = log->WriteLine(string(' 'level) + "Button." + Text() + ".handle=" + handleStr + " " + parentText + 
275                     "[" + Rect(Point()GetSize()).ToString() + "]");
276                 if (result.Error()) return result;
277             }
278             return Result<bool>(true);
279         }
280         public inline DialogResult GetDialogResult() const
281         {
282             return dialogResult;
283         }
284         public inline void SetDialogResult(DialogResult dialogResult_)
285         {
286             dialogResult = dialogResult_;
287         }
288         private void SetFlagsFromButtonStyle(ButtonStyle buttonStyle)
289         {
290             if ((buttonStyle & ButtonStyle.BS_DEFPUSHBUTTON) != 0)
291             {
292                 flags = cast<Flags>(flags | Flags.defaultButton);
293             }
294         }
295         public inline bool IsDefault() const
296         {
297             return (flags & Flags.defaultButton) != Flags.none;
298         }
299         public void SetDefault()
300         {
301             flags = cast<Flags>(flags | Flags.defaultButton);
302             SetDefaultButtonStyle();
303         }
304         public void ResetDefault()
305         {
306             flags = cast<Flags>(flags & ~Flags.defaultButton);
307             ResetDefaultButtonStyle();
308         }
309         private void SetDefaultButtonStyle()
310         {
311             if (Handle() != null)
312             {
313                 WinSendMessage(Handle()BM_SETSTYLEcast<uint>(GetWindowStyle() | ButtonStyle.BS_DEFPUSHBUTTON)cast<long>(true));
314             }
315         }
316         private void ResetDefaultButtonStyle()
317         {
318             if (Handle() != null)
319             {
320                 WinSendMessage(Handle()BM_SETSTYLEcast<uint>(GetWindowStyle() & ~ButtonStyle.BS_DEFPUSHBUTTON)cast<long>(true));
321             }
322         }
323         private Flags flags;
324         private DialogResult dialogResult;
325     }