Make QWindow::setVisible() work for widgets

QWidget has its own setVisible() code that needs to
be run in order to correctly transition widget visibility.

It is desirable to be able to show and hide (native)
widgets also from the QWindow side, for example from
the platform plugin, or from generic QWindow handling
code in QtGui.

Add a new virtual QWindowPrivate::setVisible() and
move the QWindow visibility implementation there.
Subclasses can now override this function to add custom
code.

Make QWidgetPrivate::show/hide_sys() call the QWindowPrivate
setVisible implementation instead of the QWindow setVisible
public API.

Change-Id: I082f174b100659e1221d5898b490f8a9f498abdf
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
This commit is contained in:
Morten Johan Sørvig 2017-04-19 11:19:19 +02:00
parent 3ae34b7ead
commit d7a9e08f0a
6 changed files with 120 additions and 69 deletions

View File

@ -317,6 +317,83 @@ void QWindow::setVisibility(Visibility v)
}
}
/*
Subclasses may override this function to run custom setVisible
logic. Subclasses that do so must call the base class implementation
at some point to make the native window visible, and must not
call QWindow::setVisble() since that will recurse back here.
*/
void QWindowPrivate::setVisible(bool visible)
{
Q_Q(QWindow);
if (this->visible != visible) {
this->visible = visible;
emit q->visibleChanged(visible);
updateVisibility();
} else if (platformWindow) {
// Visibility hasn't changed, and the platform window is in sync
return;
}
if (!platformWindow) {
// If we have a parent window, but the parent hasn't been created yet, we
// can defer creation until the parent is created or we're re-parented.
if (parentWindow && !parentWindow->handle())
return;
// We only need to create the window if it's being shown
if (visible)
q->create();
}
if (visible) {
// remove posted quit events when showing a new window
QCoreApplication::removePostedEvents(qApp, QEvent::Quit);
if (q->type() == Qt::Window) {
QGuiApplicationPrivate *app_priv = QGuiApplicationPrivate::instance();
QString &firstWindowTitle = app_priv->firstWindowTitle;
if (!firstWindowTitle.isEmpty()) {
q->setTitle(firstWindowTitle);
firstWindowTitle = QString();
}
if (!app_priv->forcedWindowIcon.isNull())
q->setIcon(app_priv->forcedWindowIcon);
// Handling of the -qwindowgeometry, -geometry command line arguments
static bool geometryApplied = false;
if (!geometryApplied) {
geometryApplied = true;
QGuiApplicationPrivate::applyWindowGeometrySpecificationTo(q);
}
}
QShowEvent showEvent;
QGuiApplication::sendEvent(q, &showEvent);
}
if (q->isModal()) {
if (visible)
QGuiApplicationPrivate::showModalWindow(q);
else
QGuiApplicationPrivate::hideModalWindow(q);
}
#ifndef QT_NO_CURSOR
if (visible && (hasCursor || QGuiApplication::overrideCursor()))
applyCursor();
#endif
if (platformWindow)
platformWindow->setVisible(visible);
if (!visible) {
QHideEvent hideEvent;
QGuiApplication::sendEvent(q, &hideEvent);
}
}
void QWindowPrivate::updateVisibility()
{
Q_Q(QWindow);
@ -514,71 +591,7 @@ void QWindow::setVisible(bool visible)
{
Q_D(QWindow);
if (d->visible != visible) {
d->visible = visible;
emit visibleChanged(visible);
d->updateVisibility();
} else if (d->platformWindow) {
// Visibility hasn't changed, and the platform window is in sync
return;
}
if (!d->platformWindow) {
// If we have a parent window, but the parent hasn't been created yet, we
// can defer creation until the parent is created or we're re-parented.
if (parent() && !parent()->handle())
return;
// We only need to create the window if it's being shown
if (visible)
create();
}
if (visible) {
// remove posted quit events when showing a new window
QCoreApplication::removePostedEvents(qApp, QEvent::Quit);
if (type() == Qt::Window) {
QGuiApplicationPrivate *app_priv = QGuiApplicationPrivate::instance();
QString &firstWindowTitle = app_priv->firstWindowTitle;
if (!firstWindowTitle.isEmpty()) {
setTitle(firstWindowTitle);
firstWindowTitle = QString();
}
if (!app_priv->forcedWindowIcon.isNull())
setIcon(app_priv->forcedWindowIcon);
// Handling of the -qwindowgeometry, -geometry command line arguments
static bool geometryApplied = false;
if (!geometryApplied) {
geometryApplied = true;
QGuiApplicationPrivate::applyWindowGeometrySpecificationTo(this);
}
}
QShowEvent showEvent;
QGuiApplication::sendEvent(this, &showEvent);
}
if (isModal()) {
if (visible)
QGuiApplicationPrivate::showModalWindow(this);
else
QGuiApplicationPrivate::hideModalWindow(this);
}
#ifndef QT_NO_CURSOR
if (visible && (d->hasCursor || QGuiApplication::overrideCursor()))
d->applyCursor();
#endif
if (d->platformWindow)
d->platformWindow->setVisible(visible);
if (!visible) {
QHideEvent hideEvent;
QGuiApplication::sendEvent(this, &hideEvent);
}
d->setVisible(visible);
}
bool QWindow::isVisible() const

View File

@ -132,6 +132,7 @@ public:
virtual QWindow *eventReceiver() { Q_Q(QWindow); return q; }
virtual void setVisible(bool visible);
void updateVisibility();
void _q_clearAlert();

View File

@ -7952,7 +7952,7 @@ void QWidgetPrivate::show_sys()
{
Q_Q(QWidget);
QWindow *window = q->windowHandle();
QWidgetWindow *window = qobject_cast<QWidgetWindow *>(q->windowHandle());
if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
invalidateBuffer(q->rect());
@ -7999,7 +7999,7 @@ void QWidgetPrivate::show_sys()
qt_qpa_set_cursor(q, false); // Needed in case cursor was set before show
#endif
invalidateBuffer(q->rect());
window->setVisible(true);
window->setNativeWindowVisibility(true);
// Was the window moved by the Window system or QPlatformWindow::initialGeometry() ?
if (window->isTopLevel()) {
const QPoint crectTopLeft = q->data->crect.topLeft();
@ -8091,7 +8091,7 @@ void QWidgetPrivate::hide_sys()
{
Q_Q(QWidget);
QWindow *window = q->windowHandle();
QWidgetWindow *window = qobject_cast<QWidgetWindow *>(q->windowHandle());
if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
q->setAttribute(Qt::WA_Mapped, false);
@ -8121,7 +8121,7 @@ void QWidgetPrivate::hide_sys()
}
if (window)
window->setVisible(false);
window->setNativeWindowVisibility(false);
}
/*!

View File

@ -69,6 +69,15 @@ class QWidgetWindowPrivate : public QWindowPrivate
{
Q_DECLARE_PUBLIC(QWidgetWindow)
public:
void setVisible(bool visible) override
{
Q_Q(QWidgetWindow);
if (QWidget *widget = q->widget())
widget->setVisible(visible);
else
QWindowPrivate::setVisible(visible);
}
QWindow *eventReceiver() Q_DECL_OVERRIDE {
Q_Q(QWidgetWindow);
QWindow *w = q;
@ -164,6 +173,15 @@ QObject *QWidgetWindow::focusObject() const
return widget;
}
void QWidgetWindow::setNativeWindowVisibility(bool visible)
{
Q_D(QWidgetWindow);
// Call base class setVisible() implementation to run the QWindow
// visibility logic. Don't call QWidgetWindowPrivate::setVisible()
// since that will recurse back into QWidget code.
d->QWindowPrivate::setVisible(visible);
}
static inline bool shouldBePropagatedToWidget(QEvent *event)
{
switch (event->type()) {

View File

@ -63,10 +63,12 @@ QT_BEGIN_NAMESPACE
class QCloseEvent;
class QMoveEvent;
class QWidgetWindowPrivate;
class QWidgetWindow : public QWindow
{
Q_OBJECT
Q_DECLARE_PRIVATE(QWidgetWindow)
public:
QWidgetWindow(QWidget *widget);
~QWidgetWindow();
@ -77,6 +79,7 @@ public:
#endif
QObject *focusObject() const Q_DECL_OVERRIDE;
void setNativeWindowVisibility(bool visible);
protected:
bool event(QEvent *) Q_DECL_OVERRIDE;

View File

@ -102,6 +102,8 @@ private slots:
void setWindowState_data();
void setWindowState();
void nativeShow();
};
void tst_QWidget_window::initTestCase()
@ -915,5 +917,19 @@ void tst_QWidget_window::setWindowState()
QCOMPARE(w.windowHandle()->windowStates(), state | Qt::WindowMinimized);
}
void tst_QWidget_window::nativeShow()
{
// Verify that a native widget can be shown using the QWindow::setVisible() API
QWidget w;
w.winId();
w.windowHandle()->setVisible(true);
QTest::qWaitForWindowExposed(&w);
QVERIFY(w.isVisible());
// ... and that we can hide it
w.windowHandle()->setVisible(false);
QTRY_VERIFY(!w.isVisible());
}
QTEST_MAIN(tst_QWidget_window)
#include "tst_qwidget_window.moc"