Browse Source

Deliver remote file drags in one gesture using deferred MIME

master
evgeny 3 days ago
parent
commit
4049a3f884
  1. 49
      tools/chatgui/src/member_files/browser.cpp
  2. 2
      tools/chatgui/src/member_files/browser.h

49
tools/chatgui/src/member_files/browser.cpp

@ -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"));

2
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:

Loading…
Cancel
Save