Перетащите несколько элементов в Qml

Мне нужно нарисовать кучу прямоугольников в моем приложении. Пользователь должен иметь возможность выбрать каждого из них по отдельности, свободно перемещать и менять свою позицию. Пользователь также должен иметь возможность выбрать несколько прямоугольников, переместить все выбранные одновременно и освободить их в другом месте. Я уже мог реализовать что-то на основе Gridview, которое может обрабатывать выделение и перемещение одного прямоугольника, но я не могу заставить это работать для множественного выделения / перемещения. Вот фрагмент кода, который у меня есть в настоящее время:

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 ответа

Решение

Мне не удалось заставить ваш код работать. Во-первых, я вижу ошибки на этом:

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

вам просто нужно удалить родителя Item который бесполезен, и установите Rectangle как корень делегата.

Затем вы забыли упомянуть, что целью перетаскивания является Rectangle

drag.target: parent

Вот код после исправления:

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

Тогда вы не должны использовать GridView потому что вы хотите, чтобы элементы были перемещены. Если вы используете Repeater это работает, и вам просто нужно установить x а также y в Rectangle разместить элементы в начале.

Теперь это решение вашей проблемы: вы нажимаете на элемент, чтобы выбрать его, и вы можете переместить все выбранные элементы одновременно.

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. Использовать RepeaterТаким образом, позиционирование определяется не самим видом, а самим собой.

  2. Используйте невидимого помощника Item, Это ваша drag.target,

  3. Реализуйте предпочитаемый способ выбора объектов - прогноз погоды, щелкнув или нарисовав прямоугольники и выполнив проверку, какие объекты включены в это поле. Сделайте позиции всех выбранных объектов относительно вашего невидимого вспомогательного объекта.

  4. Перетащите вспомогательный объект и соответственно переместите все остальные объекты.

  5. Когда закончите, отмените выбор объектов снова и сделайте их позиции абсолютными (в рамках родительской системы координат)

Рецепт второй:

  1. Переписать все выбранные объекты в новый элемент, сопоставляя их координаты соответственно

  2. Переместить новый предмет

  3. Восстановите все объекты обратно к исходному холсту, сопоставив их координаты.


Я надеюсь, что этого достаточно для решения вашей проблемы (насколько я понял). Если это решит другую проблему, вам нужно будет более конкретно определить ожидаемое поведение ваших перетаскиваемых объектов.

Другие вопросы по тегам