diff --git a/src/plugins/platforms/winrt/qwinrtdrag.cpp b/src/plugins/platforms/winrt/qwinrtdrag.cpp index a52ed91749..8ccb211e4f 100644 --- a/src/plugins/platforms/winrt/qwinrtdrag.cpp +++ b/src/plugins/platforms/winrt/qwinrtdrag.cpp @@ -88,6 +88,13 @@ inline QString hStringToQString(const HString &hString) 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 @@ -198,7 +205,6 @@ QStringList QWinRTInternalMimeData::formats_sys() const continue; formats.append(hStringToQString(str)); } - qDebug() << __FUNCTION__ << "Available formats:" << formats; return formats; } @@ -510,6 +516,8 @@ Q_DECLARE_DRAGHANDLER(Drop, drop) Q_GLOBAL_STATIC(QWinRTDrag, gDrag); +extern ComPtr qt_winrt_lastPointerPoint; // qwinrtscreen.cpp + QWinRTDrag::QWinRTDrag() : QPlatformDrag() , m_dragTarget(0) @@ -537,11 +545,184 @@ 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; - // ### TODO: Add dragging from Window - return Qt::IgnoreAction; + + 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); + + 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); + + 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 + + const QStringList formats = mimeData->formats(); + for (auto item : formats) { + QByteArray data = mimeData->data(item); + + ComPtr bufferFactory; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), + IID_PPV_ARGS(&bufferFactory)); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr buffer; + const UINT32 length = data.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.constData(), length); + + // 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(length); + 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.Get()->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); + + ComPtr pointStatics; + + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_Input_PointerPoint).Get(), + IID_PPV_ARGS(&pointStatics)); + 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) @@ -586,7 +767,6 @@ void QWinRTDrag::handleNativeDragEvent(IInspectable *sender, ABI::Windows::UI::X hr = e->GetPosition(m_ui.Get(), &relativePoint); RETURN_VOID_IF_FAILED("Could not query drag position."); const QPoint p(relativePoint.X, relativePoint.Y); - qDebug() << "Point received:" << relativePoint.X << "/" << relativePoint.Y; ComPtr e2; hr = e->QueryInterface(IID_PPV_ARGS(&e2)); diff --git a/src/plugins/platforms/winrt/qwinrtscreen.cpp b/src/plugins/platforms/winrt/qwinrtscreen.cpp index 3d9baf555d..43847e1ecc 100644 --- a/src/plugins/platforms/winrt/qwinrtscreen.cpp +++ b/src/plugins/platforms/winrt/qwinrtscreen.cpp @@ -982,6 +982,9 @@ HRESULT QWinRTScreen::onPointerExited(ICoreWindow *, IPointerEventArgs *args) return S_OK; } +// Required for qwinrtdrag.cpp +ComPtr qt_winrt_lastPointerPoint; + HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args) { Q_D(QWinRTScreen); @@ -989,6 +992,7 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args) if (FAILED(args->get_CurrentPoint(&pointerPoint))) return E_INVALIDARG; + qt_winrt_lastPointerPoint = pointerPoint; // Common traits - point, modifiers, properties Point point; pointerPoint->get_Position(&point);