Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Calling ListView.positionViewAtIndex from two different places with the exact same parameters provides different behaviour.
Forum Updated to NodeBB v4.3 + New Features

Calling ListView.positionViewAtIndex from two different places with the exact same parameters provides different behaviour.

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
1 Posts 1 Posters 236 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • aarelovichA Offline
    aarelovichA Offline
    aarelovich
    wrote on last edited by
    #1

    So I need to create a fully customizable combo box. I call it the VMComboBox.

    Here is the complete code for the component:

    import QtQuick 2.6
    //import QtQuick.Controls 2.3
    //import QtQuick.Controls.Styles 1.4
    //import QtQuick.Controls 1.4
    
    Item {
    
        id: vmComboBox
        height: mainWindow.height*0.043
    
        property string vmErrorMsg: ""
        property bool vmEnabled: true
        property int vmMaxDisplayItems: 5
        property int vmCurrentIndex: -1
        property string vmCurrentText: ""
        property int vmListSize: 0
        readonly property int vmMOUSE_OUT_TIME: 200
    
    
        ///////////////////// FUNCTIONS
        function setModelList(list){
            itemList.clear()
            //console.log("====================================");
            for (var i = 0; i < list.length; i++){
                //console.log(JSON.stringify(list[i]));
                if (typeof list[i] === 'object'){
                    //console.log("Is array");
                    itemList.append({"vmText": list[i]["value"], "vmIndex": i, "vmMetadata" : list[i]["metadata"], "hasMouse" : false});
                }
                else{
                    //console.log("Is not array");
                    itemList.append({"vmText": list[i], "vmIndex": i, "hasMouse" : false});
                }
            }
            vmListSize = list.length
            setSelection(0)
        }
    
        function setSelection(selectedIndex){
            vmCurrentIndex = selectedIndex;
            displayText.text = itemList.get(selectedIndex).vmText;
            vmCurrentText = displayText.text;
            mouseOutTimer.running = false;
            listContainer.visible = false;
        }
    
        function shouldStartDropDownTimer(hasMouse){
            if (!hasMouse){
                if (!mouseOutTimer.running){
                    mouseOutTimer.running = true;
                }
            }
            else mouseOutTimer.running = false;
        }
    
        function dropdownHasMouse(){
    
            //console.log("Dropdown Has Mouse");
    
            for (var i = 0; i < itemList.count; i++){
                var item = itemList.get(i);
                if (item.hasMouse) {
                    //console.log("Item " + i + " has the mouse");
                    return; // At least one item has the mouse.
                }
            }
    
            // No item has the mouse, we need to check if the scroll bar has it.
            if (scrollControlMA.containsMouse) {
                //console.log("Scroll bar has the mouse");
                return ;
            }
    
            listContainer.visible = false;
    
        }
    
        signal selectionChanged();
    
    
        ///////////////////// DROPDOWN CLOSE CHECK TIMER
        Timer {
            id: mouseOutTimer
            interval: vmMOUSE_OUT_TIME;
            running: false;
            repeat: false
            onTriggered: {
                dropdownHasMouse()
            }
        }
    
        ///////////////////// THE LIST MODEL
        ListModel {
            id: itemList
        }
    
        ///////////////////// MAIN DISPLAY
        Rectangle {
            id: display
            anchors.fill: parent
            color: vmEnabled? "#ebf3fa" : "#bcbec0"
    
            // The display Text.
            Text {
                id: displayText
                text: "Test Text"
    //            font.family: viewHome.robotoR.name
    //            font.pixelSize: 13*viewHome.vmScale
                color: "#58595b"
                verticalAlignment: Text.AlignVCenter
                anchors.bottom: divisorLine.top
                anchors.verticalCenter: parent.verticalCenter
                anchors.left: parent.left
                anchors.leftMargin: mainWindow.width*0.004
            }
    
            // The divisor line at the bottom.
            Rectangle{
                id: divisorLine
                color: "#297fca"
                border.color: "#297fca"
                border.width: 0;
                height: 1;
                width: parent.width
                anchors.bottom: parent.bottom
            }
    
            // The triangle that functions as an indicator.
            Canvas {
                id: canvas
                width: mainWindow.width*0.009
                height: mainWindow.height*0.012
                anchors.verticalCenter: parent.verticalCenter
                anchors.right: parent.right
                anchors.rightMargin: mainWindow.width*0.004
                contextType: "2d"
    
                onPaint: {
                    var ctx = canvas.getContext("2d");
                    ctx.reset();
                    ctx.moveTo(0, 0);
                    ctx.lineTo(width, 0);
                    ctx.lineTo(width / 2, height);
                    ctx.closePath();
                    ctx.fillStyle = "#000000";
                    ctx.fill();
                }
            }
    
            MouseArea {
                id: mainDisplayMA
                anchors.fill: parent
                onClicked: {
                    if (vmEnabled){
                        listContainer.visible = true;
                    }
                }
            }
    
        }
    
        ///////////////////// DROP DOWN
        Rectangle {
            id: listContainer
            color: "#dadada"
            anchors.top: display.bottom
            anchors.horizontalCenter: parent.horizontalCenter
            width: parent.width
            height: {
                var numDisplay = vmMaxDisplayItems;
                if (numDisplay > vmListSize) {
                    scrollBar.visible = false;
                    numDisplay = vmListSize;
                }
                else{
                    scrollBar.visible = true;
                }
                return numDisplay*vmComboBox.height;
            }
            visible: false
            clip: true
    
            ListView {
                id: listContainerListView
                anchors.fill: parent
                model: itemList
    
                onContentYChanged: {
                    if (scrollBar.isScrolling) return;
                    var y = (contentY - originY) * (height / contentHeight);
                    scrollControl.y = y;
                }
    
                delegate: Rectangle {
                    width: vmComboBox.width
                    height: vmComboBox.height
                    border.width: mainWindow.width*0.002
                    border.color: "#dadada"
                    color: hasMouse? "#eeeeee" : "#ffffff"
    
                    MouseArea {
                        id: mouseItemDetector
                        anchors.fill: parent
                        hoverEnabled: true
                        propagateComposedEvents: true
                        onClicked: {
                            setSelection(vmIndex);
                            selectionChanged();
                        }
                        onContainsMouseChanged: {
                            hasMouse = containsMouse;
                            shouldStartDropDownTimer(containsMouse);
                        }
                    }
    
                    Text {
                        id: textItem
                        text: vmText
    //                    font.family: viewHome.robotoR.name
    //                    font.pixelSize: 13*viewHome.vmScale
                        color: "#58595b"
                        verticalAlignment: Text.AlignVCenter
                        anchors.left: parent.left
                        anchors.verticalCenter: parent.verticalCenter
                        anchors.leftMargin: mainWindow.width*0.004
                    }
                }
    
    
            }
    
            Rectangle {
    
                id: scrollBar
                width: 0.05*listContainer.width
                height: listContainer.height
                anchors.top: listContainer.top
                anchors.right: listContainer.right
                border.color: "#dadada"
                radius: width*0.5;
    
                property bool isScrolling: false
    
                function scroll(y){
                    // Doing the scroll computations.
                    var totalH = vmListSize*vmComboBox.height;
                    var maxScrollY = scrollControlMA.height - scrollControl.height;
                    var yPosForList = totalH*y/maxScrollY
                    var itemThatShouldBeVisble = Math.ceil(yPosForList/vmComboBox.height)
                    console.log("Item that should be visible: " + itemThatShouldBeVisble)
                    listContainerListView.positionViewAtIndex(itemThatShouldBeVisble,ListView.End)
    
                }
    
                MouseArea {
                    id: scrollControlMA
                    anchors.fill: parent
                    hoverEnabled: true
                    onContainsMouseChanged: {
    
                        // Making sure that no item has the mouse.
                        // This should not be necessary but for some reason the delegates of the list view sometimes do not detect when the mouse leaves them.
                        for (var i = 0; i < itemList.count; i++){
                            itemList.setProperty(i,"hasMouse",false);
                        }
    
                        shouldStartDropDownTimer(containsMouse);
                    }
    
                    drag.target: scrollControl
                    drag.axis: Drag.YAxis
                    drag.minimumY: 0
                    drag.maximumY: scrollControlMA.height - scrollControl.height
    
                    drag.onActiveChanged: {
                        if (drag.active){
                            scrollBar.isScrolling = true;
                        }
                        else{
                            scrollBar.isScrolling = false;
                        }
                    }
    
                    onClicked: {
                        // If the click falls inside the scroll controller, then it is ignored.
                        var y = mouseY
                        var maxY = (scrollBar.height - scrollControl.height);
                        if ( (y > scrollControl.y) && (y < (scrollControl.y + scrollControl.height)) ) return;
    
                        if (scrollBar.isScrolling) return;
    
                        y = y - scrollControl.height/2;
                        if (y < 0) y = 0;
                        if (y > maxY) y = maxY;
    
                        scrollControl.y = y;
                        console.log("Scroll click is " + y);
                        scrollBar.scroll(y)
    
                    }
    
                }
    
                Rectangle {
                    id: scrollControl
                    width: parent.width
                    height: {
                        var maxH = parent.height;
                        var scale = vmMaxDisplayItems/vmListSize;
                        if (scale > 1) return parent.height;
                        if (scale < 0.08) scale = 0.08;
                        return parent.height*scale
                    }
    
                    radius: scrollBar.radius
    
                    color: "#dadada"
                    onYChanged: {
                        if (!scrollBar.isScrolling) return;
                        console.log("On moving Scroll Bar: " + y);
                        scrollBar.scroll(y)
                    }
                }
    
            }
    
        }
    
        //////////////////// THE ERROR MESSAGE
        Text{
            id: errorMsg
            text: vmErrorMsg
            color:  "#ca2026"
    //        font.family: viewHome.robotoR.name
    //        font.pixelSize: 12*viewHome.vmScale
            anchors.left: parent.left
            anchors.top: parent.bottom
            anchors.topMargin: mainWindow.height*0.007
            z: vmComboBox.z - 1
            visible: (vmErrorMsg !== "")
        }
    
    }
    

    And here is the test case:

    import QtQuick 2.12
    import QtQuick.Window 2.12
    import QtQuick.Controls 2.0
    
    Window {
    
        id: mainWindow
    
        visible: true
        //visibility: Window.Maximized
        width: 1000
        height: 800
        title: qsTr("Hello World")
    
    
        VMComboBox {
            id: testbox
            width: 839.0399999999997
            height: 89.31099999999999
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.top: parent.top
            anchors.topMargin: 20
            Component.onCompleted: {
    
                var list = []
                //for (var i = 0; i < 8; i++){
                for (var i = 0; i < 100; i++){
                    list.push("Item " + i);
                }
    
                testbox.setModelList(list);
    
            }
        }
    
    
    }
    

    My problem is in the scrollBar.scroll(y) function. If the function is called while scrolling (this means dragging the square in the scroll bar) I get the following print out, when scrolling to the bottom:

    qml: On moving Scroll Bar: 409
    qml: Item that should be visible: 100
    qml: On moving Scroll Bar: 410
    qml: Item that should be visible: 100
    qml: On moving Scroll Bar: 410.83059999999995
    qml: Item that should be visible: 100
    

    This is called on the onYChanged method of the scrollControl reactangle. This way the ComboBox behaves as expected and the Listview is updated by the positionViewAtIndex function correctly,.

    My problem comes when I click the empty bar. The function is called from the onClicked of the scrollControlMA (or mouse area). When this happens near the very bottom of the scroll bar, I get this print out

    qml: Scroll click is 410.83059999999995
    qml: Item that should be visible: 100
    

    But when I do this positionViewAtIndex does not update the list view, remaining where it is. And the what's worst, when I click anywhere else other than the bottom, the list view gets updated as expected.

    What drives me really insae is the fact that both times the positionViewAtIndex is called with the exact same parameter both times. And it is only called once because it's not anywhere else in the code and the message right above where it's called is only called only appears once when I click. So the behaviour makes absolutely no sense.

    Can anyone help me?

    1 Reply Last reply
    0

    • Login

    • Login or register to search.
    • First post
      Last post
    0
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Get Qt Extensions
    • Unsolved