I’ve seen some references to the “ a Component subclass should either implement the paint method or have children components, but not both” rule? guideline? best practice? so I’ve been trying to figure out the best way to paint a component a certain colour and also add children.
My project UI is fairly simple, the specific part that’s relevant to this question is that I want to add 3 sections to the UI laid horizontally, each taking 1/3 of the window space, which will be resizable with a minimum size. I’m working on the middle section which will be a sort of “control panel”, so I want to paint it a certain colour and add buttons, however I don’t want the buttons to change size as the window is made bigger. I thought of adding a child Rectangle with set width in that section and adding buttons as children of that component.
The only way I know is to implement the paint method and use g.fillAll on the centre control panel, but that would go against that “rule”,
The code in the Parent and child components tutorial doesn’t actually follow this. Is filling the colour of a component considered an exception, or is there actually a better way? In terms of code maintenance I don’t see a problem with considering this use case an exception however there might be other implications I’m not aware of.
TIA
Where did you see that rule?
Lesson 5.05
I’ve also been checking some open source projects such as this one and it seems to follow that rule, as far as I could tell no parent component implments paint.
Also, for the time being I actually am implementing paint and setting child components in the parent, but would like to know if there’s an agreed best practice or if it’s a matter of preference.
While the advice to have a component either paint itself, or contain children is good advice, it certainly isn’t a hard rule. For your case it’s perfectly fine to do what you’re doing - it won’t cause any bugs and performance should be ok.
ADC 2025 was also the first time I’d heard this advice myself, so you’ll probably find most examples out there in the wild don’t do this (and that’s fine).
Another bit of advice you’ll see floating around the forums is to avoid premature optimizations, and I think that’s worth applying here.
However, something I do sometimes is create background components for sections, but I generally only do that if the backgrounds are more complicated than a simple fillAll() and/or they are repeated in the design. The other benefits to doing this are being able to use the setBufferedToImage() option, and it helps with the “Don’t Repeat Yourself” rule.
1 Like
Great, thanks. I didn’t expect there to be a major issue by doing it this way but being fairly new to C++ I’m in the “I don’t know what I don’t know” stage. I wrote network automation code in Python/Go for the better part of the last 12 years so I’m definitely familiar with the consequences of premature optimisations 
I don’t plan to do anything crazy with the background but I got into the habit of painting “container” components a different colour as a starting point to make sure I was at least laying them out the way I was planning to before actually filling them up with child components.
If you look at ZL Equalizer carefully, it uses a child component as the background when:
- the background is quite complex (e.g., contains analyzer grids, shadow)
- the background might get repainted periodically (e.g., it has a child component of which
repaint triggered by a callback)
If you only use g.fillAll as the background, you can just paint that in the parent component paint(). In most cases it should have a better performance, compared to a separate child background component.
1 Like
I tend to be the one pushing for this general guideline. My reasoning is that as your GUI grows in size and complexity you’ll inevitably start to use setBufferedToImage(), and if you follow the guideline you’ll encounter far fewer surprises when you do.
I’ve seen many experienced JUCE developers (myself included) get tripped up by the fact that calling setBufferedToImage() on a component will buffer the component and all it’s children. There are good reasons for it to do this but in most cases users tend to expect that the only component that will be buffered to an image is the component for which setBufferedToImage() is called on. We have some examples in the ComponentDiagnosticsDemo in the DemoRunner that demonstrate the kind of performance issues that can occur when this happens. If you compare “Expensive (buffered to image) with child” and “Expensive (buffered to image) with sibling” you should be able to see what I’m referring to.
All that being said it’s perfectly reasonable to not follow this guideline, if you’ll never need to call setBufferedToImage() you’ll never notice any difference, and if you only use fillAll() or fillRect() then you’re unlikely to want to use setBufferedToImage() as it’s not going to improve performance.
So if I understand correctly the question is really just, if you want to follow this guideline, how can you avoid implementing the paint method.
First lets imagine you have a parent and a child component and you want some background, instead of adding that background to your parents paint method, add a third component, lets call it the background component. Implement its paint method and add it as a child of the parent component (before adding the other child component), then in the parents resized method, set the background component to fill the entires area of the parent.
It might look something like this…
class ChildComponent : public juce::Component
{
public:
void paint (juce::Graphics& g) override
{
g.fillAll (juce::Colours::red);
}
};
class BackgroundComponent : public juce::Component
{
public:
void paint (juce::Graphics& g) override
{
g.fillAll (juce::Colours::green);
}
};
class ParentComponent : public juce::Component
{
public:
ParentComponent()
{
// Make sure the background is added before the child so it appears behind it
addAndMakeVisible (background);
addAndMakeVisible (child);
}
// Note that the paint method is not required here
void resized() override
{
background.setBounds (getLocalBounds());
child.setBounds (getLocalBounds.reduced (10));
}
private:
juce::ChildComponent background;
juce::ChildComponent child;
}
Hope that helps.
3 Likes
Could we not have another alternative to setBufferedToImage() that doesn’t buffer the children as well, only the exact graphics drawn in that component’s paint() method? setPaintingBufferedToImage() perhaps? Or even just a bool includeChildren = true default argument that can be turned off on the existing method.
Seems like it could save a lot of boilerplate in a lot of projects, and reduce the confusion about when to follow the guideline by making that approach redundant.
It’s possible we could do that, although I think it would be much more complicated than it first appears. One thing to consider is that a component has a paintOverChildren() method so when you are caching a component to an image you’re caching this too which will need the children drawn in order to work, or there would have to be two images, or as you’re suggesting only the paint method is cached.
Maybe an alternative, rather than extending the Component class further, is to have a CachedImage type that could be easily adapted into any paint method. There are other uses cases I think this helps with too, such as having a cached image that can be used across multiple components. Or where the component might actually need to be redrawn more often than the image itself, for example if you have a level meter you could cache the whole meter to an image and in each paint call just draw the section of the image meter required. I have put together an idea like this internally but it needs some TLC.
How would CachedImage differ from juce::Image or juce::ImageComponent OOI?
This is very helpful, thanks. It took a while but I also got a better idea of the rationale behind it.
I will probably try to get in the habit of following this guideline, as it wouldn’t be nearly the first time I think “this will probably stay a very simple minimalistic project” and 6 months later it’s a monster much bigger than I ever imagined.
The main difference (if we were to add it) is that it would handle some edge cases that are easy to miss, for example it would call setBackupEnabled (false) and handle redrawing the image in the case that the graphics device has disappeared and therefore lost the contents of the cached image. In essence if you take a look at what StandardCachedComponentImage does internally it would likely handle a lot of that. The other key difference is that rather than drawing into the image directly you would pass a function that can draw the image and the CachedImage object would choose when to call that function.
1 Like