winrt: Enable drop support
Allow Qt applications to receive drops. Dragging is not supported yet and will be handled in a separate commit. Task-number: QTBUG-50827 Change-Id: I684e3d5685ce73f74805691f6ac7bbc45e2d19ec Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>bb10
parent
ed08e3be34
commit
0ff45fbf1b
|
|
@ -0,0 +1,672 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** 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 <QtCore/qglobal.h>
|
||||
#include <QtCore/QMimeData>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <qpa/qwindowsysteminterface.h>
|
||||
|
||||
#include <qfunctions_winrt.h>
|
||||
#include <private/qeventdispatcher_winrt_p.h>
|
||||
|
||||
#include <Windows.ApplicationModel.datatransfer.h>
|
||||
#include <windows.ui.xaml.h>
|
||||
#include <windows.foundation.collections.h>
|
||||
#include <windows.storage.streams.h>
|
||||
#include <functional>
|
||||
#include <robuffer.h>
|
||||
|
||||
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::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")
|
||||
|
||||
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<IDragEventArgs> nativeArgs;
|
||||
ComPtr<IDragOperationDeferral> deferral;
|
||||
};
|
||||
|
||||
inline QString hStringToQString(const HString &hString)
|
||||
{
|
||||
quint32 l;
|
||||
const wchar_t *raw = hString.GetRawBuffer(&l);
|
||||
return (QString::fromWCharArray(raw, l));
|
||||
}
|
||||
|
||||
namespace NativeFormatStrings {
|
||||
static ComPtr<IStandardDataFormatsStatics> 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;
|
||||
|
||||
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<IVectorView<HSTRING>> availableFormats;
|
||||
hr = dataView->get_AvailableFormats(&availableFormats);
|
||||
RETURN_IF_FAILED("Could not query available formats.", return formats);
|
||||
|
||||
quint32 size;
|
||||
hr = availableFormats->get_Size(&size);
|
||||
RETURN_IF_FAILED("Could not query format vector size.", return formats);
|
||||
for (quint32 i = 0; i < size; ++i) {
|
||||
HString str;
|
||||
hr = availableFormats->GetAt(i, str.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
continue;
|
||||
formats.append(hStringToQString(str));
|
||||
}
|
||||
qDebug() << __FUNCTION__ << "Available formats:" << formats;
|
||||
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<IAsyncOperation<HSTRING>> 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<IAsyncOperation<IVectorView<IStorageItem*>*>> op;
|
||||
hr = dataView->GetStorageItemsAsync(&op);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
ComPtr<IVectorView<IStorageItem*>> nativeItems;
|
||||
hr = QWinRTFunctions::await(op, nativeItems.GetAddressOf());
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
QList<QVariant> items;
|
||||
quint32 count;
|
||||
hr = nativeItems->get_Size(&count);
|
||||
for (quint32 i = 0; i < count; ++i) {
|
||||
ComPtr<IStorageItem> 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<IAsyncOperation<HSTRING>> 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 if (mimetype.startsWith(QLatin1String("image/"))) {
|
||||
Q_UNIMPLEMENTED();
|
||||
} else {
|
||||
// Asking for custom data
|
||||
hr = QEventDispatcherWinRT::runOnXamlThread([this, &result, mimetype]() {
|
||||
HRESULT hr;
|
||||
ComPtr<IAsyncOperation<IInspectable*>> op;
|
||||
ComPtr<IInspectable> res;
|
||||
HString type;
|
||||
type.Set(reinterpret_cast<const wchar_t*>(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<IPropertyValue> 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<IRandomAccessStream> randomAccessStream;
|
||||
hr = res.As(&randomAccessStream);
|
||||
if (SUCCEEDED(hr)) {
|
||||
UINT64 size;
|
||||
hr = randomAccessStream->get_Size(&size);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
ComPtr<IInputStream> stream;
|
||||
hr = randomAccessStream.As(&stream);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
ComPtr<IBufferFactory> 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<IBuffer> buffer;
|
||||
hr = bufferFactory->Create(length, &buffer);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> readOp;
|
||||
hr = stream->ReadAsync(buffer.Get(), length, InputStreamOptions_None, &readOp);
|
||||
|
||||
ComPtr<IBuffer> effectiveBuffer;
|
||||
hr = QWinRTFunctions::await(readOp, effectiveBuffer.GetAddressOf());
|
||||
|
||||
hr = effectiveBuffer->get_Length(&length);
|
||||
|
||||
ComPtr<Windows::Storage::Streams::IBufferByteAccess> 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<IDataPackageView> &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: \
|
||||
virtual HRESULT STDMETHODCALLTYPE 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Qt::DropAction QWinRTDrag::drag(QDrag *drag)
|
||||
{
|
||||
qCDebug(lcQpaMime) << __FUNCTION__ << drag;
|
||||
// ### TODO: Add dragging from Window
|
||||
return Qt::IgnoreAction;
|
||||
}
|
||||
|
||||
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<ABI::Windows::UI::Xaml::IUIElement> &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);
|
||||
qDebug() << "Point received:" << relativePoint.X << "/" << relativePoint.Y;
|
||||
|
||||
ComPtr<IDragEventArgs2> 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);
|
||||
|
||||
ComPtr<IDragEventArgs3> 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);
|
||||
|
||||
ComPtr<IDataPackageView> 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<IDragOperationDeferral> 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<IDragEventArgs2> 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"
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** 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 <qpa/qplatformdrag.h>
|
||||
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <QtCore/QMimeData>
|
||||
#include <QtGui/private/qdnd_p.h> // QInternalMime
|
||||
|
||||
#include <wrl.h>
|
||||
|
||||
namespace ABI {
|
||||
namespace Windows {
|
||||
namespace ApplicationModel {
|
||||
namespace DataTransfer {
|
||||
struct IDataPackageView;
|
||||
}
|
||||
}
|
||||
namespace UI {
|
||||
namespace Xaml {
|
||||
struct IUIElement;
|
||||
struct IDragEventArgs;
|
||||
struct IDragOperationDeferral;
|
||||
//struct IDataPackageView;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(lcQpaMime)
|
||||
|
||||
class QtDragEventHandlerEnter;
|
||||
class QtDragEventHandlerOver;
|
||||
class QtDragEventHandlerLeave;
|
||||
class QtDragEventHandlerDrop;
|
||||
class QWinRTInternalMimeData;
|
||||
|
||||
class QWinRTInternalMimeData : public QInternalMimeData {
|
||||
public:
|
||||
QWinRTInternalMimeData();
|
||||
virtual ~QWinRTInternalMimeData();
|
||||
|
||||
bool hasFormat_sys(const QString &mimetype) const Q_DECL_OVERRIDE;
|
||||
QStringList formats_sys() const Q_DECL_OVERRIDE;
|
||||
QVariant retrieveData_sys(const QString &mimetype, QVariant::Type preferredType) const Q_DECL_OVERRIDE;
|
||||
|
||||
void setDataView(const Microsoft::WRL::ComPtr<ABI::Windows::ApplicationModel::DataTransfer::IDataPackageView> &d);
|
||||
private:
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::ApplicationModel::DataTransfer::IDataPackageView> dataView;
|
||||
mutable QStringList formats;
|
||||
};
|
||||
|
||||
class QWinRTDrag : public QPlatformDrag {
|
||||
public:
|
||||
QWinRTDrag();
|
||||
virtual ~QWinRTDrag();
|
||||
static QWinRTDrag *instance();
|
||||
|
||||
QMimeData *platformDropData(void) Q_DECL_OVERRIDE;
|
||||
Qt::DropAction drag(QDrag *) Q_DECL_OVERRIDE;
|
||||
|
||||
void setDropTarget(QWindow *target);
|
||||
|
||||
// Native integration and registration
|
||||
void setUiElement(Microsoft::WRL::ComPtr<ABI::Windows::UI::Xaml::IUIElement> &element);
|
||||
|
||||
void handleNativeDragEvent(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e, bool drop = false);
|
||||
private:
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::UI::Xaml::IUIElement> m_ui;
|
||||
QWindow *m_dragTarget;
|
||||
QtDragEventHandlerEnter *m_enter;
|
||||
QtDragEventHandlerOver *m_over;
|
||||
QtDragEventHandlerLeave *m_leave;
|
||||
QtDragEventHandlerDrop *m_drop;
|
||||
QWinRTInternalMimeData *m_mimeData;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
@ -45,6 +45,7 @@
|
|||
#include "qwinrtfontdatabase.h"
|
||||
#include "qwinrttheme.h"
|
||||
#include "qwinrtclipboard.h"
|
||||
#include "qwinrtdrag.h"
|
||||
|
||||
#include <QtGui/QOffscreenSurface>
|
||||
#include <QtGui/QOpenGLContext>
|
||||
|
|
@ -309,6 +310,15 @@ QPlatformClipboard *QWinRTIntegration::clipboard() const
|
|||
return d->clipboard;
|
||||
}
|
||||
|
||||
QPlatformDrag *QWinRTIntegration::drag() const
|
||||
{
|
||||
#if _MSC_VER >= 1900
|
||||
return QWinRTDrag::instance();
|
||||
#else
|
||||
return QPlatformIntegration::drag();
|
||||
#endif
|
||||
}
|
||||
|
||||
Qt::KeyboardModifiers QWinRTIntegration::queryKeyboardModifiers() const
|
||||
{
|
||||
Q_D(const QWinRTIntegration);
|
||||
|
|
|
|||
|
|
@ -94,6 +94,10 @@ public:
|
|||
QPlatformInputContext *inputContext() const Q_DECL_OVERRIDE;
|
||||
QPlatformServices *services() const Q_DECL_OVERRIDE;
|
||||
QPlatformClipboard *clipboard() const Q_DECL_OVERRIDE;
|
||||
#ifndef QT_NO_DRAGANDDROP
|
||||
QPlatformDrag *drag() const Q_DECL_OVERRIDE;
|
||||
#endif
|
||||
|
||||
Qt::KeyboardModifiers queryKeyboardModifiers() const Q_DECL_OVERRIDE;
|
||||
|
||||
QStringList themeNames() const Q_DECL_OVERRIDE;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
#include "qwinrtbackingstore.h"
|
||||
#include "qwinrtinputcontext.h"
|
||||
#include "qwinrtcursor.h"
|
||||
#include "qwinrtdrag.h"
|
||||
#include "qwinrtwindow.h"
|
||||
#include <private/qeventdispatcher_winrt_p.h>
|
||||
|
||||
|
|
@ -553,6 +554,9 @@ QWinRTScreen::QWinRTScreen()
|
|||
ComPtr<Xaml::IUIElement> uiElement;
|
||||
hr = canvas.As(&uiElement);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
#if _MSC_VER >= 1900
|
||||
QWinRTDrag::instance()->setUiElement(uiElement);
|
||||
#endif
|
||||
hr = window->put_Content(uiElement.Get());
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
hr = canvas.As(&d->canvas);
|
||||
|
|
@ -761,6 +765,10 @@ void QWinRTScreen::addWindow(QWindow *window)
|
|||
QWindowSystemInterface::handleWindowActivated(window, Qt::OtherFocusReason);
|
||||
handleExpose();
|
||||
QWindowSystemInterface::flushWindowSystemEvents();
|
||||
|
||||
#if _MSC_VER >= 1900
|
||||
QWinRTDrag::instance()->setDropTarget(window);
|
||||
#endif
|
||||
}
|
||||
|
||||
void QWinRTScreen::removeWindow(QWindow *window)
|
||||
|
|
@ -775,6 +783,10 @@ void QWinRTScreen::removeWindow(QWindow *window)
|
|||
QWindowSystemInterface::handleWindowActivated(window, Qt::OtherFocusReason);
|
||||
handleExpose();
|
||||
QWindowSystemInterface::flushWindowSystemEvents();
|
||||
#if _MSC_VER >= 1900
|
||||
if (wasTopWindow)
|
||||
QWinRTDrag::instance()->setDropTarget(topWindow());
|
||||
#endif
|
||||
}
|
||||
|
||||
void QWinRTScreen::raise(QWindow *window)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ SOURCES = \
|
|||
qwinrtbackingstore.cpp \
|
||||
qwinrtclipboard.cpp \
|
||||
qwinrtcursor.cpp \
|
||||
qwinrtdrag.cpp \
|
||||
qwinrteglcontext.cpp \
|
||||
qwinrteventdispatcher.cpp \
|
||||
qwinrtfiledialoghelper.cpp \
|
||||
|
|
@ -32,6 +33,7 @@ HEADERS = \
|
|||
qwinrtbackingstore.h \
|
||||
qwinrtclipboard.h \
|
||||
qwinrtcursor.h \
|
||||
qwinrtdrag.h \
|
||||
qwinrteglcontext.h \
|
||||
qwinrteventdispatcher.h \
|
||||
qwinrtfiledialoghelper.h \
|
||||
|
|
@ -47,6 +49,11 @@ HEADERS = \
|
|||
|
||||
OTHER_FILES += winrt.json
|
||||
|
||||
*-msvc2013 {
|
||||
SOURCES -= qwinrtdrag.cpp
|
||||
HEADERS -= qwinrtdrag.h
|
||||
}
|
||||
|
||||
PLUGIN_TYPE = platforms
|
||||
PLUGIN_CLASS_NAME = QWinRTIntegrationPlugin
|
||||
!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = -
|
||||
|
|
|
|||
Loading…
Reference in New Issue