Better QWindowContainer by not relying on native widgets.
We change the behavior slightly from the initial implementation in 5.1. Forcing the use of native child widgets is causing massive performance issues so instead, we attach the embedded QWindow directly to the root window. The only exception is QScrollArea and QMdiArea which still enforces native windows for the entire parent chain to make clipping and stacking work. Task-number: QTBUG-34138 Change-Id: If713637bd4dce630552ace2f8ad6b2e86c063721 Reviewed-by: Lars Knoll <lars.knoll@digia.com>
This commit is contained in:
parent
c9ad904af9
commit
72a7882cec
@ -1594,6 +1594,7 @@ void QWidgetPrivate::createExtra()
|
|||||||
extra->autoFillBackground = 0;
|
extra->autoFillBackground = 0;
|
||||||
extra->nativeChildrenForced = 0;
|
extra->nativeChildrenForced = 0;
|
||||||
extra->inRenderWithPainter = 0;
|
extra->inRenderWithPainter = 0;
|
||||||
|
extra->hasWindowContainer = false;
|
||||||
extra->hasMask = 0;
|
extra->hasMask = 0;
|
||||||
createSysExtra();
|
createSysExtra();
|
||||||
#ifdef QWIDGET_EXTRA_DEBUG
|
#ifdef QWIDGET_EXTRA_DEBUG
|
||||||
@ -6543,6 +6544,9 @@ void QWidget::move(const QPoint &p)
|
|||||||
data->crect.moveTopLeft(p); // no frame yet
|
data->crect.moveTopLeft(p); // no frame yet
|
||||||
setAttribute(Qt::WA_PendingMoveEvent);
|
setAttribute(Qt::WA_PendingMoveEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (d->extra && d->extra->hasWindowContainer)
|
||||||
|
QWindowContainer::parentWasMoved(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! \fn void QWidget::resize(int w, int h)
|
/*! \fn void QWidget::resize(int w, int h)
|
||||||
@ -6581,6 +6585,9 @@ void QWidget::setGeometry(const QRect &r)
|
|||||||
setAttribute(Qt::WA_PendingMoveEvent);
|
setAttribute(Qt::WA_PendingMoveEvent);
|
||||||
setAttribute(Qt::WA_PendingResizeEvent);
|
setAttribute(Qt::WA_PendingResizeEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (d->extra && d->extra->hasWindowContainer)
|
||||||
|
QWindowContainer::parentWasMoved(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -9715,6 +9722,9 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
|
|||||||
ancestorProxy->d_func()->embedSubWindow(this);
|
ancestorProxy->d_func()->embedSubWindow(this);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (d->extra && d->extra->hasWindowContainer)
|
||||||
|
QWindowContainer::parentWasChanged(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -10747,6 +10757,9 @@ void QWidget::raise()
|
|||||||
if (testAttribute(Qt::WA_WState_Created))
|
if (testAttribute(Qt::WA_WState_Created))
|
||||||
d->raise_sys();
|
d->raise_sys();
|
||||||
|
|
||||||
|
if (d->extra && d->extra->hasWindowContainer)
|
||||||
|
QWindowContainer::parentWasRaised(this);
|
||||||
|
|
||||||
QEvent e(QEvent::ZOrderChange);
|
QEvent e(QEvent::ZOrderChange);
|
||||||
QApplication::sendEvent(this, &e);
|
QApplication::sendEvent(this, &e);
|
||||||
}
|
}
|
||||||
@ -10781,6 +10794,9 @@ void QWidget::lower()
|
|||||||
if (testAttribute(Qt::WA_WState_Created))
|
if (testAttribute(Qt::WA_WState_Created))
|
||||||
d->lower_sys();
|
d->lower_sys();
|
||||||
|
|
||||||
|
if (d->extra && d->extra->hasWindowContainer)
|
||||||
|
QWindowContainer::parentWasLowered(this);
|
||||||
|
|
||||||
QEvent e(QEvent::ZOrderChange);
|
QEvent e(QEvent::ZOrderChange);
|
||||||
QApplication::sendEvent(this, &e);
|
QApplication::sendEvent(this, &e);
|
||||||
}
|
}
|
||||||
|
@ -254,6 +254,7 @@ struct QWExtra {
|
|||||||
uint nativeChildrenForced : 1;
|
uint nativeChildrenForced : 1;
|
||||||
uint inRenderWithPainter : 1;
|
uint inRenderWithPainter : 1;
|
||||||
uint hasMask : 1;
|
uint hasMask : 1;
|
||||||
|
uint hasWindowContainer : 1;
|
||||||
|
|
||||||
// *************************** Platform specific values (bit fields first) **********
|
// *************************** Platform specific values (bit fields first) **********
|
||||||
#if defined(Q_WS_WIN) // <----------------------------------------------------------- WIN
|
#if defined(Q_WS_WIN) // <----------------------------------------------------------- WIN
|
||||||
|
@ -42,6 +42,10 @@
|
|||||||
#include "qwindowcontainer_p.h"
|
#include "qwindowcontainer_p.h"
|
||||||
#include "qwidget_p.h"
|
#include "qwidget_p.h"
|
||||||
#include <QtGui/qwindow.h>
|
#include <QtGui/qwindow.h>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include <QMdiSubWindow>
|
||||||
|
#include <QAbstractScrollArea>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
@ -50,11 +54,67 @@ class QWindowContainerPrivate : public QWidgetPrivate
|
|||||||
public:
|
public:
|
||||||
Q_DECLARE_PUBLIC(QWindowContainer)
|
Q_DECLARE_PUBLIC(QWindowContainer)
|
||||||
|
|
||||||
QWindowContainerPrivate() : window(0), oldFocusWindow(0) { }
|
QWindowContainerPrivate()
|
||||||
|
: window(0)
|
||||||
|
, oldFocusWindow(0)
|
||||||
|
, usesNativeWidgets(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
~QWindowContainerPrivate() { }
|
~QWindowContainerPrivate() { }
|
||||||
|
|
||||||
|
static QWindowContainerPrivate *get(QWidget *w) {
|
||||||
|
QWindowContainer *wc = qobject_cast<QWindowContainer *>(w);
|
||||||
|
if (wc)
|
||||||
|
return wc->d_func();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateGeometry() {
|
||||||
|
Q_Q(QWindowContainer);
|
||||||
|
if (usesNativeWidgets)
|
||||||
|
window->setGeometry(q->rect());
|
||||||
|
else
|
||||||
|
window->setGeometry(QRect(q->mapTo(q->window(), QPoint()), q->size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateUsesNativeWidgets()
|
||||||
|
{
|
||||||
|
if (usesNativeWidgets || window->parent() == 0)
|
||||||
|
return;
|
||||||
|
Q_Q(QWindowContainer);
|
||||||
|
QWidget *p = q->parentWidget();
|
||||||
|
while (p) {
|
||||||
|
if (qobject_cast<QMdiSubWindow *>(p) != 0
|
||||||
|
|| qobject_cast<QAbstractScrollArea *>(p) != 0) {
|
||||||
|
q->winId();
|
||||||
|
usesNativeWidgets = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p = p->parentWidget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void markParentChain() {
|
||||||
|
Q_Q(QWindowContainer);
|
||||||
|
QWidget *p = q;
|
||||||
|
while (p) {
|
||||||
|
QWidgetPrivate *d = static_cast<QWidgetPrivate *>(QWidgetPrivate::get(p));
|
||||||
|
d->createExtra();
|
||||||
|
d->extra->hasWindowContainer = true;
|
||||||
|
p = p->parentWidget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isStillAnOrphan() const {
|
||||||
|
return window->parent() == &fakeParent;
|
||||||
|
}
|
||||||
|
|
||||||
QPointer<QWindow> window;
|
QPointer<QWindow> window;
|
||||||
QWindow *oldFocusWindow;
|
QWindow *oldFocusWindow;
|
||||||
|
QWindow fakeParent;
|
||||||
|
|
||||||
|
uint usesNativeWidgets : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -78,6 +138,14 @@ public:
|
|||||||
be removed from the window container with a call to
|
be removed from the window container with a call to
|
||||||
QWindow::setParent().
|
QWindow::setParent().
|
||||||
|
|
||||||
|
The window container is attached as a native child window to the
|
||||||
|
toplevel window it is a child of. When a window container is used
|
||||||
|
as a child of a QAbstractScrollArea or QMdiArea, it will
|
||||||
|
create a \l {Native Widgets vs Alien Widgets} {native window} for
|
||||||
|
every widget in its parent chain to allow for proper stacking and
|
||||||
|
clipping in this use case. Applications with many native child
|
||||||
|
windows may suffer from performance issues.
|
||||||
|
|
||||||
The window container has a number of known limitations:
|
The window container has a number of known limitations:
|
||||||
|
|
||||||
\list
|
\list
|
||||||
@ -86,11 +154,6 @@ public:
|
|||||||
widget hierarchy as an opaque box. The stacking order of multiple
|
widget hierarchy as an opaque box. The stacking order of multiple
|
||||||
overlapping window container instances is undefined.
|
overlapping window container instances is undefined.
|
||||||
|
|
||||||
\li Window Handles; The window container will explicitly invoke
|
|
||||||
winId() which will force the use of native window handles
|
|
||||||
inside the application. See \l {Native Widgets vs Alien Widgets}
|
|
||||||
{QWidget documentation} for more details.
|
|
||||||
|
|
||||||
\li Rendering Integration; The window container does not interoperate
|
\li Rendering Integration; The window container does not interoperate
|
||||||
with QGraphicsProxyWidget, QWidget::render() or similar functionality.
|
with QGraphicsProxyWidget, QWidget::render() or similar functionality.
|
||||||
|
|
||||||
@ -132,13 +195,7 @@ QWindowContainer::QWindowContainer(QWindow *embeddedWindow, QWidget *parent, Qt:
|
|||||||
}
|
}
|
||||||
|
|
||||||
d->window = embeddedWindow;
|
d->window = embeddedWindow;
|
||||||
|
d->window->setParent(&d->fakeParent);
|
||||||
// We force this window to become a native window and reparent the
|
|
||||||
// window directly to it. This is done so that the order in which
|
|
||||||
// the QWindowContainer is added to a QWidget tree and when it
|
|
||||||
// gets a window does not matter.
|
|
||||||
winId();
|
|
||||||
d->window->setParent(windowHandle());
|
|
||||||
|
|
||||||
connect(QGuiApplication::instance(), SIGNAL(focusWindowChanged(QWindow *)), this, SLOT(focusWindowChanged(QWindow *)));
|
connect(QGuiApplication::instance(), SIGNAL(focusWindowChanged(QWindow *)), this, SLOT(focusWindowChanged(QWindow *)));
|
||||||
}
|
}
|
||||||
@ -167,8 +224,6 @@ void QWindowContainer::focusWindowChanged(QWindow *focusWindow)
|
|||||||
d->oldFocusWindow = focusWindow;
|
d->oldFocusWindow = focusWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\internal
|
\internal
|
||||||
*/
|
*/
|
||||||
@ -190,22 +245,38 @@ bool QWindowContainer::event(QEvent *e)
|
|||||||
// The only thing we are interested in is making sure our sizes stay
|
// The only thing we are interested in is making sure our sizes stay
|
||||||
// in sync, so do a catch-all case.
|
// in sync, so do a catch-all case.
|
||||||
case QEvent::Resize:
|
case QEvent::Resize:
|
||||||
|
d->updateGeometry();
|
||||||
|
break;
|
||||||
case QEvent::Move:
|
case QEvent::Move:
|
||||||
|
d->updateGeometry();
|
||||||
|
break;
|
||||||
case QEvent::PolishRequest:
|
case QEvent::PolishRequest:
|
||||||
d->window->setGeometry(0, 0, width(), height());
|
d->updateGeometry();
|
||||||
break;
|
break;
|
||||||
case QEvent::Show:
|
case QEvent::Show:
|
||||||
d->window->show();
|
d->updateUsesNativeWidgets();
|
||||||
|
if (d->isStillAnOrphan()) {
|
||||||
|
d->window->setParent(d->usesNativeWidgets
|
||||||
|
? windowHandle()
|
||||||
|
: window()->windowHandle());
|
||||||
|
}
|
||||||
|
if (d->window->parent()) {
|
||||||
|
d->markParentChain();
|
||||||
|
d->window->show();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case QEvent::Hide:
|
case QEvent::Hide:
|
||||||
d->window->hide();
|
if (d->window->parent())
|
||||||
|
d->window->hide();
|
||||||
break;
|
break;
|
||||||
case QEvent::FocusIn:
|
case QEvent::FocusIn:
|
||||||
if (d->oldFocusWindow != d->window) {
|
if (d->window->parent()) {
|
||||||
d->window->requestActivate();
|
if (d->oldFocusWindow != d->window) {
|
||||||
} else {
|
d->window->requestActivate();
|
||||||
QWidget *next = nextInFocusChain();
|
} else {
|
||||||
next->setFocus();
|
QWidget *next = nextInFocusChain();
|
||||||
|
next->setFocus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -215,4 +286,60 @@ bool QWindowContainer::event(QEvent *e)
|
|||||||
return QWidget::event(e);
|
return QWidget::event(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef void (*qwindowcontainer_traverse_callback)(QWidget *parent);
|
||||||
|
static void qwindowcontainer_traverse(QWidget *parent, qwindowcontainer_traverse_callback callback)
|
||||||
|
{
|
||||||
|
const QObjectList &children = parent->children();
|
||||||
|
for (int i=0; i<children.size(); ++i) {
|
||||||
|
QWidget *w = qobject_cast<QWidget *>(children.at(i));
|
||||||
|
if (w) {
|
||||||
|
QWidgetPrivate *wd = static_cast<QWidgetPrivate *>(QWidgetPrivate::get(w));
|
||||||
|
if (wd->extra && wd->extra->hasWindowContainer)
|
||||||
|
callback(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QWindowContainer::parentWasChanged(QWidget *parent)
|
||||||
|
{
|
||||||
|
if (QWindowContainerPrivate *d = QWindowContainerPrivate::get(parent)) {
|
||||||
|
if (d->window->parent()) {
|
||||||
|
d->updateUsesNativeWidgets();
|
||||||
|
d->markParentChain();
|
||||||
|
d->window->setParent(d->usesNativeWidgets
|
||||||
|
? parent->windowHandle()
|
||||||
|
: parent->window()->windowHandle());
|
||||||
|
d->updateGeometry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qwindowcontainer_traverse(parent, parentWasChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QWindowContainer::parentWasMoved(QWidget *parent)
|
||||||
|
{
|
||||||
|
if (QWindowContainerPrivate *d = QWindowContainerPrivate::get(parent)) {
|
||||||
|
if (d->window->parent())
|
||||||
|
d->updateGeometry();
|
||||||
|
}
|
||||||
|
qwindowcontainer_traverse(parent, parentWasMoved);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QWindowContainer::parentWasRaised(QWidget *parent)
|
||||||
|
{
|
||||||
|
if (QWindowContainerPrivate *d = QWindowContainerPrivate::get(parent)) {
|
||||||
|
if (d->window->parent())
|
||||||
|
d->window->raise();
|
||||||
|
}
|
||||||
|
qwindowcontainer_traverse(parent, parentWasRaised);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QWindowContainer::parentWasLowered(QWidget *parent)
|
||||||
|
{
|
||||||
|
if (QWindowContainerPrivate *d = QWindowContainerPrivate::get(parent)) {
|
||||||
|
if (d->window->parent())
|
||||||
|
d->window->lower();
|
||||||
|
}
|
||||||
|
qwindowcontainer_traverse(parent, parentWasLowered);
|
||||||
|
}
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -57,6 +57,11 @@ public:
|
|||||||
explicit QWindowContainer(QWindow *embeddedWindow, QWidget *parent = 0, Qt::WindowFlags f = 0);
|
explicit QWindowContainer(QWindow *embeddedWindow, QWidget *parent = 0, Qt::WindowFlags f = 0);
|
||||||
~QWindowContainer();
|
~QWindowContainer();
|
||||||
|
|
||||||
|
static void parentWasChanged(QWidget *parent);
|
||||||
|
static void parentWasMoved(QWidget *parent);
|
||||||
|
static void parentWasRaised(QWidget *parent);
|
||||||
|
static void parentWasLowered(QWidget *parent);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool event(QEvent *ev);
|
bool event(QEvent *ev);
|
||||||
|
|
||||||
|
@ -68,8 +68,6 @@ public:
|
|||||||
int numberOfObscures;
|
int numberOfObscures;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class tst_QWindowContainer: public QObject
|
class tst_QWindowContainer: public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -81,6 +79,7 @@ private slots:
|
|||||||
void testBehindTheScenesDeletion();
|
void testBehindTheScenesDeletion();
|
||||||
void testUnparenting();
|
void testUnparenting();
|
||||||
void testActivation();
|
void testActivation();
|
||||||
|
void testAncestorChange();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -188,6 +187,7 @@ void tst_QWindowContainer::testActivation()
|
|||||||
|
|
||||||
root.show();
|
root.show();
|
||||||
root.activateWindow();
|
root.activateWindow();
|
||||||
|
QVERIFY(QTest::qWaitForWindowExposed(&root));
|
||||||
|
|
||||||
QVERIFY(QTest::qWaitForWindowActive(root.windowHandle()));
|
QVERIFY(QTest::qWaitForWindowActive(root.windowHandle()));
|
||||||
QVERIFY(QGuiApplication::focusWindow() == root.windowHandle());
|
QVERIFY(QGuiApplication::focusWindow() == root.windowHandle());
|
||||||
@ -204,8 +204,7 @@ void tst_QWindowContainer::testActivation()
|
|||||||
QTest::qWait(100);
|
QTest::qWait(100);
|
||||||
|
|
||||||
window->requestActivate();
|
window->requestActivate();
|
||||||
QVERIFY(QTest::qWaitForWindowActive(window));
|
QTRY_VERIFY(QGuiApplication::focusWindow() == window);
|
||||||
QVERIFY(QGuiApplication::focusWindow() == window);
|
|
||||||
|
|
||||||
// Verify that all states in the root widget still indicate it is active
|
// Verify that all states in the root widget still indicate it is active
|
||||||
QVERIFY(root.windowHandle()->isActive());
|
QVERIFY(root.windowHandle()->isActive());
|
||||||
@ -231,6 +230,61 @@ void tst_QWindowContainer::testUnparenting()
|
|||||||
QVERIFY(!window->isVisible());
|
QVERIFY(!window->isVisible());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QWindowContainer::testAncestorChange()
|
||||||
|
{
|
||||||
|
QWidget root;
|
||||||
|
QWidget *left = new QWidget(&root);
|
||||||
|
QWidget *right = new QWidget(&root);
|
||||||
|
|
||||||
|
root.setGeometry(0, 0, 200, 100);
|
||||||
|
left->setGeometry(0, 0, 100, 100);
|
||||||
|
right->setGeometry(100, 0, 100, 100);
|
||||||
|
|
||||||
|
QWindow *window = new QWindow();
|
||||||
|
QWidget *container = QWidget::createWindowContainer(window, left);
|
||||||
|
container->setGeometry(0, 0, 100, 100);
|
||||||
|
|
||||||
|
// Root
|
||||||
|
// + left
|
||||||
|
// | + container
|
||||||
|
// | + window
|
||||||
|
// + right
|
||||||
|
root.show();
|
||||||
|
QVERIFY(QTest::qWaitForWindowExposed(&root));
|
||||||
|
QCOMPARE(window->geometry(), QRect(0, 0, 100, 100));
|
||||||
|
|
||||||
|
container->setParent(right);
|
||||||
|
// Root
|
||||||
|
// + left
|
||||||
|
// + right
|
||||||
|
// + container
|
||||||
|
// + window
|
||||||
|
QCOMPARE(window->geometry(), QRect(100, 0, 100, 100));
|
||||||
|
|
||||||
|
QWidget *newRoot = new QWidget(&root);
|
||||||
|
newRoot->setGeometry(50, 50, 200, 200);
|
||||||
|
right->setParent(newRoot);
|
||||||
|
// Root
|
||||||
|
// + left
|
||||||
|
// + newRoot
|
||||||
|
// + right
|
||||||
|
// + container
|
||||||
|
// + window
|
||||||
|
QCOMPARE(window->geometry(), QRect(150, 50, 100, 100));
|
||||||
|
newRoot->move(0, 0);
|
||||||
|
QCOMPARE(window->geometry(), QRect(100, 0, 100, 100));
|
||||||
|
|
||||||
|
newRoot->setParent(0);
|
||||||
|
newRoot->setGeometry(100, 100, 200, 200);
|
||||||
|
newRoot->show();
|
||||||
|
QVERIFY(QTest::qWaitForWindowExposed(newRoot));
|
||||||
|
// newRoot
|
||||||
|
// + right
|
||||||
|
// + container
|
||||||
|
// + window
|
||||||
|
QCOMPARE(window->geometry(), QRect(100, 0, 100, 100));
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_QWindowContainer)
|
QTEST_MAIN(tst_QWindowContainer)
|
||||||
|
|
||||||
#include "tst_qwindowcontainer.moc"
|
#include "tst_qwindowcontainer.moc"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user