Component::getVisibleArea() question


#1

Hello Jules,

I am trying to determine if a component is actually visible on screen, so I'm using this method called with the 'includeSiblings' parameter set to 'true'.

But when 'includeSiblings' is 'true', the subtractObscuredRegions() function is called starting from the component's top level container. This means that if the component's parent is opaque, its bounds are subtracted from the visible area, therefore always leading to an empty area.

Is this the intended behaviour?


#2

Hmm.. Good point. I think I probably meant that to be getParentComponent, not getTopLevelComponent. Thanks, I'll update it!


#3

Actually, sorry about this, but I just had a deeper look at that method and it has a whole bunch of subtle edge-case problems. It was written before I added component transforms, and once you start digging into what it actually does then there are some ambiguities about what it even means by the visible area.. On reflection, I've decided to remove it completely, and maybe have a re-think about how a more straightforward method could be defined in future.

What's your actual use-case for it? Could you achieve the same thing by just calling isShowing(), or by checking whether the child is inside the parent's bounds? I've found that in my own code the only time I need to know about the visible region is in a paint call, when you can get the info from the graphics context clip region.


#4

Well, getVisibleArea() is an appropriate name for what I need :)

I need to know which part of the component is visible exactly as if I were going to paint it. isShowing() is not enough, because I can have one component that is partially o totally overlapped by a sibling.

This is the code I was using until now:

Rectangle<int>    CMyClass::getTargetVisibleArea(
    const Component*      inComponent,
    const Rectangle<int>* inHotSpotArea)    // optional reduced area inside the component
{
Rectangle<int>    area;

if(inComponent->isShowing())
    {
    RectangleList<int>    clippedAreas;

    inComponent->getVisibleArea(clippedAreas, true);

    // not so accurate, if clipping area is not rectangular, but it's OK
    area = clippedAreas.getBounds();

    if(inHotSpotArea != NULL)
        area = area.getIntersection(*inHotSpotArea);
    }

return(area);
}

 


#5

Sure, but when are you calling that? And why? Despite not knowing what you're trying to do, that feels wrong to me.

If you're calling it in a paint method then you should be using the Graphics class instead. And if you're making something happen when e.g. you drag a component so that part of it is revealed, then I'd suggest writing your own function to detect that, maybe based on knowing about the types of component that may be overlapping, and give it a name that's more specific to your app.

(And please tell me CMyClass isn't a real class name in your actual code!)


#6

I am working to an interactive help so no, I'm not calling that in a paint method. I have rewritten my code to take siblings into account, but I still need a method to determine if the component is actually visible with regard to the containment hierarchy. That is precisely the old getVisibleArea() with 'includeSiblings' set to false.

Could you reintroduce that method without the 'includeSiblings' parameter, or is this still impossible?

(Actually CMyClass is not the real name, the real one is CFooBar)


#7

The thing is, once you start thinking about this, there are an endless number of parameters for such a method - e.g. should it take into account opaque child areas? If so, should that be recursive? What should it do when there are transformations - take the largest or smallest area that they occlude? If it's being clipped to the parent's bounds, should that also be recursive, or just take the immediate parent? If siblings were included, are they only siblings of the immediate parent, or of all parents recursively? Should it take into account the top level window being off the edge of the monitor? etc etc!

Sorry, but I don't feel that I can create one method that would satisfy everyone, so I'd rather leave it for people to roll their own. Although it's difficult to write a function that correctly takes into account transformations etc, it's probably pretty trivial to write one that does what you need - you probably just need to look at the parent bounds and intersect it with the area you're interested in.


Component get visible area
#8

OK, I understand, thanks anyway, your library is always the best in its class.

For anyone interested, this is what works for me.

static Rectangle<int>    getLocalUnclippedArea(const Component& c)
{
Rectangle<int>    r(c.getLocalBounds());
Component*        parent = c.getParentComponent();

if(parent != NULL)
    {
    Rectangle<int>    pr = c.getLocalArea(parent, getLocalUnclippedArea(*parent));

    r = r.getIntersection(pr);

    if(!r.isEmpty())
        {
        RectangleList<int>  result(r);
        int                 numOfSiblings = parent->getNumChildComponents(),
                            firstInFront  = parent->getIndexOfChildComponent(&c) + 1;

        for(int i = firstInFront; i < numOfSiblings; i++)
            {
            Component*    s = parent->getChildComponent(i);

            if(s->isVisible() && s->isOpaque())
                {
                Rectangle<int>    sr = c.getLocalArea(s, s->getLocalBounds());

                result.subtract(sr);
                }
            }

        // obviously incorrect if the resulting area is not rectangular...
        r = result.getBounds();
        }
    }

return(r);
}