PDA

View Full Version : Receiving events while mouse is pressed



davj
20th May 2014, 12:00
Hi,



QMenu menu;

menu.addAction("Item 1");
menu.addAction("Item 2");
menu.addAction("Item 3");

qApp->setStyleSheet("QMenu::item:selected { background: rgba(0, 0, 128, 20%); }");

This example creates a simple QMenu where the backgrounds of the single items get colored when the mouse is hovering over them. This also works if the mouse is pressed on "Item 1" and then hovered over the other ones (not releasing the mouse button).


Is there a way to achieve this behaviour on widgets in a layout?


QVBoxLayout layout;

layout.addWidget(new QLabel("Item 1"));
layout.addWidget(new QLabel("Item 2"));
layout.addWidget(new QLabel("Item 3"));

qApp->setStyleSheet("QLabel:hover { background: rgba(0, 0, 128, 20%); }");

With this example, the labels only get colored when the mouse is hovering them without a mouse button being pressed. If I press and hold the left mouse button on "Item 1" on hover over the other items, "Item 1" remains being colored. The others will not get colored.

I guess this is a problem of event propagation. When the mouse is pressed and moved, only "Item 1" receives events (QEvent::MouseMove and QEvent::HoverMove). The others are not receiving any type of event.

So is there a way to solve this problem, i.e. to let the other items also receive the events? Or if it is not a problem of event propagation, how do I achieve the same behaviour like on the QMenu?


Thanks in advance for your help.

Rajesh.Rathod
20th May 2014, 14:52
Read QMouseEvent class documentation, It says ....

"Qt automatically grabs the mouse when a mouse button is pressed inside a widget; the widget will continue to receive mouse events until the last mouse button is released."

So think about it and you should try setting "mouseTracking" false when mouse button is pressed, see if it works for you.

davj
20th May 2014, 16:06
Unfortunately this does not help. Mouse tracking is set to false. I also checked whether it is set to true from Qt, but this is not the case. A QDebug output during the mouseMoveEvent does also show that the tracking is set to false.
So tracking is not the problem.

"Qt automatically grabs the mouse when a mouse button is pressed inside a widget; the widget will continue to receive mouse events until the last mouse button is released."

Is there a way to disable this on specific widgets?
I think there has to be a way, because it works on QMenu-items (see the first example that I posted). Or is it a feature that is only possible on QActions?

Does someone know where this grabbing of the mouse is done inside the Qt-sources? I did not find it.

Added after 28 minutes:

What I am trying to achieve is that a QWidgetAction, that is added to a QMenu, does behave like a QAction (regarding the hovering and coloring).

d_stranz
21st May 2014, 04:40
QMenu is a simple QWidget, the items within it are not separate sub-widgets, they're just rectangles of space within the QMenu. So there is no moving of the mouse from one widget to another within the menu, it's all within the same widget. The highlighting and all that is just internal to the implementation of QMenu painting. So the behavior of the mouse within the QMenu is exactly as the Qt documentation says, it "grabs the mouse when a mouse button is pressed within a widget".

You might be able to work around this by implementing an event filter in the widget that contains the buttons, and installing that filter on each of the buttons you want to highlight. Turn on mouse tracking and in the event filter keep track of when the mouse leaves and enters the buttons. This is likely the same mechanism that Qt uses to distinguish between a press and release within a button (a "click") and a press on a button and release outside it (a canceled click).

Of course, if you implement this, you'll be sending all the wrong UI clues to the user. Pressing the mouse on one button, moving to another, then releasing the mouse on a different button should not result in the same behavior as an actual click on the second button. No other GUI around has that behavior. Menus are different - people understand this - when you press the mouse on a top level item then move down the list, they know that whatever item they release it on is the one that will be executed. If you want to cancel a menu action, you move off the menu. Same thing with buttons. If you accidentally move off one button and release the mouse over another, a user never expects that to behave the same way as a click on the second button.

davj
21st May 2014, 09:11
Of course, if you implement this, you'll be sending all the wrong UI clues to the user. Pressing the mouse on one button, moving to another, then releasing the mouse on a different button should not result in the same behavior as an actual click on the second button. No other GUI around has that behavior. Menus are different - people understand this - when you press the mouse on a top level item then move down the list, they know that whatever item they release it on is the one that will be executed. If you want to cancel a menu action, you move off the menu. Same thing with buttons. If you accidentally move off one button and release the mouse over another, a user never expects that to behave the same way as a click on the second button.

I have to contradict here. A QMenu does behave differently. You do not have to press and hold the mouse button on a top level item.


QMenu menu;

menu.addAction("Item 1", this, SLOT(action1()));
menu.addAction("Item 2", this, SLOT(action2()));
menu.addAction("Item 3", this, SLOT(action3()));

qApp->setStyleSheet("QMenu::item:selected { background: rgba(0, 0, 128, 20%); }");

If this QMenu is open and you press the mouse button on "Item 1", move the mouse over to "Item 3" and release it there, "action3()" will be executed. There is no need to hold the button down on the top level item. You can simply open the menu, release the button that opened it, then press and hold the button on any item in the menu and release it over another. The QAction on which the button was released will be executed.
The background coloring is done through Qt like expected, i.e. the item over which the mouse resides is colored, regardless whether the mouse button is pressed and hold or not.

This is the default behaviour in many GUIs and Qt also does it.

To return to my question: Implement it like this


QWidgetAction * action1 = new QWidgetAction(this);
QWidgetAction * action2 = new QWidgetAction(this);
QWidgetAction * action3 = new QWidgetAction(this);

action1->setDefaultWidget(new QLabel("Item 1"));
action2->setDefaultWidget(new QLabel("Item 2"));
action3->setDefaultWidget(new QLabel("Item 3"));

QMenu menu;

menu.addAction(action1);
menu.addAction(action2);
menu.addAction(action3);

qApp->setStyleSheet("QMenu::item:selected, *:hover { background: rgba(0, 0, 128, 20%); }");

The click behaviour remains the same, i.e. press and hold the mouse on "Item 1", move it over to "Item 3" and release it there, results in "action3" being executed.
BUT the background coloring behaves differently. In this case, the background of "Item 1" remains colored regardless where the mouse cursor resides.

So the user gets the visual feedback of "Item 1" being colored even if the mouse cursor is over "Item 3", but if he releases the mouse button over "Item 3" "action3" gets executed.

I suspect, that this is a problem of mouse grabbing and event propagation, but am not sure.

The question is, how can I give the user the same visual feedback when using QWidgetActions?