Fixes crash in QWidget::effectiveWinId.
Widgets are left in a transitional (and incosistent) state while being re-parented, which caused QWidget::effectiveWinId() to crash in certain circumstances. See patch for more details. Auto test included. Reviewed-by: ogoffart (cherry picked from commit 6db0153cd7e35e4a919a76ae2aadbf2d2510bfb7)
This commit is contained in:
parent
7ee981a834
commit
1e41e3076a
@ -299,6 +299,7 @@ QWidgetPrivate::QWidgetPrivate(int version)
|
|||||||
#ifndef QT_NO_IM
|
#ifndef QT_NO_IM
|
||||||
, inheritsInputMethodHints(0)
|
, inheritsInputMethodHints(0)
|
||||||
#endif
|
#endif
|
||||||
|
, inSetParent(0)
|
||||||
#if defined(Q_WS_X11)
|
#if defined(Q_WS_X11)
|
||||||
, picture(0)
|
, picture(0)
|
||||||
#elif defined(Q_WS_WIN)
|
#elif defined(Q_WS_WIN)
|
||||||
@ -2599,6 +2600,22 @@ WId QWidget::effectiveWinId() const
|
|||||||
if (id || !testAttribute(Qt::WA_WState_Created))
|
if (id || !testAttribute(Qt::WA_WState_Created))
|
||||||
return id;
|
return id;
|
||||||
QWidget *realParent = nativeParentWidget();
|
QWidget *realParent = nativeParentWidget();
|
||||||
|
if (!realParent && d_func()->inSetParent) {
|
||||||
|
// In transitional state. This is really just a workaround. The real problem
|
||||||
|
// is that QWidgetPrivate::setParent_sys (platform specific code) first sets
|
||||||
|
// the window id to 0 (setWinId(0)) before it sets the Qt::WA_WState_Created
|
||||||
|
// attribute to false. The correct way is to do it the other way around, and
|
||||||
|
// in that case the Qt::WA_WState_Created logic above will kick in and
|
||||||
|
// return 0 whenever the widget is in a transitional state. However, changing
|
||||||
|
// the original logic for all platforms is far more intrusive and might
|
||||||
|
// break existing applications.
|
||||||
|
// Note: The widget can only be in a transitional state when changing its
|
||||||
|
// parent -- everything else is an internal error -- hence explicitly checking
|
||||||
|
// against 'inSetParent' rather than doing an unconditional return whenever
|
||||||
|
// 'realParent' is 0 (which may cause strange artifacts and headache later).
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// This widget *must* have a native parent widget.
|
||||||
Q_ASSERT(realParent);
|
Q_ASSERT(realParent);
|
||||||
Q_ASSERT(realParent->internalWinId());
|
Q_ASSERT(realParent->internalWinId());
|
||||||
return realParent->internalWinId();
|
return realParent->internalWinId();
|
||||||
@ -10111,6 +10128,7 @@ void QWidget::setParent(QWidget *parent)
|
|||||||
void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
|
void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
|
||||||
{
|
{
|
||||||
Q_D(QWidget);
|
Q_D(QWidget);
|
||||||
|
d->inSetParent = true;
|
||||||
bool resized = testAttribute(Qt::WA_Resized);
|
bool resized = testAttribute(Qt::WA_Resized);
|
||||||
bool wasCreated = testAttribute(Qt::WA_WState_Created);
|
bool wasCreated = testAttribute(Qt::WA_WState_Created);
|
||||||
QWidget *oldtlw = window();
|
QWidget *oldtlw = window();
|
||||||
@ -10271,6 +10289,8 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
|
|||||||
ancestorProxy->d_func()->embedSubWindow(this);
|
ancestorProxy->d_func()->embedSubWindow(this);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
d->inSetParent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -770,6 +770,7 @@ public:
|
|||||||
#ifndef QT_NO_IM
|
#ifndef QT_NO_IM
|
||||||
uint inheritsInputMethodHints : 1;
|
uint inheritsInputMethodHints : 1;
|
||||||
#endif
|
#endif
|
||||||
|
uint inSetParent : 1;
|
||||||
|
|
||||||
// *************************** Platform specific ************************************
|
// *************************** Platform specific ************************************
|
||||||
#if defined(Q_WS_X11) // <----------------------------------------------------------- X11
|
#if defined(Q_WS_X11) // <----------------------------------------------------------- X11
|
||||||
|
@ -345,6 +345,7 @@ private slots:
|
|||||||
void immediateRepaintAfterInvalidateBuffer();
|
void immediateRepaintAfterInvalidateBuffer();
|
||||||
#endif
|
#endif
|
||||||
void effectiveWinId();
|
void effectiveWinId();
|
||||||
|
void effectiveWinId2();
|
||||||
void customDpi();
|
void customDpi();
|
||||||
void customDpiProperty();
|
void customDpiProperty();
|
||||||
|
|
||||||
@ -8495,6 +8496,30 @@ void tst_QWidget::effectiveWinId()
|
|||||||
QVERIFY(child.effectiveWinId());
|
QVERIFY(child.effectiveWinId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QWidget::effectiveWinId2()
|
||||||
|
{
|
||||||
|
QWidget parent;
|
||||||
|
|
||||||
|
class MyWidget : public QWidget {
|
||||||
|
bool event(QEvent *e)
|
||||||
|
{
|
||||||
|
if (e->type() == QEvent::WinIdChange) {
|
||||||
|
// Shouldn't crash.
|
||||||
|
effectiveWinId();
|
||||||
|
}
|
||||||
|
|
||||||
|
return QWidget::event(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MyWidget child;
|
||||||
|
child.setParent(&parent);
|
||||||
|
parent.show();
|
||||||
|
|
||||||
|
child.setParent(0);
|
||||||
|
child.setParent(&parent);
|
||||||
|
}
|
||||||
|
|
||||||
class CustomWidget : public QWidget
|
class CustomWidget : public QWidget
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
Loading…
Reference in New Issue
Block a user