BR: Viewport scrollbar go crazy if the viewed component changes size

Viewed component gets smaller, then start dragging the scrollbar and the position goes crazy. Scroll mode must be set to all setScrollOnDragMode ( juce::Viewport::ScrollOnDragMode::all );

Scroll position is being set both by by scrollbar listener and Viewport::DragToScrollListener and they get out of sync. Not sure why it ever works, the scrollbars are moving opposite direction the viewport content moves, so they should always be ignored.

Fix here: reFX: Fix scroll bars jumping · reFX/JUCE@efa7b1f · GitHub

This might not be a complete solution. The root of the issue is Viewport::DragToScrollListener has a global mouse listener that it isn’t cleaning up when it should. It’s created on mouseDown and removed on mouseUp. However I have a close button that deletes itself on mouseUp and then resizes the viewport contents. This mouseUp then never makes it to the global mouse listener in the DragToScrollListener. So the global mouse listener is still there and catches scrollbar messages is shouldn’t.

1 Like

I’ve created minimal application to reproduce:

I think the issue is here:

void Component::internalMouseUp (MouseInputSource source, const PointerState& relativePointerState, Time time, const ModifierKeys oldModifiers)
{
    if (flags.mouseDownWasBlocked && isCurrentlyBlockedByAnotherModalComponent())
        return;

    BailOutChecker checker (this);

    if (flags.repaintOnMouseActivityFlag)
        repaint();

    const auto me = makeMouseEvent (source,
                                    relativePointerState,
                                    oldModifiers,
                                    this,
                                    this,
                                    time,
                                    getLocalPoint (nullptr, source.getLastMouseDownPosition()),
                                    source.getLastMouseDownTime(),
                                    source.getNumberOfMultipleClicks(),
                                    source.isLongPressOrDrag());
    mouseUp (me);

    if (checker.shouldBailOut())
        return;

    auto& desktop = Desktop::getInstance();
    desktop.getMouseListeners().callChecked (checker, [&] (MouseListener& l) { l.mouseUp (me); });

    MouseListenerList::template sendMouseEvent<const MouseEvent&> (*this, checker, &MouseListener::mouseUp, me);

If a Component gets deleting in a mouse event, then the global mouse listener doesn’t get called. This doesn’t seem correct because the event did happen. Instead there should be a mouse event with a null component. I’m not sure that’s allowed however, and it could break a lot of code if mouse events started happening with null components.

Since the mouseUp never happens, Viewport::DragToScrollListener needs to be reworked to remove the global mouse listener somewhere other than mouseUp().

1 Like

Thanks for reporting and thank you for your thorough analysis of the problem. I can reproduce the bug thanks to your sample code.

Can you check if the following patch fixes your issue in your product? For me, it seems to fix it in your code example.

fix_crazy_scrollbar_jumping_when_component_is_deleted.patch (19.4 KB)

The patch continues to send the mouse events but replaces the originating component with the component’s nearest non-null parent. Then, at least, we can ensure that the component parameter is not null.

Even if this works, this may have unforeseen consequences so more testing is needed and we may abandon this solution altogether.

1 Like

I can confirm that your patch resolves the issue.

2 Likes

OK the issue is now fixed with commit f2de0f1 on the develop branch.

Once again, thank you for the detailed investigation.