Button hover refresh.. specific case


#1

Dear JUCE geeks,

I have a small “bug” in one of my porgram that I can’t find a simple solution.

I have an image button with both normal image and an overImage.

When the mouse enter the button, the overImage is displayed. Then if the user clicks the button, my program needs to hide the button and reset the position of the mouse to the center of the screen;

A few seconds later I make the button visible again (the mouse was still at the screen center I.e. not over the button). But the button still shows the “overImage” instead of normal Image. then if the mouse is moved by the user, the button returns to the normal image.

I suspect a problem with the mouseexit not been called.

CenterMouse(); // move louse to screencenter (outside button) Rep1Button->setState(Rep1Button->buttonNormal ); //tried this but didn't worl Rep1Button->repaint(); //tried this but didn't work Rep1Button->setVisible(false);

When I do a Rep1Button->setVisible(true); the overImage is displayed until user move mouse.
How to avoid this ?

Thanks in advance for any help

Sylvain Clément


#2

Interesting… AFAICT it should be ok if you make the button itself invisible, but perhaps you’re making its parent invisible?

Does it help if you add this line to the Button class?

[code]void Button::parentHierarchyChanged()
{
updateState();

Component* const newKeySource = (shortcuts.size() == 0) ? nullptr : getTopLevelComponent();

[/code]


#3

Thanks Jules for your answer.

unfortunately adding updateState(); in parentHierarchyChanged() did not solve the problem.

In my piece of code my Rep1Button is of type ImageButton* Rep1Button;

I did a little experiment.

Each time I want to change the visibility of the button I call this method

void myclass::toggleResponseButtons(bool state) { CenterMouse(); Rep1Button->setState(Rep1Button->buttonNormal );Rep1Button->repaint(); Rep1Button->setVisible(state); }

at the begining of th eprogram the button is diplayed
When the user click on the button (and since the mouse entered the button area overImage was displayed) I call toggleResponseButtons(false)
and 5 seconds later (triggered with a Timer) I made a new call to toggleResponseButtons(true) to make the button visible again but with overImage displayed until the user moves the mouse.

The experiment : I did comment the last line of my toggleResponseButtons button as below :

void myclass::toggleResponseButtons(bool state) { CenterMouse(); Rep1Button->setState(Rep1Button->buttonNormal );Rep1Button->repaint(); //Rep1Button->setVisible(state); <Do not change visibility of the button }

That was the only change in my program. At the 1st call to toggleResponseButtons(false) the button did not disappeared (of course) but it went back to the normalImage immediately.

Thus it seems that the setVisible(false) prevent the button from going back to the normalImage.

Any Idea ?

Sylvain


#4

When you make it invisible, Button::visibilityChanged gets called, which automatically updates the state based on the mouse, so there’s no point setting the state explicitly yourself beforehand. Try calling setState after making it invisible, and you might have more luck!


#5

unfortunately it doesn’t work…

S


#6

Can you create a little test-case code snippet that I could paste into the demo app to reproduce this?


#7

Thanks Jules,

I just reproduced the problem in jucedemo.

here the modifications I did in the DemoTabbedComponent class of “WidgetsDemo.cpp”

(1) add “, public Timer” to the declaration of the DemoTabbedComponent class.
(3) add :

private: Button* LastButton;
at the bvegining of the DemoTabbedComponent class

(3) add the following at the end of the buttonClicked method :

// Change mouse position, Hide the clicked button and wait 3s LastButton=button; CenterMouse(); LastButton->setVisible(false); startTimer(3000);

(4) and add these tso methods (Center mouse will not really center the mouse in this exemple but it did in my app). :

[code] void CenterMouse()
{
int w=this->getWidth();
int h=this->getHeight();
Point< int > target=this->getPosition();

	target.setXY( (int) target.getX()+(w/2),  (int) target.getY()+(h/2));
	
	Desktop::setMousePosition(target);
}

void timerCallback()
{
	stopTimer();
	LastButton->setVisible(true);				
}

[/code]

Now run the demo go to the widget/button page

When you click on the second button (Image only drawable button) it will diseappear and the position of the mous will change. Just wait 3s and the button will reappear but with the “overImage” despite the fact thet the mouse is not over the button.

I did the test on Mac OSX 10.6.8…


#8

Ok… Before I try it, does this only happen if you keep the mouse completely still? Because Desktop::setMousePosition will not generate any mouse-move events (all OSes work that way: they just move the visible cursor, and never act as if the user moved it), so until you actually move the mouse, you can’t really expect anything to get updated.


#9
Yes. if the user moves the mouse after the mouse center, the button goes to "normalImage". 


[quote]Because Desktop::setMousePosition will not generate any mouse-move events [/quote] 
that is probably the good explanation except that it is then strange to observe the expected behavior in my modified example where I bypass the seVisible(false). Even with no user-initiated mouse movements, the button returns to the normalImage.


[code]void myclass::toggleResponseButtons(bool state)
{
   CenterMouse();
   Rep1Button->setState(Rep1Button->buttonNormal );Rep1Button->repaint();   
   //Rep1Button->setVisible(state);   <Do not change visibility of the button
}[/code]


Anyway, is there any mean to force the update or if it is possible to simulate a user mouse movement ?

S

Yes. if the user moves the mouse after the mouse center, the button goes to “normalImage”.

that is probably the good explanation except that it is then strange to observe the expected behavior in my modified example where I bypass the seVisible(false). Even with no user-initiated mouse movements, the button returns to the normalImage.

void myclass::toggleResponseButtons(bool state) { CenterMouse(); Rep1Button->setState(Rep1Button->buttonNormal );Rep1Button->repaint(); //Rep1Button->setVisible(state); <Do not change visibility of the button }

Anyway, is there any mean to force the update or if it is possible to simulate a user mouse movement ?

S


#10

This really is a particularly edgy edge-case!

Until the mouse moves, the app will still see it at its old position inside the button, so I think everything is behaving perfectly “correctly”. AFAIK there’s no way to force a mouse-move via the OS (OSes make that difficult, probably for security reasons).

Have you tried Component::sendFakeMouseMove?


#11

Maybe disabling the button before you hide it would work? Wouldn’t it ignore the mouse when disabled?


#12

Unfortunatelyit doesn’t work. However I have tried to disable the component INSTEAD of hiding it… thus my toggleResponseButtons became :

void myclass::toggleResponseButtons(bool state) { CenterMouse(); Rep1Button->setEnabled(state); }

And guess what ? It works. Not the ideal situation for me since the buttons are still slightly visible (the software is targeted at aged people) but I think I can deal with it…

Remark : if I add “Rep1Button->setVisible(state);” at the end of the method (after the seEnabled) then The problem reappears… The execution of setVisible seems to block the update of the button status. Quite strange. One alternative solution may be to wait a little before the set visible…

Sylvain Clément


#13

Have you tried just deleting the button and creating a new one when it comes back?