Bug: Scrollbars not reappearing in Viewport corner case


#1

My apologies if this is not a bug, I am still new to Juce.

I believe that in juce_Viewport.cpp, everywhere that the horizontal or vertical scrollbar is being hidden, the range limits and current range should also be set to 0, 0. Specifically, when we call either of, or both of:

horizontalScrollBar->setVisible (false); verticalScrollBar->setVisible (false);

We should also perform

horizontalScrollBar->setRangeLimits (0.0, 0.0); horizontalScrollBar->setCurrentRange (0.0, 0.0); verticalScrollBar->setRangeLimits (0.0, 0.0); verticalScrollBar->setCurrentRange (0.0, 0.0);

Here is the reason:

If you have a non-empty juce_Viewport (one where the contentComponent has a non-empty bounding rectangle), and scrollbars are needed, they will be shown as necessary and the limits and range set appropriately.

However, if the contentComponent of the viewport changes to either fit exactly on screen, or become empty (in my case I am setting it empty), the scrollbars are hidden. This happens in juce_Viewport.cpp, updateVisibleRegion():

        if ((contentComp->getWidth() > 0) && showHScrollbar
             && getHeight() > getScrollBarThickness())
        {
            horizontalScrollBar->setRangeLimits (0.0, contentComp->getWidth());
            horizontalScrollBar->setCurrentRange (newVX, getMaximumVisibleWidth());
            horizontalScrollBar->setSingleStepSize (singleStepX);
        }
        else
        {
            horizontalScrollBar->setVisible (false);
        }

        if ((contentComp->getHeight() > 0) && showVScrollbar
             && getWidth() > getScrollBarThickness())
        {
            verticalScrollBar->setRangeLimits (0.0, contentComp->getHeight());
            verticalScrollBar->setCurrentRange (newVY, getMaximumVisibleHeight());
            verticalScrollBar->setSingleStepSize (singleStepY);
        }
        else
        {
            verticalScrollBar->setVisible (false);
        }

While hiding the scrollbars is the correct behavior (and it works properly), the scrollbars are still left with the old range limit and current range.

Now imagine that the contentComponent of the juce_Viewport again changes size, to exactly the size it had before the scrollbars were hidden. The juce_Viewport will update the scrollbar range limits and current range in updateVisibleRegion().

However, since the scrollbars already had the same range limits and current range, updateThumbPosition() never gets called. updateThumbPosition() is responsible for showing and hiding the scrollbars appropriately depending on their settings. This is where we skip the call to updateThumbPosition(), in juce_Scrollbar.cpp:

void ScrollBar::setCurrentRange (double newStart,
                                 double newSize) throw()
{
    newSize  = jlimit (0.0, maximum - minimum, newSize);
    newStart = jlimit (minimum, maximum - newSize, newStart);

    if (rangeStart != newStart
         || rangeSize != newSize)
    {
        rangeStart = newStart;
        rangeSize = newSize;

        updateThumbPosition();
        triggerAsyncUpdate();
    }
}

Since rangeStart==newStart and rangeSize==newSize, because as mentioned earlier when the scrollbars are hidden the values are not changed, the call to updateThumbPosition() is skipped.

Again my apologies if this is not an actual bug and I am just mis-using the library, I am new to Juce.


#2

That’s a good point though I’m a little suprised you managed to find a situation where you actually got it to happen - I’ve never noticed it myself!

It does need sorting out, though I’m not sure if setting the range to 0 is quite right - I can’t think of a reason why the range couldn’t be kept as whatever it would be if the scrollbars were visible… Maybe like this:

[code] horizontalScrollBar->setRangeLimits (0.0, contentComp->getWidth());
horizontalScrollBar->setCurrentRange (newVX, getMaximumVisibleWidth());
horizontalScrollBar->setSingleStepSize (singleStepX);

    if (! (contentComp->getWidth() > 0 && showHScrollbar && getHeight() > getScrollBarThickness()))
        horizontalScrollBar->setVisible (false);

    verticalScrollBar->setRangeLimits (0.0, contentComp->getHeight());
    verticalScrollBar->setCurrentRange (newVY, getMaximumVisibleHeight());
    verticalScrollBar->setSingleStepSize (singleStepY);

    if (! (contentComp->getHeight() > 0 && showVScrollbar && getWidth() > getScrollBarThickness()))
        verticalScrollBar->setVisible (false);

[/code]

(I haven’t tried this yet, BTW)


#3

I admit it is a bit obscure. Think of a treeview with two items and an associated adjacent list. Click on item 1 in the treeview, causes the list to display some large number of items. Click on tree view item 2, and the list is empty. Now click back to item 1 in the treeview, the associated list will exhibit the problem described in the op.


#4

Yes, I guess so. Did the code I suggested fix it for you?


#5

It sure did!