Lay out your components with a simple yet powerful technique that will produce elegant code with fewer bugs. The technique involves subdividing the component rectangle several times, in different ways, to fill the entire component with your content.
Level: Intermediate
Platforms: Windows , macOS , Linux , iOS , Android
Classes: Rectangle, TextButton, Colours
Download the demo project for this tutorial here: PIP | ZIP . Unzip the project and open the first header file in the Projucer.
If you need help with this step, see Tutorial: Projucer Part 1: Getting started with the Projucer.
The demo project uses a small number of button components and lays them out within the parent component. In this example we're just using buttons as placeholders, but they could be any type of JUCE component. When you build and run the application from within your IDE, the main window should look similar to the following screenshot.
In this simple application, we have a number of sections in our main window:
These are added in the MainContentComponent
constructor (see Tutorial: Parent and child components and Tutorial: Colours in JUCE):
The actual laying out is commonly done by overriding Component::resized().
Traditionally, you would lay out your component by calculating the various positions and sizes, being careful that these always sum to the correct total size. Even laying out the coloured buttons in the the main part of our window becomes tedious, and it is easy to make mistakes. To lay out four equally-sized buttons we might do something like this:
(I proved my own point when writing this tutorial by getting the final orangeContent
component in the wrong place twice!)
At the very least the calculations are time-consuming when you can be focusing your coding efforts on more important things! The Rectangle class provides some simple yet powerful features for making the job of laying out components more flexible, and in some ways easier, once you are familiar with the technique. This involves subdividing the main rectangle into smaller and smaller sub-rectangles.
Laying out your component by subdividing the main rectangle into smaller and smaller parts might seem equivalent to the traditional method. But there are a number of benefits:
The code for the MainContentComponent::resized()
function in the demo application looks like this:
Let's look in detail at the first few lines of this function. First we get the local bounds of the component we are laying out, using the Component::getLocalBounds() function. This always returns a rectangle that is at position (0, 0) with the same width and height as the component:
This is the rectangle that we are going to subdivide in order to lay out the child components. Our first subdivision is to lay out the header:
Here we take the rectangle that represents the whole component and effectively create two rectangles. The Rectangle::removeFromTop() function returns a rectangle that is at the position of the original rectangle, the same width, but only the height requested by the argument. In this case we ask for a rectangle that is 36 pixels high. The other thing that this function does is that it modifies the original rectangle removing the rectangle that we just returned. Essentially, it slices the rectangle at 36 pixels from the top, returns the upper rectangle and modifies the original rectangle to be equal to the lower rectangle.
On its own, it would look like this:
The second subdivision is to lay out the footer:
The Rectangle::removeFromBottom() function does the same as the Rectangle::removeFromTop() function, except it removes a rectangle from the bottom of the main rectangle and retains the upper rectangle. At this point, our component looks like this:
Finally we end up with the fully laid out component.
As mentioned earlier, it is really easy to reorder items using this technique. For example, we could move the orange content at the top simply by listing it first in the resized()
function [1] :
This looks like this:
We can't move the sidebar to the right-hand side simply by reordering items, but it is just matter of using the Rectangle::removeFromRight() function rather than Rectangle::removeFromLeft() [2] :
This now looks like this:
Another thing that we get for free with this approach is that resizing often "just works". Here is the component made wider but less high:
If we want some or all of the layout to be proportional, then that's easy to factor into our code. For example, we might want the sidebar to always be a quarter of the total width:
If you try this, then you will find that there is a lower useful limit. This is easy to incorporate in this approach too. Try this instead, which sets the sidebar width to a quarter of the total width, but makes 80 pixels the lower limit:
orangeContent
, limeContent
, grapefruitContent
, and lemonContent
components. Make them fill the entire remaining width. In the examples up to this point we have continued to subdivide the remaining rectangle to position the next component in our sequence. There are some cases where you will need to store one of the sub-rectangles and subdivide that instead.
For example, to place items in a list within the sidebar in our example, we would need to store the sidebar rectangle temporarily, then subdivide that. To illustrate this, add three more components to the demo project [3] , [4] , and [5] :
Then configure them in the constructor, while removing the text from the sidebar button [7] :
Finally, change the resized()
function to the following:
Notice also the use of the Rectangle::reduced() function which insets the edges of the rectangle, effectively placing the rectangle within a margin. Build and run the application and it should now look like this.
In this tutorial we have explored the use of a particular set of functions within the Rectangle class for subdividing rectangles. In particular, we have seen that using this technique for laying out components. We can: