1 // =================================
  2 // Copyright (c) 2021 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 class delegate void SplitterEventHandler();
 12 
 13     public enum Orientation
 14     {
 15         horizontalvertical
 16     }
 17 
 18     public nothrow Dock SplitDock(Orientation orientation)
 19     {
 20         switch (orientation)
 21         {
 22             case Orientation.horizontal: return Dock.left;
 23             case Orientation.vertical: return Dock.top;
 24         }
 25         return Dock.none;
 26     }
 27 
 28     public nothrow Size SplitSize(Orientation orientationint width)
 29     {
 30         switch (orientation)
 31         {
 32             case Orientation.horizontal: return Size(width0);
 33             case Orientation.vertical: return Size(0width);
 34         }
 35         return Size();
 36     }
 37 
 38     public nothrow Color DefaultSplitterEdgeColor()
 39     {
 40         return Color(204u206u219u);
 41     }
 42 
 43     public const int defaultSplitterWidth = 5;
 44 
 45     public nothrow ControlCreateParams& SplitterControlCreateParams(ControlCreateParams& controlCreateParamsOrientation orientationint width)
 46     {
 47         return controlCreateParams.SetWindowClassName("System.Windows.Splitter").SetSize(SplitSize(orientationwidth)).SetDock(SplitDock(orientation));
 48     }
 49     
 50     public class SplitterCreateParams
 51     {
 52         public nothrow SplitterCreateParams(ControlCreateParams& controlCreateParams_) : 
 53             controlCreateParams(controlCreateParams_)
 54             orientation(Orientation.horizontal)
 55             edgeColor(DefaultSplitterEdgeColor())
 56         {
 57         }
 58         public nothrow SplitterCreateParams& Defaults()
 59         {
 60             return *this;
 61         }
 62         public nothrow SplitterCreateParams& SetOrientation(Orientation orientation_)
 63         {
 64             orientation = orientation_;
 65             return *this;
 66         }
 67         public nothrow SplitterCreateParams& SetEdgeColor(const Color& edgeColor_)
 68         {
 69             edgeColor = edgeColor_;
 70             return *this;
 71         }
 72         public ControlCreateParams& controlCreateParams;
 73         public Orientation orientation;
 74         public Color edgeColor;
 75     }
 76     
 77     public class Splitter : Control
 78     {
 79         private enum Flags : sbyte
 80         {
 81             none = 0moveSplitter = 1 << 0
 82         }
 83         public Splitter(Orientation orientation_const Color& backgroundColorconst Color& edgeColor_const Point& locationint width) : 
 84             base("System.Windows.Splitter"DefaultWindowClassStyle()DefaultChildWindowStyle()DefaultExtendedWindowStyle()
 85             backgroundColor"splitter"locationSplitSize(orientation_width)SplitDock(orientation_)Anchors.none)
 86             orientation(orientation_)
 87             horizontalSplitterCursor(Application.GetResourceManager().GetCursor("horizontal.splitter.system.windows.cursor"))
 88             verticalSplitterCursor(Application.GetResourceManager().GetCursor("vertical.splitter.system.windows.cursor"))flags()container(null)
 89             edgeColor(edgeColor_)edgePen(edgeColor)
 90         {
 91         }
 92         public Splitter(Orientation orientationconst Point& location) : 
 93             this(orientationDefaultControlBackgroundColor()DefaultSplitterEdgeColor()locationdefaultSplitterWidth)
 94         {
 95         }
 96         public Splitter(SplitterCreateParams& createParams) : 
 97             base(createParams.controlCreateParams)
 98             orientation(createParams.orientation)
 99             horizontalSplitterCursor(Application.GetResourceManager().GetCursor("horizontal.splitter.system.windows.cursor"))
100             verticalSplitterCursor(Application.GetResourceManager().GetCursor("vertical.splitter.system.windows.cursor"))flags()container(null)
101             edgeColor(createParams.edgeColor)edgePen(edgeColor)
102         {
103         }
104         public nothrow void SetContainer(SplitContainer* container_)
105         {
106             container = container_;
107         }
108         public override void PrintWindowTree(int level)
109         {
110             LogView* log = Application.GetLogView();
111             if (log != null)
112             {
113                 log->WriteLine(string(' 'level) + "Splitter." + Text() + ".handle=" + ToHexString(cast<ulong>(Handle())) + " " + ParentText() + "[" + Rect(Point()GetSize()).ToString() + "]");
114             }
115         }
116         protected override void OnPaint(PaintEventArgs& args)
117         {
118             try
119             {
120                 if (Debug.Paint())
121                 {
122                     Rect r(Point()GetSize());
123                     LogView* log = Application.GetLogView();
124                     if (log != null)
125                     {
126                         log->WriteLine("Splitter.OnPaint: " + r.ToString());
127                     }
128                 }
129                 args.graphics.Clear(BackgroundColor());
130                 Size size = GetSize();
131                 LogView* log = Application.GetLogView();
132                 switch (orientation)
133                 {
134                     case Orientation.horizontal:
135                     {
136                         args.graphics.DrawLineChecked(edgePenPoint(00)Point(0size.h - 1));
137                         args.graphics.DrawLineChecked(edgePenPoint(size.w - 10)Point(size.w - 1size.h - 1));
138                         break;
139                     }
140                     case Orientation.vertical:
141                     {
142                         args.graphics.DrawLineChecked(edgePenPoint(00)Point(size.w - 10));
143                         args.graphics.DrawLineChecked(edgePenPoint(0size.h - 1)Point(size.w - 1size.h - 1));
144                         break;
145                     }
146                 }
147                 base->OnPaint(args);
148             }
149             catch (const Exception& ex)
150             {
151                 MessageBox.Show(ex.Message());
152             }
153         }
154         protected override void SetCursor()
155         {
156             switch (orientation)
157             {
158                 case Orientation.horizontal: SetCursor(horizontalSplitterCursor); break;
159                 case Orientation.vertical: SetCursor(verticalSplitterCursor); break;
160             }
161         }
162         protected override void OnMouseDown(MouseEventArgs& args)
163         {
164             try
165             {
166                 base->OnMouseDown(args);
167                 if (args.buttons == MouseButtons.lbutton)
168                 {
169                     SetFlag(Flags.moveSplitter);
170                     switch (orientation)
171                     {
172                         case Orientation.horizontal:
173                         {
174                             x = args.location.x;
175                             break;
176                         }
177                         case Orientation.vertical:
178                         {
179                             y = args.location.y;
180                             break;
181                         }
182                     }
183                     WinSetCapture(Handle());
184                 }
185             }
186             catch (const Exception& ex)
187             {
188                 MessageBox.Show(ex.Message());
189             }
190         }
191         protected override void OnMouseMove(MouseEventArgs& args)
192         {
193             try
194             {
195                 if (GetFlag(Flags.moveSplitter))
196                 {
197                     switch (orientation)
198                     {
199                         case Orientation.horizontal:
200                         {
201                             int dx = args.location.x - x;
202                             container->SetSplitterDistance(container->SplitterDistance() + dx);
203                             break;
204                         }
205                         case Orientation.vertical:
206                         {
207                             int dy = args.location.y - y;
208                             container->SetSplitterDistance(container->SplitterDistance() + dy);
209                             break;
210                         }
211                     }
212                 }
213             }
214             catch (const Exception& ex)
215             {
216                 MessageBox.Show(ex.Message());
217             }
218         }
219         protected override void OnMouseUp(MouseEventArgs& args)
220         {
221             try
222             {
223                 base->OnMouseUp(args);
224                 if (GetFlag(Flags.moveSplitter))
225                 {
226                     ResetFlag(Flags.moveSplitter);
227                     WinReleaseCapture();
228                     switch (orientation)
229                     {
230                         case Orientation.horizontal:
231                         {
232                             int dx = args.location.x - x;
233                             container->SetSplitterDistance(container->SplitterDistance() + dx);
234                             break;
235                         }
236                         case Orientation.vertical:
237                         {
238                             int dy = args.location.y - y;
239                             container->SetSplitterDistance(container->SplitterDistance() + dy);
240                             break;
241                         }
242                     }
243                 }
244             }
245             catch (const Exception& ex)
246             {
247                 MessageBox.Show(ex.Message());
248             }
249         }
250         private inline nothrow void SetFlag(Flags flag)
251         {
252             flags = cast<Flags>(flags | flag);
253         }
254         private inline nothrow void ResetFlag(Flags flag)
255         {
256             flags = cast<Flags>(flags & ~flag);
257         }
258         private inline nothrow bool GetFlag(Flags flag) const
259         {
260             return (flags & flag) != 0;
261         }
262         private Orientation orientation;
263         private Cursor& horizontalSplitterCursor;
264         private Cursor& verticalSplitterCursor;
265         private Flags flags;
266         private int x;
267         private int y;
268         private SplitContainer* container;
269         private Color edgeColor;
270         private Pen edgePen;
271     }
272 
273     public nothrow ControlCreateParams& SplitContainerControlCreateParams(ControlCreateParams& controlCreateParams)
274     {
275         return controlCreateParams.SetWindowClassName("System.Windows.SplitContainer");
276     }
277     
278     public class SplitContainerCreateParams
279     {
280         public nothrow SplitContainerCreateParams(ControlCreateParams& controlCreateParams_
281             Orientation orientation_Control* pane1_Splitter* splitter_Control* pane2_int splitterDistance_) : 
282             controlCreateParams(controlCreateParams_)orientation(orientation_)pane1(pane1_)splitter(splitter_)pane2(pane2_)
283             splitterWidth(defaultSplitterWidth)splitterDistance(splitterDistance_)
284         {
285         }
286         public nothrow SplitContainerCreateParams& Defaults()
287         {
288             return *this;
289         }
290         public nothrow SplitContainerCreateParams& SetSplitterWidth(int splitterWidth_)
291         {
292             splitterWidth = splitterWidth_;
293             return *this;
294         }
295         public nothrow SplitContainerCreateParams& SetSplitterDistance(int splitterDistance_)
296         {
297             splitterDistance = splitterDistance_;
298             return *this;
299         }
300         public ControlCreateParams& controlCreateParams;
301         public Orientation orientation;
302         public Control* pane1;
303         public Splitter* splitter;
304         public Control* pane2;
305         public int splitterWidth;
306         public int splitterDistance;
307     }
308     
309     public class SplitContainer : ContainerControl
310     {
311         public SplitContainer(Orientation orientation_Control* pane1_Splitter* splitter_Control* pane2_int splitterWidthint splitterDistance_
312             const Point& locationconst Size& sizeDock dockAnchors anchors) : 
313             base("System.Windows.SplitContainer"DefaultWindowClassStyle()DefaultChildWindowStyle()DefaultExtendedWindowStyle()
314                 DefaultControlBackgroundColor()"splitContainer"locationsizedockanchors)
315                 orientation(orientation_)pane1(pane1_)splitter(splitter_)pane2(pane2_)splitterDistance(splitterDistance_)
316         {
317             Init();
318         }
319         public SplitContainer(Orientation orientationint splitterDistanceconst Point& locationconst Size& sizeDock dockAnchors anchors) : 
320             this(orientation
321                 new Panel(locationSplitSize(orientationsplitterDistance)SplitDock(orientation)Anchors.none)
322                 new Splitter(orientationlocation)
323                 new Panel(locationSplitSize(orientationsplitterDistance)SplitDock(orientation)Anchors.none)
324                 defaultSplitterWidthsplitterDistancelocationsizedockanchors)
325         {
326         }
327         public SplitContainer(SplitContainerCreateParams& createParams) : 
328             base(createParams.controlCreateParams)
329             orientation(createParams.orientation)
330             pane1(createParams.pane1)splitter(createParams.splitter)pane2(createParams.pane2)splitterDistance(createParams.splitterDistance)
331         {
332             Init();
333         }
334         private void Init()
335         {
336             pane1->SetSize(SplitSize(orientationsplitterDistance));
337             pane1->SetDock(SplitDock(orientation));
338             AddChild(pane1);
339             splitter->SetContainer(this);
340             AddChild(splitter);
341             pane2->SetLocation(Location());
342             pane2->SetDock(Dock.fill);
343             AddChild(pane2);
344         }
345         public override void OnPaint(PaintEventArgs& args)
346         {
347             Size size = GetSize();
348             if (splitterDistance == 0)
349             {
350                 int s = 0;
351                 switch (orientation)
352                 {
353                     case Orientation.horizontal: s = size.w; break;
354                     case Orientation.vertical: s = size.h; break;
355                 }
356                 int d = cast<int>((2.0 / 3.0) * s);
357                 SetSplitterDistance(d);
358             }
359             base->OnPaint(args);
360         }
361         public override void PrintWindowTree(int level)
362         {
363             LogView* log = Application.GetLogView();
364             if (log != null)
365             {
366                 log->WriteLine(string(' 'level) + "SplitContainer." + Text() + ".handle=" + ToHexString(cast<ulong>(Handle())) + " " + ParentText() + "[" + Rect(Point()GetSize()).ToString() + "]");
367             }
368             Component* child = Children().FirstChild();
369             while (child != null)
370             {
371                 if (child is Control*)
372                 {
373                     Control* childControl = cast<Control*>(child);
374                     childControl->PrintWindowTree(level + 1);
375                 }
376                 child = child->NextSibling();
377             }
378         }
379         public inline nothrow Orientation GetOrientation() const
380         {
381             return orientation;
382         }
383         public inline nothrow ContainerControl* Pane1Container() const
384         {
385             return pane1->GetContainerControl();
386         }
387         public inline nothrow Splitter* GetSplitter() const
388         {
389             return splitter;
390         }
391         public inline nothrow ContainerControl* Pane2Container() const
392         {
393             return pane2->GetContainerControl();
394         }
395         public inline nothrow int SplitterDistance() const
396         {
397             return splitterDistance;
398         }
399         public void SetSplitterDistance(int splitterDistance_)
400         {
401             int prevSplitterDistance = splitterDistance;
402             splitterDistance = splitterDistance_;
403             switch (orientation)
404             {
405                 case Orientation.horizontal: pane1->SetSize(Size(splitterDistancepane1->GetSize().h)); break;
406                 case Orientation.vertical: pane1->SetSize(Size(pane1->GetSize().wsplitterDistance)); break;
407             }
408             DockChildren();
409             Invalidate();
410             if (prevSplitterDistance != splitterDistance)
411             {
412                 OnSplitterDistanceChanged();
413             }
414         }
415         public virtual void OnSplitterDistanceChanged()
416         {
417             splitterDistanceChangedEvent.Fire();
418         }
419         public nothrow Control* Pane1() const
420         {
421             return pane1;
422         }
423         public nothrow Control* Pane2() const
424         {
425             return pane2;
426         }
427         public nothrow Event<SplitterEventHandler>& SplitterDistanceChangedEvent() const
428         {
429             return splitterDistanceChangedEvent;
430         }
431         private Orientation orientation;
432         private int splitterDistance;
433         private Control* pane1;
434         private Splitter* splitter;
435         private Control* pane2;
436         private Event<SplitterEventHandler> splitterDistanceChangedEvent;
437     }
438 }