derM
derM

Reputation: 13691

Bidirectional Binding of mutually dependent Properties in QML

While learning QML I stumbled over the problem, that I have to properties that are mutually dependent.

E.g. the user may set a value with a slider, or enter it with a textinput.
While moving the slider, the text in the textinput should be updated while when entering a value in the textinput, the sliders position needs to be adjusted.

Now I have the two properties: the x-value of the slider, and the text in the textinput. I need to convert them into the same format (for example: percent), and update them vice versa. Setting up two bindings would result in a binding loop, which is probably not a good thing to have.

I think, this is a very common problem, so I am sure there is some "gold standard" to solve it. However I can't find a suitable solution.

The only way that comes to my mind, is not to use bindings at all, but handling the signals, that one of the values has changed manually (if I can't overwrite the setter in C++).
Is that all you can do?

Good day to you!
-m-

EDIT: I now tried it with conditionally binding the value of the slider to the percentage value.

The object handle is the marker on the slider, handleArea is the MouseArea attached to it, that allows the dragging.

Binding {
    target: root
    property: 'percent'
    value: handle.x / handleBar.width
    when: handleArea.drag.active
}

Binding {
    target: handle
    property: 'x'
    value: root.percent * handleBar.width
    when: !handleArea.drag.active
}

It works. But is it a good style?

Upvotes: 4

Views: 1975

Answers (1)

Filip Hazubski
Filip Hazubski

Reputation: 1728

I would make one property that will store the mutual value. When user inputs text or moves slider they will use a function to update the mutual value. They will also listen to the value change and adjust their value to it.

Here is working example

import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.4

Window {
    id: window
    visible: true
    width: 400
    height: 80
    title: "Mutual value test"

    property double mutualValue: 0
    function setMutualValue(value)
    {
        if (typeof(value) === "string")
        {
            value = value.replace(",", ".");
            value = parseFloat(value)
        }
        mutualValue = value
    }

    TextInput {
        width: 200
        height: 40
        validator: DoubleValidator {}
        onEditingFinished:
        {
            focus = false
            setMutualValue(text)
        }
        onFocusChanged:
        {
            if (text === inputHelp && focus)
                text = ""
        }
        property alias mutualValue: window.mutualValue
        onMutualValueChanged: setValue(mutualValue)
        property string inputHelp
        color: (text === inputHelp && !focus ? "grey" : "black")
        function setValue(mutualValue)
        {
            inputHelp = mutualValue.toFixed(3)
            text = inputHelp
        }
    }

    Slider {
        x: 200
        width: 200
        height: 40
        onValueChanged: setMutualValue(value)
        property alias mutualValue: window.mutualValue
        onMutualValueChanged: setValue(mutualValue)
        function setValue(mutualValue)
        {
            value = mutualValue
        }
    }

    Text {
        x: (parent.width - width) / 2
        y: 40 + (40 - height) / 2
        text: "Current value is: " + (window.mutualValue * 100).toFixed(2) + "%"
    }
}

Upvotes: 5

Related Questions