Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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 are propagateComposedEvents and preventStealing. 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 particular MouseArea instance. This may be resolved by declaring propagateComposedEvents = true and writing mouse.accepted = false; in the overridden onPressed event, but doing that, many other events are no longer called later, among others onClicked, 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?



  • 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. Here onWheel is no longer propagated to children, i.e nor the ListView nor its items no longer receive the onWheel event, meaning the scrolling is broken on wheel, despite of the activated properties to propagate and don't steal events, and setting mouse.accepted = false; in the onWheel function cannot be done, as mouse isn't provided by the onWheel event.

    How to resolve a such conflict?


  • Qt Champions 2018

    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) { ... } or onWheel: 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...");
        }
    }