@@ -160,6 +160,7 @@ DraggableTabComponent::DraggableTabComponent (DataViewport* parent_) : TabbedCom
160160 setTabBarDepth (28 );
161161 setOutline (0 );
162162 setIndent (5 ); // gap to leave around the edge of the content component
163+ getTabbedButtonBar ().setMinimumTabScaleFactor (0 .5f );
163164
164165 Path closeButtonPath;
165166 closeButtonPath.addLineSegment (Line<float > (0 , 0 , 12 , 12 ), 1 .0f );
@@ -512,12 +513,10 @@ void DraggableTabComponent::takeComponentSnapshot (int tabIndex, const String& t
512513 }
513514}
514515
515- AddTabbedComponentButton::AddTabbedComponentButton ( )
516- : Button (" Add Tabbed Component" )
516+ AddTabbedComponentButton::AddTabbedComponentButton (SplitType splitType )
517+ : Button (" Add Tabbed Component" ), type (splitType)
517518{
518519 path.addRoundedRectangle (1 , 1 , 18 , 18 , 3 .0f );
519- path.addLineSegment (Line<float > (9 , 1 , 9 , 19 ), 0 .0f );
520- path.addTriangle (12 , 7 , 12 , 13 , 17 , 10 );
521520}
522521
523522AddTabbedComponentButton::~AddTabbedComponentButton () = default ;
@@ -538,11 +537,21 @@ void AddTabbedComponentButton::paintButton (Graphics& g, bool isMouseOverButton,
538537 g.setColour (btnColour.withAlpha (0 .9f ));
539538 }
540539
541- g.strokePath (path, PathStrokeType (1 .0f ));
540+ Path splitPath;
541+ if (type == SplitType::Horizontal)
542+ {
543+ g.drawRoundedRectangle (3 , 3 , getWidth () - 6 , getHeight () - 6 , 2 , 1 );
544+ g.fillRect ((float ) (getWidth () / 2 ) - 0 .5f , 3 .0f , 1 .0f , (float ) (getHeight () - 6 ));
545+ }
546+ else
547+ {
548+ g.drawRoundedRectangle (3 , 3 , getWidth () - 6 , getHeight () - 6 , 2 , 1 );
549+ g.fillRect (3 .0f , (float ) (getHeight () / 2 ) - 0 .5f , (float ) (getWidth () - 6 ), 1 .0f );
550+ }
542551}
543552
544- TabbedComponentResizerBar::TabbedComponentResizerBar (StretchableLayoutManager* layoutToUse)
545- : StretchableLayoutResizerBar (layoutToUse, 1 , true ), layout (layoutToUse)
553+ TabbedComponentResizerBar::TabbedComponentResizerBar (StretchableLayoutManager* layoutToUse, bool isVerticalBar )
554+ : StretchableLayoutResizerBar (layoutToUse, 1 , isVerticalBar ), layout (layoutToUse), isVertical (isVerticalBar )
546555{
547556 dragHandle = Drawable::parseSVGPath (
548557 " M19.63,11.31,16.5,9.17a1,1,0,0,0-1.5.69v4.28a1,1,0,0,0,1.5.69l3.13-2.14A.82.82,0,0,0,19.63,11.31ZM4.37,"
@@ -553,8 +562,8 @@ TabbedComponentResizerBar::~TabbedComponentResizerBar() = default;
553562
554563void TabbedComponentResizerBar::paint (Graphics& g)
555564{
556- int w = getWidth ();
557- int h = getHeight ();
565+ const int w = getWidth ();
566+ const int h = getHeight ();
558567
559568 if (isMouseButtonDown ())
560569 g.setColour (findColour (ThemeColours::highlightedFill));
@@ -563,16 +572,30 @@ void TabbedComponentResizerBar::paint (Graphics& g)
563572 else
564573 g.setColour (findColour (ThemeColours::defaultFill).withAlpha (0 .6f ));
565574
566- g.fillRect ((w / 2 ) - 1 , 0 , 2 , h);
567-
568- g.fillPath (dragHandle, dragHandle.getTransformToScaleToFit (0 , (h / 2 ) - (w / 2 ), w, w, true ));
575+ if (isVertical)
576+ {
577+ g.fillRect ((w / 2 ) - 1 , 0 , 2 , h);
578+ g.fillPath (dragHandle, dragHandle.getTransformToScaleToFit (0 , (h / 2 ) - (w / 2 ), w, w, true ));
579+ }
580+ else
581+ {
582+ g.fillRect (0 , (h / 2 ) - 1 , w, 2 );
583+ g.fillPath (dragHandle, dragHandle.getTransformToScaleToFit ((w / 2 ) - (h / 2 ), 0 , h, h, true ).rotated (MathConstants<float >::halfPi, w / 2 .0f , h / 2 .0f ));
584+ }
569585}
570586
571587void TabbedComponentResizerBar::mouseDoubleClick (const MouseEvent& event)
572588{
573589 if (Component* parent = getParentComponent ())
574590 {
575- layout->setItemPosition (1 , (parent->getWidth () / 2 ) - (getWidth () / 2 ));
591+ if (isVertical)
592+ {
593+ layout->setItemPosition (1 , (parent->getWidth () / 2 ) - (getWidth () / 2 ));
594+ }
595+ else
596+ {
597+ layout->setItemPosition (1 , (parent->getHeight () / 2 ) - (getHeight () / 2 ));
598+ }
576599 parent->resized ();
577600 }
578601}
@@ -583,40 +606,44 @@ DataViewport::DataViewport() : shutdown (false)
583606 addAndMakeVisible (c);
584607 draggableTabComponents.add (c);
585608
586- addTabbedComponentButton = std::make_unique<AddTabbedComponentButton>( );
587- addAndMakeVisible (addTabbedComponentButton .get ());
588- addTabbedComponentButton ->addListener (this );
609+ addHorizontalSplitButton = std::make_unique<AddTabbedComponentButton> (AddTabbedComponentButton::SplitType::Horizontal );
610+ addAndMakeVisible (addHorizontalSplitButton .get ());
611+ addHorizontalSplitButton ->addListener (this );
589612
590- tabbedComponentResizer = std::make_unique<TabbedComponentResizerBar> (&tabbedComponentLayout);
591- addChildComponent (tabbedComponentResizer.get ());
613+ addVerticalSplitButton = std::make_unique<AddTabbedComponentButton> (AddTabbedComponentButton::SplitType::Vertical);
614+ addAndMakeVisible (addVerticalSplitButton.get ());
615+ addVerticalSplitButton->addListener (this );
616+
617+ recreateResizerBar ();
592618}
593619
594620void DataViewport::resized ()
595621{
596- int width = getWidth () / draggableTabComponents.size ();
622+ const int numComponents = draggableTabComponents.size ();
623+ const int width = numComponents > 0 ? getWidth () / numComponents : getWidth ();
597624
598- if (draggableTabComponents.size () == 1 )
625+ if (numComponents == 1 )
626+ {
599627 draggableTabComponents[0 ]->setBounds (0 , 0 , width, getHeight ());
600- else if (draggableTabComponents.size () == 2 )
628+ tabbedComponentResizer->setVisible (false );
629+ }
630+ else if (numComponents == 2 )
601631 {
632+ tabbedComponentResizer->setVisible (true );
602633 Component* comps[] = { draggableTabComponents[0 ], tabbedComponentResizer.get (), draggableTabComponents[1 ] };
603- tabbedComponentLayout.layOutComponents (comps, 3 , 0 , 0 , getWidth (), getHeight (), false , true );
634+ const bool layOutVertically = splitOrientation == SplitOrientation::Vertical;
635+ tabbedComponentLayout.layOutComponents (comps, 3 , 0 , 0 , getWidth (), getHeight (), layOutVertically, true );
604636 }
605637 else
606638 {
607- for (int i = 0 ; i < draggableTabComponents.size (); i++)
639+ tabbedComponentResizer->setVisible (false );
640+ for (int i = 0 ; i < numComponents; i++)
608641 {
609642 draggableTabComponents[i]->setBounds (width * i, 0 , width, getHeight ());
610643 }
611644 }
612645
613- addTabbedComponentButton->setBounds (getWidth () - 24 , getHeight () - 26 , 20 , 20 );
614- addTabbedComponentButton->toFront (false );
615-
616- if (draggableTabComponents[activeTabbedComponent]->getNumTabs () > 1 && activeTabbedComponent < 2 )
617- addTabbedComponentButton->setVisible (true );
618- else
619- addTabbedComponentButton->setVisible (false );
646+ updateControlButtons ();
620647}
621648
622649void DataViewport::addTab (String name,
@@ -654,26 +681,13 @@ void DataViewport::removeTab (int nodeId, bool sendNotification)
654681
655682void DataViewport::buttonClicked (Button* button)
656683{
657- if (button == addTabbedComponentButton .get ())
684+ if (button == addHorizontalSplitButton .get ())
658685 {
659- DraggableTabComponent* d = new DraggableTabComponent (this );
660- addAndMakeVisible (d);
661- draggableTabComponents.add (d);
662-
663- if (draggableTabComponents.size () == 2 )
664- {
665- tabbedComponentResizer->setVisible (true );
666-
667- tabbedComponentLayout.setItemLayout (0 , -0.25 , -0.75 , -0.5 );
668- tabbedComponentLayout.setItemLayout (1 , 12 , 12 , 12 );
669- tabbedComponentLayout.setItemLayout (2 , -0.25 , -0.75 , -0.5 );
670- }
671-
672- resized ();
673-
674- activeTabbedComponent++;
675-
676- addTabbedComponentButton->setVisible (false );
686+ handleSplit (SplitOrientation::Horizontal);
687+ }
688+ else if (button == addVerticalSplitButton.get ())
689+ {
690+ handleSplit (SplitOrientation::Vertical);
677691 }
678692}
679693
@@ -708,11 +722,7 @@ void DataViewport::removeTabbedComponent (DraggableTabComponent* draggableTabCom
708722
709723 if (draggableTabComponents.size () == 2 )
710724 {
711- tabbedComponentResizer->setVisible (true );
712-
713- tabbedComponentLayout.setItemLayout (0 , -0.25 , -0.75 , -0.5 );
714- tabbedComponentLayout.setItemLayout (1 , 12 , 12 , 12 );
715- tabbedComponentLayout.setItemLayout (2 , -0.25 , -0.75 , -0.5 );
725+ configureLayoutForTwoComponents ();
716726 }
717727 else
718728 {
@@ -727,6 +737,8 @@ void DataViewport::saveStateToXml (XmlElement* xml)
727737{
728738 XmlElement* dataViewportState = xml->createNewChildElement (" DATAVIEWPORT" );
729739
740+ dataViewportState->setAttribute (" splitOrientation" , splitOrientation == SplitOrientation::Vertical ? " vertical" : " horizontal" );
741+
730742 // save tab order in each draggableTabComponent
731743 for (int i = 0 ; i < draggableTabComponents.size (); i++)
732744 {
@@ -753,6 +765,9 @@ void DataViewport::loadStateFromXml (XmlElement* xml)
753765 {
754766 LOGD (" Loading DataViewport state from XML..." );
755767
768+ const auto orientationString = dvXml->getStringAttribute (" splitOrientation" , " horizontal" );
769+ setSplitOrientation (orientationString.equalsIgnoreCase (" vertical" ) ? SplitOrientation::Vertical : SplitOrientation::Horizontal);
770+
756771 // remove info, graph, and console tabs
757772 for (int i = 0 ; i < 3 ; i++)
758773 removeTab (i);
@@ -773,12 +788,8 @@ void DataViewport::loadStateFromXml (XmlElement* xml)
773788 DraggableTabComponent* d = new DraggableTabComponent (this );
774789 addAndMakeVisible (d);
775790 draggableTabComponents.add (d);
776-
777- tabbedComponentResizer->setVisible (draggableTabComponents.size () == 2 );
778-
779- tabbedComponentLayout.setItemLayout (0 , -0.25 , -0.75 , -0.5 );
780- tabbedComponentLayout.setItemLayout (1 , 12 , 12 , 12 );
781- tabbedComponentLayout.setItemLayout (2 , -0.25 , -0.75 , -0.5 );
791+ if (draggableTabComponents.size () == 2 )
792+ configureLayoutForTwoComponents ();
782793 }
783794
784795 activeTabbedComponent = index;
@@ -838,3 +849,68 @@ void DataViewport::disableConnectionToEditorViewport()
838849{
839850 shutdown = true ;
840851}
852+
853+ void DataViewport::recreateResizerBar ()
854+ {
855+ tabbedComponentResizer = std::make_unique<TabbedComponentResizerBar> (&tabbedComponentLayout, splitOrientation != SplitOrientation::Vertical);
856+ addChildComponent (tabbedComponentResizer.get ());
857+ }
858+
859+ void DataViewport::configureLayoutForTwoComponents ()
860+ {
861+ tabbedComponentLayout.clearAllItems ();
862+ tabbedComponentResizer->setVisible (true );
863+ tabbedComponentLayout.setItemLayout (0 , -0.25 , -0.75 , -0.5 );
864+ tabbedComponentLayout.setItemLayout (1 , 16 , 16 , 16 );
865+ tabbedComponentLayout.setItemLayout (2 , -0.25 , -0.75 , -0.5 );
866+ }
867+
868+ void DataViewport::updateControlButtons ()
869+ {
870+ const int buttonSize = 20 ;
871+ const int padding = 4 ;
872+ const int addButtonX = getWidth () - buttonSize - padding;
873+
874+ const bool hasActiveComponent = activeTabbedComponent < draggableTabComponents.size ();
875+ const bool canAddSplit = hasActiveComponent && draggableTabComponents[activeTabbedComponent]->getNumTabs () > 1 && activeTabbedComponent < 2 ;
876+
877+ const int vertY = getHeight () - buttonSize - padding;
878+ const int horizY = vertY - buttonSize - padding;
879+
880+ addHorizontalSplitButton->setBounds (addButtonX, horizY, buttonSize, buttonSize);
881+ addVerticalSplitButton->setBounds (addButtonX, vertY, buttonSize, buttonSize);
882+
883+ addHorizontalSplitButton->toFront (false );
884+ addVerticalSplitButton->toFront (false );
885+ }
886+
887+ void DataViewport::setSplitOrientation (SplitOrientation orientation)
888+ {
889+ splitOrientation = orientation;
890+ recreateResizerBar ();
891+ if (draggableTabComponents.size () == 2 )
892+ configureLayoutForTwoComponents ();
893+ else
894+ tabbedComponentResizer->setVisible (false );
895+ resized ();
896+ }
897+
898+ void DataViewport::handleSplit (SplitOrientation desiredOrientation)
899+ {
900+ // Ensure a second component exists
901+ if (draggableTabComponents.size () < 2 )
902+ {
903+ DraggableTabComponent* d = new DraggableTabComponent (this );
904+ addAndMakeVisible (d);
905+ draggableTabComponents.add (d);
906+
907+ activeTabbedComponent++;
908+ setSplitOrientation (desiredOrientation);
909+ }
910+ else if (splitOrientation != desiredOrientation)
911+ {
912+ setSplitOrientation (desiredOrientation);
913+ }
914+
915+ resized ();
916+ }
0 commit comments