QWidget: fix render() in RTL mode

Rendering a widget to a paintdevice via QWidget::render() did not pass
the LayoutDirection mode of the widget to the paintdevice which lead to
wrong rendering of text.
This is especially visible with the windows 11 style which does not draw
some widgets directly on the screen but through a QGraphicsEffect on a
QImage.

Pick-to: 6.5 6.2
Fixes: QTBUG-124931
Change-Id: If2cfa326d2ca45c42e203a4ae91fd857afa5c69c
Reviewed-by: Axel Spoerl <axel.spoerl@qt.io>
Reviewed-by: Wladimir Leuschner <wladimir.leuschner@qt.io>
(cherry picked from commit 5ea248155654b58fcb52ef326dc4d94de83d0409)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Christian Ehrlicher 2024-05-04 16:11:01 +02:00 committed by Qt Cherry-pick Bot
parent 0508839f7e
commit 64984f66ab
2 changed files with 45 additions and 0 deletions

View File

@ -5171,6 +5171,7 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset,
const QRegion oldSystemClip = enginePriv->systemClip;
const QRegion oldBaseClip = enginePriv->baseSystemClip;
const QRegion oldSystemViewport = enginePriv->systemViewport;
const Qt::LayoutDirection oldLayoutDirection = painter->layoutDirection();
// This ensures that all painting triggered by render() is clipped to the current engine clip.
if (painter->hasClipping()) {
@ -5179,6 +5180,7 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset,
} else {
enginePriv->setSystemViewport(oldSystemClip);
}
painter->setLayoutDirection(layoutDirection());
d->render(target, targetOffset, toBePainted, renderFlags);
@ -5186,6 +5188,7 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset,
enginePriv->baseSystemClip = oldBaseClip;
enginePriv->setSystemTransformAndViewport(oldTransform, oldSystemViewport);
enginePriv->systemStateChanged();
painter->setLayoutDirection(oldLayoutDirection);
// Restore shared painter.
d->setSharedPainter(oldPainter);

View File

@ -318,6 +318,7 @@ private slots:
void renderTargetOffset();
void renderInvisible();
void renderWithPainter();
void renderRTL();
void render_task188133();
void render_task211796();
void render_task217815();
@ -8402,6 +8403,47 @@ void tst_QWidget::renderWithPainter()
QCOMPARE(painter.renderHints(), oldRenderHints);
}
void tst_QWidget::renderRTL()
{
QFont f;
f.setStyleStrategy(QFont::NoAntialias);
const QScopedPointer<QStyle> style(QStyleFactory::create(QLatin1String("Windows")));
QMenu menu;
menu.setMinimumWidth(200);
menu.setFont(f);
menu.setStyle(style.data());
menu.addAction("I");
menu.show();
menu.setLayoutDirection(Qt::LeftToRight);
QVERIFY(QTest::qWaitForWindowExposed(&menu));
QImage imageLTR(menu.size(), QImage::Format_ARGB32);
menu.render(&imageLTR);
//imageLTR.save("/tmp/rendered_1.png");
menu.setLayoutDirection(Qt::RightToLeft);
QImage imageRTL(menu.size(), QImage::Format_ARGB32);
menu.render(&imageRTL);
imageRTL = imageRTL.mirrored(true, false);
//imageRTL.save("/tmp/rendered_2.png");
QCOMPARE(imageLTR.height(), imageRTL.height());
QCOMPARE(imageLTR.width(), imageRTL.width());
static constexpr auto border = 4;
for (int h = border; h < imageRTL.height() - border; ++h) {
// there should be no difference on the right (aka no text)
for (int w = imageRTL.width() / 2; w < imageRTL.width() - border; ++w) {
auto pixLTR = imageLTR.pixel(w, h);
auto pixRTL = imageRTL.pixel(w, h);
if (pixLTR != pixRTL)
qDebug() << "Pixel do not match at" << w << h << ":"
<< Qt::hex << pixLTR << "<->" << pixRTL;
QCOMPARE(pixLTR, pixRTL);
}
}
}
void tst_QWidget::render_task188133()
{
QMainWindow mainWindow;