From c3eb2e63425c47b8e3eeb7416e225fab10c5c15a Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Tue, 24 Apr 2012 13:45:46 +0200 Subject: XShaping drag and drop fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No shaping rectangles means no interaction with the window The shaping rectangles are based on the window coords, so need to substract the window coords when checking for the point And some tests to prove this change is needed Backport from commit 07f3c1e26aa6dcd07a9705e7b2ea02ace9ea7c5d from qtbase Change-Id: I6dd6c75c2bd70463561445b4f4a3894c80981d26 Reviewed-by: Samuel Rødal --- src/gui/kernel/qdnd_x11.cpp | 20 ++-- tests/auto/qdrag/dummy.png | Bin 0 -> 126 bytes tests/auto/qdrag/tst_qdrag.cpp | 255 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 267 insertions(+), 8 deletions(-) create mode 100644 tests/auto/qdrag/dummy.png diff --git a/src/gui/kernel/qdnd_x11.cpp b/src/gui/kernel/qdnd_x11.cpp index b312d7e..e3d5391 100644 --- a/src/gui/kernel/qdnd_x11.cpp +++ b/src/gui/kernel/qdnd_x11.cpp @@ -1427,9 +1427,8 @@ bool windowInteractsWithPosition(const QPoint & pos, Window w, int shapeType) { int nrectanglesRet, dummyOrdering; XRectangle *rectangles = XShapeGetRectangles(QX11Info::display(), w, shapeType, &nrectanglesRet, &dummyOrdering); - bool interacts = true; + bool interacts = false; if (rectangles) { - interacts = false; for (int i = 0; !interacts && i < nrectanglesRet; ++i) interacts = QRect(rectangles[i].x, rectangles[i].y, rectangles[i].width, rectangles[i].height).contains(pos); XFree(rectangles); @@ -1438,7 +1437,7 @@ bool windowInteractsWithPosition(const QPoint & pos, Window w, int shapeType) } static -Window findRealWindow(const QPoint & pos, Window w, int md) +Window findRealWindow(const QPoint & pos, Window w, int md, bool ignoreNonXdndAwareWindows) { if (xdnd_data.deco && w == xdnd_data.deco->effectiveWinId()) return 0; @@ -1452,7 +1451,7 @@ Window findRealWindow(const QPoint & pos, Window w, int md) if (attr.map_state == IsViewable && QRect(attr.x,attr.y,attr.width,attr.height).contains(pos)) { - bool windowContainsMouse = true; + bool windowContainsMouse = !ignoreNonXdndAwareWindows; { Atom type = XNone; int f; @@ -1463,12 +1462,15 @@ Window findRealWindow(const QPoint & pos, Window w, int md) AnyPropertyType, &type, &f,&n,&a,&data); if (data) XFree(data); if (type) { + const QPoint relPos = pos - QPoint(attr.x,attr.y); // When ShapeInput and ShapeBounding are not set they return a single rectangle with the geometry of the window, this is why we // need an && here so that in the case one is set and the other is not we still get the correct result. #if defined(ShapeInput) && defined(ShapeBounding) - windowContainsMouse = windowInteractsWithPosition(pos, w, ShapeInput) && windowInteractsWithPosition(pos, w, ShapeBounding); + windowContainsMouse = windowInteractsWithPosition(relPos, w, ShapeInput) && windowInteractsWithPosition(relPos, w, ShapeBounding); #elif defined(ShapeBounding) - windowContainsMouse = windowInteractsWithPosition(pos, w, ShapeBounding); + windowContainsMouse = windowInteractsWithPosition(relPos, w, ShapeBounding); +#else + windowContainsMouse = true; #endif if (windowContainsMouse) return w; @@ -1482,7 +1484,7 @@ Window findRealWindow(const QPoint & pos, Window w, int md) r=0; for (uint i=nc; !r && i--;) { r = findRealWindow(pos-QPoint(attr.x,attr.y), - c[i], md-1); + c[i], md-1, ignoreNonXdndAwareWindows); } XFree(c); if (r) @@ -1579,7 +1581,9 @@ void QDragManager::move(const QPoint & globalPos) } if (xdnd_data.deco && (!target || target == xdnd_data.deco->effectiveWinId())) { DNDDEBUG << "need to find real window"; - target = findRealWindow(globalPos, rootwin, 6); + target = findRealWindow(globalPos, rootwin, 6, true); + if (target == 0) + target = findRealWindow(globalPos, rootwin, 6, false); DNDDEBUG << "real window found" << QWidget::find(target) << target; } } diff --git a/tests/auto/qdrag/dummy.png b/tests/auto/qdrag/dummy.png new file mode 100644 index 0000000..c4b5723 Binary files /dev/null and b/tests/auto/qdrag/dummy.png differ diff --git a/tests/auto/qdrag/tst_qdrag.cpp b/tests/auto/qdrag/tst_qdrag.cpp index 07d82b3..3aac909 100644 --- a/tests/auto/qdrag/tst_qdrag.cpp +++ b/tests/auto/qdrag/tst_qdrag.cpp @@ -46,9 +46,132 @@ #include #include +#ifdef Q_WS_X11 +#include +#include +#include + +#include +#endif + //TESTED_CLASS= //TESTED_FILES= +class DragEnterCounterWidget : public QWidget +{ +public: + DragEnterCounterWidget(); + + void dragEnterEvent(QDragEnterEvent * event); + + int enterEvents; +}; + +class DragCounterAndCreatorWidget : public DragEnterCounterWidget +{ +public: + DragCounterAndCreatorWidget(); + + void mousePressEvent(QMouseEvent * event); + + int startedDrags; +}; + +class BiggerDragCounterWidget : public DragEnterCounterWidget +{ +public: + BiggerDragCounterWidget(); +}; + +DragEnterCounterWidget::DragEnterCounterWidget() : QWidget(0), enterEvents(0) +{ + setAcceptDrops(true); + setWindowFlags(Qt::FramelessWindowHint); + show(); +} + +void DragEnterCounterWidget::dragEnterEvent(QDragEnterEvent * event) +{ + event->acceptProposedAction(); + ++enterEvents; +} + +DragCounterAndCreatorWidget::DragCounterAndCreatorWidget() : startedDrags(0) +{ + resize(80, 80); + move(300, 300); +} + +void DragCounterAndCreatorWidget::mousePressEvent(QMouseEvent * /*event*/) +{ + ++startedDrags; + QDrag *drag = new QDrag(this); + drag->setMimeData(new QMimeData); + QPixmap p("dummy.png"); + drag->setHotSpot(QPoint( p.width() / 2, p.height() / 2 )); + drag->setPixmap(p); + drag->exec(); +} + +BiggerDragCounterWidget::BiggerDragCounterWidget() +{ + resize(180, 180); + move(250, 250); +} + +class DoMouseReleaseHelper : public QTimer +{ +Q_OBJECT + +public: + DoMouseReleaseHelper(QWidget *w, int timeout = 0); + +private slots: + void doMouseRelease(); + +private: + QWidget *m_w; +}; + +DoMouseReleaseHelper::DoMouseReleaseHelper(QWidget *w, int timeout) : m_w(w) +{ + setSingleShot(true); + start(timeout); + connect(this, SIGNAL(timeout()), this, SLOT(doMouseRelease())); +} + +void DoMouseReleaseHelper::doMouseRelease() +{ + QTest::mouseRelease(m_w, Qt::LeftButton); +} + +class DoMouseMoveHelper : public QTimer +{ +Q_OBJECT + +public: + DoMouseMoveHelper(QWidget *w, const QPoint &p, int timeout = 0); + +private slots: + void doMouseMove(); + +private: + QWidget *m_w; + QPoint m_p; +}; + +DoMouseMoveHelper::DoMouseMoveHelper(QWidget *w, const QPoint &p, int timeout) : m_w(w), m_p(p) +{ + setSingleShot(true); + start(timeout); + connect(this, SIGNAL(timeout()), this, SLOT(doMouseMove())); +} + +void DoMouseMoveHelper::doMouseMove() +{ + QTest::mouseMove(m_w, m_p); +} + class tst_QDrag : public QObject { Q_OBJECT @@ -59,6 +182,10 @@ public: private slots: void getSetCheck(); + void testDragEnterSelf(); + void testDragEnterNoShaping(); + void testDragEnterSomeShaping(); + void testDragEnterAllShaping(); }; tst_QDrag::tst_QDrag() @@ -90,5 +217,133 @@ void tst_QDrag::getSetCheck() QCOMPARE(result, Qt::IgnoreAction); } +void tst_QDrag::testDragEnterSelf() +{ +#ifdef Q_WS_X11 + // Widget of 80x80 at 300, 300 + DragCounterAndCreatorWidget dw; + QTest::qWaitForWindowShown(&dw); + + // Press mouse to create a drag in dw + QTest::qWait(100); + QTest::mouseMove(&dw); + DoMouseReleaseHelper aux(&dw); + QTest::mousePress(&dw, Qt::LeftButton); + + // Verify that without a window in the middle the drag goes to dw itself + QCOMPARE(dw.startedDrags, 1); + QCOMPARE(dw.enterEvents, 1); +#endif +} + +void tst_QDrag::testDragEnterNoShaping() +{ +#ifdef Q_WS_X11 + // Widget of 80x80 at 300, 300 + DragCounterAndCreatorWidget dw; + QTest::qWaitForWindowShown(&dw); + + // Widget of 180x180 at 250, 250 + BiggerDragCounterWidget widgetOnTop; + QTest::qWaitForWindowShown(&widgetOnTop); + + // Press mouse to create a drag in dw + QTest::qWait(100); + QTest::mouseMove(&dw); + DoMouseReleaseHelper aux(&dw); + QTest::mousePress(&dw, Qt::LeftButton); + + // Verify that with widgetOnTop in the middle the drag, the drag event does not go to dw + // and goes to widgetOnTop + QCOMPARE(dw.startedDrags, 1); + QCOMPARE(dw.enterEvents, 0); + QCOMPARE(widgetOnTop.enterEvents, 1); +#endif +} + +void tst_QDrag::testDragEnterSomeShaping() +{ +#ifdef Q_WS_X11 + // Widget of 80x80 at 300, 300 + DragCounterAndCreatorWidget dw; + QTest::qWaitForWindowShown(&dw); + + // Widget of 180x180 at 250, 250 + BiggerDragCounterWidget widgetOnTop; + QTest::qWaitForWindowShown(&widgetOnTop); + + // Punch a hole in widgetOnTop to let the mouse go through the widget + // in the center of dw + QBitmap inputShape(180, 180); + inputShape.fill(Qt::color1); + QPainter painter(&inputShape); + painter.fillRect(80, 80, 50, 50, Qt::color0); + painter.end(); + XShapeCombineRegion(QX11Info::display(), widgetOnTop.effectiveWinId(), ShapeInput, 0, 0, QRegion(inputShape).handle(), ShapeSet); + + // Press mouse to create a drag in dw + QTest::qWait(100); + QTest::mouseMove(&dw); + DoMouseReleaseHelper aux(&dw); + QTest::mousePress(&dw, Qt::LeftButton); + + // Verify that with a input shaped widgetOnTop in the middle the drag, the drag event goes to dw + // and does not go to widgetOnTop + QCOMPARE(dw.startedDrags, 1); + QCOMPARE(dw.enterEvents, 1); + QCOMPARE(widgetOnTop.enterEvents, 0); + + // Press mouse to create a drag in dw and move it to the corner of the dw where widgetOnTop is not shaped anymore + DoMouseMoveHelper aux2(&dw, QPoint(1, 1), 100); + DoMouseReleaseHelper aux3(&dw, 200); + QTest::mousePress(&dw, Qt::LeftButton); + + // Verify once we get out of the shaped area the drag event also goes to widgetOnTop + QCOMPARE(dw.startedDrags, 2); + QCOMPARE(dw.enterEvents, 2); + QCOMPARE(widgetOnTop.enterEvents, 1); +#endif +} + +void tst_QDrag::testDragEnterAllShaping() +{ +#ifdef Q_WS_X11 + // Widget of 80x80 at 300, 300 + DragCounterAndCreatorWidget dw; + QTest::qWaitForWindowShown(&dw); + + // Widget of 180x180 at 250, 250 + BiggerDragCounterWidget widgetOnTop; + QTest::qWaitForWindowShown(&widgetOnTop); + + // Make widgetOnTop totally a hole regarding input + QBitmap inputShape(180, 180); + inputShape.fill(Qt::color0); + XShapeCombineRegion(QX11Info::display(), widgetOnTop.effectiveWinId(), ShapeInput, 0, 0, QRegion(inputShape).handle(), ShapeSet); + + // Press mouse to create a drag in dw + QTest::qWait(100); + QTest::mouseMove(&dw); + DoMouseReleaseHelper aux(&dw); + QTest::mousePress(&dw, Qt::LeftButton); + + // Verify that with a input shaped widgetOnTop in the middle the drag, the drag event goes to dw + // and does not go to widgetOnTop + QCOMPARE(dw.startedDrags, 1); + QCOMPARE(dw.enterEvents, 1); + QCOMPARE(widgetOnTop.enterEvents, 0); + + // Press mouse to create a drag in dw and move it to the corner of the dw + DoMouseMoveHelper aux2(&widgetOnTop, QPoint(1, 1)); + DoMouseReleaseHelper aux3(&dw, 200); + QTest::mousePress(&dw, Qt::LeftButton); + + // Verify the event also went to dw + QCOMPARE(dw.startedDrags, 2); + QCOMPARE(dw.enterEvents, 2); + QCOMPARE(widgetOnTop.enterEvents, 0); +#endif +} + QTEST_MAIN(tst_QDrag) #include "tst_qdrag.moc" -- cgit v0.12