1 // =================================
   2 // Copyright (c) 2024 Seppo Laakko
   3 // Distributed under the MIT license
   4 // =================================
   5 
   6 using System;
   7 using System.Collections;
   8 
   9 namespace System.Windows
  10 {
  11     public class delegate void TabPageSelectedEventHandler();
  12 
  13     public Color DefaultTabControlFrameColor()
  14     {
  15         return Color(204u206u219u);
  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(255u255u215u);
  31     }
  32 
  33     public Color DefaultTabCloseBoxSelectedColor()
  34     {
  35         return Color(222u238u245u);
  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(8282);
  51     }
  52 
  53     public Padding DefaultTabCloseBoxPadding()
  54     {
  55         return Padding(8680);
  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             normalcloseBoxSelected
  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 = 0changed = 1 << 0
 225         }
 226         public TabControl(const Font& font_const Color& frameColor_const Point& locationconst Size& sizeDock dockAnchors anchors) : 
 227             base("System.Windows.TabControl"DefaultWindowClassStyle()DefaultChildWindowStyle()DefaultExtendedWindowStyle()
 228             DefaultControlBackgroundColor()"tabControl"locationsizedockanchors)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.centerStringAlignment.center)framePen(frameColor)textBrush(textColor)
 234             tabNormalBackgroundColor(DefaultTabNormalBackgroundColor())tabNormalBackgroundBrush(tabNormalBackgroundColor)
 235             tabSelectedBackgroundColor(DefaultTabSelectedBackgroundColor())tabSelectedBackgroundBrush(tabSelectedBackgroundColor)
 236             closeBoxPenWidth(DefaultTabCloseBoxPenWidth())closeBoxPen(textColorcloseBoxPenWidth)closeStateTabPage(null)
 237             closeBoxSelectedColor(DefaultTabCloseBoxSelectedColor())closeBoxSelectedBrush(closeBoxSelectedColor)
 238         {
 239             Init();
 240         }
 241         public TabControl(const Point& locationconst Size& sizeDock dockAnchors anchors) : 
 242             this(Font(FontFamily("Segoe UI")9.000000f)DefaultTabControlFrameColor()locationsizedockanchors)
 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.centerStringAlignment.center)framePen(frameColor)textBrush(textColor)
 260             tabNormalBackgroundColor(createParams.tabNormalBackgroundColor)tabNormalBackgroundBrush(tabNormalBackgroundColor)
 261             tabSelectedBackgroundColor(createParams.tabSelectedBackgroundColor)tabSelectedBackgroundBrush(tabSelectedBackgroundColor)
 262             closeBoxPenWidth(createParams.closeBoxPenWidth)closeBoxPen(textColorcloseBoxPenWidth)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& textconst string& key)
 363         {
 364             return AddTabPage(new TabPage(textkey));
 365         }
 366         [nodiscard]
 367         public Result<bool> AddTabPage(const string& text)
 368         {
 369             return AddTabPage(textstring());
 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(graphicsthis);
 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(graphicsthis);
 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(1headerHeight + 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(0headerHeight)
 766                 PointF(selectedTabPage->tab.leftheaderHeight));
 767             if (result.Error()) return result;
 768             result = graphics.DrawLine(framePen
 769                 PointF(selectedTabPage->tab.left + selectedTabPage->tab.widthheaderHeight)
 770                 PointF(size.w - 1headerHeight));
 771             if (result.Error()) return result;
 772             result = graphics.DrawLine(framePen
 773                 PointF(size.w - 1headerHeight)
 774                 PointF(size.w - 1size.h - 1));
 775             if (result.Error()) return result;
 776             result = graphics.DrawLine(framePen
 777                 PointF(size.w - 1size.h - 1)
 778                 PointF(0size.h - 1));
 779             if (result.Error()) return result;
 780             result = graphics.DrawLine(framePen
 781                 PointF(0size.h - 1)
 782                 PointF(0headerHeight));
 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(graphicsthis);
 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(graphicsthisleft);
 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<stringTabPage*> 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& textconst string& key_) : 
 991             base("System.Windows.TabPage"textPoint()Size()Dock.noneAnchors.noneColor.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& graphicsTabControl* 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& graphicsTabControl* tabControlint& 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 * roundingRadius2 * roundingRadius));
