diff options
Diffstat (limited to 'src/plugins/platforms/wasm')
| -rw-r--r-- | src/plugins/platforms/wasm/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/plugins/platforms/wasm/qwasmdom.cpp | 79 | ||||
| -rw-r--r-- | src/plugins/platforms/wasm/qwasmfiledialoghelper.cpp | 157 | ||||
| -rw-r--r-- | src/plugins/platforms/wasm/qwasmfiledialoghelper.h | 49 | ||||
| -rw-r--r-- | src/plugins/platforms/wasm/qwasmtheme.cpp | 13 | ||||
| -rw-r--r-- | src/plugins/platforms/wasm/qwasmtheme.h | 3 | ||||
| -rw-r--r-- | src/plugins/platforms/wasm/qwasmwindow.cpp | 2 |
7 files changed, 269 insertions, 35 deletions
diff --git a/src/plugins/platforms/wasm/CMakeLists.txt b/src/plugins/platforms/wasm/CMakeLists.txt index 03cb0d52ca1..7e9beb7e832 100644 --- a/src/plugins/platforms/wasm/CMakeLists.txt +++ b/src/plugins/platforms/wasm/CMakeLists.txt @@ -33,6 +33,7 @@ qt_internal_add_plugin(QWasmIntegrationPlugin qwasmwindownonclientarea.cpp qwasmwindownonclientarea.h qwasminputcontext.cpp qwasminputcontext.h qwasmwindowstack.h + qwasmfiledialoghelper.cpp qwasmfiledialoghelper.h DEFINES QT_EGL_NO_X11 QT_NO_FOREACH diff --git a/src/plugins/platforms/wasm/qwasmdom.cpp b/src/plugins/platforms/wasm/qwasmdom.cpp index 71efcee7887..eefd18154aa 100644 --- a/src/plugins/platforms/wasm/qwasmdom.cpp +++ b/src/plugins/platforms/wasm/qwasmdom.cpp @@ -10,6 +10,7 @@ #include <QtCore/qrect.h> #include <QtGui/qimage.h> #include <private/qstdweb_p.h> +#include <private/qwasmlocalfileengine_p.h> #include <QtCore/qurl.h> #include <utility> @@ -112,11 +113,14 @@ void DataTransfer::toMimeDataWithFile(std::function<void(QMimeData *)> callback) m_callback(mimeData); - // Delete files; we expect that the user callback reads/copies - // file content before returning. - // Fixme: tie file lifetime to lifetime of the QMimeData? - for (QUrl fileUrl: fileUrls) - QFile(fileUrl.toLocalFile()).remove(); + // Delete temporary files; we expect that the user callback reads/copies + // file content before returning.// Fixme: tie file lifetime to lifetime of the QMimeData? + // Note: QWasmFileEngine files (weblocalfile://) are managed by QWasmFileEngine + // and are not deleted here + for (QUrl fileUrl: fileUrls) { + if (!QWasmFileEngineHandler::isWasmFileName(fileUrl.toString())) + QFile(fileUrl.toLocalFile()).remove(); + } delete this; } @@ -144,49 +148,56 @@ void DataTransfer::toMimeDataWithFile(std::function<void(QMimeData *)> callback) case ItemKind::File: { qstdweb::File webfile(item.call<emscripten::val>("getAsFile")); - if (webfile.size() > 1e+9) { // limit file size to 1 GB - qWarning() << "File is too large (> 1GB) and will be skipped. File size is" << webfile.size(); + // Add a file access url for the local file. If asyncify is available, + // add a QWasmFileEngine managed url. Else fall back to placing a copy + // of the file at /tmp on Emsripten's in-memory file system. + if (qstdweb::haveAsyncify()) { + QUrl fileUrl(QWasmFileEngineHandler::addFile(webfile)); + mimeContext->fileUrls.append(fileUrl); mimeContext->deref(); - continue; - } + } else { + // Limit in-memory file size to 1 GB + if (webfile.size() > 1e+9) { + qWarning() << "File is too large (> 1GB) and will be skipped. File size is" << webfile.size(); + mimeContext->deref(); + continue; + } - QString mimeFormat = QString::fromStdString(webfile.type()); - QString fileName = QString::fromStdString(webfile.name()); + // Read file content + QByteArray fileContent(webfile.size(), Qt::Uninitialized); + webfile.stream(fileContent.data(), [=]() { + QDir qtTmpDir("/qt/tmp/"); // "tmp": indicate that these files won't stay around + qtTmpDir.mkpath(qtTmpDir.path()); + + QUrl fileUrl = QUrl::fromLocalFile(qtTmpDir.filePath(QString::fromStdString(webfile.name()))); + mimeContext->fileUrls.append(fileUrl); + + QFile file(fileUrl.toLocalFile()); + if (!file.open(QFile::WriteOnly)) { + qWarning() << "File was not opened"; + mimeContext->deref(); + return; + } + if (file.write(fileContent) < 0) + qWarning() << "Write failed"; + file.close(); + mimeContext->deref(); + }); - // there's a file, now read it - QByteArray fileContent(webfile.size(), Qt::Uninitialized); - webfile.stream(fileContent.data(), [=]() { - // If we get a single file, and that file is an image, then // try to decode the image data. This handles the case where // image data (i.e. not an image file) is pasted. The browsers // will then create a fake "image.png" file which has the image - // data. As a side effect Qt will also decode the image for + // data. As a side effect Qt will also decode the image for // single-image-file drops, since there is no way to differentiate // the fake "image.png" from a real one. + QString mimeFormat = QString::fromStdString(webfile.type()); if (fileCount == 1 && mimeFormat.contains("image/")) { QImage image; if (image.loadFromData(fileContent)) mimeContext->mimeData->setImageData(image); } - - QDir qtTmpDir("/qt/tmp/"); // "tmp": indicate that these files won't stay around - qtTmpDir.mkpath(qtTmpDir.path()); - - QUrl fileUrl = QUrl::fromLocalFile(qtTmpDir.filePath(QString::fromStdString(webfile.name()))); - mimeContext->fileUrls.append(fileUrl); - - QFile file(fileUrl.toLocalFile()); - if (!file.open(QFile::WriteOnly)) { - qWarning() << "File was not opened"; - mimeContext->deref(); - return; - } - if (file.write(fileContent) < 0) - qWarning() << "Write failed"; - file.close(); - mimeContext->deref(); - }); + } break; } case ItemKind::String: diff --git a/src/plugins/platforms/wasm/qwasmfiledialoghelper.cpp b/src/plugins/platforms/wasm/qwasmfiledialoghelper.cpp new file mode 100644 index 00000000000..1e6e2b1f644 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmfiledialoghelper.cpp @@ -0,0 +1,157 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "qwasmfiledialoghelper.h" + +#include <QtCore/QDebug> +#include <QtCore/QUrl> +#include <QtGui/private/qwasmlocalfileaccess_p.h> +#include <QtCore/private/qwasmlocalfileengine_p.h> + +QT_BEGIN_NAMESPACE + +QWasmFileDialogHelper::QWasmFileDialogHelper() + : m_eventLoop(nullptr) +{ + +} + +QWasmFileDialogHelper::~QWasmFileDialogHelper() +{ + +} + +bool QWasmFileDialogHelper::defaultNameFilterDisables() const +{ + return false; +} + +void QWasmFileDialogHelper::setDirectory(const QUrl &directory) +{ + Q_UNUSED(directory) +} + +QUrl QWasmFileDialogHelper::directory() const +{ + return QUrl(); +} + +void QWasmFileDialogHelper::selectFile(const QUrl &file) +{ + m_selectedFiles.clear(); + m_selectedFiles.append(file); +} + +QList<QUrl> QWasmFileDialogHelper::selectedFiles() const +{ + return m_selectedFiles; +} + +void QWasmFileDialogHelper::setFilter() +{ + +} + +void QWasmFileDialogHelper::selectNameFilter(const QString &filter) +{ + Q_UNUSED(filter); + // TODO +} + +QString QWasmFileDialogHelper::selectedNameFilter() const +{ + return QString(); +} + +void QWasmFileDialogHelper::exec() +{ + QEventLoop eventLoop; + m_eventLoop = &eventLoop; + eventLoop.exec(); + m_eventLoop = nullptr; +} + +bool QWasmFileDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) +{ + Q_UNUSED(flags) + Q_UNUSED(modality) + Q_UNUSED(parent) + showFileDialog(); + return true; +} + +void QWasmFileDialogHelper::hide() +{ + +} + +void QWasmFileDialogHelper::showFileDialog() +{ + if (options()->acceptMode() == QFileDialogOptions::AcceptOpen) { + // Use name filters from options + QString nameFilter = options()->nameFilters().join(";;"); + if (nameFilter.isEmpty()) + nameFilter = "*"; + + QWasmLocalFileAccess::showOpenFileDialog(nameFilter.toStdString(), [this](bool accepted, std::vector<qstdweb::File> files) { + onOpenDialogClosed(accepted, files); + }); + } else if (options()->acceptMode() == QFileDialogOptions::AcceptSave) { + QString suggestion = m_selectedFiles.isEmpty() ? QString() : QUrl(m_selectedFiles.first()).fileName(); + m_selectedFiles.clear(); + + QWasmLocalFileAccess::showSaveFileDialog(suggestion.toStdString(), [this](bool accepted, qstdweb::FileSystemFileHandle file){ + onSaveDialogClosed(accepted, file); + }); + } +} + +void QWasmFileDialogHelper::onOpenDialogClosed(bool accepted, std::vector<qstdweb::File> files) +{ + m_selectedFiles.clear(); + + if (!accepted) { + emit reject(); + return; + } + + // Track opened files + for (const auto &file : files) { + QString wasmFileName = QWasmFileEngineHandler::addFile(file); + QUrl fileUrl(wasmFileName); + m_selectedFiles.append(fileUrl); + } + + // Emit signals + if (m_selectedFiles.size() > 0) { + emit fileSelected(m_selectedFiles.first()); + emit filesSelected(m_selectedFiles); + } + emit accept(); + + // exit exec() if in exec() + if (m_eventLoop) + m_eventLoop->quit(); +} + +void QWasmFileDialogHelper::onSaveDialogClosed(bool accepted, qstdweb::FileSystemFileHandle file) +{ + if (!accepted) { + emit reject(); + return; + } + + // Track save file + QString wasmFileName = QWasmFileEngineHandler::addFile(file); + QUrl fileUrl(wasmFileName); + m_selectedFiles.append(fileUrl); + + // Emit signals + emit fileSelected(m_selectedFiles.first()); + emit accept(); + + if (m_eventLoop) + m_eventLoop->quit(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmfiledialoghelper.h b/src/plugins/platforms/wasm/qwasmfiledialoghelper.h new file mode 100644 index 00000000000..c5a5b57e518 --- /dev/null +++ b/src/plugins/platforms/wasm/qwasmfiledialoghelper.h @@ -0,0 +1,49 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef QWASMFILEDIALOGHELPER_H +#define QWASMFILEDIALOGHELPER_H + +#include <QtCore/QObject> +#include <QtCore/QUrl> +#include <QtCore/QEventLoop> +#include <QtGui/qpa/qplatformdialoghelper.h> +#include <QtGui/private/qwasmlocalfileaccess_p.h> + +QT_BEGIN_NAMESPACE + +class QWasmFileDialogHelper : public QPlatformFileDialogHelper +{ + Q_OBJECT +public: + QWasmFileDialogHelper(); + ~QWasmFileDialogHelper(); +public: + virtual void exec() override; + virtual bool show(Qt::WindowFlags windowFlags, + Qt::WindowModality windowModality, + QWindow *parent) override; + virtual void hide() override; + virtual bool defaultNameFilterDisables() const override; + virtual void setDirectory(const QUrl &directory) override; + virtual QUrl directory() const override; + virtual void selectFile(const QUrl &filename) override; + virtual QList<QUrl> selectedFiles() const override; + virtual void setFilter() override; + virtual void selectNameFilter(const QString &filter) override; + virtual QString selectedNameFilter() const override; + static QStringList cleanFilterList(const QString &filter); +signals: + void fileDone(const QUrl &); +private: + void showFileDialog(); + void onOpenDialogClosed(bool accepted, std::vector<qstdweb::File> files); + void onSaveDialogClosed(bool accepted, qstdweb::FileSystemFileHandle file); + + QList<QUrl> m_selectedFiles; + QEventLoop *m_eventLoop; +}; + +QT_END_NAMESPACE + +#endif // QWASMFILEDIALOGHELPER_H diff --git a/src/plugins/platforms/wasm/qwasmtheme.cpp b/src/plugins/platforms/wasm/qwasmtheme.cpp index b9340f31275..b1e5c208c6c 100644 --- a/src/plugins/platforms/wasm/qwasmtheme.cpp +++ b/src/plugins/platforms/wasm/qwasmtheme.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qwasmtheme.h" +#include "qwasmfiledialoghelper.h" #include <QtCore/qvariant.h> #include <QFontDatabase> #include <QList> @@ -127,6 +128,18 @@ const QFont *QWasmTheme::font(Font type) const return nullptr; } +bool QWasmTheme::usePlatformNativeDialog(DialogType type) const +{ + return (type == DialogType::FileDialog); +} + +QPlatformDialogHelper *QWasmTheme::createPlatformDialogHelper(DialogType type) const +{ + if (type == DialogType::FileDialog) + return new QWasmFileDialogHelper(); + return nullptr; +} + void QWasmTheme::onColorSchemeChange() { auto colorScheme = getColorSchemeFromMedia(); diff --git a/src/plugins/platforms/wasm/qwasmtheme.h b/src/plugins/platforms/wasm/qwasmtheme.h index 4eaad874c76..8b8dd6ebd97 100644 --- a/src/plugins/platforms/wasm/qwasmtheme.h +++ b/src/plugins/platforms/wasm/qwasmtheme.h @@ -57,6 +57,9 @@ public: Qt::ContrastPreference contrastPreference() const override; QVariant themeHint(ThemeHint hint) const override; const QFont *font(Font type) const override; + bool usePlatformNativeDialog(DialogType type) const override; + QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const override; + QFont *fixedFont = nullptr; void onColorSchemeChange(); diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp index 6e8bd46ca58..7f1dd7eb34c 100644 --- a/src/plugins/platforms/wasm/qwasmwindow.cpp +++ b/src/plugins/platforms/wasm/qwasmwindow.cpp @@ -925,7 +925,7 @@ bool QWasmWindow::deliverPointerEvent(const PointerEvent &event) std::back_inserter(touchPointList), [](const QWindowSystemInterface::TouchPoint &val) { return val; }); - if (event.type == EventType::PointerUp) + if (event.type == EventType::PointerUp || event.type == EventType::PointerCancel) m_pointerIdToTouchPoints.remove(event.pointerId); return event.type == EventType::PointerCancel |
