MouseArea - The very painful way of events propagation
-
In several of my projects I need to override the default way the mouse interacts with my components, e.g when I need to support new functionalities. For such situations Qt planned the
MouseArea
component.This works well for a huge number of cases, however there are situations where new functionalities are required WITHOUT breaking the already existing mouse behavior provided by the component itself. This is especially true when custom views are written, where advanced behavior are required, like e.g the edition or opening a popup, when an item is clicked on a particular area in the tree, while the tree should continue apply the default behavior, when the user clicked anywhere else, where it's not supposed to react to a particular action, e.g scroll the view when the tree is clicked and the mouse moved.
However putting a
MouseArea
somewhere breaks the event propagation to the children components. Qt also planned a such situation, and added properties to handle that, which arepropagateComposedEvents
andpreventStealing
. These properties are supposed to resolve the above mentioned issue by changing the way an event is propagated through components.And this works... sometimes. It's true that these properties help to resolve many complicated scenarios which may occur, but there are still issues, unfortunately. For example:
- Overriding the
onPressed
event stops the propagation inside a particularMouseArea
instance. This may be resolved by declaringpropagateComposedEvents = true
and writingmouse.accepted = false;
in the overriddenonPressed
event, but doing that, many other events are no longer called later, among othersonClicked
,onDoubleClicked
, and any keyboard event. - In a more general manner, overriding events in a particular
MouseArea
may block these events, and if you try to unblock them using the properties planned by Qt in such situations, this may works, but other events are blocked later, which is a side effect from my point of view.
Finally this is a very complicated rules set, and the ins and outs are not easy to grasp. Unless I miss something. So there is my question: What is the correct configuration to override ALL the mouse events without blocking ANY event in the children components, nor causing ANY side effect, like overriding this event blocks the emission of this other later?
- Overriding the
-
Here is another example:
MouseArea { id: maFileListView anchors.fill: parent hoverEnabled: true acceptedButtons: Qt.LeftButton | Qt.RightButton propagateComposedEvents: true preventStealing: true // called when mouse wheel changes onWheel: { ... } }
I added this code in a
ListView
. HereonWheel
is no longer propagated to children, i.e nor theListView
nor its items no longer receive theonWheel
event, meaning the scrolling is broken on wheel, despite of the activated properties to propagate and don't steal events, and settingmouse.accepted = false;
in theonWheel
function cannot be done, asmouse
isn't provided by theonWheel
event.How to resolve a such conflict?
-
Why are you adding MouseArea on top of clickable or scrollable items?
mouse.accepted = false; in the onWheel function cannot be done, as mouse isn't provided by the onWheel event.
the wheel signal provides a wheel object though, you can do
wheel.accepted = false
.
The more correct way to get parameter from signal handlers now is to define them as function, not rely on context properties. This has the advantage of letting you name the parameters and being less magic:onWheel: function (event) { ... }
oronWheel: event => { ... }
I am curious about your usecase of capturing onWheel on a ListView, can you give more details?
Anyway, have you looked at Qt Quick Input Handlers? They offer more possibilities and are more lightweight than
MouseArea
-
@GrecKo Thank you for your answer.
the wheel signal provides a wheel object though, you can do
wheel.accepted = false
.Good point, I missed that. This resolve the issue I faced with
onWheel
.The more correct way to get parameter from signal handlers now is to define them as function, not rely on context properties.
Ok, thanks for the tip. I'll try this way in the future.
I am curious about your usecase of capturing onWheel on a ListView, can you give more details?
In my case it's just to close a hint opened from a button on an item in my view. The hint position is linked to the opening button position, and as I use a
ToolTip
, the hint continue to scroll outside my view when the item containing the button go out of the view. For that reason I want to close this hint before scrolling the view.Anyway, have you looked at Qt Quick Input Handlers?
This seems to be an interesting alternative indeed. However I tried to use it, but it seems not react at all in my view. Here is the code I tested, am I doing something wrong?
TapHandler { target: myView acceptedButtons: Qt.LeftButton | Qt.RightButton onTapped: { console.log("onTapped called..."); } onSingleTapped: { console.log("onSingleTapped called..."); } }
-
I had the same issue, and used a very different approach.
I installed an event filter on the QQuickView or QQuickWidget. Now I can see all events before they are propagated to QML. As long as I don't filter the event, behavior on the QML side is unchanged (even if I choose to respond to it).
The big advantage is that I can code certain behaviors in one place for any number of QML components.
-
@GrecKo said in MouseArea - The very painful way of events propagation:
Why are you adding MouseArea on top of clickable or scrollable items?
Sorry to reply to an old post but this thread got resurrected and this caught my eye. In my case I have a
MouseArea
onTreeView
, which is clickable and scrollable. As I recall (bearing in mind it was some time ago now) it seemed to be the only way to implement a context menu popup when right clicking tree branches. Is there something different I could have done?(This is the old
TreeView
BTW - I haven't looked closely at the new one in Qt 6 to see if that is any different.)