gui: fix infinite layout loop in chat ListView
The delegate used a ColumnLayout root with parent-dependent width fallbacks (: 0, : 100) causing two-pass layouts. Combined with onContentHeightChanged -> positionViewAtEnd(), this created a feedback loop: height change -> scroll -> refill -> new delegates -> height change -> ... burning 100% CPU on the main thread. Fixes: - Replace onContentHeightChanged with onCountChanged + Qt.callLater - Replace ColumnLayout root with plain Column - Bind all child widths to root.width (top-down, no fallbacks) - Create context Menu lazily on first right-click - Add reuseItems: true to the ListView - Standardize bin path to ~/.local/bin
This commit is contained in:
parent
51b2013ac3
commit
022980bc40
|
|
@ -3,7 +3,7 @@ import QtQuick 2.15
|
|||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
ColumnLayout {
|
||||
Column {
|
||||
id: root
|
||||
spacing: 2
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ ColumnLayout {
|
|||
|
||||
// Clickable header
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
width: root.width
|
||||
height: 22
|
||||
radius: 3
|
||||
color: root.headerColor()
|
||||
|
|
@ -115,7 +115,7 @@ ColumnLayout {
|
|||
|
||||
// Block content rendered as segments
|
||||
Column {
|
||||
Layout.fillWidth: true
|
||||
width: root.width
|
||||
visible: root.blockExpanded
|
||||
spacing: 4
|
||||
|
||||
|
|
@ -123,7 +123,7 @@ ColumnLayout {
|
|||
model: root.blockSegments || []
|
||||
|
||||
delegate: Loader {
|
||||
width: parent ? parent.width : 0
|
||||
width: root.width
|
||||
active: modelData !== null && modelData !== undefined
|
||||
sourceComponent: (modelData && modelData.type === "diagram") ? diagramComponent : textComponent
|
||||
property var segmentData: modelData || {}
|
||||
|
|
@ -137,7 +137,7 @@ ColumnLayout {
|
|||
|
||||
TextEdit {
|
||||
id: textEdit
|
||||
width: parent ? parent.width : 100
|
||||
width: root.width
|
||||
text: segmentData.html || ""
|
||||
textFormat: Text.RichText
|
||||
readOnly: true
|
||||
|
|
@ -150,23 +150,11 @@ ColumnLayout {
|
|||
selectedTextColor: theme.highlightedTextColor
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (!activeFocus && root.parentListView)
|
||||
root.parentListView.interactive = true
|
||||
}
|
||||
|
||||
Menu {
|
||||
id: contextMenu
|
||||
MenuItem {
|
||||
text: "Copy"
|
||||
enabled: textEdit.selectedText.length > 0
|
||||
onTriggered: textEdit.copy()
|
||||
}
|
||||
MenuItem {
|
||||
text: "Select All"
|
||||
onTriggered: textEdit.selectAll()
|
||||
}
|
||||
if (!activeFocus && root.parentListView)
|
||||
root.parentListView.interactive = true
|
||||
}
|
||||
|
||||
// Left-click: disable ListView flick while selecting text
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
|
|
@ -182,11 +170,41 @@ ColumnLayout {
|
|||
}
|
||||
}
|
||||
|
||||
// Right-click: lazy context menu
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
cursorShape: Qt.IBeamCursor
|
||||
onClicked: contextMenu.popup()
|
||||
onClicked: {
|
||||
if (!textEdit._menu) {
|
||||
textEdit._menu = contextMenuComponent.createObject(textEdit)
|
||||
}
|
||||
textEdit._menu.popup()
|
||||
}
|
||||
}
|
||||
|
||||
property Menu _menu: null
|
||||
|
||||
Component.onDestruction: {
|
||||
if (_menu) _menu.destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shared context menu component (created on demand)
|
||||
Component {
|
||||
id: contextMenuComponent
|
||||
Menu {
|
||||
id: ctxMenu
|
||||
property TextEdit target: parent
|
||||
MenuItem {
|
||||
text: "Copy"
|
||||
enabled: ctxMenu.target ? ctxMenu.target.selectedText.length > 0 : false
|
||||
onTriggered: if (ctxMenu.target) ctxMenu.target.copy()
|
||||
}
|
||||
MenuItem {
|
||||
text: "Select All"
|
||||
onTriggered: if (ctxMenu.target) ctxMenu.target.selectAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -197,7 +215,7 @@ ColumnLayout {
|
|||
|
||||
Rectangle {
|
||||
id: diagramRect
|
||||
width: parent ? parent.width : 100
|
||||
width: root.width
|
||||
height: diagramContent.height + diagramHeader.height + 8
|
||||
color: "transparent"
|
||||
border.color: theme.midColor
|
||||
|
|
|
|||
17
gui/main.qml
17
gui/main.qml
|
|
@ -883,18 +883,21 @@ ApplicationWindow {
|
|||
visible: ollie.daemonConnected && ollie.activeAgentId !== ""
|
||||
|
||||
property bool followTail: true
|
||||
property bool scrollingNow: false
|
||||
|
||||
onContentHeightChanged: {
|
||||
if (followTail && !scrollingNow && contentHeight > 0) {
|
||||
scrollingNow = true
|
||||
positionViewAtEnd()
|
||||
scrollingNow = false
|
||||
}
|
||||
reuseItems: true
|
||||
|
||||
onCountChanged: {
|
||||
if (followTail)
|
||||
Qt.callLater(chatView.scrollToEnd)
|
||||
}
|
||||
|
||||
onMovementEnded: followTail = atYEnd
|
||||
|
||||
function scrollToEnd() {
|
||||
if (followTail && contentHeight > height)
|
||||
positionViewAtEnd()
|
||||
}
|
||||
|
||||
delegate: ChatBlockDelegate {
|
||||
width: chatView.width - 8
|
||||
parentListView: chatView
|
||||
|
|
|
|||
Reference in New Issue