QML- Right Click Not Detected in MouseArea - qt

I'm working on an image editor and specifically working on setting the current left or right button's associated color using a MouseArea (inside a Button type). The problem I'm having is that I can't seem to filter for particular buttons at all. Here's the snippet giving me trouble:
Button {
x: 60
width: 80
height: 40
text: "Blue"
anchors.verticalCenter: parent.verticalCenter
onButtonClick: {
if(mouseArea.pressedButtons & Qt.RightButton) {
console.log("Right button used");
GlobalState.setRightColorValues(0.0, 0.0, 1.0, 1.0);
} else {
console.log("Left button used");
GlobalState.setLeftColorValues(0.0, 0.0, 1.0, 1.0);
}
}
}
(If needed I can provide the entirety of Button.qml, but it's mostly from here).
I'm trying to follow the example here, but the method used to filter for right mouse clicks doesn't seem to work (anymore, anyway). What happens is the statement "defaults" to assuming a left click. I've also tried separating the two into different if-statements, but doing so causes no buttons to be filtered explicitly.
What needs to be changed in order to filter for specific mouse buttons? Or will I have to implement the sort of "switch primary color" button used in Paint/Paint.NET?
Edit 1: I've realized that there was a relevant snippet missing from Button.qml-
MouseArea{
id: buttonMouseArea;
acceptedButtons: Qt.AllButtons;
hoverEnabled: true
onEntered: parent.color = onHoverColor
onExited: parent.color = buttonColor
anchors.fill: parent;
onClicked: buttonClick();
}
This is nested inside a Rectangle, which also holds a Text field.

By default MouseArea only handles the left mouse button. You can handle other buttons by setting the acceptedButtons property. You can determine which button caused the click by using the mouse MouseEvent that is accessible in the onClicked handler.
MouseArea {
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if (mouse.button === Qt.RightButton) { // 'mouse' is a MouseEvent argument passed into the onClicked signal handler
console.log("right button clicked!")
} else if (mouse.button === Qt.LeftButton) {
console.log("left button clicked!")
}
}
}
See acceptedButtons, and mouse MouseEvent

You can check the button this way:
MouseArea {
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if(mouse.button & Qt.RightButton) {
}
}
}

You can avoid using if statements if you dedicate a MouseArea for each mouse button you're interested in:
import QtQuick
import QtQuick.Controls
Page {
Rectangle {
anchors.centerIn: parent
color: "#ffe"
border.color: "grey"
width: parent.width / 2
height: parent.height / 2
Text {
id: txt
anchors.centerIn: parent
text: "Use Mouse here"
}
MouseArea {
anchors.fill: parent
onClicked: txt.text = "left click detected";
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: txt.text = "right click detected";
}
}
}
You can Try it Online!

What I have observed is that pressedButtons works only with onPressed, not with onClicked.
I find that kind of odd, because clicked() is nothing but a press followed by a release. Hence I assumed that it will work with clicked() as well, but sadly it doesn't.
Example:
MouseArea {
id: idMouseArea
acceptedButtons: Qt.LeftButton | Qt.RightButton
anchors.fill: parent
//onClicked: { // pressedButtons not identified with onClicked
onPressed: { // pressedButtons identified and works well with onPressed
if (idMouseArea.pressedButtons & Qt.RightButton) {
console.log("right-button pressed")
} else if (idMouseArea.pressedButtons & Qt.LeftButton) {
console.log("left-button pressed")
}
}
}
This is what I have observed with Qt 5.5.1 and QtQuick 2.5. The documentation doesn't show how to use pressedButtons property with if-else. Please correct/comment if the observation is wrong.
Update:
If you direly need to use pressedButtons with onClicked you can use following hack to do so.
MouseArea {
property int mouseButtonClicked: Qt.NoButton
acceptedButtons: Qt.RightButton | Qt.LeftButton
anchors.fill: parent
onPressed: {
if (pressedButtons & Qt.LeftButton) {
mouseButtonClicked = Qt.LeftButton
} else if (pressedButtons & Qt.RightButton) {
mouseButtonClicked = Qt.RightButton
}
}
onClicked: {
if (mouseButtonClicked === Qt.LeftButton) {
console.log("left button clicked")
} else if (mouseButtonClicked === Qt.RightButton) {
console.log("right button clicked")
}
}
}

Related

QML SplitView auto collapse on handlebar mouse release

