How to set ComboBox width to fit the largest item
There is no built-in mechanism in the Quick-Controls-2 assembly (at the time of writing, Qt 5.9), so you need to do it yourself. Something like that...
main.qml
MyComboBox {
id: comboBox1
sizeToContents: false
model: [ "Banana", "Apple", "ThisIsTheLongestWordThatIHave", "Coconut" ]
}
MyComboBox {
id: comboBox2
anchors.top: comboBox1.bottom
sizeToContents: true
model: [ "Banana", "Apple", "ThisIsTheLongestWordThatIHave", "Coconut" ]
}
MyComboBox.qml
ComboBox {
id: control
property bool sizeToContents
property int modelWidth
width: (sizeToContents) ? modelWidth + 2*leftPadding + 2*rightPadding : implicitWidth
delegate: ItemDelegate {
width: control.width
text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) : modelData
font.weight: control.currentIndex === index ? Font.DemiBold : Font.Normal
font.family: control.font.family
font.pointSize: control.font.pointSize
highlighted: control.highlightedIndex === index
hoverEnabled: control.hoverEnabled
}
TextMetrics {
id: textMetrics
}
onModelChanged: {
textMetrics.font = control.font
for(var i = 0; i < model.length; i++){
textMetrics.text = model[i]
modelWidth = Math.max(textMetrics.width, modelWidth)
}
}
}
Please note that if you change the model type from the QML list to another type, for example C ++ QStringList
, QList<QObject*>
or QAbstractListModel
, then you need to change this line textMetrics.text = model[i]
to get the text from the model elements in a slightly different way.
source to share
@Mark Ch - MyComboBox doesn't work with Controls 2; the width of the indicator is not taken into account, so it is too narrow if the indicator has any width.
This worked for me, replacing the assignment for the width with the following:
width: sizeToContents
? (modelWidth + leftPadding + contentItem.leftPadding
+ rightPadding + contentItem.rightPadding)
: implicitWidth
source to share
Here's a different approach, which is less dependent on internals, works with any model and with alternative ComboBox styles such as "material":
The idea is to simply set currentItem
for every possible value and let the ComboBox internals do their thing; then respect the resulting width. ComboBox.contentItem
is TextField
, but TextField.contentWidth
has what we want. We don't need to know how to iterate over the model or emulate what it can do delegate
to change formatting. The desired ComboBox width is the maximum of these contentWidths, plus padding and indicator width.
The computation cannot be directly linked to width
because the binding cycle will take place. Instead, the width is computed and set statically when the onCompleted signal occurs.
Note. The following code does not yet handle dynamically updated models. I can update this post later ...
USING:
import QtQuick 2.9
import QtQuick.Controls 2.2
import "ComboBoxHacks.js" as CBH
...
ComboBox {
id: myCB
Component.onCompleted: width = CBH.calcComboBoxImplicitWidth(myCB)
...
}
And here is the JavaScript code:
/* ComboBoxHacks.js */
function calcComboBoxImplicitWidth(cb) {
var widest = 0
if (cb.count===0) return cb.width
var originalCI = cb.currentIndex
if (originalCI < 0) return cb.width // currentIndex → deleted item
do {
widest = Math.max(widest, cb.contentItem.contentWidth)
cb.currentIndex = (cb.currentIndex + 1) % cb.count
} while(cb.currentIndex !== originalCI)
return widest + cb.contentItem.leftPadding + cb.contentItem.rightPadding
+ cb.indicator.width
}
source to share
You just need to update the minimum width when the model changes.
import QtQml 2.12
import QtQuick 2.12
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.12
ComboBox {
id: root
onModelChanged: {
var _maxWidth = 0
for(var i = 0; i < model.length; i++){
// TextMetrics does not work with Material Style
_maxWidth = Math.max((model[i].length+1)*Qt.application.font.pixelSize, _maxWidth)
}
Layout.minimumWidth = _maxWidth + implicitIndicatorWidth + leftPadding + rightPadding
}
}
source to share