QMLにおけるドラッグ&ドロップ

Drag & Drop on QML
QMLへDropAreaを組み込むことでドラッグ&ドロップを実装することができます。

[テキストやファイルのドロップ]
テキストやファイルなどのOSからのドロップであれば、DropAreaのenteredとdroppedシグナルでデータの受け入れを制御できます。

onEnteredではdragプロパティ(DragEventタイプ)が使えます。対象がファイルなら「hasUrl」を、テキストなら「hasText」の値を調べ、条件に一致しないようであれば「drag.accepted = false;」とすることでデータのドロップを禁止します。

onDroppedではdropプロパティが使用できます。こちらも実体はDragEventなので「drop.text」などからデータを改めて取得します。
textdrop.qml
Rectangle {
    x: 100
    y: 210
    width: 100
    height: 100
    color: "blue"

    DropArea {
        id: dta
        anchors.fill: parent

        onEntered:{
            // drag.accepted = false;
        }

        onDropped:{
            if (drop.hasUrls){
                lbl.text = drop.urls.toString();
            }else if (drop.hasText) {
                lbl.text = drop.text;
            }
        }
    }
}

[QMLアイテムのドラッグ&ドロップ]
QMLアイテムをドラッグできるようにするには、アイテムの「Drag.active」プロパティをtrueにします。Dragを有効にしてもマウス操作が使えるようになるわけではないので、MouseAreaを追加してドラッグさせたいアイテムと連動させる必要があります。

DropAreaで受け入れることのできるQMLアイテムを限定したいのであれば「keys」プロパティを使用します。DropAreaの「keys」配列内の文字列のいずれかと、ドラッグしたアイテムに含まれる「Drag.keys」配列内のいずれかが一致すると、ドロップ操作が受け入れられるようになります。


これらを踏まえたコード例がこちらになります。緑と青色の矩形は両方ともDropAreaですが、緑色にはkeysが指定されているので、ウィンドウにある赤色の矩形しかドラッグ&ドロップを受け付けません。
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Drag and Drop")

    Text{
         id: lbl
         wrapMode: Text.Wrap
         width: parent.width
         text: "test"
    }

    DropArea {
        id: da
        x: 100
        y: 100
        width: 100
        height: 100

        Rectangle {
            id: rc
            anchors.fill: parent
            color: "green"
        }

        // ドラッグキーに「test」を持つものだけを受け入れる
        keys:["test"]

        states: [
            // ドラッグ&ドロップできるアイテムがあれば見た目を変えるようにする例
            State{
                when: da.containsDrag
                PropertyChanges {
                    target: rc
                    color: "yellow"
                }
            }
        ]
    }

    Rectangle {
        x: 100
        y: 210
        width: 100
        height: 100
        color: "blue"

        DropArea {
            id: dta
            anchors.fill: parent

            onEntered:{
                // drag.accepted = false;
            }

            onDropped:{
                if (drop.hasUrls){
                    lbl.text = drop.urls.toString();
                }else if (drop.hasText) {
                    lbl.text = drop.text;
                }
            }
        }
    }

    Rectangle {
        x: 210
        y: 100
        width: 20
        height: 20
        color: "red"

        Drag.active: true
        Drag.keys: ["test"]

        MouseArea {
            anchors.fill: parent
            // ドラッグを連動させたいアイテム
            drag.target: parent
            // ドラッグできるx方向の最小値と最大値
            drag.minimumX: 100
            drag.maximumX: 500
            // QMLアイテムのドロップはMouseArea側で実装する必要がある
            onReleased: {
                if(parent.Drag.target === da){
                    lbl.text = "Dropped";
                }
            }
        }
    }
}
2018/11/02