uniquenamehere
uniquenamehere

Reputation: 1949

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.

Upvotes: 4

Views: 6605

Answers (3)

Tobias Sch.
Tobias Sch.

Reputation: 97

It is possible to access the hidden MenuStyle within the ComboBoxStyle component. There you can use all the things and hidden things you have within a MenuStyle, including its maximum height.

The thing looks roughly like this. Not pretty but it works well enough.

import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.3
import QtQuick.Window 2.2

ComboBox {
  id: comboBox

  style: ComboBoxStyle {

    // drop-down customization here
    property Component __dropDownStyle: MenuStyle {
      __maxPopupHeight: 400
      __menuItemType: "comboboxitem" //not 100% sure if this is needed
  }
}

As it came up resonantly in our team, here is a updated version of the idea shown above. The new version restricts the size automatically to the size of your application.

ComboBox {
  id: root

  style: ComboBoxStyle {
    id: comboBoxStyle

    // drop-down customization here
    property Component __dropDownStyle: MenuStyle {
      __maxPopupHeight: Math.max(55, //min value to keep it to a functional size even if it would not look nice
                                 Math.min(400,
                                          //limit the max size so the menu is inside the application bounds
                                            comboBoxStyle.control.Window.height
                                          - mapFromItem(comboBoxStyle.control, 0,0).y
                                          - comboBoxStyle.control.height))
      __menuItemType: "comboboxitem" //not 100% sure if this is needed
    } //Component __dropDownStyle: MenuStyle
  } //style: ComboBoxStyle
} //ComboBox

Upvotes: 1

David K. Hess
David K. Hess

Reputation: 17246

In case you are working with ComboBox from Qt Quick Controls 2, here's the source code for it:

https://github.com/qt/qtquickcontrols2/blob/5.12/src/imports/controls/ComboBox.qml

Based on that, this override of the behavior works to limit the height to something reasonable:

    myComboBox.popup.contentItem.implicitHeight = Qt.binding(function () {
        return Math.min(250, myComboBox.popup.contentItem.contentHeight);
    });

Upvotes: 2

Mido
Mido

Reputation: 1112

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
    }
}

Upvotes: 2

Related Questions