Drag a proxy (drag.target) out of a clipped sub view



  • I must say I am enjoying QtQuick a fair bit and I am making good progress. However, I have stumbled accross another puzzler. I hope the example is not getting too complicated, but I want to provide some context as to what I am trying to do and have self-contained code.

    Essentially, I have my StackView working perfectly now, allowing me to browse through different ListViews. Now, my problem is that I would like each item in those ListViews to have a handle, which can be used to drag ListItems via proxy from the ListView into another View (e.g. let's say a shopping basket). However, since the proxy is defined as a child, it is getting clipped by the ancestor Rectangle. Now, please note that the appearance of the proxy depends on the Item from which the drag was initiated, so I cannot simply avoid the problem by making one "global" proxy. Conceptually, each ListItem should have its own proxy.

    import QtQuick 2.5
    import QtQuick.Controls 1.4
    import QtQuick.Window 2.2
    
    Window {
        id: root
        width: 800; height: 600
        visible: true
    
        ListModel {
            id: fruitModel
    
            ListElement {
                title: "Apple"
                elementColor: "green"
            }
            ListElement {
                title: "Orange"
                elementColor: "orange"
            }
            ListElement {
                title: "Banana"
                elementColor: "yellow"
            }
        }
    
        ListView {
            id: fruitList
            model: fruitModel
            delegate: Item {
                width: parent.width; height: 80
                Rectangle {
                    anchors.fill: parent
                    color: "lightblue"
                    Text {
                        anchors.fill: parent
                        color: "black"
                        text: title
                    }
                    Rectangle
                    {
                        id: dragHandle
                        anchors.right: parent.right
                        anchors.rightMargin: 10
                        width: 60; height: 60
                        color: elementColor
    
                        MouseArea {
                            id: dragArea
                            anchors.fill: parent
    
                            drag.target: dragProxy
    
                            Rectangle
                            {
                                id: dragProxy
                                width: 60; height: 60
                                color: elementColor
    
                                //Drag.active: parent.drag.active
    
                                Drag.dragType: Drag.Automatic
    
                                property bool dragActive: dragArea.drag.active
    
                                onDragActiveChanged: {
                                    if (dragActive) {
                                        console.log("started")
                                        Drag.start();
                                    } else {
                                        console.log("finished")
                                        // Reset position for next drag
                                        dragProxy.x = dragArea.x;
                                        dragProxy.y = dragArea.y;
                                        Drag.drop();
                                    }
                                }
                                visible: parent.drag.active
                            }
                        }
                    }
                }
            }
            visible: false
        }
    
        Item {
            anchors.top: aTitleBar.bottom; anchors.left: aTitleBar.left
            width: 300; height: 480
            clip: true
    
            StackView {
                id: aStackView
                anchors.fill: parent
    
                initialItem: Rectangle {
                    color: "lightblue"
                    Text {
                        anchors.fill: parent
                        color: "black"
                        text: "Fruit"
                    }
                    MouseArea {
                        id: mouse
                        anchors.fill: parent
                        onClicked: aStackView.push({ item: fruitList, properties: { text: title, color: color }})
                    }
                }
            }
        }
    
        Rectangle {
            id: aTitleBar
            x: 50; y: 20
            width: 300; height: 80
            color: "darkblue"
            Text {
                anchors.fill: parent
                color: "white"
                text: "Store"
            }
            MouseArea {
                anchors.fill: parent
                onClicked: aStackView.pop()
            }
        }
    
        Rectangle {
            id: aBasket
            x: 450; y: 20
            width: 300; height: 560
            color: "lightblue"
            Text {
                anchors.fill: parent
                color: "black"
                text: "Basket"
            }
            DropArea {
                anchors.fill: parent
                onEntered: console.log("entered")
                onExited: console.log("exited")
                onDropped: console.log("dropped")
            }
        }
    }
    

    Any suggestions on how to proceed?



  • Ok, maybe the example was a bit too complicated. Here is a simplified version:

    import QtQuick 2.5
    import QtQuick.Controls 1.4
    import QtQuick.Window 2.2
    
    Window {
        id: root
        width: 800; height: 600
        visible: true
    
        Rectangle {
            id: aDragSource
            x: 50; y: 20
            width: 300; height: 560
            color: "lightblue"
    
            clip: true
    
            Text {
                anchors.fill: parent
                color: "black"
                text: "Source"
            }
    
            Rectangle
            {
                id: aDragHandle
                anchors.right: parent.right
                anchors.rightMargin: 10
                width: 60; height: 60
                color: "red"
    
                MouseArea {
                    id: aDragArea
                    anchors.fill: parent
    
                    drag.target: aDragProxy
    
                    Rectangle
                    {
                        id: aDragProxy
                        width: 60; height: 60
                        color: "red"
    
                        //Drag.active: parent.drag.active
    
                        Drag.dragType: Drag.Automatic
    
                        property bool dragActive: aDragArea.drag.active
    
                        onDragActiveChanged: {
                            if (dragActive) {
                                console.log("started")
                                Drag.start();
                            } else {
                                console.log("finished")
                                // Reset position for next drag
                                aDragProxy.x = aDragArea.x;
                                aDragProxy.y = aDragArea.y;
                                Drag.drop();
                            }
                        }
                        visible: parent.drag.active
                    }
                }
            }
        }
    
        Rectangle {
            id: aDropTraget
            x: 450; y: 20
            width: 300; height: 560
            color: "lightblue"
    
            Text {
                anchors.fill: parent
                color: "black"
                text: "Target"
            }
    
            DropArea {
                anchors.fill: parent
                onEntered: console.log("entered")
                onExited: console.log("exited")
                onDropped: console.log("dropped")
            }
        }
    }
    

    This illustrates the basic problem. Ideally, I would like to define the proxy locally as is, but have it rendered on top of the scene and not be clipped by any of its ancestors. For more on the motivation behind this, have a look at the first example. Any feedback/help would be very much appreciated!



  • Well, this is proving much more difficult than I had expected. Since all my attempts to solve the problem in a declarative way have failed, I turned to programmatically creating the proxy element:

    import QtQuick 2.5
    import QtQuick.Controls 1.4
    import QtQuick.Window 2.2
    
    Window {
        id: window
        width: 800; height: 600
        visible: true
    
        Item
        {
            id: root
            anchors.fill: parent
    
            Rectangle {
                id: aDropTraget
                x: 450; y: 20
                width: 300; height: 560
                color: "lightblue"
    
                Text {
                    anchors.fill: parent
                    color: "black"
                    text: "Target"
                }
    
                DropArea {
                    anchors.fill: parent
                    onEntered: console.log("entered")
                    onExited: console.log("exited")
                    onDropped: {
                        console.log(drop.hasText, drop.text)
                        console.log("dropped")
                    }
                }
            }
    
            Rectangle
            {
                id: aDragHandle
                x: 10; y: 80
                width: 60; height: 60
                color: "red"
    
                MouseArea {
                    id: aDragArea
                    anchors.fill: parent
    
                    property var dragComponent: null
                    property var dragProxy: null
    
                    function moveProxy(mouse) {
                        var pos = root.mapFromItem(aDragHandle, mouse.x, mouse.y);
                        dragProxy.x = pos.x - dragProxy.height/2;
                        dragProxy.y = pos.y - dragProxy.width/2;
                    }
    
                    onPressed: {
                        if (!dragProxy) {
                            dragProxy = dragComponent.createObject(root, { "width": aDragHandle.width, "height": aDragHandle.height, "color": "orange" });
                            dragProxy.Drag.mimeData = { "text/plain": "Hello Drag!" };
                        }
    
                        dragProxy.Drag.start();
                        moveProxy(mouse);
                    }
    
                    onPositionChanged: {
                        moveProxy(mouse);
                    }
    
                    onReleased: {
                        moveProxy(mouse);
                        dragProxy.Drag.drop();
    
                        if (dragProxy) {
                            dragProxy.destroy();
                            dragProxy = null;
                        }
                    }
    
                    Component.onCompleted: {
                        dragComponent = Qt.createComponent("controls/DragProxy.qml");
                        if (dragComponent.status === Component.Error) {
                            console.log("Error creating drag component.");
                            console.log(dragComponent.errorString());
                        }
                    }
                }
            }
        }
    }
    

    At first, this almost looks like it is working. However, the mime data is always empty, which means there is no way to send any information to the DropArea, rendering the whole thing useless. I am honestly at the end of my wits here...


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.