New QML language features in Qt 5.15
March 27, 2020 by Fabian Kosmale | Comments
While big changes are on their way for Qt 6.0, QML got some new language features already in 5.15. Read on to to learn about required properties, inline components and nullish coalescing.
Required Properties
Sometimes, you have components which require some property to be set, and do not really have good default values. You might for instance care about accessibility of your buttons, so you create an AccessibleButton, which should always have a description.
// AccessibleButton.qml
Button {
property string description
Accessible.description: description
}
However, the fact that the button has a description property, doesn't imply that anyone will set it. So you or your colleague might at some point instantiate the component with:
AccessibleButton {
onClicked: (mouse) => { /* fancy business logic here */ }
}
So much for accessibility: The description is now simply an empty string! You could of course give the property a default value, but which one? "Button"? Hardly helpful. "You should not hear this"? Well, at least QA might catch it now. But wouldn't it be more useful if the QML engine would know that this property need to be set?
Before Qt 5.15, there was unfortunately no way to enforce that the description gets set. But starting with Qt 5.15, this becomes possible:
Button {
required property string description
Accessible.description: description
}
Now, if an AccessibleButton is created, and description is not set, the whole application will fail to start. This can, however, not be done if the component gets created dynamically, for example via a Loader. In that case, there will only be a runtime warning.
We also plan to add further tooling support to qmllint and QtCreator to show a warning if required properties are not set.
Required Properties and Delegates
Additionally, required properties play a special role in delegates. As you might know, delegates can access the model roles of the provided model directly by name, as well as some further properties, like model and index.
ListView {
model: root.myModel
delegate: Text {
id: delegate
color: index % 2 ? "gray" : "black"
text: description
}
}
And if your delegates do not contain required properties, nothing changes here. However, if they contain at least one required property, those names are not accessible anymore. Instead, you have to explicitly opt in by specifying them as required properties.
ListView {
model: root.myModel
delegate: Text {
id: delegate
required property int index
required property string description
color: index % 2 ? "gray" : "black"
text: description
}
}
The QML engine will then set the required properties accordingly. Note that there is one significant difference between the new approach and the old one in case your model was editable: With the old approach, you could write
Text {
id: delegate
Component.onCompleted: description = "My fancy new text"
}
and the model would have been updated accordingly. But if you do
Text {
id: delegate
required property string description
Component.onCompleted: delegate.description = "My fancy new text"
}
then the binding to description gets broken (and the QML engine will print a warning), and the model will not be updated. We decided on this behavior to ensure that components do not behave too differently when used in delegates and when used outsied of them. Furthermore, we did not want not encourage anyone to do imperative assignments to properties (as this breaks bindings in general).
If you want to actually update the model value, there is of course still a way to achieve this: Make model
a required property and write
Component.onCompleted: model.description= "My fancy new text"
We recommend that you always use required properties in delegates. This avoids unqualified lookups, which are problematic for tooling, and tend to be slower.
Inline Components
Another new feature in 5.15 are inline components. As the name suggests, they allow you to define a new component inside a file. The basic syntax is
component <component name> : BaseType {
// declare properties and bindings here
}
Inside the file, you can then refer to the new component by its name, just like if it were defined in its own file. Let's consider a LabeledImage component as an example to show how this works:
// Images.qml
import QtQuick 2.15
Item {
component LabeledImage: Column {
property alias source: image.source
property alias caption: text.text
Image {
id: image
width: 50
height: 50
}
Text {
id: text
font.bold: true
}
}
Row {
LabeledImage {
id: before
source: "before.png"
caption: "Before"
}
LabeledImage {
id: after
source: "after.png"
caption: "After"
}
}
property LabeledImage selectedImage: before
}
It is also possible to refer to the component in other files. In that case you need to prefix its name by the name of the containing component:
// LabeledImageBox.qml
import QtQuick 2.15
Rectangle {
property alias caption: image.caption
property alias source: image.source
border.width: 2
border.color: "black"
Images.LabeledImage {
id: image
}
}
You might wonder at this point why we need inline components when QML already has the Component
type. Looking at the previous examples, we can see that inline components allow you to do the following things which Component
doesn't:
- You can create an instance of the component, without the overhead of using a
Loader
. - You can use the component type in property declarations.
- You can refer to the component in other files than the one it is defined in.
We hope that you find inline components as convenient as we do!
Nullish Coalescing
The last new language feature was implemented by our intern Maximilian Goldstein. While QML generally only supports EcmaScript 6, Max added supported for one upcoming language feature that is currently in the process of being added to the latest EcmaScript standard: nullish coalescing. Quoting MDN:
The nullish coalescing operator (??) is a logical operator that returns its right-hand side operand when its left-hand side operand is null or undefined, and otherwise returns its left-hand side operand.
See the the MDN page for the details. Here's an example how it can be used in QML to set properties from JSON and provide sane defaults in case they were not provided.
Item {
property var settings
property int brightness: settings.brightness ?? 100
property color color: settings.color ?? "blue"
Component.onCompleted: settings = JSON.parse(settingsString)
}
Note that we couldn't have used ||
instead of ??
for brightness
, as settings.brightness
might have been 0, in which case we would have gotten the default value.
Looking forward
With Qt 6 on the horizon, many more changes are bound to land in QML. Besides revamping the internals of the QML engine, we want to leverage static typing to both generate faster code (including compilation to C++) and to improve our tooling. Moreover, while we focus on those large topics for the initial 6.0 release, we keep an open mind regarding small quality of life improvements: optional chaining or adding support for the fetch API are but two examples for feature requests from the community which we consider for the larger 6.x timeline (though not the initial 6.0 release). Do you have a feature that you want to see in QML? Feel free to post a comment, or even better, create a suggestion in our bugtracker. The future is written with Qt!
Blog Topics:
Comments
Subscribe to our newsletter
Subscribe Newsletter
Try Qt 6.8 Now!
Download the latest release here: www.qt.io/download.
Qt 6.8 release focuses on technology trends like spatial computing & XR, complex data visualization in 2D & 3D, and ARM-based development for desktop.
We're Hiring
Check out all our open positions here and follow us on Instagram to see what it's like to be #QtPeople.