Fix conversion between char and string.
authorChristian Strømme <christian.stromme@theqtcompany.com>
Thu, 12 Mar 2015 14:33:50 +0000 (15:33 +0100)
committerChristian Stromme <christian.stromme@theqtcompany.com>
Sat, 21 Mar 2015 11:15:31 +0000 (11:15 +0000)
If a QChar (or char) was used to set a QString property, the
intermediate value used by the QML engine (int), would be
converted to a string representation of the integer and not the actual
character. To avoid this behavior, characters are now stored as string
objects and the string is then converted to the target char type if
possible.
A side effect of this solution is that it is makes it possible to
assign a string to a char property as well, but only if the string
contains exactly one character.

[ChangeLog][QtQml][Important Behavior Changes] Assigning a char to a
string will now create a string with the actual character instead of a
string representation of the character's code-point. A side effect of
this change is that a one-character string also can be assigned to a
character type.

Task-number: QTBUG-44934
Change-Id: Ifd15386933ee11354ee1bbb5598a5f0b00a08616
Reviewed-by: Alan Alpert (Personal) <416365416c@gmail.com>
Reviewed-by: Simon Hausmann <simon.hausmann@theqtcompany.com>
src/qml/jsruntime/qv4engine.cpp
src/qml/qml/qqmlproperty.cpp
tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp

index 54dd5979cf34a2a410ebf81db83ae696e49c4f1c..eecf874e19f0de03d07095f3aa697448eff905bd 100644 (file)
@@ -1390,11 +1390,11 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
             case QMetaType::UShort:
                 return QV4::Encode((int)*reinterpret_cast<const unsigned short*>(ptr));
             case QMetaType::Char:
-                return QV4::Encode((int)*reinterpret_cast<const char*>(ptr));
+                return newString(QChar::fromLatin1(*reinterpret_cast<const char *>(ptr)))->asReturnedValue();
             case QMetaType::UChar:
-                return QV4::Encode((int)*reinterpret_cast<const unsigned char*>(ptr));
+                return newString(QChar::fromLatin1(*reinterpret_cast<const unsigned char *>(ptr)))->asReturnedValue();
             case QMetaType::QChar:
-                return QV4::Encode((int)(*reinterpret_cast<const QChar*>(ptr)).unicode());
+                return newString(*reinterpret_cast<const QChar *>(ptr))->asReturnedValue();
             case QMetaType::QDateTime:
                 return QV4::Encode(newDateObject(*reinterpret_cast<const QDateTime *>(ptr)));
             case QMetaType::QDate:
index d45f3ac19b71a4819459c7278dd94348bda569f0..931adb9a13d02cd7dc99152454433f580533868c 100644 (file)
@@ -1387,8 +1387,30 @@ bool QQmlPropertyPrivate::write(QObject *object,
 
         bool ok = false;
         QVariant v;
-        if (variantType == QVariant::String)
-            v = QQmlStringConverters::variantFromString(value.toString(), propertyType, &ok);
+        if (variantType == QVariant::String) {
+            const QString &str = value.toString();
+            const bool targetIsChar = (propertyType == qMetaTypeId<QChar>()
+                                       || propertyType == qMetaTypeId<char>()
+                                       || propertyType == qMetaTypeId<unsigned char>());
+            // If the string contains only one character and the target is a char, try converting it.
+            if (targetIsChar) {
+                if (str.size() != 1)
+                    return false; // We can only convert if the string contains exactly one character.
+
+                const QChar &qChar = str.at(0);
+                if (propertyType == qMetaTypeId<QChar>()) {
+                    v = qChar;
+                    ok = true;
+                } else if (propertyType == qMetaTypeId<char>() || propertyType == qMetaTypeId<unsigned char>()) {
+                    const char c = qChar.toLatin1();
+                    v = c;
+                    ok = (qChar == c);
+                }
+            } else {
+                v = QQmlStringConverters::variantFromString(str, propertyType, &ok);
+            }
+        }
+
         if (!ok) {
             v = value;
             if (v.convert(propertyType)) {
index f8af13582ed292653f75cc78062883db5bbf865c..c4b2325843d8d1c6704e251c270b9d653f366d52 100644 (file)
@@ -326,10 +326,16 @@ class PropertyObject : public QObject
     Q_PROPERTY(int propertyWithNotify READ propertyWithNotify WRITE setPropertyWithNotify NOTIFY oddlyNamedNotifySignal)
     Q_PROPERTY(MyQmlObject *qmlObject READ qmlObject)
     Q_PROPERTY(MyQObject *qObject READ qObject WRITE setQObject NOTIFY qObjectChanged)
+    Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty)
+    Q_PROPERTY(char charProperty READ charProperty WRITE setCharProperty)
+    Q_PROPERTY(QChar qcharProperty READ qcharProperty WRITE setQcharProperty)
+    Q_PROPERTY(QChar constQChar READ constQChar STORED false CONSTANT FINAL)
+    Q_PROPERTY(char constChar READ constChar STORED false CONSTANT FINAL)
+    Q_PROPERTY(int constInt READ constInt STORED false CONSTANT FINAL)
 
     Q_CLASSINFO("DefaultProperty", "defaultProperty")
 public:
-    PropertyObject() : m_resetProperty(9), m_qObject(0) {}
+    PropertyObject() : m_resetProperty(9), m_qObject(0), m_stringProperty("foo") {}
 
     int defaultProperty() { return 10; }
     QRect rectProperty() { return QRect(10, 10, 1, 209); }
@@ -361,6 +367,18 @@ public:
         }
     }
 
