diff --git a/tools/chatgui/src/member_files/browser.cpp b/tools/chatgui/src/member_files/browser.cpp index c80a9db6..131dc6ac 100644 --- a/tools/chatgui/src/member_files/browser.cpp +++ b/tools/chatgui/src/member_files/browser.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -53,6 +54,26 @@ public: protected: void startDrag(Qt::DropActions) override { exportRequested(); } }; + +// Системный менеджер может запросить URI после drop. До завершения скачивания путь не выдаётся. +class DeferredFileMimeData : public QMimeData { +public: + std::function 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(this)->setUrls({QUrl::fromLocalFile(local)}); + return QMimeData::retrieveData(format, type); + } +}; static QString quotaSize(quint64 bytes) { return bytes <= (quint64)std::numeric_limits::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 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")); diff --git a/tools/chatgui/src/member_files/browser.h b/tools/chatgui/src/member_files/browser.h index 704ce44d..c64bce55 100644 --- a/tools/chatgui/src/member_files/browser.h +++ b/tools/chatgui/src/member_files/browser.h @@ -29,6 +29,8 @@ public: void uploadOwnFiles(const QString& channel, const QStringList& paths); void onResponse(const uint8_t* data, int length); void onTransfer(const uint8_t* data, int length); +signals: + void exportFinished(); protected: bool eventFilter(QObject* target, QEvent* event) override; private: