Setting mouse position after unbounded drag

Hello.
I’d like to hide the mouse cursor while dragging a slider and show it again at its original (mouseDown) position on mouseUp().

As others have suggested (MouseInputSource::enableUnboundedMouseMovement behaviour and Locking the mouse and making it invisible when using a slider?), I enable unbounded mouse movement in mouseDown() and disable it mouseUp(). I also reset the mouse position in mouseUp() using Desktop::getInstance().getMainMouseSource().setScreenPosition().

The mouse position doesn’t seem update immediately - it takes a short moment. The result is that:

  • mouseEnter() will fire on another component if the invisible/unbounded mouse position happens to be over that component on mouseUp().
  • The cursor occasionally is made visible in its unbounded position and flickers when moving to the desired position.

Is this expected behaviour? Is there a way to set the mouse position more synchronously/immediately? I tried calling forceMouseCursorUpdate() after setting the position but this doesn’t seem to help. I’m using JUCE 7.0.10 and have the same behaviour on macOS and Windows.

Thanks!

I’ve been reimplementing this in a new project - and have come across some of this odd behaviour. Would love to know whats going on here.
this is the whole of my slider class:

class MySlider : public juce::Slider
{
public:
    MySlider() : juce::Slider()
    {
        setSliderStyle(juce::Slider::RotaryHorizontalVerticalDrag);
        setTextBoxStyle(juce::Slider::NoTextBox, true, 0, 0);
    }
    
    void mouseDown(const juce::MouseEvent &event) override
    {
        juce::Slider::mouseDown(event);
        setMouseCursor(juce::MouseCursor::NoCursor);
        event.source.enableUnboundedMouseMovement(true);
        mousePos = event.source.getScreenPosition();
    }
    
    void mouseUp(const juce::MouseEvent &event) override
    {
        juce::Slider::mouseUp(event);
        juce::MouseInputSource src = juce::MouseInputSource(event.source);
        src.enableUnboundedMouseMovement(false);
        src.setScreenPosition(mousePos);
        setMouseCursor(juce::MouseCursor::NormalCursor);
    }
private:
    juce::Point<float> mousePos;

};

Thanks dariop. This is very similar to my implementation.

Is anyone from JUCE able to confirm this issue and suggest a possible fix?

Thank you for reporting. We are tracking this issue, but we still have to investigate it, before we can say anything concrete.

1 Like

Any update on this?

I’ve heard nothing more. I normally use JUCE 7 but it appears JUCE 8 has the same behaviour.

On a mac, one related issue could be in juce_Windowing_mac.mm: setRawMousePosition() has this comment:

Mouse enter and exit events seem to be always generated as a consequence of programmatically moving the mouse. However, when the mouse stays within the same peer no mouse move event is generated, and we lose track of the correct Component under the mouse. Hence, we need to generate this missing event here.

It seems that the code following the comment never executes. It’s guarded by findPeerContainingPoint() which always returns nullptr, even though the peer exists and seems to contain the point.

In the unbounded drag situation it seems that enter and exit aren’t always generated. For example, I start an unbounded drag on component A, and while the mouse cursor is invisible, I happen to release on component B, and then A’s mouseUp() puts the mouse position back where it started. The sequence of events is mouseUp() on A, mouseExit() on A, then mouseEnter() on B. The cursor flickers to its correct position on A. It would be nice if no mouse events happen on B, but at least mouseEnter() could be called on A again since this is where the mouse is.

On Windows the sequence is the same as above, followed by mouseExit() on B, and mouseEnter() on A. This is better, but it would be better still if no mouse events happened on B.

On both platforms, the mouse events on B also say they originated from B so I can’t use that guard.

One final observation, on Mac, if I modify findPeerContainingPoint() to return the peer when it contains the point, I get the same behaviour as Windows. The JUCE implementation calls into an OS-specific function to determine if peer contains the point, which always returns false. My modification was to call instead peer->getBounds().contains(globalPos.toInt()), which seems to work, but maybe it’s not as simple as that.

1 Like