Skip to content

Commit

Permalink
QtWidgets: Send show/hide event to children on restore/minimize
Browse files Browse the repository at this point in the history
Child widgets should get the show/hide event when the TLW changes its
state, because child widgets are also visible or invisible. This
restores the Qt4 behavior (fixes the Qt4->Qt5 regression).

Restoring/minimizing the TLW now sends the spontaneous show/hide event.

Show events are now handled also in the expose event handler in the
QWidgetWindow class, because the show event must occur before the
expose event to avoid possible flicker e.g. the OpenGL content. This
can happen e.g. on XCB platform. If the "WindowStateChange" event occur
before the expose event (e.g. Windows platform) then the code in expose
event handler will be ignored to prevent event duplications.

Added autotest.

Task-number: QTBUG-50589
Change-Id: Ie9a9329b1f29bff876de28d5948d0d5fb6bc1f05
Reviewed-by: Friedemann Kleint <[email protected]>
  • Loading branch information
zaps166 committed Sep 12, 2016
1 parent 66fcd0c commit 3b8df0e
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 10 deletions.
18 changes: 15 additions & 3 deletions src/widgets/kernel/qwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,8 @@ QWidgetPrivate::QWidgetPrivate(int version)
, renderToTextureReallyDirty(1)
, renderToTextureComposeActive(0)
#endif
, childrenHiddenByWState(0)
, childrenShownByExpose(0)
#if defined(Q_OS_WIN)
, noPaintOnScreen(0)
#endif
Expand Down Expand Up @@ -9005,13 +9007,23 @@ bool QWidget::event(QEvent *event)
case QEvent::WindowStateChange: {
const bool wasMinimized = static_cast<const QWindowStateChangeEvent *>(event)->oldState() & Qt::WindowMinimized;
if (wasMinimized != isMinimized()) {
QWidget *widget = const_cast<QWidget *>(this);
if (wasMinimized) {
QShowEvent showEvent;
QCoreApplication::sendEvent(const_cast<QWidget *>(this), &showEvent);
// Always send the spontaneous events here, otherwise it can break the application!
if (!d->childrenShownByExpose) {
// Show widgets only when they are not yet shown by the expose event
d->showChildren(true);
QShowEvent showEvent;
QCoreApplication::sendSpontaneousEvent(widget, &showEvent);
}
d->childrenHiddenByWState = false; // Set it always to "false" when window is restored
} else {
QHideEvent hideEvent;
QCoreApplication::sendEvent(const_cast<QWidget *>(this), &hideEvent);
QCoreApplication::sendSpontaneousEvent(widget, &hideEvent);
d->hideChildren(true);
d->childrenHiddenByWState = true;
}
d->childrenShownByExpose = false; // Set it always to "false" when window state changes
}
changeEvent(event);
}
Expand Down
2 changes: 2 additions & 0 deletions src/widgets/kernel/qwidget_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,8 @@ class Q_WIDGETS_EXPORT QWidgetPrivate : public QObjectPrivate
uint renderToTextureReallyDirty : 1;
uint renderToTextureComposeActive : 1;
#endif
uint childrenHiddenByWState : 1;
uint childrenShownByExpose : 1;

// *************************** Platform specific ************************************
#if defined(Q_OS_WIN)
Expand Down
34 changes: 32 additions & 2 deletions src/widgets/kernel/qwidgetwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -871,10 +871,40 @@ void QWidgetWindow::handleDropEvent(QDropEvent *event)

