QComboBox: add property placeholderText

QComboBox had no option to tell the user that he must select an item -
there was no placeholder text like e.g. in QLineEdit. This feature is
widely used in html forms so we should support it also.
Therefore add a new property 'placeholderText' to specify a text which
should be shown when the current selected index is invalid.

[ChangeLog][QtWidgets][QComboBox] QComboBox got a new property
'placeholderText'

Change-Id: If6dac45c9f43455474e267907b0b0d893301c611
Fixes: QTBUG-1556
Fixes: QTBUG-2776
Fixes: QTBUG-77141
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
This commit is contained in:
Christian Ehrlicher 2019-09-24 13:58:08 +02:00
parent 5abb976f23
commit 360df2cf74
4 changed files with 80 additions and 6 deletions

View File

@ -368,6 +368,8 @@ QSize QComboBoxPrivate::recomputeSizeHint(QSize &sh) const
} }
if (minimumContentsLength > 0) if (minimumContentsLength > 0)
sh.setWidth(qMax(sh.width(), minimumContentsLength * fm.horizontalAdvance(QLatin1Char('X')) + (hasIcon ? iconSize.width() + 4 : 0))); sh.setWidth(qMax(sh.width(), minimumContentsLength * fm.horizontalAdvance(QLatin1Char('X')) + (hasIcon ? iconSize.width() + 4 : 0)));
if (!placeholderText.isEmpty())
sh.setWidth(qMax(sh.width(), fm.boundingRect(placeholderText).width()));
// height // height
@ -1110,8 +1112,9 @@ void QComboBoxPrivate::_q_rowsInserted(const QModelIndex &parent, int start, int
q->updateGeometry(); q->updateGeometry();
} }
// set current index if combo was previously empty // set current index if combo was previously empty and there is no placeholderText
if (start == 0 && (end - start + 1) == q->count() && !currentIndex.isValid()) { if (start == 0 && (end - start + 1) == q->count() && !currentIndex.isValid() &&
placeholderText.isEmpty()) {
q->setCurrentIndex(0); q->setCurrentIndex(0);
// need to emit changed if model updated index "silently" // need to emit changed if model updated index "silently"
} else if (currentIndex.row() != indexBeforeChange) { } else if (currentIndex.row() != indexBeforeChange) {
@ -1214,10 +1217,9 @@ void QComboBox::initStyleOption(QStyleOptionComboBox *option) const
} else { } else {
option->activeSubControls = d->hoverControl; option->activeSubControls = d->hoverControl;
} }
if (d->currentIndex.isValid()) { option->currentText = currentText();
option->currentText = currentText(); if (d->currentIndex.isValid())
option->currentIcon = d->itemIcon(d->currentIndex); option->currentIcon = d->itemIcon(d->currentIndex);
}
option->iconSize = iconSize(); option->iconSize = iconSize();
if (d->container && d->container->isVisible()) if (d->container && d->container->isVisible())
option->state |= QStyle::State_On; option->state |= QStyle::State_On;
@ -1771,6 +1773,45 @@ void QComboBox::setIconSize(const QSize &size)
updateGeometry(); updateGeometry();
} }
/*!
\property QComboBox::placeholderText
\brief Sets a \a placeholderText text shown when no valid index is set
The \a placeholderText will be shown when an invalid index is set. The
text is not accessible in the dropdown list. When this function is called
before items are added the placeholder text will be shown, otherwise you
have to call setCurrentIndex(-1) programmatically if you want to show the
placeholder text.
Set an empty placeholder text to reset the setting.
When the QComboBox is editable, use QLineEdit::setPlaceholderText()
instead.
\since 5.15
*/
void QComboBox::setPlaceholderText(const QString &placeholderText)
{
Q_D(QComboBox);
if (placeholderText == d->placeholderText)
return;
d->placeholderText = placeholderText;
if (currentIndex() == -1) {
if (d->placeholderText.isEmpty() && currentIndex() == -1)
setCurrentIndex(0);
else
update();
} else {
updateGeometry();
}
}
QString QComboBox::placeholderText() const
{
Q_D(const QComboBox);
return d->placeholderText;
}
/*! /*!
\property QComboBox::editable \property QComboBox::editable
\brief whether the combo box can be edited by the user \brief whether the combo box can be edited by the user
@ -2249,7 +2290,7 @@ QString QComboBox::currentText() const
else if (d->currentIndex.isValid()) else if (d->currentIndex.isValid())
return d->itemText(d->currentIndex); return d->itemText(d->currentIndex);
else else
return QString(); return d->placeholderText;
} }
/*! /*!
@ -3079,6 +3120,9 @@ void QComboBox::paintEvent(QPaintEvent *)
initStyleOption(&opt); initStyleOption(&opt);
painter.drawComplexControl(QStyle::CC_ComboBox, opt); painter.drawComplexControl(QStyle::CC_ComboBox, opt);
if (currentIndex() < 0)
opt.palette.setBrush(QPalette::ButtonText, opt.palette.brush(QPalette::ButtonText).color().lighter());
// draw the icon and text // draw the icon and text
painter.drawControl(QStyle::CE_ComboBoxLabel, opt); painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
} }

View File

@ -71,6 +71,7 @@ class Q_WIDGETS_EXPORT QComboBox : public QWidget
Q_PROPERTY(SizeAdjustPolicy sizeAdjustPolicy READ sizeAdjustPolicy WRITE setSizeAdjustPolicy) Q_PROPERTY(SizeAdjustPolicy sizeAdjustPolicy READ sizeAdjustPolicy WRITE setSizeAdjustPolicy)
Q_PROPERTY(int minimumContentsLength READ minimumContentsLength WRITE setMinimumContentsLength) Q_PROPERTY(int minimumContentsLength READ minimumContentsLength WRITE setMinimumContentsLength)
Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize) Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize)
Q_PROPERTY(QString placeholderText READ placeholderText WRITE setPlaceholderText)
#if QT_CONFIG(completer) #if QT_CONFIG(completer)
#if QT_DEPRECATED_SINCE(5, 13) #if QT_DEPRECATED_SINCE(5, 13)
@ -148,6 +149,9 @@ public:
QSize iconSize() const; QSize iconSize() const;
void setIconSize(const QSize &size); void setIconSize(const QSize &size);
void setPlaceholderText(const QString &placeholderText);
QString placeholderText() const;
bool isEditable() const; bool isEditable() const;
void setEditable(bool editable); void setEditable(bool editable);
void setLineEdit(QLineEdit *edit); void setLineEdit(QLineEdit *edit);

View File

@ -417,6 +417,7 @@ public:
int maxVisibleItems; int maxVisibleItems;
int maxCount; int maxCount;
int modelColumn; int modelColumn;
QString placeholderText;
bool inserting; bool inserting;
mutable QSize minimumSizeHint; mutable QSize minimumSizeHint;
mutable QSize sizeHint; mutable QSize sizeHint;

View File

@ -397,6 +397,31 @@ void tst_QComboBox::getSetCheck()
QCOMPARE(4, obj1.currentIndex()); // Valid QCOMPARE(4, obj1.currentIndex()); // Valid
obj1.setCurrentIndex(INT_MAX); obj1.setCurrentIndex(INT_MAX);
QCOMPARE(-1, obj1.currentIndex()); // Invalid => -1 QCOMPARE(-1, obj1.currentIndex()); // Invalid => -1
obj1.setIconSize(QSize(64, 32));
QCOMPARE(obj1.iconSize(), QSize(64, 32));
obj1.setIconSize(QSize());
const int iconWidth = obj1.style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, &obj1);
QCOMPARE(obj1.iconSize(), QSize(iconWidth, iconWidth));
const QString placeholderText("Please select");
obj1.setCurrentIndex(1);
obj1.setPlaceholderText(placeholderText);
QCOMPARE(obj1.placeholderText(), placeholderText);
QCOMPARE(obj1.currentText(), "2");
QCOMPARE(obj1.currentIndex(), 1);
obj1.setPlaceholderText(QString()); // should not change anything
QCOMPARE(obj1.placeholderText(), QString());
QCOMPARE(obj1.currentText(), "2");
obj1.clear();
obj1.setPlaceholderText(placeholderText);
obj1.addItems({"1", "2", "3", "4", "5"});
QCOMPARE(obj1.currentText(), placeholderText);
QCOMPARE(obj1.currentIndex(), -1);
obj1.setPlaceholderText(QString()); // should not change anything
QCOMPARE(obj1.currentText(), "1");
QCOMPARE(obj1.currentIndex(), 0);
} }
typedef QList<QVariant> VariantList; typedef QList<QVariant> VariantList;