fallerd
fallerd

Reputation: 1738

Binding Type: Is it possible to listen to a temporarily overridden binding?

I'd like to create a widget that has a value that can be bound to a value outside of itself, but can also internally set this value. A scenario I'd like to be able to support:

  1. Developer using the widget binds the widget's value to some external value
  2. At run-time, widget value follows any external value changes via this binding
  3. User interacts with widget, setting their own value
  4. Some time later, external value is updated
  5. Ideally, widget value would then return to being bound to the external value

Point 5 does not seem possible when using only bindings. Here is an example where 'textItem' is our imaginary widget:

import QtQuick 2.7
import QtQuick.Controls 2.0

ApplicationWindow {
    visible: true
    width: 640
    height: 480

    property real externalValue: (Math.random()* 50).toFixed(0)

    Timer {
        running: true
        repeat: true
        interval: 1000
        onTriggered: {
            // Simulate externalValue changes out of our control
            externalValue = (Math.random()* 50).toFixed(0)
        }
    }

    Component.onCompleted: {
        // Unknown external binding set by developer using textItem widget
        textItem.text = Qt.binding(function(){return externalValue})
    }

    Column {

        Text {
            id: textItem
            text: ""

            property real internalValue: (Math.random()* 50).toFixed(0)

            Binding on text {
                id: binding
                value: textItem.internalValue
                when: false
            }
        }

        Button {
            text: "Change Internal Value"
            onClicked: {
                textItem.internalValue = (Math.random()* 50).toFixed(0)
                binding.when = true
            }
        }
    }
}

So textItem.text listens to the externalValue binding until a user interacts with the button, which then binds textItem.text to the user-set internal value. Assuming textEdit is a black-box widget, and it has no concept of externalValue before its runtime binding, is there any way for textEdit to internally listen to the overridden externalValue binding and restore it (by setting binding.when = false) the next time that externalValue is updated by the timer?

One way to make the scenario work would be to use direct assignments in place of all of the bindings, but this seems like it would cause a confusing widget API since I can't stop users from trying to use bindings which would confusingly get broken by the widget's black-box internal assignments...

Upvotes: 1

Views: 47

Answers (1)

Amfasis
Amfasis

Reputation: 4208

You can temporarily override bindings using States, like in the code below.

The real problem here is detecting when the external value has changed, in my solution I'm using a Timer for this, but your requirements ask for the external value to change again. Since you are externally binding to property text and also overriding the binding to text you temporarily loose the change signals from the external binding, hence cannot undo the temporary binding.

If you have control over the widget, I would implement a property which should be set externally and internally assign that value to where it should go. This gives you the ability to receive the changes and for example stop the tempBoundedTimer (Since I still think you should have a timer to not indefinitely override the binding in case the external value fails to update).

If you don't have control over the widget, I would settly down on a suitable interval for tempBoundedTimer

(In any case, I don't adding a Timer in each instance of the widget is very nice)

import QtQuick 2.7
import QtQuick.Controls 2.0

ApplicationWindow {
    visible: true
    width: 640
    height: 480

    property real externalValue: (Math.random()* 50).toFixed(0)

    Timer {
        running: true
        repeat: true
        interval: 1000
        onTriggered: {
            // Simulate externalValue changes out of our control
            externalValue = (Math.random()* 50).toFixed(0)
        }
    }


    Component.onCompleted: {
        // Unknown external binding set by developer using textItem widget
        textItem.text = Qt.binding(function(){return externalValue})
    }

    Column {

        Text {
            id: textItem
            text: ""

            property real internalValue: (Math.random()* 50).toFixed(0)

            Timer {
                id: tempBoundedTimer
                repeat: false
                interval: 2000
            }

            states: [
                State {
                    when: tempBoundedTimer.running
                    PropertyChanges {
                        target: textItem
                        text: "internal:" + internalValue
                    }
                }
            ]
        }

        Button {
            text: "Change Internal Value"
            onClicked: {
                textItem.internalValue = (Math.random()* 50).toFixed(0)
                tempBoundedTimer.start()
            }
        }
    }
}

BTW, I think your Binding object should actually also work if the when is bound to tempBoundedTimer.running, but I couldn't get it to play nice. It seems the Qt.binding has priority

Upvotes: 1

Related Questions