QWidget: fix propagating style with descendant selectors

The fix for QTBUG-133332 introduced a check to avoid an endless
recursion within setStyle_helper() but it prevents the correct
propagation of the style with a descendant selector. Therefore use
another approach and make sure QWidgetPrivate::inheritStyle() is not
called recursivly.
This amends 3252e1808c.

Pick-to: 6.10 6.9
Task-number: QTBUG-133332
Fixes: QTBUG-139924
Change-Id: Ia0a1eec652380397f861364bbdc303dfd17b34f3
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
This commit is contained in:
Christian Ehrlicher 2025-09-04 20:04:25 +02:00
parent 71707eadfa
commit 14aa698cc3
3 changed files with 44 additions and 4 deletions

View File

@ -161,6 +161,7 @@ QWidgetPrivate::QWidgetPrivate(decltype(QObjectPrivateVersion) version)
, childrenHiddenByWState(0)
, childrenShownByExpose(0)
, dontSetExplicitShowHide(0)
, inheritStyleRecursionGuard(0)
#if defined(Q_OS_WIN)
, noPaintOnScreen(0)
#endif
@ -2692,7 +2693,7 @@ void QWidgetPrivate::setStyle_helper(QStyle *newStyle, bool propagate)
extra->style = newStyle;
// repolish
if (polished && q->windowType() != Qt::Desktop && oldStyle != q->style()) {
if (polished && q->windowType() != Qt::Desktop) {
oldStyle->unpolish(q);
q->style()->polish(q);
}
@ -2740,6 +2741,12 @@ void QWidgetPrivate::inheritStyle()
proxy->repolish(q);
return;
}
if (inheritStyleRecursionGuard)
return;
inheritStyleRecursionGuard = true;
const auto resetGuard = qScopeGuard([&]() {
inheritStyleRecursionGuard = false;
});
QStyle *origStyle = proxy ? proxy->base : extraStyle;
QWidget *parent = q->parentWidget();

View File

@ -742,6 +742,7 @@ public:
uint childrenHiddenByWState : 1;
uint childrenShownByExpose : 1;
uint dontSetExplicitShowHide : 1;
uint inheritStyleRecursionGuard : 1;
// *************************** Focus abstraction ************************************
enum class FocusDirection {

View File

@ -11,7 +11,9 @@
#include <qlabel.h>
#include <qlayout.h>
#include <qlineedit.h>
#include <qlistview.h>
#if QT_CONFIG(listwidget)
#include <qlistwidget.h>
#endif
#include <qmessagebox.h>
#include <qmimedata.h>
#include <qpainter.h>
@ -22,6 +24,7 @@
#include <qstylefactory.h>
#include <private/qwidget_p.h>
#include <private/qwidgetrepaintmanager_p.h>
#include <private/qwindowsstyle_p.h>
#include <private/qapplication_p.h>
#include <private/qhighdpiscaling_p.h>
#include <qcalendarwidget.h>
@ -6806,7 +6809,32 @@ void tst_QWidget::deleteStyle()
QCoreApplication::processEvents();
}
class TestStyle : public QCommonStyle
#if QT_CONFIG(listwidget)
class DontCrashOnSetStyleWidget : public QWidget
{
Q_OBJECT
public:
DontCrashOnSetStyleWidget()
{
lw = new QListWidget;
lwi = new QListWidgetItem;
lw->addItem(lwi);
lw->setItemWidget(lwi, new QLabel(u"test"_s));
auto l = new QVBoxLayout(this);
l->addWidget(lw);
}
bool testStyleSheetTarget() const
{
return lw->itemWidget(lwi)->testAttribute(Qt::WA_StyleSheetTarget);
}
private:
QListWidget *lw = nullptr;
QListWidgetItem *lwi = nullptr;
};
#endif
class TestStyle : public QWindowsStyle
{
void polish(QWidget *w) override
{
@ -6827,13 +6855,17 @@ void tst_QWidget::dontCrashOnSetStyle()
});
{
qApp->setStyle(new TestStyle);
qApp->setStyleSheet("blub");
qApp->setStyleSheet(u"DontCrashOnSetStyleWidget QLabel {color:red;}"_s);
QComboBox w;
w.show();
QVERIFY(QTest::qWaitForWindowExposed(&w));
// this created an infinite loop / stack overflow inside setStyle_helper()
// directly call polish instead waiting for the polish event
qApp->style()->polish(&w);
#if QT_CONFIG(listwidget)
DontCrashOnSetStyleWidget widget;
QVERIFY(widget.testStyleSheetTarget());
#endif
}
}