In my application I'm displaying a list of audio files and the user can drag an external file to add it to the list. I want to be able to refuse the drag if no file in the list is supported by my application.
The issue is that when I call drag.accepted = false;
in onEntered
of my DropArea
then it becomes completely unresponsive to any other event.
Here is some sample code showing the issue. If you drag an MP3 in the window you see that it works. Then if you drag any other file it won't work, as expected. But then dragging an MP3
file back will not work either.
import QtQuick 2.1
import QtQuick.Window 2.0
ApplicationWindow {
title: qsTr("Hello World")
width: 640
height: 480
DropArea {
anchors.fill: parent
onEntered: {
console.log("[Droparea] entered");
// Ensure at least one file is supported before accepted the drag
var validFile = false;
for(var i = 0; i < drag.urls.length; i++) {
if(validateFileExtension(drag.urls[i])) {
validFile = true;
break;
}
}
if(!validFile) {
console.log("No valid files, refusing drag event");
drag.accepted = false;
return false;
}
}
onExited: {
console.log("[Droparea] entered");
}
onDropped: {
console.log("[Droparea] dropped");
}
// Only MP3s
function validateFileExtension(filePath) {
var extension = filePath.split('.').pop();
var valid = false;
if(extension == "mp3") {
valid = true;
}
return valid;
}
}
Text {
id: textDrop
anchors.centerIn: parent
text: "Please drag element"
}
}
Is there a bug in the DropArea
or did I misunderstood something? I know I can filter the files in the onDropped but then you loose the visual feedback you get on OSX when dragging file on an area that does not accept them.
you never put accepteed = true
just add drag.accepted = true after you set the valid as valid
It has been a known bug for a long time. A patch has been submitted and after been stalled for several months is now merged into 5.6 branch.
Anyone who wants to use this functionality MUST upgrade to Qt 5.6 or MANULLY integrate the available patch into his/her Qt version.
QQuickDropAreaPrivate
, contained inDropArea
, updates thecontainsDrag
flag totrue
when adragEnterEvent
occurs, emitting theentered
signal. It updatescontainsDrag
tofalse
when adragLeaveEvent
occurs, emitting anexited
signal. However, when the drag event is not accepteddragLeaveEvent
is never called, leaving the private object in a incosistent state. Each subsequentdragEnterEvent
is discarded sincecontainsDrag
is stilltrue
, i.e. the previous drag event is still considered active and theentered
is no more emitted.Since the issue is related to an interaction between private APIs and usage of the public APIs, the problem does not affect filtering using
keys
. Unfortunately, this approach does not seem to fit for the presented use case.A quite partial workaround is to use a
MouseArea
along with theDropArea
. The latter disables itself when a rejection occurs while the former enables back theDropArea
for the next drop. This workaround covers the common case in which a wrong item is dropped inside theDropArea
, which is the most common and intuitive for an end user. Releasing the wrong item outside theDropArea
invalidate the mechanism (until the next drop).Here's the code: