在不破坏 DropArea 的情况下拒绝拖放到 DropArea 中的外部文件

Posted

技术标签:

【中文标题】在不破坏 DropArea 的情况下拒绝拖放到 DropArea 中的外部文件【英文标题】:Reject external files dragged in a DropArea without breaking the DropArea 【发布时间】:2014-04-01 13:23:12 【问题描述】:

在我的应用程序中,我正在显示音频文件列表,用户可以拖动外部文件将其添加到列表中。如果我的应用程序不支持列表中的文件,我希望能够拒绝拖动。

问题是,当我在 onEnteredDropArea 中调用 drag.accepted = false; 时,它对任何其他事件都完全没有响应。

这里是一些显示问题的示例代码。如果您在窗口中拖动 MP3,您会看到它可以正常工作。然后,如果您拖动任何其他文件,它将无法正常工作,正如预期的那样。但随后将MP3 文件拖回也不行。

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"
  


DropArea 中有错误还是我误解了什么?我知道我可以过滤 onDropped 中的文件,但是当您将文件拖动到不接受它们的区域时,您会失去在 OSX 上获得的视觉反馈。

【问题讨论】:

您找到解决方案了吗?我现在正在处理完全相同的问题。 不,我最终接受了文件,然后只过滤了我自己想要的文件 这已在 Qt 5.6 中修复。 【参考方案1】:

很久以来一直是known bug。已经提交了一个补丁,经过几个月的停滞,现在是merged into 5.6 branch


任何想要使用此功能的人都必须升级到 Qt 5.6 或可以将可用的补丁集成到他/她的 Qt 版本中。


QQuickDropAreaPrivate,包含在DropArea 中,当dragEnterEvent 出现时,将containsDrag 标志更新为true,发出entered 信号。当dragLeaveEvent 发生时,它将containsDrag 更新为false,发出exited 信号。但是,当不接受拖动事件时,永远不会调用 dragLeaveEvent,从而使私有对象处于不一致状态。每个后续的dragEnterEvent 都被丢弃,因为containsDrag 仍然是true,即之前的拖动事件仍然被认为是活动的,并且不再发出entered

由于该问题与私有 API 之间的交互和公共 API 的使用有关,因此该问题影响使用 keys 进行过滤。不幸的是,这种方法似乎不适合所呈现的用例。

相当部分的解决方法是使用MouseAreaDropArea。后者在发生拒绝时自行禁用,而前者为下一次丢弃启用DropArea。此解决方法涵盖了将错误项目放入DropArea 的常见情况,这对于最终用户来说是最常见和最直观的。在DropArea 之外释放错误的项目会使机制失效(直到下一次丢弃)。

代码如下:

import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Window 2.0

ApplicationWindow 
    title: qsTr("Hello World")
    width: 640
    height: 480
    visible: true

    MouseArea 
        anchors.fill: parent
        hoverEnabled: true
        enabled: !drop.enabled
        onContainsMouseChanged: drop.enabled = true
    

    DropArea 
        id: drop
        anchors.fill: parent

        onEntered: 
            console.log("[Droparea] entered");
            // Ensure at least one file is supported before accepted the drag
            for(var i = 0; i < drag.urls.length; i++)
                if(validateFileExtension(drag.urls[i]))
                    return
            console.log("No valid files, refusing drag event")
            drag.accept()
            drop.enabled = false
        

        onExited: console.log("[Droparea] exited")

        onDropped: console.log("[Droparea] dropped")

        // Only MP3s
        function validateFileExtension(filePath) 
            return filePath.split('.').pop() == "mp3"
        
    

    Text 
        id: textDrop
        anchors.centerIn: parent
        text: "Please drag element"
    

【讨论】:

【参考方案2】:

你从来没有把accepteed = true

将valid设置为valid后只需添加drag.accepted = true

for(var i = 0; i < drag.urls.length; i++) 
    if(validateFileExtension(drag.urls[i])) 
      validFile = true;
      drag.accepted = true;
      break;
    
  

【讨论】:

已经尝试过了,它并没有改变任何东西。接受拖动的行为与什么都不做一样

以上是关于在不破坏 DropArea 的情况下拒绝拖放到 DropArea 中的外部文件的主要内容,如果未能解决你的问题,请参考以下文章

如何在不破坏文件的情况下重命名xcode项目中的文件夹名称

ipad:将 UITableViewCell 从一个 UITableViewController 拖放到另一个

Android 啥时候会在不破坏整个进程的情况下破坏活动?

如何在不破坏遗留代码的情况下增加函数的返回值?

在不破坏保留元素的情况下更改向量的大小

在不破坏管道的情况下与进程多次通信?