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?
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.