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

Let's talk about the context properties in QML

    XMLWordPrintable

Details

    Description

      Let's talk about the context properties in QML

      Background

      QML has fairly complicated rules of name resolution, involving (in no-particular order) local component IDs, nearest top-level component's properties, imported types and import aliases, IDs and properties defined higher up in the QML context chain, component's own properties, and of course local JavaScript variables and arguments of methods and signal handlers. If that is not enough, one can supply additional properties through a C++ API of QQmlContext.

      Usage

      Supplying context properties covers at least three or four use-cases:

      1. Provide some global instance, for example, a model, a backend controller, an application "about" data, etc. These can be generally ported and registered as QML singletons nowadays.
      2. Splitting a big component in multiple files, and still referring to the IDs and properties defined in a higher level component without having to explicitly pass down a reference to that component at each level. Just because people do be lazy. We have tons of those in Plasma applets.
      3. Being actually context-specific (not singletons), and still not having to pass the reference around. Sometimes can be ported to attached properties with internal lookup up the context chain, except implementing an attached property is quite an involved process (high code complexity, low reward) which also requires a public access to the corresponding C++ type (and we all know the story of public API in Qt/QML). Examples of such context-specific usages include: applicationWindow function in Kirigami, which may return a subclassed of QQC2.ApplicationWindow (no public C++ API) or an Item-based Kirigami.AbstractApplicationItem, both of which provide stack navigation and toast notifications among other things. Another example is settings state highlighter — an addon for CheckBoxes and other controls which modifies their appearance if their value differs from the defaults; and it uses the current context's KCM (config module) object.
      4. A Loader with declaratively bound source or sourceComponent does not have a hook for setting initial properties, so even the officially recommended way is to declare properties on the Loader, quoting: «this works because the Loader sets itself as the context object for the component it is loading». (See the proposal to address that: https://bugreports.qt.io/browse/QTBUG-125071)

      Problem

      Context properties are hard to follow and reason about, often impossible to compile to native code, and are subject to name shadowing without workarounds (even view delegates can use modelData to bypass the required properties).

      As outlined above, some things may be ported to singletons, attached or explicit properties. But for the remaining legit cases QML language should provide some sane alternatives.

      Solution

      "Context" doesn't have to mean "implicit". The solution is to acknowledge component's reliance on a set of specific and typed context properties. Optionally, actively ensure that context properties are available at the component creation time, i.e. the usual required vs. non-required mechanism.

      Possible implementations I could think of:

      A new modified variant of the `property` declaration syntax.

      Downsides: such properties won't be able to be bound through the usual bindings syntax which might bring confusion; also can't rename without an accompanying new syntax for renaming properties just like model roles (see: https://bugreports.qt.io/browse/QTBUG-79876 for proposals on handling name collisions).

      Example:

      import QtQml
      
      QtObject {
          required context property int bar
          // name shadowing can be resolved through renaming
          required context property int foo as bar
      
          required property real foo
      }
      

      An annotation on the top-level component

      …as well as on the inline components and Component declarations.

      Upsides: may use the regular `property` syntax inside the annotation object without causing confusion with the bindable properties of the component itself; may use object id which would avoid the problem of name collisions.

      Downsides (or rather difficulties): QML Engine would have to be extended to support annotations more meaningfully than discarding them at parsing stage — which, I believe, would be a long-term win for the language and ecosystem as a whole.

      Example:

      import QtQml
      
      @Context {
          id: ctx
          required property int bar
          // require "foo" but re-export it as "bar"
          required property int foo as bar
      }
      QtObject {
          id: root
      
          // name shadowing can be resolved through id-qualified access
          property real bar
      
          Component.onCompleted: {
              print(ctx.bar, root.bar);
          }
      }
      

      Attachments

        Issue Links

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.

          Activity

            People

              qtqmlteam Qt Qml Team User
              ratijas ivan tkachenko
              Votes:
              1 Vote for this issue
              Watchers:
              6 Start watching this issue

              Dates

                Created:
                Updated:

                Gerrit Reviews

                  There are no open Gerrit changes