How to implement drag release control in Qt

This article will introduce how to implement drag and drop function in Qml. The following steps are required to drag and release the control:

  1. Add a mouse response event for the control and listen for the horizontal and vertical coordinates of mouse movement.
  2. Move the control position according to the position of the mouse.
  3. 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?

  1. The values of mouseX and mouseY cannot be used directly because they depend on the position of the rectangle.
  2. 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.

Tags: Qt event

Posted on Sun, 05 Dec 2021 13:32:38 -0500 by cjliu42