[solved] A sort of tree view in QML?



  • Hi all...
    I'm here again with another problem, or better, in this case, I'm wondering how to do a certain thing... :)
    I have an xml file containing the comments to a certain wordpress blog post. Each comment in the list has a key saying of which other comment it is child of (i.e. there is the id of the parent).
    Now I need to show this comments in the app, but in threaded way, such as, maybe, any comment is a few pixel indented with respect to its parent... Some easy and practical ideas on how to do this? I think that the listview is not a solution. Maybe it should be usefoul to have a TreeView view, to connect to an XmlListModel, but I don't think such a component is available in QML!

    As always, thanks all :P



  • It should not be a problem in QML to build a TreeView. As outer most element you talke a ListView and as delegate a Text element which represents the top level item and a Column that contains the children. The Column delegates again can have a top level item + a column again...understand what I mean? I think the most difficult thing is to build a model for such a view. But I think it is doable.



  • Will try to write up some code, then will come back here, let's see what I can sort out :P



  • Ok, I have had an idea on hot to "solve" this, that maybe is easier than the Column one (btw: hot to define a delegate for a Column?).
    The idea is to use a "Comment" model for the delegate of the ListView, that has as first item an empty item deciding the distance from the border, and then the content of the comment, that are the text, author and date. Code:

    @
    import QtQuick 1.1
    import com.nokia.symbian 1.1

    Component {
    property int commentId;
    property int level;
    property alias text: commentText.text
    property alias author: commentAuthor.text
    property alias date: commentDate.text

    Item {
        id: commentWrapper
        height: childrenRect.height
    
        Item {
            id: levelMarginElement
            width: (level > 6 ? 7 : level) * 5
            anchors.left: parent.left
        }
        Item {
            anchors.left: levelMarginElement.right
            anchors.verticalCenter: commentWrapper.verticalCenter
    
            Text {
                id: commentText
            }
            Row {
                id: commentInfos
    
                height: childrenRect.height
                anchors {
                    top: commentText.bottom
                    left: parent.left
                    right: parent.right
                    topMargin: 10
                }
                Label {
                    id: commentAuthor
                    anchors {
                        top: parent.top
                        left: parent.left
                    }
                    width: (parent.width / 2)
    
                    platformInverted: window.platformInverted
                    font.pixelSize: platformStyle.fontSizeSmall
                    horizontalAlignment: Text.AlignLeft
                }
                Label {
                    id: commentDate
                    anchors {
                        top: parent.top
                        right: parent.right
                        left: commentAuthor.right
                    }
    
                    platformInverted: window.platformInverted
                    font.pixelSize: platformStyle.fontSizeSmall
                    horizontalAlignment: Text.AlignRight
                }
            }
            Button {
                id: commentsReply
                anchors {
                    top: commentInfos.bottom
                    right: parent.right
                }
                text: "Reply"
            }
        }
    }
    

    }@

    Now the problem shoul be to define the ListModel for this ListView.... :S



  • I think I get the first part of it :D

    So, the idea is to have a XmlListModel for handling data, inside a standard ListModel. Once the data are loaded into the xmlListModel, I use javascript and sort item, inserting them into the outermost list model, in the desirable order. This seems to work!

    Now I need to better understand how I can select the right level for the comment object of the previous post in all of this thing...

    An easy way is to add a tag to the server generated xml file, and use it, but is not really a good solution, i suppose! :)

    @
    import QtQuick 1.1

    ListModel {
    id: root

    property alias source: xmlListModel.source
    property alias query: xmlListModel.query
    property alias namespaceDeclarations: xmlListModel.namespaceDeclarations
    
    property alias status: xmlListModel.status
    
    function push(item) {
        append(item);
    }
    
    property variant __xmlListModel: XmlListModel {
        id: xmlListModel
    
        XmlRole {
            name: "id"
            query: "id/number()"
            isKey: true
        }
    
        XmlRole {
            name: "author"
            query: "author/string()"
        }
    
        XmlRole {
            name: "comment"
            query: "comment/string()"
        }
    
        XmlRole {
            name: "pubDate"
            query: "date/string()"
        }
    
        XmlRole {
            name: "parentId"
            query: "parent/number()"
        }
    
        property variant keys: []
        onSourceChanged: {
            console.debug(source)
        }
    
        function searchForChilds(itemId, startingFrom) {
            for(var i = startingFrom; i < xmlListModel.count; i++) {
                var item = xmlListModel.get(i);
                if(item.parentId === itemId) {
                    push(item);
                    item.parentId = -1;
                    searchForChilds(item.id, i + 1);
                }
            }
        }
    
        function reconstructCommentsTree() {
            if(xmlListModel.count === 0)
                return;
            var item = xmlListModel.get(0);
            push(item);
            searchForChilds(item.id, 1);
            for(var i = 1; i < xmlListModel.count; i++) {
                item = xmlListModel.get(i);
                if(item.parentId === 0) {
                    push(item);
                    item.parentId = -1;
                    searchForChilds(item.id, i + 1);
                }
            }
            for(i = 0; i < root.count; i++) {
                item = root.get(i);
                console.debug(item.id + " " + item.pubDate + " " + item.parentId);
            }
        }
    
    
    
        onItemsInserted: reconstructCommentsTree()
    }
    
    Component.onCompleted: {
        var keys = new Array()
        for(var i = 0; i &lt; xmlListModel.roles.length; i++) {
            if(xmlListModel.roles[i].isKey) {
                keys.push(xmlListModel.roles[i].name);
            }
        }
        xmlListModel.keys = keys;
    }
    

    }
    @



  • Yeahhh! Find the way this morning :P
    I slightly changed the order algorithm in this way:

    @
    function searchForChilds(itemId, startingFrom, currLevel) {
    for(var i = startingFrom; i < xmlListModel.count; i++) {
    var item = xmlListModel.get(i);
    if(item.parentId === itemId) {
    item.commentLevel = currLevel;
    push(item);
    searchForChilds(item.commentId, i + 1, currLevel + 1);
    }
    }
    }

    function reconstructCommentsTree() {
    if(xmlListModel.count === 0)
    return;
    var item;// = xmlListModel.get(0);

    for(var i = 0; i &lt; xmlListModel.count; i++) {
        item = xmlListModel.get(i);
        if(item.parentId === 0) {
            item.commentLevel = 0;
            push(item);
            searchForChilds(item.commentId, i + 1, 1);
        }
    }
    

    }
    @

    In this way, i add the property commentLevel to the items of the outer listmodel, so that I can use this value in the delegate of the listview. ;)

    hope this is not to much complex as an algorithm for a mobile device...


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.