JUCE 7 also repaints the complete area when it’s partially covered by opaque components, it’s just that before it does so it clips all regions in the context covered by those opaque components, so it won’t be displayed (but the work is done).
To the best of my knowledge this was never meant to be a “feature” it was just an implementation detail. The idea was that it would clip all the regions covered by opaque components, if as a result of that, the clip region became empty, it could skip painting the component, this was the feature.
In other words clipping the component is normally only a time saver if it results in the context being entirely clipped such that the paint call can be avoided altogether.
Looking at the JUCE 7 docs it said this…
Components that always paint all of their contents with solid colour and
thus completely cover any components behind them should use this method
to tell the repaint system that they are opaque.
This information is used to optimise drawing, because it means that
objects underneath opaque windows don’t need to be painted.
There’s no mention of clipping other components.
Regardless I tried reintroducing clipping and at least in my tests it is noticeably more expensive. For example in the software renderer I have tests that increase a single paint call by as much as 27ms on a modern MacBook Pro!
Given that we’re talking about an un-documented observable side effect, I’m not sure I could justify re-introducing the clipping behaviour you’re relying on in JUCE 7. It seems it wouldn’t be worth it for all the customers who are using opaque components as documented.
What you are asking for sounds like a different feature altogether. I have some ideas of how we could offer this if we ever were to add support for it (for that it would help to have a clearer picture of when this is useful).
However, for now how about something like this? It should clip the context much like JUCE 7 does without the need to rely on any undocumented behaviour.
class PortalComponent : public juce::Component {};
class PortalRenderer
{
public:
PortalRenderer (juce::Component& c)
: attachedComponent (&c)
{
attachedComponent->setCachedComponentImage (std::make_unique<Impl> (this).release());
}
~PortalRenderer()
{
attachedComponent->setCachedComponentImage (nullptr);
}
private:
class Impl : public juce::CachedComponentImage
{
public:
Impl (PortalRenderer* r) : renderer (r) {}
void paint (juce::Graphics& g) final
{
openPortals (*renderer->attachedComponent, g);
renderer->attachedComponent->paintEntireComponent (g, false);
}
bool invalidateAll() final { return true; }
bool invalidate (const juce::Rectangle<int>&) final { return true; }
void releaseResources() final {}
private:
static void openPortals (const juce::Component& component, juce::Graphics& g)
{
for (const auto* child : component.getChildren())
{
if (dynamic_cast<const PortalComponent*> (child))
g.excludeClipRegion (component.getLocalArea (child, child->getLocalBounds()));
openPortals (*child, g);
}
}
PortalRenderer* renderer;
};
juce::Component* attachedComponent;
};
class ColourComponent : public juce::Component
{
public:
ColourComponent (juce::Colour c) : colour (c) {}
void paint (juce::Graphics& g) { g.fillAll (colour); };
private:
juce::Colour colour;
};
class MainComponent : public juce::Component, public juce::OpenGLRenderer
{
public:
ColourComponent background { juce::Colours::green };
PortalRenderer portalRenderer { background };
ColourComponent filled { juce::Colours::yellow };
PortalComponent portal;
MainComponent()
{
setSize (600, 400);
glContext.setRenderer (this);
glContext.setContinuousRepainting (true);
glContext.attachTo (*this);
addAndMakeVisible (&background);
background.addAndMakeVisible (&filled);
background.addAndMakeVisible (&portal);
}
~MainComponent() override {}
//==============================================================================
void paint (juce::Graphics& g) override
{
g.fillAll (juce::Colours::blue);
}
void resized() override
{
background.setBounds (getLocalBounds());
filled.setBounds (100, 100, 100, 100);
portal.setBounds (300, 100, 100, 100);
};
void newOpenGLContextCreated() override {}
void renderOpenGL() override { juce::OpenGLHelpers::clear (juce::Colours::blue); }
void openGLContextClosing() override {}
juce::OpenGLContext glContext;
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};