1157             tab.leftRoundingRect.Offset(lefttopMarginHeight);
1158             tab.rightRoundingRect = RectF(PointF()SizeF(2 * roundingRadius2 * roundingRadius));
1159             tab.rightRoundingRect.Offset(left + tab.width - 2 * roundingRadiustopMarginHeight);
1160             tab.topRect = RectF(PointF()SizeF(tab.width - 2 * roundingRadiusroundingRadius));
1161             tab.topRect.Offset(left + roundingRadiustopMarginHeight);
1162             tab.bottomRect = RectF(PointF()SizeF(tab.widthtab.height - roundingRadius + 1));
1163             tab.bottomRect.Offset(lefttopMarginHeight + roundingRadius);
1164             tab.textRect = RectF(PointF()SizeF(tab.width - (tabControl->TabCloseBoxPadding().Horizontal() + tab.closeBoxWidth)tab.height));
1165             tab.textRect.Offset(lefttopMarginHeight);
1166             tab.selectRect = Rect(Point(tab.lefttopMarginHeight)
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.closeBoxWidthtab.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(33);
1175             tab.closeBoxRect.Inflate(-1-1);
1176             left = left + tab.width - tabControl->OverlapWidth();
1177         }
1178         internal Result<bool> DrawTab(Graphics& graphicsTabControl* 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(*backgroundBrushtab.leftRoundingRect);
1197             if (result.Error()) return result;
1198             result = graphics.FillEllipse(*backgroundBrushtab.rightRoundingRect);
1199             if (result.Error()) return result;
1200             result = graphics.FillRectangle(*backgroundBrushtab.topRect);
1201             if (result.Error()) return result;
1202             result = graphics.FillRectangle(*backgroundBrushtab.bottomRect);
1203             if (result.Error()) return result;
1204             result = graphics.DrawLine(framePenPointF(tab.lefttab.bottomRect.location.y + tab.bottomRect.size.h)PointF(tab.lefttab.bottomRect.location.y));
1205             if (result.Error()) return result;
1206             result = graphics.DrawArc(framePentab.leftRoundingRect-180.000000f90.000000f);
1207             if (result.Error()) return result;
1208             result = graphics.DrawString(Text()tabControl->GetFont()tab.textRecttabControl->CenterFormat()tabControl->TextBrush());
1209             if (result.Error()) return result;
1210             result = graphics.DrawLine(framePen
1211                 PointF(tab.leftRoundingRect.location.x + roundingRadiustopMarginHeight)
1212                 PointF(tab.rightRoundingRect.location.x + roundingRadiustopMarginHeight));
1213             if (result.Error()) return result;
1214             result = graphics.DrawArc(framePentab.rightRoundingRect-90.000000f90.000000f);
1215             if (result.Error()) return result;
1216             result = graphics.DrawLine(framePen
1217                 PointF(tab.left + tab.widthtab.bottomRect.location.y)
1218                 PointF(tab.left + tab.widthtab.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(33);
1224                 result = graphics.FillRectangle(*backgroundBrushr);
1225                 if (result.Error()) return result;
1226             }
1227             else if (tab.state == Tab.State.closeBoxSelected)
1228             {
1229                 RectF r = tab.closeBoxRect;
1230                 r.Inflate(33);
1231                 const Brush& selectedBrush = tabControl->CloseBoxSelectedBrush();
1232                 result = graphics.FillRectangle(selectedBrushr);
1233                 if (result.Error()) return result;
1234             }
1235             result = graphics.DrawLine(closeBoxPen
1236                 tab.closeBoxRect.locationPointF(tab.closeBoxRect.location.x + tab.closeBoxRect.size.wtab.closeBoxRect.location.y + tab.closeBoxRect.size.h));
1237             if (result.Error()) return result;
1238             result = graphics.DrawLine(closeBoxPen
1239                 PointF(tab.closeBoxRect.location.xtab.closeBoxRect.location.y + tab.closeBoxRect.size.h)
1240                 PointF(tab.closeBoxRect.location.x + tab.closeBoxRect.size.wtab.closeBoxRect.location.y));
1241             if (result.Error()) return result;
1242             return Result<bool>(true);
1243         }
1244         private string key;
1245         internal Tab tab;
1246     }