Nasty JUCE bug?


#1

The following program demonstrates the bug:

What the program does is: when you press the mouse button, a new little blue “TestComponent” is added to the main component and as long as the mouse button is held the freshly added component follows the mouse position until the mouse button is released and then it stays there.

There’s also another (semi-transparent) “TestComponent” following the mouse pointer all the time. Its position gets updated in the mouseMove() function of the MainComponent. That component does not react to mouse clicks, hence mouse clicks were disabled for component with setInterceptsMouseClicks(false, false).

If you remove the one single line inside mouseMove() that updates the semi-transparent Component’s position, the program will work as it should. If you let it in, sometimes it happens that the blue TestComponent you add will stop following the mouse right from the start, although it should as long as the mouse button stays pressed! [Since the mouse button was pressed ON the MainComponent, all subsequent mouse movement should be routed to the MainComponent’s mouseDrag().]

So it seems like somehow JUCE has problems with the fact that this semi-transparent TestComponent that follows the mouse gets its position updated in mouseMove() or with the fact that a new Component is added while the mouse button is down!?

No idea.

Here’s the code (file “MainComponent.h”):

#ifndef _MAINCOMPONENT_H_
#define _MAINCOMPONENT_H_

#include "juce.h"

//==============================================================================
class TestComponent:public Component
{
public:
   TestComponent(Colour c_)
   {
      c=c_;
   }
private:
   void paint(Graphics &g)
   {
      g.fillAll(c);
   }
   Colour c;
};
//==============================================================================
class MainComponent  : public Component
{
public:
 
    MainComponent ()
    {
      addAndMakeVisible(dummyComp=new TestComponent(Colours::green.withAlpha(0.3f)));
      dummyComp->setBounds(0,0,20,20);
      dummyComp->setInterceptsMouseClicks(false, false);
   
      lastAddedComponent=0;
    }

    ~MainComponent ()
    {
      deleteAllChildren();
    }

    void mouseDown(const MouseEvent &e)
   {
      TestComponent *t=new TestComponent(Colours::blue);
      t->setBounds(e.x,e.y,10,10);
      addAndMakeVisible(t);

      lastAddedComponent=t;
   }

   void mouseMove(const MouseEvent &e)
   {
      dummyComp->setTopLeftPosition(e.x, e.y); // <--- REMOVE THIS LINE AND THE BUG WILL NOT OCCUR ANYMORE!!
   }

   void mouseDrag(const MouseEvent &e)
   {
      if (lastAddedComponent==0) return;
      lastAddedComponent->setTopLeftPosition(e.x, e.y);
   }

private:
   TestComponent *lastAddedComponent, *dummyComp;
};

#endif//_MAINCOMPONENT_H_

#2

I have a strong suspicion that the function sendFakeMouseMove() in setBounds() is the culprit. It gets called due to the call to setTopLeftPosition() in mouseMove(). And because it works asynchronously (arghh) it will maybe reach its destination even after there’s a new component above it!?

Why is sendFakeMouseMove() called anyway when the semi-transparent component should not intercept mouse clicks (because I used setInterceptsMouseClicks(false,false) on that component) ? Shouldn’t there be a check?


#3

Further investigation led to this conclusion:

It seems that, when pressing the mouse button on a component, and adding some new component somewhere under (!) the mouse cursor while the button is still down causes problems: The component the mouse was pressed on will not receive any further mouse messages although it should be the only component mouse messages are routed to until the mouse button is released again!

In my eyes, this is a (rather heavy) bug, because JUCE’s current behaviour goes against its own specs which are (for Component::mouseDown()): “Once a button is held down, the mouseDrag method will be called when the mouse moves, until the button is released.” . :?


#4

Yes, I think you might be right there… Sending a fake mouse move is a perfectly valid thing to do, but I think the problem is that it shouldn’t actually be allowed to reach its destination.

How about this as a possible fix:

void ComponentPeer::handleMessage (const Message& message) { if (message.intParameter1 == fakeMouseMoveMessage) { if (! ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown()) handleMouseMove (message.intParameter2, message.intParameter3, Time::currentTimeMillis()); } }


#5

Thanks fo the fix, seems to be alright now. Gonna do some more testing…
…I nearly went mad figuring out what was going on in my Piano Roll editor until I realized it wasn’t my fault…