ImaginativeThinking.ca


A developers blog

QML Mouse/Keyboard Event Handling

By: Brad

In QML you can accept user inputs via the mouse with the MouseArea element and via the keyboard with the Keys element. These elements have built in signals and slots to handle input events (i.e. click event, key press event); however, they work a little differently then the other signals/slots in QML. These events bubble up through the application hierarchy being processed by each applicable event handle until accepted.

Rectangle {
    width: 200
    height: 200

    MouseArea {
        anchors.fill: parent
        onClicked: { console.log("Bar"); }
    }
    MouseArea {
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        anchors.right: parent.right
        height: 100
        onClicked: { console.log("Foo"); }
    }
}

My meaning is that in the above example if the user clicked the mouse in the bottom 100px of the Rectangle both onClicked event handlers will execute; that is “Foo” and “Bar” will be printed.

Assuming that what we wanted is for just to have “Foo” printed we have to tell the event handling system that the event was handled in the first event handler and not to bubble it up the application hierarchy. We do this with the accepted property of the MouseEvent object.

The event handlers of the MouseArea element take in the MouseEvent object as a parameter named mouse; therefore to tell the event handling system that the event was processed we need to add mouse.accepted = true to the above code example.

Rectangle {
    width: 200
    height: 200

    MouseArea {
        anchors.fill: parent
        onClicked: { console.log("Bar"); }
    }
    MouseArea {
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        anchors.right: parent.right
        height: 100
        onClicked: { 
            console.log("Foo"); 
            mouse.accepted = true
        }
    }
}

With the above change now only “Foo” will be printed.

For mouse clicks setting the mouse.accepted to true to me seems to be the 99%; off the top of my head I’m not sure of to many instances where I’d want overlapping MouseAreas to process the same mouse click. Its a bit of a shame that this isn’t the default behaviour. However the same bubbling up of event processing is also true for keyboard inputs and there it does make sense to allow the event to bubble up on default.

The Keys element’s event handlers work much the same way as the MouseArea’s event handlers where events are bubbled up the application hierarchy until reaching the root or explicitly accepted. Similarly they take an event object but instead of MouseEvent its the KeyEvent object and the parameter is named differently too (in the MouseArea handlers its called mouse but in the Keys event handlers its called event).

Rectangle {
    width: 200
    height: 200

    Column {
        MyControl {
            Keys.onPressed: { 
                console.log("Foo"); 
            }
        }
        MyOtherControl {
        }
    }
    Keys.onPressed: {
        console.log("Bar"); 
    }
}

In the above code example if the MyOtherControl or the root element has focus and I press any key on the keyboard the string “Bar” will be printed; if however the MyControl has focus and I press any key on the keyboard both strings “Foo” and “Bar” will be printed.

Now what I want to have happen might be to have the string “Foo” printed if the Enter key is pressed while focus is on the MyControl element but I might also want a global F1 key that will print “Bar” regardless of what controls in the UI have focus. This is where the bubbling up of system events comes in hand; because I can re-write the above to allow the MyControl’s onPressed event handler to process the key press while still allowing the root to accept key presses at any time.

Rectangle {
    width: 200
    height: 200

    Column {
        MyControl {
            Keys.onPressed: {
                if ( event.key === Qt.Key_Return ) { 
                    console.log("Foo"); 
                    event.accepted = true
                }
            }
        }
        MyOtherControl {
        }
    }
    Keys.onPressed: {
        console.log("Bar"); 
    }
}

Now if the focus is on the root or the MyOtherControl element I can press any key on the keyboard, including the enter key, and I’ll get “Bar” printed; however, if the focus is on the MyControl element I’ll still get “Bar” printed when I press any key other then the enter key but if I press the enter key only “Foo” will be printed.

Now to make the root elements onPressed event handler only print “Bar” on the F1 key press I can add a similar condition to its event handler.

Rectangle {
    width: 200
    height: 200

    Column {
        MyControl { ... }
        MyOtherControl { ... }
    }
    Keys.onPressed: {
        if ( event.key === Qt.Key_F1 ) { 
            console.log("Bar"); 
            event.accepted = true
        }
    }
}

I have found issues however when the implicit non-acceptance of the event hasn’t always worked so sometimes its not a bad idea to be explicit about things by setting the event.accepted to false.

MyControl {
    Keys.onPressed: {
        if ( event.key === Qt.Key_Return ) {
            console.log("Foo"); 
            event.accepted = true
        } else {
            event.accepted = false
        }
    }
}

Well there you have it the QML Event Handling System in a nut shell. I encourage you to look up the MouseEvent and KeyEvent objects and the MouseArea and Keys elements in the Qt documentation for more details.

If you have any questions or comments feel free to leave a comment below and I’ll respond as time permits

Until next time think imaginatively and design creatively

Brad

My interest in computer programming started back in high school and Software Development has remained a hobby of mine ever since. I graduated as a Computer Engineering Technologist and have been working as a Software Developer for many years. I believe that software is crafted; understanding that how it is done is as important as getting it done. I enjoy the aesthetics in crafting elegant solutions to complex problems and revel in the knowledge that my code is maintainable and thus, will have longevity. I hold the designation Certified Technician (C.Tech.) with the Ontario Association of Computer Engineering Technicians and Technologists (OACETT), have been certified as a Professional Scrum Master level 1 (PSM I) and as a Professional Scrum Developer level 1 (PSD I) by Scrum.org as well as designated as an Officially Certified Qt Developer by the Qt Company. For more on my story check out the about page here

Feel free to write a reply or comment.