Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

TableView - Show an optional item covering the whole row of a multi-columns table view



  • In a project, I added a TableView component, which is composed by a multiple columns header and several rows, as shown in the below screenshot:

    tableview with progress.png

    Depending on events happening in the application, I need to cover a part of some items with a kind of overlay, which e.g shows an error or ask a confirmation to the user, as shown in the below screenshot:

    tableview with overlay.png

    My idea was to add a special Item component in my TableWiew delegate, which would be optionally visible under certain conditions, and only drawn with the first column (i.e visible: column === 0) but covering the last part of my row by using the delegate anchoring and the parent view width.

    This solution seems possible, but has several issues:

    1. The content of the columns covered by the item are visible above the item, even if they z order is set to 0 whereas the item z order is set to 1. Probably because the TableView draws the columns randomly, depending on what it needs.
    2. I tried to draw the item on the last column instead of the first one. This resolve partially the issue mentioned in point 1 but randomly the content of several columns appear above my item, depending on if the tree decide to refresh the content of these particular columns.
    3. I tried to paint the background of each column covered by my item. This resolve the point 1 and 2, but the components contained in my item are also partially cropped by the background.

    So my questions are:

    • Is my idea correct or is there another way to achieve that?
    • If my idea isn't the good one, what is the best way to create a such item?
    • From the qml, is there a signal which notifies that a row was painted, or a way to know that a particular row was painted?


  • Thanks to all for the replies.

    I finally resolved the issue by replacing the TableView by a ListView and rewriting the columns handling manually. As the ListView draws the whole item content once, all the above mentioned issues are resolved at the same time.

    However very interesting concepts were presented here, and they may help me for other similar situations in the future. Once again, thank you very much to took the time to help me and reply to my questions.



  • @jeanmilost Hi:
    Do you want something like this?

    rowswithbutttons.gif

    Regards,
    Emmanuel



  • How did you do the status bars? Did you use a popup?
    I imagine you could use another popup and ensure it covers the status bar?
    Here is the status bar code I played with:

    import QtQuick 2.15
    import QtQuick.Window 2.15
    import QtQuick.Controls 2.12
    
    import Qt.labs.qmlmodels 1.0
    
    Window {
        id: root
    
        visible: true
        width: 800
        height: 600
        title: qsTr("TableView Testing")
    
        Row {
            id: root_row
    
            TableView {
                id: tableview1
    
                width: root.width/2
                height: root.height - 50
    
                clip: true
    
                model: tablemodel
    
                delegate: Item {
                    id: del_item
    
                    implicitWidth: tableview1.width/3
                    implicitHeight: del_height + progbar_height
    
                    property bool column0: column == 0
                    property int del_height: 25
                    property int progbar_height: statusTip < 1.0 ? 20 : 0
    
                    Rectangle {
                        anchors.fill: parent
                        color: "lightsteelblue"
                        clip: true
                        border.width: 1
                        border.color: "steelblue"
    
                        Text {
                            id: del_text
    
                            height: del_item.del_height
    
                            text: display
                        }
    
                        Popup {
                            y: del_item.del_height
                            width: tableview1.width
                            height: del_item.progbar_height
                            visible: del_item.column0 && del_item.progbar_height > 0
                            modal: false
                            focus: false
                            closePolicy: Popup.NoAutoClose
    
                            background: Rectangle {
                                color: "transparent"
                            }
    
                            contentItem: ProgressBar {
                                from: 0
                                to: 1.0
                                value: statusTip
    
                                anchors.fill: parent
                                anchors.margins: 5
                            }
                        }
                    }
                }
            }
        }
    
        Slider {
            id: progress_slider
    
            anchors.top: root_row.bottom
    
            value: 1.0
    
            from: 0
            to: 1.0
        }
    
        TableModel {
            id: tablemodel
    
            property real progress: progress_slider.value
    
            TableModelColumn { display: "text"; statusTip: "progress" }
            TableModelColumn { display: "path"; statusTip: "progress" }
            TableModelColumn { display: "image"; statusTip: "progress" }
    
            rows: [
                {
                    text: "one",
                    path: "/root/one.txt",
                    image: "/root/one.png",
                    progress: progress
                },
                {
                    text: "two",
                    path: "/root/two.txt",
                    image: "/root/two.png",
                    progress: progress
                },
                {
                    text: "three",
                    path: "/root/three.txt",
                    image: "/root/three.png",
                    progress: progress
                },
            ]
        }
    }
    


  • Like this:

    import QtQuick 2.15
    import QtQuick.Window 2.15
    import QtQuick.Controls 2.12
    
    import Qt.labs.qmlmodels 1.0
    
    Window {
        id: root
    
        visible: true
        width: 800
        height: 600
        title: qsTr("TableView Testing")
    
        Row {
            id: root_row
    
            TableView {
                id: tableview1
    
                width: root.width/2
                height: root.height - 50
    
                clip: true
    
                model: tablemodel
    
                delegate: Item {
                    id: del_item
    
                    implicitWidth: tableview1.width/3
                    implicitHeight: del_height + progbar_height
    
                    property bool column0: column == 0
                    property bool column1: column == 1
                    property int del_height: 25
                    property int progbar_height: statusTip < 1.0 ? 20 : 0
    
                    Rectangle {
                        anchors.fill: parent
                        color: "lightsteelblue"
                        clip: true
                        border.width: 1
                        border.color: "steelblue"
    
                        Text {
                            id: del_text
    
                            height: del_item.del_height
    
                            text: display
                        }
    
                        Popup {
                            y: del_item.del_height
                            width: tableview1.width
                            height: del_item.progbar_height
                            visible: del_item.column0 && del_item.progbar_height > 0
                            modal: false
                            focus: false
                            closePolicy: Popup.NoAutoClose
    
                            background: Rectangle {
                                color: "transparent"
                            }
    
                            contentItem: ProgressBar {
                                from: 0
                                to: 1.0
                                value: statusTip
    
                                anchors.fill: parent
                                anchors.margins: 5
                            }
                        }
                        Popup {
                            width: del_item.implicitWidth*2
                            height: del_item.implicitHeight // del_item.del_height // if you want to show progress bar
                            visible: del_item.column1 && decoration
                            modal: false
                            focus: false
                            closePolicy: Popup.NoAutoClose
    
                            onHeightChanged: {
                                //console.log(height)
                            }
    
                            background: Rectangle {
                                color: "darkred"
                            }
    
                            Text {
                                anchors.centerIn: parent
                                text: "BIG BAD!"
                            }
                        }
                    }
                }
            }
        }
    
        Slider {
            id: progress_slider
    
            anchors.top: root_row.bottom
    
            value: 1.0
    
            from: 0
            to: 1.0
        }
    
        TableModel {
            id: tablemodel
    
            property real progress: progress_slider.value
    
            TableModelColumn { display: "text"; statusTip: "progress"; decoration: "warning"}
            TableModelColumn { display: "path"; statusTip: "progress"; decoration: "warning"}
            TableModelColumn { display: "image"; statusTip: "progress"; decoration: "warning"}
    
            rows: [
                {
                    text: "one",
                    path: "/root/one.txt",
                    image: "/root/one.png",
                    progress: progress,
                    warning: false
                },
                {
                    text: "two",
                    path: "/root/two.txt",
                    image: "/root/two.png",
                    progress: progress_slider.to - progress,
                    warning: true
                },
                {
                    text: "three",
                    path: "/root/three.txt",
                    image: "/root/three.png",
                    progress: progress,
                    warning: false
                },
            ]
        }
    }
    


  • Thank for all the answers.

    @Allon Not exactly. In fact I need an area which covers a part of the existing row, showing extra information like a confirmation to user, or an error message. This area should appear on the top of all existing components already contained in the row, and may cover them , partially or completely. The area should also not be dependent of the columns, i.e it may cover several columns, partially or completely. I know that this behavior may be somewhat illogical, but it's a feature required by our client.

    @fcarney The status bar is just a Rectangle covering the bottom of the row. It is drawn while the first cell of the row is drawn, and as the clipping is ignored it may cover the whole row without problems. The key is that no child component is drawn on the same location covered by the progress, so there is no drawing conflicts here and I can draw it once for all (to be honest I implemented this progress based on another question answered by yourself, here: https://forum.qt.io/topic/117322/how-to-add-a-component-onto-the-whole-row-of-a-multi-column-tableview).

    But the situation isn't the same here, unfortunately. The overlay I want should cover already existing children components, and they enter in conflict with it. I tried several solutions, among others:

    • drawing my overlay while the first column is drawn. This failed because all the components contained in the other cells were overprinted above my overlay, whatever the z value.
    • drawing my overlay while the last column is drawn. This worked better, however the view seems to refresh several cells randomly, and they appear overprinted when this scenario happens.
    • force some columns to erase their background before painting the overlay (by drawing a rectangle covering the whole cell content after the normal content was painted). This worked, except that the components drawn in the overlay were also cropped when they overlapped other columns.
    • using a popup for my overlay. This was the best solution until now, however there is an issue: the popup isn't clipped into the TableView bounds.

    About the popup, this would be the solution to my issue if I can apply the above mentioned clipping. Do you know how to achieve that?



  • @jeanmilost Hi
    An area which covers a part of the existing row, is it not what happens when the cursor goes over the three points and the rectangle appears with the pen and the bin?
    Regards



  • @Allon First of all, thank you for your help. Yes, your example may match with what I need, however it's not representative of the issue I face. AFAIK, the idea is to put the three dots component and its 2 buttons overlay in a well bounded cell, or in an Item placed at the end of the row if the tree doesn't contain columns.

    But in my case, things are different. I have a multi-columns tree, and I need an overlay which overlap several cells, completely or partially, and remain independent of these cells, breaking thus the logical provided by the tree. And a such overlay is incompatible with the standard painting process, unfortunately.

    I'm aware that creating a multi-columns tree to break the rules and show graphical elements which overlap the cells sounds like a conception issue, and I already reported that to my client. But as he really want the tree working this way, I need to find a solution.

    I'm also trying another approach, which use a ListView instead of a TableView, but although this may resolve the overlay issue I described above, I need to rewrite manually the whole columns management, and this brings performance issues as described in this post: https://forum.qt.io/topic/118005/listview-how-to-refresh-rows-which-content-changed-without-slowdown



  • @jeanmilost said in TableView - Show an optional item covering the whole row of a multi-columns table view:

    About the popup, this would be the solution to my issue if I can apply the above mentioned clipping. Do you know how to achieve that?

    I didn't, but now I do:

             delegate: Item {
                    id: del_item
    
                    implicitWidth: tableview1.width/3
                    implicitHeight: del_height + progbar_height
    
                    property bool column0: column == 0
                    property int del_height: 25
                    property int progbar_height: statusTip < 1.0 ? 20 : 0
    
                    property bool column1: column == 1
                    property var coords
                    onCoordsChanged: {
                        badrect.x = coords.x
                        badrect.y = coords.y
                    }
                    onXChanged: {
                        coords = mapToItem(tableview1,0,0)
                    }
    
                    Rectangle {
                        id: badrect
    
                        color: "darkred"
    
                        parent: tableview1
    
                        visible: del_item.column1 && decoration
    
                        width: del_item.implicitWidth*2
                        height: del_item.implicitHeight
                    }
    
                    Rectangle {
                        anchors.fill: parent
                        color: "lightsteelblue"
                        clip: true
                        border.width: 1
                        border.color: "steelblue"
    
                        Text {
                            id: del_text
    
                            height: del_item.del_height
    
                            text: display
                        }
    
                        Popup {
                            y: del_item.del_height
                            width: tableview1.width
                            height: del_item.progbar_height
                            visible: del_item.column0 && del_item.progbar_height > 0
                            modal: false
                            focus: false
                            closePolicy: Popup.NoAutoClose
    
                            background: Rectangle {
                                color: "transparent"
                            }
    
                            contentItem: ProgressBar {
                                from: 0
                                to: 1.0
                                value: statusTip
    
                                anchors.fill: parent
                                anchors.margins: 5
                            }
                        }
                        /*
                        Popup {
                            width: del_item.implicitWidth*2
                            height: del_item.implicitHeight // del_item.del_height // if you want to show progress bar
                            visible: del_item.column1 && decoration
                            modal: false
                            focus: false
                            closePolicy: Popup.NoAutoClose
    
                            onHeightChanged: {
                                //console.log(height)
                            }
    
                            background: Rectangle {
                                color: "darkred"
                            }
    
                            Text {
                                anchors.centerIn: parent
                                text: "BIG BAD!"
                            }
                        }
                        */
                    }
                }
    

    The trick is re-parenting the rectangle (badrect) to the tableview1 and capturing when the coordinates of the del_item changes. You cannot get the coordinates during the Component.onCompleted signal. It has to be done later. There might be a more elegant way to get this, but whatever. If x changes more than once you should be okay. Not sure if this will work when a cell is on the left side of a table, or at the top if tracking y. So maybe calling mapToItem with x and y in the call as dummy change triggers would be better.

    Edit: Maybe this won't work. It still seems to have clipping issues. I don't understand why. My tableview is set to clip.
    Edit 2: This may have more to do with my tableview not resize when the window resizes. Not sure what the issues is there.



  • @jeanmilost
    Hi,
    I would still not use popup.
    Here is how I did it.
    https://invent.kde.org/education/gcompris/-/blob/server_allon/src/activities/server/components/ManageGroupsBar.qml

    The line that will interest you is the following:
    https://invent.kde.org/education/gcompris/-/blob/server_allon/src/activities/server/components/ManageGroupsBar.qml#L179

    If I get hover the elipses mousearea I set modifyGroupCommandsRectangle to visible, which means I display the rectangle with my pen and my bin.
    In your case this would be one rectangle with "download failed and cancel".

    At the moment I am working with RowLayout and a repeater, but I will change it soon to a Listview + inner rectangles to be able to take advantage of the flickable property.
    If you want to cover several cells you can anchor your modifyGroupCommandsRectangle to the anchors.right of your listview element and set the length of it to the addition of several cells length.
    You can even using repeater index to set your "download failed and cancel" on any element of you listview.

    Hope I got it right.



  • Thanks to all for the replies.

    I finally resolved the issue by replacing the TableView by a ListView and rewriting the columns handling manually. As the ListView draws the whole item content once, all the above mentioned issues are resolved at the same time.

    However very interesting concepts were presented here, and they may help me for other similar situations in the future. Once again, thank you very much to took the time to help me and reply to my questions.