From 296422fc636acce0af3afc5824dcc3128f319f7c Mon Sep 17 00:00:00 2001 From: Samuel Nevala Date: Fri, 28 Aug 2015 13:32:55 +0300 Subject: [PATCH] winrt: Add support for native pickers for Windows Phone. Support picking file(s), folder and save file via native component on Windows Phone. Task-Id: QTBUG-44833 Change-Id: Ibffc1cf3c133c9fbf9c92e0a4f874c4e197c50ee Reviewed-by: Oliver Wolff --- .../winrt/qwinrtfiledialoghelper.cpp | 294 +++++++++++++++--- .../platforms/winrt/qwinrtfiledialoghelper.h | 12 +- src/winmain/qtmain_winrt.cpp | 24 +- 3 files changed, 280 insertions(+), 50 deletions(-) diff --git a/src/plugins/platforms/winrt/qwinrtfiledialoghelper.cpp b/src/plugins/platforms/winrt/qwinrtfiledialoghelper.cpp index e1b2a07d5f..05b1fd76b1 100644 --- a/src/plugins/platforms/winrt/qwinrtfiledialoghelper.cpp +++ b/src/plugins/platforms/winrt/qwinrtfiledialoghelper.cpp @@ -37,25 +37,32 @@ #include "qwinrtfiledialoghelper.h" #include "qwinrtfileengine.h" +#include #include #include #include #include +#include +#include #include #include #include +#include using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::ApplicationModel::Activation; using namespace ABI::Windows::Foundation; using namespace ABI::Windows::Foundation::Collections; using namespace ABI::Windows::Storage; using namespace ABI::Windows::Storage::Pickers; +#ifndef Q_OS_WINPHONE typedef IAsyncOperationCompletedHandler SingleFileHandler; typedef IAsyncOperationCompletedHandler *> MultipleFileHandler; typedef IAsyncOperationCompletedHandler SingleFolderHandler; +#endif QT_BEGIN_NAMESPACE @@ -142,6 +149,16 @@ private: QVector impl; }; +#ifdef Q_OS_WINPHONE +class QActivationEvent : public QEvent +{ +public: + IInspectable *args() const { + return reinterpret_cast(d); + } +}; +#endif + template static bool initializePicker(HSTRING runtimeId, T **picker, const QSharedPointer &options) { @@ -200,6 +217,99 @@ static bool initializeOpenPickerOptions(T *picker, const QSharedPointer picker2; + hr = picker->QueryInterface(IID_PPV_ARGS(picker2.GetAddressOf())); + RETURN_HR_IF_FAILED("Failed to cast file picker"); + if (singleFile) + return picker2->PickSingleFileAndContinue(); + else + return picker2->PickMultipleFilesAndContinue(); + }); + RETURN_FALSE_IF_FAILED("Failed to open file picker"); + QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher(); + Q_ASSERT(eventDispatcher); + eventDispatcher->installEventFilter(helper); + return true; +#else + if (singleFile) { + ComPtr> op; + hr = picker->PickSingleFileAsync(&op); + RETURN_FALSE_IF_FAILED("Failed to open single file picker"); + hr = op->put_Completed(Callback(helper, &QWinRTFileDialogHelper::onSingleFilePicked).Get()); + RETURN_FALSE_IF_FAILED("Failed to attach file picker callback"); + } else { + ComPtr *>> op; + hr = picker->PickMultipleFilesAsync(&op); + RETURN_FALSE_IF_FAILED("Failed to open multi file picker"); + hr = op->put_Completed(Callback(helper, &QWinRTFileDialogHelper::onMultipleFilesPicked).Get()); + RETURN_FALSE_IF_FAILED("Failed to attach multi file callback"); + } + return true; +#endif +} + +static bool pickFolder(IFolderPicker *picker, QWinRTFileDialogHelper *helper) +{ + Q_ASSERT(picker); + Q_ASSERT(helper); + HRESULT hr; +#ifdef Q_OS_WINPHONE + hr = QEventDispatcherWinRT::runOnXamlThread([picker]() { + HRESULT hr; + ComPtr picker2; + hr = picker->QueryInterface(IID_PPV_ARGS(picker2.GetAddressOf())); + RETURN_HR_IF_FAILED("Failed to cast folder picker"); + return picker2->PickFolderAndContinue(); + }); + RETURN_FALSE_IF_FAILED("Failed to open folder picker"); + QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher(); + Q_ASSERT(eventDispatcher); + eventDispatcher->installEventFilter(helper); +#else + ComPtr> op; + hr = picker->PickSingleFolderAsync(&op); + RETURN_FALSE_IF_FAILED("Failed to open folder picker"); + hr = op->put_Completed(Callback(helper, &QWinRTFileDialogHelper::onSingleFolderPicked).Get()); + RETURN_FALSE_IF_FAILED("Failed to attach folder picker callback"); +#endif + return true; +} + +static bool pickSaveFile(IFileSavePicker *picker, QWinRTFileDialogHelper *helper) +{ + Q_ASSERT(picker); + Q_ASSERT(helper); + HRESULT hr; +#ifdef Q_OS_WINPHONE + hr = QEventDispatcherWinRT::runOnXamlThread([picker]() { + HRESULT hr; + ComPtr picker2; + hr = picker->QueryInterface(IID_PPV_ARGS(picker2.GetAddressOf())); + RETURN_HR_IF_FAILED("Failed to cast save file picker"); + return picker2->PickSaveFileAndContinue(); + }); + RETURN_FALSE_IF_FAILED("Failed to open single file picker"); + QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher(); + Q_ASSERT(eventDispatcher); + eventDispatcher->installEventFilter(helper); +#else + ComPtr> op; + hr = picker->PickSaveFileAsync(&op); + RETURN_FALSE_IF_FAILED("Failed to open save file picker"); + hr = op->put_Completed(Callback(helper, &QWinRTFileDialogHelper::onSingleFilePicked).Get()); + RETURN_FALSE_IF_FAILED("Failed to attach save file picker callback"); +#endif + return true; +} + class QWinRTFileDialogHelperPrivate { public: @@ -260,18 +370,9 @@ bool QWinRTFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModalit if (!initializeOpenPickerOptions(picker.Get(), dialogOptions)) return false; - if (dialogOptions->fileMode() == QFileDialogOptions::ExistingFiles) { - ComPtr *>> op; - hr = picker->PickMultipleFilesAsync(&op); - RETURN_FALSE_IF_FAILED("Failed to open multi file picker"); - hr = op->put_Completed(Callback(this, &QWinRTFileDialogHelper::onMultipleFilesPicked).Get()); - } else { - ComPtr> op; - hr = picker->PickSingleFileAsync(&op); - RETURN_FALSE_IF_FAILED("Failed to open single file picker"); - hr = op->put_Completed(Callback(this, &QWinRTFileDialogHelper::onSingleFilePicked).Get()); - } - RETURN_FALSE_IF_FAILED("Failed to attach file picker callback"); + if (!pickFiles(picker.Get(), this, dialogOptions->fileMode() == QFileDialogOptions::ExistingFile)) + return false; + break; } case QFileDialogOptions::Directory: @@ -284,11 +385,9 @@ bool QWinRTFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModalit if (!initializeOpenPickerOptions(picker.Get(), dialogOptions)) return false; - ComPtr> op; - hr = picker->PickSingleFolderAsync(&op); - RETURN_FALSE_IF_FAILED("Failed to open folder picker"); - hr = op->put_Completed(Callback(this, &QWinRTFileDialogHelper::onSingleFolderPicked).Get()); - RETURN_FALSE_IF_FAILED("Failed to attach folder picker callback"); + if (!pickFolder(picker.Get(), this)) + return false; + break; } } @@ -344,11 +443,9 @@ bool QWinRTFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModalit RETURN_FALSE_IF_FAILED("Failed to set suggested file name"); } - ComPtr> op; - hr = picker->PickSaveFileAsync(&op); - RETURN_FALSE_IF_FAILED("Failed to open save file picker"); - hr = op->put_Completed(Callback(this, &QWinRTFileDialogHelper::onSingleFilePicked).Get()); - RETURN_FALSE_IF_FAILED("Failed to attach file picker callback"); + if (!pickSaveFile(picker.Get(), this)) + return false; + break; } } @@ -367,6 +464,68 @@ void QWinRTFileDialogHelper::hide() d->shown = false; } +#ifdef Q_OS_WINPHONE +bool QWinRTFileDialogHelper::eventFilter(QObject *, QEvent *e) +{ + if (e->type() != QEvent::WinEventAct) + return false; + + HRESULT hr; + QActivationEvent *event = static_cast(e); + ComPtr inspectable = event->args(); + ComPtr arguments; + hr = inspectable.As(&arguments); + Q_ASSERT_SUCCEEDED(hr); + + ActivationKind activationKind; + hr = arguments->get_Kind(&activationKind); + Q_ASSERT_SUCCEEDED(hr); + + // Handle only File, Folder and Save file pick continuation here. + if (activationKind != ActivationKind_PickFileContinuation + && activationKind != ActivationKind_PickFolderContinuation + && activationKind != ActivationKind_PickSaveFileContinuation) { + return false; + } + + QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher(); + Q_ASSERT(eventDispatcher); + eventDispatcher->removeEventFilter(this); + e->accept(); + + if (activationKind == ActivationKind_PickFileContinuation) { + ComPtr fileContinuationArgs; + hr = arguments.As(&fileContinuationArgs); + Q_ASSERT_SUCCEEDED(hr); + ComPtr> files; + hr = fileContinuationArgs->get_Files(&files); + Q_ASSERT_SUCCEEDED(hr); + hr = onFilesPicked(files.Get()); + Q_ASSERT_SUCCEEDED(hr); + } else if (activationKind == ActivationKind_PickFolderContinuation) { + ComPtr folderContinuationArgs; + hr = arguments.As(&folderContinuationArgs); + Q_ASSERT_SUCCEEDED(hr); + ComPtr folder; + hr = folderContinuationArgs->get_Folder(&folder); + Q_ASSERT_SUCCEEDED(hr); + hr = onFolderPicked(folder.Get()); + Q_ASSERT_SUCCEEDED(hr); + } else { + ComPtr saveFileContinuationArgs; + hr = arguments.As(&saveFileContinuationArgs); + Q_ASSERT_SUCCEEDED(hr); + ComPtr file; + hr = saveFileContinuationArgs->get_File(&file); + Q_ASSERT_SUCCEEDED(hr); + hr = onFilePicked(file.Get()); + Q_ASSERT_SUCCEEDED(hr); + } + + return true; +} +#endif + void QWinRTFileDialogHelper::setDirectory(const QUrl &directory) { Q_D(QWinRTFileDialogHelper); @@ -403,6 +562,7 @@ QString QWinRTFileDialogHelper::selectedNameFilter() const return d->selectedNameFilter; } +#ifndef Q_OS_WINPHONE HRESULT QWinRTFileDialogHelper::onSingleFilePicked(IAsyncOperation *args, AsyncStatus status) { Q_D(QWinRTFileDialogHelper); @@ -419,14 +579,7 @@ HRESULT QWinRTFileDialogHelper::onSingleFilePicked(IAsyncOperation file; hr = args->GetResults(&file); Q_ASSERT_SUCCEEDED(hr); - if (!file) { - emit reject(); - return S_OK; - } - - appendFile(file.Get()); - emit accept(); - return S_OK; + return onFilePicked(file.Get()); } HRESULT QWinRTFileDialogHelper::onMultipleFilesPicked(IAsyncOperation *> *args, AsyncStatus status) @@ -445,23 +598,7 @@ HRESULT QWinRTFileDialogHelper::onMultipleFilesPicked(IAsyncOperation> fileList; hr = args->GetResults(&fileList); RETURN_HR_IF_FAILED("Failed to get file list"); - - quint32 size; - hr = fileList->get_Size(&size); - Q_ASSERT_SUCCEEDED(hr); - if (!size) { - emit reject(); - return S_OK; - } - for (quint32 i = 0; i < size; ++i) { - ComPtr file; - hr = fileList->GetAt(i, &file); - Q_ASSERT_SUCCEEDED(hr); - appendFile(file.Get()); - } - - emit accept(); - return S_OK; + return onFilesPicked(fileList.Get()); } HRESULT QWinRTFileDialogHelper::onSingleFolderPicked(IAsyncOperation *args, AsyncStatus status) @@ -480,12 +617,73 @@ HRESULT QWinRTFileDialogHelper::onSingleFolderPicked(IAsyncOperation folder; hr = args->GetResults(&folder); Q_ASSERT_SUCCEEDED(hr); + return onFolderPicked(folder.Get()); +} +#endif //Q_OS_WINPHONE + +HRESULT QWinRTFileDialogHelper::onFilesPicked(IVectorView *files) +{ +#ifdef Q_OS_WINPHONE + Q_D(QWinRTFileDialogHelper); + QEventLoopLocker locker(&d->loop); + d->shown = false; + d->selectedFiles.clear(); +#endif + + HRESULT hr; + quint32 size; + hr = files->get_Size(&size); + Q_ASSERT_SUCCEEDED(hr); + if (!size) { + emit reject(); + return S_OK; + } + + for (quint32 i = 0; i < size; ++i) { + ComPtr file; + hr = files->GetAt(i, &file); + Q_ASSERT_SUCCEEDED(hr); + appendFile(file.Get()); + } + + emit accept(); + return S_OK; +} + +HRESULT QWinRTFileDialogHelper::onFolderPicked(IStorageFolder *folder) +{ +#ifdef Q_OS_WINPHONE + Q_D(QWinRTFileDialogHelper); + QEventLoopLocker locker(&d->loop); + d->shown = false; + d->selectedFiles.clear(); +#endif + if (!folder) { emit reject(); return S_OK; } - appendFile(folder.Get()); + appendFile(folder); + emit accept(); + return S_OK; +} + +HRESULT QWinRTFileDialogHelper::onFilePicked(IStorageFile *file) +{ +#ifdef Q_OS_WINPHONE + Q_D(QWinRTFileDialogHelper); + QEventLoopLocker locker(&d->loop); + d->shown = false; + d->selectedFiles.clear(); +#endif + + if (!file) { + emit reject(); + return S_OK; + } + + appendFile(file); emit accept(); return S_OK; } diff --git a/src/plugins/platforms/winrt/qwinrtfiledialoghelper.h b/src/plugins/platforms/winrt/qwinrtfiledialoghelper.h index 51b79c84ef..d6bacd2db9 100644 --- a/src/plugins/platforms/winrt/qwinrtfiledialoghelper.h +++ b/src/plugins/platforms/winrt/qwinrtfiledialoghelper.h @@ -47,6 +47,7 @@ namespace ABI { class StorageFile; class StorageFolder; struct IStorageFile; + struct IStorageFolder; } namespace Foundation { enum class AsyncStatus; @@ -71,6 +72,9 @@ public: void exec() Q_DECL_OVERRIDE; bool show(Qt::WindowFlags, Qt::WindowModality, QWindow *) Q_DECL_OVERRIDE; void hide() Q_DECL_OVERRIDE; +#ifdef Q_OS_WINPHONE + bool eventFilter(QObject *o, QEvent *e) Q_DECL_OVERRIDE; +#endif bool defaultNameFilterDisables() const Q_DECL_OVERRIDE { return false; } void setDirectory(const QUrl &directory) Q_DECL_OVERRIDE; @@ -81,13 +85,19 @@ public: void selectNameFilter(const QString &selectedNameFilter) Q_DECL_OVERRIDE; QString selectedNameFilter() const; -private: +#ifndef Q_OS_WINPHONE HRESULT onSingleFilePicked(ABI::Windows::Foundation::IAsyncOperation *, ABI::Windows::Foundation::AsyncStatus); HRESULT onMultipleFilesPicked(ABI::Windows::Foundation::IAsyncOperation *> *, ABI::Windows::Foundation::AsyncStatus); HRESULT onSingleFolderPicked(ABI::Windows::Foundation::IAsyncOperation *, ABI::Windows::Foundation::AsyncStatus); +#endif + +private: + HRESULT onFilesPicked(ABI::Windows::Foundation::Collections::IVectorView *files); + HRESULT onFolderPicked(ABI::Windows::Storage::IStorageFolder *folder); + HRESULT onFilePicked(ABI::Windows::Storage::IStorageFile *file); void appendFile(IInspectable *); QScopedPointer d_ptr; diff --git a/src/winmain/qtmain_winrt.cpp b/src/winmain/qtmain_winrt.cpp index d9f8c8f991..b7125eec59 100644 --- a/src/winmain/qtmain_winrt.cpp +++ b/src/winmain/qtmain_winrt.cpp @@ -70,6 +70,7 @@ extern "C" { #include #include #include +#include #include #include @@ -109,6 +110,24 @@ static void devMessageHandler(QtMsgType type, const QMessageLogContext &context, defaultMessageHandler(type, context, message); } +class QActivationEvent : public QEvent +{ +public: + explicit QActivationEvent(IInspectable *args) + : QEvent(QEvent::WinEventAct) + { + setAccepted(false); + args->AddRef(); + d = reinterpret_cast(args); + } + + ~QActivationEvent() { + IUnknown *args = reinterpret_cast(d); + args->Release(); + d = nullptr; + } +}; + class AppContainer : public RuntimeClass { public: @@ -158,7 +177,10 @@ public: private: HRESULT __stdcall OnActivated(IActivatedEventArgs *args) Q_DECL_OVERRIDE { - return base->OnActivated(args); + QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher(); + if (dispatcher) + QCoreApplication::postEvent(dispatcher, new QActivationEvent(args)); + return S_OK; } HRESULT __stdcall OnLaunched(ILaunchActivatedEventArgs *launchArgs) Q_DECL_OVERRIDE