Feature/table view#3142
Conversation
We use repeaters instead of a real tableview as tablemodel does not seem to support dynamic layout generation.
There was a problem hiding this comment.
Code Review
This pull request introduces a new tableViewAttributeComponent in AttributeItemDelegate.qml to render list attributes of type GroupAttribute as a table. The reviewer provided valuable feedback, identifying that directly setting cell values bypasses Meshroom's scene management and undo/redo stack, and that resizing columns and rows using local coordinates on moving MouseArea elements causes feedback loops and jitter. Additionally, the reviewer suggested replacing the manual scrollbar implementation with a Flickable component to support native scrolling features.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| TextInput { | ||
| id: cellInput | ||
| anchors.fill: parent | ||
| anchors.margins: 4 | ||
| verticalAlignment: TextInput.AlignVCenter | ||
| horizontalAlignment: TextInput.AlignHCenter | ||
| color: "#aaaaaa" | ||
| selectionColor: "#5599ff" | ||
| clip: true | ||
| text: { | ||
| var cell = rowItem.object.value.at(index) | ||
| return cell ? String(cell.value) : "" | ||
| } | ||
| onEditingFinished: { | ||
| var cell = rowItem.object.value.at(cellRect.index) | ||
| if (cell) cell.value = text | ||
| } | ||
| } |
There was a problem hiding this comment.
Directly setting cell.value = text bypasses Meshroom's scene management and undo/redo stack (_currentScene). This can lead to desynchronization between the UI and the underlying Python graph/history. Additionally, values for numeric parameters (IntParam, FloatParam) should be converted to numbers.
We can also optimize the text binding by declaring cell as a property of the TextInput to make the dependency explicit and avoid block bindings.
TextInput {
id: cellInput
property var cell: rowItem.object.value.at(index)
anchors.fill: parent
anchors.margins: 4
verticalAlignment: TextInput.AlignVCenter
horizontalAlignment: TextInput.AlignHCenter
color: "#aaaaaa"
selectionColor: "#5599ff"
clip: true
text: cell ? String(cell.value) : ""
onEditingFinished: {
if (cell) {
var val = text
if (cell.type === "IntParam" || cell.type === "FloatParam") {
val = Number(text)
}
_currentScene.setAttribute(cell, val)
}
}
}
| MouseArea { | ||
| width: 6 | ||
| height: parent.height | ||
| anchors.right: parent.right | ||
| cursorShape: Qt.SizeHorCursor | ||
| property real startX: 0 | ||
| property real startW: 0 | ||
| onPressed: function(mouse) { | ||
| startX = mouse.x | ||
| startW = tableLayout.columnWidths[headerCell.index] | ||
| } | ||
| onPositionChanged: function(mouse) { | ||
| if (!pressed) return | ||
| var newW = Math.max(40, startW + (mouse.x - startX)) | ||
| var arr = tableLayout.columnWidths.slice() | ||
| arr[headerCell.index] = newW | ||
| tableLayout.columnWidths = arr | ||
| } | ||
| } |
There was a problem hiding this comment.
Resizing columns using local coordinates (mouse.x) on a MouseArea that moves as a result of the resize causes a feedback loop. When the column width increases, the MouseArea shifts to the right, which immediately decreases the local mouse.x coordinate, leading to severe jitter and unstable dragging.
To fix this, map the mouse coordinates to a stable parent coordinate space (like tableLayout) that does not shift during resizing.
MouseArea {
width: 6
height: parent.height
anchors.right: parent.right
cursorShape: Qt.SizeHorCursor
property real startX: 0
property real startW: 0
onPressed: function(mouse) {
var stablePoint = mapToItem(tableLayout, mouse.x, mouse.y)
startX = stablePoint.x
startW = tableLayout.columnWidths[headerCell.index]
}
onPositionChanged: function(mouse) {
if (!pressed) return
var stablePoint = mapToItem(tableLayout, mouse.x, mouse.y)
var newW = Math.max(40, startW + (stablePoint.x - startX))
var arr = tableLayout.columnWidths.slice()
arr[headerCell.index] = newW
tableLayout.columnWidths = arr
}
}
| MouseArea { | ||
| width: parent.width | ||
| height: 6 | ||
| anchors.bottom: parent.bottom | ||
| cursorShape: Qt.SizeVerCursor | ||
| property real startY: 0 | ||
| property real startH: 0 | ||
| onPressed: function(mouse) { | ||
| startY = mouse.y | ||
| startH = tableLayout.rowHeights[rowItem.index] | ||
| } | ||
| onPositionChanged: function(mouse) { | ||
| if (!pressed) return | ||
| var newH = Math.max(20, startH + (mouse.y - startY)) | ||
| var arr = tableLayout.rowHeights.slice() | ||
| arr[rowItem.index] = newH | ||
| tableLayout.rowHeights = arr | ||
| } | ||
| } |
There was a problem hiding this comment.
Similar to the column resizing issue, resizing row heights using local coordinates (mouse.y) on a MouseArea anchored to the bottom of the row causes a feedback loop and jitter. As the row height increases, the MouseArea moves down, shifting the local mouse.y coordinate.
Map the coordinates to the stable tableLayout item to ensure smooth dragging.
MouseArea {
width: parent.width
height: 6
anchors.bottom: parent.bottom
cursorShape: Qt.SizeVerCursor
property real startY: 0
property real startH: 0
onPressed: function(mouse) {
var stablePoint = mapToItem(tableLayout, mouse.x, mouse.y)
startY = stablePoint.y
startH = tableLayout.rowHeights[rowItem.index]
}
onPositionChanged: function(mouse) {
if (!pressed) return
var stablePoint = mapToItem(tableLayout, mouse.x, mouse.y)
var newH = Math.max(20, startH + (stablePoint.y - startY))
var arr = tableLayout.rowHeights.slice()
arr[rowItem.index] = newH
tableLayout.rowHeights = arr
}
}
| Item { | ||
| id: outerFrame | ||
| Layout.fillWidth: true | ||
| Layout.preferredHeight: Math.min(tableLayout.totalTableHeight + 30, 330) | ||
| ScrollBar { | ||
| id: hBar | ||
| orientation: Qt.Horizontal | ||
| anchors.left: outerFrame.left | ||
| anchors.right: outerFrame.right | ||
| anchors.bottom: outerFrame.bottom | ||
| anchors.rightMargin: vBar.width | ||
| policy: ScrollBar.AlwaysOn | ||
| size: (outerFrame.width - vBar.width) / | ||
| Math.max(tableLayout.totalTableWidth, 1) | ||
| } | ||
| ScrollBar { | ||
| id: vBar | ||
| orientation: Qt.Vertical | ||
| anchors.top: outerFrame.top | ||
| anchors.bottom: outerFrame.bottom | ||
| anchors.right: outerFrame.right | ||
| anchors.bottomMargin: hBar.height | ||
| policy: ScrollBar.AlwaysOn | ||
| size: (outerFrame.height - hBar.height) / | ||
| Math.max(tableLayout.totalTableHeight + 30, 1) | ||
| } | ||
| Item { | ||
| id: viewport | ||
| anchors.left: outerFrame.left | ||
| anchors.top: outerFrame.top | ||
| anchors.right: outerFrame.right | ||
| anchors.bottom: outerFrame.bottom | ||
| anchors.rightMargin: vBar.width | ||
| anchors.bottomMargin: hBar.height | ||
| clip: true | ||
| Item { | ||
| id: content | ||
| width: tableLayout.totalTableWidth | ||
| height: tableLayout.totalTableHeight + 30 | ||
| x: -hBar.position * tableLayout.totalTableWidth | ||
| y: -vBar.position * (tableLayout.totalTableHeight + 30) |
There was a problem hiding this comment.
The current manual scrollbar implementation binds x and y coordinates of a plain Item to the scrollbar positions. This approach lacks native scrolling features such as mouse wheel scrolling, trackpad panning, keyboard navigation, and kinetic flicking.
Using a Flickable instead of a plain Item viewport allows you to get all these native features for free, while still keeping the scrollbars positioned exactly where they are.
Item {
id: outerFrame
Layout.fillWidth: true
Layout.preferredHeight: Math.min(tableLayout.totalTableHeight + 30, 330)
Flickable {
id: flickable
anchors.left: outerFrame.left
anchors.top: outerFrame.top
anchors.right: outerFrame.right
anchors.bottom: outerFrame.bottom
anchors.rightMargin: vBar.width
anchors.bottomMargin: hBar.height
clip: true
contentWidth: tableLayout.totalTableWidth
contentHeight: tableLayout.totalTableHeight + 30
ScrollBar.horizontal: ScrollBar {
id: hBar
parent: outerFrame
anchors.left: outerFrame.left
anchors.right: outerFrame.right
anchors.bottom: outerFrame.bottom
anchors.rightMargin: vBar.width
policy: ScrollBar.AlwaysOn
}
ScrollBar.vertical: ScrollBar {
id: vBar
parent: outerFrame
anchors.top: outerFrame.top
anchors.bottom: outerFrame.bottom
anchors.right: outerFrame.right
anchors.bottomMargin: hBar.height
policy: ScrollBar.AlwaysOn
}
Item {
id: content
width: tableLayout.totalTableWidth
height: tableLayout.totalTableHeight + 30
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #3142 +/- ##
========================================
Coverage 85.35% 85.35%
========================================
Files 73 73
Lines 11453 11453
========================================
Hits 9776 9776
Misses 1677 1677 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Description
Features list
Implementation remarks
We use repeaters instead of a real tableview as tablemodel does not seem to support dynamic layout generation.