/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qwinrtmessagedialoghelper.h" #include "qwinrttheme.h" #include #include #include #include #include #include #include #include using namespace ABI::Windows::Foundation; using namespace ABI::Windows::Foundation::Collections; using namespace ABI::Windows::UI::Popups; using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; typedef IAsyncOperationCompletedHandler DialogCompletedHandler; QT_BEGIN_NAMESPACE class CommandId : public RuntimeClass { public: CommandId(QPlatformDialogHelper::StandardButton button) : button(button) { } QPlatformDialogHelper::StandardButton button; }; class QWinRTMessageDialogHelperPrivate { public: const QWinRTTheme *theme; bool shown; ComPtr info; QEventLoop loop; }; QWinRTMessageDialogHelper::QWinRTMessageDialogHelper(const QWinRTTheme *theme) : QPlatformMessageDialogHelper(), d_ptr(new QWinRTMessageDialogHelperPrivate) { Q_D(QWinRTMessageDialogHelper); d->theme = theme; d->shown = false; } QWinRTMessageDialogHelper::~QWinRTMessageDialogHelper() { Q_D(QWinRTMessageDialogHelper); if (d->shown) hide(); } void QWinRTMessageDialogHelper::exec() { Q_D(QWinRTMessageDialogHelper); if (!d->shown) show(Qt::Dialog, Qt::ApplicationModal, 0); d->loop.exec(); } bool QWinRTMessageDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) { Q_UNUSED(windowFlags) Q_UNUSED(windowModality) Q_UNUSED(parent) Q_D(QWinRTMessageDialogHelper); QSharedPointer options = this->options(); if (!options.data()) return false; const QString informativeText = options->informativeText(); const QString title = options->windowTitle(); const QString text = informativeText.isEmpty() ? options->text() : (options->text() + QLatin1Char('\n') + informativeText); if (Qt::mightBeRichText(text)) { qWarning("Rich text detected, defaulting to QtWidgets-based dialog."); return false; } HRESULT hr; ComPtr dialogFactory; hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_Popups_MessageDialog).Get(), IID_PPV_ARGS(&dialogFactory)); RETURN_FALSE_IF_FAILED("Failed to create dialog factory"); ComPtr commandFactory; hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_Popups_UICommand).Get(), IID_PPV_ARGS(&commandFactory)); RETURN_FALSE_IF_FAILED("Failed to create command factory"); ComPtr dialog; HStringReference nativeText(reinterpret_cast(text.utf16()), text.size()); if (!title.isEmpty()) { HStringReference nativeTitle(reinterpret_cast(title.utf16()), title.size()); hr = dialogFactory->CreateWithTitle(nativeText.Get(), nativeTitle.Get(), &dialog); RETURN_FALSE_IF_FAILED("Failed to create dialog with title"); } else { hr = dialogFactory->Create(nativeText.Get(), &dialog); RETURN_FALSE_IF_FAILED("Failed to create dialog"); } hr = QEventDispatcherWinRT::runOnXamlThread([this, d, options, commandFactory, dialog]() { HRESULT hr; // Add Buttons ComPtr> dialogCommands; hr = dialog->get_Commands(&dialogCommands); RETURN_HR_IF_FAILED("Failed to get dialog commands"); // If no button is specified we need to create one to get close notification int buttons = options->standardButtons(); if (buttons == 0) buttons = Ok; for (int i = FirstButton; i < LastButton; i<<=1) { if (!(buttons & i)) continue; // Add native command const QString label = d->theme->standardButtonText(i); HStringReference nativeLabel(reinterpret_cast(label.utf16()), label.size()); ComPtr command; hr = commandFactory->Create(nativeLabel.Get(), &command); RETURN_HR_IF_FAILED("Failed to create message box command"); ComPtr id = Make(static_cast(i)); hr = command->put_Id(id.Get()); RETURN_HR_IF_FAILED("Failed to set command ID"); hr = dialogCommands->Append(command.Get()); if (hr == E_BOUNDS) { qErrnoWarning(hr, "The WinRT message dialog supports a maximum of three buttons"); continue; } RETURN_HR_IF_FAILED("Failed to append message box command"); if (i == Abort || i == Cancel || i == Close) { quint32 size; hr = dialogCommands->get_Size(&size); RETURN_HR_IF_FAILED("Failed to get command list size"); hr = dialog->put_CancelCommandIndex(size - 1); RETURN_HR_IF_FAILED("Failed to set cancel index"); } } ComPtr> op; hr = dialog->ShowAsync(&op); RETURN_HR_IF_FAILED("Failed to show dialog"); hr = op->put_Completed(Callback(this, &QWinRTMessageDialogHelper::onCompleted).Get()); RETURN_HR_IF_FAILED("Failed to set dialog callback"); d->shown = true; hr = op.As(&d->info); RETURN_HR_IF_FAILED("Failed to acquire AsyncInfo for MessageDialog"); return hr; }); RETURN_FALSE_IF_FAILED("Failed to show dialog") return true; } void QWinRTMessageDialogHelper::hide() { Q_D(QWinRTMessageDialogHelper); if (!d->shown) return; HRESULT hr = d->info->Cancel(); if (FAILED(hr)) qErrnoWarning(hr, "Failed to cancel dialog operation"); d->shown = false; } HRESULT QWinRTMessageDialogHelper::onCompleted(IAsyncOperation *asyncInfo, AsyncStatus status) { Q_UNUSED(status); Q_D(QWinRTMessageDialogHelper); QEventLoopLocker locker(&d->loop); d->shown = false; if (status == Canceled) { emit reject(); return S_OK; } HRESULT hr; ComPtr command; hr = asyncInfo->GetResults(&command); RETURN_OK_IF_FAILED("Failed to get command"); ComPtr id; hr = command->get_Id(&id); RETURN_OK_IF_FAILED("Failed to get command ID"); ButtonRole role = buttonRole(id->button); emit clicked(id->button, role); return S_OK; } QT_END_NAMESPACE