diff options
Diffstat (limited to 'src/widgets/widgets/qcombobox.cpp')
-rw-r--r-- | src/widgets/widgets/qcombobox.cpp | 237 |
1 files changed, 169 insertions, 68 deletions
diff --git a/src/widgets/widgets/qcombobox.cpp b/src/widgets/widgets/qcombobox.cpp index fdf1a87bba..df8e7b4a3f 100644 --- a/src/widgets/widgets/qcombobox.cpp +++ b/src/widgets/widgets/qcombobox.cpp @@ -91,30 +91,11 @@ QT_BEGIN_NAMESPACE QComboBoxPrivate::QComboBoxPrivate() : QWidgetPrivate(), - model(0), - lineEdit(0), - container(0), - insertPolicy(QComboBox::InsertAtBottom), - sizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow), - minimumContentsLength(0), shownOnce(false), autoCompletion(true), duplicatesEnabled(false), frame(true), - maxVisibleItems(10), - maxCount(INT_MAX), - modelColumn(0), - inserting(false), - arrowState(QStyle::State_None), - hoverControl(QStyle::SC_None), - autoCompletionCaseSensitivity(Qt::CaseInsensitive), - indexBeforeChange(-1) -#ifdef Q_OS_MAC - , m_platformMenu(0) -#endif -#if QT_CONFIG(completer) - , completer(0) -#endif + inserting(false) { } @@ -148,18 +129,26 @@ QStyleOptionMenuItem QComboMenuDelegate::getStyleOption(const QStyleOptionViewIt if (option.state & QStyle::State_Selected) menuOption.state |= QStyle::State_Selected; menuOption.checkType = QStyleOptionMenuItem::NonExclusive; - menuOption.checked = mCombo->currentIndex() == index.row(); + // a valid checkstate means that the model has checkable items + const QVariant checkState = index.data(Qt::CheckStateRole); + if (!checkState.isValid()) { + menuOption.checked = mCombo->currentIndex() == index.row(); + } else { + menuOption.checked = qvariant_cast<int>(checkState) == Qt::Checked; + menuOption.state |= qvariant_cast<int>(checkState) == Qt::Checked + ? QStyle::State_On : QStyle::State_Off; + } if (QComboBoxDelegate::isSeparator(index)) menuOption.menuItemType = QStyleOptionMenuItem::Separator; else menuOption.menuItemType = QStyleOptionMenuItem::Normal; QVariant variant = index.model()->data(index, Qt::DecorationRole); - switch (variant.type()) { - case QVariant::Icon: + switch (variant.userType()) { + case QMetaType::QIcon: menuOption.icon = qvariant_cast<QIcon>(variant); break; - case QVariant::Color: { + case QMetaType::QColor: { static QPixmap pixmap(option.decorationSize); pixmap.fill(qvariant_cast<QColor>(variant)); menuOption.icon = pixmap; @@ -183,7 +172,7 @@ QStyleOptionMenuItem QComboMenuDelegate::getStyleOption(const QStyleOptionViewIt // that order, also override the font for the popup menu. QVariant fontRoleData = index.data(Qt::FontRole); if (fontRoleData.isValid()) { - menuOption.font = fontRoleData.value<QFont>(); + menuOption.font = qvariant_cast<QFont>(fontRoleData); } else if (mCombo->testAttribute(Qt::WA_SetFont) || mCombo->testAttribute(Qt::WA_MacSmallSize) || mCombo->testAttribute(Qt::WA_MacMiniSize) @@ -198,6 +187,55 @@ QStyleOptionMenuItem QComboMenuDelegate::getStyleOption(const QStyleOptionViewIt return menuOption; } +bool QComboMenuDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index) +{ + Q_ASSERT(event); + Q_ASSERT(model); + + // make sure that the item is checkable + Qt::ItemFlags flags = model->flags(index); + if (!(flags & Qt::ItemIsUserCheckable) || !(option.state & QStyle::State_Enabled) + || !(flags & Qt::ItemIsEnabled)) + return false; + + // make sure that we have a check state + const QVariant checkState = index.data(Qt::CheckStateRole); + if (!checkState.isValid()) + return false; + + // make sure that we have the right event type + if ((event->type() == QEvent::MouseButtonRelease) + || (event->type() == QEvent::MouseButtonDblClick) + || (event->type() == QEvent::MouseButtonPress)) { + QMouseEvent *me = static_cast<QMouseEvent*>(event); + if (me->button() != Qt::LeftButton) + return false; + + if ((event->type() == QEvent::MouseButtonPress) + || (event->type() == QEvent::MouseButtonDblClick)) { + pressedIndex = index.row(); + return false; + } + + if (index.row() != pressedIndex) + return false; + pressedIndex = -1; + + } else if (event->type() == QEvent::KeyPress) { + if (static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space + && static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select) + return false; + } else { + return false; + } + + // we don't support user-tristate items in QComboBox (not implemented in any style) + Qt::CheckState newState = (static_cast<Qt::CheckState>(checkState.toInt()) == Qt::Checked) + ? Qt::Unchecked : Qt::Checked; + return model->setData(index, newState, Qt::CheckStateRole); +} + #if QT_CONFIG(completer) void QComboBoxPrivate::_q_completerActivated(const QModelIndex &index) { @@ -255,8 +293,7 @@ void QComboBoxPrivate::_q_modelReset() lineEdit->setText(QString()); updateLineEditGeometry(); } - if (currentIndex.row() != indexBeforeChange) - _q_emitCurrentIndexChanged(currentIndex); + trySetValidIndex(); modelChanged(); q->update(); } @@ -266,6 +303,25 @@ void QComboBoxPrivate::_q_modelDestroyed() model = QAbstractItemModelPrivate::staticEmptyModel(); } +void QComboBoxPrivate::trySetValidIndex() +{ + Q_Q(QComboBox); + bool currentReset = false; + + const int rowCount = q->count(); + for (int pos = 0; pos < rowCount; ++pos) { + const QModelIndex idx(model->index(pos, modelColumn, root)); + if (idx.flags() & Qt::ItemIsEnabled) { + setCurrentIndex(idx); + currentReset = true; + break; + } + } + + if (!currentReset) + setCurrentIndex(QModelIndex()); +} + QRect QComboBoxPrivate::popupGeometry(int screen) const { return QStylePrivate::useFullScreenForPopup() @@ -368,6 +424,8 @@ QSize QComboBoxPrivate::recomputeSizeHint(QSize &sh) const } if (minimumContentsLength > 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 @@ -445,12 +503,8 @@ void QComboBoxPrivateContainer::paintEvent(QPaintEvent *e) QFrame::paintEvent(e); } -void QComboBoxPrivateContainer::leaveEvent(QEvent *) -{ -} - QComboBoxPrivateContainer::QComboBoxPrivateContainer(QAbstractItemView *itemView, QComboBox *parent) - : QFrame(parent, Qt::Popup), combo(parent), view(0), top(0), bottom(0), maybeIgnoreMouseButtonRelease(false) + : QFrame(parent, Qt::Popup), combo(parent) { // we need the combobox and itemview Q_ASSERT(parent); @@ -555,7 +609,7 @@ void QComboBoxPrivateContainer::updateScrollers() */ void QComboBoxPrivateContainer::viewDestroyed() { - view = 0; + view = nullptr; setItemView(new QComboBoxListView()); } @@ -589,7 +643,7 @@ void QComboBoxPrivateContainer::setItemView(QAbstractItemView *itemView) if (isAncestorOf(view)) delete view; - view = 0; + view = nullptr; } // setup the item view @@ -840,7 +894,7 @@ QStyleOptionComboBox QComboBoxPrivateContainer::comboStyleOption() const \value AdjustToContents The combobox will always adjust to the contents \value AdjustToContentsOnFirstShow The combobox will adjust to its contents the first time it is shown. - \value AdjustToMinimumContentsLength Use AdjustToContents or AdjustToContentsOnFirstShow instead. + \omitvalue AdjustToMinimumContentsLength \value AdjustToMinimumContentsLengthWithIcon The combobox will adjust to \l minimumContentsLength plus space for an icon. For performance reasons use this policy on large models. */ @@ -905,6 +959,8 @@ QStyleOptionComboBox QComboBoxPrivateContainer::comboStyleOption() const changes either through user interaction or programmatically. The item's \a index is passed or -1 if the combobox becomes empty or the currentIndex was reset. + + \obsolete Use currentIndexChanged(int index, const QString &text) instead */ /*! @@ -914,6 +970,18 @@ QStyleOptionComboBox QComboBoxPrivateContainer::comboStyleOption() const This signal is sent whenever the currentIndex in the combobox changes either through user interaction or programmatically. The item's \a text is passed. + + \obsolete Use currentIndexChanged(int index, const QString &text) instead +*/ + +/*! + \fn void QComboBox::currentIndexChanged(int index, const QString &text) + \since 5.15 + + This signal is sent whenever the currentIndex in the combobox + changes either through user interaction or programmatically. The + item's \a index is passed or -1 if the combobox becomes empty or + the currentIndex was reset. The item's \a text is also passed. */ /*! @@ -929,7 +997,7 @@ QStyleOptionComboBox QComboBoxPrivateContainer::comboStyleOption() const model QStandardItemModel. */ QComboBox::QComboBox(QWidget *parent) - : QWidget(*new QComboBoxPrivate(), parent, 0) + : QWidget(*new QComboBoxPrivate(), parent, { }) { Q_D(QComboBox); d->init(); @@ -939,7 +1007,7 @@ QComboBox::QComboBox(QWidget *parent) \internal */ QComboBox::QComboBox(QComboBoxPrivate &dd, QWidget *parent) - : QWidget(dd, parent, 0) + : QWidget(dd, parent, { }) { Q_D(QComboBox); d->init(); @@ -1110,8 +1178,9 @@ void QComboBoxPrivate::_q_rowsInserted(const QModelIndex &parent, int start, int q->updateGeometry(); } - // set current index if combo was previously empty - if (start == 0 && (end - start + 1) == q->count() && !currentIndex.isValid()) { + // set current index if combo was previously empty and there is no placeholderText + if (start == 0 && (end - start + 1) == q->count() && !currentIndex.isValid() && + placeholderText.isEmpty()) { q->setCurrentIndex(0); // need to emit changed if model updated index "silently" } else if (currentIndex.row() != indexBeforeChange) { @@ -1214,10 +1283,9 @@ void QComboBox::initStyleOption(QStyleOptionComboBox *option) const } else { 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->iconSize = iconSize(); if (d->container && d->container->isVisible()) option->state |= QStyle::State_On; @@ -1398,13 +1466,14 @@ void QComboBoxPrivate::_q_emitCurrentIndexChanged(const QModelIndex &index) { Q_Q(QComboBox); const QString text = itemText(index); - emit q->currentIndexChanged(index.row()); -#if QT_DEPRECATED_SINCE(5, 13) +#if QT_DEPRECATED_SINCE(5, 15) QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED + emit q->currentIndexChanged(index.row()); emit q->currentIndexChanged(text); QT_WARNING_POP #endif + emit q->currentIndexChanged(index.row(), text); // signal lineEdit.textChanged already connected to signal currentTextChanged, so don't emit double here if (!lineEdit) emit q->currentTextChanged(text); @@ -1566,7 +1635,7 @@ void QComboBox::setAutoCompletion(bool enable) d->lineEdit->setCompleter(d->completer); d->completer->setWidget(this); } else { - d->lineEdit->setCompleter(0); + d->lineEdit->setCompleter(nullptr); } } @@ -1755,7 +1824,7 @@ QSize QComboBox::iconSize() const if (d->iconSize.isValid()) return d->iconSize; - int iconWidth = style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this); + int iconWidth = style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, this); return QSize(iconWidth, iconWidth); } @@ -1772,6 +1841,45 @@ void QComboBox::setIconSize(const QSize &size) } /*! + \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 \brief whether the combo box can be edited by the user @@ -1786,7 +1894,7 @@ void QComboBox::setIconSize(const QSize &size) bool QComboBox::isEditable() const { Q_D(const QComboBox); - return d->lineEdit != 0; + return d->lineEdit != nullptr; } /*! \internal @@ -1813,7 +1921,7 @@ void QComboBoxPrivate::updateDelegate(bool force) QIcon QComboBoxPrivate::itemIcon(const QModelIndex &index) const { QVariant decoration = model->data(index, Qt::DecorationRole); - if (decoration.type() == QVariant::Pixmap) + if (decoration.userType() == QMetaType::QPixmap) return QIcon(qvariant_cast<QPixmap>(decoration)); else return qvariant_cast<QIcon>(decoration); @@ -1842,7 +1950,7 @@ void QComboBox::setEditable(bool editable) setAttribute(Qt::WA_InputMethodEnabled, false); d->lineEdit->hide(); d->lineEdit->deleteLater(); - d->lineEdit = 0; + d->lineEdit = nullptr; } d->updateDelegate(); @@ -1891,6 +1999,8 @@ void QComboBox::setLineEdit(QLineEdit *edit) d->lineEdit->setFocusProxy(this); d->lineEdit->setAttribute(Qt::WA_MacShowFocusRect, false); #if QT_DEPRECATED_SINCE(5, 13) +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED #if QT_CONFIG(completer) setAutoCompletion(d->autoCompletion); @@ -1907,6 +2017,7 @@ void QComboBox::setLineEdit(QLineEdit *edit) } #endif #endif +QT_WARNING_POP #endif setAttribute(Qt::WA_InputMethodEnabled); @@ -1955,7 +2066,7 @@ void QComboBox::setValidator(const QValidator *v) const QValidator *QComboBox::validator() const { Q_D(const QComboBox); - return d->lineEdit ? d->lineEdit->validator() : 0; + return d->lineEdit ? d->lineEdit->validator() : nullptr; } #endif // QT_NO_VALIDATOR @@ -1966,7 +2077,7 @@ const QValidator *QComboBox::validator() const \since 4.2 Sets the \a completer to use instead of the current completer. - If \a completer is 0, auto completion is disabled. + If \a completer is \nullptr, auto completion is disabled. By default, for an editable combo box, a QCompleter that performs case insensitive inline completion is automatically created. @@ -1999,7 +2110,7 @@ void QComboBox::setCompleter(QCompleter *c) QCompleter *QComboBox::completer() const { Q_D(const QComboBox); - return d->lineEdit ? d->lineEdit->completer() : 0; + return d->lineEdit ? d->lineEdit->completer() : nullptr; } #endif // QT_CONFIG(completer) @@ -2122,20 +2233,7 @@ void QComboBox::setModel(QAbstractItemModel *model) setRootModelIndex(QModelIndex()); - bool currentReset = false; - - const int rowCount = count(); - for (int pos=0; pos < rowCount; pos++) { - if (d->model->index(pos, d->modelColumn, d->root).flags() & Qt::ItemIsEnabled) { - setCurrentIndex(pos); - currentReset = true; - break; - } - } - - if (!currentReset) - setCurrentIndex(-1); - + d->trySetValidIndex(); d->modelChanged(); } @@ -2249,7 +2347,7 @@ QString QComboBox::currentText() const else if (d->currentIndex.isValid()) return d->itemText(d->currentIndex); else - return QString(); + return d->placeholderText; } /*! @@ -2886,7 +2984,7 @@ void QComboBox::hidePopup() QSignalBlocker containerBlocker(d->container); // Flash selected/triggered item (if any). if (style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem)) { - QItemSelectionModel *selectionModel = view() ? view()->selectionModel() : 0; + QItemSelectionModel *selectionModel = view() ? view()->selectionModel() : nullptr; if (selectionModel && selectionModel->hasSelection()) { QEventLoop eventLoop; const QItemSelection selection = selectionModel->selection(); @@ -3081,6 +3179,9 @@ void QComboBox::paintEvent(QPaintEvent *) initStyleOption(&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 painter.drawControl(QStyle::CE_ComboBoxLabel, opt); } |