QML Flickable with TextArea: property binding for contentY is overwritten - by whom?

1.3k Views Asked by At

I'm making a terminal widget. I want the Flickable to scroll down to the latest input when TextArea.text is updated. My code looks like as follows.

ColumnLayout {
    anchors.fill: parent
    Flickable {
        id: scroller
        clip: true
        contentY: contentHeight - height
        onContentYChanged: {
            console.log("contentY:", contentY)
        }
        TextArea.flickable: TextArea {
            id: textArea
            Layout.fillWidth: true
        }
        Layout.fillWidth: true
        Layout.fillHeight: true
    }
    RowLayout {
        id: prompt
        Label {
            text: " > $ "
        }
        TextField {
            id: textInput
            Layout.fillWidth: true
        }
    }
}

When I run this, I see that contentY is being overwritten right after it's set by my binding:

qml: contentY: 1498
qml: contentY: 0
qml: contentY: 1517
qml: contentY: 0

I checked to make sure my binding is not setting it to 0. I tried to debug the binding loop using export QT_LOGGING_RULES="qt.qml.binding.removal.info=true", that came out clean. I had a look at the source for Flickable and I don't think any of the methods there are the culprit.

Is binding contentY the right way to do what I want? Why is my binding is not being respected?

1

There are 1 best solutions below

2
David K. Hess On BEST ANSWER

The issue is that contentY is going to be overwritten constantly by the Flickable as the content inside of it moves around. You can't place a binding on it because as you can see, it will be immediately overwritten with a static value that updates as the user interacts with the flickable.

What you need to do instead is something like

onContentHeightChanged: Qt.callLater(() => contentY = contentHeight - height)

Now, whenever the text area grows, it will jump the contentY via an immediate assignment instead of trying to rely on a binding. The callLater ensures that it happens after the flickable resets contentY to 0 on its own due to the height change.