Disabled components lead to stack overflow


#1

I have a tabbed component which has three group components as its only children. At some point, I disable these group components. If I click on one of these groups while it’s disabled, I get a stack overflow.

The problem is in Component::grabFocusInternal(). It tries to select a child component, but none are enabled, so it recurses into the parent to find a suitable sibling. In there, it chooses the “most interested” child, which is the tabbed component, and recurses into it to find its most interested child. None of the children are enabled, so it recurses to the parent, tab, parent, tab, … BOOM!

I can probably get around this by disabling the contents of the groups, rather than the groups themselves, but I thought I’d point out this issue.


#2

ahh… nice bit of bug-hunting there, Randy!

Here’s a fix:

void Component::grabFocusInternal (FocusChangeType cause, const bool canTryParent)
{
    if (isShowing() && isEnabled())
    {
        if (wantsFocusFlag)
        {
            takeKeyboardFocus (cause);
        }
        else
        {
            if (isParentOf (currentlyFocusedComponent)
                 && currentlyFocusedComponent->isShowing())
            {
                // do nothing if the focused component is actually a child of ours..
            }
            else
            {
                // look for the most interested of our child components..
                uint32 mostInterest = 0x7fffffff;
                Component* mostInterested = 0;

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

                    if (child->isVisible() && child->isEnabled())
                    {
                        const uint32 howMuch = child->getFocusOrder();

                        if (howMuch < mostInterest)
                        {
                            mostInterest = howMuch;
                            mostInterested = child;
                        }
                    }
                }

                if (mostInterested != 0)
                {
                    mostInterested->grabFocusInternal (cause, false);
                }
                else if (canTryParent && parentComponent_ != 0)
                {
                    // if no children want it and we're allowed to try our parent comp, 
                    // then pass up to parent, which will try our siblings.
                    parentComponent_->grabFocusInternal (cause, true);
                }
            }
        }
    }
}

and you’ll need to change the prototype to

    void grabFocusInternal (FocusChangeType cause, const bool canTryParent = true);

Hopefully that’ll work - but the logic in there’s a bit of a brain-twister, so let me know if you still have problems.


#3

Thanks for the quick fix.


#4