Fix focus for items inside a QQuickWidget in a QGraphicsProxyWidget
QQuickWidgetRenderControl::renderWindowFor() did not take the proxy widget into account, making it impossible to give focus to items inside a QGraphicsProxyWidget by clicking on the item. This patch is based on a patch by Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>, which fixed renderWindowFor(), but did not handle the case where a proxy widget was in multiple views. This version of the patch adds QQuickRenderControlPrivate::isRenderWindowFor(), which allows all the views of the proxy widget to handle focus. This patch also carefully preserves the non-obvious feature of the previous implementation where all windows are considered to have focus if QGuiApplication::focusWindow() == nullptr. [ChangeLog][QuickWidget][Quick items inside a QuickWidget that is inside a QGraphicsProxyWidget can now get focus by clicking.] Fixes: QTBUG-91479 Pick-to: 6.2 Change-Id: I4a6fbbbeda2d14b5a6d8eb8218d5b14a3404d9c3 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
parent
61e325460c
commit
8c0b1e06d9
|
@ -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.
|
||||
|
||||
|
|
|
@ -80,6 +80,9 @@ public:
|
|||
|
||||
QQuickWindow *window() const;
|
||||
|
||||
protected:
|
||||
QQuickRenderControl(QQuickRenderControlPrivate &dd, QObject * parent);
|
||||
|
||||
Q_SIGNALS:
|
||||
void renderRequested();
|
||||
void sceneChanged();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
#include <QtQuick/private/qquickdrag_p.h>
|
||||
#endif
|
||||
#include <QtQuick/private/qquickprofiler_p.h>
|
||||
#include <QtQuick/qquickrendercontrol.h>
|
||||
#include <QtQuick/private/qquickrendercontrol_p.h>
|
||||
#include <QtQuick/private/qquickwindow_p.h>
|
||||
|
||||
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
|
||||
|
|
|
@ -83,6 +83,11 @@
|
|||
#include <QtQuick/qquickgraphicsdevice.h>
|
||||
#include <QtQuick/qquickrendertarget.h>
|
||||
|
||||
#include "private/qwidget_p.h"
|
||||
|
||||
#include <QtWidgets/qgraphicsscene.h>
|
||||
#include <QtWidgets/qgraphicsview.h>
|
||||
|
||||
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);
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -48,6 +48,10 @@
|
|||
|
||||
#include <QtQuickWidgets/QQuickWidget>
|
||||
|
||||
#if QT_CONFIG(graphicsview)
|
||||
# include <QtWidgets/QGraphicsView>
|
||||
# include <QtWidgets/QGraphicsProxyWidget>
|
||||
#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<QQuickItem *>("text1");
|
||||
QVERIFY(text1);
|
||||
QQuickItem *text2 = rootItem->findChild<QQuickItem *>("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<QQuickItem *>("text1");
|
||||
QVERIFY(text1);
|
||||
QQuickItem *text2 = rootItem->findChild<QQuickItem *>("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"
|
||||
|
|
Loading…
Reference in New Issue