Any way to clip the entire graphics region of a component and its children?

For example, I have a custom ListBox implementation that I want to have rounded corners. It’s easy enough to draw the background of this component with rounded borders, but there’s no way I can find to make the child ListBox cells and accompanying scrollbar obey this roundness as well. Ideally I’m looking for something like Graphics::reduceClipRegion(juce::Path) but for an entire component and its children to obey. i.e. Component::reduceClipRegion

I find this to be a common problem with Juce and am often able to hack something like this by manually clipping a component’s paint routine and any children that might lie in the perceived clip region. But that’s not an option here given that all of the cells are a moving target.

1 Like

I’m very interested in this issue. I thought about it a while ago and I was wondering if you could use a “cover” component for this,which is transparent except for the corners?

1 Like

I’ve done this before as well and it will work fine in cases that have a solid or simple background. My particular case also has a drop shadow (another thing Juce Graphics has issues with) behind my component which makes this solution kind of impossible. It also just feels unfortunate to have to hack a solution like this just to implement something as common as rounded corners.

If you have a simple background color, this function draws the rounded corners over your child component. It’s a hack, but it works.

void YourComponent::paintOverChildren(juce::Graphics &g)
{
    auto backgroundColour = juce::Colour(0xffff0000);
    auto size = 20;
    auto area = getLocalBounds ();
    g.setColour(backgroundColour);
    juce::Path topLeft;

    topLeft.addArc (area.getX(),area.getY(), size, size
              , juce::MathConstants<float>::pi * 1.5
              , juce::MathConstants<float>::pi * 2
              , true);
    topLeft.lineTo (area.getX(),area.getY());
    topLeft.closeSubPath ();
    g.fillPath (topLeft);

    juce::Path topRight;
    topRight.addArc (
                area.getWidth () - size
              , area.getY ()
              , size, size
              , juce::MathConstants<float>::pi * 2
              , juce::MathConstants<float>::pi * 2.5
              , true);
    topRight.lineTo (area.getWidth (), area.getY ());
    topRight.closeSubPath ();
    g.fillPath (topRight);

    juce::Path bottomRight;
    bottomRight.addArc (area.getWidth () - size, area.getHeight () - size, size, size
              , juce::MathConstants<float>::pi * 2.5
              , juce::MathConstants<float>::pi * 3
              , true);
    bottomRight.lineTo (area.getWidth () , area.getHeight () );
    bottomRight.closeSubPath ();
    g.fillPath (bottomRight);

    juce::Path bottomLeft;
    bottomLeft.addArc (area.getX (), area.getHeight () - size, size, size
              , juce::MathConstants<float>::pi * 3
              , juce::MathConstants<float>::pi * 3.5
              , true);
    bottomLeft.lineTo (area.getX (), area.getHeight ());
    bottomLeft.closeSubPath ();
    g.fillPath (bottomLeft);
}

Nice! Even simpler would be:

Path fakeRoundedCorners;
juce::Rectangle<float> bounds = {100, 100, 200, 200}; //your component's bounds

const float cornerSize = 10.f; //desired corner size
fakeRoundedCorners.addRectangle(bounds); //What you start with
fakeRoundedCorners.setUsingNonZeroWinding(false); //The secret sauce
fakeRoundedCorners.addRoundedRectangle(bounds, cornerSize); //subtract this shape

g.setColour(Colours::green);
g.fillPath(fakeRoundedCorners);

This gives you:

But unfortunately it’s not something I can use so we just opted to get rid of the rounded corners until such time that juce can accommodate something like that.

2 Likes