1

I need to draw bunch of Rectangles in my application. The user should be able to select each of them individually and move them freely and change their position. The user also should be able to select multiple Rectangles and move all the selected one simultaneously and release them somewhere else. I could already implement something based on Gridview that can handle selection and movement of one Rectangle, but I cannot make it work for multiple selection/movement. Here is a snippet of code I have currently:

GridView { 
        id: mainGrid
        cellWidth: 7;
        cellHeight: 7;

        ListModel {
            id: myModel
            function createModel() {
                for(var i = 0; i < totalZoneList.length; i++)
                {
                    for (var j = 0; j < moduleZoneList.length; j++)
                    {
                         myModel.append({"item1": ITEM1, "item2": ITEM2})
                    }
                }
            }
            Component.onCompleted: {createModel()}
        }

        Component { 
            id: myblocks
            Item {
                id: item
                width: mainGrid.cellWidth; 
                height: mainGrid.cellHeight;
                Rectangle {
                    id: box
                    parent: mainGrid
                    x: //CALCULATED BASED ON MODEL
                    y: //CALCULATED BASED ON MODEL
                    width: //CALCULATED BASED ON MODEL
                    height: //CALCULATED BASED ON MODEL


                    MouseArea {
                        id: gridArea
                        anchors.fill: parent
                        hoverEnabled: true
                        drag.axis: Drag.XandYAxis
                        drag.minimumX: 0
                        drag.minimumY: 0


                        property int mX: (mouseX < 0) ? 0 : ((mouseX < mainGrid.width - mainGrid.cellWidth) ? mouseX : mainGrid.width - mainGrid.cellWidth)
                        property int mY: (mouseY < 0) ? 0 : ((mouseY < mainGrid.height - mainGrid.cellHeight) ? mouseY : mainGrid.height - mainGrid.cellHeight)
                        property int index: parseInt(mX/mainGrid.cellWidth) + 5*parseInt(mY/mainGrid.cellHeight)  //item underneath cursor
                        property int activeIndex
                        property var xWhenPressed
                        property var yWhenPressed
                        propagateComposedEvents: true

                        onPressed: {
                            activeIndex = index
                            drag.target = box
                            xWhenPressed = box.x
                            yWhenPressed = box.y

                            gridArea.drag.maximumX = mainGrid.width - box.width
                            gridArea.drag.maximumY = mainGrid.height - box.height
                        }
                        onReleased: {
                           if(xWhenPressed !== box.x || yWhenPressed !== box.y)
                            {
                              //RECALCULATE THE POSITION
                            }
                        }
                        onPositionChanged: {
                            if (drag.active && index !== -1 && index !== activeIndex) {
                                                            mainGrid.model.move(activeIndex, activeIndex = index, 1)
                            }
                        }
                    } // Mousearea
                } // Rectangle
            } // Item
        } // Component

    } //mainGrid

2 Answers 2

2

I didn't manage to have your code working. First, I see mistakes on it:

Rectangle {
    id: box
    parent: mainGrid
...
}

you just need to remove the parent Item which is of no use, and set the Rectangle as root of the delegate.

Then, you forgot to mention that the target of drag is the Rectangle

drag.target: parent

Here is the code after correcting:

Component {
    id: myblocks

    Rectangle {
        id: box
        color: "red"
        width: 20
        height: 20

        MouseArea {
            id: gridArea
            anchors.fill: parent
            drag.target: parent
            hoverEnabled: true
            drag.axis: Drag.XandYAxis
        } // Mousearea
    } // Rectangle
} // Component

Then, you shouldn't use a GridView because you want elements to be moved. If you use a Repeater it works, and you just have to set x and y in the Rectangle to place the elements at the beginning.

Now this is a solution for your problem: you click on an element to select it, and you can move all selected items at once.

Window {
    visible: true
    width: 640
    height: 480

    property var totalZoneList: ["total1", "total2"]
    property var moduleZoneList: ["module1", "module2"]

    Repeater{
        id: iRepeater
        model: ListModel {
                    id: myModel
                    function createModel() {
                        for(var i = 0; i < totalZoneList.length; i++)
                        {
                            for (var j = 0; j < moduleZoneList.length; j++)
                            {
                                myModel.append({"item1": totalZoneList[i], "item2": moduleZoneList[j]})
                            }
                        }
                    }
                    Component.onCompleted: {createModel()}
                }
        delegate: myblocks

    }

    Component {
        id: myblocks

        Rectangle {
            id: box
            color: {
                switch(index){
                case 0: selected ? "red" : "#FF9999";break;
                case 1: selected ? "blue" : "lightblue";break;
                case 2: selected ? "green" : "lightgreen";break;
                case 3: selected ? "grey" : "lightgrey";break;
                }
            }
            x: (width + 5)*index

            width: 20
            height: 20
            property int offsetX:0
            property int offsetY:0
            property bool selected: false
            function setRelative(pressedRect){
                disableDrag();
                x = Qt.binding(function (){ return pressedRect.x + offsetX; })
                y = Qt.binding(function (){ return pressedRect.y + offsetY; })
            }
            function enableDrag(){
                gridArea.drag.target = box
            }
            function disableDrag(){
                gridArea.drag.target = null
            }

            MouseArea {
                id: gridArea
                anchors.fill: parent
                hoverEnabled: true
                drag.axis: Drag.XandYAxis
                onClicked: parent.selected=!parent.selected

                onPressed: {

                    var pressedRect = iRepeater.itemAt(index);
                    if (pressedRect.selected == true){
                        for (var i=0; i<iRepeater.count; i++ ){
                            var rect = iRepeater.itemAt(i);
                            if (i != index){
                                //init for breaking existing binding
                                rect.x = rect.x
                                rect.y = rect.y
                                rect.disableDrag()
                                if (rect.selected == true){
                                    rect.offsetX = rect.x - pressedRect.x
                                    rect.offsetY = rect.y - pressedRect.y
                                    rect.setRelative(pressedRect)
                                }
                            }
                        }
                        pressedRect.enableDrag()
                    }
                }
            } // Mousearea
        } // Rectangle
    } // Component
}
1
  • thanks for the reply, but I don't understand how that can help moving multiple items. As I said, I don't want to move all the Rectangles. Sometimes just one of them, sometimes a subset of them! Commented Oct 5, 2016 at 11:42
1

Recipe One:

  1. Use a Repeater, so the positioning is not determined by a view but by yourself.

  2. Use an invisible helper Item. This is your drag.target.

  3. Implement your prefered way of selecting your objects - weather by clicking, or drawing boxes and performing a check which objects are included in this box. Make the positions of all selected objects relative to your invisible helper object.

  4. Drag the helper object, and move all other objects accordingly.

  5. When finished, unselect the objects again and make their positions absolute (within the parents coordinate system)

Recipe Two:

  1. Reparent all selected Objects to a new Item, while mapping their coordinates accordingly

  2. Move the new Item

  3. Reparen all objects back to the original canvas, mapping their coordinates back.


I hope this is sufficient to solve your problem (as far as I understood it) If it solves another problem, than you need to be more specific about the expected behavior of your draged objects.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.