aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@qt.io>2018-05-18 13:41:04 +0200
committerSimon Hausmann <simon.hausmann@qt.io>2018-05-28 15:10:05 +0000
commit2e17c1bed2f0690342f98130701ebfe088075a60 (patch)
tree1a5d21784a7dbebe6d53a5b9c29a6a29c83a5c25
parent7f93ecd31494d17351ff8e16820b7117aa07d90b (diff)
Fix language change support for qsTr in ListElement
We must store translations by binding reference in order to support translation changes. This is similar to commit db15c3455971f47b86078a44a30e0f0a13b54204. [ChangeLog][QtQml] Fix QQmlEngine::retranslate() with ListElement objects that use translation functions such as qsTr. Task-number: QTBUG-68350 Change-Id: Ie5b4d5beb0505a260b524da820c0ce1142893d54 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Lars Knoll <lars.knoll@qt.io>
-rw-r--r--src/qml/types/qqmllistmodel.cpp135
-rw-r--r--src/qml/types/qqmllistmodel_p.h2
-rw-r--r--src/qml/types/qqmllistmodel_p_p.h19
-rw-r--r--tests/auto/qml/qqmltranslation/data/translationChange.qml8
-rw-r--r--tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp2
5 files changed, 145 insertions, 21 deletions
diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp
index e92e27ab62..b4507cb264 100644
--- a/src/qml/types/qqmllistmodel.cpp
+++ b/src/qml/types/qqmllistmodel.cpp
@@ -62,6 +62,8 @@
#include <QtCore/qdatetime.h>
#include <QScopedValueRollback>
+Q_DECLARE_METATYPE(const QV4::CompiledData::Binding*);
+
QT_BEGIN_NAMESPACE
// Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models.
@@ -124,8 +126,8 @@ const ListLayout::Role &ListLayout::getRoleOrCreate(QV4::String *key, Role::Data
const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type)
{
- const int dataSizes[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QPointer<QObject>), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) };
- const int dataAlignments[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) };
+ const int dataSizes[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QPointer<QObject>), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) };
+ const int dataAlignments[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) };
Role *r = new Role;
r->name = key;
@@ -227,6 +229,10 @@ const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QV
data.value<QJSValue>().isCallable()) {
type = Role::Function;
break;
+ } else if (data.userType() == qMetaTypeId<const QV4::CompiledData::Binding*>()
+ && data.value<const QV4::CompiledData::Binding*>()->containsTranslations()) {
+ type = Role::String;
+ break;
} else {
type = Role::List;
break;
@@ -261,6 +267,75 @@ const ListLayout::Role *ListLayout::getExistingRole(QV4::String *key) const
return r;
}
+StringOrTranslation::StringOrTranslation(const QString &s)
+{
+ d.setFlag();
+ setString(s);
+}
+
+StringOrTranslation::StringOrTranslation(const QV4::CompiledData::Binding *binding)
+{
+ d.setFlag();
+ clear();
+ d = binding;
+}
+
+StringOrTranslation::~StringOrTranslation()
+{
+ clear();
+}
+
+void StringOrTranslation::setString(const QString &s)
+{
+ d.setFlag();
+ clear();
+ QStringData *stringData = const_cast<QString &>(s).data_ptr();
+ d = stringData;
+ if (stringData)
+ stringData->ref.ref();
+}
+
+void StringOrTranslation::setTranslation(const QV4::CompiledData::Binding *binding)
+{
+ d.setFlag();
+ clear();
+ d = binding;
+}
+
+QString StringOrTranslation::toString(const QQmlListModel *owner) const
+{
+ if (d.isNull())
+ return QString();
+ if (d.isT1()) {
+ QStringDataPtr holder = { d.asT1() };
+ holder.ptr->ref.ref();
+ return QString(holder);
+ }
+ if (!owner)
+ return QString();
+ return d.asT2()->valueAsString(owner->m_compilationUnit->data);
+}
+
+QString StringOrTranslation::asString() const
+{
+ if (d.isNull())
+ return QString();
+ if (!d.isT1())
+ return QString();
+ QStringDataPtr holder = { d.asT1() };
+ holder.ptr->ref.ref();
+ return QString(holder);
+}
+
+void StringOrTranslation::clear()
+{
+ if (QStringData *strData = d.isT1() ? d.asT1() : nullptr) {
+ if (!strData->ref.deref())
+ QStringData::deallocate(strData);
+ }
+ d = static_cast<QStringData *>(nullptr);
+}
+
QObject *ListModel::getOrCreateModelObject(QQmlListModel *model, int elementIndex)
{
ListElement *e = elements[elementIndex];
@@ -717,11 +792,11 @@ ModelNodeMetaObject *ListElement::objectCache()
return ModelNodeMetaObject::get(m_objectCache);
}
-QString *ListElement::getStringProperty(const ListLayout::Role &role)
+StringOrTranslation *ListElement::getStringProperty(const ListLayout::Role &role)
{
char *mem = getPropertyMemory(role);
- QString *s = reinterpret_cast<QString *>(mem);
- return s->data_ptr() ? s : nullptr;
+ StringOrTranslation *s = reinterpret_cast<StringOrTranslation *>(mem);
+ return s;
}
QObject *ListElement::getQObjectProperty(const ListLayout::Role &role)
@@ -806,9 +881,9 @@ QVariant ListElement::getProperty(const ListLayout::Role &role, const QQmlListMo
break;
case ListLayout::Role::String:
{
- QString *value = reinterpret_cast<QString *>(mem);
- if (value->data_ptr() != nullptr)
- data = *value;
+ StringOrTranslation *value = reinterpret_cast<StringOrTranslation *>(mem);
+ if (value->isSet())
+ data = value->toString(owner);
}
break;
case ListLayout::Role::Bool:
@@ -878,15 +953,13 @@ int ListElement::setStringProperty(const ListLayout::Role &role, const QString &
if (role.type == ListLayout::Role::String) {
char *mem = getPropertyMemory(role);
- QString *c = reinterpret_cast<QString *>(mem);
+ StringOrTranslation *c = reinterpret_cast<StringOrTranslation *>(mem);
bool changed;
- if (c->data_ptr() == nullptr) {
- new (mem) QString(s);
+ if (!c->isSet() || c->isTranslation())
changed = true;
- } else {
- changed = c->compare(s) != 0;
- *c = s;
- }
+ else
+ changed = c->asString().compare(s) != 0;
+ c->setString(s);
if (changed)
roleIndex = role.index;
}
@@ -1048,11 +1121,25 @@ int ListElement::setFunctionProperty(const ListLayout::Role &role, const QJSValu
return roleIndex;
}
+int ListElement::setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b)
+{
+ int roleIndex = -1;
+
+ if (role.type == ListLayout::Role::String) {
+ char *mem = getPropertyMemory(role);
+ StringOrTranslation *s = reinterpret_cast<StringOrTranslation *>(mem);
+ s->setTranslation(b);
+ roleIndex = role.index;
+ }
+
+ return roleIndex;
+}
+
void ListElement::setStringPropertyFast(const ListLayout::Role &role, const QString &s)
{
char *mem = getPropertyMemory(role);
- new (mem) QString(s);
+ new (mem) StringOrTranslation(s);
}
void ListElement::setDoublePropertyFast(const ListLayout::Role &role, double d)
@@ -1219,9 +1306,9 @@ void ListElement::destroy(ListLayout *layout)
switch (r.type) {
case ListLayout::Role::String:
{
- QString *string = getStringProperty(r);
+ StringOrTranslation *string = getStringProperty(r);
if (string)
- string->~QString();
+ string->~StringOrTranslation();
}
break;
case ListLayout::Role::List:
@@ -1284,7 +1371,10 @@ int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant
roleIndex = setDoubleProperty(role, d.toDouble());
break;
case ListLayout::Role::String:
- roleIndex = setStringProperty(role, d.toString());
+ if (d.userType() == qMetaTypeId<const QV4::CompiledData::Binding *>())
+ roleIndex = setTranslationProperty(role, d.value<const QV4::CompiledData::Binding*>());
+ else
+ roleIndex = setStringProperty(role, d.toString());
break;
case ListLayout::Role::Bool:
roleIndex = setBoolProperty(role, d.toBool());
@@ -1817,6 +1907,7 @@ QQmlListModel::QQmlListModel(const QQmlListModel *owner, ListModel *data, QV4::E
m_listModel = data;
m_engine = engine;
+ m_compilationUnit = owner->m_compilationUnit;
}
QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent)
@@ -1836,6 +1927,7 @@ QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agen
ListModel::sync(orig->m_listModel, m_listModel);
m_engine = nullptr;
+ m_compilationUnit = orig->m_compilationUnit;
}
QQmlListModel::~QQmlListModel()
@@ -2639,7 +2731,9 @@ bool QQmlListModelParser::applyProperty(QV4::CompiledData::CompilationUnit *comp
} else {
QVariant value;
- if (binding->evaluatesToString()) {
+ if (binding->containsTranslations()) {
+ value = QVariant::fromValue<const QV4::CompiledData::Binding*>(binding);
+ } else if (binding->evaluatesToString()) {
value = binding->valueAsString(qmlUnit);
} else if (binding->type == QV4::CompiledData::Binding::Type_Number) {
value = binding->valueAsNumber();
@@ -2701,6 +2795,7 @@ void QQmlListModelParser::applyBindings(QObject *obj, QV4::CompiledData::Compila
QQmlListModel *rv = static_cast<QQmlListModel *>(obj);
rv->m_engine = qmlEngine(rv)->handle();
+ rv->m_compilationUnit = compilationUnit;
const QV4::CompiledData::Unit *qmlUnit = compilationUnit->data;
diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h
index 0a9d29ac05..56a1a95a02 100644
--- a/src/qml/types/qqmllistmodel_p.h
+++ b/src/qml/types/qqmllistmodel_p.h
@@ -124,6 +124,7 @@ private:
friend class ListElement;
friend class DynamicRoleModelNode;
friend class DynamicRoleModelNodeMetaObject;
+ friend struct StringOrTranslation;
// Constructs a flat list model for a worker agent
QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent);
@@ -135,6 +136,7 @@ private:
QQmlListModelWorkerAgent *m_agent;
mutable QV4::ExecutionEngine *m_engine;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit;
bool m_mainThread;
bool m_primary;
diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h
index 738ed0713e..75eeaeed0c 100644
--- a/src/qml/types/qqmllistmodel_p_p.h
+++ b/src/qml/types/qqmllistmodel_p_p.h
@@ -244,6 +244,22 @@ private:
QStringHash<Role *> roleHash;
};
+struct StringOrTranslation
+{
+ explicit StringOrTranslation(const QString &s);
+ explicit StringOrTranslation(const QV4::CompiledData::Binding *binding);
+ ~StringOrTranslation();
+ bool isSet() const { return d.flag(); }
+ bool isTranslation() const { return d.isT2(); }
+ void setString(const QString &s);
+ void setTranslation(const QV4::CompiledData::Binding *binding);
+ QString toString(const QQmlListModel *owner) const;
+ QString asString() const;
+private:
+ void clear();
+ QBiPointer<QStringData, const QV4::CompiledData::Binding> d;
+};
+
/*!
\internal
*/
@@ -279,6 +295,7 @@ private:
int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m);
int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt);
int setFunctionProperty(const ListLayout::Role &role, const QJSValue &f);
+ int setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b);
void setStringPropertyFast(const ListLayout::Role &role, const QString &s);
void setDoublePropertyFast(const ListLayout::Role &role, double n);
@@ -293,7 +310,7 @@ private:
QVariant getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV4::ExecutionEngine *eng);
ListModel *getListProperty(const ListLayout::Role &role);
- QString *getStringProperty(const ListLayout::Role &role);
+ StringOrTranslation *getStringProperty(const ListLayout::Role &role);
QObject *getQObjectProperty(const ListLayout::Role &role);
QPointer<QObject> *getGuardProperty(const ListLayout::Role &role);
QVariantMap *getVariantMapProperty(const ListLayout::Role &role);
diff --git a/tests/auto/qml/qqmltranslation/data/translationChange.qml b/tests/auto/qml/qqmltranslation/data/translationChange.qml
index c48a482138..ae3231935c 100644
--- a/tests/auto/qml/qqmltranslation/data/translationChange.qml
+++ b/tests/auto/qml/qqmltranslation/data/translationChange.qml
@@ -3,6 +3,13 @@ import QtQuick 2.0
TranslationChangeBase {
id: root
+ ListModel {
+ id: listModel
+ ListElement {
+ text: qsTr("translate me")
+ }
+ }
+
baseProperty: "do not translate"
property string text1: qsTr("translate me")
function weDoTranslations() {
@@ -10,6 +17,7 @@ TranslationChangeBase {
}
property string text2: weDoTranslations()
property string text3
+ property string fromListModel: listModel.get(0).text
states: [
State {
diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
index 733fe77346..5042a236d4 100644
--- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
+++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp
@@ -195,6 +195,7 @@ void tst_qqmltranslation::translationChange()
QCOMPARE(object->property("text1").toString(), QString::fromUtf8("translate me"));
QCOMPARE(object->property("text2").toString(), QString::fromUtf8("translate me"));
QCOMPARE(object->property("text3").toString(), QString::fromUtf8("translate me"));
+ QCOMPARE(object->property("fromListModel").toString(), QString::fromUtf8("translate me"));
DummyTranslator translator;
QCoreApplication::installTranslator(&translator);
@@ -206,6 +207,7 @@ void tst_qqmltranslation::translationChange()
QCOMPARE(object->property("text1").toString(), QString::fromUtf8("xxx"));
QCOMPARE(object->property("text2").toString(), QString::fromUtf8("xxx"));
QCOMPARE(object->property("text3").toString(), QString::fromUtf8("xxx"));
+ QCOMPARE(object->property("fromListModel").toString(), QString::fromUtf8("xxx"));
QCoreApplication::removeTranslator(&translator);
}