Solving a Draggable TabbedBarButton glitch?

I’m working on a TabBar that has draggable buttons.
There’s a forum post here:

@jules explains how to get it basically functional, which I have done:

  • ExtendedTabBarButton own ComponentDragger instances.
  • ExtendedTabButtonBar is both a DragAndDropTarget and DragAndDropContainer

I am dealing with this graphical issue when dragging the tabs (GIF):
ezgif-1-6ca6b4b11e

I’m trying to get the following drag behavior (excluding the animated position change) (GIF):
ezgif-3-65bc7bfff1

Is there a better/different way to solve this graphical glitch than the one detailed below?

Here is how everything is being put together, as well why I think this bug is happening:
in my ExtendedTabButtonBar::dragItemMoved function, i’m doing this:

/* 
idx is the index of the tab being dragged in the list of tabs owned 
by the TabButtonBar.
nextTab is the tab to the right of the tab being dragged. 
previousTab is the tab to the left
*/
            if( centerX > nextTab->getX() )
            {
                DBG( "switching " << tabBeingDragged->getName() << " with nextTab: " << nextTab->getName() );
                moveTab(idx, nextTabIndex);
            }
            else if( centerX < previousTab->getRight() )
            {
                DBG( "switching " << tabBeingDragged->getName() << " with previousTab: " << previousTab->getName() );
                moveTab(idx, previousTabIndex);
            }

the tabBeingDragged has a ComponentDragger member controlling its position while being dragged.
the TabbedButtonBar::moveTab(currentIndex, newIndex) call is causing the dragged tab’s position to be snapped to a new position, inside of TabbedButtonBar::updateTabPositions():

             auto newBounds = isVertical() ? Rectangle<int> (0, pos, getWidth(), bestLength)
                                           : Rectangle<int> (pos, 0, bestLength, getHeight());

             if (animate)
             {
                 animator.animateComponent (tb, newBounds, 1.0f, 200, false, 3.0, 0.0);
             }
             else
             {
                 animator.cancelAnimation (tb, false);
                 tb->setBounds (newBounds);
             }

I have determined that this call to setBounds() for the tab being dragged is the culprit because it is overriding the setBounds() being called inside of the tab’s mouseDrag operation:

    void mouseDrag (const juce::MouseEvent& e) override
    {
        dragger.dragComponent (this, e, constrainer.get());
    }

specifically:

void ComponentDragger::dragComponent (Component* const componentToDrag, const MouseEvent& e,
                                      ComponentBoundsConstrainer* const constrainer)
{
    jassert (componentToDrag != nullptr);
    jassert (e.mods.isAnyMouseButtonDown()); // The event has to be a drag event!

    if (componentToDrag != nullptr)
    {
        auto bounds = componentToDrag->getBounds();

        // If the component is a window, multiple mouse events can get queued while it's in the same position,
        // so their coordinates become wrong after the first one moves the window, so in that case, we'll use
        // the current mouse position instead of the one that the event contains...
        if (componentToDrag->isOnDesktop())
            bounds += componentToDrag->getLocalPoint (nullptr, e.source.getScreenPosition()).roundToInt() - mouseDownWithinTarget;
        else
            bounds += e.getEventRelativeTo (componentToDrag).getPosition() - mouseDownWithinTarget;

        if (constrainer != nullptr)
            constrainer->setBoundsForComponent (componentToDrag, bounds, false, false, false, false);
        else
            componentToDrag->setBounds (bounds);
    }
}

The only solution i’ve been able to think of is to somehow block the setBounds() call inside of TabbedButtonBar::updateTabPositions()
However, updateTabPositions() is private, and setBounds() cannot be overridden, so that leaves me with duplicating the entire TabbedButtonBar class and adding the following check:

             auto newBounds = isVertical() ? Rectangle<int> (0, pos, getWidth(), bestLength)
                                           : Rectangle<int> (pos, 0, bestLength, getHeight());

             if (animate)
             {
                 animator.animateComponent (tb, newBounds, 1.0f, 200, false, 3.0, 0.0);
             }
             else
             {
                 animator.cancelAnimation (tb, false);
                 //check if being dragged
                 if( auto etbb = dynamic_cast<ExtendedTabBarButton*>(tb) )
                 {
                     if( ! etbb->isBeingDragged )
                     {
                         etbb->setBounds (newBounds);
                     }
                 }
                 else
                 {
                     tb->setBounds (newBounds);
                 }
             }

That is a lot of code duplication which is not guaranteed to solve the problem.

Any ideas?

Without looking throught the code, I think you need to find a way to avoid triggering a swap for the area where the order is ambiguous.
The ambiguous area is the overlaping rectangle of the bigger rectangle on both possible positions.