Size of GUI component and viewport use with relative widths?


#1

Using Projucer, I generated a new GUI application, and then a new GUI component.

I want the GUI component to contain 10 sliders, one below another, such that the sliders have 24px height and 50% of the container/parent width.

Then the GUI component should stretch to the width of the parent window, and to be on top (i.e. top, left and right edges should be “snapped” to the parent/window) - but I’d like to have it have fixed height in pixels (200 px), and to get a vertical scrollbar as I expect the slider content to overflow.

Closest I could find to this was to use:

myViewport.setBoundsRelative(0.0f, 0.0f, 1.0f, 0.2f);

… so I cannot set the height to 200px if I want relative, so I’ve set it to 20% of parent window - would have been good enough for me.

I have posted the code I’ve used on this gist (.jucer file and Source/ files):

https://gist.github.com/anonymous/5b49804c0733906069fc053fbb464871

… however, when I compile and run it (Ubuntu 14.04), I get something like this:

… that is, when I stretch the width beyond some size the component inside the viewport doesn’t stretch to the width of the parent. Also, right at start the height of the viewort is more than the 20% height expected, so I have to manually resize it to get the approx 20% height as is shown on the image.

Also, when I exit the application by closing the window, I get:

*** Error in `./Builds/LinuxMakefile/build/TestCompSize': munmap_chunk(): invalid pointer: 0x0000000002006810 ***
Aborted (core dumped)

Is it possible that I achieve such a component: where its top, left and right edges “stick” or “snap” to the its top, left and right edges of the window, is otherwise 200px in height, and has a vertical scrollbar for showing the overflowed content (sliders) inside it (without it crashing on exit)?


#2

Well, I think I got somewhere… this would be the modified MainComponent.cpp (in respect to the gist code):

#include "MainComponent.h"

//==============================================================================
MainContentComponent::MainContentComponent()
{
    setSize (600, 400);
    addAndMakeVisible(myGSliders);
    addAndMakeVisible(myViewport);
    myViewport.setViewedComponent(&myGSliders, false);
    resized();
}

MainContentComponent::~MainContentComponent()
{
}

void MainContentComponent::paint (Graphics& g)
{
    //~ g.fillAll (Colour (0xff001F36));
    g.fillAll (Colour (0xff888888));

    g.setFont (Font (16.0f));
    g.setColour (Colours::white);
    g.drawText ("Hello World!", getLocalBounds(), Justification::centred, true);
}

void MainContentComponent::resized()
{
    // This is called when the MainContentComponent is resized.
    // If you add any child components, this is where you should
    // update their positions.
    myGSliders.setSize(getWidth()-0, myGSliders.getHeight()); // needs some space for v.scrollbar
    myViewport.setBounds(0, 0, proportionOfWidth(1.0f), 76); // but first size the view
    myGSliders.setSize(myViewport.getMaximumVisibleWidth(), myGSliders.getHeight()); // then resize component again, leaving space for scrollbar
}

The problem with the exception at exit is actually discussed in Access violation on closing Window when using Viewport - the problem is a double free; note that in MainComponent.h, the myViewport and myGSliders properties are declared as objects:

class MainContentComponent   : public Component
{
public:
    //==============================================================================
    MainContentComponent();
    ~MainContentComponent();

    void paint (Graphics&) override;
    void resized() override;

private:
    //==============================================================================
    MyGuiSliders myGSliders;
    Viewport myViewport;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};

This crudely means that “C++” will take of their destruction, we do not have to manually delete them. However, the .setViewedComponent method has a deleteComponentWhenNoLongerNeeded argument which is by default true, and as such it will attempt to delete the viewed component at application exit, even if it has been already deleted. To prevent that, one must explicitly set false and then the unmap exception disappears.

As far as the sizing goes, first, I noticed that unless I call resized() in the constructor, something was weird with the scrollbars at very first display (i.e. I got horizontal instead of vertical scrollbar); then after a window resize, this is corrected. (something similar is mentioned in How to Use a Viewport)

Then, in the resized() menthod, I have to change the sizes of both myGSliders and myViewport, and myGSliders needs to be resized twice. First, I do:

myGSliders.setSize(getWidth(), myGSliders.getHeight());

In principle, I just need to set the width of myGSliders, not its height - however, there is no SetWidth method of JUCE Component, so I must do a .setSize using getWidth(), which would get the size of the parent (the window), and setting the height to the current height of myGSliders so it doesn’t change.

With the myGSliders sized, I can size the Viewport:

myViewport.setBounds(0, 0, proportionOfWidth(1.0f), 76);

… noting that instead of using .setBoundsRelative, which forces me to use relative percentages for all parameters, I use .setBounds which forces me to use pixels - however, there is a proportionOfWidth function which accepts a float/percent, and returns pixels (In respect to parent width, I guess).

Since now the total size of the viewport is set, we can now use the .getMaximumVisibleWidth method to find out (implicitly) what is the width of the vertical scrollbar that needs to be taken into account - or rather, what is the remaining width of the viewport - and set the myGSliders width to it:

myGSliders.setSize(myViewport.getMaximumVisibleWidth(), myGSliders.getHeight());

Without this, myGSliders is set at the width of the window, and since the vertical scrollbar is also shown (taking up a bit of the window), the engine thinks that a portion is missing, and insists on showing a horizontal scrollbar (while the vertical does not have its thumb element rendered).

And finally, the layout is as intended:

… and it scales with window resize (inner sliders too).