Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-82443

Value types and Containers in QML

    XMLWordPrintable

Details

    Description

      Our current conception of value types and containers in QML has a few deficiencies, as expressed in other bugs and tasks. In particular:

      • We have a qv4sequenceobject.cpp that spells out a number of combinations of value types and containers and generates adapters for them. The result is both incomplete by design and a huge amount of code due to template explosion. (QTBUG-71574)
      • We have a list<Foo> syntax for declaring QQmlListProperty properties in QML, but we don't have a corresponding map<Foo>. list<Foo> only works for object types, not for value types. (QTBUG-79264)
      • Value types can only be created with specific built-in constructor functions and there is no way to declare additional value types, either in C++ or in QML. (QTBUG-54983, QTBUG-56484, QTBUG-72223, QTBUG-81593, and more)
      • Assigning to list<Foo> actually appends instead of replacing the contents. This behavior specifically targets the "children" property of QQuickItem. (QTBUG-77529)
      • We need a consistent way of specifying the data a view requires from its model and that it can pass on to its delegates. (QTBUG-80797, QTBUG-76848)

      Those problems are interrelated and can be fixed by adding a few new concepts to the language. In particular:

      1. Allow assignment to value types from any compatible JavaScript object or other value type:
        property font f: { bold: true, family: "sans" }
        property font f: unrelatedThingThatLooksLikeFont
        

        The assignment shall be done property-by-property, for properties named and typed (or convertible) like the target. Additional properties on the source object are ignored, properties on the target object that are not present on the source object are reset to default constructed values.
        You can also assign by only specifying the target's default property as single value. That is a short hand for "p: { <nameOfDefaultProperty> : <newValue> }". Allowing generic assignment makes the special constructor functions unnecessary. You can always default-construct a value type and then assign to it.

      2. Maybe introduce an operator "+:" that amends a property, rather than replacing it. This is not particularly elegant and needs some further research. The exact semantics would depend on the target object. For lists, it would mean "append", for structured value types, it would replace the properties mentioned in the argument, without resetting the others:
        // T.qml
        QtObject {
            property list<QtObject> l
        
            // destroys "family" binding below if b is updated
            property font f: { bold: b } 
            property bool b: true
        }
        
        // U.qml
        T {
            // Does _not_ destroy "bold" binding above if f is updated
            f+: { family: f }
            property string f: "Tahoma"
            
            l+: QtObject { objectName: "add me" } // Appends to the list
        }
        

        The nice thing about this, if you only use +: for your composite value types, is that you can escape the grouped properties trap. Suddenly you can have "whole object" replacement and "grouped" property access on the same property without causing ambiguity (unless you change the same sub-property from both). We can also deprecate the strange assignment-is-append behavior of list<Foo> this way.

      3. Introduce sequential<foo> and associative<bar> as interfaces. "sequential" and "associative" are keywords, not specific container implementations. C++ classes can declare QML_SEQUENTIAL or QML_ASSOCIATIVE and implement the respective functions to fulfill those interfaces. In addition, an elementMetaObject property needs to be available from the class. That property could be used to check whether the container can be assigned to a specific sequential<> or
        associative<>. A view would then declare its model as having the type sequential<data> for some value type "data". This would be fulfilled by any container that provides compatible objects.
      4. Allow registration of lower case value types. Those can again be either simple Q_GADGET or container types. They can only exist as properties of object types and they have no signals. Much like current value types, they will signal on the enclosing type if changed. You can register custom simple containers like QVector<int> that way. This makes qv4sequenceobject.cpp unnecessary. You would generally register a specific combination of container and value that way, like "QVector<int>" would be registered as "vector<int>" using a special macro in the C++ type declaration. You can have additional properties, even default properties, on those containers. Corollary: A "range<int>" may be registered by QtQml.Models, with an "int count" as default property. That would do what we currently see when assigning a plain number to the "model" property of e.g. Repeater.
      5. list<foo> and map<foo> for value types is just a QVariantList or QVariantMap. For object types we stick to QQmlListProperty and introduce QQmlMapProperty as complement. The assignment-is-append behavior gets deprecated. You will be reminded to use "+:" for appending and (for now) clear() and append() for replacing.
      6. We may provide syntax for declaring value types in QML. The specifics are still to be defined.

      Attachments

        Issue Links

          For Gerrit Dashboard: QTBUG-82443
          # Subject Branch Project Status CR V

          Activity

            People

              ulherman Ulf Hermann
              ulherman Ulf Hermann
              Vladimir Minenko Vladimir Minenko
              Alex Blasche Alex Blasche
              Votes:
              5 Vote for this issue
              Watchers:
              21 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: