Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

QML: GridView with movable cells (drag and drop)

I am following the approach given on http://qt-project.org/wiki/Drag_and_Drop_within_a_GridView .

I have the presented program. Drag and drop is working, but after the drop I get empty space in my grid. How can I improve this?

Moreover, I have also noticed that function indexAt() in line 46 not working correctly during the drag operation. Thus I had to add explicit calculation of gridArea.index.

(EDITED) I seems to me that the empty space appears because after droping the element it is returned to its original position (it is below the visible element). GridView is not updated and finish in an irregular state.

import QtQuick 1.1

GridView {
    id: mainGrid
    cellWidth: 165; cellHeight: 95
    width: 5*cellWidth; height: 4*cellHeight
    model: myModel
    delegate: myButton

    ListModel {
        id: myModel
        function createModel() {
            for (var i=1; i<=20; i++) {
                myModel.append({"display": i, "uid": i})
            }
        }
        Component.onCompleted: {createModel()}
    }

    Component {
        id: myButton
        Rectangle {
            id: item
            width: mainGrid.cellWidth-5; height: mainGrid.cellHeight-5;
            border.width: 1
            property int uid: (index >= 0) ? myModel.get(index).uid : -1
            Text {
                anchors.centerIn: parent
                text: display
                font.pixelSize: 48
            }
            states: [
                State {
                    name: "active"; when: gridArea.activeId == item.uid
                    PropertyChanges {target: item; x: gridArea.mouseX-80; y: gridArea.mouseY-45; z: 10; smooth: false}
                }
            ]
        }
    }

    MouseArea {
        id: gridArea
        anchors.fill: parent
        hoverEnabled: true
        preventStealing : true
        //property int index: mainGrid.indexAt(mouseX, mouseY) //WHY IS THIS NOT WORKING RELIABLE?
        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 activeId: -1 //uid of active item
        property int activeIndex //current position of active item
        onPressAndHold: {
            activeId = mainGrid.model.get(activeIndex=index).uid
        }
        onReleased: {
            activeId = -1
        }
        onPositionChanged: {
            if (activeId != -1 && index != -1 && index != activeIndex) {
                mainGrid.model.move(activeIndex, activeIndex = index, 1)
            }
        }
    }
}
like image 584
meolic Avatar asked Aug 31 '12 12:08

meolic


1 Answers

Here, I give the working code. I have added additional hiearchy and put Rectangle into Item. Moreover, I had to reparent Rectangle. There are many similar solutions available, e.g. here.

In the given solution, there is no problem with function indexAt() and thus explicit calculation of gridArea.index is not needed anymore.

I will appreciate any comment on this solution and why these changes are so important. I still think that the original solution is intuitive and I do not understand why it is not working.

import QtQuick 1.1

GridView {
    id: mainGrid
    cellWidth: 165; cellHeight: 95
    width: 5*cellWidth; height: 4*cellHeight
    model: myModel
    delegate: myButton

    ListModel {
        id: myModel
        function createModel() {
            for (var i=1; i<=20; i++) {
                myModel.append({"display": i, "uid": i})
            }
        }
        Component.onCompleted: {createModel()}
    }

    Component {
        id: myButton
        Item {
            id: item
            width: mainGrid.cellWidth-5; height: mainGrid.cellHeight-5;
            Rectangle {
                id: box
                parent: mainGrid
                x: item.x; y: item.y;
                width: item.width; height: item.height;
                border.width: 1
                property int uid: (index >= 0) ? myModel.get(index).uid : -1
                Text {
                    anchors.centerIn: parent
                    text: display
                    font.pixelSize: 48
                }
                states: [
                    State {
                        name: "active"; when: gridArea.activeId == box.uid
                        PropertyChanges {target: box; x: gridArea.mouseX-80; y: gridArea.mouseY-45; z: 10}
                    }
                ]
            }
        }
    }

    MouseArea {
        id: gridArea
        anchors.fill: parent
        hoverEnabled: true
        preventStealing : true
        property int index: mainGrid.indexAt(mouseX, mouseY) //item underneath cursor
        property int activeId: -1 //uid of active item
        property int activeIndex //current position of active item

        onPressAndHold: {
            activeId = mainGrid.model.get(activeIndex=index).uid
        }
        onReleased: {
            activeId = -1
        }
        onPositionChanged: {
            if (activeId != -1 && index != -1 && index != activeIndex) {
                mainGrid.model.move(activeIndex, activeIndex = index, 1)
            }
        }
    }
}
like image 167
meolic Avatar answered Sep 26 '22 03:09

meolic