QGuiApplication: use translation-based layout direction unless explicitly set

The stored layout direction used to get changed during initialization
to what was auto-detected based on the translation. Changing the
translation then overwrote that stored value, even if an explicit call
to setLayoutDirection was made by the application.

Calling QGuiApplication::setLayoutDirection(Auto) has so far been a
no-op.

Change this logic so that the stored layout direction continues to be
LayoutDirectionAuto also if it's set based on auto-detection, and only
overwrite it when explicitly called with a non-Auto value. This way,
applications can set a layout direction that stays unchanged even when
translators are installed.

Add test coverage that uses a QTranslator.

In practice, this is not a change of behavior, unless applications called
setLayoutDirection(Auto) (which is no longer a no-op), or called
setLayoutDirection() and then installed a translator and expected the
translator's layout direction to come into effect in spite of the explicit
setting.

[ChangeLog][Gui][QGuiApplication] Calling setLayoutDirection with a non-
auto value now disables the auto-detection based on installed
translators. Applications that explicitly set a layout direction and also
want translators installed afterwards to take effect should reset the
layout direction to Auto, which is now no longer a no-op.

Fixes: QTBUG-100632
Pick-to: 6.3
Change-Id: I1fdcebd43a9b1b468ff95bf15f53f441bb214e08
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
bb10
Volker Hilsheimer 2022-02-21 16:18:51 +01:00
parent 32b596b7b1
commit 4cca8ee527
2 changed files with 83 additions and 14 deletions

View File

