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 enum BorderStyle : sbyte
 11     {
 12         singlestyle3D
 13     }
 14 
 15     public inline int DefaultSingleBorderWidth()
 16     {
 17         return 1;
 18     }
 19 
 20     public inline int Default3DBorderWidth()
 21     {
 22         return 2;
 23     }
 24 
 25     public int GetBorderWidth(BorderStyle borderStyle)
 26     {
 27         switch (borderStyle)
 28         {
 29             case BorderStyle.single: return DefaultSingleBorderWidth();
 30             case BorderStyle.style3D: return Default3DBorderWidth();
 31         }
 32         return 0;
 33     }
 34 
 35     public Color DefaultBorderedControlNormalBorderdColor()
 36     {
 37         return Color.Black();
 38     }
 39 
 40     public Color DefaultBorderedControlFocusedBorderColor()
 41     {
 42         return Color.Blue();
 43     }
 44 
 45     public ControlCreateParams& BorderedControlControlCreateParams(ControlCreateParams& controlCreateParamsControl* child)
 46     {
 47         return controlCreateParams.SetWindowClassName("System.Windows.BorderedControl").SetBackgroundColor(child->BackgroundColor());
 48     }
 49 
 50     public class BorderedControlCreateParams
 51     {
 52         public BorderedControlCreateParams(ControlCreateParams& controlCreateParams_Control* child_) : 
 53             controlCreateParams(controlCreateParams_)
 54             child(child_)
 55             borderStyle(BorderStyle.single)
 56             borderWidth(GetBorderWidth(borderStyle))
 57             normalBorderColor(DefaultBorderedControlNormalBorderdColor())
 58             focusedBorderColor(DefaultBorderedControlFocusedBorderColor())
 59         {
 60         }
 61         public BorderedControlCreateParams& Defaults()
 62         {
 63             return *this;
 64         }
 65         public BorderedControlCreateParams& SetBorderStyle(BorderStyle borderStyle_)
 66         {
 67             borderStyle = borderStyle_;
 68             return *this;
 69         }
 70         public BorderedControlCreateParams& SetBorderWidth(int borderWidth_)
 71         {
 72             borderWidth = borderWidth_;
 73             return *this;
 74         }
 75         public BorderedControlCreateParams& SetNormalBorderColor(const Color& normalBorderColor_)
 76         {
 77             normalBorderColor = normalBorderColor_;
 78             return *this;
 79         }
 80         public BorderedControlCreateParams& SetFocusedBorderColor(const Color& focusedBorderColor_)
 81         {
 82             focusedBorderColor = focusedBorderColor_;
 83             return *this;
 84         }
 85         public ControlCreateParams& controlCreateParams;
 86         public Control* child;
 87         public BorderStyle borderStyle;
 88         public int borderWidth;
 89         public Color normalBorderColor;
 90         public Color focusedBorderColor;
 91     }
 92 
 93     public class BorderedControl : Control
 94     {
 95         private enum Flags : sbyte
 96         {
 97             none = 0childFocused = 1 << 0
 98         }
 99         public BorderedControl(Control* child_BorderStyle borderStyle_const Color& normalBorderColor_const Color& focusedBorderColor_
100             const Point& locationconst Size& sizeDock dockAnchors anchors) : 
101             base("System.Windows.BorderedControl"DefaultWindowClassStyle()DefaultChildWindowStyle()DefaultExtendedWindowStyle()
102             child_->BackgroundColor()"borderedControl"locationsizedockanchors)
103             container(this)borderStyle(borderStyle_)normalBorderColor(normalBorderColor_)focusedBorderColor(focusedBorderColor_)
104             borderWidth(GetBorderWidth(borderStyle))child(child_)
105         {
106             auto result = container.AddChild(child);
107             if (result.Error())
108             {
109                 SetErrorId(result.GetErrorId());
110                 return;
111             }
112             result = SetChildPos();
113             if (result.Error())
114             {
115                 SetErrorId(result.GetErrorId());
116                 return;
117             }
118         }
119         public BorderedControl(Control* childBorderStyle borderStyleconst Point& locationconst Size& sizeDock dockAnchors anchors) : 
120             this(childborderStyleColor.Black()Color.Blue()locationsizedockanchors)
121         {
122         }
123         public BorderedControl(Control* childconst Point& locationconst Size& sizeDock dockAnchors anchors) : 
124             this(childBorderStyle.singlelocationsizedockanchors)
125         {
126         }
127         public explicit BorderedControl(Control* child) : this(childPoint()Size()Dock.noneAnchors.none)
128         {
129         }
130         public BorderedControl(BorderedControlCreateParams& createParams) : 
131             base(createParams.controlCreateParams)
132             container(this)
133             borderStyle(createParams.borderStyle)
134             borderWidth(createParams.borderWidth)
135             normalBorderColor(createParams.normalBorderColor)
136             focusedBorderColor(createParams.focusedBorderColor)
137             child(createParams.child)
138             flags(Flags.none)
139         {
140             auto result = container.AddChild(child);
141             if (result.Error())
142             {
143                 SetErrorId(result.GetErrorId());
144                 return;
145             }
146             result = SetChildPos();
147             if (result.Error())
148             {
149                 SetErrorId(result.GetErrorId());
150                 return;
151             }
152         }
153         [nodiscard]
154         public Result<bool> SetNormalBorderColor(const Color& normalBorderColor_)
155         {
156             if (normalBorderColor != normalBorderColor_)
157             {
158                 normalBorderColor = normalBorderColor_;
159                 auto result = Invalidate();
160                 if (result.Error()) return result;
161             }
162             return Result<bool>(true);
163         }
164         [nodiscard]
165         public Result<bool> SetFocusedBorderColor(const Color& focusedBorderColor_)
166         {
167             if (focusedBorderColor != focusedBorderColor_)
168             {
169                 focusedBorderColor = focusedBorderColor_;
170                 auto result = Invalidate();
171                 if (result.Error()) return result;
172             }
173             return Result<bool>(true);
174         }
175         [nodiscard]
176         public Result<bool> SetBorderWidth(int borderWidth_)
177         {
178             borderWidth = borderWidth_;
179             auto result = Invalidate();
180             if (result.Error()) return result;
181             return Result<bool>(true);
182         }
183         protected override bool IsDecoratorControl() const
184         {
185             return true;
186         }
187         public override ContainerControl* GetContainerControl() const
188         {
189             if (child is ContainerControl*)
190             {
191                 return cast<ContainerControl*>(child);
192             }
193             return base->GetContainerControl();
194         }
195         internal override Control* GetFirstEnabledTabStopControl() const
196         {
197             return child->GetFirstEnabledTabStopControl();
198         }
199         internal override Control* GetLastEnabledTabStopControl() const
200         {
201             return child->GetLastEnabledTabStopControl();
202         }
203         protected override void OnChildContentLocationChanged(ControlEventArgs& args)
204         {
205             base->OnChildContentLocationChanged(args);
206             Control* parentControl = ParentControl();
207             if (parentControl != null)
208             {
209                 parentControl->OnChildContentLocationChanged(args);
210             }
211         }
212         protected override void OnChildContentChanged(ControlEventArgs& args)
213         {
214             base->OnChildContentChanged(args);
215             Control* parentControl = ParentControl();
216             if (parentControl != null)
217             {
218                 parentControl->OnChildContentChanged(args);
219             }
220         }
221         [nodiscard]
222         protected override Result<bool> OnChildContentSizeChanged(ControlEventArgs& args)
223         {
224             auto result = base->OnChildContentSizeChanged(args);
225             if (result.Error()) return result;
226             Control* parentControl = ParentControl();
227             if (parentControl != null)
228             {
229                 result = parentControl->OnChildContentSizeChanged(args);
230                 if (result.Error()) return result;
231             }
232             return Result<bool>(true);
233         }
234         [nodiscard]
235         protected override Result<bool> OnChildGotFocus(ControlEventArgs& args)
236         {
237             auto result = base->OnChildGotFocus(args);
238             if (result.Error()) return result;
239             SetChildFocused();
240             Control* parentControl = ParentControl();
241             if (parentControl != null)
242             {
243                 result = parentControl->OnChildGotFocus(args);
244                 if (result.Error()) return result;
245             }
246             result = Invalidate();
247             if (result.Error()) return result;
248             return Result<bool>(true);
249         }
250         [nodiscard]
251         protected override Result<bool> OnChildLostFocus(ControlEventArgs& args)
252         {
253             auto result = base->OnChildLostFocus(args);
254             if (result.Error()) return result;
255             ResetChildFocused();
256             Control* parentControl = ParentControl();
257             if (parentControl != null)
258             {
259                 result = parentControl->OnChildLostFocus(args);
260                 if (result.Error()) return result;
261             }
262             result = Invalidate();
263             if (result.Error()) return result;
264             return Result<bool>(true);
265         }
266         [nodiscard]
267         public override Result<bool> PrintWindowTree(int level)
268         {
269             LogView* log = Application.GetLogView();
270             if (log != null)
271             {
272                 auto handleResult = ToHexString(cast<ulong>(Handle()));
273                 if (handleResult.Error())
274                 {
275                     return Result<bool>(ErrorId(handleResult.GetErrorId()));
276                 }
277                 auto parentTextResult = ParentText();
278                 if (parentTextResult.Error())
279                 {
280                     return Result<bool>(ErrorId(parentTextResult.GetErrorId()));
281                 }
282                 auto locationResult = Location();
283                 if (locationResult.Error())
284                 {
285                     return Result<bool>(ErrorId(locationResult.GetErrorId()));
286                 }
287                 auto result = log->WriteLine(string(' 'level) + "BorderedControl." + Text() + ".handle=" + handleResult.Value() + " " + 
288                     parentTextResult.Value() + "[" + Rect(locationResult.Value()GetSize()).ToString() + "]");
289                 if (result.Error()) return result;
290             }
291             Component* child = container.FirstChild();
292             while (child != null)
293             {
294                 if (child is Control*)
295                 {
296                     Control* childControl = cast<Control*>(child);
297                     auto result = childControl->PrintWindowTree(level + 1);
298                     if (result.Error())
299                     {
300                         return Result<bool>(ErrorId(result.GetErrorId()));
301                     }
302                 }
303                 child = child->NextSibling();
304             }
305             return Result<bool>(true);
306         }
307         [nodiscard]
308         protected override Result<bool> OnLocationChanged()
309         {
310             auto result = base->OnLocationChanged();
311             if (result.Error()) return result;
312             result = SetChildPos();
313             if (result.Error()) return result;
314             return Result<bool>(true);
315         }
316         [nodiscard]
317         protected override Result<bool> OnSizeChanged(SizeChangedEventArgs& args)
318         {
319             auto result = base->OnSizeChanged(args);
320             if (result.Error()) return result;
321             result = SetChildPos();
322             if (result.Error()) return result;
323             return Result<bool>(true);
324         }
325         [nodiscard]
326         protected override Result<bool> OnChildSizeChanged(ControlEventArgs& args)
327         {
328             auto result = base->OnChildSizeChanged(args);
329             if (result.Error()) return result;
330             Control* parentControl = ParentControl();
331             if (parentControl != null)
332             {
333                 result = parentControl->FireChildSizeChanged(args);
334                 if (result.Error()) return result;
335             }
336             return Result<bool>(true);
337         }
338         [nodiscard]
339         protected override Result<bool> OnPaint(PaintEventArgs& args)
340         {
341             if (Debug.Paint())
342             {
343                 Rect r(Point()GetSize());
344                 LogView* log = Application.GetLogView();
345                 if (log != null)
346                 {
347                     auto result = log->WriteLine("BorderedControl.OnPaint: " + r.ToString());
348                     if (result.Error()) return result;
349                 }
350             }
351             auto result = DrawBorder(args.graphics);
352             if (result.Error())
353             {
354                 return Result<bool>(ErrorId(result.GetErrorId()));
355             }
356             return base->OnPaint(args);
357         }
358         [nodiscard]
359         private Result<bool> SetChildPos()
360         {
361             Point loc;
362             Size size = GetSize();
363             Rect childRect(locsize);
364             childRect.Inflate(-borderWidth-borderWidth);
365             auto result = child->SetLocation(childRect.location);
366             if (result.Error()) return result;
367             result = child->SetSize(childRect.size);
368             if (result.Error()) return result;
369             return Result<bool>(true);
370         }
371         private Result<bool> DrawBorder(Graphics& graphics)
372         {
373             switch (borderStyle)
374             {
375                 case BorderStyle.single:
376                 {
377                     auto result = DrawSingleBorder(graphics);
378                     if (result.Error())
379                     {
380                         return Result<bool>(ErrorId(result.GetErrorId()));
381                     }
382                     break;
383                 }
384                 case BorderStyle.style3D:
385                 {
386                     auto result = Draw3DBorder(graphics);
387                     if (result.Error())
388                     {
389                         return Result<bool>(ErrorId(result.GetErrorId()));
390                     }
391                     break;
392                 }
393             }
394             return Result<bool>(true);
395         }
396         private Result<bool> DrawSingleBorder(Graphics& graphics)
397         {
398             Rect r(Point()GetSize());
399             r.size.w = r.size.w - 1;
400             r.size.h = r.size.h - 1;
401             if (ChildFocused())
402             {
403                 Pen pen(focusedBorderColor1);
404                 if (pen.Error())
405                 {
406                     return Result<bool>(ErrorId(pen.GetErrorId()));
407                 }
408                 auto drawResult = graphics.DrawRectangle(penr);
409                 if (drawResult.Error())
410                 {
411                     return Result<bool>(ErrorId(drawResult.GetErrorId()));
412                 }
413             }
414             else
415             {
416                 Pen pen(normalBorderColor1);
417                 if (pen.Error())
418                 {
419                     return Result<bool>(ErrorId(pen.GetErrorId()));
420                 }
421                 auto drawResult = graphics.DrawRectangle(penr);
422                 if (drawResult.Error())
423                 {
424                     return Result<bool>(ErrorId(drawResult.GetErrorId()));
425                 }
426             }
427             return Result<bool>(true);
428         }
429         private Result<bool> Draw3DBorder(Graphics& graphics)
430         {
431             Rect r(Point()GetSize());
432             r.size.w = r.size.w - 1;
433             r.size.h = r.size.h - 1;
434             Pen outerLeftTopEdgePen(Color(160u160u160u)1);
435             if (outerLeftTopEdgePen.Error())
436             {
437                 return Result<bool>(ErrorId(outerLeftTopEdgePen.GetErrorId()));
438             }
439             auto drawResult = graphics.DrawLine(outerLeftTopEdgePenr.locationPoint(r.location.x + r.size.w - 1r.location.y));
440             if (drawResult.Error())
441             {
442                 return Result<bool>(ErrorId(drawResult.GetErrorId()));
443             }
444             drawResult = graphics.DrawLine(outerLeftTopEdgePenr.locationPoint(r.location.xr.location.y + r.size.h - 1));
445             if (drawResult.Error())
446             {
447                 return Result<bool>(ErrorId(drawResult.GetErrorId()));
448             }
449             Pen outerRightBottomEdgePen(Color.White()1);
450             if (outerRightBottomEdgePen.Error())
451             {
452                 return Result<bool>(ErrorId(outerRightBottomEdgePen.GetErrorId()));
453             }
454             drawResult = graphics.DrawLine(outerRightBottomEdgePenPoint(r.location.x + r.size.wr.location.y)Point(r.location.x + r.size.wr.location.y + r.size.h));
455             if (drawResult.Error())
456             {
457                 return Result<bool>(ErrorId(drawResult.GetErrorId()));
458             }
459             drawResult = graphics.DrawLine(outerRightBottomEdgePenPoint(r.location.xr.location.y + r.size.h)Point(r.location.x + r.size.wr.location.y + r.size.h));
460             if (drawResult.Error())
461             {
462                 return Result<bool>(ErrorId(drawResult.GetErrorId()));
463             }
464             r.Inflate(-1-1);
465             Pen innerLeftTopEdgePen(Color(105u105u105u)1);
466             if (innerLeftTopEdgePen.Error())
467             {
468                 return Result<bool>(ErrorId(innerLeftTopEdgePen.GetErrorId()));
469             }
470             drawResult = graphics.DrawLine(innerLeftTopEdgePenr.locationPoint(r.location.x + r.size.w - 1r.location.y));
471             if (drawResult.Error())
472             {
473                 return Result<bool>(ErrorId(drawResult.GetErrorId()));
474             }
475             drawResult = graphics.DrawLine(innerLeftTopEdgePenr.locationPoint(r.location.xr.location.y + r.size.h - 1));
476             if (drawResult.Error())
477             {
478                 return Result<bool>(ErrorId(drawResult.GetErrorId()));
479             }
480             Pen innerRightBottomEdgePen(Color(227u227u227u)1);
481             if (innerRightBottomEdgePen.Error())
482             {
483                 return Result<bool>(ErrorId(innerRightBottomEdgePen.GetErrorId()));
484             }
485             drawResult = graphics.DrawLine(innerRightBottomEdgePenPoint(r.location.x + r.size.wr.location.y)Point(r.location.x + r.size.wr.location.y + r.size.h));
486             if (drawResult.Error())
487             {
488                 return Result<bool>(ErrorId(drawResult.GetErrorId()));
489             }
490             drawResult = graphics.DrawLine(innerRightBottomEdgePenPoint(r.location.xr.location.y + r.size.h)Point(r.location.x + r.size.wr.location.y + r.size.h));
491             if (drawResult.Error())
492             {
493                 return Result<bool>(ErrorId(drawResult.GetErrorId()));
494             }
495             return Result<bool>(true);
496         }
497         private inline bool ChildFocused() const
498         {
499             return (flags & Flags.childFocused) != Flags.none;
500         }
501         private inline void SetChildFocused()
502         {
503             flags = cast<Flags>(flags | Flags.childFocused);
504         }
505         private inline void ResetChildFocused()
506         {
507             flags = cast<Flags>(flags & ~Flags.childFocused);
508         }
509         private ComponentContainer container;
510         private BorderStyle borderStyle;
511         private int borderWidth;
512         private Color normalBorderColor;
513         private Color focusedBorderColor;
514         private Control* child;
515         private Flags flags;
516     }