Deliver remote file drags in one gesture using deferred MIME
This commit is contained in:
1 parent
e8ca27b590
commit
4049a3f884
2 files changed
+50
-1
No files matched your search
@@ -33,6 +33,7 @@
|
||||
#include <QDir>
|
||||
#include <QTemporaryDir>
|
||||
#include <QStandardPaths>
|
||||
#include <QEventLoop>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <cstring>
|
||||
@@ -53,6 +54,26 @@ public:
|
||||
protected:
|
||||
void startDrag(Qt::DropActions) override { exportRequested(); }
|
||||
};
|
||||
|
||||
// Системный менеджер может запросить URI после drop. До завершения скачивания путь не выдаётся.
|
||||
class DeferredFileMimeData : public QMimeData {
|
||||
public:
|
||||
std::function<QString()> localFile;
|
||||
QStringList formats() const override { return {QStringLiteral("text/uri-list")}; }
|
||||
bool hasFormat(const QString& format) const override { return format == QStringLiteral("text/uri-list"); }
|
||||
protected:
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
QVariant retrieveData(const QString& format, QMetaType type) const override {
|
||||
#else
|
||||
QVariant retrieveData(const QString& format, QVariant::Type type) const override {
|
||||
#endif
|
||||
if (!hasFormat(format)) return {};
|
||||
QString local = localFile();
|
||||
if (local.isEmpty()) return {};
|
||||
const_cast<DeferredFileMimeData*>(this)->setUrls({QUrl::fromLocalFile(local)});
|
||||
return QMimeData::retrieveData(format, type);
|
||||
}
|
||||
};
|
||||
static QString quotaSize(quint64 bytes) {
|
||||
return bytes <= (quint64)std::numeric_limits<qint64>::max() ? QLocale().formattedDataSize((qint64)bytes, 2) :
|
||||
text("%1 bytes").arg(QLocale().toString(bytes));
|
||||
@@ -585,9 +606,33 @@ void MemberFileBrowser::exportFile(bool clipboard) {
|
||||
if (local.isEmpty() || !startTransfer(remote, local, false, version)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "file browser: export could not start remote=%s local=%s", qPrintable(remote), qPrintable(local));
|
||||
m_exportId = 0; updateActions(); m_status->setText(text("Cannot prepare a local copy"));
|
||||
emit exportFinished();
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
}));
|
||||
if (!clipboard) {
|
||||
QPointer<MemberFileBrowser> guard(this);
|
||||
auto* mime = new DeferredFileMimeData;
|
||||
mime->localFile = [guard, id, key] {
|
||||
if (!guard) return QString();
|
||||
if (guard->m_exportId == id) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "file browser: drag waiting for local copy id=%llu", (unsigned long long)id);
|
||||
QEventLoop wait;
|
||||
QObject::connect(guard, &MemberFileBrowser::exportFinished, &wait, &QEventLoop::quit);
|
||||
QObject::connect(guard, &QObject::destroyed, &wait, &QEventLoop::quit);
|
||||
wait.exec();
|
||||
}
|
||||
QString local = guard ? guard->m_exportCache.value(key) : QString();
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "file browser: drag data ready id=%llu local=%s", (unsigned long long)id, qPrintable(local));
|
||||
return local;
|
||||
};
|
||||
auto* drag = new QDrag(m_entries); drag->setParent(QCoreApplication::instance()); drag->setMimeData(mime);
|
||||
// MIME живёт до возврата из native drag, даже если окно файлов закрыли во вложенном цикле событий.
|
||||
connect(this, &QObject::destroyed, drag, [] { QDrag::cancel(); });
|
||||
Qt::DropAction action = drag->exec(Qt::CopyAction, Qt::CopyAction);
|
||||
if (guard && m_exportId == id) cancel();
|
||||
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "file browser: native drag finished action=%d", int(action));
|
||||
}
|
||||
}
|
||||
|
||||
bool MemberFileBrowser::startTransfer(const QString& remote, const QString& local, bool uploading, const QByteArray& version) {
|
||||
@@ -623,8 +668,9 @@ void MemberFileBrowser::onTransfer(const uint8_t* data, int length) {
|
||||
m_exportCache.insert(m_exportKey, m_local);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "file browser: local copy ready path=%s", qPrintable(m_local));
|
||||
if (m_exportClipboard) publishExport(m_local, true);
|
||||
else m_status->setText(text("Local copy ready. Drag the file again, or use Copy file."));
|
||||
else m_status->setText(text("Local copy ready"));
|
||||
}
|
||||
emit exportFinished();
|
||||
return;
|
||||
}
|
||||
if (m_uploadBatchId) {
|
||||
@@ -675,6 +721,7 @@ void MemberFileBrowser::cancel() {
|
||||
if (m_exportId) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "file browser: export cancelled id=%llu", (unsigned long long)m_exportId);
|
||||
m_exportId = 0; m_status->setText(text("Copy cancelled")); updateActions();
|
||||
emit exportFinished();
|
||||
}
|
||||
if (m_dropId) { m_dropId = 0; m_uploadStatus->setText(text("Upload queue cancelled")); updateActions(); }
|
||||
if (m_uploadBatchId) finishUploads(text("Upload queue cancelled"));
|
||||
|
||||
Reference in new issue
Block a user