@ -191,6 +191,7 @@ static int touchDoubleTapDistance = 0;
QWindow *QGuiApplicationPrivate::currentMousePressWindow = nullptr;
static Qt::LayoutDirection layout_direction = Qt::LayoutDirectionAuto;
static Qt::LayoutDirection effective_layout_direction = Qt::LeftToRight;
static bool force_reverse = false;
QGuiApplicationPrivate *QGuiApplicationPrivate::self = nullptr;
@ -1672,8 +1673,8 @@ void QGuiApplicationPrivate::init()
Q_UNUSED(loadTestability);
#endif // QT_CONFIG(library)
if (layout_direction == Qt::LayoutDirectionAuto || force_reverse)
QGuiApplication::setLayoutDirection(qt_detectRTLLanguage() ? Qt::RightToLeft : Qt::LeftToRight);
// trigger changed signal and event delivery
QGuiApplication::setLayoutDirection(layout_direction);
if (!QGuiApplicationPrivate::displayName)
QObject::connect(q, &QGuiApplication::applicationNameChanged,
@ -1699,7 +1700,7 @@ QGuiApplicationPrivate::~QGuiApplicationPrivate()
QCursorData::cleanup();
#endif
layout_direction = Qt::LeftToRight;
layout_direction = Qt::LayoutDirectionAuto;
cleanupThreadData();
@ -1943,7 +1944,9 @@ bool QGuiApplication::notify(QObject *object, QEvent *event)
bool QGuiApplication::event(QEvent *e)
{
if (e->type() == QEvent::LanguageChange) {
setLayoutDirection(qt_detectRTLLanguage()?Qt::RightToLeft:Qt::LeftToRight);
// if the layout direction was set explicitly, then don't override it here
if (layout_direction == Qt::LayoutDirectionAuto)
setLayoutDirection(layout_direction);
for (auto *topLevelWindow : QGuiApplication::topLevelWindows()) {
if (topLevelWindow->flags() != Qt::Desktop)
postEvent(topLevelWindow, new QEvent(QEvent::LanguageChange));
@ -3881,7 +3884,8 @@ void QGuiApplication::sync()
\property QGuiApplication::layoutDirection
\brief the default layout direction for this application
On system start-up, the default layout direction depends on the
On system start-up, or when the direction is explicitly set to
Qt::LayoutDirectionAuto, the default layout direction depends on the
application's language.
The notifier signal was introduced in Qt 5.4.
@ -3891,11 +3895,15 @@ void QGuiApplication::sync()
void QGuiApplication::setLayoutDirection(Qt::LayoutDirection direction)
{
if (layout_direction == direction || direction == Qt::LayoutDirectionAuto)
layout_direction = direction;
if (direction == Qt::LayoutDirectionAuto)
direction = qt_detectRTLLanguage() ? Qt::RightToLeft : Qt::LeftToRight;
// no change to the explicitly set or auto-detected layout direction
if (direction == effective_layout_direction)
return;
layout_direction = direction;
effective_layout_direction = direction;
if (qGuiApp) {
emit qGuiApp->layoutDirectionChanged(direction);
QGuiApplicationPrivate::self->notifyLayoutDirectionChange();
@ -3904,10 +3912,15 @@ void QGuiApplication::setLayoutDirection(Qt::LayoutDirection direction)
Qt::LayoutDirection QGuiApplication::layoutDirection()
{
// layout_direction is only ever Qt::LayoutDirectionAuto if setLayoutDirection
// was never called, or called with Qt::LayoutDirectionAuto (which is a no-op).
// In that case we return the default LeftToRight.
return layout_direction == Qt::LayoutDirectionAuto ? Qt::LeftToRight : layout_direction;
/*
effective_layout_direction defaults to Qt::LeftToRight, and is updated with what is
auto-detected by a call to setLayoutDirection(Qt::LayoutDirectionAuto). This happens in
QGuiApplicationPrivate::init and when the language changes (or before if the application
calls the static function, but then no translators are installed so the auto-detection
always yields Qt::LeftToRight).
So we can be certain that it's always the right value.
*/
return effective_layout_direction;
}
/*!

View File

@ -1158,8 +1158,8 @@ void tst_QGuiApplication::layoutDirection()
{
qRegisterMetaType<Qt::LayoutDirection>();
Qt::LayoutDirection oldDirection = QGuiApplication::layoutDirection();
Qt::LayoutDirection newDirection = oldDirection == Qt::LeftToRight ? Qt::RightToLeft : Qt::LeftToRight;
const Qt::LayoutDirection oldDirection = QGuiApplication::layoutDirection();
const Qt::LayoutDirection newDirection = oldDirection == Qt::LeftToRight ? Qt::RightToLeft : Qt::LeftToRight;
QGuiApplication::setLayoutDirection(newDirection);
QCOMPARE(QGuiApplication::layoutDirection(), newDirection);
@ -1177,8 +1177,64 @@ void tst_QGuiApplication::layoutDirection()
QGuiApplication::setLayoutDirection(oldDirection);
QCOMPARE(QGuiApplication::layoutDirection(), oldDirection);
QCOMPARE(signalSpy.count(), 1);
// with QGuiApplication instantiated, install a translator that gives us control
class LayoutDirectionTranslator : public QTranslator
{
public:
LayoutDirectionTranslator(Qt::LayoutDirection direction)
: direction(direction)
{}
bool isEmpty() const override { return false; }
QString translate(const char *context, const char *sourceText, const char *disambiguation, int n) const override
{
if (QByteArrayView(sourceText) == "QT_LAYOUT_DIRECTION")
return direction == Qt::LeftToRight ? QLatin1String("LTR") : QLatin1String("RTL");
return QTranslator::translate(context, sourceText, disambiguation, n);
}
const Qt::LayoutDirection direction;
};
int layoutDirectionChangedCount = 0;
// reset to auto-detection, should be back to oldDirection now
QGuiApplication::setLayoutDirection(Qt::LayoutDirectionAuto);
QCOMPARE(QGuiApplication::layoutDirection(), oldDirection);
signalSpy.clear();
{
// this translator doesn't change the direction
LayoutDirectionTranslator translator(oldDirection);
QGuiApplication::installTranslator(&translator);
QCOMPARE(QGuiApplication::layoutDirection(), translator.direction);
QCOMPARE(signalSpy.count(), layoutDirectionChangedCount);
}
QCOMPARE(signalSpy.count(), layoutDirectionChangedCount); // ltrTranslator removed, no change
// install a new translator that changes the direction
{
LayoutDirectionTranslator translator(newDirection);
QGuiApplication::installTranslator(&translator);
QCOMPARE(QGuiApplication::layoutDirection(), translator.direction);
QCOMPARE(signalSpy.count(), ++layoutDirectionChangedCount);
}
// rtlTranslator removed
QCOMPARE(signalSpy.count(), ++layoutDirectionChangedCount);
// override translation
QGuiApplication::setLayoutDirection(newDirection);
QCOMPARE(signalSpy.count(), ++layoutDirectionChangedCount);
{
// this translator will be ignored
LayoutDirectionTranslator translator(oldDirection);
QGuiApplication::installTranslator(&translator);
QCOMPARE(QGuiApplication::layoutDirection(), newDirection);
QCOMPARE(signalSpy.count(), layoutDirectionChangedCount);
}
QCOMPARE(signalSpy.count(), layoutDirectionChangedCount);
}
void tst_QGuiApplication::globalShareContext()
{
#ifndef QT_NO_OPENGL