Debugging Tool: Paint Rectangle Around Components


#1

Hi Jules,

I have a request for adding a minor debugging tool that could prove useful for those who don’t use the jucer for designing interfaces (aka placing widgets on juce::Components): a means of viewing of boxes surrounding components, and accompanying macro for enabling/disabling it.

This gives a way of seeing the relation between widgets (undesirable offsets?), and seeing their possible hit-test areas. (That’s what I use it for. Would be nice if it were part of the library somehow…)

What I do, to check things out fairly quickly when necessary, is add a small function to juce_Component.cpp, and use this function on parent components and their children:

void paintBoundingBoxAroundComponent (juce::Graphics& g, juce::Component& component)
{
    g.setColour (juce::Colours::red);
    g.drawRect (component.getX(), component.getY(), component.getWidth(), component.getHeight());
}
void Component::paintComponentAndChildren (Graphics& g)
{
    const Rectangle<int> clipBounds (g.getClipBounds());

    ///////
    paintBoundingBoxAroundComponent (g, *this); //HERE (and again below)
    ///////

    if (flags.dontClipGraphicsFlag)
    {
        paint (g);
    }
    else
    {
        g.saveState();
        ComponentHelpers::clipObscuredRegions (*this, g, clipBounds, Point<int>());

        if (! g.isClipEmpty())
            paint (g);

        g.restoreState();
    }

    for (int i = 0; i < childComponentList.size(); ++i)
    {
        Component& child = *childComponentList.getUnchecked (i);

        if (child.isVisible())
        {
            if (child.affineTransform != nullptr)
            {
                g.saveState();
                g.addTransform (*child.affineTransform);

                if ((child.flags.dontClipGraphicsFlag && ! g.isClipEmpty()) || g.reduceClipRegion (child.getBounds()))
                    child.paintWithinParentContext (g);

                g.restoreState();
            }
            else if (clipBounds.intersects (child.getBounds()))
            {
                g.saveState();

                if (child.flags.dontClipGraphicsFlag)
                {
                    child.paintWithinParentContext (g);
                }
                else if (g.reduceClipRegion (child.getBounds()))
                {
                    bool nothingClipped = true;

                    for (int j = i + 1; j < childComponentList.size(); ++j)
                    {
                        const Component& sibling = *childComponentList.getUnchecked (j);

                        if (sibling.flags.opaqueFlag && sibling.isVisible() && sibling.affineTransform == nullptr)
                        {
                            nothingClipped = false;
                            g.excludeClipRegion (sibling.getBounds());
                        }
                    }

                    if (nothingClipped || ! g.isClipEmpty())
                        child.paintWithinParentContext (g);
                }

                g.restoreState();
            }
        }

        ///////
        paintBoundingBoxAroundComponent (g, child); //HERE (don't care if child is marked as visible or not)
        ///////
    }

    g.saveState();
    paintOverChildren (g);
    g.restoreState();
}

There are probably other uses for this (drawing rectangles around drawn paths from Graphics, or text and glyphs?)… but just gave the most basic application.


#2

Yeah, that could be quite handy. I guess I could enable it with a macro, like JUCE_ENABLE_REPAINT_DEBUGGING.

But why did you add the call to paintBoundingBoxAroundComponent (g, *this)? I can’t really see why you’d need that one.


#3

This doesn’t need to be part of the library, you don’t have to add it to juce::Component, and it doesn’t need a macro to turn it on or off.

Instead, you can write a simple Component which is transparent to mouse clicks, and implements paintOverChildren(). In this function. just recursively iterate over all child components and do your framing / hiliting in there.

Finally instead of using a macro to turn this on or off you can write a quick convenience function that takes an existing Component pointer and then creates and adds this debugging component on top of it. So whenever you want to show component bounding rects you add one line of code - if you want to control this with your own application specific macro so be it.


#4

You’re right - it’s not necessary. I’m not even sure why I put it there anymore! After doing copy-paste from a text file a couple times I stopped thinking about it.

That’s an interesting approach TheVinn; makes things really flexible. The only way to have that technique apply to an application as a whole would be to have such a component be the “main” component that the DocumentWindow sets.


#5

True, but this could all be handled in the function that creates the debugging component. It would look at the parent Component and determine if it is a subclass of DocumentWindow via dynamic_cast, and then call setContentComponent() or something suitable after storing the previous content component.

This assumes that your interface is not dependent on hierarchy (which it shouldn’t be).