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->nativeChildrenForced = 0;
|
||||
extra->inRenderWithPainter = 0;
|
||||
extra->hasWindowContainer = false;
|
||||
extra->hasMask = 0;
|
||||
createSysExtra();
|
||||
#ifdef QWIDGET_EXTRA_DEBUG
|
||||
@ -6543,6 +6544,9 @@ void QWidget::move(const QPoint &p)
|
||||
data->crect.moveTopLeft(p); // no frame yet
|
||||
setAttribute(Qt::WA_PendingMoveEvent);
|
||||
}
|
||||
|
||||
if (d->extra && d->extra->hasWindowContainer)
|
||||
QWindowContainer::parentWasMoved(this);
|
||||
}
|
||||
|
||||
/*! \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_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);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (d->extra && d->extra->hasWindowContainer)
|
||||
QWindowContainer::parentWasChanged(this);
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -10747,6 +10757,9 @@ void QWidget::raise()
|
||||
if (testAttribute(Qt::WA_WState_Created))
|
||||
d->raise_sys();
|
||||
|
||||
if (d->extra && d->extra->hasWindowContainer)
|
||||
QWindowContainer::parentWasRaised(this);
|
||||
|
||||
QEvent e(QEvent::ZOrderChange);
|
||||
QApplication::sendEvent(this, &e);
|
||||
}
|
||||
@ -10781,6 +10794,9 @@ void QWidget::lower()
|
||||
if (testAttribute(Qt::WA_WState_Created))
|
||||
d->lower_sys();
|
||||
|
||||
if (d->extra && d->extra->hasWindowContainer)
|
||||
QWindowContainer::parentWasLowered(this);
|
||||
|
||||
QEvent e(QEvent::ZOrderChange);
|
||||
QApplication::sendEvent(this, &e);
|
||||
}
|
||||
|
@ -254,6 +254,7 @@ struct QWExtra {
|
||||
uint nativeChildrenForced : 1;
|
||||
uint inRenderWithPainter : 1;
|
||||
uint hasMask : 1;
|
||||
uint hasWindowContainer : 1;
|
||||
|
||||
// *************************** Platform specific values (bit fields first) **********
|
||||
#if defined(Q_WS_WIN) // <----------------------------------------------------------- WIN
|
||||
|
@ -42,6 +42,10 @@
|
||||
#include "qwindowcontainer_p.h"
|
||||
#include "qwidget_p.h"
|
||||
#include <QtGui/qwindow.h>
|
||||
#include <QDebug>
|
||||
|
||||
#include <QMdiSubWindow>
|
||||
#include <QAbstractScrollArea>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
@ -50,11 +54,67 @@ class QWindowContainerPrivate : public QWidgetPrivate
|
||||
public:
|
||||
Q_DECLARE_PUBLIC(QWindowContainer)
|
||||
|
||||
QWindowContainerPrivate() : window(0), oldFocusWindow(0) { }
|
||||
QWindowContainerPrivate()
|
||||
: window(0)
|
||||
, oldFocusWindow(0)
|
||||
, usesNativeWidgets(false)
|
||||
{
|
||||
}
|
||||
|
||||
~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;
|
||||
QWindow *oldFocusWindow;
|
||||
QWindow fakeParent;
|
||||
|
||||
uint usesNativeWidgets : 1;
|
||||
};
|
||||
|
||||
|
||||
@ -78,6 +138,14 @@ public:
|
||||
be removed from the window container with a call to
|
||||
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:
|
||||
|
||||
\list
|
||||
@ -86,11 +154,6 @@ public:
|
||||
widget hierarchy as an opaque box. The stacking order of multiple
|
||||
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
|
||||
with QGraphicsProxyWidget, QWidget::render() or similar functionality.
|
||||
|
||||
@ -132,13 +195,7 @@ QWindowContainer::QWindowContainer(QWindow *embeddedWindow, QWidget *parent, Qt:
|
||||
}
|
||||
|
||||
d->window = embeddedWindow;
|
||||
|
||||
// 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());
|
||||
d->window->setParent(&d->fakeParent);
|
||||
|
||||
connect(QGuiApplication::instance(), SIGNAL(focusWindowChanged(QWindow *)), this, SLOT(focusWindowChanged(QWindow *)));
|
||||
}
|
||||
@ -167,8 +224,6 @@ void QWindowContainer::focusWindowChanged(QWindow *focusWindow)
|
||||
d->oldFocusWindow = focusWindow;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
@ -190,22 +245,38 @@ bool QWindowContainer::event(QEvent *e)
|
||||
// The only thing we are interested in is making sure our sizes stay
|
||||
// in sync, so do a catch-all case.
|
||||
case QEvent::Resize:
|
||||
d->updateGeometry();
|
||||
break;
|
||||
case QEvent::Move:
|
||||
d->updateGeometry();
|
||||
break;
|
||||
case QEvent::PolishRequest:
|
||||
d->window->setGeometry(0, 0, width(), height());
|
||||
d->updateGeometry();
|
||||
break;
|
||||
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;
|
||||
case QEvent::Hide:
|
||||
d->window->hide();
|
||||
if (d->window->parent())
|
||||
d->window->hide();
|
||||
break;
|
||||
case QEvent::FocusIn:
|
||||
if (d->oldFocusWindow != d->window) {
|
||||
d->window->requestActivate();
|
||||
} else {
|
||||
QWidget *next = nextInFocusChain();
|
||||
next->setFocus();
|
||||
if (d->window->parent()) {
|
||||
if (d->oldFocusWindow != d->window) {
|
||||
d->window->requestActivate();
|
||||
} else {
|
||||
QWidget *next = nextInFocusChain();
|
||||
next->setFocus();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -215,4 +286,60 @@ bool QWindowContainer::event(QEvent *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
|
||||
|
@ -57,6 +57,11 @@ public:
|
||||
explicit QWindowContainer(QWindow *embeddedWindow, QWidget *parent = 0, Qt::WindowFlags f = 0);
|
||||
~QWindowContainer();
|
||||
|
||||
static void parentWasChanged(QWidget *parent);
|
||||
static void parentWasMoved(QWidget *parent);
|
||||
static void parentWasRaised(QWidget *parent);
|
||||
static void parentWasLowered(QWidget *parent);
|
||||
|
||||
protected:
|
||||
bool event(QEvent *ev);
|
||||
|
||||
|
@ -68,8 +68,6 @@ public:
|
||||
int numberOfObscures;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class tst_QWindowContainer: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -81,6 +79,7 @@ private slots:
|
||||
void testBehindTheScenesDeletion();
|
||||
void testUnparenting();
|
||||
void testActivation();
|
||||
void testAncestorChange();
|
||||
};
|
||||
|
||||
|
||||
@ -188,6 +187,7 @@ void tst_QWindowContainer::testActivation()
|
||||
|
||||
root.show();
|
||||
root.activateWindow();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(&root));
|
||||
|
||||
QVERIFY(QTest::qWaitForWindowActive(root.windowHandle()));
|
||||
QVERIFY(QGuiApplication::focusWindow() == root.windowHandle());
|
||||
@ -204,8 +204,7 @@ void tst_QWindowContainer::testActivation()
|
||||
QTest::qWait(100);
|
||||
|
||||
window->requestActivate();
|
||||
QVERIFY(QTest::qWaitForWindowActive(window));
|
||||
QVERIFY(QGuiApplication::focusWindow() == window);
|
||||
QTRY_VERIFY(QGuiApplication::focusWindow() == window);
|
||||
|
||||
// Verify that all states in the root widget still indicate it is active
|
||||
QVERIFY(root.windowHandle()->isActive());
|
||||
@ -231,6 +230,61 @@ void tst_QWindowContainer::testUnparenting()
|
||||
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)
|
||||
|
||||
#include "tst_qwindowcontainer.moc"
|
||||
|
Loading…
x
Reference in New Issue
Block a user