How to use a ViewPort in this case?


#1

I have a system of Flexboxes that automatically organizes groups of sliders or other components on the screen. It is working well. A crude early version of what this can look like is this for perspective:

The trouble is my development and implementation has gotten far more advanced and I need a Viewport so I can vertically scroll as the groups are passing the viewable limit horizontally.

Here is my broken attempt to add a ViewPort (at the end). Not only is no viewport or scrollbar showing up, it is giving me a breakpoint on building and saying Unhandled exception at 0x00007FF80B0C47FB (ntdll.dll) in AudioPlugIn-Private.exe: 0xC0000374: A heap has been corrupted (parameters: 0x00007FF80B1297B0).. So obviously I am doing this completely wrong.

As I understand it the Viewport needs a component to base itself around, right? And it wouldn’t take “parentBox” which is the appropriate item in the design (since “parentBox” is the overarching FlexBox that contains all the groups and everything inside). So I thought maybe I could just make a generic new Component, find some way to make it the same size as ParentBox, and then use that as my Viewport component.

This is from my PluginEditor.cpp:

void AudioPlugInAudioProcessorEditor::resized()
{
    // This is generally where you'll want to lay out the positions of any
    // subcomponents in your editor..


	FlexBox groupsBox;
	groupsBox.flexWrap = FlexBox::Wrap::wrap;
	groupsBox.justifyContent = FlexBox::JustifyContent::flexStart;
	groupsBox.alignContent = FlexBox::AlignContent::flexStart;


	for (int i = 0; i< mLabeledGroups.size(); i++) {

		int rowCount = (mLabeledGroups[i]->getWidth() / (getWidth() - outerScreenMargin * 2 - betweenLabeledGroupMargin * 2) + (mLabeledGroups[i]->getWidth() % (getWidth() - outerScreenMargin * 2 - betweenLabeledGroupMargin * 2) != 0));

		groupsBox.items.add(FlexItem(*mLabeledGroups[i])
			.withMinHeight((mLabeledGroups[i]->getHeight()) * rowCount
				+ verticalLabeledGroupMargin 
				+ betweenLabeledComponentMargin * 2 * (rowCount - 1) 
			)

			.withMinWidth(mLabeledGroups[i]->getWidth())
			.withMaxWidth(getWidth() - outerScreenMargin * 2 - betweenLabeledGroupMargin * 2)
			.withMargin(betweenLabeledGroupMargin) 
			.withFlex(1));

	}
		
	FlexBox parentBox;
	parentBox.flexDirection = FlexBox::Direction::column;
	parentBox.items.add(FlexItem(groupsBox).withFlex(2.5, 2).withMargin(outerScreenMargin));
	parentBox.performLayout(getLocalBounds().toFloat());

//BROKEN PART FOLLOWS
	Component generalBoundsComponent;
	addAndMakeVisible(generalBoundsComponent);
	generalBoundsComponent.setSize(1000, 1000);
	
	Viewport mainViewPort;
	addAndMakeVisible(mainViewPort);
	mainViewPort.setScrollBarsShown(true, false);
	mainViewPort.setSize(screenWidth, screenHeight);
    	mainViewPort.setViewedComponent(&generalBoundsComponent);
	}

Any help? Thanks.


#2

You declared an instance of Component as well as of ViewPort locally, so they will be destroyed after resized() is finished.


#3

You of course must not create your components as local variables in the resized() method. They will just be immediately destroyed when the function finishes. And you do setViewedComponent with the deleteComponentWhenNoLongerNeeded parameter set to true, which will cause the crash because of a double deletion.

You need to add your ViewPort and ViewPort content component as member variables of your class and initialize them in the constructor.


#4

Thanks @MBO and @Xenakios. That worked perfectly. I can do a lot of things in C++ now but there are still holes in my knowledge. For example, I didn’t actually realize that objects/variables declared in a function disappear after that function is done.

I fixed it by declaring the generic component in the right place, then adding the mLabeledGroups[i] as childComponents to the generalBoundsComponent inside the already existing for loop, then setting the size of the generic generalBoundsComponent to the bottom of the last mLabeledGroups item after Flexbox was done:

for (int i = 0; i< mLabeledGroups.size(); i++) {
...
generalBoundsComponent.addChildComponent(*mLabeledGroups[i]);

}
	
parentBox.flexDirection = FlexBox::Direction::column;
parentBox.items.add(FlexItem(groupsBox).withFlex(2.5, 2).withMargin(outerScreenMargin)); //adds margin around edge of screen
parentBox.performLayout(getLocalBounds().toFloat());

addAndMakeVisible(generalBoundsComponent);
generalBoundsComponent.setSize(getParentWidth(), mLabeledGroups[mLabeledGroups.size() - 1]->getBottom() + outerScreenMargin);

addAndMakeVisible(mainViewPort);
mainViewPort.setScrollBarsShown(true, false);
mainViewPort.setSize(screenWidth, screenHeight-1);
mainViewPort.setViewedComponent(&generalBoundsComponent);

Funny little glitch - I have to use screenHeight-1 for height of the ViewPort or it imprints a line of pixels along the bottom of the screen edge from the initial default rendering of the groupComponents that is permanent. But with -1 it is fine and looks good.

Thanks again guys. This is perfect.