Missing mouseEnter callback


#1

Basically I’ve found that when the text editor inside of a label is dismissed (pressing return or esc, mouse click, etc) if the mouse is over another component at the time, that component doesn’t get the relevant mouseEnter until the mouse is moved outside the bounds of the component and back in again.

Although I’ve only reproduced this on mac here, I’ve seen this on Windows too. I’ve reproduced this with both the latest tip and I’ve seen what I believe is the the same issue with the commit directly before version 4.2.0 so this probably isn’t a new issue.

Below is a gif demonstrating the issue.

Here’s some printed output that shows the missing mouseEnter.

label 2: virtual void MainContentComponent::mouseUp(const juce::MouseEvent &)
label 2: virtual void MainContentComponent::mouseExit(const juce::MouseEvent &)
label 1: virtual void MainContentComponent::mouseMove(const juce::MouseEvent &)

Here’s the code I used to produce the example.

MainComponent.h:

#ifndef MAINCOMPONENT_H_INCLUDED
#define MAINCOMPONENT_H_INCLUDED

#include "../JuceLibraryCode/JuceHeader.h"

class MainContentComponent   : public Component
{
public:
    MainContentComponent();
    ~MainContentComponent();

    void paint (Graphics&) override;
    void resized() override;
    void mouseEnter (const MouseEvent& event) override;
    void mouseExit (const MouseEvent& event) override;
    void mouseDown (const MouseEvent& event) override;
    void mouseUp (const MouseEvent& event) override;
    void mouseDrag (const MouseEvent& event) override;
    void mouseMove (const MouseEvent& event) override;
    void mouseDoubleClick (const MouseEvent& event) override;
    
private:
    Label basicLabel;
    Label editableLabel;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};

#endif  // MAINCOMPONENT_H_INCLUDED

MainComponent.cpp:

#include "MainComponent.h"

MainContentComponent::MainContentComponent()
    : basicLabel ("label 1", "read me")
    , editableLabel ("label 2", "click me")
{
    setSize (600, 400);
    
    basicLabel.setColour (Label::backgroundColourId, Colours::white);
    editableLabel.setColour (Label::backgroundColourId, Colours::white);
    
    basicLabel.addMouseListener (this, false);
    editableLabel.addMouseListener (this, false);
    
    editableLabel.setEditable (true);
    
    for (auto mouseSource : Desktop::getInstance().getMouseSources())
    {
        mouseSource.enableUnboundedMouseMovement (true);
    }
    
    addAndMakeVisible (basicLabel);
    addAndMakeVisible (editableLabel);
}

MainContentComponent::~MainContentComponent()
{
}

void MainContentComponent::paint (Graphics& g)
{
    g.fillAll (Colour (0xff001F36));
}

void MainContentComponent::resized()
{
    basicLabel.setBounds (10, 10, 100, 50);
    editableLabel.setBounds (10, 70, 100, 50);
}

void MainContentComponent::mouseEnter (const MouseEvent& event)
{
    if (dynamic_cast<Label*>(event.eventComponent))
    {
        DBG (event.eventComponent->getName() << ": " << __PRETTY_FUNCTION__);
        event.eventComponent->setColour (Label::backgroundColourId, Colours::red);
    }
}

void MainContentComponent::mouseExit (const MouseEvent& event)
{
    if (dynamic_cast<Label*>(event.eventComponent))
    {
        DBG (event.eventComponent->getName() << ": " << __PRETTY_FUNCTION__);
        event.eventComponent->setColour (Label::backgroundColourId, Colours::white);
    }
}

void MainContentComponent::mouseDown (const MouseEvent& event)
{
    if (dynamic_cast<Label*>(event.eventComponent))
    {
        DBG (event.eventComponent->getName() << ": " << __PRETTY_FUNCTION__);
    }
}

void MainContentComponent::mouseUp (const MouseEvent& event)
{
    if (dynamic_cast<Label*>(event.eventComponent))
    {
        DBG (event.eventComponent->getName() << ": " << __PRETTY_FUNCTION__);
    }
}

void MainContentComponent::mouseDrag (const MouseEvent& event)
{
    if (dynamic_cast<Label*>(event.eventComponent))
    {
        DBG (event.eventComponent->getName() << ": " << __PRETTY_FUNCTION__);
    }
}

void MainContentComponent::mouseMove (const MouseEvent& event)
{
    if (dynamic_cast<Label*>(event.eventComponent))
    {
        DBG (event.eventComponent->getName() << ": " << __PRETTY_FUNCTION__);
    }
}

void MainContentComponent::mouseDoubleClick (const MouseEvent& event)
{
    if (dynamic_cast<Label*>(event.eventComponent))
    {
        DBG (event.eventComponent->getName() << ": " << __PRETTY_FUNCTION__);
    }
}

Mouse-Exit Bug
#2

Looking into this a little more it seems there is also some other slightly unexpected mouse events occurring here too.

While the editor is open on label 2 (the click me label) if I move the mouse in and out of the area occupied by label 1 (the read me label) I still get mouseExit() events but no other mouse events! If I dismiss the editor while my mouse is over label 2 (say with esc or return), without touching the mouse, I get a mouseMove() event on label 1.


#3

Thanks for the detailed report. I’ve just pushed a fix for this to the develop branch - does it solve the issue for you?

Ed


#4

That does appear to solve both the issue of the missing mouseEnter and the mouseExit that I continued to get called. So many thanks for the quick fix regarding that.

However there is still possibly a mouseMove call that maybe shouldn’t be. If you dismiss the editor in label 2 with the mouse over label 1 the output looks like so…

label 2: virtual void MainContentComponent::mouseExit(const juce::MouseEvent &)
label 1: virtual void MainContentComponent::mouseEnter(const juce::MouseEvent &)
label 1: virtual void MainContentComponent::mouseMove(const juce::MouseEvent &)

I assume the move indicates the change in mouse position from when the editor was shown, however if the editor is dismissed by clicking on label 1 then the order is as follows…

label 2: virtual void MainContentComponent::mouseExit(const juce::MouseEvent &)
label 1: virtual void MainContentComponent::mouseEnter(const juce::MouseEvent &)
label 1: virtual void MainContentComponent::mouseDown(const juce::MouseEvent &)
label 1: virtual void MainContentComponent::mouseDrag(const juce::MouseEvent &)
label 1: virtual void MainContentComponent::mouseUp(const juce::MouseEvent &)
label 1: virtual void MainContentComponent::mouseMove(const juce::MouseEvent &)

This is unexpected but hardly problematic. Given that there’s always the possibility of other side effects when dealing with an area of code like this, I’ll leave this to your discretion to decide if you think this should be fixed.

Many thanks.


#5

One possible thing I noticed looking at the commit, shouldn’t

if (MouseInputSource* mouse = Desktop::getInstance().getMouseSource (0))

either be getMainMouseSource() or better still shouldn’t it loop through all the mouse sources and do the work for each?


#6

Yeah that’s a good suggestion, thanks. I’ll add that and take a look at the other odd mouse behaviour but like you said this area of the code can be quite tricky so I can’t make any promises!

Ed