+    QString stringProperty() const { return m_stringProperty;}
+    char charProperty() const { return m_charProperty; }
+    QChar qcharProperty() const { return m_qcharProperty; }
+
+    QChar constQChar() const { return 0x25cf; /* Unicode: black circle */ }
+    char constChar() const { return 'A'; }
+    int constInt() const { return 123456; }
+
+    void setStringProperty(QString arg) { m_stringProperty = arg; }
+    void setCharProperty(char arg) { m_charProperty = arg; }
+    void setQcharProperty(QChar arg) { m_qcharProperty = arg; }
+
 signals:
     void clicked();
     void oddlyNamedNotifySignal();
@@ -374,6 +392,9 @@ private:
     int m_propertyWithNotify;
     MyQmlObject m_qmlObject;
     MyQObject *m_qObject;
+    QString m_stringProperty;
+    char m_charProperty;
+    QChar m_qcharProperty;
 };
 
 QML_DECLARE_TYPE(PropertyObject);
@@ -1382,6 +1403,71 @@ void tst_qqmlproperty::write()
         QCOMPARE(o.url(), result);
     }
 
+    // Char/string-property
+    {
+        PropertyObject o;
+        QQmlProperty charProperty(&o, "charProperty");
+        QQmlProperty qcharProperty(&o, "qcharProperty");
+        QQmlProperty stringProperty(&o, "stringProperty");
+
+        const int black_circle = 0x25cf;
+
+        QCOMPARE(charProperty.write(QString("foo")), false);
+        QCOMPARE(charProperty.write('Q'), true);
+        QCOMPARE(charProperty.read(), QVariant('Q'));
+        QCOMPARE(charProperty.write(QString("t")), true);
+        QCOMPARE(charProperty.read(), QVariant('t'));
+
+        QCOMPARE(qcharProperty.write(QString("foo")), false);
+        QCOMPARE(qcharProperty.write('Q'), true);
+        QCOMPARE(qcharProperty.read(), QVariant('Q'));
+        QCOMPARE(qcharProperty.write(QString("t")), true);
+        QCOMPARE(qcharProperty.read(), QVariant('t'));
+        QCOMPARE(qcharProperty.write(QChar(black_circle)), true);
+        QCOMPARE(qcharProperty.read(), QVariant(QChar(black_circle)));
+
+        QCOMPARE(o.stringProperty(), QString("foo")); // Default value
+        QCOMPARE(stringProperty.write(QString("bar")), true);
+        QCOMPARE(o.stringProperty(), QString("bar"));
+        QCOMPARE(stringProperty.write(QVariant(1234)), true);
+        QCOMPARE(stringProperty.read().toString(), QString::number(1234));
+        QCOMPARE(stringProperty.write(QChar(black_circle)), true);
+        QCOMPARE(stringProperty.read(), QVariant(QString(QChar(black_circle))));
+
+        { // char -> QString
+            QQmlComponent component(&engine);
+            component.setData("import Test 1.0\nPropertyObject { stringProperty: constChar }", QUrl());
+            PropertyObject *obj = qobject_cast<PropertyObject*>(component.create());
+            QVERIFY(obj != 0);
+            if (obj) {
+                QQmlProperty stringProperty(obj, "stringProperty");
+                QCOMPARE(stringProperty.read(), QVariant(QString(obj->constChar())));
+            }
+        }
+
+        { // QChar -> QString
+            QQmlComponent component(&engine);
+            component.setData("import Test 1.0\nPropertyObject { stringProperty: constQChar }", QUrl());
+            PropertyObject *obj = qobject_cast<PropertyObject*>(component.create());
+            QVERIFY(obj != 0);
+            if (obj) {
+                QQmlProperty stringProperty(obj, "stringProperty");
+                QCOMPARE(stringProperty.read(), QVariant(QString(obj->constQChar())));
+            }
+        }
+
+        { // int -> QString
+            QQmlComponent component(&engine);
+            component.setData("import Test 1.0\nPropertyObject { stringProperty: constInt }", QUrl());
+            PropertyObject *obj = qobject_cast<PropertyObject*>(component.create());
+            QVERIFY(obj != 0);
+            if (obj) {
+                QQmlProperty stringProperty(obj, "stringProperty");
+                QCOMPARE(stringProperty.read(), QVariant(QString::number(obj->constInt())));
+            }
+        }
+    }
+
     // VariantMap-property
     QVariantMap vm;
     vm.insert("key", "value");