1
2
3
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 horizontal, vertical
16 }
17
18 public 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 Size SplitSize(Orientation orientation, int width)
29 {
30 switch (orientation)
31 {
32 case Orientation.horizontal: return Size(width, 0);
33 case Orientation.vertical: return Size(0, width);
34 }
35 return Size();
36 }
37
38 public Color DefaultSplitterEdgeColor()
39 {
40 return Color(204u, 206u, 219u);
41 }
42
43 public const int defaultSplitterWidth = 5;
44
45 public ControlCreateParams& SplitterControlCreateParams(ControlCreateParams& controlCreateParams, Orientation orientation, int width)
46 {
47 return controlCreateParams.SetWindowClassName("System.Windows.Splitter").SetSize(SplitSize(orientation, width)).SetDock(SplitDock(orientation));
48 }
49
50 public class SplitterCreateParams
51 {
52 public SplitterCreateParams(ControlCreateParams& controlCreateParams_) :
53 controlCreateParams(controlCreateParams_),
54 orientation(Orientation.horizontal),
55 edgeColor(DefaultSplitterEdgeColor())
56 {
57 }
58 public SplitterCreateParams& Defaults()
59 {
60 return *this;
61 }
62 public SplitterCreateParams& SetOrientation(Orientation orientation_)
63 {
64 orientation = orientation_;
65 return *this;
66 }
67 public 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 = 0, moveSplitter = 1 << 0
82 }
83 public Splitter(Orientation orientation_, const Color& backgroundColor, const Color& edgeColor_, const Point& location, int width) :
84 base("System.Windows.Splitter", DefaultWindowClassStyle(), DefaultChildWindowStyle(), DefaultExtendedWindowStyle(),
85 backgroundColor, "splitter", location, SplitSize(orientation_, width), SplitDock(orientation_), Anchors.none),
86 orientation(orientation_), horizontalSplitterCursor(), verticalSplitterCursor(), flags(), container(null),
87 edgeColor(edgeColor_), edgePen(edgeColor)
88 {
89 auto result = LoadCursors();
90 if (result.Error())
91 {
92 SetErrorId(result.GetErrorId());
93 }
94 }
95 public Splitter(Orientation orientation, const Point& location) :
96 this(orientation, DefaultControlBackgroundColor(), DefaultSplitterEdgeColor(), location, defaultSplitterWidth)
97 {
98 }
99 public Splitter(SplitterCreateParams& createParams) :
100 base(createParams.controlCreateParams),
101 orientation(createParams.orientation), horizontalSplitterCursor(), verticalSplitterCursor(), flags(), container(null),
102 edgeColor(createParams.edgeColor), edgePen(edgeColor)
103 {
104 auto result = LoadCursors();
105 if (result.Error())
106 {
107 SetErrorId(result.GetErrorId());
108 }
109 }
110 private Result<bool> LoadCursors()
111 {
112 auto result = Application.GetResourceManager().GetCursor("horizontal.splitter.system.windows.cursor");
113 if (result.Error())
114 {
115 return Result<bool>(ErrorId(result.GetErrorId()));
116 }
117 horizontalSplitterCursor = result.Value();
118 result = Application.GetResourceManager().GetCursor("vertical.splitter.system.windows.cursor");
119 if (result.Error())
120 {
121 return Result<bool>(ErrorId(result.GetErrorId()));
122 }
123 verticalSplitterCursor = result.Value();
124 return Result<bool>(true);
125 }
126 public void SetContainer(SplitContainer* container_)
127 {
128 container = container_;
129 }
130 [nodiscard]
131 public override Result<bool> PrintWindowTree(int level)
132 {
133 LogView* log = Application.GetLogView();
134 if (log != null)
135 {
136 auto hexStringResult = ToHexString(cast<ulong>(Handle()));
137 if (hexStringResult.Error())
138 {
139 return Result<bool>(ErrorId(hexStringResult.GetErrorId()));
140 }
141 auto parentTextResult = ParentText();
142 if (parentTextResult.Error())
143 {
144 return Result<bool>(ErrorId(parentTextResult.GetErrorId()));
145 }
146 auto result = log->WriteLine(string(' ', level) + "Splitter." + Text() + ".handle=" + hexStringResult.Value() + " " +
147 parentTextResult.Value() + "[" + Rect(Point(), GetSize()).ToString() + "]");
148 if (result.Error()) return result;
149 }
150 return Result<bool>(true);
151 }
152 [nodiscard]
153 protected override Result<bool> OnPaint(PaintEventArgs& args)
154 {
155 if (Debug.Paint())
156 {
157 Rect r(Point(), GetSize());
158 LogView* log = Application.GetLogView();
159 if (log != null)
160 {
161 auto result = log->WriteLine("Splitter.OnPaint: " + r.ToString());
162 if (result.Error()) return result;
163 }
164 }
165 auto result = args.graphics.Clear(BackgroundColor());
166 if (result.Error())
167 {
168 return Result<bool>(ErrorId(result.GetErrorId()));
169 }
170 Size size = GetSize();
171 LogView* log = Application.GetLogView();
172 switch (orientation)
173 {
174 case Orientation.horizontal:
175 {
176 auto result = args.graphics.DrawLine(edgePen, Point(0, 0), Point(0, size.h - 1));
177 if (result.Error())
178 {
179 return Result<bool>(ErrorId(result.GetErrorId()));
180 }
181 result = args.graphics.DrawLine(edgePen, Point(size.w - 1, 0), Point(size.w - 1, size.h - 1));
182 if (result.Error())
183 {
184 return Result<bool>(ErrorId(result.GetErrorId()));
185 }
186 break;
187 }
188 case Orientation.vertical:
189 {
190 auto result = args.graphics.DrawLine(edgePen, Point(0, 0), Point(size.w - 1, 0));
191 if (result.Error())
192 {
193 return Result<bool>(ErrorId(result.GetErrorId()));
194 }
195 result = args.graphics.DrawLine(edgePen, Point(0, size.h - 1), Point(size.w - 1, size.h - 1));
196 if (result.Error())
197 {
198 return Result<bool>(ErrorId(result.GetErrorId()));
199 }
200 break;
201 }
202 }
203 return base->OnPaint(args);
204 }
205 protected override Result<bool> SetCursor()
206 {
207 switch (orientation)
208 {
209 case Orientation.horizontal: SetCursor(horizontalSplitterCursor); break;
210 case Orientation.vertical: SetCursor(verticalSplitterCursor); break;
211 }
212 return Result<bool>(true);
213 }
214 [nodiscard]
215 protected override Result<bool> OnMouseDown(MouseEventArgs& args)
216 {
217 auto result = base->OnMouseDown(args);
218 if (result.Error()) return result;
219 if (args.buttons == MouseButtons.lbutton)
220 {
221 SetFlag(Flags.moveSplitter);
222 switch (orientation)
223 {
224 case Orientation.horizontal:
225 {
226 x = args.location.x;
227 break;
228 }
229 case Orientation.vertical:
230 {
231 y = args.location.y;
232 break;
233 }
234 }
235 WinSetCapture(Handle());
236 }
237 return Result<bool>(true);
238 }
239 [nodiscard]
240 protected override Result<bool> OnMouseMove(MouseEventArgs& args)
241 {
242 if (GetFlag(Flags.moveSplitter))
243 {
244 switch (orientation)
245 {
246 case Orientation.horizontal:
247 {
248 int dx = args.location.x - x;
249 auto result = container->SetSplitterDistance(container->SplitterDistance() + dx);
250 if (result.Error()) return result;
251 break;
252 }
253 case Orientation.vertical:
254 {
255 int dy = args.location.y - y;
256 auto result = container->SetSplitterDistance(container->SplitterDistance() + dy);
257 if (result.Error()) return result;
258 break;
259 }
260 }
261 }
262 return Result<bool>(true);
263 }
264 [nodiscard]
265 protected override Result<bool> OnMouseUp(MouseEventArgs& args)
266 {
267 auto result = base->OnMouseUp(args);
268 if (result.Error()) return result;
269 if (GetFlag(Flags.moveSplitter))
270 {
271 ResetFlag(Flags.moveSplitter);
272 WinReleaseCapture();
273 switch (orientation)
274 {
275 case Orientation.horizontal:
276 {
277 int dx = args.location.x - x;
278 auto result = container->SetSplitterDistance(container->SplitterDistance() + dx);
279 if (result.Error()) return result;
280 break;
281 }
282 case Orientation.vertical:
283 {
284 int dy = args.location.y - y;
285 auto result = container->SetSplitterDistance(container->SplitterDistance() + dy);
286 if (result.Error()) return result;
287 break;
288 }
289 }
290 }
291 return Result<bool>(true);
292 }
293 private inline void SetFlag(Flags flag)
294 {
295 flags = cast<Flags>(flags | flag);
296 }
297 private inline void ResetFlag(Flags flag)
298 {
299 flags = cast<Flags>(flags & ~flag);
300 }
301 private inline bool GetFlag(Flags flag) const
302 {
303 return (flags & flag) != 0;
304 }
305 private Orientation orientation;
306 private Cursor* horizontalSplitterCursor;
307 private Cursor* verticalSplitterCursor;
308 private Flags flags;
309 private int x;
310 private int y;
311 private SplitContainer* container;
312 private Color edgeColor;
313 private Pen edgePen;
314 }
315
316 public ControlCreateParams& SplitContainerControlCreateParams(ControlCreateParams& controlCreateParams)
317 {
318 return controlCreateParams.SetWindowClassName("System.Windows.SplitContainer");
319 }
320
321 public class SplitContainerCreateParams
322 {
323 public SplitContainerCreateParams(ControlCreateParams& controlCreateParams_,
324 Orientation orientation_, Control* pane1_, Splitter* splitter_, Control* pane2_, int splitterDistance_) :
325 controlCreateParams(controlCreateParams_), orientation(orientation_), pane1(pane1_), splitter(splitter_), pane2(pane2_),
326 splitterWidth(defaultSplitterWidth), splitterDistance(splitterDistance_)
327 {
328 }
329 public SplitContainerCreateParams& Defaults()
330 {
331 return *this;
332 }
333 public SplitContainerCreateParams& SetSplitterWidth(int splitterWidth_)
334 {
335 splitterWidth = splitterWidth_;
336 return *this;
337 }
338 public SplitContainerCreateParams& SetSplitterDistance(int splitterDistance_)
339 {
340 splitterDistance = splitterDistance_;
341 return *this;
342 }
343 public ControlCreateParams& controlCreateParams;
344 public Orientation orientation;
345 public Control* pane1;
346 public Splitter* splitter;
347 public Control* pane2;
348 public int splitterWidth;
349 public int splitterDistance;
350 }
351
352 public class SplitContainer : ContainerControl
353 {
354 public SplitContainer(Orientation orientation_, Control* pane1_, Splitter* splitter_, Control* pane2_, int splitterWidth, int splitterDistance_,
355 const Point& location, const Size& size, Dock dock, Anchors anchors) :
356 base("System.Windows.SplitContainer", DefaultWindowClassStyle(), DefaultChildWindowStyle(), DefaultExtendedWindowStyle(),
357 DefaultControlBackgroundColor(), "splitContainer", location, size, dock, anchors),
358 orientation(orientation_), pane1(pane1_), splitter(splitter_), pane2(pane2_), splitterDistance(splitterDistance_)
359 {
360 Init();
361 }
362 public SplitContainer(Orientation orientation, int splitterDistance, const Point& location, const Size& size, Dock dock, Anchors anchors) :
363 this(orientation,
364 new Panel(location, SplitSize(orientation, splitterDistance), SplitDock(orientation), Anchors.none),
365 new Splitter(orientation, location),
366 new Panel(location, SplitSize(orientation, splitterDistance), SplitDock(orientation), Anchors.none),
367 defaultSplitterWidth, splitterDistance, location, size, dock, anchors)
368 {
369 }
370 public SplitContainer(SplitContainerCreateParams& createParams) :
371 base(createParams.controlCreateParams),
372 orientation(createParams.orientation),
373 pane1(createParams.pane1), splitter(createParams.splitter), pane2(createParams.pane2), splitterDistance(createParams.splitterDistance)
374 {
375 Init();
376 }
377 private void Init()
378 {
379 auto result = pane1->SetSize(SplitSize(orientation, splitterDistance));
380 if (result.Error())
381 {
382 SetErrorId(result.GetErrorId());
383 return;
384 }
385 result = pane1->SetDock(SplitDock(orientation));
386 if (result.Error())
387 {
388 SetErrorId(result.GetErrorId());
389 return;
390 }
391 result = AddChild(pane1);
392 if (result.Error())
393 {
394 SetErrorId(result.GetErrorId());
395 return;
396 }
397 splitter->SetContainer(this);
398 result = AddChild(splitter);
399 if (result.Error())
400 {
401 SetErrorId(result.GetErrorId());
402 return;
403 }
404 auto locationResult = Location();
405 if (locationResult.Error())
406 {
407 SetErrorId(locationResult.GetErrorId());
408 return;
409 }
410 Point location = locationResult.Value();
411 result = pane2->SetLocation(location);
412 if (result.Error())
413 {
414 SetErrorId(result.GetErrorId());
415 return;
416 }
417 result = pane2->SetDock(Dock.fill);
418 if (result.Error())
419 {
420 SetErrorId(result.GetErrorId());
421 return;
422 }
423 result = AddChild(pane2);
424 if (result.Error())
425 {
426 SetErrorId(result.GetErrorId());
427 return;
428 }
429 }
430 [nodiscard]
431 public override Result<bool> OnPaint(PaintEventArgs& args)
432 {
433 Size size = GetSize();
434 if (splitterDistance == 0)
435 {
436 int s = 0;
437 switch (orientation)
438 {
439 case Orientation.horizontal: s = size.w; break;
440 case Orientation.vertical: s = size.h; break;
441 }
442 int d = cast<int>((2.000000 / 3.000000) * s);
443 auto result = SetSplitterDistance(d);
444 if (result.Error()) return result;
445 }
446 return base->OnPaint(args);
447 }
448 [nodiscard]
449 public override Result<bool> PrintWindowTree(int level)
450 {
451 LogView* log = Application.GetLogView();
452 if (log != null)
453 {
454 auto hexStringResult = ToHexString(cast<ulong>(Handle()));
455 if (hexStringResult.Error())
456 {
457 return Result<bool>(ErrorId(hexStringResult.GetErrorId()));
458 }
459 auto parentTextResult = ParentText();
460 if (parentTextResult.Error())
461 {
462 return Result<bool>(ErrorId(parentTextResult.GetErrorId()));
463 }
464 auto result = log->WriteLine(string(' ', level) + "SplitContainer." + Text() + ".handle=" + hexStringResult.Value() + " " + parentTextResult.Value() +
465 "[" + Rect(Point(), GetSize()).ToString() + "]");
466 if (result.Error()) return result;
467 }
468 Component* child = Children().FirstChild();
469 while (child != null)
470 {
471 if (child is Control*)
472 {
473 Control* childControl = cast<Control*>(child);
474 auto result = childControl->PrintWindowTree(level + 1);
475 if (result.Error())
476 {
477 return Result<bool>(ErrorId(result.GetErrorId()));
478 }
479 }
480 child = child->NextSibling();
481 }
482 return Result<bool>(true);
483 }
484 public inline Orientation GetOrientation() const
485 {
486 return orientation;
487 }
488 public inline ContainerControl* Pane1Container() const
489 {
490 return pane1->GetContainerControl();
491 }
492 public inline Splitter* GetSplitter() const
493 {
494 return splitter;
495 }
496 public inline ContainerControl* Pane2Container() const
497 {
498 return pane2->GetContainerControl();
499 }
500 public inline int SplitterDistance() const
501 {
502 return splitterDistance;
503 }
504 [nodiscard]
505 public Result<bool> SetSplitterDistance(int splitterDistance_)
506 {
507 int prevSplitterDistance = splitterDistance;
508 splitterDistance = splitterDistance_;
509 switch (orientation)
510 {
511 case Orientation.horizontal:
512 {
513 auto result = pane1->SetSize(Size(splitterDistance, pane1->GetSize().h));
514 if (result.Error()) return result;
515 break;
516 }
517 case Orientation.vertical:
518 {
519 auto result = pane1->SetSize(Size(pane1->GetSize().w, splitterDistance));
520 if (result.Error()) return result;
521 break;
522 }
523 }
524 auto result = DockChildren();
525 if (result.Error()) return result;
526 result = Invalidate();
527 if (result.Error()) return result;
528 if (prevSplitterDistance != splitterDistance)
529 {
530 OnSplitterDistanceChanged();
531 }
532 return Result<bool>(true);
533 }
534 public virtual void OnSplitterDistanceChanged()
535 {
536 splitterDistanceChangedEvent.Fire();
537 }
538 public Control* Pane1() const
539 {
540 return pane1;
541 }
542 public Control* Pane2() const
543 {
544 return pane2;
545 }
546 public Event<SplitterEventHandler>& SplitterDistanceChangedEvent() const
547 {
548 return splitterDistanceChangedEvent;
549 }
550 private Orientation orientation;
551 private int splitterDistance;
552 private Control* pane1;
553 private Splitter* splitter;
554 private Control* pane2;
555 private Event<SplitterEventHandler> splitterDistanceChangedEvent;
556 }