void QWidgetWindow::handleExposeEvent(QExposeEvent *event)
{
if (isExposed()) {
QWidgetPrivate *wPriv = m_widget->d_func();
const bool exposed = isExposed();

if (wPriv->childrenHiddenByWState) {
// If widgets has been previously hidden by window state change event
// and they aren't yet shown...
if (exposed) {
// If the window becomes exposed...
if (!wPriv->childrenShownByExpose) {
// ... and they haven't been shown by this function yet - show it.
wPriv->showChildren(true);
QShowEvent showEvent;
QCoreApplication::sendSpontaneousEvent(m_widget, &showEvent);
wPriv->childrenShownByExpose = true;
}
} else {
// If the window becomes not exposed...
if (wPriv->childrenShownByExpose) {
// ... and child widgets was previously shown by the expose event - hide widgets again.
// This is a workaround, because sometimes when window is minimized programatically,
// the QPA can notify that the window is exposed after changing window state to minimized
// and then, the QPA can send next expose event with null exposed region (not exposed).
wPriv->hideChildren(true);
QHideEvent hideEvent;
QCoreApplication::sendSpontaneousEvent(m_widget, &hideEvent);
wPriv->childrenShownByExpose = false;
}
}
}

if (exposed) {
m_widget->setAttribute(Qt::WA_Mapped);
if (!event->region().isNull())
m_widget->d_func()->syncBackingStore(event->region());
wPriv->syncBackingStore(event->region());
} else {
m_widget->setAttribute(Qt::WA_Mapped, false);
}
Expand Down
48 changes: 43 additions & 5 deletions tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ private slots:
void showHideEvent_data();
void showHideEvent();
void showHideEventWhileMinimize();
void showHideChildrenWhileMinimize_QTBUG50589();

void lostUpdatesOnHide();

Expand Down Expand Up @@ -4078,19 +4079,30 @@ class ShowHideEventWidget : public QWidget
{
public:
int numberOfShowEvents, numberOfHideEvents;
int numberOfSpontaneousShowEvents, numberOfSpontaneousHideEvents;

ShowHideEventWidget(QWidget *parent = 0)
: QWidget(parent), numberOfShowEvents(0), numberOfHideEvents(0)
: QWidget(parent)
, numberOfShowEvents(0), numberOfHideEvents(0)
, numberOfSpontaneousShowEvents(0), numberOfSpontaneousHideEvents(0)
{ }

void create()
{ QWidget::create(); }

void showEvent(QShowEvent *)
{ ++numberOfShowEvents; }
void showEvent(QShowEvent *e)
{
++numberOfShowEvents;
if (e->spontaneous())
++numberOfSpontaneousShowEvents;
}

void hideEvent(QHideEvent *)
{ ++numberOfHideEvents; }
void hideEvent(QHideEvent *e)
{
++numberOfHideEvents;
if (e->spontaneous())
++numberOfSpontaneousHideEvents;
}
};

void tst_QWidget::showHideEvent_data()
Expand Down Expand Up @@ -4182,6 +4194,32 @@ void tst_QWidget::showHideEventWhileMinimize()
QTRY_COMPARE(widget.numberOfShowEvents, showEventsBeforeMinimize + 1);
}

void tst_QWidget::showHideChildrenWhileMinimize_QTBUG50589()
{
const QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration();
if (!pi->hasCapability(QPlatformIntegration::MultipleWindows)
|| !pi->hasCapability(QPlatformIntegration::NonFullScreenWindows)
|| !pi->hasCapability(QPlatformIntegration::WindowManagement)) {
QSKIP("This test requires window management capabilities");
}

QWidget parent;
ShowHideEventWidget child(&parent);

parent.setWindowTitle(QTest::currentTestFunction());
parent.resize(m_testWidgetSize);
centerOnScreen(&parent);
parent.show();
QVERIFY(QTest::qWaitForWindowExposed(&parent));

const int showEventsBeforeMinimize = child.numberOfSpontaneousShowEvents;
const int hideEventsBeforeMinimize = child.numberOfSpontaneousHideEvents;
parent.showMinimized();
QTRY_COMPARE(child.numberOfSpontaneousHideEvents, hideEventsBeforeMinimize + 1);
parent.showNormal();
QTRY_COMPARE(child.numberOfSpontaneousShowEvents, showEventsBeforeMinimize + 1);
}

void tst_QWidget::update()
{
#ifdef Q_OS_OSX
Expand Down

0 comments on commit 3b8df0e

Please sign in to comment.