This article will introduce how to implement drag and drop function in Qml. The following steps are required to drag and release the control:
- Add a mouse response event for the control and listen for the horizontal and vertical coordinates of mouse movement.
- Move the control position according to the position of the mouse.
- Modify the host of the control.
Handle mouse events
To handle mouse events, we can add a MouseArea object to the qml file. This object has position and size, but it is not visible. Here, to demonstrate the use of MouseArea, we first add a click event listener.
Rectangle{ width: 100 height: 100 color: "green" MouseArea{ anchors.fill:parent onClicked: { console.debug("onClick MouseArea") } } }
In order to implement the drag and drop function, we need to listen for more specific events. Including press event, move event and release event.
MouseArea{ anchors.fill:parent onClicked: { console.debug("onClick MouseArea") } onPressed: { console.debug("onPressed x:",mouseX," y:",mouseY) } onPositionChanged: { console.debug("onPositionChanged x:",mouseX," y:",mouseY) } onReleased: { console.debug("onReleased x:",mouseX," y:",mouseY) } }
onPressed monitors the mouse press event, onPositionChanged monitors the mouse position change, and onRelease monitors the mouse release event.
Move control
We can move the control by changing the x and y coordinates of the control. Here, we assign mouseX and mouseY directly to the control.
Rectangle{ id:rect width: 100 height: 100 color: "green" MouseArea{ anchors.fill:parent onClicked: { console.debug("onClick MouseArea") } onPressed: { console.debug("onPressed x:",mouseX," y:",mouseY) rect.x = mouseX rect.y = mouseY } onPositionChanged: { console.debug("onPositionChanged x:",mouseX," y:",mouseY) rect.x = mouseX rect.y = mouseY } onReleased: { console.debug("onReleased x:",mouseX," y:",mouseY) rect.x = mouseX rect.y = mouseY } } }
After running the drag experiment, we found that the rectangular box kept jumping, which was far from the structure we expected. The MouseArea here is inside the rectangle. The position of the rectangular box is constantly modified during dragging. The position transformation of the rectangular box affects the values of mouseX and mouseY, so the rectangular box jumps badly. How to solve this problem?
- The values of mouseX and mouseY cannot be used directly because they depend on the position of the rectangle.
- The movement of the rectangle is controlled by the offset in the x and y directions.
Use the mapToGlobal method of Item to convert mouseX and mouseY into globalX and globalY to avoid affecting the MouseArea position movement calculation after modifying the rectangular position. Move the rectangle by recording the position offset twice before and after.
Rectangle{ id:rect width: 100 height: 100 x:300 y:300 color: "green" MouseArea{ anchors.fill:parent property real lastX: 0 property real lastY: 0 onClicked: { console.debug("onClick MouseArea") } onPressed: { var coordinate = mapToGlobal(mouseX,mouseY) lastX = coordinate.x lastY = coordinate.y } onPositionChanged: { var coordinate = mapToGlobal(mouseX,mouseY) var offsetX = coordinate.x - lastX var offsetY = coordinate.y - lastY lastX = coordinate.x lastY = coordinate.y rect.x += offsetX rect.y += offsetY } onReleased: { var coordinate = mapToGlobal(mouseX,mouseY) var offsetX = coordinate.x - lastX var offsetY = coordinate.y - lastY lastX = coordinate.x lastY = coordinate.y rect.x += offsetX rect.y += offsetY } } }
After the above modification, the rectangle dragging process becomes particularly smooth.
Modify host
Modifying the host refers to modifying the parent of the rectangular box to change the object tree structure and rendering tree structure.
First, when dragging and releasing, we need to determine whether to modify the host of the rectangle and which host to use. This process needs to be judged according to the coordinate position when the mouse is released.
function dockToHost(dragItem, globalX, globalY){ var local = host1.mapFromGlobal(globalX,globalY) if(host1.contains(local)){ dragItem.x = 0 dragItem.y = 0 dragItem.z = 100 dragItem.parent = host1 return } local = host2.mapFromGlobal(globalX, globalY) if(host2.contains(local)){ dragItem.x = 0 dragItem.y = 0 dragItem.z = 100 dragItem.parent = host2 return } } Rectangle{ id:host1 width: 120 height: 120 x:100 y:100 color: "red" } Rectangle{ id:host2 width: 150 height: 150 x:220 y:220 color: "blue" }
When the mouse is released, the dockToHost method is called to determine which host the rectangle is changed to. Here, the mapFromGlobal method of item is also called to convert the global coordinates into the local coordinates of item, and then judge whether it is in the item area. After finding the host item, modify the parent of the rectangular box to the host item and reset the coordinates of the rectangular box.
summary
After the above steps, we can implement a simple control drag function, and by dragging the control, we can change the host item of the control. This is mainly applied to the MouseArea control to monitor mouse events and modify the position by changing the x and y offsets of the rectangular box. At the same time, in order that the movement of the rectangular box does not affect mouseX and mouseY of MouseArea, we use mapToGlobal and mapFromGlobal methods to realize the mutual conversion of global coordinates and local coordinates. Then we modify the parent of the rectangle to change the host of the rectangle and dynamically add controls to the host item.