diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp index d096abd061..1f690f1e85 100644 --- a/src/quick/items/qquickrendercontrol.cpp +++ b/src/quick/items/qquickrendercontrol.cpp @@ -181,6 +181,14 @@ QQuickRenderControl::QQuickRenderControl(QObject *parent) { } +/*! + \internal +*/ +QQuickRenderControl::QQuickRenderControl(QQuickRenderControlPrivate &dd, QObject * parent) + : QObject(dd, parent) +{ +} + /*! Destroys the instance. Releases all scenegraph resources. @@ -555,6 +563,14 @@ QWindow *QQuickRenderControl::renderWindowFor(QQuickWindow *win, QPoint *offset) return nullptr; } +bool QQuickRenderControlPrivate::isRenderWindowFor(QQuickWindow *quickWin, const QWindow *renderWin) +{ + QQuickRenderControl *rc = QQuickWindowPrivate::get(quickWin)->renderControl; + if (rc) + return QQuickRenderControlPrivate::get(rc)->isRenderWindow(renderWin); + return false; +} + /*! \return the QQuickWindow this QQuickRenderControl is associated with. diff --git a/src/quick/items/qquickrendercontrol.h b/src/quick/items/qquickrendercontrol.h index 160aad73ab..3b1646c3f6 100644 --- a/src/quick/items/qquickrendercontrol.h +++ b/src/quick/items/qquickrendercontrol.h @@ -80,6 +80,9 @@ public: QQuickWindow *window() const; +protected: + QQuickRenderControl(QQuickRenderControlPrivate &dd, QObject * parent); + Q_SIGNALS: void renderRequested(); void sceneChanged(); diff --git a/src/quick/items/qquickrendercontrol_p.h b/src/quick/items/qquickrendercontrol_p.h index 9496205e23..acf84c8a23 100644 --- a/src/quick/items/qquickrendercontrol_p.h +++ b/src/quick/items/qquickrendercontrol_p.h @@ -71,6 +71,9 @@ public: return renderControl->d_func(); } + static bool isRenderWindowFor(QQuickWindow *quickWin, const QWindow *renderWin); + virtual bool isRenderWindow(const QWindow *w) { Q_UNUSED(w); return false; } + static void cleanup(); void windowDestroyed(); diff --git a/src/quick/util/qquickdeliveryagent.cpp b/src/quick/util/qquickdeliveryagent.cpp index 96a6fee35a..6c0596c4b3 100644 --- a/src/quick/util/qquickdeliveryagent.cpp +++ b/src/quick/util/qquickdeliveryagent.cpp @@ -49,7 +49,7 @@ #include #endif #include -#include +#include #include QT_BEGIN_NAMESPACE @@ -316,7 +316,7 @@ void QQuickDeliveryAgentPrivate::translateTouchEvent(QTouchEvent *touchEvent) static inline bool windowHasFocus(QQuickWindow *win) { const QWindow *focusWindow = QGuiApplication::focusWindow(); - return win == focusWindow || QQuickRenderControl::renderWindowFor(win) == focusWindow; + return win == focusWindow || QQuickRenderControlPrivate::isRenderWindowFor(win, focusWindow) || !focusWindow; } #ifdef Q_OS_WEBOS diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index d0cc6f9048..37b3823bde 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -83,6 +83,11 @@ #include #include +#include "private/qwidget_p.h" + +#include +#include + QT_BEGIN_NAMESPACE QQuickWidgetOffscreenWindow::QQuickWidgetOffscreenWindow(QQuickWindowPrivate &dd, QQuickRenderControl *control) @@ -104,19 +109,80 @@ public: } }; +class QQuickWidgetRenderControlPrivate; + class QQuickWidgetRenderControl : public QQuickRenderControl { + Q_DECLARE_PRIVATE(QQuickWidgetRenderControl) public: - QQuickWidgetRenderControl(QQuickWidget *quickwidget) : m_quickWidget(quickwidget) {} - QWindow *renderWindow(QPoint *offset) override { - if (offset) - *offset = m_quickWidget->mapTo(m_quickWidget->window(), QPoint()); - return m_quickWidget->window()->windowHandle(); + QQuickWidgetRenderControl(QQuickWidget *quickwidget); + QWindow *renderWindow(QPoint *offset) override; + +}; + +class QQuickWidgetRenderControlPrivate : public QQuickRenderControlPrivate +{ +public: + Q_DECLARE_PUBLIC(QQuickWidgetRenderControl) + QQuickWidgetRenderControlPrivate(QQuickWidgetRenderControl *renderControl, QQuickWidget *qqw) + : QQuickRenderControlPrivate(renderControl) + , m_quickWidget(qqw) + { + } + + bool isRenderWindow(const QWindow *w) override { +#if QT_CONFIG(graphicsview) + QWidgetPrivate *widgetd = QWidgetPrivate::get(m_quickWidget); + auto *proxy = (widgetd && widgetd->extra) ? widgetd->extra->proxyWidget : nullptr; + auto *scene = proxy ? proxy->scene() : nullptr; + if (scene) { + for (const auto &view : scene->views()) { + if (view->window()->windowHandle() == w) + return true; + } + } + + return m_quickWidget->window()->windowHandle() == w; +#endif } -private: QQuickWidget *m_quickWidget; }; +QQuickWidgetRenderControl::QQuickWidgetRenderControl(QQuickWidget *quickWidget) + : QQuickRenderControl(*(new QQuickWidgetRenderControlPrivate(this, quickWidget)), nullptr) +{ +} + +QWindow *QQuickWidgetRenderControl::renderWindow(QPoint *offset) +{ + Q_D(QQuickWidgetRenderControl); + if (offset) + *offset = d->m_quickWidget->mapTo(d->m_quickWidget->window(), QPoint()); + + QWindow *result = nullptr; +#if QT_CONFIG(graphicsview) + QWidgetPrivate *widgetd = QWidgetPrivate::get(d->m_quickWidget); + if (widgetd->extra) { + if (auto proxy = widgetd->extra->proxyWidget) { + auto scene = proxy->scene(); + if (scene) { + const auto views = scene->views(); + if (!views.isEmpty()) { + // Get the first QGV containing the proxy. Not ideal, but the callers + // of this function aren't prepared to handle more than one render window. + auto candidateView = views.first(); + result = candidateView->window()->windowHandle(); + } + } + } + } +#endif + if (!result) + result = d->m_quickWidget->window()->windowHandle(); + + return result; +} + void QQuickWidgetPrivate::initOffscreenWindow() { Q_Q(QQuickWidget); diff --git a/tests/auto/quickwidgets/qquickwidget/data/FocusOnClick.qml b/tests/auto/quickwidgets/qquickwidget/data/FocusOnClick.qml new file mode 100644 index 0000000000..6b1ca0f15a --- /dev/null +++ b/tests/auto/quickwidgets/qquickwidget/data/FocusOnClick.qml @@ -0,0 +1,33 @@ +import QtQuick + +Rectangle { + width: 300 + height: 100 + color: "lightblue" + Column { + Rectangle { + width: 150 + height: 50 + color: text1.activeFocus ? "pink" : "gray" + TextInput + { + id: text1 + objectName: "text1" + anchors.fill: parent + text: "Enter text" + } + } + Rectangle { + width: 150 + height: 50 + color: text2.activeFocus ? "yellow" : "lightgreen" + TextInput + { + id: text2 + objectName: "text2" + anchors.fill: parent + text: "Enter text" + } + } + } +} diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp index c564a29c51..a63ebde084 100644 --- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp +++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp @@ -48,6 +48,10 @@ #include +#if QT_CONFIG(graphicsview) +# include +# include +#endif Q_LOGGING_CATEGORY(lcTests, "qt.quick.tests") class MouseRecordingQQWidget : public QQuickWidget @@ -145,6 +149,10 @@ private slots: void tabKey(); void resizeOverlay(); void controls(); + void focusOnClick(); +#if QT_CONFIG(graphicsview) + void focusOnClickInProxyWidget(); +#endif private: QPointingDevice *device = QTest::createTouchDevice(); @@ -767,6 +775,92 @@ void tst_qquickwidget::controls() QVERIFY(QTest::qWaitForWindowExposed(&widget)); } +void tst_qquickwidget::focusOnClick() +{ + QQuickWidget quick; + quick.setSource(testFileUrl("FocusOnClick.qml")); + quick.show(); + QVERIFY(QTest::qWaitForWindowExposed(&quick)); + QQuickItem *rootItem = quick.rootObject(); + QVERIFY(rootItem); + QWindow *window = quick.windowHandle(); + QVERIFY(window); + + QQuickItem *text1 = rootItem->findChild("text1"); + QVERIFY(text1); + QQuickItem *text2 = rootItem->findChild("text2"); + QVERIFY(text2); + + QTest::mouseClick(window, Qt::LeftButton, Qt::KeyboardModifiers(), QPoint(75, 25)); + QTRY_VERIFY(text1->hasActiveFocus()); + QVERIFY(!text2->hasActiveFocus()); + + QTest::mouseClick(window, Qt::LeftButton, Qt::KeyboardModifiers(), QPoint(75, 75)); + QTRY_VERIFY(text2->hasActiveFocus()); + QVERIFY(!text1->hasActiveFocus()); + +} + +#if QT_CONFIG(graphicsview) +void tst_qquickwidget::focusOnClickInProxyWidget() +{ + QGraphicsScene scene(0,0,400,400); + + QGraphicsView view1(&scene); + view1.setFrameStyle(QFrame::NoFrame); + view1.resize(400,400); + view1.show(); + + + + QQuickWidget* quick = new QQuickWidget(testFileUrl("FocusOnClick.qml")); + quick->resize(300,100); + quick->setAttribute(Qt::WA_AcceptTouchEvents); + QGraphicsProxyWidget* proxy = scene.addWidget(quick); + proxy->setAcceptTouchEvents(true); + + // QTRY_VERIFY(quick->rootObject()); + QQuickItem *rootItem = quick->rootObject(); + QVERIFY(rootItem); + QQuickItem *text1 = rootItem->findChild("text1"); + QVERIFY(text1); + QQuickItem *text2 = rootItem->findChild("text2"); + QVERIFY(text2); + + QVERIFY(QTest::qWaitForWindowExposed(&view1)); + QWindow *window1 = view1.windowHandle(); + QVERIFY(window1); + + // Click in the QGraphicsView, outside the QuickWidget + QTest::mouseClick(window1, Qt::LeftButton, Qt::KeyboardModifiers(), QPoint(300, 300)); + QTRY_VERIFY(!text1->hasActiveFocus()); + QTRY_VERIFY(!text2->hasActiveFocus()); + + // Click on text1 + QTest::mouseClick(window1, Qt::LeftButton, Qt::KeyboardModifiers(), QPoint(75, 25)); + QTRY_VERIFY(text1->hasActiveFocus()); + QVERIFY(!text2->hasActiveFocus()); + + + // Now create a second view and repeat, in order to verify that we handle one QQuickItem being in multiple windows + QGraphicsView view2(&scene); + view2.resize(400,400); + view2.show(); + + QVERIFY(QTest::qWaitForWindowExposed(&view2)); + QWindow *window2 = view2.windowHandle(); + QVERIFY(window2); + + QTest::mouseClick(window2, Qt::LeftButton, Qt::KeyboardModifiers(), QPoint(300, 300)); + QTRY_VERIFY(!text1->hasActiveFocus()); + QTRY_VERIFY(!text2->hasActiveFocus()); + + QTest::mouseClick(window2, Qt::LeftButton, Qt::KeyboardModifiers(), QPoint(75, 25)); + QTRY_VERIFY(text1->hasActiveFocus()); + QVERIFY(!text2->hasActiveFocus()); +} +#endif + QTEST_MAIN(tst_qquickwidget) #include "tst_qquickwidget.moc"