Search code examples
qtqmlqt-quickqcomboboxqtquickcontrols

ComboBox implicit width not working when containing images


I am trying to create a ComboBox that has both text and images in the dropdown options. However, the implicitContentWidthPolicy doesn't seem to be working properly and the width appears to be sized only to the first list element.

import QtQuick 2.15
import QtQuick.Controls 2.15

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    ComboBox{
        id: testComboBox
        //implicitContentWidthPolicy: ComboBox.ContentItemImplicitWidth
        //implicitContentWidthPolicy: ComboBox.WidestTextWhenCompleted + 24
        implicitContentWidthPolicy: ComboBox.WidestTextWhenCompleted

        model: ListModel {
            id: model
            ListElement {language: "English" ; flag: "english_flag.png"}
            ListElement {language: "German" ; flag: "german_flag.png"}
            ListElement {language: "Italian" ; flag: "italian_flag.png"}
        }
        delegate: ItemDelegate {
            Row {
                Image {
                    source: model.flag
                    width: 24
                    height: 16
                }
                Text {
                    text: model.language
                    font.pixelSize: 16
                    anchors.verticalCenter: parent.verticalCenter
                }
            }
        }
        Row {
            Image {
                source: testComboBox.model.get(testComboBox.currentIndex).flag
                width: 24
                height: 16
            }
            Text {
                text: testComboBox.model.get(testComboBox.currentIndex).language
                font.pixelSize: 16
                verticalAlignment: Text.AlignVCenter
            }
        }
    }
}

english_flag german_flag italian_flag

I have tried using ComboBox.ContentItemImplicitWidth and ComboBox.WidestTextWhenCompleted and neither works properly. In both cases the size is only correct for the first list element.


Solution

  • In the following, the ComboBox's progressively increases its width to cover the combined width of each delegates. The moment the ComboBox is opened it will trigger a resize of the ComboBox's width:

    ComboBox {
        id: control
        delegate: ItemDelegate {
            Component.onCompleted: control.width = Math.max(control.width, width + 40)
        }
    }
    

    Other things I changed:

    • Set valueRole and textRole so that I can use currentValue and currentText later
    • Use ItemDelegate.contentItem to rerender the control
    • Use RowLayout and Layout properties to size the flags
        ComboBox {
            id: control
            model: ListModel {
                id: listModel
                ListElement {language: "English" ; flag: "https://upload.wikimedia.org/wikipedia/commons/thumb/8/83/Flag_of_the_United_Kingdom_%283-5%29.svg/320px-Flag_of_the_United_Kingdom_%283-5%29.svg.png"}
                ListElement {language: "German (Deutsch)" ; flag: "https://upload.wikimedia.org/wikipedia/en/b/ba/Flag_of_Germany.svg"}
                ListElement {language: "Italian" ; flag: "https://upload.wikimedia.org/wikipedia/en/0/03/Flag_of_Italy.svg"}
            }
            valueRole: "language"
            textRole: "flag"
            delegate: ItemDelegate {
                Component.onCompleted: control.width = Math.max(control.width, width + 40)
                contentItem: RowLayout {
                    Image {
                        Layout.preferredWidth: 24
                        Layout.preferredHeight: 16
                        source: flag
                    }
                    Text {
                        Layout.alignment: Qt.AlignLeft
                        Layout.fillWidth: true
                        text: language
                        font: control.font
                    }
                }
                highlighted: control.highlightedIndex === index
            }
            contentItem: ItemDelegate {
                contentItem: RowLayout {
                    Image {
                        Layout.preferredWidth: 24
                        Layout.preferredHeight: 16
                        source: control.currentText
                    }
                    Text {
                        Layout.alignment: Qt.AlignLeft
                        Layout.fillWidth: true
                        text: control.currentValue
                        font: control.font
                        elide: Text.ElideRight
                    }
                }
                onClicked: control.popup.open()
            }
        }
    

    You can Try it Online!