Unsolved GridView - Issues with scrolling and internal Drag&Drop
-
With Qt 5.15.2 and QtQuick controls 2.0, I'm trying to create a photo view, which may support a vertical scrolling to navigate between many items, and allows also any item to be swapped with an internal drag&drop.
I have a solution which roughly works. Below is the code.
WQTMainForm.qml
import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.15 /** * Application main window *@author JMR */ Window { // advanced properties property var m_Model: ListModel {} // common properties visible: true width: 640 height: 480 title: qsTr("Photo view") /** * Background rect */ Rectangle { // common properties anchors.fill: parent anchors.margins: 10 /** * Background mouse area */ MouseArea { // common properties anchors.fill: parent hoverEnabled: true /** * Photo grid view */ GridView { // common properties id: gvPhotos //interactive: false anchors.fill: parent cellWidth: 120 cellHeight: 120 flickableDirection: Flickable.VerticalFlick keyNavigationEnabled: true highlightFollowsCurrentItem: true activeFocusOnTab: false focus: true clip: true // model model: m_Model // item delegate delegate: WQTPhotoItem {} /** * Vertical scrollbar */ ScrollBar.vertical: ScrollBar { // common properties id: sbFileListView parent: gvPhotos visible: true minimumSize: 0.1 } /** * Grid mouse area (for drag&drop) */ MouseArea { // advanced properties property int rowCount: parseInt(gvPhotos.width / gvPhotos.cellWidth) property int mX: (mouseX < 0.0) ? 0 : (mouseX > gvPhotos.width) ? gvPhotos.width : mouseX property int mY: (mouseY < 0.0) ? 0 : (mouseY > gvPhotos.height) ? gvPhotos.width : mouseY property int index: parseInt((gvPhotos.contentY + mY) / gvPhotos.cellHeight) * rowCount + parseInt(mX / gvPhotos.cellWidth) property int activeIndex: -1 // common properties id: maGridView anchors.fill: parent hoverEnabled: true drag.axis: Drag.XandYAxis // called when the mouse button is pressed and maintained for a small time onPressAndHold: { gvPhotos.currentIndex = index; gvPhotos.currentItem.z = 10; drag.target = gvPhotos.currentItem; activeIndex = index; } // called when the mouse button is released after a press (and hold) onReleased: { var targetIndex = index; if (targetIndex < 0) targetIndex = 0; else if (targetIndex >= gvPhotos.count) targetIndex = gvPhotos.count - 1; gvPhotos.currentItem.x = gvPhotos.cellWidth * (targetIndex % rowCount); gvPhotos.currentItem.y = gvPhotos.cellHeight * parseInt(targetIndex / rowCount); gvPhotos.currentItem.z = 1; //REM console.log(targetIndex + " - " + gvPhotos.currentItem.z);//REM gvPhotos.currentIndex = -1; activeIndex = -1; drag.target = null; } // called when mouse cursor position changed in the mouse area onPositionChanged: { if (drag.active && index >= 0 && index < gvPhotos.count && index !== activeIndex) gvPhotos.model.move(activeIndex, activeIndex = index, 1); } } } // called when the mouse enters in the grid onEntered: function() { console.log("Mouse entered the grid"); } // called when the mouse leaves the grid onExited: function() { console.log("Mouse exited the grid"); } } } /** * Called after the main window object was created */ Component.onCompleted: { // fill the grid view for (var i = 0; i < 1000; ++i) gvPhotos.model.append({"icon": "Images/widget" + ((i % 9) + 1) + ".png", "gridId": i}); } }
WQTPhotoItem:
import QtQuick 2.15 /** * Photo item *@author JMR */ Component { /** * Item */ Item { // advanced properties property bool m_Active: false // common properties id: itItem width: gvPhotos.cellWidth height: gvPhotos.cellHeight /** * Item background */ Rectangle { // common properties /* x: itItem.x; y: itItem.y width: itItem.width; height: itItem.height; parent: gvPhotos */ anchors.fill: parent color: "darkgray" /** * Item content background */ Rectangle { // common properties anchors.fill: parent anchors.margins: 5 color: "black" /** * Photo */ Image { // common properties id: imImage anchors.fill: parent fillMode: Image.PreserveAspectFit smooth: true source: icon /** * Photo outline */ Rectangle { anchors.fill: parent border.color: "#326487" border.width: 6 color: "transparent" radius: 5 } /** * Photo wobbling animation */ SequentialAnimation on rotation { NumberAnimation {to: 2; duration: 60} NumberAnimation {to: -2; duration: 120} NumberAnimation {to: 0; duration: 60} running: maGridView.activeIndex !== -1 && !m_Active loops: Animation.Infinite alwaysRunToEnd: true } /** * Item state */ states: State { name: "active" when: maGridView.activeIndex === index PropertyChanges { target: imImage x: maGridView.mouseX - width / 2 y: maGridView.mouseY - height / 2 scale: 0.5 z: 10 } } /** * Animation transitions */ transitions: Transition { NumberAnimation { property: "scale" duration: 200 } } } } /** * Photo x animation */ /* Behavior on x { enabled: !m_Active NumberAnimation { duration: 400 easing.type: Easing.OutBack } } */ /** * Photo y animation */ /* Behavior on y { enabled: !m_Active NumberAnimation { duration: 400 easing.type: Easing.OutBack } } */ } } }
The above view works as long as no special case happens. However there are several remaining issues I cannot solve, among which:
- When I try to drag an image before the first item or after the last item, the scroll continues infinitely, and the drop behavior becomes inconsistent and random.
- I cannot animate the x and y positions. I found several examples about how to do that, which I tried to implement (it's the commented code). However this solution works as long as the view isn't scrolled. But it breaks the scrolling completely.
Can someone explain me what is wrong in my code, and how I should correct it to fix the still existing issues described above?
Also I need to add a multi-selection in the above view, how can I achieve that?
And finally, is there a better/more simpler way to reach the above described view? Or an existing example about a similar view?
-
I could find a solution for the issue #2 mentioned above, by using the
moveDisplaced
property of theGridView
component, instead of trying to animate the items directly, like that:// animate the item move, when its x and/or y position changes moveDisplaced: Transition { NumberAnimation { properties: "x,y" duration: 200 } }
However the other questions remain opened.