Search code examples
qtcomboboxqmlqtquick2

How to limit the size of drop-down of a ComboBox in QML


I am using a ComboBox in QML and when populated with a lot of data it exceeds my main windows bottom boarder. From googling I have learned that the drop-down list of a ComboBox is put on top of the current application window and therefore it does not respect its boundaries.

Ideally I would want the ComboBox to never exceed the main applications boundary, but I can not find any property in the documentation.

A different approach would be to limit the number of visible items of the drop-down list so that it do not exceed the window limits for a given window geometry. I was not able to find this in the documentation either and I have run out of ideas.


Solution

  • Take a look to the ComboBox source code, the popup is of a Menu type and it doesn't have any property to limit its size. Moreover, the z property of the Menu is infinite, i.e. it's always on top.

    If you Find no way but to use the ComboBox of Qt you can create two models one for visual purpose, I will call it visual model, you will show it in your ComboBox and the complete one , it will be the reference model. Items count in your VisualModel wil be equal to some int property maximumComboBoxItemsCount that you declare . you'll need o find a way that onHovered find the index under the mouse in the visualmodel if it's === to maximumComboBoxIemsCount you do visualModel.remove(0) et visualModel.add(referenceModel.get(maximum.. + 1) and you'll need another property minimumComboBoxIemsCount, same logic but for Scroll Up , I dont know if it will work. but it's an idea

    I think there is no solution using the built-in component and you should create your own comboBox. You can start from the following code.

    ComboBox.qml

    import QtQuick 2.0 
    
    Item {
        id: comboBox
    
        property string initialText
        property int maxHeight
        property int selectedItem:0
        property variant listModel
        signal expanded
        signal closed
        //    signal sgnSelectedChoice(var choice)
        width: 100
        height: 40
        ComboBoxButton {
            id: comboBoxButton
            width: comboBox.width
            height: 40
            borderColor: "#fff"
            radius: 10
            margin: 5
            borderWidth: 2
            text: initialText
            textSize: 12
    
            onClicked: {
                if (listView.height == 0)
                {
                    listView.height = Math.min(maxHeight, listModel.count*comboBoxButton.height)
                    comboBox.expanded()
                    source = "qrc:/Images/iconUp.png"
                }
                else
                {
                    listView.height = 0
                    comboBox.closed()
                    source = "qrc:/Images/iconDown.png"
                }
            }
        }
    
        Component {
            id: comboBoxDelegate
    
            Rectangle {
                id: delegateRectangle
                width: comboBoxButton.width
                height: comboBoxButton.height
                color: "#00000000"
                radius: comboBoxButton.radius
                border.width: comboBoxButton.borderWidth
                border.color: comboBoxButton.borderColor
    
    
    
                Text {
                    color: index == listView.currentIndex ? "#ffff00" : "#ffffff"
                    anchors.centerIn: parent
                    anchors.margins: 3
                    font.pixelSize: 12
                    text: value
                    font.bold: true
                }
    
    
                MouseArea {
                    anchors.fill: parent
    
                    onClicked: {
                        listView.height = 0
                        listView.currentIndex = index
                        comboBox.selectedItem = index
                        tools.writePersistence(index,5)
                        comboBoxButton.text = value
                        comboBox.closed()
                    }
                }
            }
        }
    
        ListView {
            id: listView
            anchors.top: comboBoxButton.bottom
            anchors.left: comboBoxButton.left
            width: parent.width
            height: 0
            clip: true
            model: listModel
            delegate: comboBoxDelegate
            currentIndex: selectedItem
        }
        onClosed:  comboBoxButton.source = "qrc:/Images/iconDown.png"
        Component.onCompleted: {
            var cacheChoice = tools.getPersistence(5);
            listView.currentIndex = tools.toInt(cacheChoice)
            selectedItem = listView.currentIndex
            comboBoxButton.text = cacheModel.get(selectedItem).value
        }
    }
    

    ComboBoxButton.qml

        import QtQuick 2.0 
    
    Item {
        id: container
        signal clicked
        property string text
        property alias source : iconDownUp.source
        property string color: "#ffffff"
        property int textSize: 12
        property string borderColor: "#00000000"
        property int borderWidth: 0
        property int radius: 0
        property int margin: 0
    
        Rectangle {
            id: buttonRectangle
            anchors.fill: parent
            color: "#00000000"
            radius: container.radius
            border.width: container.borderWidth
            border.color: container.borderColor
    
            Image {
                id: image
                anchors.fill:  parent
                source: "qrc:/Images/buttonBackground.png"
    
                Image {
                    id: iconDownUp
                    source: "qrc:/Images/iconDown.png"
                    sourceSize.height:20
                    sourceSize.width: 20
                    anchors.verticalCenter: parent.verticalCenter
                }
            }
    
            Text {
                id:label
                color: container.color
                anchors.centerIn: parent
                font.pixelSize: 10
                text: container.text
                font.bold: true
            }
    
            MouseArea {
                id: mouseArea;
                anchors.fill: parent
                onClicked: {
                    container.clicked()
                    buttonRectangle.state = "pressed"
                    startTimer.start()
                }
            }
            Timer{
                id:startTimer
                interval: 200
                running: false;
                repeat: false
                onTriggered: buttonRectangle.state = ""
            }
            states: State {
                name: "pressed"
                when: mouseArea.pressed
                PropertyChanges { target: image; scale: 0.7 }
                PropertyChanges { target: label; scale: 0.7 }
            }
    
            transitions: Transition {
                NumberAnimation { properties: "scale"; duration: 200; easing.type: Easing.InOutQuad }
            }
        }
    }
    

    I've used it in some software of mine, hence it is possible that It could not work "out of the box". I use it like this:

    ComboBox{
        id:cacheChoice
        initialText: "None"
        anchors.top: baseContainer.top
        anchors.topMargin: 2
        anchors.right: baseContainer.right
        maxHeight: 500
        listModel: cacheModel
    
        onExpanded: {
            cacheChoice.height = 500
        }
    
        onClosed: {
            cacheChoice.height = 20
        }
    }