Reusing the default value of a property in QML

QML is a language designed to ease the work of writing user interfaces (UI). It is part of the Qt environment. Actually, QML is not only useable for UI, but you’ll find a lot of stuff for this purpose in Qt. You can learn more about it on the Qt site.

One of the best things that ease your life with QML are bindings. Another nice feature is the ability to create a new type so easily : just put an existing type in a MyNewType.qml file and add your own code. As a result, you can now use MyNewType  in your code.

I use this feature to build new behaviours. I also use it sometimes in order to set some default values. Here is an example. In a file MyRectangle.qml, let’s write :

import QtQuick 2.9
Rectangle
{
 color: "blue"
}

Now, in your code, you can instantiate your new type :

import QtQuick 2.9 
MyRectangle
{
 height: 100
 width: 100
}

It declares a Rectangle having its color "blue".

Now, if you want, you can modify it :

MyRectangle
{
 color: "yellow"
 height: 100
 width: 100
}

You may also set its color depending on another value (and here come the bindings) :

MyRectangle
{
 property bool important: false
 color: important ? "yellow" : "blue"
 height: 100
 width: 100
}

Now imagine this scenario : when important is true, you want to set the color to "yellow". But when important is false, you want to use the default value instead of "blue". In addition, you do not want to duplicate the code of the default value each time you use a MyRectangle, or it would not be a default value anymore.

If you write something like that :

MyRectangle
{
 property bool important: false
 color: important ? "yellow" : color
 height: 100
 width: 100
}

It won’t work. QML is not able to do something as simple as, in Javascript :

var a = 1;
a = a;

In Javascript a = a means : get the value of the variable a, set the value of the variable a with what you just got. Quite simple !

In QML a: a means : get the value of the property a, set the value of the property a with what you just got and then signal that the value of a has changed. As a consequence, any binding involving a is re-evaluated and… a: a is one of these bindings. This leads to the famous binding loop that must be avoided.

A solution to this problem is to avoid the binding loop by relying on an intermediate value.

First, you have to get the default value. It is the property value. Second, you have to put your binding on the property. But doing so erases the property value. So, you must do it in sequence. A way to have a sequence is to use a sequential language. And, good news, it happens that Javascript is one of them !

Let’s do this :

MyRectangle
{
 property bool important: false
 height: 100
 width: 100
 property var initColor: null
 Component.onCompleted:
 {
  initColor = color;
  color =
       Qt.binding(function() { return important ? "yellow" : initColor});
 } 
}

 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *