/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL3$ ** 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 http://www.qt.io/terms-conditions. For further ** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free ** Software Foundation and appearing in the file LICENSE.GPL included in ** the packaging of this file. Please review the following information to ** ensure the GNU General Public License version 2.0 requirements will be ** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qwinrtdrag.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ABI::Windows::ApplicationModel::DataTransfer; using namespace ABI::Windows::ApplicationModel::DataTransfer::DragDrop; using namespace ABI::Windows::Foundation; using namespace ABI::Windows::Foundation::Collections; using namespace ABI::Windows::Graphics::Imaging; using namespace ABI::Windows::Storage; using namespace ABI::Windows::Storage::Streams; using namespace ABI::Windows::UI::Xaml; using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaMime, "qt.qpa.mime") ComPtr createIBufferFromData(const char *data, qint32 size) { static ComPtr bufferFactory; HRESULT hr; if (!bufferFactory) { hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), IID_PPV_ARGS(&bufferFactory)); Q_ASSERT_SUCCEEDED(hr); } ComPtr buffer; const UINT32 length = size; hr = bufferFactory->Create(length, &buffer); Q_ASSERT_SUCCEEDED(hr); hr = buffer->put_Length(length); Q_ASSERT_SUCCEEDED(hr); ComPtr byteArrayAccess; hr = buffer.As(&byteArrayAccess); Q_ASSERT_SUCCEEDED(hr); byte *bytes; hr = byteArrayAccess->Buffer(&bytes); Q_ASSERT_SUCCEEDED(hr); memcpy(bytes, data, length); return buffer; } class DragThreadTransferData : public QObject { Q_OBJECT public slots: void handleDrag(); public: explicit DragThreadTransferData(QObject *parent = Q_NULLPTR); QWindow *window; QWinRTInternalMimeData *mime; QPoint point; Qt::DropActions actions; bool dropAction; ComPtr nativeArgs; ComPtr deferral; }; inline QString hStringToQString(const HString &hString) { quint32 l; const wchar_t *raw = hString.GetRawBuffer(&l); return (QString::fromWCharArray(raw, l)); } inline HString qStringToHString(const QString &qString) { HString h; h.Set(reinterpret_cast(qString.utf16()), qString.size()); return h; } namespace NativeFormatStrings { static ComPtr dataStatics; static HSTRING text; // text/plain static HSTRING html; // text/html static HSTRING storage; // text/uri-list } static inline DataPackageOperation translateFromQDragDropActions(const Qt::DropAction action) { switch (action) { case Qt::CopyAction: return DataPackageOperation_Copy; case Qt::MoveAction: return DataPackageOperation_Move; case Qt::LinkAction: return DataPackageOperation_Link; case Qt::IgnoreAction: default: return DataPackageOperation_None; } } static inline Qt::DropActions translateToQDragDropActions(const DataPackageOperation op) { Qt::DropActions actions = Qt::IgnoreAction; // None needs to be interpreted as the sender being able to handle // anything and let the receiver decide if (op == DataPackageOperation_None) actions = Qt::LinkAction | Qt::CopyAction | Qt::MoveAction; if (op & DataPackageOperation_Link) actions |= Qt::LinkAction; if (op & DataPackageOperation_Copy) actions |= Qt::CopyAction; if (op & DataPackageOperation_Move) actions |= Qt::MoveAction; return actions; } QWinRTInternalMimeData::QWinRTInternalMimeData() : QInternalMimeData() { qCDebug(lcQpaMime) << __FUNCTION__; if (!NativeFormatStrings::dataStatics) { HRESULT hr; hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_ApplicationModel_DataTransfer_StandardDataFormats).Get(), IID_PPV_ARGS(&NativeFormatStrings::dataStatics)); Q_ASSERT_SUCCEEDED(hr); hr = NativeFormatStrings::dataStatics->get_Text(&NativeFormatStrings::text); Q_ASSERT_SUCCEEDED(hr); hr = NativeFormatStrings::dataStatics->get_Html(&NativeFormatStrings::html); Q_ASSERT_SUCCEEDED(hr); hr = NativeFormatStrings::dataStatics->get_StorageItems(&NativeFormatStrings::storage); Q_ASSERT_SUCCEEDED(hr); } } QWinRTInternalMimeData::~QWinRTInternalMimeData() { } bool QWinRTInternalMimeData::hasFormat_sys(const QString &mimetype) const { qCDebug(lcQpaMime) << __FUNCTION__ << mimetype; if (!dataView) return false; return formats_sys().contains(mimetype); } QStringList QWinRTInternalMimeData::formats_sys() const { qCDebug(lcQpaMime) << __FUNCTION__; if (!dataView) return QStringList(); if (!formats.isEmpty()) return formats; HRESULT hr; hr = QEventDispatcherWinRT::runOnXamlThread([this]() { boolean contains; HRESULT hr; hr = dataView->Contains(NativeFormatStrings::text, &contains); if (SUCCEEDED(hr) && contains) formats.append(QLatin1String("text/plain")); hr = dataView->Contains(NativeFormatStrings::html, &contains); if (SUCCEEDED(hr) && contains) formats.append(QLatin1String("text/html")); hr = dataView->Contains(NativeFormatStrings::storage, &contains); if (SUCCEEDED(hr) && contains) formats.append(QLatin1String("text/uri-list")); // We need to add any additional format as well, for legacy windows // reasons, but also in case someone adds custom formats. ComPtr> availableFormats; hr = dataView->get_AvailableFormats(&availableFormats); RETURN_OK_IF_FAILED("Could not query available formats."); quint32 size; hr = availableFormats->get_Size(&size); RETURN_OK_IF_FAILED("Could not query format vector size."); for (quint32 i = 0; i < size; ++i) { HString str; hr = availableFormats->GetAt(i, str.GetAddressOf()); if (FAILED(hr)) continue; formats.append(hStringToQString(str)); } return S_OK; }); Q_ASSERT_SUCCEEDED(hr); return formats; } QVariant QWinRTInternalMimeData::retrieveData_sys(const QString &mimetype, QVariant::Type preferredType) const { qCDebug(lcQpaMime) << __FUNCTION__ << mimetype << preferredType; if (!dataView || !formats.contains(mimetype)) return QVariant(); QVariant result; HRESULT hr; if (mimetype == QLatin1String("text/plain")) { hr = QEventDispatcherWinRT::runOnXamlThread([this, &result]() { HRESULT hr; ComPtr> op; HString res; hr = dataView->GetTextAsync(&op); Q_ASSERT_SUCCEEDED(hr); hr = QWinRTFunctions::await(op, res.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); result.setValue(hStringToQString(res)); return S_OK; }); } else if (mimetype == QLatin1String("text/uri-list")) { hr = QEventDispatcherWinRT::runOnXamlThread([this, &result]() { HRESULT hr; ComPtr*>> op; hr = dataView->GetStorageItemsAsync(&op); Q_ASSERT_SUCCEEDED(hr); ComPtr> nativeItems; hr = QWinRTFunctions::await(op, nativeItems.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); QList items; quint32 count; hr = nativeItems->get_Size(&count); for (quint32 i = 0; i < count; ++i) { ComPtr item; hr = nativeItems->GetAt(i, &item); Q_ASSERT_SUCCEEDED(hr); HString path; hr = item->get_Path(path.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); items.append(QUrl::fromLocalFile(hStringToQString(path))); } result.setValue(items); return S_OK; }); } else if (mimetype == QLatin1String("text/html")) { hr = QEventDispatcherWinRT::runOnXamlThread([this, &result]() { HRESULT hr; ComPtr> op; HString res; hr = dataView->GetHtmlFormatAsync(&op); Q_ASSERT_SUCCEEDED(hr); hr = QWinRTFunctions::await(op, res.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); result.setValue(hStringToQString(res)); return S_OK; }); } else { // Asking for custom data hr = QEventDispatcherWinRT::runOnXamlThread([this, &result, mimetype]() { HRESULT hr; ComPtr> op; ComPtr res; HString type; type.Set(reinterpret_cast(mimetype.utf16()), mimetype.size()); hr = dataView->GetDataAsync(type.Get(), &op); RETURN_OK_IF_FAILED("Could not query custom drag data."); hr = QWinRTFunctions::await(op, res.GetAddressOf()); if (FAILED(hr) || !res) { qCDebug(lcQpaMime) << "Custom drop data operation returned no results or failed."; return S_OK; } // Test for properties ComPtr propertyValue; hr = res.As(&propertyValue); if (SUCCEEDED(hr)) { // We need to check which type of custom data we are receiving PropertyType type; propertyValue->get_Type(&type); switch (type) { case PropertyType_UInt8: { quint8 v; hr = propertyValue->GetUInt8(&v); Q_ASSERT_SUCCEEDED(hr); result.setValue(v); return S_OK; } case PropertyType_Int16: { qint16 v; hr = propertyValue->GetInt16(&v); Q_ASSERT_SUCCEEDED(hr); result.setValue(v); return S_OK; } case PropertyType_UInt16: { quint16 v; hr = propertyValue->GetUInt16(&v); Q_ASSERT_SUCCEEDED(hr); result.setValue(v); return S_OK; } case PropertyType_Int32: { qint32 v; hr = propertyValue->GetInt32(&v); Q_ASSERT_SUCCEEDED(hr); result.setValue(v); return S_OK; } case PropertyType_UInt32: { quint32 v; hr = propertyValue->GetUInt32(&v); Q_ASSERT_SUCCEEDED(hr); result.setValue(v); return S_OK; } case PropertyType_Int64: { qint64 v; hr = propertyValue->GetInt64(&v); Q_ASSERT_SUCCEEDED(hr); result.setValue(v); return S_OK; } case PropertyType_UInt64: { quint64 v; hr = propertyValue->GetUInt64(&v); Q_ASSERT_SUCCEEDED(hr); result.setValue(v); return S_OK; } case PropertyType_Single: { float v; hr = propertyValue->GetSingle(&v); Q_ASSERT_SUCCEEDED(hr); result.setValue(v); return S_OK; } case PropertyType_Double: { double v; hr = propertyValue->GetDouble(&v); Q_ASSERT_SUCCEEDED(hr); result.setValue(v); return S_OK; } case PropertyType_Char16: { wchar_t v; hr = propertyValue->GetChar16(&v); Q_ASSERT_SUCCEEDED(hr); result.setValue(QString::fromWCharArray(&v, 1)); return S_OK; } case PropertyType_Boolean: { boolean v; hr = propertyValue->GetBoolean(&v); Q_ASSERT_SUCCEEDED(hr); result.setValue(v); return S_OK; } case PropertyType_String: { HString stringProperty; hr = propertyValue->GetString(stringProperty.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); result.setValue(hStringToQString(stringProperty)); return S_OK; } default: qCDebug(lcQpaMime) << "Unknown property type dropped:" << type; } return S_OK; } // Custom data can be read via input streams ComPtr randomAccessStream; hr = res.As(&randomAccessStream); if (SUCCEEDED(hr)) { UINT64 size; hr = randomAccessStream->get_Size(&size); Q_ASSERT_SUCCEEDED(hr); ComPtr stream; hr = randomAccessStream.As(&stream); Q_ASSERT_SUCCEEDED(hr); ComPtr bufferFactory; hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), IID_PPV_ARGS(&bufferFactory)); Q_ASSERT_SUCCEEDED(hr); UINT32 length = qBound(quint64(0), quint64(size), quint64(UINT_MAX)); ComPtr buffer; hr = bufferFactory->Create(length, &buffer); Q_ASSERT_SUCCEEDED(hr); ComPtr> readOp; hr = stream->ReadAsync(buffer.Get(), length, InputStreamOptions_None, &readOp); ComPtr effectiveBuffer; hr = QWinRTFunctions::await(readOp, effectiveBuffer.GetAddressOf()); hr = effectiveBuffer->get_Length(&length); ComPtr byteArrayAccess; hr = effectiveBuffer.As(&byteArrayAccess); byte *bytes; hr = byteArrayAccess->Buffer(&bytes); QByteArray array((char *)bytes, length); result.setValue(array); return S_OK; } HSTRING runtimeClass; hr = res->GetRuntimeClassName(&runtimeClass); Q_ASSERT_SUCCEEDED(hr); HString converted; converted.Set(runtimeClass); qCDebug(lcQpaMime) << "Unknown drop data type received (" << hStringToQString(converted) << "). Ignoring..."; return S_OK; }); } return result; } void QWinRTInternalMimeData::setDataView(const Microsoft::WRL::ComPtr &d) { dataView = d; formats.clear(); } static HRESULT qt_drag_enter(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e) { QWinRTDrag::instance()->handleNativeDragEvent(sender, e); return S_OK; } static HRESULT qt_drag_over(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e) { QWinRTDrag::instance()->handleNativeDragEvent(sender, e); return S_OK; } static HRESULT qt_drag_leave(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e) { // Qt internally checks for new drags and auto sends leave events // Also there is no QPA function for handling leave Q_UNUSED(sender); Q_UNUSED(e); return S_OK; } static HRESULT qt_drop(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e) { QWinRTDrag::instance()->handleNativeDragEvent(sender, e, true); return S_OK; } #define Q_DECLARE_DRAGHANDLER(name,func) \ class QtDragEventHandler##name : public IDragEventHandler \ { \ public: \ STDMETHODIMP Invoke(IInspectable *sender, \ ABI::Windows::UI::Xaml::IDragEventArgs *e) \ { \ return qt_##func(sender, e);\ } \ \ STDMETHODIMP \ QueryInterface(REFIID riid, void FAR* FAR* ppvObj) \ { \ if (riid == IID_IUnknown || riid == IID_IDragEventHandler) { \ *ppvObj = this; \ AddRef(); \ return NOERROR; \ } \ *ppvObj = NULL; \ return ResultFromScode(E_NOINTERFACE); \ } \ \ STDMETHODIMP_(ULONG) \ AddRef(void) \ { \ return ++m_refs; \ } \ \ STDMETHODIMP_(ULONG) \ Release(void) \ { \ if (--m_refs == 0) { \ delete this; \ return 0; \ } \ return m_refs; \ } \ private: \ ULONG m_refs{0}; \ }; Q_DECLARE_DRAGHANDLER(Enter, drag_enter) Q_DECLARE_DRAGHANDLER(Over, drag_over) Q_DECLARE_DRAGHANDLER(Leave, drag_leave) Q_DECLARE_DRAGHANDLER(Drop, drop) #define Q_INST_DRAGHANDLER(name) QtDragEventHandler##name() Q_GLOBAL_STATIC(QWinRTDrag, gDrag); extern ComPtr qt_winrt_lastPointerPoint; // qwinrtscreen.cpp QWinRTDrag::QWinRTDrag() : QPlatformDrag() , m_dragTarget(0) { qCDebug(lcQpaMime) << __FUNCTION__; m_enter = new Q_INST_DRAGHANDLER(Enter); m_over = new Q_INST_DRAGHANDLER(Over); m_leave = new Q_INST_DRAGHANDLER(Leave); m_drop = new Q_INST_DRAGHANDLER(Drop); m_mimeData = new QWinRTInternalMimeData; } QWinRTDrag::~QWinRTDrag() { qCDebug(lcQpaMime) << __FUNCTION__; delete m_enter; delete m_over; delete m_leave; delete m_drop; delete m_mimeData; } QWinRTDrag *QWinRTDrag::instance() { return gDrag; } inline HRESULT resetUiElementDrag(ComPtr &elem3, EventRegistrationToken startingToken) { return QEventDispatcherWinRT::runOnXamlThread([elem3, startingToken]() { HRESULT hr; hr = elem3->put_CanDrag(false); Q_ASSERT_SUCCEEDED(hr); hr = elem3->remove_DragStarting(startingToken); Q_ASSERT_SUCCEEDED(hr); return S_OK; }); } Qt::DropAction QWinRTDrag::drag(QDrag *drag) { qCDebug(lcQpaMime) << __FUNCTION__ << drag; if (!qt_winrt_lastPointerPoint) { Q_ASSERT_X(qt_winrt_lastPointerPoint, Q_FUNC_INFO, "No pointerpoint known"); return Qt::IgnoreAction; } ComPtr elem3; HRESULT hr = m_ui.As(&elem3); Q_ASSERT_SUCCEEDED(hr); ComPtr> op; EventRegistrationToken startingToken; hr = QEventDispatcherWinRT::runOnXamlThread([drag, &op, &hr, elem3, &startingToken, this]() { hr = elem3->put_CanDrag(true); Q_ASSERT_SUCCEEDED(hr); auto startingCallback = Callback> ([drag](IInspectable *, IDragStartingEventArgs *args) { qCDebug(lcQpaMime) << "Drag starting" << args; ComPtr dataPackage; HRESULT hr; hr = args->get_Data(dataPackage.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); Qt::DropAction action = drag->defaultAction(); hr = dataPackage->put_RequestedOperation(translateFromQDragDropActions(action)); Q_ASSERT_SUCCEEDED(hr); #ifndef QT_WINRT_LIMITED_DRAGANDDROP ComPtr args2; hr = args->QueryInterface(IID_PPV_ARGS(&args2)); Q_ASSERT_SUCCEEDED(hr); Qt::DropActions actions = drag->supportedActions(); DataPackageOperation allowedOperations = DataPackageOperation_None; if (actions & Qt::CopyAction) allowedOperations |= DataPackageOperation_Copy; if (actions & Qt::MoveAction) allowedOperations |= DataPackageOperation_Move; if (actions & Qt::LinkAction) allowedOperations |= DataPackageOperation_Link; hr = args2->put_AllowedOperations(allowedOperations); Q_ASSERT_SUCCEEDED(hr); #endif // QT_WINRT_LIMITED_DRAGANDDROP QMimeData *mimeData = drag->mimeData(); if (mimeData->hasText()) { hr = dataPackage->SetText(qStringToHString(mimeData->text()).Get()); Q_ASSERT_SUCCEEDED(hr); } if (mimeData->hasHtml()) { hr = dataPackage->SetHtmlFormat(qStringToHString(mimeData->html()).Get()); Q_ASSERT_SUCCEEDED(hr); } // ### TODO: Missing: weblink, image if (!drag->pixmap().isNull()) { const QImage image2 = drag->pixmap().toImage(); const QImage image = image2.convertToFormat(QImage::Format_ARGB32); if (!image.isNull()) { // Create IBuffer containing image ComPtr imageBuffer = createIBufferFromData(reinterpret_cast(image.bits()), image.byteCount()); ComPtr bitmapFactory; hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Graphics_Imaging_SoftwareBitmap).Get(), IID_PPV_ARGS(&bitmapFactory)); Q_ASSERT_SUCCEEDED(hr); ComPtr bitmap; hr = bitmapFactory->Create(BitmapPixelFormat_Rgba8, image.width(), image.height(), &bitmap); Q_ASSERT_SUCCEEDED(hr); hr = bitmap->CopyFromBuffer(imageBuffer.Get()); Q_ASSERT_SUCCEEDED(hr); ComPtr dragUi; hr = args->get_DragUI(dragUi.GetAddressOf()); Q_ASSERT_SUCCEEDED(hr); hr = dragUi->SetContentFromSoftwareBitmap(bitmap.Get()); Q_ASSERT_SUCCEEDED(hr); } } const QStringList formats = mimeData->formats(); for (auto item : formats) { QByteArray data = mimeData->data(item); ComPtr buffer = createIBufferFromData(data.constData(), data.size()); // We cannot push the buffer to the data package as the result on // recipient side is different from native events. It still sends a // buffer, but that potentially cannot be parsed. Hence we need to create // a IRandomAccessStream which gets forwarded as is to the drop side. ComPtr ras; hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_InMemoryRandomAccessStream).Get(), &ras); Q_ASSERT_SUCCEEDED(hr); hr = ras->put_Size(data.size()); ComPtr outputStream; hr = ras->GetOutputStreamAt(0, &outputStream); Q_ASSERT_SUCCEEDED(hr); ComPtr> writeOp; hr = outputStream->WriteAsync(buffer.Get(), &writeOp); Q_ASSERT_SUCCEEDED(hr); UINT32 result; hr = QWinRTFunctions::await(writeOp, &result); Q_ASSERT_SUCCEEDED(hr); unsigned char flushResult; ComPtr> flushOp; hr = outputStream->FlushAsync(&flushOp); Q_ASSERT_SUCCEEDED(hr); hr = QWinRTFunctions::await(flushOp, &flushResult); Q_ASSERT_SUCCEEDED(hr); hr = dataPackage->SetData(qStringToHString(item).Get(), ras.Get()); Q_ASSERT_SUCCEEDED(hr); } return S_OK; }); hr = elem3->add_DragStarting(startingCallback.Get(), &startingToken); Q_ASSERT_SUCCEEDED(hr); hr = elem3->StartDragAsync(qt_winrt_lastPointerPoint.Get(), &op); Q_ASSERT_SUCCEEDED(hr); return hr; }); if (!op || FAILED(hr)) { qCDebug(lcQpaMime) << "Drag failed:" << hr; hr = resetUiElementDrag(elem3, startingToken); Q_ASSERT_SUCCEEDED(hr); return Qt::IgnoreAction; } DataPackageOperation nativeOperationType; // Do not yield, as that can cause deadlocks when dropping inside the same app hr = QWinRTFunctions::await(op, &nativeOperationType, QWinRTFunctions::ProcessThreadEvents); Q_ASSERT_SUCCEEDED(hr); hr = resetUiElementDrag(elem3, startingToken); Q_ASSERT_SUCCEEDED(hr); Qt::DropAction resultAction; switch (nativeOperationType) { case DataPackageOperation_Link: resultAction = Qt::LinkAction; break; case DataPackageOperation_Copy: resultAction = Qt::CopyAction; break; case DataPackageOperation_Move: resultAction = Qt::MoveAction; break; case DataPackageOperation_None: default: resultAction = Qt::IgnoreAction; break; } return resultAction; } void QWinRTDrag::setDropTarget(QWindow *target) { qCDebug(lcQpaMime) << __FUNCTION__ << target; m_dragTarget = target; } QMimeData *QWinRTDrag::platformDropData() { qCDebug(lcQpaMime) << __FUNCTION__; return m_mimeData; } void QWinRTDrag::setUiElement(ComPtr &element) { qCDebug(lcQpaMime) << __FUNCTION__; m_ui = element; // We set the element to always accept drops and then evaluate during // runtime HRESULT hr = element->put_AllowDrop(TRUE); EventRegistrationToken tok; hr = element->add_DragEnter(m_enter, &tok); RETURN_VOID_IF_FAILED("Failed to add DragEnter handler."); hr = element->add_DragOver(m_over, &tok); RETURN_VOID_IF_FAILED("Failed to add DragOver handler."); hr = element->add_DragLeave(m_leave, &tok); RETURN_VOID_IF_FAILED("Failed to add DragLeave handler."); hr = element->add_Drop(m_drop, &tok); RETURN_VOID_IF_FAILED("Failed to add Drop handler."); } void QWinRTDrag::handleNativeDragEvent(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e, bool drop) { Q_UNUSED(sender); if (!m_dragTarget) return; HRESULT hr; Point relativePoint; hr = e->GetPosition(m_ui.Get(), &relativePoint); RETURN_VOID_IF_FAILED("Could not query drag position."); const QPoint p(relativePoint.X, relativePoint.Y); ComPtr e2; hr = e->QueryInterface(IID_PPV_ARGS(&e2)); RETURN_VOID_IF_FAILED("Could not convert drag event args"); DragDropModifiers modifiers; hr = e2->get_Modifiers(&modifiers); #ifndef QT_WINRT_LIMITED_DRAGANDDROP ComPtr e3; hr = e->QueryInterface(IID_PPV_ARGS(&e3)); Q_ASSERT_SUCCEEDED(hr); DataPackageOperation dataOp; hr = e3->get_AllowedOperations(&dataOp); if (FAILED(hr)) qCDebug(lcQpaMime) << __FUNCTION__ << "Could not query drag operations"; const Qt::DropActions actions = translateToQDragDropActions(dataOp); #else // !QT_WINRT_LIMITED_DRAGANDDROP const Qt::DropActions actions = Qt::LinkAction | Qt::CopyAction | Qt::MoveAction;; #endif // !QT_WINRT_LIMITED_DRAGANDDROP ComPtr dataView; hr = e2->get_DataView(&dataView); Q_ASSERT_SUCCEEDED(hr); m_mimeData->setDataView(dataView); // We use deferral as we need to jump to the Qt thread to handle // the drag event ComPtr deferral; hr = e2->GetDeferral(&deferral); Q_ASSERT_SUCCEEDED(hr); DragThreadTransferData *transferData = new DragThreadTransferData; transferData->moveToThread(qGuiApp->thread()); transferData->window = m_dragTarget; transferData->point = p; transferData->mime = m_mimeData; transferData->actions = actions; transferData->dropAction = drop; transferData->nativeArgs = e; transferData->deferral = deferral; QMetaObject::invokeMethod(transferData, "handleDrag", Qt::QueuedConnection); } DragThreadTransferData::DragThreadTransferData(QObject *parent) : QObject(parent) , dropAction(false) { } void DragThreadTransferData::handleDrag() { bool accepted = false; Qt::DropAction acceptedAction; if (dropAction) { QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(window, mime, point, actions); accepted = response.isAccepted(); acceptedAction = response.acceptedAction(); } else { QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(window, mime, point, actions); accepted = response.isAccepted(); acceptedAction = response.acceptedAction(); } HRESULT hr; hr = QEventDispatcherWinRT::runOnXamlThread([accepted, acceptedAction, this]() { HRESULT hr; hr = nativeArgs->put_Handled(accepted); if (acceptedAction != Qt::IgnoreAction) { ComPtr e2; hr = nativeArgs.As(&e2); if (SUCCEEDED(hr)) hr = e2->put_AcceptedOperation(translateFromQDragDropActions(acceptedAction)); } deferral->Complete(); return S_OK; }); Q_ASSERT_SUCCEEDED(hr); deleteLater(); } QT_END_NAMESPACE #include "qwinrtdrag.moc"