1
2
3
4
5
6 using System;
7 using System.Collections;
8
9 namespace System.Windows
10 {
11 public class delegate void TreeViewNodeMouseClickEventHandler(TreeViewNodeMouseClickEventArgs& args);
12 public class delegate void TreeViewNodeEventHandler(TreeViewNodeEventArgs& args);
13
14 public string DefaultTreeViewNormalNodeFontFamilyName()
15 {
16 return "Segoe UI";
17 }
18
19 public float DefaultTreeViewNormalNodeFontSize()
20 {
21 return 9.000000f;
22 }
23
24 public FontStyle DefaultTreeViewNormalNodeFontStyle()
25 {
26 return FontStyle.regular;
27 }
28
29 public string DefaultTreeViewActiveNodeFontFamilyName()
30 {
31 return "Segoe UI";
32 }
33
34 public float DefaultTreeViewActiveNodeFontSize()
35 {
36 return 9.000000f;
37 }
38
39 public FontStyle DefaultTreeViewActiveNodeFontStyle()
40 {
41 return FontStyle.bold;
42 }
43
44 public Color DefaultTreeViewBackgroundColor()
45 {
46 return Color.White();
47 }
48
49 public Color DefaultTreeViewStateIndicatorColor()
50 {
51 return Color.Red();
52 }
53
54 public Color DefaultTreeViewNodeSelectedColor()
55 {
56 return Color(201u, 222u, 245u);
57 }
58
59 public float DefaultTreeViewStateIndicatorPercentage()
60 {
61 return 50.000000f;
62 }
63
64 public float DefaultTreeViewNodeIndentPercent()
65 {
66 return 200.000000f;
67 }
68
69 public float DefaultTreeViewNodeTextIndentPercent()
70 {
71 return 100.000000f;
72 }
73
74 public Padding DefaultTreeViewNodeImagePadding()
75 {
76 return Padding(0, 0, 0, 0);
77 }
78
79 public ControlCreateParams& TreeViewControlCreateParams(ControlCreateParams& controlCreateParams)
80 {
81 return controlCreateParams.SetWindowClassName("System.Windows.TreeView").SetWindowClassStyle(DoubleClickWindowClassStyle()).
82 SetWindowClassBackgroundColor(SystemColor.COLOR_WINDOW).SetBackgroundColor(DefaultTreeViewBackgroundColor());
83 }
84
85 public class TreeViewCreateParams
86 {
87 public TreeViewCreateParams(ControlCreateParams& controlCreateParams_) :
88 controlCreateParams(controlCreateParams_),
89 normalNodeFontFamilyName(DefaultTreeViewNormalNodeFontFamilyName()),
90 normalNodeFontSize(DefaultTreeViewNormalNodeFontSize()),
91 normalNodeFontStyle(DefaultTreeViewNormalNodeFontStyle()),
92 activeNodeFontFamilyName(DefaultTreeViewActiveNodeFontFamilyName()),
93 activeNodeFontSize(DefaultTreeViewActiveNodeFontSize()),
94 activeNodeFontStyle(DefaultTreeViewActiveNodeFontStyle()),
95 stateIndicatorColor(DefaultTreeViewStateIndicatorColor()),
96 nodeSelectedColor(DefaultTreeViewNodeSelectedColor()),
97 stateIndicatorPercentage(DefaultTreeViewStateIndicatorPercentage()),
98 nodeIndentPercent(DefaultTreeViewNodeIndentPercent()),
99 nodeTextIndentPercent(DefaultTreeViewNodeTextIndentPercent()),
100 nodeImagePadding(DefaultTreeViewNodeImagePadding())
101 {
102 }
103 public TreeViewCreateParams& Defaults()
104 {
105 return *this;
106 }
107 public TreeViewCreateParams& SetNormalNodeFontFamilyName(const string& normalNodeFontFamilyName_)
108 {
109 normalNodeFontFamilyName = normalNodeFontFamilyName_;
110 return *this;
111 }
112 public TreeViewCreateParams& SetNormalNodeFontSize(float normalNodeFontSize_)
113 {
114 normalNodeFontSize = normalNodeFontSize_;
115 return *this;
116 }
117 public TreeViewCreateParams& SetNormalNodeFontStyle(FontStyle normalNodeFontStyle_)
118 {
119 normalNodeFontStyle = normalNodeFontStyle_;
120 return *this;
121 }
122 public TreeViewCreateParams& SetActiveNodeFontFamilyName(const string& activeNodeFontFamilyName_)
123 {
124 activeNodeFontFamilyName = activeNodeFontFamilyName_;
125 return *this;
126 }
127 public TreeViewCreateParams& SetActiveNodeFontSize(float activeNodeFontSize_)
128 {
129 activeNodeFontSize = activeNodeFontSize_;
130 return *this;
131 }
132 public TreeViewCreateParams& SetActiveNodeFontStyle(FontStyle activeNodeFontStyle_)
133 {
134 activeNodeFontStyle = activeNodeFontStyle_;
135 return *this;
136 }
137 public TreeViewCreateParams& SetStateIndicatorColor(const Color& stateIndicatorColor_)
138 {
139 stateIndicatorColor = stateIndicatorColor_;
140 return *this;
141 }
142 public TreeViewCreateParams& SetNodeSelectedColor(const Color& nodeSelectedColor_)
143 {
144 nodeSelectedColor = nodeSelectedColor_;
145 return *this;
146 }
147 public TreeViewCreateParams& SetTextColor(const Color& textColor_)
148 {
149 textColor = textColor_;
150 return *this;
151 }
152 public TreeViewCreateParams& SetStateIndicatorPercentage(float stateIndicatorPercentage_)
153 {
154 stateIndicatorPercentage = stateIndicatorPercentage_;
155 return *this;
156 }
157 public TreeViewCreateParams& SetNodeIndentPercent(float nodeIndentPercent_)
158 {
159 nodeIndentPercent = nodeIndentPercent_;
160 return *this;
161 }
162 public TreeViewCreateParams& SetNodeTextIndentPercent(float nodeTextIndentPercent_)
163 {
164 nodeTextIndentPercent = nodeTextIndentPercent_;
165 return *this;
166 }
167 public TreeViewCreateParams& SetNodeImagePadding(const Padding& nodeImagePadding_)
168 {
169 nodeImagePadding = nodeImagePadding_;
170 return *this;
171 }
172 public ControlCreateParams& controlCreateParams;
173 public string normalNodeFontFamilyName;
174 public float normalNodeFontSize;
175 public FontStyle normalNodeFontStyle;
176 public string activeNodeFontFamilyName;
177 public float activeNodeFontSize;
178 public FontStyle activeNodeFontStyle;
179 public Color stateIndicatorColor;
180 public Color nodeSelectedColor;
181 public Color textColor;
182 public float stateIndicatorPercentage;
183 public float nodeIndentPercent;
184 public float nodeTextIndentPercent;
185 public Padding nodeImagePadding;
186 }
187
188 public class TreeView : Control
189 {
190 private enum Flags : sbyte
191 {
192 none = 0, changed = 1 << 0, treeViewNodeChanged = 1 << 1, treeViewNodeStateChanged = 1 << 2, toolTipWindowAdded = 1 << 3
193 }
194 public TreeView(const Font& normalNodeFont_, const Font& activeNodeFont_,
195 const Color& backgroundColor, const Color& stateIndicatorColor_, const Color& nodeSelectedColor_, const Color& textColor_,
196 const Point& location, const Size& size, Dock dock, Anchors anchors) :
197 base("System.Windows.TreeView", DoubleClickWindowClassStyle(), DefaultChildWindowStyle(), DefaultExtendedWindowStyle(),
198 backgroundColor, "treeView",
199 location, size, dock, anchors), flags(Flags.none), root(), normalNodeFont(normalNodeFont_), activeNodeFont(activeNodeFont_),
200 textHeight(0), stateIndicatorPercentage(50.000000f),
201 stateIndicatorColor(stateIndicatorColor_), nodeSelectedColor(nodeSelectedColor_), nodeSelectedBrush(nodeSelectedColor),
202 textColor(textColor_), textBrush(textColor),
203 nodeIndentPercent(DefaultTreeViewNodeIndentPercent()),
204 nodeTextIndentPercent(DefaultTreeViewNodeTextIndentPercent()),
205 format(StringAlignment.near, StringAlignment.near), bitmapIndex(-1), animationMs(5), selectedNode(null), trackedNode(null),
206 toolTipWindow(new ToolTip())
207 {
208 SetChanged();
209 SetMouseHoverMs(DefaultMouseHoverMs());
210 }
211 public TreeView(const Point& location, const Size& size, Dock dock, Anchors anchors) :
212 this(Font(FontFamily("Segoe UI"), 9.000000f), Font(FontFamily("Segoe UI"), 9.000000f, FontStyle.bold, Unit.point),
213 Color.White(), Color.Red(), Color(201u, 222u, 245u), Color.Black(), location, size, dock, anchors)
214 {
215 }
216 public TreeView(TreeViewCreateParams& createParams) :
217 base(createParams.controlCreateParams),
218 flags(Flags.none), root(),
219 normalNodeFont(
220 Font(FontFamily(createParams.normalNodeFontFamilyName),
221 createParams.normalNodeFontSize,
222 createParams.normalNodeFontStyle,
223 Unit.point)),
224 activeNodeFont(
225 Font(FontFamily(createParams.activeNodeFontFamilyName),
226 createParams.activeNodeFontSize,
227 createParams.activeNodeFontStyle,
228 Unit.point)),
229 textHeight(0), stateIndicatorPercentage(createParams.stateIndicatorPercentage),
230 stateIndicatorColor(createParams.stateIndicatorColor),
231 nodeSelectedColor(createParams.nodeSelectedColor), nodeSelectedBrush(nodeSelectedColor),
232 textColor(createParams.textColor), textBrush(textColor),
233 nodeIndentPercent(createParams.nodeIndentPercent),
234 nodeTextIndentPercent(createParams.nodeTextIndentPercent),
235 nodeImagePadding(createParams.nodeImagePadding),
236 format(StringAlignment.near, StringAlignment.near), bitmapIndex(-1), animationMs(5),
237 selectedNode(null), trackedNode(null), toolTipWindow(new ToolTip())
238 {
239 SetChanged();
240 SetMouseHoverMs(DefaultMouseHoverMs());
241 }
242 public ~TreeView()
243 {
244 if (toolTipWindow != null && ToolTipWindowAdded())
245 {
246 ResetToolTipWindowAdded();
247 Window* window = GetWindow();
248 if (window != null)
249 {
250 auto result = window->RemoveChild(toolTipWindow);
251 toolTipWindow = null;
252 }
253 }
254 }
255 [nodiscard]
256 public override Result<bool> PrintWindowTree(int level)
257 {
258 LogView* log = Application.GetLogView();
259 if (log != null)
260 {
261 auto handleResult = ToHexString(cast<ulong>(Handle()));
262 if (handleResult.Error())
263 {
264 return Result<bool>(ErrorId(handleResult.GetErrorId()));
265 }
266 const string& handleStr = handleResult.Value();
267 auto parentTextResult = ParentText();
268 if (parentTextResult.Error())
269 {
270 return Result<bool>(ErrorId(parentTextResult.GetErrorId()));
271 }
272 const string& parentText = parentTextResult.Value();
273 auto result = log->WriteLine(string(' ', level) + "TreeView." + Text() + ".handle=" + handleStr + " " + parentText + "[" +
274 Rect(Point(), GetSize()).ToString() + "]");
275 if (result.Error()) return result;
276 }
277 return Result<bool>(true);
278 }
279 [nodiscard]
280 public Result<bool> EnsureNodeVisible(TreeViewNode* node)
281 {
282 Size size = GetSize();
283 Point contentLocation = ContentLocation();
284 Point nodeLocation = node->Location();
285 if (nodeLocation.y >= contentLocation.y && nodeLocation.y < contentLocation.y + size.h)
286 {
287 return Result<bool>(false);
288 }
289 if (nodeLocation.y > contentLocation.y)
290 {
291 int y = nodeLocation.y - Max(cast<int>(0), cast<int>(size.h - 2 * textHeight));
292 Point newOrigin(0, y);
293 SetContentLocation(newOrigin);
294 auto result = Invalidate();
295 if (result.Error()) return result;
296 }
297 else
298 {
299 int y = nodeLocation.y;
300 Point newOrigin(0, y);
301 SetContentLocation(newOrigin);
302 auto result = Invalidate();
303 if (result.Error()) return result;
304 }
305 return Result<bool>(true);
306 }
307 public bool NodeVisible(TreeViewNode* node) const
308 {
309 Size size = GetSize();
310 Point contentLocation = ContentLocation();
311 Point nodeLocation = node->Location();
312 if (nodeLocation.y >= contentLocation.y && nodeLocation.y < contentLocation.y + size.h)
313 {
314 return true;
315 }
316 else
317 {
318 return false;
319 }
320 }
321 public List<TreeViewNode*> GetVisibleNodes(int level) const
322 {
323 List<TreeViewNode*> visibleNodes;
324 root->GetVisibleNodes(visibleNodes, this, level);
325 return visibleNodes;
326 }
327 public List<TreeViewNode*> GetVisibleNodes() const
328 {
329 return GetVisibleNodes(-1);
330 }
331 [nodiscard]
332 protected override Result<bool> OnPaint(PaintEventArgs& args)
333 {
334 if (Debug.Paint())
335 {
336 Rect r(Point(), GetSize());
337 LogView* log = Application.GetLogView();
338 if (log != null)
339 {
340 auto result = log->WriteLine("TreeView.OnPaint: " + r.ToString());
341 if (result.Error()) return result;
342 }
343 }
344 if (!ToolTipWindowAdded())
345 {
346 SetToolTipWindowAdded();
347 Window* window = GetWindow();
348 if (window != null)
349 {
350 auto result = window->AddChild(toolTipWindow);
351 if (result.Error())
352 {
353 return Result<bool>(ErrorId(result.GetErrorId()));
354 }
355 }
356 }
357 if (Changed())
358 {
359 ResetChanged();
360 auto measureResult = Measure(args.graphics);
361 if (measureResult.Error())
362 {
363 return Result<bool>(ErrorId(measureResult.GetErrorId()));
364 }
365 MakeStateIndicatorBitmaps(args.graphics);
366 SetTreeViewNodeChanged();
367 SetTreeViewNodeStateChanged();
368 }
369 if (TreeViewNodeChanged())
370 {
371 ResetTreeViewNodeChanged();
372 if (!root.IsNull())
373 {
374 auto result = root->MeasureSize(args.graphics, this);
375 if (result.Error()) return result;
376 }
377 }
378 if (TreeViewNodeStateChanged())
379 {
380 ResetTreeViewNodeStateChanged();
381 if (!root.IsNull())
382 {
383 Rect r(Point(), GetSize());
384 r.size.w = r.size.w - 1;
385 r.size.h = r.size.h - 1;
386 int idx = 0;
387 Rect rect;
388 auto measureResult = root->Measure(args.graphics, r.location, this, 0, idx, rect);
389 if (measureResult.Error())
390 {
391 return Result<bool>(ErrorId(measureResult.GetErrorId()));
392 }
393 auto result = SetContentSize(rect.size);
394 if (result.Error()) return result;
395 }
396 }
397 auto clearResult = args.graphics.Clear(BackgroundColor());
398 if (clearResult.Error())
399 {
400 return Result<bool>(ErrorId(clearResult.GetErrorId()));
401 }
402 if (bitmapIndex != -1)
403 {
404 Point location;
405 PointF loc(location.x, location.y);
406 auto drawResult = args.graphics.DrawImage(stateIndicatorBitmaps[bitmapIndex], loc);
407 if (drawResult.Error())
408 {
409 return Result<bool>(ErrorId(drawResult.GetErrorId()));
410 }
411 }
412 else
413 {
414 if (!root.IsNull())
415 {
416 auto drawResult = root->Draw(args.graphics, this);
417 if (drawResult.Error())
418 {
419 return Result<bool>(ErrorId(drawResult.GetErrorId()));
420 }
421 }
422 auto paintResult = base->OnPaint(args);
423 if (paintResult.Error())
424 {
425 return Result<bool>(ErrorId(paintResult.GetErrorId()));
426 }
427 }
428 return Result<bool>(true);
429 }
430 public inline TreeViewNode* Root() const
431 {
432 return root.Get();
433 }
434 public TreeViewNode* ReleaseRoot()
435 {
436 return root.Release();
437 }
438 public void ResetRoot(TreeViewNode* root_)
439 {
440 root.Reset(root_);
441 }
442 [nodiscard]
443 public Result<bool> SetRoot(TreeViewNode* root_)
444 {
445 if (!root.IsNull())
446 {
447 root->SetTreeView(null);
448 }
449 root.Reset(root_);
450 SetContentLocation(Point(0, 0));
451 if (!root.IsNull())
452 {
453 root->SetTreeView(this);
454 SetTreeViewNodeChanged();
455 SetTreeViewNodeStateChanged();
456 SetContentChanged();
457 }
458 SetChanged();
459 return Invalidate();
460 }
461 public inline const Font& GetNormalNodeFont() const
462 {
463 return normalNodeFont;
464 }
465 public inline const Font& GetActiveNodeFont() const
466 {
467 return activeNodeFont;
468 }
469 public void SetNormalNodeFont(const Font& normalNodeFont_)
470 {
471 normalNodeFont = normalNodeFont_;
472 SetChanged();
473 }
474 public void SetActiveNodeFont(const Font& activeNodeFont_)
475 {
476 activeNodeFont = activeNodeFont_;
477 SetChanged();
478 }
479 public inline const SolidBrush& TextBrush() const
480 {
481 return textBrush;
482 }
483 public inline const SolidBrush& NodeSelectedBrush() const
484 {
485 return nodeSelectedBrush;
486 }
487 public inline float TextHeight() const
488 {
489 return textHeight;
490 }
491 public inline float StateIndicatorPercentage() const
492 {
493 return stateIndicatorPercentage;
494 }
495 public inline void SetStateIndicatorPercentage(float stateIndicatorPercentage_)
496 {
497 stateIndicatorPercentage = stateIndicatorPercentage_;
498 SetChanged();
499 }
500 public inline float StateIndicatorHeight() const
501 {
502 return stateIndicatorHeight;
503 }
504 public inline const Color& StateIndicatorColor() const
505 {
506 return stateIndicatorColor;
507 }
508 public void SetStateIndicatorColor(const Color& stateIndicatorColor_)
509 {
510 stateIndicatorColor = stateIndicatorColor_;
511 SetChanged();
512 }
513 public inline const Color& NodeSelectedColor() const
514 {
515 return nodeSelectedColor;
516 }
517 public void SetNodeSelectedColor(const Color& nodeSelectedColor_)
518 {
519 if (nodeSelectedColor != nodeSelectedColor_)
520 {
521 nodeSelectedColor = nodeSelectedColor_;
522 nodeSelectedBrush = SolidBrush(nodeSelectedColor);
523 }
524 }
525 public inline const Color& TextColor() const
526 {
527 return textColor;
528 }
529 public void SetTextColor(const Color& textColor_)
530 {
531 if (textColor != textColor_)
532 {
533 textColor = textColor_;
534 textBrush = SolidBrush(textColor);
535 SetChanged();
536 }
537 }
538 public inline float NodeIndentPercent() const
539 {
540 return nodeIndentPercent;
541 }
542 public inline float NodeTextIndentPercent() const
543 {
544 return nodeTextIndentPercent;
545 }
546 public inline int AnimationMs() const
547 {
548 return animationMs;
549 }
550 public void SetAnimationMs(int animationMs_)
551 {
552 animationMs = animationMs_;
553 }
554 [nodiscard]
555 public Result<bool> ShowToolTipWindow(const string& toolTipText, TreeViewNode* node)
556 {
557 auto result = toolTipWindow->Hide();
558 if (result.Error()) return result;
559 Point loc = node->Location();
560 Size size = node->GetSize();
561 Point pt(loc.x, loc.y + size.h + 4);
562 if (!node->Children().IsEmpty())
563 {
564 pt.x = pt.x + cast<int>(textHeight + 2.000000f);
565 }
566 TranslateContentLocation(pt);
567 auto clientToScreenResult = ClientToScreen(pt);
568 if (clientToScreenResult.Error())
569 {
570 return Result<bool>(ErrorId(clientToScreenResult.GetErrorId()));
571 }
572 pt = clientToScreenResult.Value();
573 result = toolTipWindow->SetText(toolTipText);
574 if (result.Error()) return result;
575 auto measureResult = toolTipWindow->MeasureExtent();
576 if (measureResult.Error())
577 {
578 return Result<bool>(ErrorId(measureResult.GetErrorId()));
579 }
580 Window* window = GetWindow();
581 if (window != null)
582 {
583 auto screenToClientResult = window->ScreenToClient(pt);
584 if (screenToClientResult.Error())
585 {
586 return Result<bool>(ErrorId(screenToClientResult.GetErrorId()));
587 }
588 pt = screenToClientResult.Value();
589 Size sz = toolTipWindow->GetSize();
590 auto wlocResult = window->Location();
591 if (wlocResult.Error())
592 {
593 return Result<bool>(ErrorId(wlocResult.GetErrorId()));
594 }
595 Point wloc = wlocResult.Value();
596 Size wsz = window->GetSize();
597 if (pt.y + sz.h > wloc.y + wsz.h)
598 {
599 pt.y = wloc.y + wsz.h - sz.h;
600 }
601 if (pt.x + sz.w > wloc.x + wsz.w)
602 {
603 pt.x = wloc.x + wsz.w - sz.w;
604 }
605 result = toolTipWindow->SetLocation(pt);
606 if (result.Error()) return result;
607 result = toolTipWindow->BringToFront();
608 if (result.Error()) return result;
609 result = toolTipWindow->Show();
610 if (result.Error()) return result;
611 result = toolTipWindow->Invalidate();
612 if (result.Error()) return result;
613 toolTipWindow->Update();
614 }
615 return Result<bool>(true);
616 }
617 [nodiscard]
618 public Result<bool> HideToolTipWindow()
619 {
620 return toolTipWindow->Hide();
621 }
622 public Event<TreeViewNodeMouseClickEventHandler, TreeViewNodeMouseClickEventArgs>& NodeClickEvent()
623 {
624 return nodeClickEvent;
625 }
626 public Event<TreeViewNodeMouseClickEventHandler, TreeViewNodeMouseClickEventArgs>& NodeDoubleClickEvent()
627 {
628 return nodeDoubleClickEvent;
629 }
630 public Event<TreeViewNodeEventHandler, TreeViewNodeEventArgs>& NodeEnterEvent()
631 {
632 return nodeEnterEvent;
633 }
634 public Event<TreeViewNodeEventHandler, TreeViewNodeEventArgs>& NodeLeaveEvent()
635 {
636 return nodeLeaveEvent;
637 }
638 public Event<TreeViewNodeEventHandler, TreeViewNodeEventArgs>& NodeSelectedEvent()
639 {
640 return nodeSelectedEvent;
641 }
642 public Event<TreeViewNodeEventHandler, TreeViewNodeEventArgs>& NodeExpandedEvent()
643 {
644 return nodeExpandedEvent;
645 }
646 public Event<TreeViewNodeEventHandler, TreeViewNodeEventArgs>& NodeCollapsedEvent()
647 {
648 return nodeCollapsedEvent;
649 }
650 public Event<TreeViewNodeEventHandler, TreeViewNodeEventArgs>& NodeHoveredEvent()
651 {
652 return nodeHoveredEvent;
653 }
654 [nodiscard]
655 protected override Result<bool> OnMouseDown(MouseEventArgs& args)
656 {
657 auto result = base->OnMouseDown(args);
658 if (result.Error()) return result;
659 if (!root.IsNull())
660 {
661 bool handled = false;
662 result = root->DispatchMouseDown(args, handled);
663 if (result.Error()) return result;
664 if (TreeViewNodeStateChanged())
665 {
666 DeselectNode();
667 result = Invalidate();
668 if (result.Error()) return result;
669 }
670 }
671 return Result<bool>(true);
672 }
673 [nodiscard]
674 protected override Result<bool> OnMouseUp(MouseEventArgs& args)
675 {
676 auto result = base->OnMouseUp(args);
677 if (result.Error()) return result;
678 if (!root.IsNull())
679 {
680 bool handled = false;
681 root->DispatchMouseUp(args, handled);
682 }
683 return Result<bool>(true);
684 }
685 [nodiscard]
686 protected override Result<bool> OnMouseDoubleClick(MouseEventArgs& args)
687 {
688 auto result = base->OnMouseDoubleClick(args);
689 if (result.Error()) return result;
690 if (!root.IsNull())
691 {
692 bool handled = false;
693 result = root->DispatchMouseDoubleClick(args, handled);
694 if (result.Error()) return result;
695 if (TreeViewNodeStateChanged())
696 {
697 DeselectNode();
698 result = Invalidate();
699 if (result.Error()) return result;
700 }
701 }
702 return Result<bool>(true);
703 }
704 [nodiscard]
705 protected override Result<bool> OnMouseEnter(EnterLeaveEventArgs& args)
706 {
707 auto result = base->OnMouseEnter(args);
708 if (result.Error()) return result;
709 trackedNode = null;
710 return Result<bool>(true);
711 }
712 [nodiscard]
713 protected override Result<bool> OnMouseMove(MouseEventArgs& args)
714 {
715 auto result = base->OnMouseMove(args);
716 if (result.Error()) return result;
717 if (!root.IsNull())
718 {
719 bool handled = false;
720 root->DispatchMouseMove(args, handled);
721 if (!handled)
722 {
723 if (trackedNode != null)
724 {
725 if (trackedNode->MouseEntered())
726 {
727 trackedNode->ResetMouseEntered();
728 trackedNode->MouseLeaveInternal();
729 }
730 trackedNode = null;
731 }
732 }
733 }
734 return Result<bool>(true);
735 }
736 [nodiscard]
737 protected override Result<bool> OnMouseLeave(EnterLeaveEventArgs& args)
738 {
739 auto result = base->OnMouseLeave(args);
740 if (result.Error()) return result;
741 if (trackedNode != null)
742 {
743 if (trackedNode->MouseEntered())
744 {
745 trackedNode->ResetMouseEntered();
746 trackedNode->MouseLeaveInternal();
747 }
748 trackedNode = null;
749 }
750 return Result<bool>(true);
751 }
752 protected override void OnMouseHover(MouseEventArgs& args)
753 {
754 base->OnMouseHover(args);
755 if (!root.IsNull())
756 {
757 bool handled = false;
758 root->DispatchMouseHover(args, handled);
759 }
760 }
761 internal inline bool Changed() const
762 {
763 return (flags & Flags.changed) != Flags.none;
764 }
765 public inline void SetChanged()
766 {
767 flags = cast<Flags>(flags | Flags.changed);
768 }
769 private inline void ResetChanged()
770 {
771 flags = cast<Flags>(flags & ~Flags.changed);
772 }
773 internal inline bool TreeViewNodeChanged() const
774 {
775 return (flags & Flags.treeViewNodeChanged) != Flags.none;
776 }
777 internal inline void SetTreeViewNodeChanged()
778 {
779 flags = cast<Flags>(flags | Flags.treeViewNodeChanged);
780 }
781 private inline void ResetTreeViewNodeChanged()
782 {
783 flags = cast<Flags>(flags & ~Flags.treeViewNodeChanged);
784 }
785 internal inline bool TreeViewNodeStateChanged() const
786 {
787 return (flags & Flags.treeViewNodeStateChanged) != Flags.none;
788 }
789 private inline void ResetTreeViewNodeStateChanged()
790 {
791 flags = cast<Flags>(flags & ~Flags.treeViewNodeStateChanged);
792 }
793 internal inline void SetTreeViewNodeStateChanged()
794 {
795 flags = cast<Flags>(flags | Flags.treeViewNodeStateChanged);
796 }
797 private inline bool ToolTipWindowAdded() const
798 {
799 return (flags & Flags.toolTipWindowAdded) != Flags.none;
800 }
801 private inline void SetToolTipWindowAdded()
802 {
803 flags = cast<Flags>(flags | Flags.toolTipWindowAdded);
804 }
805 private inline void ResetToolTipWindowAdded()
806 {
807 flags = cast<Flags>(flags & ~Flags.toolTipWindowAdded);
808 }
809 private Result<bool> Measure(Graphics& graphics)
810 {
811 string s = "RootNode";
812 auto measureStringResult = graphics.MeasureStringRectF(s, normalNodeFont, PointF(0, 0), format);
813 if (measureStringResult.Error())
814 {
815 return Result<bool>(ErrorId(measureStringResult.GetErrorId()));
816 }
817 RectF normalTextRect = measureStringResult.Value();
818 textHeight = normalTextRect.size.h;
819 measureStringResult = graphics.MeasureStringRectF(s, activeNodeFont, PointF(0, 0), format);
820 if (measureStringResult.Error())
821 {
822 return Result<bool>(ErrorId(measureStringResult.GetErrorId()));
823 }
824 RectF activeTextRect = measureStringResult.Value();
825 textHeight = Max(textHeight, activeTextRect.size.h);
826 stateIndicatorHeight = stateIndicatorPercentage * textHeight / 100.000000f;
827 SetScrollUnits(cast<int>(textHeight + 0.500000f), cast<int>(textHeight + 0.500000f));
828 return Result<bool>(true);
829 }
830 private Result<bool> MakeStateIndicatorBitmaps(Graphics& graphics)
831 {
832 SolidBrush stateIndicatorBrush(stateIndicatorColor);
833 stateIndicatorBitmaps.Clear();
834 PointF center(textHeight / 2, textHeight / 2);
835 float w = cast<float>(cast<float>(Sqrt(3.000000) / 2) * stateIndicatorHeight);
836 PointF pt1(center.x - w / 2, center.y - stateIndicatorHeight / 2);
837 PointF pt2(center.x - w / 2, center.y + stateIndicatorHeight / 2);
838 PointF pt3(center.x + w / 2, center.y);
839 for (int i = 9; i >= 0; --i;)
840 {
841 float angle = i * 10.000000f;
842 List<PointF> triangle;
843 triangle.Add(pt1);
844 triangle.Add(pt2);
845 triangle.Add(pt3);
846 Bitmap bm(cast<int>(textHeight), cast<int>(textHeight), graphics);
847 auto imageGraphicsResult = Graphics.FromImage(bm);
848 if (imageGraphicsResult.Error())
849 {
850 return Result<bool>(ErrorId(imageGraphicsResult.GetErrorId()));
851 }
852 Graphics& bmGraphics = imageGraphicsResult.Value();
853 auto clearResult = bmGraphics.Clear(BackgroundColor());
854 if (clearResult.Error())
855 {
856 return Result<bool>(ErrorId(clearResult.GetErrorId()));
857 }
858 if (i > 0)
859 {
860 Matrix id;
861 Result<Matrix> rotateResult = RotateAt(id, angle, center);
862 if (rotateResult.Error())
863 {
864 return Result<bool>(ErrorId(rotateResult.GetErrorId()));
865 }
866 Matrix rm = Rvalue(rotateResult.Value());
867 auto transformResult = rm.TransformPoints(triangle);
868 if (transformResult.Error())
869 {
870 return Result<bool>(ErrorId(transformResult.GetErrorId()));
871 }
872 }
873 List<Point> indicatorTriangle;
874 for (const PointF& pt : triangle)
875 {
876 indicatorTriangle.Add(Point(cast<int>(pt.x), cast<int>(pt.y)));
877 }
878 auto setSmoothingModeResult = bmGraphics.SetSmoothingMode(SmoothingMode.highQuality);
879 if (setSmoothingModeResult.Error())
880 {
881 return Result<bool>(ErrorId(setSmoothingModeResult.GetErrorId()));
882 }
883 auto fillResult = bmGraphics.FillPolygon(stateIndicatorBrush, 3, indicatorTriangle.Begin().Ptr());
884 if (fillResult.Error())
885 {
886 return Result<bool>(ErrorId(fillResult.GetErrorId()));
887 }
888 stateIndicatorBitmaps.Add(Rvalue(bm));
889 }
890 return Result<bool>(true);
891 }
892 [nodiscard]
893 internal Result<bool> DoAnimation(TreeViewNode.State targetState, const Rect& stateRect)
894 {
895 if (targetState == TreeViewNode.State.expanded)
896 {
897 for (long i = stateIndicatorBitmaps.Count() - 1; i >= 0; --i;)
898 {
899 UpdateGuard guard(this, i);
900 auto result = Invalidate(stateRect.ToWinRect());
901 if (result.Error()) return result;
902 Update();
903 Sleep(Duration.FromMilliseconds(animationMs));
904 }
905 }
906 else if (targetState == TreeViewNode.State.collapsed)
907 {
908 for (long i = 0; i < stateIndicatorBitmaps.Count(); ++i;)
909 {
910 UpdateGuard guard(this, i);
911 auto result = Invalidate(stateRect.ToWinRect());
912 if (result.Error()) return result;
913 Update();
914 Sleep(Duration.FromMilliseconds(animationMs));
915 }
916 }
917 return Result<bool>(true);
918 }
919 private class UpdateGuard
920 {
921 public UpdateGuard(TreeView* view_, long bitmapIndex) : view(view_)
922 {
923 view->SetBitmapIndex(bitmapIndex);
924 }
925 public ~UpdateGuard()
926 {
927 view->ResetBitmapIndex();
928 }
929 private TreeView* view;
930 }
931 private void SetBitmapIndex(long bitmapIndex_)
932 {
933 bitmapIndex = bitmapIndex_;
934 }
935 private void ResetBitmapIndex()
936 {
937 bitmapIndex = -1;
938 }
939 [nodiscard]
940 public Result<Bitmap*> NodeExpandedBitmap() const
941 {
942 if (stateIndicatorBitmaps.IsEmpty())
943 {
944 int errorId = AllocateError("no state indicator bitmaps");
945 return Result<Bitmap*>(ErrorId(errorId));
946 }
947 return Result<Bitmap*>(&stateIndicatorBitmaps.Front());
948 }
949 [nodiscard]
950 public Result<Bitmap*> NodeCollapsedBitmap() const
951 {
952 if (stateIndicatorBitmaps.IsEmpty())
953 {
954 int errorId = AllocateError("no state indicator bitmaps");
955 return Result<Bitmap*>(ErrorId(errorId));
956 }
957 return Result<Bitmap*>(&stateIndicatorBitmaps.Back());
958 }
959 public inline const StringFormat& Format() const
960 {
961 return format;
962 }
963 internal void SetSelectedNode(TreeViewNode* node)
964 {
965 if (selectedNode != node)
966 {
967 if (selectedNode != null)
968 {
969 selectedNode->ResetSelected();
970 }
971 selectedNode = node;
972 SetTreeViewNodeStateChanged();
973 }
974 }
975 public TreeViewNode* SelectedNode() const
976 {
977 return selectedNode;
978 }
979 public void DeselectNode()
980 {
981 if (selectedNode != null)
982 {
983 TreeViewNode* selectedNodeParent = selectedNode->Parent();
984 while (selectedNodeParent != null)
985 {
986 if (selectedNodeParent->GetState() == TreeViewNode.State.collapsed)
987 {
988 SetSelectedNode(null);
989 break;
990 }
991 selectedNodeParent = selectedNodeParent->Parent();
992 }
993 }
994 }
995 internal inline TreeViewNode* TrackedNode() const
996 {
997 return trackedNode;
998 }
999 internal void SetTrackedNode(TreeViewNode* trackedNode_)
1000 {
1001 trackedNode = trackedNode_;
1002 }
1003 public inline const Padding& NodeImagePadding() const
1004 {
1005 return nodeImagePadding;
1006 }
1007 protected virtual void OnNodeClick(TreeViewNodeMouseClickEventArgs& args)
1008 {
1009 nodeClickEvent.Fire(args);
1010 }
1011 internal void NodeClick(const Point& location, MouseButtons buttons, TreeViewNode* node)
1012 {
1013 TreeViewNodeMouseClickEventArgs args(location, buttons, 1, node);
1014 OnNodeClick(args);
1015 }
1016 protected virtual void OnNodeDoubleClick(TreeViewNodeMouseClickEventArgs& args)
1017 {
1018 nodeDoubleClickEvent.Fire(args);
1019 }
1020 internal void NodeDoubleClick(const Point& location, MouseButtons buttons, TreeViewNode* node)
1021 {
1022 TreeViewNodeMouseClickEventArgs args(location, buttons, 2, node);
1023 OnNodeDoubleClick(args);
1024 }
1025 protected virtual void OnNodeEnter(TreeViewNodeEventArgs& args)
1026 {
1027 nodeEnterEvent.Fire(args);
1028 }
1029 internal void NodeEnter(TreeViewNode* node)
1030 {
1031 TreeViewNodeEventArgs args(node);
1032 OnNodeEnter(args);
1033 }
1034 protected virtual void OnNodeLeave(TreeViewNodeEventArgs& args)
1035 {
1036 nodeLeaveEvent.Fire(args);
1037 }
1038 internal void NodeLeave(TreeViewNode* node)
1039 {
1040 TreeViewNodeEventArgs args(node);
1041 OnNodeLeave(args);
1042 }
1043 protected virtual void OnNodeSelected(TreeViewNodeEventArgs& args)
1044 {
1045 nodeSelectedEvent.Fire(args);
1046 }
1047 internal void NodeSelected(TreeViewNode* node)
1048 {
1049 TreeViewNodeEventArgs args(node);
1050 OnNodeSelected(args);
1051 }
1052 protected virtual void OnNodeExpanded(TreeViewNodeEventArgs& args)
1053 {
1054 nodeExpandedEvent.Fire(args);
1055 }
1056 internal void NodeExpanded(TreeViewNode* node)
1057 {
1058 TreeViewNodeEventArgs args(node);
1059 OnNodeExpanded(args);
1060 }
1061 protected virtual void OnNodeCollapsed(TreeViewNodeEventArgs& args)
1062 {
1063 nodeCollapsedEvent.Fire(args);
1064 }
1065 protected virtual void OnNodeHovered(TreeViewNodeEventArgs& args)
1066 {
1067 nodeHoveredEvent.Fire(args);
1068 }
1069 internal void NodeCollapsed(TreeViewNode* node)
1070 {
1071 TreeViewNodeEventArgs args(node);
1072 OnNodeCollapsed(args);
1073 }
1074 internal void NodeHovered(TreeViewNode* node)
1075 {
1076 TreeViewNodeEventArgs args(node);
1077 OnNodeHovered(args);
1078 }
1079 public inline ImageList* GetImageList() const
1080 {
1081 return imageList;
1082 }
1083 public void SetImageList(ImageList* imageList_)
1084 {
1085 imageList = imageList_;
1086 }
1087 private Flags flags;
1088 private UniquePtr<TreeViewNode> root;
1089 private Font normalNodeFont;
1090 private Font activeNodeFont;
1091 private float textHeight;
1092 private float stateIndicatorPercentage;
1093 private float stateIndicatorHeight;
1094 private Color stateIndicatorColor;
1095 private Color nodeSelectedColor;
1096 private Color textColor;
1097 private float nodeIndentPercent;
1098 private float nodeTextIndentPercent;
1099 private Padding nodeImagePadding;
1100 private SolidBrush nodeSelectedBrush;
1101 private SolidBrush textBrush;
1102 private List<Bitmap> stateIndicatorBitmaps;
1103 private StringFormat format;
1104 private long bitmapIndex;
1105 private int animationMs;
1106 private TreeViewNode* selectedNode;
1107 private TreeViewNode* trackedNode;
1108 private ToolTip* toolTipWindow;
1109 private ImageList* imageList;
1110 private Event<TreeViewNodeMouseClickEventHandler, TreeViewNodeMouseClickEventArgs> nodeClickEvent;
1111 private Event<TreeViewNodeMouseClickEventHandler, TreeViewNodeMouseClickEventArgs> nodeDoubleClickEvent;
1112 private Event<TreeViewNodeEventHandler, TreeViewNodeEventArgs> nodeEnterEvent;
1113 private Event<TreeViewNodeEventHandler, TreeViewNodeEventArgs> nodeLeaveEvent;
1114 private Event<TreeViewNodeEventHandler, TreeViewNodeEventArgs> nodeSelectedEvent;
1115 private Event<TreeViewNodeEventHandler, TreeViewNodeEventArgs> nodeExpandedEvent;
1116 private Event<TreeViewNodeEventHandler, TreeViewNodeEventArgs> nodeCollapsedEvent;
1117 private Event<TreeViewNodeEventHandler, TreeViewNodeEventArgs> nodeHoveredEvent;
1118 }
1119
1120 public class TreeViewNode : Component
1121 {
1122 public enum State : sbyte
1123 {
1124 collapsed = 0, expanded = 1
1125 }
1126 private enum Flags : sbyte
1127 {
1128 none = 0, mouseEntered = 1 << 0, selected = 1 << 1, active = 1 << 2
1129 }
1130 public explicit TreeViewNode(const string& text_) :
1131 treeView(null), text(text_), children(this), state(State.collapsed), flags(Flags.none), index(-1), imageIndex(-1), expandedImageIndex(-1)
1132 {
1133 }
1134 public void SetTreeView(TreeView* treeView_)
1135 {
1136 treeView = treeView_;
1137 }
1138 public TreeViewNode* Parent() const
1139 {
1140 ComponentContainer* container = GetContainer();
1141 if (container != null)
1142 {
1143 Component* parent = container->Parent();
1144 if (parent != null)
1145 {
1146 if (parent is TreeViewNode*)
1147 {
1148 return cast<TreeViewNode*>(parent);
1149 }
1150 }
1151 }
1152 return null;
1153 }
1154 public TreeView* GetTreeView() const
1155 {
1156 if (treeView != null)
1157 {
1158 return treeView;
1159 }
1160 TreeViewNode* parent = Parent();
1161 if (parent != null)
1162 {
1163 return parent->GetTreeView();
1164 }
1165 return null;
1166 }
1167 [nodiscard]
1168 public Result<bool> AddChild(TreeViewNode* child)
1169 {
1170 auto result = children.AddChild(child);
1171 if (result.Error())
1172 {
1173 return Result<bool>(ErrorId(result.GetErrorId()));
1174 }
1175 TreeView* view = GetTreeView();
1176 if (view != null)
1177 {
1178 view->SetTreeViewNodeChanged();
1179 }
1180 return Result<bool>(true);
1181 }
1182 public UniquePtr<TreeViewNode> RemoveChild(TreeViewNode* child)
1183 {
1184 UniquePtr<Component> removed = children.RemoveChild(child);
1185 TreeView* view = GetTreeView();
1186 if (view != null)
1187 {
1188 view->SetTreeViewNodeChanged();
1189 }
1190 return UniquePtr<TreeViewNode>(cast<TreeViewNode*>(removed.Release()));
1191 }
1192 public void RemoveChildren()
1193 {
1194 Component* component = children.FirstChild();
1195 while (component != null)
1196 {
1197 Component* next = component->NextSibling();
1198 UniquePtr<Component> removed = children.RemoveChild(component);
1199 component = next;
1200 }
1201 }
1202 public int Level() const
1203 {
1204 TreeViewNode* parent = Parent();
1205 if (parent != null)
1206 {
1207 return parent->Level() + 1;
1208 }
1209 return 0;
1210 }
1211 public const Point& Location() const
1212 {
1213 return location;
1214 }
1215 public const Size& GetSize() const
1216 {
1217 return size;
1218 }
1219 [nodiscard]
1220 public Result<bool> MeasureSize(Graphics& graphics, TreeView* treeView)
1221 {
1222 auto measureStringResult = graphics.MeasureStringRectF(text, treeView->GetNormalNodeFont(), PointF(0, 0), treeView->Format());
1223 if (measureStringResult.Error())
1224 {
1225 return Result<bool>(ErrorId(measureStringResult.GetErrorId()));
1226 }
1227 RectF textRect = measureStringResult.Value();
1228 SizeF imageSize(0, 0);
1229 if (imageIndex != -1)
1230 {
1231 Padding padding = treeView->NodeImagePadding();
1232 ImageList* imageList = treeView->GetImageList();
1233 if (imageList != null)
1234 {
1235 Bitmap* bitmap = imageList->GetImage(imageIndex);
1236 imageSize.w = bitmap->GetWidth() + padding.Horizontal();
1237 imageSize.h = bitmap->GetHeight() + padding.Vertical();
1238 }
1239 }
1240 size = Size(cast<int>(treeView->TextHeight() + textRect.size.w + imageSize.w), cast<int>(Max(textRect.size.h, imageSize.h)));
1241 Component* child = children.FirstChild();
1242 while (child != null)
1243 {
1244 if (child is TreeViewNode*)
1245 {
1246 TreeViewNode* childNode = cast<TreeViewNode*>(child);
1247 auto result = childNode->MeasureSize(graphics, treeView);
1248 if (result.Error()) return result;
1249 }
1250 child = child->NextSibling();
1251 }
1252 return Result<bool>(true);
1253 }
1254 internal Result<bool> Measure(Graphics& graphics, const Point& loc, TreeView* treeView, int level, int& idx, Rect& parentRect)
1255 {
1256
1257 float imageHeight = 0;
1258 if (imageIndex != -1)
1259 {
1260 Padding padding = treeView->NodeImagePadding();
1261 ImageList* imageList = treeView->GetImageList();
1262 if (imageList != null)
1263 {
1264 Bitmap* image = imageList->GetImage(imageIndex);
1265 imageHeight = image->GetHeight() + padding.Vertical();
1266 }
1267 }
1268 location = Point(cast<int>(loc.x + level * treeView->NodeIndentPercent() * treeView->TextHeight() / 100.000000f),
1269 cast<int>(loc.y + idx * Max(treeView->TextHeight(), imageHeight)));
1270 Rect rect(location, size);
1271 childRect = rect;
1272 parentRect = Rect.Union(parentRect, childRect);
1273 if (state == State.expanded)
1274 {
1275 Component* child = children.FirstChild();
1276 while (child != null)
1277 {
1278 if (child is TreeViewNode*)
1279 {
1280 TreeViewNode* childNode = cast<TreeViewNode*>(child);
1281 ++idx;
1282 auto measureResult = childNode->Measure(graphics, loc, treeView, level + 1, idx, childRect);
1283 if (measureResult.Error())
1284 {
1285 return Result<bool>(ErrorId(measureResult.GetErrorId()));
1286 }
1287 parentRect = Rect.Union(parentRect, childRect);
1288 }
1289 child = child->NextSibling();
1290 }
1291 }
1292 return Result<bool>(true);
1293 }
1294 [nodiscard]
1295 internal Result<bool> DispatchMouseDown(MouseEventArgs& args, bool& handled)
1296 {
1297 Rect r(location, size);
1298 if (r.Contains(args.location))
1299 {
1300 auto result = OnMouseDown(args);
1301 if (result.Error()) return result;
1302 handled = true;
1303 }
1304 else
1305 {
1306 if (childRect.Contains(args.location))
1307 {
1308 Component* child = children.FirstChild();
1309 while (child != null)
1310 {
1311 if (child is TreeViewNode*)
1312 {
1313 TreeViewNode* childNode = cast<TreeViewNode*>(child);
1314 auto result = childNode->DispatchMouseDown(args, handled);
1315 if (result.Error()) return result;
1316 if (handled)
1317 {
1318 return Result<bool>(true);
1319 }
1320 }
1321 child = child->NextSibling();
1322 }
1323 }
1324 }
1325 return Result<bool>(true);
1326 }
1327 internal void DispatchMouseUp(MouseEventArgs& args, bool& handled)
1328 {
1329 Rect r(location, size);
1330 if (r.Contains(args.location))
1331 {
1332 OnMouseUp(args);
1333 handled = true;
1334 }
1335 else
1336 {
1337 if (childRect.Contains(args.location))
1338 {
1339 Component* child = children.FirstChild();
1340 while (child != null)
1341 {
1342 if (child is TreeViewNode*)
1343 {
1344 TreeViewNode* childNode = cast<TreeViewNode*>(child);
1345 childNode->DispatchMouseUp(args, handled);
1346 if (handled)
1347 {
1348 return;
1349 }
1350 }
1351 child = child->NextSibling();
1352 }
1353 }
1354 }
1355 }
1356 [nodiscard]
1357 internal Result<bool> DispatchMouseDoubleClick(MouseEventArgs& args, bool& handled)
1358 {
1359 Rect r(location, size);
1360 if (r.Contains(args.location))
1361 {
1362 auto result = OnMouseDoubleClick(args);
1363 if (result.Error()) return result;
1364 handled = true;
1365 }
1366 else
1367 {
1368 if (childRect.Contains(args.location))
1369 {
1370 Component* child = children.FirstChild();
1371 while (child != null)
1372 {
1373 if (child is TreeViewNode*)
1374 {
1375 TreeViewNode* childNode = cast<TreeViewNode*>(child);
1376 auto result = childNode->DispatchMouseDoubleClick(args, handled);
1377 if (result.Error()) return result;
1378 if (handled)
1379 {
1380 return Result<bool>(true);
1381 }
1382 }
1383 child = child->NextSibling();
1384 }
1385 }
1386 }
1387 return Result<bool>(true);
1388 }
1389 internal void DispatchMouseMove(MouseEventArgs& args, bool& handled)
1390 {
1391 Rect r(location, size);
1392 if (r.Contains(args.location))
1393 {
1394 handled = true;
1395 TreeView* view = GetTreeView();
1396 if (view != null)
1397 {
1398 TreeViewNode* prevNode = view->TrackedNode();
1399 if (prevNode != null && prevNode != this)
1400 {
1401 if (prevNode->MouseEntered())
1402 {
1403 prevNode->ResetMouseEntered();
1404 prevNode->OnMouseLeave();
1405 }
1406 }
1407 view->SetTrackedNode(this);
1408 if (!MouseEntered())
1409 {
1410 SetMouseEntered();
1411 OnMouseEnter();
1412 }
1413 }
1414 }
1415 else
1416 {
1417 if (childRect.Contains(args.location))
1418 {
1419 Component* child = children.FirstChild();
1420 while (child != null)
1421 {
1422 if (child is TreeViewNode*)
1423 {
1424 TreeViewNode* childNode = cast<TreeViewNode*>(child);
1425 childNode->DispatchMouseMove(args, handled);
1426 if (handled)
1427 {
1428 return;
1429 }
1430 }
1431 child = child->NextSibling();
1432 }
1433 }
1434 }
1435 }
1436 internal void MouseLeaveInternal()
1437 {
1438 OnMouseLeave();
1439 }
1440 internal void DispatchMouseHover(MouseEventArgs& args, bool& handled)
1441 {
1442 Rect r(location, size);
1443 if (r.Contains(args.location))
1444 {
1445 handled = true;
1446 OnMouseHover();
1447 }
1448 else
1449 {
1450 if (childRect.Contains(args.location))
1451 {
1452 Component* child = children.FirstChild();
1453 while (child != null)
1454 {
1455 if (child is TreeViewNode*)
1456 {
1457 TreeViewNode* childNode = cast<TreeViewNode*>(child);
1458 childNode->DispatchMouseHover(args, handled);
1459 if (handled)
1460 {
1461 return;
1462 }
1463 }
1464 child = child->NextSibling();
1465 }
1466 }
1467 }
1468 }
1469 [nodiscard]
1470 private Result<bool> OnMouseDown(MouseEventArgs& args)
1471 {
1472 if (args.buttons == MouseButtons.lbutton)
1473 {
1474 TreeView* view = GetTreeView();
1475 if (view != null)
1476 {
1477 if (children.IsEmpty())
1478 {
1479 Select();
1480 }
1481 else
1482 {
1483 int stateSquareSideLength = cast<int>(view->TextHeight());
1484 Rect stateRect(location, Size(stateSquareSideLength, stateSquareSideLength));
1485 if (stateRect.Contains(args.location))
1486 {
1487 auto result = Toggle();
1488 if (result.Error()) return result;
1489 }
1490 else
1491 {
1492 Select();
1493 }
1494 }
1495 }
1496 }
1497 else if (args.buttons == MouseButtons.rbutton)
1498 {
1499 Select();
1500 }
1501 return Result<bool>(true);
1502 }
1503 private void OnMouseUp(MouseEventArgs& args)
1504 {
1505 if (args.buttons == MouseButtons.lbutton)
1506 {
1507 if (Selected())
1508 {
1509 TreeView* view = GetTreeView();
1510 if (view != null)
1511 {
1512 view->NodeClick(args.location, args.buttons, this);
1513 }
1514 }
1515 }
1516 else if (args.buttons == MouseButtons.rbutton)
1517 {
1518 if (Selected())
1519 {
1520 TreeView* view = GetTreeView();
1521 if (view != null)
1522 {
1523 view->NodeClick(args.location, args.buttons, this);
1524 }
1525 }
1526 }
1527 }
1528 [nodiscard]
1529 private Result<bool> OnMouseDoubleClick(MouseEventArgs& args)
1530 {
1531 TreeView* view = GetTreeView();
1532 if (view != null)
1533 {
1534 view->NodeDoubleClick(args.location, args.buttons, this);
1535 }
1536 return Result<bool>(true);
1537 }
1538 private void OnMouseEnter()
1539 {
1540 TreeView* view = GetTreeView();
1541 if (view != null)
1542 {
1543 view->NodeEnter(this);
1544 }
1545 }
1546 private void OnMouseLeave()
1547 {
1548 TreeView* view = GetTreeView();
1549 if (view != null)
1550 {
1551 view->NodeLeave(this);
1552 }
1553 }
1554 private void OnMouseHover()
1555 {
1556 TreeView* view = GetTreeView();
1557 if (view != null)
1558 {
1559 view->NodeHovered(this);
1560 }
1561 }
1562 [nodiscard]
1563 public Result<bool> Toggle()
1564 {
1565 switch (state)
1566 {
1567 case State.expanded:
1568 {
1569 auto result = Collapse();
1570 if (result.Error()) return result;
1571 break;
1572 }
1573 case State.collapsed:
1574 {
1575 auto result = Expand();
1576 if (result.Error()) return result;
1577 break;
1578 }
1579 }
1580 return Result<bool>(true);
1581 }
1582 public void Select()
1583 {
1584 if (!Selected())
1585 {
1586 SetSelected();
1587 TreeView* view = GetTreeView();
1588 if (view != null)
1589 {
1590 view->SetSelectedNode(this);
1591 view->NodeSelected(this);
1592 }
1593 }
1594 }
1595 public void Deselect()
1596 {
1597 if (Selected())
1598 {
1599 ResetSelected();
1600 TreeView* view = GetTreeView();
1601 if (view != null)
1602 {
1603 view->SetSelectedNode(null);
1604 }
1605 }
1606 }
1607 [nodiscard]
1608 public Result<bool> Expand()
1609 {
1610 if (children.IsEmpty()) return Result<bool>(false);
1611 auto result = DoAnimation();
1612 if (result.Error()) return result;
1613 Expand(false);
1614 TreeView* view = GetTreeView();
1615 if (view != null)
1616 {
1617 view->NodeExpanded(this);
1618 }
1619 return Result<bool>(true);
1620 }
1621 [nodiscard]
1622 public Result<bool> ExpandAll()
1623 {
1624 if (children.IsEmpty()) return Result<bool>(false);
1625 auto result = DoAnimation();
1626 if (result.Error()) return result;
1627 Expand(true);
1628 TreeView* view = GetTreeView();
1629 if (view != null)
1630 {
1631 view->NodeExpanded(this);
1632 }
1633 return Result<bool>(true);
1634 }
1635 public void Expand(bool all)
1636 {
1637 if (children.IsEmpty()) return;
1638 SetState(State.expanded);
1639 if (all)
1640 {
1641 Component* child = children.FirstChild();
1642 while (child != null)
1643 {
1644 if (child is TreeViewNode*)
1645 {
1646 TreeViewNode* childNode = cast<TreeViewNode*>(child);
1647 childNode->Expand(all);
1648 }
1649 child = child->NextSibling();
1650 }
1651 }
1652 }
1653 [nodiscard]
1654 public Result<bool> Collapse()
1655 {
1656 if (children.IsEmpty()) return Result<bool>(false);
1657 auto result = DoAnimation();
1658 if (result.Error()) return result;
1659 Collapse(false);
1660 TreeView* view = GetTreeView();
1661 if (view != null)
1662 {
1663 view->NodeCollapsed(this);
1664 }
1665 return Result<bool>(true);
1666 }
1667 [nodiscard]
1668 public Result<bool> CollapseAll()
1669 {
1670 if (children.IsEmpty()) return Result<bool>(false);
1671 auto result = DoAnimation();
1672 if (result.Error()) return result;
1673 Collapse(true);
1674 TreeView* view = GetTreeView();
1675 if (view != null)
1676 {
1677 view->NodeCollapsed(this);
1678 }
1679 return Result<bool>(true);
1680 }
1681 public void Collapse(bool all)
1682 {
1683 if (children.IsEmpty()) return;
1684 SetState(State.collapsed);
1685 if (all)
1686 {
1687 Component* child = children.FirstChild();
1688 while (child != null)
1689 {
1690 if (child is TreeViewNode*)
1691 {
1692 TreeViewNode* childNode = cast<TreeViewNode*>(child);
1693 childNode->Collapse(all);
1694 }
1695 child = child->NextSibling();
1696 }
1697 }
1698 }
1699 [nodiscard]
1700 private Result<bool> DoAnimation()
1701 {
1702 TreeView* view = GetTreeView();
1703 if (view != null)
1704 {
1705 int stateSquareSideLength = cast<int>(view->TextHeight());
1706 Rect stateRect(location, Size(stateSquareSideLength, stateSquareSideLength));
1707 auto result = view->DoAnimation(cast<State>(1 - state), stateRect);
1708 if (result.Error()) return result;
1709 }
1710 return Result<bool>(true);
1711 }
1712 public inline const string& Text() const
1713 {
1714 return text;
1715 }
1716 public void SetText(const string& text_)
1717 {
1718 text = text_;
1719 TreeView* view = GetTreeView();
1720 if (view != null)
1721 {
1722 view->SetTreeViewNodeChanged();
1723 }
1724 }
1725 public inline ComponentContainer& Children() const
1726 {
1727 return children;
1728 }
1729 public inline State GetState() const
1730 {
1731 return state;
1732 }
1733 public void SetState(State state_)
1734 {
1735 if (state != state_)
1736 {
1737 state = state_;
1738 TreeView* view = GetTreeView();
1739 if (view != null)
1740 {
1741 view->SetTreeViewNodeStateChanged();
1742 if (state == State.collapsed)
1743 {
1744 view->SetContentLocation(Point(0, 0));
1745 }
1746 }
1747 }
1748 }
1749 public inline void* Data() const
1750 {
1751 return data;
1752 }
1753 public inline void SetData(void* data_)
1754 {
1755 data = data_;
1756 }
1757 [nodiscard]
1758 public Result<bool> EnsureVisible()
1759 {
1760 TreeView* treeView = GetTreeView();
1761 if (treeView != null)
1762 {
1763 auto result = treeView->EnsureNodeVisible(this);
1764 if (result.Error()) return result;
1765 }
1766 return Result<bool>(true);
1767 }
1768 public void GetVisibleNodes(List<TreeViewNode*>& visibleNodes, TreeView* treeView, int selectLevel)
1769 {
1770 if (selectLevel == -1 || Level() == selectLevel)
1771 {
1772 if (treeView->NodeVisible(this))
1773 {
1774 visibleNodes.Add(this);
1775 }
1776 }
1777 if (state == State.expanded)
1778 {
1779 Component* child = children.FirstChild();
1780 while (child != null)
1781 {
1782 if (child is TreeViewNode*)
1783 {
1784 TreeViewNode* childNode = cast<TreeViewNode*>(child);
1785 childNode->GetVisibleNodes(visibleNodes, treeView, selectLevel);
1786 }
1787 child = child->NextSibling();
1788 }
1789 }
1790 }
1791 [nodiscard]
1792 public Result<bool> Draw(Graphics& graphics, TreeView* treeView)
1793 {
1794 bool isVisible = treeView->NodeVisible(this);
1795 Point loc = location;
1796 if (!children.IsEmpty())
1797 {
1798 if (isVisible)
1799 {
1800 switch (state)
1801 {
1802 case State.expanded:
1803 {
1804 auto bitmapResult = treeView->NodeExpandedBitmap();
1805 if (bitmapResult.Error())
1806 {
1807 return Result<bool>(ErrorId(bitmapResult.GetErrorId()));
1808 }
1809 Bitmap* bm = bitmapResult.Value();
1810 auto drawImageResult = graphics.DrawImage(*bm, PointF(loc.x, loc.y));
1811 if (drawImageResult.Error())
1812 {
1813 return Result<bool>(ErrorId(drawImageResult.GetErrorId()));
1814 }
1815 break;
1816 }
1817 case State.collapsed:
1818 {
1819 auto bitmapResult = treeView->NodeCollapsedBitmap();
1820 if (bitmapResult.Error())
1821 {
1822 return Result<bool>(ErrorId(bitmapResult.GetErrorId()));
1823 }
1824 Bitmap* bm = bitmapResult.Value();
1825 auto drawImageResult = graphics.DrawImage(*bm, PointF(loc.x, loc.y));
1826 if (drawImageResult.Error())
1827 {
1828 return Result<bool>(ErrorId(drawImageResult.GetErrorId()));
1829 }
1830 break;
1831 }
1832 }
1833 }
1834 loc.x = cast<int>(loc.x + treeView->NodeTextIndentPercent() * treeView->TextHeight() / 100.000000f);
1835 }
1836 if (isVisible)
1837 {
1838 if (imageIndex != -1)
1839 {
1840 auto drawImageResult = DrawImage(treeView, graphics, loc);
1841 if (drawImageResult.Error())
1842 {
1843 return Result<bool>(ErrorId(drawImageResult.GetErrorId()));
1844 }
1845 }
1846 }
1847 if (isVisible)
1848 {
1849 if (Selected())
1850 {
1851 Rect rect(loc, size);
1852 auto fillResult = graphics.FillRectangle(treeView->NodeSelectedBrush(), rect);
1853 if (fillResult.Error())
1854 {
1855 return Result<bool>(ErrorId(fillResult.GetErrorId()));
1856 }
1857 }
1858 if (Active())
1859 {
1860 auto drawStringResult = graphics.DrawString(text, treeView->GetActiveNodeFont(), PointF(loc.x, loc.y), treeView->TextBrush());
1861 if (drawStringResult.Error())
1862 {
1863 return Result<bool>(ErrorId(drawStringResult.GetErrorId()));
1864 }
1865 }
1866 else
1867 {
1868 auto drawStringResult = graphics.DrawString(text, treeView->GetNormalNodeFont(), PointF(loc.x, loc.y), treeView->TextBrush());
1869 if (drawStringResult.Error())
1870 {
1871 return Result<bool>(ErrorId(drawStringResult.GetErrorId()));
1872 }
1873 }
1874 }
1875 if (state == State.expanded)
1876 {
1877 Component* child = children.FirstChild();
1878 while (child != null)
1879 {
1880 if (child is TreeViewNode*)
1881 {
1882 TreeViewNode* childNode = cast<TreeViewNode*>(child);
1883 auto childDrawResult = childNode->Draw(graphics, treeView);
1884 if (childDrawResult.Error())
1885 {
1886 return Result<bool>(ErrorId(childDrawResult.GetErrorId()));
1887 }
1888 }
1889 child = child->NextSibling();
1890 }
1891 }
1892 return Result<bool>(true);
1893 }
1894 internal inline bool MouseEntered() const
1895 {
1896 return (flags & Flags.mouseEntered) != Flags.none;
1897 }
1898 internal inline void SetMouseEntered()
1899 {
1900 flags = cast<Flags>(flags | Flags.mouseEntered);
1901 }
1902 internal inline void ResetMouseEntered()
1903 {
1904 flags = cast<Flags>(flags & ~Flags.mouseEntered);
1905 }
1906 public inline bool Selected() const
1907 {
1908 return (flags & Flags.selected) != Flags.none;
1909 }
1910 private inline void SetSelected()
1911 {
1912 flags = cast<Flags>(flags | Flags.selected);
1913 }
1914 internal inline void ResetSelected()
1915 {
1916 flags = cast<Flags>(flags & ~Flags.selected);
1917 }
1918 public inline bool Active() const
1919 {
1920 return (flags & Flags.active) != Flags.none;
1921 }
1922 public void SetActive(bool active)
1923 {
1924 if (Active() != active)
1925 {
1926 if (active)
1927 {
1928 flags = cast<Flags>(flags | Flags.active);
1929 }
1930 else
1931 {
1932 flags = cast<Flags>(flags & ~Flags.active);
1933 }
1934 TreeView* view = GetTreeView();
1935 if (view != null)
1936 {
1937 view->SetTreeViewNodeStateChanged();
1938 }
1939 }
1940 }
1941 public inline int Index() const
1942 {
1943 return index;
1944 }
1945 public void SetIndex(int index_)
1946 {
1947 index = index_;
1948 }
1949 public inline int ImageIndex() const
1950 {
1951 return imageIndex;
1952 }
1953 [nodiscard]
1954 public Result<bool> SetImageIndex(int imageIndex_)
1955 {
1956 if (imageIndex != imageIndex_)
1957 {
1958 imageIndex = imageIndex_;
1959 TreeView* view = GetTreeView();
1960 if (view != null)
1961 {
1962 view->SetTreeViewNodeStateChanged();
1963 view->SetChanged();
1964 auto result = view->Invalidate();
1965 if (result.Error()) return result;
1966 }
1967 }
1968 return Result<bool>(true);
1969 }
1970 [nodiscard]
1971 public Result<bool> SetExpandedImageIndex(int expandedImageIndex_)
1972 {
1973 if (expandedImageIndex != expandedImageIndex_)
1974 {
1975 expandedImageIndex = expandedImageIndex_;
1976 TreeView* view = GetTreeView();
1977 if (view != null)
1978 {
1979 view->SetTreeViewNodeStateChanged();
1980 view->SetChanged();
1981 auto result = view->Invalidate();
1982 if (result.Error()) return result;
1983 }
1984 }
1985 return Result<bool>(true);
1986 }
1987 [nodiscard]
1988 public Result<bool> DrawImage(TreeView* view, Graphics& graphics, Point& loc)
1989 {
1990 ImageList* imageList = view->GetImageList();
1991 if (imageList != null)
1992 {
1993 int imgIndex = imageIndex;
1994 if (state == State.expanded)
1995 {
1996 if (expandedImageIndex != -1)
1997 {
1998 imgIndex = expandedImageIndex;
1999 }
2000 }
2001 Bitmap* image = imageList->GetImage(imgIndex);
2002 if (image != null)
2003 {
2004 int imageWidth = cast<int>(image->GetWidth());
2005 int imageHeight = cast<int>(image->GetHeight());
2006 Padding padding = view->NodeImagePadding();
2007 Rect r(loc, Size(imageWidth + padding.Horizontal(), imageHeight + padding.Vertical()));
2008 ImageAttributes attributes;
2009 Color transparentColor = System.Windows.Color.DefaultBitmapTransparent();
2010 auto setColorKeyResult = attributes.SetColorKey(transparentColor, transparentColor, ColorAdjustType.default_);
2011 if (setColorKeyResult.Error())
2012 {
2013 return Result<bool>(ErrorId(setColorKeyResult.GetErrorId()));
2014 }
2015 auto drawImageResult = graphics.DrawImage(*image, r, 0, 0, imageWidth + padding.Horizontal(), imageHeight + padding.Vertical(),
2016 Unit.pixel, attributes);
2017 if (drawImageResult.Error())
2018 {
2019 return Result<bool>(ErrorId(drawImageResult.GetErrorId()));
2020 }
2021 loc.x = loc.x + imageWidth + padding.Horizontal();
2022 }
2023 }
2024 return Result<bool>(true);
2025 }
2026 private TreeView* treeView;
2027 private string text;
2028 private ComponentContainer children;
2029 private State state;
2030 private Flags flags;
2031 private void* data;
2032 private Point location;
2033 private Size size;
2034 private Rect childRect;
2035 private int index;
2036 private int imageIndex;
2037 private int expandedImageIndex;
2038 }