From 3a72a1c7ed5adcdf3e473b50cc9c932e9290ee81 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 22 Feb 2012 10:58:28 +0100 Subject: Refactor the QPA dnd interface. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Give QPlatformDrag a synchronous drag() function returning the Qt::DropAction - Move the base functionality for asynchronous event handling to the platformsupport library as QBasicDrag (extendable base class handling drag icon and providing new virtuals) and QSimpleDrag (sample implementation for drag within the Qt application). - Change the Windows implementation accordingly. - Change XCB to be based on QBasicDrag. - Clean up QDragManager. Change-Id: I654f76f0e55a385ba189bd74f3ceaded6a8fe318 Reviewed-by: Morten Johan Sørvig --- src/platformsupport/dnd/dnd.pri | 6 +- src/platformsupport/dnd/qshapedpixmapdndwindow.cpp | 100 +++++++ src/platformsupport/dnd/qshapedpixmapdndwindow_p.h | 78 +++++ src/platformsupport/dnd/qsimpledrag.cpp | 318 +++++++++++++++------ src/platformsupport/dnd/qsimpledrag_p.h | 64 ++++- 5 files changed, 465 insertions(+), 101 deletions(-) create mode 100644 src/platformsupport/dnd/qshapedpixmapdndwindow.cpp create mode 100644 src/platformsupport/dnd/qshapedpixmapdndwindow_p.h (limited to 'src/platformsupport') diff --git a/src/platformsupport/dnd/dnd.pri b/src/platformsupport/dnd/dnd.pri index e100dd10cb..47feb81ef2 100644 --- a/src/platformsupport/dnd/dnd.pri +++ b/src/platformsupport/dnd/dnd.pri @@ -1,4 +1,6 @@ HEADERS += \ - $$PWD/qsimpledrag_p.h + $$PWD/qsimpledrag_p.h \ + $$PWD/qshapedpixmapdndwindow_p.h SOURCES += \ - $$PWD/qsimpledrag.cpp + $$PWD/qsimpledrag.cpp \ + $$PWD/qshapedpixmapdndwindow.cpp diff --git a/src/platformsupport/dnd/qshapedpixmapdndwindow.cpp b/src/platformsupport/dnd/qshapedpixmapdndwindow.cpp new file mode 100644 index 0000000000..4eed1e7d85 --- /dev/null +++ b/src/platformsupport/dnd/qshapedpixmapdndwindow.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qshapedpixmapdndwindow_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +QShapedPixmapWindow::QShapedPixmapWindow() + : QWindow(), + m_backingStore(0) +{ + setSurfaceType(RasterSurface); + setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint | + Qt::X11BypassWindowManagerHint | Qt::WindowTransparentForInput); + create(); + m_backingStore = new QBackingStore(this); +} + +void QShapedPixmapWindow::render() +{ + QRect rect(QPoint(), geometry().size()); + + m_backingStore->beginPaint(rect); + + QPaintDevice *device = m_backingStore->paintDevice(); + + { + QPainter p(device); + p.drawPixmap(0, 0, m_pixmap); + } + + m_backingStore->endPaint(); + m_backingStore->flush(rect); +} + +void QShapedPixmapWindow::setPixmap(const QPixmap &pixmap) +{ + m_pixmap = pixmap; +} + +void QShapedPixmapWindow::setHotspot(const QPoint &hotspot) +{ + m_hotSpot = hotspot; +} + +void QShapedPixmapWindow::updateGeometry() +{ + QRect rect(QCursor::pos() - m_hotSpot, m_pixmap.size()); + if (m_backingStore->size() != m_pixmap.size()) + m_backingStore->resize(m_pixmap.size()); + setGeometry(rect); +} + +void QShapedPixmapWindow::exposeEvent(QExposeEvent *) +{ + render(); +} + +QT_END_NAMESPACE diff --git a/src/platformsupport/dnd/qshapedpixmapdndwindow_p.h b/src/platformsupport/dnd/qshapedpixmapdndwindow_p.h new file mode 100644 index 0000000000..36ca6040dd --- /dev/null +++ b/src/platformsupport/dnd/qshapedpixmapdndwindow_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 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 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSHAPEDPIXMAPDNDWINDOW_H +#define QSHAPEDPIXMAPDNDWINDOW_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QT_BEGIN_HEADER + +class QShapedPixmapWindow : public QWindow +{ +public: + QShapedPixmapWindow(); + + void render(); + + void setPixmap(const QPixmap &pixmap); + void setHotspot(const QPoint &hotspot); + + void updateGeometry(); + +protected: + void exposeEvent(QExposeEvent *); + +private: + QBackingStore *m_backingStore; + QPixmap m_pixmap; + QPoint m_hotSpot; +}; + +QT_END_HEADER + +QT_END_NAMESPACE + +#endif // QSHAPEDPIXMAPDNDWINDOW_H diff --git a/src/platformsupport/dnd/qsimpledrag.cpp b/src/platformsupport/dnd/qsimpledrag.cpp index d0d08c2445..d3cecd4e44 100644 --- a/src/platformsupport/dnd/qsimpledrag.cpp +++ b/src/platformsupport/dnd/qsimpledrag.cpp @@ -56,147 +56,283 @@ #include "qimagereader.h" #include "qimagewriter.h" +#include +#include + #include #include +#include + QT_BEGIN_NAMESPACE -class QDropData : public QInternalMimeData -{ -public: - QDropData(); - ~QDropData(); +/*! + \class QBasicDrag + \brief QBasicDrag is a base class for implementing platform drag and drop. + \since 5.0 + \internal + \ingroup qpa -protected: - bool hasFormat_sys(const QString &mimeType) const; - QStringList formats_sys() const; - QVariant retrieveData_sys(const QString &mimeType, QVariant::Type type) const; -}; + QBasicDrag implements QPlatformDrag::drag() by running a local event loop in which + it tracks mouse movements and moves the drag icon (QShapedPixmapWindow) accordingly. + It provides new virtuals allowing for querying whether the receiving window + (within the Qt application or outside) accepts the drag and sets the state accordingly. +*/ -QSimpleDrag::QSimpleDrag() +QBasicDrag::QBasicDrag() : + m_restoreCursor(false), m_eventLoop(0), + m_executed_drop_action(Qt::IgnoreAction), m_can_drop(false), + m_drag(0), m_drag_icon_window(0), m_cursor_drop_action(Qt::IgnoreAction) { - m_dropData = new QDropData(); - currentWindow = 0; } -QSimpleDrag::~QSimpleDrag() +QBasicDrag::~QBasicDrag() { - delete m_dropData; + delete m_drag_icon_window; } -QMimeData *QSimpleDrag::platformDropData() +void QBasicDrag::enableEventFilter() { - return m_dropData; + qApp->installEventFilter(this); } -void QSimpleDrag::cancel() +void QBasicDrag::disableEventFilter() { - QDragManager *m = QDragManager::self(); -// qDebug("QDragManager::cancel"); - if (m->object->target()) { - QDragLeaveEvent dle; - QCoreApplication::sendEvent(m->object->target(), &dle); - } - + qApp->removeEventFilter(this); } -void QSimpleDrag::move(const QMouseEvent *me) +bool QBasicDrag::eventFilter(QObject *o, QEvent *e) { - QWindow *window = QGuiApplication::topLevelAt(me->globalPos()); - QPoint pos; - if (window) - pos = me->globalPos() - window->geometry().topLeft(); + if (!m_drag) { + if (e->type() == QEvent::KeyRelease && static_cast(e)->key() == Qt::Key_Escape) { + disableEventFilter(); + exitDndEventLoop(); + return true; // block the key release + } + return false; + } - QDragManager *m = QDragManager::self(); + if (!qobject_cast(o)) + return false; - if (me->buttons()) { - Qt::DropAction prevAction = m->global_accepted_action; + switch (e->type()) { + case QEvent::ShortcutOverride: + // prevent accelerators from firing while dragging + e->accept(); + return true; + + case QEvent::KeyPress: + case QEvent::KeyRelease: + { + QKeyEvent *ke = static_cast(e); + if (ke->key() == Qt::Key_Escape && e->type() == QEvent::KeyPress) { + cancel(); + resetDndState(true); + disableEventFilter(); + exitDndEventLoop(); - if (currentWindow != window) { - if (currentWindow) { - QDragLeaveEvent dle; - QCoreApplication::sendEvent(currentWindow, &dle); - m->willDrop = false; - m->global_accepted_action = Qt::IgnoreAction; - } - currentWindow = window; - if (currentWindow) { - QDragEnterEvent dee(pos, m->possible_actions, m->dropData(), me->buttons(), me->modifiers()); - QCoreApplication::sendEvent(currentWindow, &dee); - m->willDrop = dee.isAccepted() && dee.dropAction() != Qt::IgnoreAction; - m->global_accepted_action = m->willDrop ? dee.dropAction() : Qt::IgnoreAction; - } - m->updateCursor(); - } else if (window) { - Q_ASSERT(currentWindow); - QDragMoveEvent dme(pos, m->possible_actions, m->dropData(), me->buttons(), me->modifiers()); - if (m->global_accepted_action != Qt::IgnoreAction) { - dme.setDropAction(m->global_accepted_action); - dme.accept(); } - QCoreApplication::sendEvent(currentWindow, &dme); - m->willDrop = dme.isAccepted(); - m->global_accepted_action = m->willDrop ? dme.dropAction() : Qt::IgnoreAction; - m->updatePixmap(); - m->updateCursor(); + return true; // Eat all key events } - if (m->global_accepted_action != prevAction) - m->emitActionChanged(m->global_accepted_action); + + case QEvent::MouseMove: + move(static_cast(e)); + return true; // Eat all mouse events + + case QEvent::MouseButtonRelease: + disableEventFilter(); + + if (canDrop()) { + drop(static_cast(e)); + resetDndState(false); + } else { + cancel(); + resetDndState(true); + } + exitDndEventLoop(); + return true; // Eat all mouse events + + case QEvent::MouseButtonPress: + case QEvent::MouseButtonDblClick: + case QEvent::Wheel: + return true; + default: + break; } + return false; } -void QSimpleDrag::drop(const QMouseEvent *me) +Qt::DropAction QBasicDrag::drag(QDrag *o) { - QDragManager *m = QDragManager::self(); + m_drag = o; + m_executed_drop_action = Qt::IgnoreAction; + m_can_drop = false; + m_restoreCursor = true; +#ifndef QT_NO_CURSOR + qApp->setOverrideCursor(Qt::DragCopyCursor); + updateCursor(m_executed_drop_action); +#endif + startDrag(); + m_eventLoop = new QEventLoop; + m_eventLoop->exec(); + delete m_eventLoop; + m_eventLoop = 0; + m_drag = 0; + endDrag(); + return m_executed_drop_action; +} - QWindow *window = QGuiApplication::topLevelAt(me->globalPos()); +void QBasicDrag::resetDndState(bool /* deleteSource */) +{ + if (m_restoreCursor) { +#ifndef QT_NO_CURSOR + QGuiApplication::restoreOverrideCursor(); +#endif + m_restoreCursor = false; + } +} + +void QBasicDrag::startDrag() +{ + if (!m_drag_icon_window) + m_drag_icon_window = new QShapedPixmapWindow(); + + m_drag_icon_window->setPixmap(m_drag->pixmap()); + m_drag_icon_window->setHotspot(m_drag->hotSpot()); + m_drag_icon_window->updateGeometry(); + m_drag_icon_window->setVisible(true); + + enableEventFilter(); +} + +void QBasicDrag::endDrag() +{ +} + +void QBasicDrag::cancel() +{ + disableEventFilter(); + m_drag_icon_window->setVisible(false); +} + +void QBasicDrag::move(const QMouseEvent *) +{ + if (m_drag) + m_drag_icon_window->updateGeometry(); +} - if (window) { - QPoint pos = me->globalPos() - window->geometry().topLeft(); +void QBasicDrag::drop(const QMouseEvent *) +{ + disableEventFilter(); + m_drag_icon_window->setVisible(false); +} + +void QBasicDrag::exitDndEventLoop() +{ + if (m_eventLoop && m_eventLoop->isRunning()) + m_eventLoop->exit(); +} - QDropEvent de(pos, m->possible_actions, m->dropData(), me->buttons(), me->modifiers()); - QCoreApplication::sendEvent(window, &de); - if (de.isAccepted()) - m->global_accepted_action = de.dropAction(); - else - m->global_accepted_action = Qt::IgnoreAction; +void QBasicDrag::updateCursor(Qt::DropAction action) +{ + Qt::CursorShape cursorShape = Qt::ForbiddenCursor; + if (canDrop()) { + switch (action) { + case Qt::CopyAction: + cursorShape = Qt::DragCopyCursor; + break; + case Qt::LinkAction: + cursorShape = Qt::DragLinkCursor; + break; + default: + cursorShape = Qt::DragMoveCursor; + break; + } } - currentWindow = 0; + + QCursor *cursor = qApp->overrideCursor(); + if (cursor && cursorShape != cursor->shape()) { + qApp->changeOverrideCursor(QCursor(cursorShape)); + } + updateAction(action); } +/*! + \class QSimpleDrag + \brief QSimpleDrag implements QBasicDrag for Drag and Drop operations within the Qt Application itself. + \since 5.0 + \internal + \ingroup qpa + The class checks whether the receiving window is a window of the Qt application + and sets the state accordingly. It does not take windows of other applications + into account. +*/ -QDropData::QDropData() - : QInternalMimeData() +QSimpleDrag::QSimpleDrag() : m_current_window(0) { } -QDropData::~QDropData() +QMimeData *QSimpleDrag::platformDropData() { + if (drag()) + return drag()->mimeData(); + return 0; } -QVariant QDropData::retrieveData_sys(const QString &mimetype, QVariant::Type type) const +void QSimpleDrag::startDrag() { - QDrag *object = QDragManager::self()->object; - if (!object) - return QVariant(); - QByteArray data = object->mimeData()->data(mimetype); - if (type == QVariant::String) - return QString::fromUtf8(data); - return data; + QBasicDrag::startDrag(); + m_current_window = QGuiApplication::topLevelAt(QCursor::pos()); + if (m_current_window) { + QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_current_window, drag()->mimeData(), QCursor::pos(), drag()->supportedActions()); + setCanDrop(response.isAccepted()); + updateCursor(response.acceptedAction()); + } else { + setCanDrop(false); + updateCursor(Qt::IgnoreAction); + } + setExecutedDropAction(Qt::IgnoreAction); } -bool QDropData::hasFormat_sys(const QString &format) const +void QSimpleDrag::cancel() { - return formats().contains(format); + QBasicDrag::cancel(); + if (drag()) + QWindowSystemInterface::handleDrag(m_current_window, 0, QPoint(), Qt::IgnoreAction); + m_current_window = 0; } -QStringList QDropData::formats_sys() const +void QSimpleDrag::move(const QMouseEvent *me) { - QDrag *object = QDragManager::self()->object; - if (object) - return object->mimeData()->formats(); - return QStringList(); + QBasicDrag::move(me); + QWindow *window = QGuiApplication::topLevelAt(me->globalPos()); + if (!window) + return; + + const QPoint pos = me->globalPos() - window->geometry().topLeft(); + const QPlatformDragQtResponse qt_response = + QWindowSystemInterface::handleDrag(window, drag()->mimeData(), pos, drag()->supportedActions()); + + updateCursor(qt_response.acceptedAction()); + setCanDrop(qt_response.isAccepted()); +} + +void QSimpleDrag::drop(const QMouseEvent *me) +{ + QBasicDrag::drop(me); + QWindow *window = QGuiApplication::topLevelAt(me->globalPos()); + if (!window) + return; + + const QPoint pos = me->globalPos() - window->geometry().topLeft(); + const QPlatformDropQtResponse response = + QWindowSystemInterface::handleDrop(window, drag()->mimeData(),pos, drag()->supportedActions()); + if (response.isAccepted()) { + setExecutedDropAction(response.acceptedAction()); + } else { + setExecutedDropAction(Qt::IgnoreAction); + } } QT_END_NAMESPACE diff --git a/src/platformsupport/dnd/qsimpledrag_p.h b/src/platformsupport/dnd/qsimpledrag_p.h index 536ae241ff..7270684082 100644 --- a/src/platformsupport/dnd/qsimpledrag_p.h +++ b/src/platformsupport/dnd/qsimpledrag_p.h @@ -44,32 +44,80 @@ #include +#include + QT_BEGIN_NAMESPACE +QT_BEGIN_HEADER + class QMouseEvent; class QWindow; - +class QEventLoop; class QDropData; +class QShapedPixmapWindow; -class QSimpleDrag : public QPlatformDrag +class QBasicDrag : public QPlatformDrag, public QObject { public: - QSimpleDrag(); - ~QSimpleDrag(); + virtual ~QBasicDrag(); - virtual QMimeData *platformDropData(); + virtual Qt::DropAction drag(QDrag *drag); + + virtual bool eventFilter(QObject *o, QEvent *e); -// virtual Qt::DropAction drag(QDrag *); +protected: + QBasicDrag(); + virtual void startDrag(); virtual void cancel(); virtual void move(const QMouseEvent *me); virtual void drop(const QMouseEvent *me); + virtual void endDrag(); + + QShapedPixmapWindow *shapedPixmapWindow() const { return m_drag_icon_window; } + void updateCursor(Qt::DropAction action); + + bool canDrop() const { return m_can_drop; } + void setCanDrop(bool c) { m_can_drop = c; } + + Qt::DropAction executedDropAction() const { return m_executed_drop_action; } + void setExecutedDropAction(Qt::DropAction da) { m_executed_drop_action = da; } + + QDrag *drag() const { return m_drag; } + private: - QDropData *m_dropData; + void enableEventFilter(); + void disableEventFilter(); + void resetDndState(bool deleteSource); + void exitDndEventLoop(); + + bool m_restoreCursor; + QEventLoop *m_eventLoop; + Qt::DropAction m_executed_drop_action; + bool m_can_drop; + QDrag *m_drag; + QShapedPixmapWindow *m_drag_icon_window; + Qt::DropAction m_cursor_drop_action; +}; + +class QSimpleDrag : public QBasicDrag +{ +public: + QSimpleDrag(); + virtual QMimeData *platformDropData(); - QWindow *currentWindow; +protected: + virtual void startDrag(); + virtual void cancel(); + virtual void move(const QMouseEvent *me); + virtual void drop(const QMouseEvent *me); + +private: + QWindow *m_current_window; }; +QT_END_HEADER + QT_END_NAMESPACE #endif -- cgit v1.2.3