winrt: enable drag support

Allow applications to initiate drag operations.

Task-number: QTBUG-50827
Change-Id: I3c29b54756af1af24544f49803305f0c95d8b7f9
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
This commit is contained in:
Maurice Kalinowski 2016-05-26 10:19:28 +02:00
parent 0ff45fbf1b
commit 94f319a2bb
2 changed files with 188 additions and 4 deletions

View File

@ -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<const wchar_t*>(qString.utf16()), qString.size());
return h;
}
namespace NativeFormatStrings {
static ComPtr<IStandardDataFormatsStatics> 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<ABI::Windows::UI::Input::IPointerPoint> qt_winrt_lastPointerPoint; // qwinrtscreen.cpp
QWinRTDrag::QWinRTDrag()
: QPlatformDrag()
, m_dragTarget(0)
@ -537,11 +545,184 @@ QWinRTDrag *QWinRTDrag::instance()
return gDrag;
}
inline HRESULT resetUiElementDrag(ComPtr<IUIElement3> &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<IUIElement3> elem3;
HRESULT hr = m_ui.As(&elem3);
Q_ASSERT_SUCCEEDED(hr);
ComPtr<IAsyncOperation<ABI::Windows::ApplicationModel::DataTransfer::DataPackageOperation>> op;
EventRegistrationToken startingToken;
hr = QEventDispatcherWinRT::runOnXamlThread([drag, &op, &hr, elem3, &startingToken, this]() {
hr = elem3->put_CanDrag(true);
Q_ASSERT_SUCCEEDED(hr);
auto startingCallback = Callback<ITypedEventHandler<UIElement*, DragStartingEventArgs*>> ([drag](IInspectable *, IDragStartingEventArgs *args) {
qCDebug(lcQpaMime) << "Drag starting" << args;
ComPtr<IDataPackage> 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<IDragStartingEventArgs2> 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<IBufferFactory> bufferFactory;
hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(),
IID_PPV_ARGS(&bufferFactory));
Q_ASSERT_SUCCEEDED(hr);
ComPtr<IBuffer> 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<Windows::Storage::Streams::IBufferByteAccess> 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<IRandomAccessStream> ras;
hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_InMemoryRandomAccessStream).Get(), &ras);
Q_ASSERT_SUCCEEDED(hr);
hr = ras->put_Size(length);
ComPtr<IOutputStream> outputStream;
hr = ras->GetOutputStreamAt(0, &outputStream);
Q_ASSERT_SUCCEEDED(hr);
ComPtr<IAsyncOperationWithProgress<UINT32,UINT32>> 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<IAsyncOperation<bool>> 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<ABI::Windows::UI::Input::IPointerPointStatics> 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<IDragEventArgs2> e2;
hr = e->QueryInterface(IID_PPV_ARGS(&e2));

View File

@ -982,6 +982,9 @@ HRESULT QWinRTScreen::onPointerExited(ICoreWindow *, IPointerEventArgs *args)
return S_OK;
}
// Required for qwinrtdrag.cpp
ComPtr<IPointerPoint> 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);