I have a QML Controls 2 SplitView and a redefined handle, which works well, but I want detect mouse release event on the handler, so I could collapse the SplitView under a certain threshold of width. Adding a MouseArea on top of the existing handle will absorb drag events, so I'm unable to move the handlebar. Any idea how could I gather the mouse release event, or any other solution which solves this problem?
Alright, I have created an example application. As you can see in this example, my MouseArea is marked with yellow and collapses the right view programmatically when double clicked, which is nice, but I also want to drag the handlebar and upon mouse release under a certain width threshold I want to collapse the view as well. The black part of the handlebar where my MouseArea is not covering the handlebar, responds to drag, but since there is no signal I can gather from it, the width threshold already set shouldCollapse boolean property, so the view won't update. Probably I could solve this issue with a timer, but I need a more sophisticated solution.
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 800
height: 400
visible: true
SplitView {
id: splitView
anchors.fill: parent
orientation: Qt.Horizontal
function toggleCollapse() { collapsibleRect.shouldCollapse = !collapsibleRect.shouldCollapse }
handle: Rectangle {
implicitWidth: 20
implicitHeight: 20
color: "black"
MouseArea {
anchors.centerIn: parent
width: parent.width
height: parent.height / 2
onDoubleClicked: splitView.toggleCollapse()
Rectangle {
anchors.fill: parent
color: "yellow"
Text {
anchors.centerIn: parent
text: "Double click to collapse"
rotation: 90
}
}
}
}
Rectangle {
id: mainRect
color: "green"
SplitView.fillWidth: true
Text {
anchors.centerIn: parent
font.pixelSize: 24
text: "Main scene"
}
}
Rectangle {
id: collapsibleRect
property bool shouldCollapse: false
SplitView.preferredWidth: shouldCollapse ? 0 : 300
color: "purple"
clip: true
onWidthChanged: {
if(width < 200) shouldCollapse = true
else shouldCollapse = false
}
Text {
anchors.centerIn: parent
rotation: parent.shouldCollapse ? 90 : 0
font.pixelSize: 24
text: parent.shouldCollapse ? "SHOULD BE COLLAPSED" : "NOT COLLAPSED"
Behavior on rotation { NumberAnimation { duration: 100 } }
}
}
}
}
I had a similar problem and was able to solve it thanks to the hint of #Ponzifex that the SplitView's resizing property will be set to true as soon as the handle is clicked. Using a Timer I managed to detect whether the handle was quickly pressed twice in a row.
SplitView {
id: view
...
handle: Rectangle {
...
}
//============================================================
// double click behavior
Timer {
id: doubleClickTimer
interval: 300 // number of ms between clicks that should be considered a double click
}
property bool doubleClicked: false
// `resizing` will be set to true even if the handle is just pressed
onResizingChanged: {
if (view.resizing) {
if (!doubleClickTimer.running) {
doubleClickTimer.start();
return;
}
view.doubleClicked = true;
} else {
if (view.doubleClicked) {
// do any manual resizing in here
view.doubleClicked = false;
}
}
}
}
It is important to note, however, that it is only possible to resize the contents of a SplitView when resizing is false. That's why I need to have the doubleClicked helper property.
Add this to MouseArea:
onPressed: {
mouse.accepted = (mouse.flags & Qt.MouseEventCreatedDoubleClick);
}
propagateComposedEvents: true
cursorShape: Qt.SplitHCursor

QML MouseArea, trigger and propagate events

