1
2
3
4
5
6 using System;
7 using System.Collections;
8
9 namespace System.Windows
10 {
11 public class delegate void TabPageSelectedEventHandler();
12
13 public Color DefaultTabControlFrameColor()
14 {
15 return Color(204u, 206u, 219u);
16 }
17
18 public Color DefaultTabTextColor()
19 {
20 return Color.Black();
21 }
22
23 public Color DefaultTabNormalBackgroundColor()
24 {
25 return Color.White();
26 }
27
28 public Color DefaultTabSelectedBackgroundColor()
29 {
30 return Color(255u, 255u, 215u);
31 }
32
33 public Color DefaultTabCloseBoxSelectedColor()
34 {
35 return Color(222u, 238u, 245u);
36 }
37
38 public inline int DefaultTabLeadingWidth()
39 {
40 return 0;
41 }
42
43 public inline int DefaultTabControlTopMarginHeight()
44 {
45 return 4;
46 }
47
48 public Padding DefaultTabPadding()
49 {
50 return Padding(8, 2, 8, 2);
51 }
52
53 public Padding DefaultTabCloseBoxPadding()
54 {
55 return Padding(8, 6, 8, 0);
56 }
57
58 public inline int DefaultTabOverlapWidth()
59 {
60 return 2;
61 }
62
63 public inline float DefaultTabRoundingRadius()
64 {
65 return 8;
66 }
67
68 public inline float DefaultTabCloseBoxPenWidth()
69 {
70 return 1.000000f;
71 }
72
73 internal class Tab
74 {
75 public enum State
76 {
77 normal, closeBoxSelected
78 }
79 public bool visible;
80 public State state;
81 public float textHeight;
82 public float textWidth;
83 public float closeBoxWidth;
84 public int height;
85 public int width;
86 public int left;
87 public RectF leftRoundingRect;
88 public RectF rightRoundingRect;
89 public RectF topRect;
90 public RectF bottomRect;
91 public RectF textRect;
92 public RectF closeBoxRect;
93 public Rect selectRect;
94 public Rect closeRect;
95 }
96
97 public ControlCreateParams& TabControlControlCreateParams(ControlCreateParams& controlCreateParams)
98 {
99 return controlCreateParams.SetWindowClassName("System.Windows.TabControl");
100 }
101
102 public class TabControlCreateParams
103 {
104 public TabControlCreateParams(ControlCreateParams& controlCreateParams_) :
105 controlCreateParams(controlCreateParams_),
106 fontFamilyName("Segoe UI"),
107 fontSize(9.000000f),
108 frameColor(DefaultTabControlFrameColor()),
109 textColor(DefaultTabTextColor()),
110 leadingWidth(DefaultTabLeadingWidth()),
111 topMarginHeight(DefaultTabControlTopMarginHeight()),
112 headerHeight(0),
113 tabPadding(DefaultTabPadding()),
114 tabCloseBoxPadding(DefaultTabCloseBoxPadding()),
115 overlapWidth(DefaultTabOverlapWidth()),
116 tabNormalBackgroundColor(DefaultTabNormalBackgroundColor()),
117 tabSelectedBackgroundColor(DefaultTabSelectedBackgroundColor()),
118 roundingRadius(DefaultTabRoundingRadius()),
119 closeBoxPenWidth(DefaultTabCloseBoxPenWidth()),
120 closeBoxSelectedColor(DefaultTabCloseBoxSelectedColor())
121 {
122 }
123 public TabControlCreateParams& Defaults()
124 {
125 return *this;
126 }
127 public TabControlCreateParams& SetFontFamilyName(const string& fontFamilyName_)
128 {
129 fontFamilyName = fontFamilyName_;
130 return *this;
131 }
132 public TabControlCreateParams& SetFontSize(float fontSize_)
133 {
134 fontSize = fontSize_;
135 return *this;
136 }
137 public TabControlCreateParams& SetFrameColor(const Color& frameColor_)
138 {
139 frameColor = frameColor_;
140 return *this;
141 }
142 public TabControlCreateParams& SetTextColor(const Color& textColor_)
143 {
144 textColor = textColor_;
145 return *this;
146 }
147 public TabControlCreateParams& SetLeadingWidth(int leadingWidth_)
148 {
149 leadingWidth = leadingWidth_;
150 return *this;
151 }
152 public TabControlCreateParams& SetTopMarginHeight(int topMarginHeight_)
153 {
154 topMarginHeight = topMarginHeight_;
155 return *this;
156 }
157 public TabControlCreateParams& SetHeaderHeight(int headerHeight_)
158 {
159 headerHeight = headerHeight_;
160 return *this;
161 }
162 public TabControlCreateParams& SetTabPadding(const Padding& tabPadding_)
163 {
164 tabPadding = tabPadding_;
165 return *this;
166 }
167 public TabControlCreateParams& SetTabCloseBoxPadding(const Padding& tabCloseBoxPadding_)
168 {
169 tabCloseBoxPadding = tabCloseBoxPadding_;
170 return *this;
171 }
172 public TabControlCreateParams& SetOverlapWidth(int overlapWidth_)
173 {
174 overlapWidth = overlapWidth_;
175 return *this;
176 }
177 public TabControlCreateParams& SetRoundingRadius(float roundingRadius_)
178 {
179 roundingRadius = roundingRadius_;
180 return *this;
181 }
182 public TabControlCreateParams& SetTabNormalBackgroundColor(const Color& tabNormalBackgroundColor_)
183 {
184 tabNormalBackgroundColor = tabNormalBackgroundColor_;
185 return *this;
186 }
187 public TabControlCreateParams& SetTabSelectedBackgroundColor(const Color& tabSelectedBackgroundColor_)
188 {
189 tabSelectedBackgroundColor = tabSelectedBackgroundColor_;
190 return *this;
191 }
192 public TabControlCreateParams& SetCloseBoxPenWidth(float closeBoxPenWidth_)
193 {
194 closeBoxPenWidth = closeBoxPenWidth_;
195 return *this;
196 }
197 public TabControlCreateParams& SetCloseBoxSelectedColor(const Color& closeBoxSelectedColor_)
198 {
199 closeBoxSelectedColor = closeBoxSelectedColor_;
200 return *this;
201 }
202 public ControlCreateParams& controlCreateParams;
203 public string fontFamilyName;
204 public float fontSize;
205 public Color frameColor;
206 public Color textColor;
207 public int leadingWidth;
208 public int topMarginHeight;
209 public int headerHeight;
210 public Padding tabPadding;
211 public Padding tabCloseBoxPadding;
212 public int overlapWidth;
213 public float roundingRadius;
214 public Color tabNormalBackgroundColor;
215 public Color tabSelectedBackgroundColor;
216 public float closeBoxPenWidth;
217 public Color closeBoxSelectedColor;
218 }
219
220 public class TabControl : Control
221 {
222 private enum Flags : sbyte
223 {
224 none = 0, changed = 1 << 0
225 }
226 public TabControl(const Font& font_, const Color& frameColor_, const Point& location, const Size& size, Dock dock, Anchors anchors) :
227 base("System.Windows.TabControl", DefaultWindowClassStyle(), DefaultChildWindowStyle(), DefaultExtendedWindowStyle(),
228 DefaultControlBackgroundColor(), "tabControl", location, size, dock, anchors), flags(Flags.none), font(font_), frameColor(frameColor_),
229 textColor(DefaultTabTextColor()), tabPages(this), selectedTabPage(null), leadingWidth(DefaultTabLeadingWidth()),
230 topMarginHeight(DefaultTabControlTopMarginHeight()), headerHeight(0), tabPadding(DefaultTabPadding()),
231 tabCloseBoxPadding(DefaultTabCloseBoxPadding()),
232 overlapWidth(DefaultTabOverlapWidth()), roundingRadius(DefaultTabRoundingRadius()), stringFormat(),
233 centerFormat(StringAlignment.center, StringAlignment.center), framePen(frameColor), textBrush(textColor),
234 tabNormalBackgroundColor(DefaultTabNormalBackgroundColor()), tabNormalBackgroundBrush(tabNormalBackgroundColor),
235 tabSelectedBackgroundColor(DefaultTabSelectedBackgroundColor()), tabSelectedBackgroundBrush(tabSelectedBackgroundColor),
236 closeBoxPenWidth(DefaultTabCloseBoxPenWidth()), closeBoxPen(textColor, closeBoxPenWidth), closeStateTabPage(null),
237 closeBoxSelectedColor(DefaultTabCloseBoxSelectedColor()), closeBoxSelectedBrush(closeBoxSelectedColor)
238 {
239 Init();
240 }
241 public TabControl(const Point& location, const Size& size, Dock dock, Anchors anchors) :
242 this(Font(FontFamily("Segoe UI"), 9.000000f), DefaultTabControlFrameColor(), location, size, dock, anchors)
243 {
244 }
245 public TabControl(TabControlCreateParams& createParams) :
246 base(createParams.controlCreateParams),
247 flags(Flags.none), font(FontFamily(createParams.fontFamilyName), createParams.fontSize),
248 frameColor(createParams.frameColor),
249 textColor(createParams.textColor),
250 tabPages(this), selectedTabPage(null),
251 leadingWidth(createParams.leadingWidth),
252 topMarginHeight(createParams.topMarginHeight),
253 headerHeight(createParams.headerHeight),
254 tabPadding(createParams.tabPadding),
255 tabCloseBoxPadding(createParams.tabCloseBoxPadding),
256 overlapWidth(createParams.overlapWidth),
257 roundingRadius(createParams.roundingRadius),
258 stringFormat(),
259 centerFormat(StringAlignment.center, StringAlignment.center), framePen(frameColor), textBrush(textColor),
260 tabNormalBackgroundColor(createParams.tabNormalBackgroundColor), tabNormalBackgroundBrush(tabNormalBackgroundColor),
261 tabSelectedBackgroundColor(createParams.tabSelectedBackgroundColor), tabSelectedBackgroundBrush(tabSelectedBackgroundColor),
262 closeBoxPenWidth(createParams.closeBoxPenWidth), closeBoxPen(textColor, closeBoxPenWidth), closeStateTabPage(null),
263 closeBoxSelectedColor(createParams.closeBoxSelectedColor), closeBoxSelectedBrush(closeBoxSelectedColor)
264 {
265 }
266 private void Init()
267 {
268 if (framePen.Error())
269 {
270 SetErrorId(framePen.GetErrorId());
271 return;
272 }
273 if (textBrush.Error())
274 {
275 SetErrorId(textBrush.GetErrorId());
276 return;
277 }
278 if (closeBoxPen.Error())
279 {
280 SetErrorId(closeBoxPen.GetErrorId());
281 return;
282 }
283 if (closeBoxSelectedBrush.Error())
284 {
285 SetErrorId(closeBoxSelectedBrush.GetErrorId());
286 return;
287 }
288 if (tabNormalBackgroundBrush.Error())
289 {
290 SetErrorId(tabNormalBackgroundBrush.GetErrorId());
291 return;
292 }
293 if (tabSelectedBackgroundBrush.Error())
294 {
295 SetErrorId(tabSelectedBackgroundBrush.GetErrorId());
296 return;
297 }
298 SetChanged();
299 }
300 [nodiscard]
301 public Result<bool> SetTextColor(const Color& textColor_)
302 {
303 if (textColor_ != textColor)
304 {
305 textColor = textColor_;
306 textBrush = SolidBrush(textColor);
307 if (textBrush.Error())
308 {
309 return Result<bool>(ErrorId(textBrush.GetErrorId()));
310 }
311 auto result = Invalidate();
312 if (result.Error()) return result;
313 }
314 return Result<bool>(true);
315 }
316 [nodiscard]
317 public Result<bool> SetTabNormalBackgroundColor(const Color& tabNormalBackgroundColor_)
318 {
319 if (tabNormalBackgroundColor_ != tabNormalBackgroundColor)
320 {
321 tabNormalBackgroundColor = tabNormalBackgroundColor_;
322 tabNormalBackgroundBrush = SolidBrush(tabNormalBackgroundColor);
323 if (tabNormalBackgroundBrush.Error())
324 {
325 return Result<bool>(ErrorId(tabNormalBackgroundBrush.GetErrorId()));
326 }
327 auto result = Invalidate();
328 if (result.Error()) return result;
329 }
330 return Result<bool>(true);
331 }
332 [nodiscard]
333 public Result<bool> SetTabSelectedBackgroundColor(const Color& tabSelectedBackgroundColor_)
334 {
335 if (tabSelectedBackgroundColor_ != tabSelectedBackgroundColor)
336 {
337 tabSelectedBackgroundColor = tabSelectedBackgroundColor_;
338 tabSelectedBackgroundBrush = SolidBrush(tabSelectedBackgroundColor);
339 if (tabSelectedBackgroundBrush.Error())
340 {
341 return Result<bool>(ErrorId(tabSelectedBackgroundBrush.GetErrorId()));
342 }
343 }
344 return Result<bool>(true);
345 }
346 public const ComponentContainer& TabPages() const
347 {
348 return tabPages;
349 }
350 [nodiscard]
351 public Result<bool> AddTabPage(TabPage* tabPage)
352 {
353 AddTabPageToTabPageMap(tabPage);
354 auto result = tabPages.AddChild(tabPage);
355 if (result.Error()) return result;
356 result = SetSelectedTabPage(tabPage);
357 if (result.Error()) return result;
358 SetChanged();
359 return Result<bool>(true);
360 }
361 [nodiscard]
362 public Result<bool> AddTabPage(const string& text, const string& key)
363 {
364 return AddTabPage(new TabPage(text, key));
365 }
366 [nodiscard]
367 public Result<bool> AddTabPage(const string& text)
368 {
369 return AddTabPage(text, string());
370 }
371 [nodiscard]
372 public Result<bool> CloseTabPage(TabPage* tabPage)
373 {
374 auto result = tabPage->Hide();
375 if (result.Error()) return result;
376 RemoveTabPageFromTabPageMap(tabPage);
377 if (tabPage == selectedTabPage)
378 {
379 if (selectedTabPage->NextSibling() != null)
380 {
381 result = SetSelectedTabPage(cast<TabPage*>(selectedTabPage->NextSibling()));
382 if (result.Error()) return result;
383 }
384 else if (selectedTabPage->PrevSibling() != null)
385 {
386 result = SetSelectedTabPage(cast<TabPage*>(selectedTabPage->PrevSibling()));
387 if (result.Error()) return result;
388 }
389 else
390 {
391 result = SetSelectedTabPage(null);
392 if (result.Error()) return result;
393 }
394 }
395 UniquePtr<Component> component = tabPages.RemoveChild(tabPage);
396 TabPage* tb = cast<TabPage*>(component.Get());
397 ControlEventArgs args(tb);
398 result = OnControlRemoved(args);
399 if (result.Error()) return result;
400 SetChanged();
401 result = Invalidate();
402 if (result.Error()) return result;
403 return Result<bool>(true);
404 }
405 [nodiscard]
406 public Result<bool> CloseAllTabPages()
407 {
408 if (selectedTabPage != null)
409 {
410 auto result = selectedTabPage->Hide();
411 if (result.Error()) return result;
412 selectedTabPage = null;
413 }
414 Component* component = tabPages.FirstChild();
415 while (component != null)
416 {
417 Component* next = component->NextSibling();
418 UniquePtr<Component> comp = tabPages.RemoveChild(component);
419 TabPage* tabPage = cast<TabPage*>(comp.Get());
420 RemoveTabPageFromTabPageMap(tabPage);
421 ControlEventArgs args(tabPage);
422 auto result = OnControlRemoved(args);
423 if (result.Error()) return result;
424 component = next;
425 }
426 SetChanged();
427 auto result = Invalidate();
428 if (result.Error()) return result;
429 return Result<bool>(true);
430 }
431 public int IndexOf(TabPage* tabPage) const
432 {
433 int index = 0;
434 Component* component = tabPages.FirstChild();
435 while (component != null)
436 {
437 if (component == tabPage)
438 {
439 return index;
440 }
441 component = component->NextSibling();
442 ++index;
443 }
444 return -1;
445 }
446 public TabPage* GetTabPageByKey(const string& key) const
447 {
448 auto it = tabPageMap.Find(key);
449 if (it != tabPageMap.End())
450 {
451 return it->second;
452 }
453 else
454 {
455 return null;
456 }
457 }
458 [nodiscard]
459 protected override Result<bool> OnPaint(PaintEventArgs& args)
460 {
461 if (Debug.Paint())
462 {
463 Rect r(Point(), GetSize());
464 LogView* log = Application.GetLogView();
465 if (log != null)
466 {
467 auto result = log->WriteLine("TabControl.OnPaint: " + r.ToString());
468 if (result.Error()) return result;
469 }
470 }
471 auto smoothingModeResult = args.graphics.GetSmoothingMode();
472 if (smoothingModeResult.Error())
473 {
474 return Result<bool>(ErrorId(smoothingModeResult.GetErrorId()));
475 }
476 SmoothingMode prevSmoothingMode = smoothingModeResult.Value();
477 auto result = args.graphics.SetSmoothingMode(SmoothingMode.highQuality);
478 if (result.Error())
479 {
480 return Result<bool>(ErrorId(result.GetErrorId()));
481 }
482 if (Changed())
483 {
484 ResetChanged();
485 auto result = Measure(args.graphics);
486 if (result.Error())
487 {
488 return Result<bool>(ErrorId(result.GetErrorId()));
489 }
490 }
491 result = args.graphics.Clear(BackgroundColor());
492 if (result.Error())
493 {
494 return Result<bool>(ErrorId(result.GetErrorId()));
495 }
496 result = DrawTabs(args.graphics);
497 if (result.Error())
498 {
499 return Result<bool>(ErrorId(result.GetErrorId()));
500 }
501 result = DrawSelectedTabPage(args.clipRect);
502 if (result.Error())
503 {
504 return Result<bool>(ErrorId(result.GetErrorId()));
505 }
506 result = DrawFrame(args.graphics);
507 if (result.Error())
508 {
509 return Result<bool>(ErrorId(result.GetErrorId()));
510 }
511 result = args.graphics.SetSmoothingMode(prevSmoothingMode);
512 if (result.Error())
513 {
514 return Result<bool>(ErrorId(result.GetErrorId()));
515 }
516 return base->OnPaint(args);
517 }
518 [nodiscard]
519 protected override Result<bool> OnMouseEnter(EnterLeaveEventArgs& args)
520 {
521 auto result = base->OnMouseEnter(args);
522 if (result.Error()) return result;
523 closeStateTabPage = null;
524 return Result<bool>(true);
525 }
526 [nodiscard]
527 protected override Result<bool> OnMouseLeave(EnterLeaveEventArgs& args)
528 {
529 auto result = base->OnMouseLeave(args);
530 if (result.Error()) return result;
531 if (closeStateTabPage != null)
532 {
533 closeStateTabPage->tab.state = Tab.State.normal;
534 result = Invalidate(closeStateTabPage->tab.closeRect.ToWinRect());
535 if (result.Error()) return result;
536 closeStateTabPage = null;
537 }
538 return Result<bool>(true);
539 }
540 [nodiscard]
541 protected override Result<bool> OnMouseMove(MouseEventArgs& args)
542 {
543 auto result = base->OnMouseMove(args);
544 if (result.Error()) return result;
545 Component* component = tabPages.FirstChild();
546 while (component != null)
547 {
548 TabPage* tabPage = cast<TabPage*>(component);
549 if (tabPage->tab.closeRect.Contains(args.location))
550 {
551 if (tabPage->tab.state == Tab.State.normal)
552 {
553 tabPage->tab.state = Tab.State.closeBoxSelected;
554 closeStateTabPage = tabPage;
555 result = Invalidate(tabPage->tab.closeRect.ToWinRect());
556 if (result.Error()) return result;
557 return Result<bool>(true);
558 }
559 }
560 else if (closeStateTabPage != null)
561 {
562 if (tabPage->tab.state == Tab.State.closeBoxSelected)
563 {
564 closeStateTabPage->tab.state = Tab.State.normal;
565 result = Invalidate(closeStateTabPage->tab.closeRect.ToWinRect());
566 if (result.Error()) return result;
567 closeStateTabPage = null;
568 return Result<bool>(true);
569 }
570 }
571 component = component->NextSibling();
572 }
573 return Result<bool>(true);
574 }
575 [nodiscard]
576 protected override Result<bool> OnMouseDown(MouseEventArgs& args)
577 {
578 auto result = base->OnMouseDown(args);
579 if (result.Error()) return result;
580 Component* component = tabPages.FirstChild();
581 while (component != null)
582 {
583 TabPage* tabPage = cast<TabPage*>(component);
584 if (tabPage->tab.selectRect.Contains(args.location))
585 {
586 result = tabPage->Select();
587 if (result.Error()) return result;
588 return Result<bool>(true);
589 }
590 else if (tabPage->tab.closeRect.Contains(args.location))
591 {
592 result = tabPage->Close();
593 if (result.Error()) return result;
594 return Result<bool>(true);
595 }
596 component = component->NextSibling();
597 }
598 return Result<bool>(true);
599 }
600 public inline TabPage* SelectedTabPage() const
601 {
602 return selectedTabPage;
603 }
604 [nodiscard]
605 public Result<bool> SetSelectedTabPage(TabPage* tabPage)
606 {
607 if (selectedTabPage != tabPage)
608 {
609 if (selectedTabPage != null)
610 {
611 auto result = selectedTabPage->Hide();
612 if (result.Error()) return result;
613 }
614 selectedTabPage = tabPage;
615 if (selectedTabPage != null)
616 {
617 auto result = selectedTabPage->Show();
618 if (result.Error()) return result;
619 Control* control = selectedTabPage->GetFirstEnabledTabStopControl();
620 if (control != null)
621 {
622 control->SetFocus();
623 }
624 OnTabPageSelected();
625 }
626 auto result = Invalidate();
627 if (result.Error()) return result;
628 }
629 return Result<bool>(true);
630 }
631 [nodiscard]
632 public Result<bool> SelectNextTabPage()
633 {
634 if (selectedTabPage == null) return Result<bool>(false);
635 TabPage* nextTabPage = cast<TabPage*>(selectedTabPage->NextSibling());
636 if (nextTabPage != null)
637 {
638 auto result = SetSelectedTabPage(nextTabPage);
639 if (result.Error()) return result;
640 }
641 else
642 {
643 TabPage* firstTabPage = cast<TabPage*>(tabPages.FirstChild());
644 if (firstTabPage != null)
645 {
646 auto result = SetSelectedTabPage(firstTabPage);
647 if (result.Error()) return result;
648 }
649 }
650 return Result<bool>(true);
651 }
652 [nodiscard]
653 public Result<bool> SelectPreviousTabPage()
654 {
655 if (selectedTabPage == null) return Result<bool>(false);
656 TabPage* prevTabPage = cast<TabPage*>(selectedTabPage->PrevSibling());
657 if (prevTabPage != null)
658 {
659 auto result = SetSelectedTabPage(prevTabPage);
660 if (result.Error()) return result;
661 }
662 else
663 {
664 TabPage* lastTabPage = cast<TabPage*>(tabPages.LastChild());
665 if (lastTabPage != null)
666 {
667 auto result = SetSelectedTabPage(lastTabPage);
668 if (result.Error()) return result;
669 }
670 }
671 return Result<bool>(true);
672 }
673 protected virtual void OnTabPageSelected()
674 {
675 tabPageSelectedEvent.Fire();
676 }
677 public Event<TabPageSelectedEventHandler>& TabPageSelectedEvent()
678 {
679 return tabPageSelectedEvent;
680 }
681 private Result<bool> DrawTabs(Graphics& graphics)
682 {
683 Component* component = tabPages.FirstChild();
684 int left = leadingWidth;
685 while (component != null)
686 {
687 TabPage* tabPage = cast<TabPage*>(component);
688 if (tabPage != selectedTabPage)
689 {
690 auto result = tabPage->DrawTab(graphics, this);
691 if (result.Error())
692 {
693 return Result<bool>(ErrorId(result.GetErrorId()));
694 }
695 }
696 component = component->NextSibling();
697 }
698 if (selectedTabPage != null)
699 {
700 auto result = selectedTabPage->DrawTab(graphics, this);
701 if (result.Error())
702 {
703 return Result<bool>(ErrorId(result.GetErrorId()));
704 }
705 }
706 return Result<bool>(true);
707 }
708 [nodiscard]
709 protected override Result<bool> OnLocationChanged()
710 {
711 auto result = base->OnLocationChanged();
712 if (result.Error()) return result;
713 result = SetSelectedTabPagePos();
714 if (result.Error()) return result;
715 return Result<bool>(true);
716 }
717 [nodiscard]
718 protected override Result<bool> OnSizeChanged(SizeChangedEventArgs& args)
719 {
720 auto result = base->OnSizeChanged(args);
721 if (result.Error()) return result;
722 result = SetSelectedTabPagePos();
723 if (result.Error()) return result;
724 return Result<bool>(true);
725 }
726 [nodiscard]
727 private Result<bool> SetSelectedTabPagePos()
728 {
729 if (selectedTabPage == null) return Result<bool>(false);
730 Point loc(1, headerHeight + 1);
731 auto result = selectedTabPage->SetLocation(loc);
732 if (result.Error()) return result;
733 Size size = GetSize();
734 size.w = size.w - 2;
735 size.h = size.h - headerHeight - 2;
736 result = selectedTabPage->SetSize(size);
737 if (result.Error()) return result;
738 result = selectedTabPage->DockChildren();
739 if (result.Error()) return result;
740 return Result<bool>(true);
741 }
742 [nodiscard]
743 private Result<bool> DrawSelectedTabPage(const Rect& clipRect)
744 {
745 if (selectedTabPage == null) return Result<bool>(false);
746 auto result = SetSelectedTabPagePos();
747 if (result.Error()) return result;
748 auto locationResult = selectedTabPage->Location();
749 if (locationResult.Error())
750 {
751 return Result<bool>(ErrorId(locationResult.GetErrorId()));
752 }
753 Rect r(locationResult.Value(), selectedTabPage->GetSize());
754 if (!r.IntersectsWith(clipRect)) return Result<bool>(false);
755 result = selectedTabPage->Invalidate();
756 if (result.Error()) return result;
757 return Result<bool>(true);
758 }
759 [nodiscard]
760 private Result<bool> DrawFrame(Graphics& graphics)
761 {
762 if (selectedTabPage == null) return Result<bool>(false);
763 Size size = GetSize();
764 auto result = graphics.DrawLine(framePen,
765 PointF(0, headerHeight),
766 PointF(selectedTabPage->tab.left, headerHeight));
767 if (result.Error()) return result;
768 result = graphics.DrawLine(framePen,
769 PointF(selectedTabPage->tab.left + selectedTabPage->tab.width, headerHeight),
770 PointF(size.w - 1, headerHeight));
771 if (result.Error()) return result;
772 result = graphics.DrawLine(framePen,
773 PointF(size.w - 1, headerHeight),
774 PointF(size.w - 1, size.h - 1));
775 if (result.Error()) return result;
776 result = graphics.DrawLine(framePen,
777 PointF(size.w - 1, size.h - 1),
778 PointF(0, size.h - 1));
779 if (result.Error()) return result;
780 result = graphics.DrawLine(framePen,
781 PointF(0, size.h - 1),
782 PointF(0, headerHeight));
783 if (result.Error()) return result;
784 return Result<bool>(true);
785 }
786 internal void AddTabPageToTabPageMap(TabPage* tabPage)
787 {
788 if (!tabPage->Key().IsEmpty())
789 {
790 tabPageMap[tabPage->Key()] = tabPage;
791 }
792 }
793 internal void RemoveTabPageFromTabPageMap(TabPage* tabPage)
794 {
795 if (!tabPage->Key().IsEmpty())
796 {
797 tabPageMap.Remove(tabPage->Key());
798 }
799 }
800 private Result<bool> Measure(Graphics& graphics)
801 {
802 headerHeight = 0;
803 auto result = MeasureWidthsAndHeight(graphics);
804 if (result.Error()) return result;
805 SetVisibility(graphics);
806 CalculateMetrics(graphics);
807 return Result<bool>(true);
808 }
809 private Result<bool> MeasureWidthsAndHeight(Graphics& graphics)
810 {
811 Component* component = tabPages.FirstChild();
812 while (component != null)
813 {
814 TabPage* tabPage = cast<TabPage*>(component);
815 auto result = tabPage->MeasureWidthAndHeight(graphics, this);
816 if (result.Error()) return result;
817 component = component->NextSibling();
818 }
819 return Result<bool>(true);
820 }
821 private void SetVisibility(Graphics& graphics)
822 {
823 Component* component = tabPages.FirstChild();
824 TabPage* firstVisibleTabPage = cast<TabPage*>(component);
825 int width = GetSize().w;
826 int sum = leadingWidth;
827 bool selectedPassed = false;
828 while (component != null)
829 {
830 TabPage* tabPage = cast<TabPage*>(component);
831 int w = tabPage->tab.width;
832 sum = sum + w;
833 if (tabPage == selectedTabPage)
834 {
835 if (sum < width)
836 {
837 firstVisibleTabPage->tab.visible = true;
838 while (firstVisibleTabPage != selectedTabPage)
839 {
840 firstVisibleTabPage = cast<TabPage*>(firstVisibleTabPage->NextSibling());
841 firstVisibleTabPage->tab.visible = true;
842 }
843 selectedPassed = true;
844 }
845 else
846 {
847 sum = sum - firstVisibleTabPage->tab.width;
848 firstVisibleTabPage->tab.visible = false;
849 component = firstVisibleTabPage;
850 }
851 }
852 else if (selectedPassed)
853 {
854 if (sum < width)
855 {
856 tabPage->tab.visible = true;
857 }
858 else
859 {
860 tabPage->tab.visible = false;
861 }
862 }
863 sum = sum - overlapWidth;
864 component = component->NextSibling();
865 }
866 }
867 private void CalculateMetrics(Graphics& graphics)
868 {
869 Component* component = tabPages.FirstChild();
870 int left = leadingWidth;
871 while (component != null)
872 {
873 TabPage* tabPage = cast<TabPage*>(component);
874 tabPage->CalculateMetrics(graphics, this, left);
875 component = component->NextSibling();
876 }
877 }
878 internal inline const Font& GetFont() const
879 {
880 return font;
881 }
882 internal inline const Pen& FramePen() const
883 {
884 return framePen;
885 }
886 internal inline const Pen& CloseBoxPen() const
887 {
888 return closeBoxPen;
889 }
890 internal inline const SolidBrush& TextBrush() const
891 {
892 return textBrush;
893 }
894 internal inline const SolidBrush& TabNormalBackgroundBrush() const
895 {
896 return tabNormalBackgroundBrush;
897 }
898 internal inline const SolidBrush& TabSelectedBackgroundBrush() const
899 {
900 return tabSelectedBackgroundBrush;
901 }
902 internal inline const SolidBrush& CloseBoxSelectedBrush() const
903 {
904 return closeBoxSelectedBrush;
905 }
906 internal inline int LeadingWidth() const
907 {
908 return leadingWidth;
909 }
910 internal inline int TopMarginHeight() const
911 {
912 return topMarginHeight;
913 }
914 internal inline int HeaderHeight() const
915 {
916 return headerHeight;
917 }
918 internal void SetHeaderHeight(int headerHeight_)
919 {
920 headerHeight = headerHeight_;
921 }
922 internal inline const Padding& TabPadding() const
923 {
924 return tabPadding;
925 }
926 internal inline const Padding& TabCloseBoxPadding() const
927 {
928 return tabCloseBoxPadding;
929 }
930 internal inline int OverlapWidth() const
931 {
932 return overlapWidth;
933 }
934 internal inline float RoundingRadius() const
935 {
936 return roundingRadius;
937 }
938 internal inline const StringFormat& GetStringFormat() const
939 {
940 return stringFormat;
941 }
942 internal inline const StringFormat& CenterFormat() const
943 {
944 return centerFormat;
945 }
946 private inline bool Changed() const
947 {
948 return (flags & Flags.changed) != Flags.none;
949 }
950 internal inline void SetChanged()
951 {
952 flags = cast<Flags>(flags | Flags.changed);
953 }
954 private inline void ResetChanged()
955 {
956 flags = cast<Flags>(flags & ~Flags.changed);
957 }
958 private Flags flags;
959 private Font font;
960 private Color frameColor;
961 private Color textColor;
962 private ComponentContainer tabPages;
963 private HashMap<string, TabPage*> tabPageMap;
964 private TabPage* selectedTabPage;
965 private int leadingWidth;
966 private int topMarginHeight;
967 private int headerHeight;
968 private Padding tabPadding;
969 private Padding tabCloseBoxPadding;
970 private int overlapWidth;
971 private float roundingRadius;
972 private StringFormat stringFormat;
973 private StringFormat centerFormat;
974 private Color tabNormalBackgroundColor;
975 private Color tabSelectedBackgroundColor;
976 private Pen framePen;
977 private float closeBoxPenWidth;
978 private Pen closeBoxPen;
979 private SolidBrush textBrush;
980 private SolidBrush tabNormalBackgroundBrush;
981 private SolidBrush tabSelectedBackgroundBrush;
982 private Color closeBoxSelectedColor;
983 private SolidBrush closeBoxSelectedBrush;
984 private TabPage* closeStateTabPage;
985 private Event<TabPageSelectedEventHandler> tabPageSelectedEvent;
986 }
987
988 public class TabPage : Panel
989 {
990 public TabPage(const string& text, const string& key_) :
991 base("System.Windows.TabPage", text, Point(), Size(), Dock.none, Anchors.none, Color.White()), key(key_), tab()
992 {
993 }
994 public inline const string& Key() const
995 {
996 return key;
997 }
998 public void SetKey(const string& key_)
999 {
1000 TabControl* tabControl = GetTabControl();
1001 if (tabControl != null)
1002 {
1003 tabControl->RemoveTabPageFromTabPageMap(this);
1004 }
1005 key = key_;
1006 if (tabControl != null)
1007 {
1008 tabControl->AddTabPageToTabPageMap(this);
1009 }
1010 }
1011 [nodiscard]
1012 public Result<bool> Select()
1013 {
1014 TabControl* tabControl = GetTabControl();
1015 if (tabControl != null)
1016 {
1017 auto result = tabControl->SetSelectedTabPage(this);
1018 if (result.Error()) return result;
1019 }
1020 return Result<bool>(true);
1021 }
1022 [nodiscard]
1023 public Result<bool> Close()
1024 {
1025 TabControl* tabControl = GetTabControl();
1026 if (tabControl != null)
1027 {
1028 auto result = tabControl->CloseTabPage(this);
1029 if (result.Error()) return result;
1030 }
1031 return Result<bool>(true);
1032 }
1033 [nodiscard]
1034 public Result<bool> SelectNextTabPage()
1035 {
1036 TabControl* tabControl = GetTabControl();
1037 if (tabControl != null)
1038 {
1039 auto result = tabControl->SelectNextTabPage();
1040 if (result.Error()) return result;
1041 }
1042 return Result<bool>(true);
1043 }
1044 [nodiscard]
1045 public Result<bool> SelectPreviousTabPage()
1046 {
1047 TabControl* tabControl = GetTabControl();
1048 if (tabControl != null)
1049 {
1050 auto result = tabControl->SelectPreviousTabPage();
1051 if (result.Error()) return result;
1052 }
1053 return Result<bool>(true);
1054 }
1055 public nothrow TabControl* GetTabControl() const
1056 {
1057 Control* parentControl = ParentControl();
1058 if (parentControl != null && parentControl is TabControl*)
1059 {
1060 return cast<TabControl*>(parentControl);
1061 }
1062 return null;
1063 }
1064 [nodiscard]
1065 protected override Result<bool> OnLocationChanged()
1066 {
1067 return base->OnLocationChanged();
1068 }
1069 [nodiscard]
1070 protected override Result<bool> OnSizeChanged(SizeChangedEventArgs& args)
1071 {
1072 return base->OnSizeChanged(args);
1073 }
1074 [nodiscard]
1075 protected override Result<bool> OnTextChanged()
1076 {
1077 auto result = base->OnTextChanged();
1078 if (result.Error()) return result;
1079 tab.width = 0;
1080 TabControl* tabControl = GetTabControl();
1081 if (tabControl != null)
1082 {
1083 tabControl->SetChanged();
1084 result = tabControl->Invalidate();
1085 if (result.Error()) return result;
1086 }
1087 return Result<bool>(true);
1088 }
1089 [nodiscard]
1090 protected override Result<bool> OnKeyDown(KeyEventArgs& args)
1091 {
1092 auto result = base->OnKeyDown(args);
1093 if (result.Error()) return result;
1094 if (args.errorId != 0) return Result<bool>(ErrorId(args.errorId));
1095 if (!args.handled)
1096 {
1097 switch (args.key)
1098 {
1099 case cast<Keys>(Keys.controlModifier | Keys.f4):
1100 {
1101 result = Close();
1102 if (result.Error()) return result;
1103 args.handled = true;
1104 break;
1105 }
1106 case cast<Keys>(Keys.controlModifier | Keys.tab):
1107 {
1108 result = SelectNextTabPage();
1109 if (result.Error()) return result;
1110 args.handled = true;
1111 break;
1112 }
1113 case cast<Keys>(Keys.controlModifier | Keys.shiftModifier | Keys.tab):
1114 {
1115 result = SelectPreviousTabPage();
1116 if (result.Error()) return result;
1117 args.handled = true;
1118 break;
1119 }
1120 }
1121 }
1122 return Result<bool>(true);
1123 }
1124 internal Result<bool> MeasureWidthAndHeight(Graphics& graphics, TabControl* tabControl)
1125 {
1126 if (tab.width == 0)
1127 {
1128 auto result = graphics.MeasureStringRectF(Text(), tabControl->GetFont(), PointF(), tabControl->GetStringFormat());
1129 if (result.Error())
1130 {
1131 return Result<bool>(ErrorId(result.GetErrorId()));
1132 }
1133 RectF textRect = result.Value();
1134 result = graphics.MeasureStringRectF("x", tabControl->GetFont(), PointF(), tabControl->GetStringFormat());
1135 if (result.Error())
1136 {
1137 return Result<bool>(ErrorId(result.GetErrorId()));
1138 }
1139 RectF closeRect = result.Value();
1140 tab.textHeight = textRect.size.h;
1141 tab.textWidth = textRect.size.w;
1142 tab.closeBoxWidth = closeRect.size.w;
1143 tab.height = cast<int>(tabControl->TabPadding().Vertical() + tabControl->TabCloseBoxPadding().Vertical() + tab.textHeight);
1144 tab.width = cast<int>(tabControl->TabPadding().Horizontal() + tab.textWidth + tabControl->OverlapWidth() +
1145 tabControl->TabCloseBoxPadding().Horizontal() + tab.closeBoxWidth);
1146 }
1147 tabControl->SetHeaderHeight(Max(tabControl->HeaderHeight(), tab.height + tabControl->TopMarginHeight()));
1148 return Result<bool>(true);
1149 }
1150 internal void CalculateMetrics(Graphics& graphics, TabControl* tabControl, int& left)
1151 {
1152 if (!tab.visible) return;
1153 float roundingRadius = tabControl->RoundingRadius();
1154 int topMarginHeight = tabControl->TopMarginHeight();
1155 tab.left = left;
1156 tab.leftRoundingRect = RectF(PointF(), SizeF(2 * roundingRadius, 2 * roundingRadius));
1157 tab.leftRoundingRect.Offset(left, topMarginHeight);
1158 tab.rightRoundingRect = RectF(PointF(), SizeF(2 * roundingRadius, 2 * roundingRadius));
1159 tab.rightRoundingRect.Offset(left + tab.width - 2 * roundingRadius, topMarginHeight);
1160 tab.topRect = RectF(PointF(), SizeF(tab.width - 2 * roundingRadius, roundingRadius));
1161 tab.topRect.Offset(left + roundingRadius, topMarginHeight);
1162 tab.bottomRect = RectF(PointF(), SizeF(tab.width, tab.height - roundingRadius + 1));
1163 tab.bottomRect.Offset(left, topMarginHeight + roundingRadius);
1164 tab.textRect = RectF(PointF(), SizeF(tab.width - (tabControl->TabCloseBoxPadding().Horizontal() + tab.closeBoxWidth), tab.height));
1165 tab.textRect.Offset(left, topMarginHeight);
1166 tab.selectRect = Rect(Point(tab.left, topMarginHeight),
1167 Size(cast<int>(tab.width - (tab.closeBoxWidth + tabControl->TabCloseBoxPadding().right)), tab.height));
1168 tab.closeBoxRect = RectF(
1169 PointF(tab.left + tab.selectRect.size.w,
1170 topMarginHeight + tabControl->TabPadding().top + tabControl->TabCloseBoxPadding().top),
1171 SizeF(tab.closeBoxWidth, tab.closeBoxWidth));
1172 tab.closeRect = Rect(Point(cast<int>(tab.closeBoxRect.location.x), cast<int>(tab.closeBoxRect.location.y)),
1173 Size(cast<int>(tab.closeBoxRect.size.w), cast<int>(tab.closeBoxRect.size.h)));
1174 tab.closeRect.Inflate(3, 3);
1175 tab.closeBoxRect.Inflate(-1, -1);
1176 left = left + tab.width - tabControl->OverlapWidth();
1177 }
1178 internal Result<bool> DrawTab(Graphics& graphics, TabControl* tabControl)
1179 {
1180 if (!tab.visible) return Result<bool>(false);
1181 Brush* backgroundBrush = null;
1182 if (this == tabControl->SelectedTabPage())
1183 {
1184 const Brush& brush = tabControl->TabSelectedBackgroundBrush();
1185 backgroundBrush = &brush;
1186 }
1187 else
1188 {
1189 const Brush& brush = tabControl->TabNormalBackgroundBrush();
1190 backgroundBrush = &brush;
1191 }
1192 const Pen& framePen = tabControl->FramePen();
1193 const Pen& closeBoxPen = tabControl->CloseBoxPen();
1194 int topMarginHeight = tabControl->TopMarginHeight();
1195 float roundingRadius = tabControl->RoundingRadius();
1196 auto result = graphics.FillEllipse(*backgroundBrush, tab.leftRoundingRect);
1197 if (result.Error()) return result;
1198 result = graphics.FillEllipse(*backgroundBrush, tab.rightRoundingRect);
1199 if (result.Error()) return result;
1200 result = graphics.FillRectangle(*backgroundBrush, tab.topRect);
1201 if (result.Error()) return result;
1202 result = graphics.FillRectangle(*backgroundBrush, tab.bottomRect);
1203 if (result.Error()) return result;
1204 result = graphics.DrawLine(framePen, PointF(tab.left, tab.bottomRect.location.y + tab.bottomRect.size.h), PointF(tab.left, tab.bottomRect.location.y));
1205 if (result.Error()) return result;
1206 result = graphics.DrawArc(framePen, tab.leftRoundingRect, -180.000000f, 90.000000f);
1207 if (result.Error()) return result;
1208 result = graphics.DrawString(Text(), tabControl->GetFont(), tab.textRect, tabControl->CenterFormat(), tabControl->TextBrush());
1209 if (result.Error()) return result;
1210 result = graphics.DrawLine(framePen,
1211 PointF(tab.leftRoundingRect.location.x + roundingRadius, topMarginHeight),
1212 PointF(tab.rightRoundingRect.location.x + roundingRadius, topMarginHeight));
1213 if (result.Error()) return result;
1214 result = graphics.DrawArc(framePen, tab.rightRoundingRect, -90.000000f, 90.000000f);
1215 if (result.Error()) return result;
1216 result = graphics.DrawLine(framePen,
1217 PointF(tab.left + tab.width, tab.bottomRect.location.y),
1218 PointF(tab.left + tab.width, tab.bottomRect.location.y + tab.bottomRect.size.h - 1));
1219 if (result.Error()) return result;
1220 if (tab.state == Tab.State.normal)
1221 {
1222 RectF r = tab.closeBoxRect;
1223 r.Inflate(3, 3);
1224 result = graphics.FillRectangle(*backgroundBrush, r);
1225 if (result.Error()) return result;
1226 }
1227 else if (tab.state == Tab.State.closeBoxSelected)
1228 {
1229 RectF r = tab.closeBoxRect;
1230 r.Inflate(3, 3);
1231 const Brush& selectedBrush = tabControl->CloseBoxSelectedBrush();
1232 result = graphics.FillRectangle(selectedBrush, r);
1233 if (result.Error()) return result;
1234 }
1235 result = graphics.DrawLine(closeBoxPen,
1236 tab.closeBoxRect.location, PointF(tab.closeBoxRect.location.x + tab.closeBoxRect.size.w, tab.closeBoxRect.location.y + tab.closeBoxRect.size.h));
1237 if (result.Error()) return result;
1238 result = graphics.DrawLine(closeBoxPen,
1239 PointF(tab.closeBoxRect.location.x, tab.closeBoxRect.location.y + tab.closeBoxRect.size.h),
1240 PointF(tab.closeBoxRect.location.x + tab.closeBoxRect.size.w, tab.closeBoxRect.location.y));
1241 if (result.Error()) return result;
1242 return Result<bool>(true);
1243 }
1244 private string key;
1245 internal Tab tab;
1246 }