Fix screen handling for child windows.

Change the semantics of QWindowPrivate::screen to contain the screen of
top level window only. Child windows always return the screen of their
toplevel window by recursing up.

The QPA plugins then no longer need to report screen changes for child
windows.

Change setScreen() accordingly, bail out for child windows, and
emit screenChanged() recursively.

Also add a check to setParent() preventing screen changes.

Task-number: QTBUG-36659
Change-Id: I19c8e12217cba1513e947a027f2492abc7b98816
Reviewed-by: Shawn Rutledge <shawn.rutledge@digia.com>
Reviewed-by: Laszlo Agocs <laszlo.agocs@digia.com>
This commit is contained in:
Friedemann Kleint 2014-05-19 11:51:31 +02:00
parent a29b65af05
commit bcaf2f08d9
3 changed files with 83 additions and 37 deletions

View File

@ -1956,7 +1956,7 @@ void QGuiApplicationPrivate::processWindowScreenChangedEvent(QWindowSystemInterf
{
if (QWindow *window = wse->window.data()) {
if (QScreen *screen = wse->screen.data())
window->d_func()->setScreen(screen, false /* recreate */);
window->d_func()->setTopLevelScreen(screen, false /* recreate */);
else // Fall back to default behavior, and try to find some appropriate screen
window->setScreen(0);
}

View File

@ -156,9 +156,7 @@ QWindow::QWindow(QScreen *targetScreen)
, QSurface(QSurface::Window)
{
Q_D(QWindow);
d->screen = targetScreen;
if (!d->screen)
d->screen = QGuiApplication::primaryScreen();
d->connectToScreen(targetScreen ? targetScreen : QGuiApplication::primaryScreen());
d->init();
}
@ -178,10 +176,8 @@ QWindow::QWindow(QWindow *parent)
{
Q_D(QWindow);
d->parentWindow = parent;
if (parent)
d->screen = parent->screen();
if (!d->screen)
d->screen = QGuiApplication::primaryScreen();
if (!parent)
d->connectToScreen(QGuiApplication::primaryScreen());
d->init();
}
@ -203,10 +199,8 @@ QWindow::QWindow(QWindowPrivate &dd, QWindow *parent)
{
Q_D(QWindow);
d->parentWindow = parent;
if (parent)
d->screen = parent->screen();
if (!d->screen)
d->screen = QGuiApplication::primaryScreen();
if (!parent)
d->connectToScreen(QGuiApplication::primaryScreen());
d->init();
}
@ -231,11 +225,10 @@ void QWindowPrivate::init()
// If your application aborts here, you are probably creating a QWindow
// before the screen list is populated.
if (!screen) {
if (!parentWindow && !topLevelScreen) {
qFatal("Cannot create window: no screens available");
exit(1);
}
QObject::connect(screen, SIGNAL(destroyed(QObject*)), q, SLOT(screenDestroyed(QObject*)));
QGuiApplicationPrivate::window_list.prepend(q);
}
@ -353,23 +346,57 @@ void QWindowPrivate::updateVisibility()
emit q->visibilityChanged(visibility);
}
void QWindowPrivate::setScreen(QScreen *newScreen, bool recreate)
inline bool QWindowPrivate::windowRecreationRequired(QScreen *newScreen) const
{
Q_Q(const QWindow);
const QScreen *oldScreen = q->screen();
return oldScreen != newScreen && platformWindow
&& !(oldScreen && oldScreen->virtualSiblings().contains(newScreen));
}
inline void QWindowPrivate::disconnectFromScreen()
{
if (topLevelScreen) {
Q_Q(QWindow);
QObject::disconnect(topLevelScreen, &QObject::destroyed, q, &QWindow::screenDestroyed);
topLevelScreen = 0;
}
}
void QWindowPrivate::connectToScreen(QScreen *screen)
{
Q_Q(QWindow);
if (newScreen != screen) {
const bool shouldRecreate = recreate && platformWindow != 0
&& !(screen && screen->virtualSiblings().contains(newScreen));
disconnectFromScreen();
topLevelScreen = screen;
if (topLevelScreen)
QObject::connect(topLevelScreen, &QObject::destroyed, q, &QWindow::screenDestroyed);
}
void QWindowPrivate::emitScreenChangedRecursion(QScreen *newScreen)
{
Q_Q(QWindow);
emit q->screenChanged(newScreen);
foreach (QObject *child, q->children()) {
if (child->isWindowType())
static_cast<QWindow *>(child)->d_func()->emitScreenChangedRecursion(newScreen);
}
}
void QWindowPrivate::setTopLevelScreen(QScreen *newScreen, bool recreate)
{
Q_Q(QWindow);
if (parentWindow) {
qWarning() << this << Q_FUNC_INFO << '(' << newScreen << "): Attempt to set a screen on a child window.";
return;
}
if (newScreen != topLevelScreen) {
const bool shouldRecreate = recreate && windowRecreationRequired(newScreen);
if (shouldRecreate)
q->destroy();
if (screen)
q->disconnect(screen, SIGNAL(destroyed(QObject*)), q, SLOT(screenDestroyed(QObject*)));
screen = newScreen;
if (newScreen) {
q->connect(screen, SIGNAL(destroyed(QObject*)), q, SLOT(screenDestroyed(QObject*)));
if (shouldRecreate)
q->create();
}
emit q->screenChanged(newScreen);
connectToScreen(newScreen);
if (newScreen && shouldRecreate)
q->create();
emitScreenChangedRecursion(newScreen);
}
}
@ -552,8 +579,20 @@ QWindow *QWindow::parent() const
void QWindow::setParent(QWindow *parent)
{
Q_D(QWindow);
if (d->parentWindow == parent)
return;
QScreen *newScreen = parent ? parent->screen() : screen();
if (d->windowRecreationRequired(newScreen)) {
qWarning() << this << Q_FUNC_INFO << '(' << parent << "): Cannot change screens (" << screen() << newScreen << ')';
return;
}
QObject::setParent(parent);
if (parent)
d->disconnectFromScreen();
else
d->connectToScreen(newScreen);
if (d->platformWindow) {
if (parent && parent->d_func()->platformWindow) {
@ -1606,15 +1645,14 @@ bool QWindow::setMouseGrabEnabled(bool grab)
/*!
Returns the screen on which the window is shown.
The value returned will not change when the window is moved
between virtual screens (as returned by QScreen::virtualSiblings()).
For child windows, this returns the screen of the corresponding top level window.
\sa setScreen(), QScreen::virtualSiblings()
*/
QScreen *QWindow::screen() const
{
Q_D(const QWindow);
return d->screen;
return d->parentWindow ? d->parentWindow->screen() : d->topLevelScreen;
}
/*!
@ -1625,6 +1663,8 @@ QScreen *QWindow::screen() const
Note that if the screen is part of a virtual desktop of multiple screens,
the window can appear on any of the screens returned by QScreen::virtualSiblings().
This function only works for top level windows.
\sa screen(), QScreen::virtualSiblings()
*/
void QWindow::setScreen(QScreen *newScreen)
@ -1632,13 +1672,15 @@ void QWindow::setScreen(QScreen *newScreen)
Q_D(QWindow);
if (!newScreen)
newScreen = QGuiApplication::primaryScreen();
d->setScreen(newScreen, true /* recreate */);
d->setTopLevelScreen(newScreen, true /* recreate */);
}
void QWindow::screenDestroyed(QObject *object)
{
Q_D(QWindow);
if (object == static_cast<QObject *>(d->screen)) {
if (d->parentWindow)
return;
if (object == static_cast<QObject *>(d->topLevelScreen)) {
const bool wasVisible = isVisible();
setScreen(0);
// destroy() might have hidden our window, show it again.
@ -2326,7 +2368,7 @@ void QWindowPrivate::setCursor(const QCursor *newCursor)
hasCursor = false;
}
// Only attempt to set cursor and emit signal if there is an actual platform cursor
if (screen->handle()->cursor()) {
if (q->screen()->handle()->cursor()) {
applyCursor();
QEvent event(QEvent::CursorChange);
QGuiApplication::sendEvent(q, &event);
@ -2337,7 +2379,7 @@ void QWindowPrivate::applyCursor()
{
Q_Q(QWindow);
if (platformWindow) {
if (QPlatformCursor *platformCursor = screen->handle()->cursor()) {
if (QPlatformCursor *platformCursor = q->screen()->handle()->cursor()) {
QCursor *c = QGuiApplication::overrideCursor();
if (!c && hasCursor)
c = &cursor;

View File

@ -95,7 +95,7 @@ public:
, modality(Qt::NonModal)
, blockedByModalWindow(false)
, transientParent(0)
, screen(0)
, topLevelScreen(0)
#ifndef QT_NO_CURSOR
, cursor(Qt::ArrowCursor)
, hasCursor(false)
@ -131,7 +131,11 @@ public:
void updateVisibility();
void _q_clearAlert();
void setScreen(QScreen *newScreen, bool recreate);
bool windowRecreationRequired(QScreen *newScreen) const;
void setTopLevelScreen(QScreen *newScreen, bool recreate);
void connectToScreen(QScreen *topLevelScreen);
void disconnectFromScreen();
void emitScreenChangedRecursion(QScreen *newScreen);
virtual void clearFocusObject();
@ -165,7 +169,7 @@ public:
bool blockedByModalWindow;
QPointer<QWindow> transientParent;
QScreen *screen;
QScreen *topLevelScreen;
#ifndef QT_NO_CURSOR
QCursor cursor;