I have a QML component with buttons, ... in it. I put a MouseArea which covers this entire component because I need to execute an action wherever I click on the component. However, I also want to execute the action behind the MouseArea.
For example, if I click on a button in the component, I want to execute the MouseArea action and then the button action.
Item {
Button{
anchors: ...
onClicked: console.info("Button clicked")
}
MouseArea{
anchors.fill: parent
propagateComposedEvents: true
onClicked: console.info("MouseArea clicked")
}
}
If propagateComposedEvents: true, then MouseArea onClicked is not executed, but Button onClicked is. If false, MouseArea is executed but not Button onClicked.
I want to have both MouseArea (first) and Button (second) signal onClicked to be executed.
You just need to set the accepted property of the mouse to false.
Item {
Button{
anchors: ...
onClicked: console.info("Button clicked")
}
MouseArea{
anchors.fill: parent
propagateComposedEvents: true
onClicked: {
console.info("MouseArea clicked");
mouse.accepted =false;
}
}
From MouseArea documentation:
If propagateComposedEvents is set to true, then composed events will
be automatically propagated to other MouseAreas in the same location
in the scene. Each event is propagated to the next enabled MouseArea
beneath it in the stacking order, propagating down this visual
hierarchy until a MouseArea accepts the event. Unlike pressed events,
composed events will not be automatically accepted if no handler is
present.
If you want to exclusively catch mouse click on one of them, just move the MouseArea under Button regarding its z level. Or, just move its definition before the Button.
Item {
MouseArea{
anchors.fill: parent
onClicked: console.info("MouseArea clicked")
}
Button{
anchors: ...
onClicked: console.info("Button clicked")
}
}
OR:
Item {
Button{
anchors: ...
onClicked: console.info("Button clicked")
z: 1
}
MouseArea{
anchors.fill: parent
onClicked: console.info("MouseArea clicked")
z: 0
}
}

QML: Mouse wheel event propagation in ListView

Have strange situation with ListView scrolling on mouse wheel. Have Items structure similar to this:
MainAppWindow {
// Some zoomable map item
Map {
anchors.fill: parent
}
PopupMenu { // Simple Rectangle item
anchors.top: parent.top
width: 200
height: parent.height / 2
z: parent.z + 1
ListView {
anchors.fill: parent
clip: true
...
delegate: Item {
...
MouseArea {
anchors.fill: parent
onClick: {
someHandler()
}
}
}
}
}
}
ListView with vertical scroll works and scrolls just fine until it stops at bounds (top or bottom - whatever) and after this mouse event starts to propagate to underlying layer and ZoomableMap starts to zoom which is not we want: should be propagated there only if PopupMenu is not visible. Adding
onWheel: wheel.accepted = true
into MouseArea inside ListView delegate could partially solve the problem - it disables wheel and allows scrolling only by dragging the content. However better allow scrolling by the wheel as well. MouseArea in PopupMenu blocks wheel and dragging in the ListView completely as well - not helps also.
So what is problem here, how to fix? Or we doing something wrong here?
Need to add another MouseArea into PopupMenu which blocks all mouse events and is disabled by default and enable it only if popup is visible (optional):
enabled: popupMenu.visible
MainAppWindow {
// Some zoomable map item
Map {
id: map
anchors.fill: parent
}
PopupMenu { // Simple Rectangle item
id: popupMenu
anchors.top: parent.top
width: 200
height: parent.height / 2
z: parent.z + 1
MouseArea {
id: mapMouseArea
anchors.fill: parent
enabled: popupMenu.visible
preventStealing:true
hoverEnabled: true
onWheel: { wheel.accepted = true; }
onPressed: { mouse.accepted = true; }
onReleased: { mouse.accepted = true; }
}
ListView {
anchors.fill: parent
clip: true
...
delegate: Item {
...
MouseArea {
anchors.fill: parent
onClick: {
someHandler()
}
}
}
}
}
}
Note: however this solution does not work if ListView (or any other control) is a Map descendant item: item dragging causes map panning. To make it work need to make it at least sibling.

in Qt How to enable ListView and its item all receive MouseArea events?

I'm using Qt 5.6
I want ListView and its items all receive MouseArea onEntered, onClicked signals.
I tried the examples and changed:
ListView {
anchors.fill: parent
model: searchModel
delegate: Component {
Row {
spacing: 5
Marker { height: parent.height }
Column {
Text { text: title; font.bold: true
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: console.log("eeee");
}
}
Text { text: place.location.address.text }
}
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: console.log("entered");
}
}
Only ListView can accept onEntered signal, there is no response from its items.
How to enable items receive MouseArea events ?
To propagate clicked events, you should set propagateComposedEvent to true to the outermost MouseArea.
Guess if the same applies to the entered event.

Can't emit signal in QML custom Item

I created my own Item with signal clicked, that contatins MouseArea. I want to emit signal clicked, when MouseArea is clicked. But nothing works.
Here is my .qml code:
import QtQuick 2.4
Item {
id: baseButton
property alias text: txt.text
width: txt.width
height: txt.height
signal clicked
onClicked : console.log("Clicked!")
Text {
id: txt
color: "white"
font.pointSize: 8
anchors.centerIn: parent
}
MouseArea {
id: mousearea
anchors.fill: parent
hoverEnabled: true
onEntered: {
txt.color = "yellow"
txt.font.pointSize = 15
}
onExited: {
txt.color = "white"
txt.font.pointSize = 8
}
onClicked: baseButton.clicked
}
}
I'll be very grateful for your help!
Functions (which signals are) are first class objects in JS, so it is not an error to refer to them without parentheses. But you need them in order to execute the function (i.e. emit the signal).
So just change this line:
onClicked: baseButton.clicked()

Resources