diff options
author | Samuel Nevala <samuel.nevala@intopalo.com> | 2015-08-28 13:32:55 +0300 |
---|---|---|
committer | Samuel Nevala <samuel.nevala@intopalo.com> | 2015-09-07 06:05:05 +0000 |
commit | 296422fc636acce0af3afc5824dcc3128f319f7c (patch) | |
tree | 01cae38fe545f6e39289760b3fd88d5cf1d90624 | |
parent | 5953109a04e16df1abb87d37a676a0a51e7a2eca (diff) |
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 <oliver.wolff@theqtcompany.com>
-rw-r--r-- | src/plugins/platforms/winrt/qwinrtfiledialoghelper.cpp | 280 | ||||
-rw-r--r-- | src/plugins/platforms/winrt/qwinrtfiledialoghelper.h | 12 | ||||
-rw-r--r-- | src/winmain/qtmain_winrt.cpp | 24 |
3 files changed, 273 insertions, 43 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 <QtCore/qcoreapplication.h> #include <QtCore/QEventLoop> #include <QtCore/QMap> #include <QtCore/QVector> #include <QtCore/qfunctions_winrt.h> +#include <private/qeventdispatcher_winrt_p.h> +#include <functional> #include <wrl.h> #include <windows.foundation.h> #include <windows.storage.pickers.h> +#include <Windows.ApplicationModel.activation.h> 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<StorageFile *> SingleFileHandler; typedef IAsyncOperationCompletedHandler<IVectorView<StorageFile *> *> MultipleFileHandler; typedef IAsyncOperationCompletedHandler<StorageFolder *> SingleFolderHandler; +#endif QT_BEGIN_NAMESPACE @@ -142,6 +149,16 @@ private: QVector<HSTRING> impl; }; +#ifdef Q_OS_WINPHONE +class QActivationEvent : public QEvent +{ +public: + IInspectable *args() const { + return reinterpret_cast<IInspectable *>(d); + } +}; +#endif + template<typename T> static bool initializePicker(HSTRING runtimeId, T **picker, const QSharedPointer<QFileDialogOptions> &options) { @@ -200,6 +217,99 @@ static bool initializeOpenPickerOptions(T *picker, const QSharedPointer<QFileDia return true; } +static bool pickFiles(IFileOpenPicker *picker, QWinRTFileDialogHelper *helper, bool singleFile) +{ + Q_ASSERT(picker); + Q_ASSERT(helper); + HRESULT hr; +#ifdef Q_OS_WINPHONE + hr = QEventDispatcherWinRT::runOnXamlThread([picker, singleFile]() { + HRESULT hr; + ComPtr<IFileOpenPicker2> 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<IAsyncOperation<StorageFile *>> op; + hr = picker->PickSingleFileAsync(&op); + RETURN_FALSE_IF_FAILED("Failed to open single file picker"); + hr = op->put_Completed(Callback<SingleFileHandler>(helper, &QWinRTFileDialogHelper::onSingleFilePicked).Get()); + RETURN_FALSE_IF_FAILED("Failed to attach file picker callback"); + } else { + ComPtr<IAsyncOperation<IVectorView<StorageFile *> *>> op; + hr = picker->PickMultipleFilesAsync(&op); + RETURN_FALSE_IF_FAILED("Failed to open multi file picker"); + hr = op->put_Completed(Callback<MultipleFileHandler>(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<IFolderPicker2> 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<IAsyncOperation<StorageFolder *>> op; + hr = picker->PickSingleFolderAsync(&op); + RETURN_FALSE_IF_FAILED("Failed to open folder picker"); + hr = op->put_Completed(Callback<SingleFolderHandler>(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<IFileSavePicker2> 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<IAsyncOperation<StorageFile *>> op; + hr = picker->PickSaveFileAsync(&op); + RETURN_FALSE_IF_FAILED("Failed to open save file picker"); + hr = op->put_Completed(Callback<SingleFileHandler>(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<IAsyncOperation<IVectorView<StorageFile *> *>> op; - hr = picker->PickMultipleFilesAsync(&op); - RETURN_FALSE_IF_FAILED("Failed to open multi file picker"); - hr = op->put_Completed(Callback<MultipleFileHandler>(this, &QWinRTFileDialogHelper::onMultipleFilesPicked).Get()); - } else { - ComPtr<IAsyncOperation<StorageFile *>> op; - hr = picker->PickSingleFileAsync(&op); - RETURN_FALSE_IF_FAILED("Failed to open single file picker"); - hr = op->put_Completed(Callback<SingleFileHandler>(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<IAsyncOperation<StorageFolder *>> op; - hr = picker->PickSingleFolderAsync(&op); - RETURN_FALSE_IF_FAILED("Failed to open folder picker"); - hr = op->put_Completed(Callback<SingleFolderHandler>(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<IAsyncOperation<StorageFile *>> op; - hr = picker->PickSaveFileAsync(&op); - RETURN_FALSE_IF_FAILED("Failed to open save file picker"); - hr = op->put_Completed(Callback<SingleFileHandler>(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<QActivationEvent *>(e); + ComPtr<IInspectable> inspectable = event->args(); + ComPtr<IActivatedEventArgs> 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<IFileOpenPickerContinuationEventArgs> fileContinuationArgs; + hr = arguments.As(&fileContinuationArgs); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IVectorView<StorageFile *>> files; + hr = fileContinuationArgs->get_Files(&files); + Q_ASSERT_SUCCEEDED(hr); + hr = onFilesPicked(files.Get()); + Q_ASSERT_SUCCEEDED(hr); + } else if (activationKind == ActivationKind_PickFolderContinuation) { + ComPtr<IFolderPickerContinuationEventArgs> folderContinuationArgs; + hr = arguments.As(&folderContinuationArgs); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IStorageFolder> folder; + hr = folderContinuationArgs->get_Folder(&folder); + Q_ASSERT_SUCCEEDED(hr); + hr = onFolderPicked(folder.Get()); + Q_ASSERT_SUCCEEDED(hr); + } else { + ComPtr<IFileSavePickerContinuationEventArgs> saveFileContinuationArgs; + hr = arguments.As(&saveFileContinuationArgs); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IStorageFile> 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<StorageFile *> *args, AsyncStatus status) { Q_D(QWinRTFileDialogHelper); @@ -419,14 +579,7 @@ HRESULT QWinRTFileDialogHelper::onSingleFilePicked(IAsyncOperation<StorageFile * ComPtr<IStorageFile> 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<IVectorView<StorageFile *> *> *args, AsyncStatus status) @@ -445,17 +598,50 @@ HRESULT QWinRTFileDialogHelper::onMultipleFilesPicked(IAsyncOperation<IVectorVie ComPtr<IVectorView<StorageFile *>> fileList; hr = args->GetResults(&fileList); RETURN_HR_IF_FAILED("Failed to get file list"); + return onFilesPicked(fileList.Get()); +} + +HRESULT QWinRTFileDialogHelper::onSingleFolderPicked(IAsyncOperation<StorageFolder *> *args, AsyncStatus status) +{ + Q_D(QWinRTFileDialogHelper); + + QEventLoopLocker locker(&d->loop); + d->shown = false; + d->selectedFiles.clear(); + if (status == Canceled || status == Error) { + emit reject(); + return S_OK; + } + + HRESULT hr; + ComPtr<IStorageFolder> folder; + hr = args->GetResults(&folder); + Q_ASSERT_SUCCEEDED(hr); + return onFolderPicked(folder.Get()); +} +#endif //Q_OS_WINPHONE + +HRESULT QWinRTFileDialogHelper::onFilesPicked(IVectorView<StorageFile *> *files) +{ +#ifdef Q_OS_WINPHONE + Q_D(QWinRTFileDialogHelper); + QEventLoopLocker locker(&d->loop); + d->shown = false; + d->selectedFiles.clear(); +#endif + HRESULT hr; quint32 size; - hr = fileList->get_Size(&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<IStorageFile> file; - hr = fileList->GetAt(i, &file); + hr = files->GetAt(i, &file); Q_ASSERT_SUCCEEDED(hr); appendFile(file.Get()); } @@ -464,28 +650,40 @@ HRESULT QWinRTFileDialogHelper::onMultipleFilesPicked(IAsyncOperation<IVectorVie return S_OK; } -HRESULT QWinRTFileDialogHelper::onSingleFolderPicked(IAsyncOperation<StorageFolder *> *args, AsyncStatus status) +HRESULT QWinRTFileDialogHelper::onFolderPicked(IStorageFolder *folder) { +#ifdef Q_OS_WINPHONE Q_D(QWinRTFileDialogHelper); - QEventLoopLocker locker(&d->loop); d->shown = false; d->selectedFiles.clear(); - if (status == Canceled || status == Error) { +#endif + + if (!folder) { emit reject(); return S_OK; } - HRESULT hr; - ComPtr<IStorageFolder> folder; - hr = args->GetResults(&folder); - Q_ASSERT_SUCCEEDED(hr); - if (!folder) { + 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(folder.Get()); + 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::Storage::StorageFile *> *, ABI::Windows::Foundation::AsyncStatus); HRESULT onMultipleFilesPicked(ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Foundation::Collections::IVectorView<ABI::Windows::Storage::StorageFile *> *> *, ABI::Windows::Foundation::AsyncStatus); HRESULT onSingleFolderPicked(ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::StorageFolder *> *, ABI::Windows::Foundation::AsyncStatus); +#endif + +private: + HRESULT onFilesPicked(ABI::Windows::Foundation::Collections::IVectorView<ABI::Windows::Storage::StorageFile *> *files); + HRESULT onFolderPicked(ABI::Windows::Storage::IStorageFolder *folder); + HRESULT onFilePicked(ABI::Windows::Storage::IStorageFile *file); void appendFile(IInspectable *); QScopedPointer<QWinRTFileDialogHelperPrivate> 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 <qdir.h> #include <qstandardpaths.h> #include <qfunctions_winrt.h> +#include <qcoreapplication.h> #include <wrl.h> #include <Windows.ApplicationModel.core.h> @@ -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<QEventPrivate *>(args); + } + + ~QActivationEvent() { + IUnknown *args = reinterpret_cast<IUnknown *>(d); + args->Release(); + d = nullptr; + } +}; + class AppContainer : public RuntimeClass<Xaml::IApplicationOverrides> { 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 |