7#include <qstylepainter.h>
8#include <qpa/qplatformtheme.h>
9#include <qpa/qplatformmenu.h>
10#include <qpa/qplatformwindow.h>
11#include <qpa/qplatformwindow_p.h>
14#include <qapplication.h>
16#if QT_CONFIG(tableview)
17#include <qtableview.h>
19#include <qabstractitemdelegate.h>
26#include <qscrollbar.h>
27#if QT_CONFIG(treeview)
30#include <qheaderview.h>
32#include <qmetaobject.h>
33#if QT_CONFIG(proxymodel)
34#include <qabstractproxymodel.h>
36#include <qstylehints.h>
37#include <private/qguiapplication_p.h>
38#include <private/qhighdpiscaling_p.h>
39#include <private/qapplication_p.h>
40#include <private/qabstractitemmodel_p.h>
41#include <private/qabstractscrollarea_p.h>
42#include <private/qlineedit_p.h>
43#if QT_CONFIG(completer)
44#include <private/qcompleter_p.h>
48# include <private/qeffects_p.h>
50#include <private/qstyle_p.h>
52#if QT_CONFIG(accessibility)
53#include "qaccessible.h"
55#include <QtWidgets/qstyleoption.h>
57#include <QtGui/qstandarditemmodel.h>
58#include <QtGui/qpainter.h>
60#include <QtCore/qpointer.h>
67using namespace Qt::StringLiterals;
68using namespace std::chrono_literals;
77 setScreen(cmb->screen());
85 resizeContents(viewport()->width(), contentsSize().height());
86 QListView::resizeEvent(event);
91 QListView::initViewItemOption(option);
92 option->showDecorationSelected =
true;
94 option->font = combo->font();
100 QStyleOptionComboBox opt;
102 opt.editable = combo->isEditable();
103 if (combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo)) {
105 QStyleOptionMenuItem menuOpt;
106 menuOpt.initFrom(
this);
107 menuOpt.palette = palette();
108 menuOpt.state = QStyle::State_None;
109 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
110 menuOpt.menuRect = e->rect();
111 menuOpt.maxIconWidth = 0;
112 menuOpt.reservedShortcutWidth = 0;
113 QPainter p(viewport());
114 combo->style()->drawControl(QStyle::CE_MenuEmptyArea, &menuOpt, &p,
this);
117 QListView::paintEvent(e);
124QComboBoxPrivateScroller::QComboBoxPrivateScroller(QAbstractSlider::SliderAction action,
129 setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
130 setAttribute(Qt::WA_NoMousePropagation);
133QComboBoxPrivateScroller::~QComboBoxPrivateScroller()
136QSize QComboBoxPrivateScroller::sizeHint()
const
138 return QSize(20, style()->pixelMetric(QStyle::PM_MenuScrollerHeight,
nullptr,
this));
141void QComboBoxPrivateScroller::stopTimer()
146void QComboBoxPrivateScroller::startTimer() {
147 timer.start(100ms,
this);
151void QComboBoxPrivateScroller::enterEvent(QEnterEvent *)
156void QComboBoxPrivateScroller::leaveEvent(QEvent *)
161void QComboBoxPrivateScroller::timerEvent(QTimerEvent *e)
163 if (e->matches(timer)) {
164 emit doScroll(sliderAction);
166 emit doScroll(sliderAction);
167 emit doScroll(sliderAction);
172void QComboBoxPrivateScroller::hideEvent(QHideEvent *)
177void QComboBoxPrivateScroller::mouseMoveEvent(QMouseEvent *e)
180 const int mouseX = e->position().toPoint().x();
181 const int mouseY = e->position().toPoint().y();
182 const bool horizontallyInside = pos().x() < mouseX && mouseX < rect().right() + 1;
183 const bool verticallyOutside = (sliderAction == QAbstractSlider::SliderSingleStepAdd) ?
184 rect().bottom() + 1 < mouseY : mouseY < pos().y();
186 fast = horizontallyInside && verticallyOutside;
189void QComboBoxPrivateScroller::paintEvent(QPaintEvent *)
192 QStyleOptionMenuItem menuOpt;
193 menuOpt.initFrom(
this);
194 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
195 menuOpt.menuRect = rect();
196 menuOpt.maxIconWidth = 0;
197 menuOpt.reservedShortcutWidth = 0;
198 menuOpt.menuItemType = QStyleOptionMenuItem::Scroller;
199 if (sliderAction == QAbstractSlider::SliderSingleStepAdd)
200 menuOpt.state |= QStyle::State_DownArrow;
202 style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p);
205QComboBoxPrivate::QComboBoxPrivate()
208 duplicatesEnabled(
false),
215QComboBoxPrivate::~QComboBoxPrivate()
219 cleanupNativePopup();
227QComboMenuDelegate::QComboMenuDelegate(QObject *parent, QComboBox *cmb)
228 : QAbstractItemDelegate(parent), mCombo(cmb), pressedIndex(-1)
232QComboMenuDelegate::~QComboMenuDelegate()
235void QComboMenuDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option,
236 const QModelIndex &index)
const
238 const QStyleOptionMenuItem opt = getStyleOption(option, index);
239 painter->fillRect(option.rect, opt.palette.window());
240 mCombo->style()->drawControl(QStyle::CE_MenuItem, &opt, painter, mCombo);
243QSize QComboMenuDelegate::sizeHint(
const QStyleOptionViewItem &option,
244 const QModelIndex &index)
const
246 const QStyleOptionMenuItem opt = getStyleOption(option, index);
247 return mCombo->style()->sizeFromContents(QStyle::CT_MenuItem, &opt,
248 option.rect.size(), mCombo);
251QStyleOptionMenuItem QComboMenuDelegate::getStyleOption(
const QStyleOptionViewItem &option,
252 const QModelIndex &index)
const
254 QStyleOptionMenuItem menuOption;
256 QPalette resolvedpalette = option.palette.resolve(QApplication::palette(
"QMenu"));
257 QVariant value = index.data(Qt::ForegroundRole);
258 if (value.canConvert<QBrush>()) {
259 resolvedpalette.setBrush(QPalette::WindowText, qvariant_cast<QBrush>(value));
260 resolvedpalette.setBrush(QPalette::ButtonText, qvariant_cast<QBrush>(value));
261 resolvedpalette.setBrush(QPalette::Text, qvariant_cast<QBrush>(value));
263 menuOption.palette = resolvedpalette;
264 menuOption.state = QStyle::State_None;
265 if (mCombo->window()->isActiveWindow())
266 menuOption.state = QStyle::State_Active;
267 if ((option.state & QStyle::State_Enabled) && (index.model()->flags(index) & Qt::ItemIsEnabled))
268 menuOption.state |= QStyle::State_Enabled;
270 menuOption.palette.setCurrentColorGroup(QPalette::Disabled);
271 if (option.state & QStyle::State_Selected)
272 menuOption.state |= QStyle::State_Selected;
273 menuOption.checkType = QStyleOptionMenuItem::NonExclusive;
275 const QVariant checkState = index.data(Qt::CheckStateRole);
276 if (!checkState.isValid()) {
277 menuOption.checked = mCombo->currentIndex() == index.row();
279 menuOption.checked = qvariant_cast<
int>(checkState) == Qt::Checked;
280 menuOption.state |= qvariant_cast<
int>(checkState) == Qt::Checked
281 ? QStyle::State_On : QStyle::State_Off;
283 if (QComboBoxDelegate::isSeparator(index))
284 menuOption.menuItemType = QStyleOptionMenuItem::Separator;
286 menuOption.menuItemType = QStyleOptionMenuItem::Normal;
288 const QVariant variant = index.data(Qt::DecorationRole);
289 switch (variant.userType()) {
290 case QMetaType::QIcon:
291 menuOption.icon = qvariant_cast<QIcon>(variant);
293 case QMetaType::QColor: {
294 static QPixmap pixmap(option.decorationSize);
295 pixmap.fill(qvariant_cast<QColor>(variant));
296 menuOption.icon = pixmap;
299 menuOption.icon = qvariant_cast<QPixmap>(variant);
302 if (index.data(Qt::BackgroundRole).canConvert<QBrush>()) {
303 menuOption.palette.setBrush(QPalette::All, QPalette::Window,
304 qvariant_cast<QBrush>(index.data(Qt::BackgroundRole)));
306 menuOption.text = index.data(Qt::DisplayRole).toString().replace(u'&',
"&&"_L1);
307 menuOption.reservedShortcutWidth = 0;
308 menuOption.maxIconWidth = option.decorationSize.width() + 4;
309 menuOption.menuRect = option.rect;
310 menuOption.rect = option.rect;
314 QVariant fontRoleData = index.data(Qt::FontRole);
315 if (fontRoleData.isValid()) {
316 menuOption.font = qvariant_cast<QFont>(fontRoleData);
317 }
else if (mCombo->testAttribute(Qt::WA_SetFont)
318 || mCombo->testAttribute(Qt::WA_MacSmallSize)
319 || mCombo->testAttribute(Qt::WA_MacMiniSize)
320 || mCombo->font() != qt_app_fonts_hash()->value(
"QComboBox", QFont())) {
321 menuOption.font = mCombo->font();
323 menuOption.font = qt_app_fonts_hash()->value(
"QComboMenuItem", mCombo->font());
326 menuOption.fontMetrics = QFontMetrics(menuOption.font);
331bool QComboMenuDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
332 const QStyleOptionViewItem &option,
const QModelIndex &index)
338 Qt::ItemFlags flags = model->flags(index);
339 if (!(flags & Qt::ItemIsUserCheckable) || !(option.state & QStyle::State_Enabled)
340 || !(flags & Qt::ItemIsEnabled))
344 const QVariant checkState = index.data(Qt::CheckStateRole);
345 if (!checkState.isValid())
349 if ((event->type() == QEvent::MouseButtonRelease)
350 || (event->type() == QEvent::MouseButtonDblClick)
351 || (event->type() == QEvent::MouseButtonPress)) {
352 QMouseEvent *me =
static_cast<QMouseEvent*>(event);
353 if (me->button() != Qt::LeftButton)
356 if ((event->type() == QEvent::MouseButtonPress)
357 || (event->type() == QEvent::MouseButtonDblClick)) {
358 pressedIndex = index.row();
362 if (index.row() != pressedIndex)
366 }
else if (event->type() == QEvent::KeyPress) {
367 if (
static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space
368 &&
static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select)
375 Qt::CheckState newState = (
static_cast<Qt::CheckState>(checkState.toInt()) == Qt::Checked)
376 ? Qt::Unchecked : Qt::Checked;
377 return model->setData(index, newState, Qt::CheckStateRole);
384QComboBoxDelegate::QComboBoxDelegate(QObject *parent, QComboBox *cmb)
385 : QStyledItemDelegate(parent),
389QComboBoxDelegate::~QComboBoxDelegate()
392bool QComboBoxDelegate::isSeparator(
const QModelIndex &index)
394 return index.data(Qt::AccessibleDescriptionRole).toString() ==
"separator"_L1;
397void QComboBoxDelegate::setSeparator(QAbstractItemModel *model,
const QModelIndex &index)
400 static const QString sepString =
"separator"_L1;
401 model->setData(index, sepString, Qt::AccessibleDescriptionRole);
402 if (QStandardItemModel *m = qobject_cast<QStandardItemModel*>(model))
403 if (QStandardItem *item = m->itemFromIndex(index))
404 item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled));
407void QComboBoxDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option,
408 const QModelIndex &index)
const
410 if (isSeparator(index)) {
411 QRect rect = option.rect;
412 if (
const QAbstractItemView *view = qobject_cast<
const QAbstractItemView*>(option.widget))
413 rect.setWidth(view->viewport()->width());
416 mCombo->style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator,
417 &opt, painter, mCombo);
419 QStyledItemDelegate::paint(painter, option, index);
423QSize QComboBoxDelegate::sizeHint(
const QStyleOptionViewItem &option,
424 const QModelIndex &index)
const
426 if (isSeparator(index)) {
427 const int pm = mCombo->style()->pixelMetric(QStyle::PM_DefaultFrameWidth,
nullptr, mCombo);
428 return QSize(pm, pm);
430 return QStyledItemDelegate::sizeHint(option, index);
434#if QT_CONFIG(completer)
435void QComboBoxPrivate::completerActivated(
const QModelIndex &index)
438#if QT_CONFIG(proxymodel)
439 if (index.isValid() && q->completer()) {
440 QAbstractProxyModel *proxy = qobject_cast<QAbstractProxyModel *>(q->completer()->completionModel());
442 const QModelIndex &completerIndex = proxy->mapToSource(index);
444 if (completerIndex.model() == model) {
445 row = completerIndex.row();
448 QAbstractProxyModel *completerProxy = qobject_cast<QAbstractProxyModel *>(q->completer()->model());
449 if (completerProxy && completerProxy->sourceModel() == model) {
450 row = completerProxy->mapToSource(completerIndex).row();
452 QString match = q->completer()->model()->data(completerIndex).toString();
453 row = q->findText(match, matchFlags());
456 q->setCurrentIndex(row);
457 emitActivated(currentIndex);
464void QComboBoxPrivate::updateArrow(QStyle::StateFlag state)
467 if (arrowState == state)
470 QStyleOptionComboBox opt;
471 q->initStyleOption(&opt);
472 q->update(q->rect());
475void QComboBoxPrivate::modelReset()
479 lineEdit->setText(QString());
480 updateLineEditGeometry();
487void QComboBoxPrivate::modelDestroyed()
489 model = QAbstractItemModelPrivate::staticEmptyModel();
492void QComboBoxPrivate::trySetValidIndex()
495 bool currentReset =
false;
497 const int rowCount = q->count();
498 for (
int pos = 0; pos < rowCount; ++pos) {
499 const QModelIndex idx(model->index(pos, modelColumn, root));
500 if (idx.flags() & Qt::ItemIsEnabled) {
501 setCurrentIndex(idx);
508 setCurrentIndex(QModelIndex());
511QRect QComboBoxPrivate::popupGeometry(
const QPoint &globalPosition)
const
513 Q_Q(
const QComboBox);
514 return QStylePrivate::useFullScreenForPopup()
515 ? QWidgetPrivate::screenGeometry(q, globalPosition)
516 : QWidgetPrivate::availableScreenGeometry(q, globalPosition);
519bool QComboBoxPrivate::updateHoverControl(
const QPoint &pos)
523 QRect lastHoverRect = hoverRect;
524 QStyle::SubControl lastHoverControl = hoverControl;
525 bool doesHover = q->testAttribute(Qt::WA_Hover);
526 if (lastHoverControl != newHoverControl(pos) && doesHover) {
527 q->update(lastHoverRect);
528 q->update(hoverRect);
534QStyle::SubControl QComboBoxPrivate::newHoverControl(
const QPoint &pos)
537 QStyleOptionComboBox opt;
538 q->initStyleOption(&opt);
539 opt.subControls = QStyle::SC_All;
540 hoverControl = q->style()->hitTestComplexControl(QStyle::CC_ComboBox, &opt, pos, q);
541 hoverRect = (hoverControl != QStyle::SC_None)
542 ? q->style()->subControlRect(QStyle::CC_ComboBox, &opt, hoverControl, q)
548
549
550
551int QComboBoxPrivate::computeWidthHint()
const
553 Q_Q(
const QComboBox);
556 const int count = q->count();
557 const int iconWidth = q->iconSize().width() + 4;
558 const QFontMetrics &fontMetrics = q->fontMetrics();
560 for (
int i = 0; i < count; ++i) {
561 const int textWidth = fontMetrics.horizontalAdvance(q->itemText(i));
562 if (q->itemIcon(i).isNull())
563 width = (qMax(width, textWidth));
565 width = (qMax(width, textWidth + iconWidth));
568 QStyleOptionComboBox opt;
569 q->initStyleOption(&opt);
571 tmp = q->style()->sizeFromContents(QStyle::CT_ComboBox, &opt, tmp, q);
575QSize QComboBoxPrivate::recomputeSizeHint(QSize &sh)
const
577 Q_Q(
const QComboBox);
579 if (q->itemDelegate() && q->labelDrawingMode() == QComboBox::LabelDrawingMode::UseDelegate) {
580 QStyleOptionViewItem option;
581 initViewItemOption(&option);
582 sh = q->itemDelegate()->sizeHint(option, currentIndex);
585 bool hasIcon = sizeAdjustPolicy == QComboBox::AdjustToMinimumContentsLengthWithIcon;
586 int count = q->count();
587 QSize iconSize = q->iconSize();
588 const QFontMetrics &fm = q->fontMetrics();
591 if (&sh == &sizeHint || minimumContentsLength == 0) {
592 switch (sizeAdjustPolicy) {
593 case QComboBox::AdjustToContents:
594 case QComboBox::AdjustToContentsOnFirstShow:
596 sh.rwidth() = 7 * fm.horizontalAdvance(u'x');
598 for (
int i = 0; i < count; ++i) {
599 if (!q->itemIcon(i).isNull()) {
601 sh.setWidth(qMax(sh.width(), fm.boundingRect(q->itemText(i)).width() + iconSize.width() + 4));
603 sh.setWidth(qMax(sh.width(), fm.boundingRect(q->itemText(i)).width()));
608 case QComboBox::AdjustToMinimumContentsLengthWithIcon:
612 for (
int i = 0; i < count && !hasIcon; ++i)
613 hasIcon = !q->itemIcon(i).isNull();
615 if (minimumContentsLength > 0) {
616 auto r = qint64{minimumContentsLength} * fm.horizontalAdvance(u'X');
618 r += iconSize.width() + 4;
619 if (r <= QWIDGETSIZE_MAX) {
620 sh.setWidth(qMax(sh.width(),
int(r)));
622 qWarning(
"QComboBox: cannot take minimumContentsLength %d into account for sizeHint(), "
623 "since it causes the widget to be wider than QWIDGETSIZE_MAX. "
624 "Consider setting it to a less extreme value.",
625 minimumContentsLength);
628 if (!placeholderText.isEmpty())
629 sh.setWidth(qMax(sh.width(), fm.boundingRect(placeholderText).width()));
633 sh.setHeight(qMax(qCeil(QFontMetricsF(fm).height()), 14) + 2);
635 sh.setHeight(qMax(sh.height(), iconSize.height() + 2));
639 QStyleOptionComboBox opt;
640 q->initStyleOption(&opt);
641 sh = q->style()->sizeFromContents(QStyle::CT_ComboBox, &opt, sh, q);
646void QComboBoxPrivate::adjustComboBoxSize()
648 viewContainer()->adjustSizeTimer.start(20, container);
651void QComboBoxPrivate::updateLayoutDirection()
653 Q_Q(
const QComboBox);
654 QStyleOptionComboBox opt;
655 q->initStyleOption(&opt);
656 Qt::LayoutDirection dir = Qt::LayoutDirection(
657 q->style()->styleHint(QStyle::SH_ComboBox_LayoutDirection, &opt, q));
659 lineEdit->setLayoutDirection(dir);
661 container->setLayoutDirection(dir);
665void QComboBoxPrivateContainer::timerEvent(QTimerEvent *timerEvent)
667 if (timerEvent->timerId() == adjustSizeTimer.timerId()) {
668 adjustSizeTimer.stop();
669 if (combo->sizeAdjustPolicy() == QComboBox::AdjustToContents) {
670 combo->updateGeometry();
677void QComboBoxPrivateContainer::resizeEvent(QResizeEvent *e)
679 QStyleOptionComboBox opt = comboStyleOption();
680 if (combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo)) {
682 myOpt.initFrom(
this);
683 QStyleHintReturnMask mask;
684 if (combo->style()->styleHint(QStyle::SH_Menu_Mask, &myOpt,
this, &mask)) {
685 setMask(mask.region);
690 QFrame::resizeEvent(e);
693void QComboBoxPrivateContainer::paintEvent(QPaintEvent *e)
695 QStyleOptionComboBox cbOpt = comboStyleOption();
696 if (combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &cbOpt, combo)
697 && mask().isEmpty()) {
701 style()->drawPrimitive(QStyle::PE_PanelMenu, &opt, &p,
this);
704 QFrame::paintEvent(e);
707QComboBoxPrivateContainer::QComboBoxPrivateContainer(QAbstractItemView *itemView, QComboBox *parent)
708 : QFrame(parent, Qt::Popup), combo(parent)
714 setAttribute(Qt::WA_WindowPropagation);
715 setAttribute(Qt::WA_X11NetWmWindowTypeCombo);
718 blockMouseReleaseTimer.setSingleShot(
true);
721 QBoxLayout *layout =
new QBoxLayout(QBoxLayout::TopToBottom,
this);
722 layout->setSpacing(0);
723 layout->setContentsMargins(QMargins());
726 setItemView(itemView);
729 QStyleOptionComboBox opt = comboStyleOption();
730 const bool usePopup = combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo);
732 top =
new QComboBoxPrivateScroller(QAbstractSlider::SliderSingleStepSub,
this);
733 bottom =
new QComboBoxPrivateScroller(QAbstractSlider::SliderSingleStepAdd,
this);
741 layout->insertWidget(0, top);
742 connect(top, &QComboBoxPrivateScroller::doScroll,
743 this, &QComboBoxPrivateContainer::scrollItemView);
746 layout->addWidget(bottom);
747 connect(bottom, &QComboBoxPrivateScroller::doScroll,
748 this, &QComboBoxPrivateContainer::scrollItemView);
752 layout->insertSpacing(0, 0);
753 layout->addSpacing(0);
754 updateStyleSettings();
757QComboBoxPrivateContainer::~QComboBoxPrivateContainer()
759 disconnect(view, &QAbstractItemView::destroyed,
760 this, &QComboBoxPrivateContainer::viewDestroyed);
763void QComboBoxPrivateContainer::scrollItemView(
int action)
765#if QT_CONFIG(scrollbar)
766 if (view->verticalScrollBar())
767 view->verticalScrollBar()->triggerAction(
static_cast<QAbstractSlider::SliderAction>(action));
771void QComboBoxPrivateContainer::hideScrollers()
780
781
782void QComboBoxPrivateContainer::updateScrollers()
784#if QT_CONFIG(scrollbar)
788 if (isVisible() ==
false)
791 QStyleOptionComboBox opt = comboStyleOption();
792 if (combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo) &&
793 view->verticalScrollBar()->minimum() < view->verticalScrollBar()->maximum()) {
795 bool needTop = view->verticalScrollBar()->value()
796 > (view->verticalScrollBar()->minimum() + topMargin());
797 bool needBottom = view->verticalScrollBar()->value()
798 < (view->verticalScrollBar()->maximum() - bottomMargin() - topMargin());
815
816
817void QComboBoxPrivateContainer::viewDestroyed()
820 setItemView(
new QComboBoxListView());
824
825
826QAbstractItemView *QComboBoxPrivateContainer::itemView()
const
832
833
834
835
838
839
840void QComboBoxPrivateContainer::setItemView(QAbstractItemView *itemView)
846 view->removeEventFilter(
this);
847 view->viewport()->removeEventFilter(
this);
848#if QT_CONFIG(scrollbar)
849 disconnect(view->verticalScrollBar(), &QScrollBar::valueChanged,
850 this, &QComboBoxPrivateContainer::updateScrollers);
851 disconnect(view->verticalScrollBar(), &QScrollBar::rangeChanged,
852 this, &QComboBoxPrivateContainer::updateScrollers);
854 disconnect(view, &QAbstractItemView::destroyed,
855 this, &QComboBoxPrivateContainer::viewDestroyed);
857 if (isAncestorOf(view))
864 view->setParent(
this);
865 view->setAttribute(Qt::WA_MacShowFocusRect,
false);
866 qobject_cast<QBoxLayout*>(layout())->insertWidget(top ? 2 : 0, view);
867 view->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
868 view->installEventFilter(
this);
869 view->viewport()->installEventFilter(
this);
870 view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
871 QStyleOptionComboBox opt = comboStyleOption();
872 const auto *style = combo->style();
873 const bool usePopup = style->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo);
874#if QT_CONFIG(scrollbar)
876 view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
879 style->styleHint(QStyle::SH_ComboBox_ListMouseTracking_Current, &opt, combo) ||
880 style->styleHint(QStyle::SH_ComboBox_ListMouseTracking_Active, &opt, combo)
882 view->setMouseTracking(
true);
884 view->setSelectionMode(QAbstractItemView::SingleSelection);
885 view->setFrameStyle(QFrame::NoFrame);
886 view->setLineWidth(0);
887 view->setEditTriggers(QAbstractItemView::NoEditTriggers);
888#if QT_CONFIG(scrollbar)
889 connect(view->verticalScrollBar(), &QScrollBar::valueChanged,
890 this, &QComboBoxPrivateContainer::updateScrollers);
891 connect(view->verticalScrollBar(), &QScrollBar::rangeChanged,
892 this, &QComboBoxPrivateContainer::updateScrollers);
894 connect(view, &QAbstractItemView::destroyed,
895 this, &QComboBoxPrivateContainer::viewDestroyed);
899
900
901int QComboBoxPrivateContainer::topMargin()
const
903 if (
const QListView *lview = qobject_cast<
const QListView*>(view))
904 return lview->spacing();
905#if QT_CONFIG(tableview)
906 if (
const QTableView *tview = qobject_cast<
const QTableView*>(view))
907 return tview->showGrid() ? 1 : 0;
913
914
915int QComboBoxPrivateContainer::spacing()
const
917 QListView *lview = qobject_cast<QListView*>(view);
919 return 2 * lview->spacing();
920#if QT_CONFIG(tableview)
921 QTableView *tview = qobject_cast<QTableView*>(view);
923 return tview->showGrid() ? 1 : 0;
928void QComboBoxPrivateContainer::updateTopBottomMargin()
930 if (!layout() || layout()->count() < 1)
933 QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(layout());
937 const QStyleOptionComboBox opt = comboStyleOption();
938 const auto *style = combo->style();
939 const bool usePopup = style->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo);
940 const int margin = usePopup ? style->pixelMetric(QStyle::PM_MenuVMargin, &opt, combo) : 0;
942 QSpacerItem *topSpacer = boxLayout->itemAt(0)->spacerItem();
944 topSpacer->changeSize(0, margin, QSizePolicy::Minimum, QSizePolicy::Fixed);
946 QSpacerItem *bottomSpacer = boxLayout->itemAt(boxLayout->count() - 1)->spacerItem();
947 if (bottomSpacer && bottomSpacer != topSpacer)
948 bottomSpacer->changeSize(0, margin, QSizePolicy::Minimum, QSizePolicy::Fixed);
950 boxLayout->invalidate();
953void QComboBoxPrivateContainer::updateStyleSettings()
956 QStyleOptionComboBox opt = comboStyleOption();
957 const auto *style = combo->style();
958 view->setMouseTracking(style->styleHint(QStyle::SH_ComboBox_ListMouseTracking_Current, &opt, combo) ||
959 style->styleHint(QStyle::SH_ComboBox_ListMouseTracking_Active, &opt, combo) ||
960 style->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo));
961 setFrameStyle(style->styleHint(QStyle::SH_ComboBox_PopupFrameStyle, &opt, combo));
962 updateTopBottomMargin();
965void QComboBoxPrivateContainer::changeEvent(QEvent *e)
967 if (e->type() == QEvent::StyleChange)
968 updateStyleSettings();
970 QFrame::changeEvent(e);
974bool QComboBoxPrivateContainer::eventFilter(QObject *o, QEvent *e)
977 case QEvent::ShortcutOverride: {
978 QKeyEvent *keyEvent =
static_cast<QKeyEvent*>(e);
979 switch (keyEvent->key()) {
982 if (view->currentIndex().isValid() && view->currentIndex().flags().testFlag(Qt::ItemIsEnabled)) {
985 emit itemSelected(view->currentIndex());
989 if (!(keyEvent->modifiers() & Qt::AltModifier))
995 emit itemSelected(view->currentIndex());
998#if QT_CONFIG(shortcut)
999 if (keyEvent->matches(QKeySequence::Cancel) && isVisible()) {
1008 case QEvent::MouseMove:
1010 QMouseEvent *m =
static_cast<QMouseEvent *>(e);
1011 QWidget *widget =
static_cast<QWidget *>(o);
1012 const QPointF vector = widget->mapToGlobal(m->position()) - initialClickPosition;
1013 if (vector.manhattanLength() > 9 && blockMouseReleaseTimer.isActive())
1014 blockMouseReleaseTimer.stop();
1015 if (combo->style()->styleHint(QStyle::SH_ComboBox_ListMouseTracking_Current,
nullptr, combo)) {
1016 QModelIndex indexUnderMouse = view->indexAt(m->position().toPoint());
1017 if (indexUnderMouse.isValid()
1018 && !QComboBoxDelegate::isSeparator(indexUnderMouse)) {
1019 view->setCurrentIndex(indexUnderMouse);
1024 case QEvent::MouseButtonPress:
1025 maybeIgnoreMouseButtonRelease =
false;
1027 case QEvent::MouseButtonRelease: {
1028 bool ignoreEvent = maybeIgnoreMouseButtonRelease && popupTimer.elapsed() < QApplication::doubleClickInterval();
1030 QMouseEvent *m =
static_cast<QMouseEvent *>(e);
1031 if (isVisible() && view->rect().contains(m->position().toPoint()) && view->currentIndex().isValid()
1032 && !blockMouseReleaseTimer.isActive() && !ignoreEvent
1033 && (view->currentIndex().flags().testFlag(Qt::ItemIsEnabled))
1034 && (view->currentIndex().flags().testFlag(Qt::ItemIsSelectable))) {
1036 emit itemSelected(view->currentIndex());
1044 return QFrame::eventFilter(o, e);
1047void QComboBoxPrivateContainer::showEvent(QShowEvent *)
1052void QComboBoxPrivateContainer::hideEvent(QHideEvent *)
1056#if QT_CONFIG(graphicsview)
1060 if (QGraphicsProxyWidget *proxy = graphicsProxyWidget())
1065void QComboBoxPrivateContainer::mousePressEvent(QMouseEvent *e)
1068 QStyleOptionComboBox opt = comboStyleOption();
1069 opt.subControls = QStyle::SC_All;
1070 opt.activeSubControls = QStyle::SC_ComboBoxArrow;
1071 QStyle::SubControl sc = combo->style()->hitTestComplexControl(QStyle::CC_ComboBox, &opt,
1072 combo->mapFromGlobal(e->globalPosition()).toPoint(),
1074 if ((combo->isEditable() && sc == QStyle::SC_ComboBoxArrow)
1075 || (!combo->isEditable() && sc != QStyle::SC_None))
1076 setAttribute(Qt::WA_NoMouseReplay);
1080void QComboBoxPrivateContainer::mouseReleaseEvent(QMouseEvent *e)
1083 if (!blockMouseReleaseTimer.isActive()) {
1089QStyleOptionComboBox QComboBoxPrivateContainer::comboStyleOption()
const
1093 QStyleOptionComboBox opt;
1094 opt.initFrom(combo);
1095 opt.subControls = QStyle::SC_All;
1096 opt.activeSubControls = QStyle::SC_None;
1097 opt.editable = combo->isEditable();
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1129
1130
1131
1132
1133
1134
1135
1136
1139
1140
1141
1142
1143
1144
1145
1146
1147
1150
1151
1152
1153
1154
1157
1158
1159
1160
1161
1162
1165
1166
1167
1168
1169
1170
1171
1172
1175
1176
1177
1178
1179
1180
1181
1182
1183
1186
1187
1188
1189QComboBox::QComboBox(QWidget *parent)
1190 : QWidget(*
new QComboBoxPrivate(), parent, { })
1197
1198
1199QComboBox::QComboBox(QComboBoxPrivate &dd, QWidget *parent)
1200 : QWidget(dd, parent, { })
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1292void QComboBoxPrivate::init()
1300 if (!q->isEditable())
1301 q->setFocusPolicy(Qt::TabFocus);
1304 q->setFocusPolicy(Qt::WheelFocus);
1306 q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed,
1307 QSizePolicy::ComboBox));
1308 setLayoutItemMargins(QStyle::SE_ComboBoxLayoutItem);
1309 q->setModel(
new QStandardItemModel(0, 1, q));
1310 if (!q->isEditable())
1311 q->setAttribute(Qt::WA_InputMethodEnabled,
false);
1313 q->setAttribute(Qt::WA_InputMethodEnabled);
1316QComboBoxPrivateContainer* QComboBoxPrivate::viewContainer()
1322 container =
new QComboBoxPrivateContainer(
new QComboBoxListView(q), q);
1324 container->itemView()->setModel(model);
1326 container->itemView()->setTextElideMode(Qt::ElideMiddle);
1327 updateDelegate(
true);
1328 updateLayoutDirection();
1329 updateViewContainerPaletteAndOpacity();
1330 QObjectPrivate::connect(container, &QComboBoxPrivateContainer::itemSelected,
1331 this, &QComboBoxPrivate::itemSelected);
1332 QObjectPrivate::connect(container->itemView()->selectionModel(),
1333 &QItemSelectionModel::currentChanged,
1334 this, &QComboBoxPrivate::emitHighlighted);
1335 QObjectPrivate::connect(container, &QComboBoxPrivateContainer::resetButton,
1336 this, &QComboBoxPrivate::resetButton);
1341void QComboBoxPrivate::resetButton()
1343 updateArrow(QStyle::State_None);
1346void QComboBoxPrivate::dataChanged(
const QModelIndex &topLeft,
const QModelIndex &bottomRight)
1349 if (inserting || topLeft.parent() != root)
1352 if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
1354 adjustComboBoxSize();
1355 q->updateGeometry();
1358 if (currentIndex.row() >= topLeft.row() && currentIndex.row() <= bottomRight.row()) {
1359 const QString text = q->itemText(currentIndex.row());
1361 lineEdit->setText(text);
1362 updateLineEditGeometry();
1364 updateCurrentText(text);
1367#if QT_CONFIG(accessibility)
1368 QAccessibleValueChangeEvent event(q, text);
1369 QAccessible::updateAccessibility(&event);
1374void QComboBoxPrivate::rowsInserted(
const QModelIndex &parent,
int start,
int end)
1377 if (inserting || parent != root)
1380 if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
1382 adjustComboBoxSize();
1383 q->updateGeometry();
1387 if (start == 0 && (end - start + 1) == q->count() && !currentIndex.isValid() &&
1388 placeholderText.isEmpty()) {
1389#if QT_CONFIG(accessibility)
1394 if (container && container->itemView()) {
1395 QAccessibleTableModelChangeEvent event(container->itemView(),
1396 QAccessibleTableModelChangeEvent::ModelReset);
1397 QAccessible::updateAccessibility(&event);
1400 q->setCurrentIndex(0);
1402 }
else if (currentIndex.row() != indexBeforeChange) {
1404 emitCurrentIndexChanged(currentIndex);
1408void QComboBoxPrivate::updateIndexBeforeChange()
1410 indexBeforeChange = currentIndex.row();
1413void QComboBoxPrivate::rowsRemoved(
const QModelIndex &parent,
int ,
int )
1419 if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
1421 adjustComboBoxSize();
1422 q->updateGeometry();
1426 if (model->rowCount(root) == 0) {
1427 setCurrentIndex(QModelIndex());
1432 if (currentIndex.row() != indexBeforeChange) {
1433 if (!currentIndex.isValid() && q->count()) {
1434 q->setCurrentIndex(qMin(q->count() - 1, qMax(indexBeforeChange, 0)));
1438 lineEdit->setText(q->itemText(currentIndex.row()));
1439 updateLineEditGeometry();
1442 emitCurrentIndexChanged(currentIndex);
1447void QComboBoxPrivate::updateViewContainerPaletteAndOpacity()
1452 QStyleOptionComboBox opt;
1453 q->initStyleOption(&opt);
1455 if (q->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, q)) {
1457 menu.ensurePolished();
1458 container->setPalette(menu.palette());
1459 container->setWindowOpacity(menu.windowOpacity());
1463 container->setPalette(q->palette());
1464 container->setWindowOpacity(1.0);
1467 lineEdit->setPalette(q->palette());
1470void QComboBoxPrivate::updateFocusPolicy()
1476 if (q->isEditable())
1477 q->setFocusPolicy(Qt::WheelFocus);
1479 q->setFocusPolicy(Qt::TabFocus);
1484
1485
1486
1487
1488
1489
1490void QComboBox::initStyleOption(QStyleOptionComboBox *option)
const
1495 Q_D(
const QComboBox);
1496 option->initFrom(
this);
1497 option->editable = isEditable();
1498 option->frame = d->frame;
1499 if (hasFocus() && !option->editable)
1500 option->state |= QStyle::State_Selected;
1501 option->subControls = QStyle::SC_All;
1502 if (d->arrowState == QStyle::State_Sunken) {
1503 option->activeSubControls = QStyle::SC_ComboBoxArrow;
1504 option->state |= d->arrowState;
1506 option->activeSubControls = d->hoverControl;
1508 option->currentText = currentText();
1509 if (d->currentIndex.isValid()) {
1510 option->currentIcon = d->itemIcon(d->currentIndex);
1511 QVariant alignment = d->model->data(d->currentIndex, Qt::TextAlignmentRole);
1512 if (alignment.isValid())
1513 option->textAlignment =
static_cast<Qt::Alignment>(alignment.toUInt());
1515 option->iconSize = iconSize();
1516 if (d->container && d->container->isVisible())
1517 option->state |= QStyle::State_On;
1520void QComboBoxPrivate::initViewItemOption(QStyleOptionViewItem *option)
const
1522 Q_Q(
const QComboBox);
1523 q->view()->initViewItemOption(option);
1525 option->index = currentIndex;
1526 option->text = q->currentText();
1527 option->icon = itemIcon(currentIndex);
1530void QComboBoxPrivate::updateLineEditGeometry()
1536 QStyleOptionComboBox opt;
1537 q->initStyleOption(&opt);
1538 QRect editRect = q->style()->subControlRect(QStyle::CC_ComboBox, &opt,
1539 QStyle::SC_ComboBoxEditField, q);
1540 if (currentIndex.isValid() && !q->itemIcon(q->currentIndex()).isNull()) {
1541 QRect comboRect(editRect);
1542 editRect.setWidth(editRect.width() - q->iconSize().width() - 4);
1543 editRect = QStyle::alignedRect(q->layoutDirection(), Qt::AlignRight,
1544 editRect.size(), comboRect);
1546 lineEdit->setGeometry(editRect);
1549Qt::MatchFlags QComboBoxPrivate::matchFlags()
const
1552 Qt::MatchFlags flags = Qt::MatchFixedString;
1553#if QT_CONFIG(completer)
1554 if (!lineEdit->completer() || lineEdit->completer()->caseSensitivity() == Qt::CaseSensitive)
1556 flags |= Qt::MatchCaseSensitive;
1561void QComboBoxPrivate::editingFinished()
1566 const auto leText = lineEdit->text();
1567 if (!leText.isEmpty() && itemText(currentIndex) != leText) {
1568#if QT_CONFIG(completer)
1569 const auto *leCompleter = lineEdit->completer();
1570 const auto *popup = leCompleter ? QCompleterPrivate::get(leCompleter)->popup :
nullptr;
1571 if (popup && popup->isVisible()) {
1576 const QItemSelectionModel *selModel = popup->selectionModel();
1577 const QModelIndex curIndex = popup->currentIndex();
1578 const bool completerIsActive = selModel && selModel->selectedIndexes().contains(curIndex);
1580 if (completerIsActive)
1584 const int index = q_func()->findText(leText, matchFlags());
1586 q->setCurrentIndex(index);
1587 emitActivated(currentIndex);
1593void QComboBoxPrivate::returnPressed()
1601 if (insertPolicy == QComboBox::NoInsert)
1604 if (lineEdit && !lineEdit->text().isEmpty()) {
1605 if (q->count() >= maxCount && !(
this->insertPolicy == QComboBox::InsertAtCurrent))
1607 lineEdit->deselect();
1608 lineEdit->end(
false);
1609 QString text = lineEdit->text();
1612 if (!duplicatesEnabled) {
1613 index = q->findText(text, matchFlags());
1615 q->setCurrentIndex(index);
1616 emitActivated(currentIndex);
1620 switch (insertPolicy) {
1621 case QComboBox::InsertAtTop:
1624 case QComboBox::InsertAtBottom:
1627 case QComboBox::InsertAtCurrent:
1628 case QComboBox::InsertAfterCurrent:
1629 case QComboBox::InsertBeforeCurrent:
1630 if (!q->count() || !currentIndex.isValid())
1632 else if (insertPolicy == QComboBox::InsertAtCurrent)
1633 q->setItemText(q->currentIndex(), text);
1634 else if (insertPolicy == QComboBox::InsertAfterCurrent)
1635 index = q->currentIndex() + 1;
1636 else if (insertPolicy == QComboBox::InsertBeforeCurrent)
1637 index = q->currentIndex();
1639 case QComboBox::InsertAlphabetically:
1641 for (
int i = 0; i < q->count(); ++i, ++index) {
1642 if (text.toLower() < q->itemText(i).toLower())
1650 q->insertItem(index, text);
1651 q->setCurrentIndex(index);
1652 emitActivated(currentIndex);
1657void QComboBoxPrivate::itemSelected(
const QModelIndex &item)
1660 if (item != currentIndex) {
1661 setCurrentIndex(item);
1662 }
else if (lineEdit) {
1663 lineEdit->selectAll();
1664 lineEdit->setText(q->itemText(currentIndex.row()));
1666 emitActivated(currentIndex);
1669void QComboBoxPrivate::emitActivated(
const QModelIndex &index)
1672 if (!index.isValid())
1674 QString text(itemText(index));
1675 emit q->activated(index.row());
1676 emit q->textActivated(text);
1679void QComboBoxPrivate::emitHighlighted(
const QModelIndex &index)
1682 if (!index.isValid())
1684 QString text(itemText(index));
1685 emit q->highlighted(index.row());
1686 emit q->textHighlighted(text);
1689void QComboBoxPrivate::emitCurrentIndexChanged(
const QModelIndex &index)
1692 const QString text = itemText(index);
1693 emit q->currentIndexChanged(index.row());
1696 updateCurrentText(text);
1697#if QT_CONFIG(accessibility)
1698 QAccessibleValueChangeEvent event(q, text);
1699 QAccessible::updateAccessibility(&event);
1703QString QComboBoxPrivate::itemText(
const QModelIndex &index)
const
1705 return index.isValid() ? model->data(index, itemRole()).toString() : QString();
1708int QComboBoxPrivate::itemRole()
const
1710 return q_func()->isEditable() ? Qt::EditRole : Qt::DisplayRole;
1714
1715
1716QComboBox::~QComboBox()
1722 d->disconnectModel();
1731 d->container->close();
1732 delete d->container;
1733 d->container =
nullptr;
1738
1739
1740
1741
1742
1743
1744
1745
1746int QComboBox::maxVisibleItems()
const
1748 Q_D(
const QComboBox);
1749 return d->maxVisibleItems;
1752void QComboBox::setMaxVisibleItems(
int maxItems)
1755 if (Q_UNLIKELY(maxItems < 0)) {
1756 qWarning(
"QComboBox::setMaxVisibleItems: "
1757 "Invalid max visible items (%d) must be >= 0", maxItems);
1760 d->maxVisibleItems = maxItems;
1764
1765
1766
1767
1768
1769int QComboBox::count()
const
1771 Q_D(
const QComboBox);
1772 return d->model->rowCount(d->root);
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787void QComboBox::setMaxCount(
int max)
1790 if (Q_UNLIKELY(max < 0)) {
1791 qWarning(
"QComboBox::setMaxCount: Invalid count (%d) must be >= 0", max);
1795 const int rowCount = count();
1797 d->model->removeRows(max, rowCount - max, d->root);
1802int QComboBox::maxCount()
const
1804 Q_D(
const QComboBox);
1809
1810
1811
1812
1813
1814
1815
1816
1817bool QComboBox::duplicatesEnabled()
const
1819 Q_D(
const QComboBox);
1820 return d->duplicatesEnabled;
1823void QComboBox::setDuplicatesEnabled(
bool enable)
1826 d->duplicatesEnabled = enable;
1830
1831
1832
1833
1834
1835
1838
1839
1840
1841
1842
1843int QComboBox::findData(
const QVariant &data,
int role, Qt::MatchFlags flags)
const
1845 Q_D(
const QComboBox);
1846 QModelIndex start = d->model->index(0, d->modelColumn, d->root);
1847 const QModelIndexList result = d->model->match(start, role, data, 1, flags);
1848 if (result.isEmpty())
1850 return result.first().row();
1854
1855
1856
1857
1858
1859
1860
1861
1862
1864QComboBox::InsertPolicy QComboBox::insertPolicy()
const
1866 Q_D(
const QComboBox);
1867 return d->insertPolicy;
1870void QComboBox::setInsertPolicy(InsertPolicy policy)
1873 d->insertPolicy = policy;
1877
1878
1879
1880
1881
1882
1883
1884
1886QComboBox::SizeAdjustPolicy QComboBox::sizeAdjustPolicy()
const
1888 Q_D(
const QComboBox);
1889 return d->sizeAdjustPolicy;
1892void QComboBox::setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy policy)
1895 if (policy == d->sizeAdjustPolicy)
1898 d->sizeAdjustPolicy = policy;
1899 d->sizeHint = QSize();
1900 d->adjustComboBoxSize();
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915int QComboBox::minimumContentsLength()
const
1917 Q_D(
const QComboBox);
1918 return d->minimumContentsLength;
1921void QComboBox::setMinimumContentsLength(
int characters)
1924 if (characters == d->minimumContentsLength || characters < 0)
1927 d->minimumContentsLength = characters;
1929 if (d->sizeAdjustPolicy == AdjustToContents
1930 || d->sizeAdjustPolicy == AdjustToMinimumContentsLengthWithIcon) {
1931 d->sizeHint = QSize();
1932 d->adjustComboBoxSize();
1938
1939
1940
1941
1942
1943
1944
1946QSize QComboBox::iconSize()
const
1948 Q_D(
const QComboBox);
1949 if (d->iconSize.isValid())
1952 int iconWidth = style()->pixelMetric(QStyle::PM_SmallIconSize,
nullptr,
this);
1953 return QSize(iconWidth, iconWidth);
1956void QComboBox::setIconSize(
const QSize &size)
1959 if (size == d->iconSize)
1962 view()->setIconSize(size);
1964 d->sizeHint = QSize();
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984void QComboBox::setPlaceholderText(
const QString &placeholderText)
1987 if (placeholderText == d->placeholderText)
1990 d->placeholderText = placeholderText;
1991 if (currentIndex() == -1) {
1992 if (d->placeholderText.isEmpty())
2001QString QComboBox::placeholderText()
const
2003 Q_D(
const QComboBox);
2004 return d->placeholderText;
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019bool QComboBox::isEditable()
const
2021 Q_D(
const QComboBox);
2022 return d->lineEdit !=
nullptr;
2026
2027
2028
2029
2030
2031
2032void QComboBoxPrivate::updateDelegate(
bool force)
2035 QStyleOptionComboBox opt;
2036 q->initStyleOption(&opt);
2037 if (q->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, q)) {
2038 if (force || qobject_cast<QComboBoxDelegate *>(q->itemDelegate()))
2039 q->setItemDelegate(
new QComboMenuDelegate(q->view(), q));
2041 if (force || qobject_cast<QComboMenuDelegate *>(q->itemDelegate()))
2042 q->setItemDelegate(
new QComboBoxDelegate(q->view(), q));
2046QIcon QComboBoxPrivate::itemIcon(
const QModelIndex &index)
const
2048 if (!index.isValid())
2050 QVariant decoration = model->data(index, Qt::DecorationRole);
2051 if (decoration.userType() == QMetaType::QPixmap)
2052 return QIcon(qvariant_cast<QPixmap>(decoration));
2054 return qvariant_cast<QIcon>(decoration);
2057void QComboBox::setEditable(
bool editable)
2060 if (isEditable() == editable)
2063 QStyleOptionComboBox opt;
2064 initStyleOption(&opt);
2066 if (style()->styleHint(QStyle::SH_ComboBox_Popup, &opt,
this)) {
2067 d->viewContainer()->updateScrollers();
2068 view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
2070 QLineEdit *le =
new QLineEdit(
this);
2071 le->setPalette(palette());
2074 if (style()->styleHint(QStyle::SH_ComboBox_Popup, &opt,
this)) {
2075 d->viewContainer()->updateScrollers();
2076 view()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
2078 setAttribute(Qt::WA_InputMethodEnabled,
false);
2079 d->lineEdit->hide();
2080 d->lineEdit->deleteLater();
2081 d->lineEdit =
nullptr;
2084 d->updateDelegate();
2085 d->updateFocusPolicy();
2087 d->viewContainer()->updateTopBottomMargin();
2088 if (!testAttribute(Qt::WA_Resized))
2093
2094
2095
2096
2097
2098
2099
2100void QComboBox::setLineEdit(QLineEdit *edit)
2103 if (Q_UNLIKELY(!edit)) {
2104 qWarning(
"QComboBox::setLineEdit: cannot set a 0 line edit");
2108 if (edit == d->lineEdit)
2111 edit->setText(currentText());
2116 qt_widget_private(d->lineEdit)->inheritsInputMethodHints = 1;
2118 if (d->lineEdit->parent() !=
this)
2119 d->lineEdit->setParent(
this);
2120 QObjectPrivate::connect(d->lineEdit, &QLineEdit::returnPressed,
2121 d, &QComboBoxPrivate::returnPressed);
2122 QObjectPrivate::connect(d->lineEdit, &QLineEdit::editingFinished,
2123 d, &QComboBoxPrivate::editingFinished);
2124 connect(d->lineEdit, &QLineEdit::textChanged,
this, &QComboBox::editTextChanged);
2125 connect(d->lineEdit, &QLineEdit::textChanged,
this, &QComboBox::currentTextChanged);
2126 QObjectPrivate::connect(d->lineEdit, &QLineEdit::cursorPositionChanged,
2127 d, &QComboBoxPrivate::updateMicroFocus);
2128 QObjectPrivate::connect(d->lineEdit, &QLineEdit::selectionChanged,
2129 d, &QComboBoxPrivate::updateMicroFocus);
2130 QObjectPrivate::connect(d->lineEdit->d_func()->control, &QWidgetLineControl::updateMicroFocus,
2131 d, &QComboBoxPrivate::updateMicroFocus);
2132 d->lineEdit->setFrame(
false);
2133 d->lineEdit->setContextMenuPolicy(Qt::NoContextMenu);
2134 d->updateFocusPolicy();
2135 d->lineEdit->setFocusProxy(
this);
2136 d->lineEdit->setAttribute(Qt::WA_MacShowFocusRect,
false);
2138#if QT_CONFIG(completer)
2140 if (!d->lineEdit->completer()) {
2141 QCompleter *completer =
new QCompleter(d->model, d->lineEdit);
2142 completer->setCaseSensitivity(Qt::CaseInsensitive);
2143 completer->setCompletionMode(QCompleter::InlineCompletion);
2144 completer->setCompletionColumn(d->modelColumn);
2146 setCompleter(completer);
2150 setAttribute(Qt::WA_InputMethodEnabled);
2151 d->updateLayoutDirection();
2152 d->updateLineEditGeometry();
2154 d->lineEdit->show();
2160
2161
2162
2163
2164
2165QLineEdit *QComboBox::lineEdit()
const
2167 Q_D(
const QComboBox);
2171#ifndef QT_NO_VALIDATOR
2173
2174
2175
2176
2177
2178
2180void QComboBox::setValidator(
const QValidator *v)
2184 d->lineEdit->setValidator(v);
2188
2189
2190
2191
2192
2193const QValidator *QComboBox::validator()
const
2195 Q_D(
const QComboBox);
2196 return d->lineEdit ? d->lineEdit->validator() :
nullptr;
2200#if QT_CONFIG(completer)
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216void QComboBox::setCompleter(QCompleter *c)
2220 qWarning(
"Setting a QCompleter on non-editable QComboBox is not allowed.");
2223 d->lineEdit->setCompleter(c);
2225 QObjectPrivate::connect(c, QOverload<
const QModelIndex &>::of(&QCompleter::activated),
2226 d, &QComboBoxPrivate::completerActivated);
2232
2233
2234
2235
2236
2237
2238
2239QCompleter *QComboBox::completer()
const
2241 Q_D(
const QComboBox);
2242 return d->lineEdit ? d->lineEdit->completer() :
nullptr;
2248
2249
2250
2251
2252QAbstractItemDelegate *QComboBox::itemDelegate()
const
2254 return view()->itemDelegate();
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272void QComboBox::setItemDelegate(QAbstractItemDelegate *delegate)
2274 if (Q_UNLIKELY(!delegate)) {
2275 qWarning(
"QComboBox::setItemDelegate: cannot set a 0 delegate");
2278 view()->setItemDelegate(delegate);
2282
2283
2285QAbstractItemModel *QComboBox::model()
const
2287 Q_D(
const QComboBox);
2288 if (d->model == QAbstractItemModelPrivate::staticEmptyModel()) {
2289 QComboBox *that =
const_cast<QComboBox*>(
this);
2290 that->setModel(
new QStandardItemModel(0, 1, that));
2296
2297
2298
2299
2300
2301
2302
2303
2304void QComboBox::setModel(QAbstractItemModel *model)
2308 if (Q_UNLIKELY(!model)) {
2309 qWarning(
"QComboBox::setModel: cannot set a 0 model");
2313 if (model == d->model)
2316#if QT_CONFIG(completer)
2317 if (d->lineEdit && d->lineEdit->completer())
2318 d->lineEdit->completer()->setModel(model);
2320 d->disconnectModel();
2321 if (d->model && d->model->QObject::parent() ==
this) {
2329 d->container->itemView()->setModel(model);
2330 QObjectPrivate::connect(d->container->itemView()->selectionModel(),
2331 &QItemSelectionModel::currentChanged,
2332 d, &QComboBoxPrivate::emitHighlighted, Qt::UniqueConnection);
2337 setRootModelIndex(QModelIndex());
2339 d->trySetValidIndex();
2343void QComboBoxPrivate::connectModel()
2348 modelConnections = {
2349 QObjectPrivate::connect(model, &QAbstractItemModel::dataChanged,
2350 this, &QComboBoxPrivate::dataChanged),
2351 QObjectPrivate::connect(model, &QAbstractItemModel::rowsAboutToBeInserted,
2352 this, &QComboBoxPrivate::updateIndexBeforeChange),
2353 QObjectPrivate::connect(model, &QAbstractItemModel::rowsInserted,
2354 this, &QComboBoxPrivate::rowsInserted),
2355 QObjectPrivate::connect(model, &QAbstractItemModel::rowsAboutToBeRemoved,
2356 this, &QComboBoxPrivate::updateIndexBeforeChange),
2357 QObjectPrivate::connect(model, &QAbstractItemModel::rowsRemoved,
2358 this, &QComboBoxPrivate::rowsRemoved),
2359 QObjectPrivate::connect(model, &QObject::destroyed,
2360 this, &QComboBoxPrivate::modelDestroyed),
2361 QObjectPrivate::connect(model, &QAbstractItemModel::modelAboutToBeReset,
2362 this, &QComboBoxPrivate::updateIndexBeforeChange),
2363 QObjectPrivate::connect(model, &QAbstractItemModel::modelReset,
2364 this, &QComboBoxPrivate::modelReset)
2368void QComboBoxPrivate::disconnectModel()
2370 for (
auto &connection : modelConnections)
2371 QObject::disconnect(connection);
2375
2376
2377
2378
2380QModelIndex QComboBox::rootModelIndex()
const
2382 Q_D(
const QComboBox);
2383 return QModelIndex(d->root);
2387
2388
2389
2390
2391void QComboBox::setRootModelIndex(
const QModelIndex &index)
2394 if (d->root == index)
2396 d->root = QPersistentModelIndex(index);
2397 view()->setRootIndex(index);
2402
2403
2404
2405
2406
2407
2408
2409
2410int QComboBox::currentIndex()
const
2412 Q_D(
const QComboBox);
2413 return d->currentIndex.row();
2416void QComboBox::setCurrentIndex(
int index)
2419 QModelIndex mi = index >= 0 ? d->model->index(index, d->modelColumn, d->root) : QModelIndex();
2420 d->setCurrentIndex(mi);
2423void QComboBox::setCurrentText(
const QString &text)
2428 const int i = findText(text);
2434void QComboBoxPrivate::setCurrentIndex(
const QModelIndex &mi)
2438 QModelIndex normalized = mi.sibling(mi.row(), modelColumn);
2439 if (!normalized.isValid())
2442 bool indexChanged = (normalized != currentIndex);
2444 currentIndex = QPersistentModelIndex(normalized);
2446 const QString newText = itemText(normalized);
2447 if (lineEdit->text() != newText) {
2448 lineEdit->setText(newText);
2449#if QT_CONFIG(completer)
2450 if (lineEdit && lineEdit->completer())
2451 lineEdit->completer()->setCompletionPrefix(newText);
2454 updateLineEditGeometry();
2461 const bool modelResetToEmpty = !normalized.isValid() && indexBeforeChange != -1;
2462 if (modelResetToEmpty)
2463 indexBeforeChange = -1;
2465 if (indexChanged || modelResetToEmpty) {
2466 QItemSelectionModel::SelectionFlags selectionMode = QItemSelectionModel::ClearAndSelect;
2467 if (q->view()->selectionBehavior() == QAbstractItemView::SelectRows)
2468 selectionMode.setFlag(QItemSelectionModel::Rows);
2469 if (
auto *model = q->view()->selectionModel())
2470 model->setCurrentIndex(currentIndex, selectionMode);
2473 emitCurrentIndexChanged(currentIndex);
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491QString QComboBox::currentText()
const
2493 Q_D(
const QComboBox);
2495 return d->lineEdit->text();
2496 if (d->currentIndex.isValid())
2497 return d->itemText(d->currentIndex);
2502
2503
2504
2505
2506
2507
2508
2509QVariant QComboBox::currentData(
int role)
const
2511 Q_D(
const QComboBox);
2512 return d->currentIndex.data(role);
2516
2517
2518QString QComboBox::itemText(
int index)
const
2520 Q_D(
const QComboBox);
2521 QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
2522 return d->itemText(mi);
2526
2527
2528QIcon QComboBox::itemIcon(
int index)
const
2530 Q_D(
const QComboBox);
2531 QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
2532 return d->itemIcon(mi);
2536
2537
2538
2539QVariant QComboBox::itemData(
int index,
int role)
const
2541 Q_D(
const QComboBox);
2542 QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
2543 return d->model->data(mi, role);
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572void QComboBox::insertItem(
int index,
const QIcon &icon,
const QString &text,
const QVariant &userData)
2575 int itemCount = count();
2576 index = qBound(0, index, itemCount);
2577 if (index >= d->maxCount)
2582 if (QStandardItemModel *m = qobject_cast<QStandardItemModel*>(d->model)) {
2583 QStandardItem *item =
new QStandardItem(text);
2584 if (!icon.isNull()) item->setData(icon, Qt::DecorationRole);
2585 if (userData.isValid()) item->setData(userData, Qt::UserRole);
2586 m->insertRow(index, item);
2589 d->inserting =
true;
2590 if (d->model->insertRows(index, 1, d->root)) {
2591 QModelIndex item = d->model->index(index, d->modelColumn, d->root);
2592 if (icon.isNull() && !userData.isValid()) {
2593 d->model->setData(item, text, Qt::EditRole);
2595 QMap<
int, QVariant> values;
2596 if (!text.isNull()) values.insert(Qt::EditRole, text);
2597 if (!icon.isNull()) values.insert(Qt::DecorationRole, icon);
2598 if (userData.isValid()) values.insert(Qt::UserRole, userData);
2599 if (!values.isEmpty()) d->model->setItemData(item, values);
2601 d->inserting =
false;
2602 d->rowsInserted(d->root, index, index);
2605 d->inserting =
false;
2609 if (itemCount > d->maxCount)
2610 d->model->removeRows(itemCount - 1, itemCount - d->maxCount, d->root);
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623void QComboBox::insertItems(
int index,
const QStringList &list)
2628 index = qBound(0, index, count());
2629 int insertCount = qMin(d->maxCount - index, list.size());
2630 if (insertCount <= 0)
2634 if (QStandardItemModel *m = qobject_cast<QStandardItemModel*>(d->model)) {
2635 QList<QStandardItem *> items;
2636 items.reserve(insertCount);
2637 QStandardItem *hiddenRoot = m->invisibleRootItem();
2638 for (
int i = 0; i < insertCount; ++i)
2639 items.append(
new QStandardItem(list.at(i)));
2640 hiddenRoot->insertRows(index, items);
2642 d->inserting =
true;
2643 if (d->model->insertRows(index, insertCount, d->root)) {
2645 for (
int i = 0; i < insertCount; ++i) {
2646 item = d->model->index(i+index, d->modelColumn, d->root);
2647 d->model->setData(item, list.at(i), Qt::EditRole);
2649 d->inserting =
false;
2650 d->rowsInserted(d->root, index, index + insertCount - 1);
2652 d->inserting =
false;
2657 if (mc > d->maxCount)
2658 d->model->removeRows(d->maxCount, mc - d->maxCount, d->root);
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672void QComboBox::insertSeparator(
int index)
2675 int itemCount = count();
2676 index = qBound(0, index, itemCount);
2677 if (index >= d->maxCount)
2679 insertItem(index, QIcon(), QString());
2680 QComboBoxDelegate::setSeparator(d->model, d->model->index(index, 0, d->root));
2684
2685
2686
2687
2688
2689void QComboBox::removeItem(
int index)
2692 if (index < 0 || index >= count())
2694 d->model->removeRows(index, 1, d->root);
2698
2699
2700void QComboBox::setItemText(
int index,
const QString &text)
2702 Q_D(
const QComboBox);
2703 QModelIndex item = d->model->index(index, d->modelColumn, d->root);
2704 if (item.isValid()) {
2705 d->model->setData(item, text, Qt::EditRole);
2710
2711
2712void QComboBox::setItemIcon(
int index,
const QIcon &icon)
2714 Q_D(
const QComboBox);
2715 QModelIndex item = d->model->index(index, d->modelColumn, d->root);
2716 if (item.isValid()) {
2717 d->model->setData(item, icon, Qt::DecorationRole);
2722
2723
2724
2725void QComboBox::setItemData(
int index,
const QVariant &value,
int role)
2727 Q_D(
const QComboBox);
2728 QModelIndex item = d->model->index(index, d->modelColumn, d->root);
2729 if (item.isValid()) {
2730 d->model->setData(item, value, role);
2735
2736
2737QAbstractItemView *QComboBox::view()
const
2739 Q_D(
const QComboBox);
2740 return const_cast<QComboBoxPrivate*>(d)->viewContainer()->itemView();
2744
2745
2746
2747
2748
2749
2750
2751
2752void QComboBox::setView(QAbstractItemView *itemView)
2755 if (Q_UNLIKELY(!itemView)) {
2756 qWarning(
"QComboBox::setView: cannot set a 0 view");
2760 if (itemView->model() != d->model) {
2761 d->disconnectModel();
2762 itemView->setModel(d->model);
2765 d->viewContainer()->setItemView(itemView);
2769
2770
2771QSize QComboBox::minimumSizeHint()
const
2773 Q_D(
const QComboBox);
2774 return d->recomputeSizeHint(d->minimumSizeHint);
2778
2779
2780
2781
2782
2783
2784QSize QComboBox::sizeHint()
const
2786 Q_D(
const QComboBox);
2787 return d->recomputeSizeHint(d->sizeHint);
2791void QComboBoxPrivate::cleanupNativePopup()
2793 if (!m_platformMenu)
2796 m_platformMenu->setVisible(
false);
2797 int count =
int(m_platformMenu->tag());
2798 for (
int i = 0; i < count; ++i)
2799 m_platformMenu->menuItemAt(i)->deleteLater();
2801 delete m_platformMenu;
2802 m_platformMenu =
nullptr;
2806
2807
2808
2809
2810
2811bool QComboBoxPrivate::showNativePopup()
2815 cleanupNativePopup();
2817 QPlatformTheme *theme = QGuiApplicationPrivate::instance()->platformTheme();
2818 m_platformMenu = theme->createPlatformMenu();
2819 if (!m_platformMenu)
2822 int itemsCount = q->count();
2823 m_platformMenu->setTag(quintptr(itemsCount));
2825 QPlatformMenuItem *currentItem =
nullptr;
2826 int currentIndex = q->currentIndex();
2828 for (
int i = 0; i < itemsCount; ++i) {
2829 QPlatformMenuItem *item = theme->createPlatformMenuItem();
2830 QModelIndex rowIndex = model->index(i, modelColumn, root);
2831 QVariant textVariant = model->data(rowIndex, Qt::EditRole);
2832 item->setText(textVariant.toString());
2833 QVariant iconVariant = model->data(rowIndex, Qt::DecorationRole);
2834 const Qt::ItemFlags itemFlags = model->flags(rowIndex);
2835 if (iconVariant.canConvert<QIcon>())
2836 item->setIcon(iconVariant.value<QIcon>());
2837 item->setCheckable(
true);
2838 item->setChecked(i == currentIndex);
2839 item->setEnabled(itemFlags & Qt::ItemIsEnabled);
2840 if (!currentItem || i == currentIndex)
2843 IndexSetter setter = { i, q };
2844 QObject::connect(item, &QPlatformMenuItem::activated, q, setter);
2846 m_platformMenu->insertMenuItem(item, 0);
2847 m_platformMenu->syncMenuItem(item);
2850 QWindow *tlw = q->window()->windowHandle();
2851 m_platformMenu->setFont(q->font());
2852 m_platformMenu->setMinimumWidth(q->rect().width());
2853 QPoint offset = QPoint(0, 7);
2854 if (q->testAttribute(Qt::WA_MacSmallSize))
2855 offset = QPoint(-1, 7);
2856 else if (q->testAttribute(Qt::WA_MacMiniSize))
2857 offset = QPoint(-2, 6);
2859 [[maybe_unused]] QPointer<QComboBox> guard(q);
2860 const QRect targetRect = QRect(tlw->mapFromGlobal(q->mapToGlobal(offset)), QSize());
2861 m_platformMenu->showPopup(tlw, QHighDpi::toNativePixels(targetRect, tlw), currentItem);
2867 QMouseEvent mouseReleased(QEvent::MouseButtonRelease, q->pos(), q->mapToGlobal(QPoint(0, 0)),
2868 Qt::LeftButton, Qt::MouseButtons(Qt::LeftButton), {});
2869 QCoreApplication::sendEvent(q, &mouseReleased);
2879
2880
2881
2882
2883
2884
2885
2886
2887void QComboBox::showPopup()
2893 QStyle *
const style =
this->style();
2894 QStyleOptionComboBox opt;
2895 initStyleOption(&opt);
2896 const bool usePopup = style->styleHint(QStyle::SH_ComboBox_Popup, &opt,
this);
2901 || (qobject_cast<QComboBoxListView*>(view())
2902 && qobject_cast<QComboMenuDelegate*>(view()->itemDelegate())))
2903 && style->styleHint(QStyle::SH_ComboBox_UseNativePopup, &opt,
this)
2904 && d->showNativePopup())
2908 QComboBoxPrivateContainer* container = d->viewContainer();
2909 QRect listRect(style->subControlRect(QStyle::CC_ComboBox, &opt,
2910 QStyle::SC_ComboBoxListBoxPopup,
this));
2911 QRect screen = d->popupGeometry(mapToGlobal(listRect.topLeft()));
2913 QPoint below = mapToGlobal(listRect.bottomLeft());
2914 int belowHeight = screen.bottom() - below.y();
2915 QPoint above = mapToGlobal(listRect.topLeft());
2916 int aboveHeight = above.y() - screen.y();
2917 bool boundToScreen = !window()->testAttribute(Qt::WA_DontShowOnScreen);
2918 const auto listView = qobject_cast<QListView *>(d->viewContainer()->itemView());
2923 QStack<QModelIndex> toCheck;
2924 toCheck.push(view()->rootIndex());
2925#if QT_CONFIG(treeview)
2926 QTreeView *treeView = qobject_cast<QTreeView*>(view());
2927 if (treeView && treeView->header() && !treeView->header()->isHidden())
2928 listHeight += treeView->header()->height();
2930 while (!toCheck.isEmpty()) {
2931 QModelIndex parent = toCheck.pop();
2932 for (
int i = 0, end = d->model->rowCount(parent); i < end; ++i) {
2933 if (listView && listView->isRowHidden(i))
2935 QModelIndex idx = d->model->index(i, d->modelColumn, parent);
2938 listHeight += view()->visualRect(idx).height();
2939#if QT_CONFIG(treeview)
2940 if (d->model->hasChildren(idx) && treeView && treeView->isExpanded(idx))
2944 if (!usePopup && count >= d->maxVisibleItems) {
2951 listHeight += (count - 1) * container->spacing();
2952 listRect.setHeight(listHeight);
2957 int heightMargin = container->topMargin() + container->bottomMargin();
2960 const QMargins cm = container->contentsMargins();
2961 heightMargin += cm.top() + cm.bottom();
2964 const QMargins vm = view()->contentsMargins();
2965 heightMargin += vm.top() + vm.bottom();
2966 heightMargin +=
static_cast<QAbstractScrollAreaPrivate *>(QObjectPrivate::get(view()))->top;
2967 heightMargin +=
static_cast<QAbstractScrollAreaPrivate *>(QObjectPrivate::get(view()))->bottom;
2969 listRect.setHeight(listRect.height() + heightMargin);
2974 listRect.setHeight(listRect.height() + style->pixelMetric(QStyle::PM_MenuVMargin, &opt,
this) * 2);
2978 const int diff = d->computeWidthHint() - width();
2980 listRect.setWidth(listRect.width() + diff);
2984 container->layout()->activate();
2986 listRect.setSize( listRect.size().expandedTo(container->minimumSize())
2987 .boundedTo(container->maximumSize()));
2990 if (boundToScreen) {
2991 if (listRect.width() > screen.width() )
2992 listRect.setWidth(screen.width());
2993 if (mapToGlobal(listRect.bottomRight()).x() > screen.right()) {
2994 below.setX(screen.x() + screen.width() - listRect.width());
2995 above.setX(screen.x() + screen.width() - listRect.width());
2997 if (mapToGlobal(listRect.topLeft()).x() < screen.x() ) {
2998 below.setX(screen.x());
2999 above.setX(screen.x());
3005 listRect.moveLeft(above.x());
3014 view()->scrollToTop();
3015 const QRect currentItemRect = view()->visualRect(view()->currentIndex());
3016 const int offset = listRect.top() - currentItemRect.top();
3017 listRect.moveTop(above.y() + offset - listRect.top());
3022 const int height = !boundToScreen ? listRect.height() : qMin(listRect.height(), screen.height());
3023 listRect.setHeight(height);
3025 if (boundToScreen) {
3026 if (listRect.top() < screen.top())
3027 listRect.moveTop(screen.top());
3028 if (listRect.bottom() > screen.bottom())
3029 listRect.moveBottom(screen.bottom());
3031 }
else if (!boundToScreen || listRect.height() <= belowHeight) {
3032 listRect.moveTopLeft(below);
3033 }
else if (listRect.height() <= aboveHeight) {
3034 listRect.moveBottomLeft(above);
3035 }
else if (belowHeight >= aboveHeight) {
3036 listRect.setHeight(belowHeight);
3037 listRect.moveTopLeft(below);
3039 listRect.setHeight(aboveHeight);
3040 listRect.moveBottomLeft(above);
3044 QGuiApplication::inputMethod()->reset();
3047 const QScrollBar *sb = view()->horizontalScrollBar();
3048 const auto needHorizontalScrollBar = [
this, sb]{
3049 const Qt::ScrollBarPolicy policy = view()->horizontalScrollBarPolicy();
3050 return (policy == Qt::ScrollBarAsNeeded || policy == Qt::ScrollBarAlwaysOn)
3051 && sb->minimum() < sb->maximum();
3053 const bool neededHorizontalScrollBar = needHorizontalScrollBar();
3054 if (neededHorizontalScrollBar)
3055 listRect.adjust(0, 0, 0, sb->height());
3060 container->hideScrollers();
3061 container->setGeometry(listRect);
3064 const bool updatesEnabled = container->updatesEnabled();
3067#if QT_CONFIG(effects)
3068 bool scrollDown = (listRect.topLeft() == below);
3069 if (QApplication::isEffectEnabled(Qt::UI_AnimateCombo)
3070 && !style->styleHint(QStyle::SH_ComboBox_Popup, &opt,
this) && !window()->testAttribute(Qt::WA_DontShowOnScreen))
3071 qScrollEffect(container, scrollDown ? QEffects::DownScroll : QEffects::UpScroll, 150);
3080 container->setUpdatesEnabled(
false);
3083 bool startTimer = !container->isVisible();
3085 container->create();
3086 if (QWindow *containerWindow = qt_widget_private(container)->windowHandle(QWidgetPrivate::WindowHandleMode::TopLevel)) {
3087 QScreen *currentScreen = d->associatedScreen();
3088 if (currentScreen && !currentScreen->virtualSiblings().contains(containerWindow->screen())) {
3089 containerWindow->setScreen(currentScreen);
3098#if QT_CONFIG(wayland)
3099 if (
auto waylandWindow =
dynamic_cast<QNativeInterface::Private::QWaylandWindow*>(container->windowHandle()->handle())) {
3100 const QRect popup(style->subControlRect(QStyle::CC_ComboBox, &opt,
3101 QStyle::SC_ComboBoxListBoxPopup,
this));
3102 const QRect controlGeometry = QRect(mapTo(window(), popup.topLeft()), popup.size());
3103 waylandWindow->setParentControlGeometry(controlGeometry);
3104 waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::ComboBox);
3109 if (!neededHorizontalScrollBar && needHorizontalScrollBar()) {
3110 listRect.adjust(0, 0, 0, sb->height());
3111 container->setGeometry(listRect);
3114 container->updateScrollers();
3117 view()->scrollTo(view()->currentIndex(),
3118 style->styleHint(QStyle::SH_ComboBox_Popup, &opt,
this)
3119 ? QAbstractItemView::PositionAtCenter
3120 : QAbstractItemView::EnsureVisible);
3123 container->setUpdatesEnabled(updatesEnabled);
3126 container->update();
3128 container->popupTimer.start();
3129 container->maybeIgnoreMouseButtonRelease =
true;
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143void QComboBox::hidePopup()
3148 d->hidingPopup =
true;
3150 auto resetHidingPopup = qScopeGuard([d]{
3151 d->hidingPopup =
false;
3154 if (!d->container || !d->container->isVisible())
3157#if QT_CONFIG(effects)
3158 QItemSelectionModel *selectionModel = d->container->itemView()
3159 ? d->container->itemView()->selectionModel() :
nullptr;
3161 if (style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem,
nullptr,
this) &&
3162 selectionModel && selectionModel->hasSelection()) {
3163 const QItemSelection selection = selectionModel->selection();
3165 QTimer::singleShot(0, d->container, [d, selection, selectionModel]{
3166 QSignalBlocker modelBlocker(d->model);
3167 QSignalBlocker viewBlocker(d->container->itemView());
3168 QSignalBlocker containerBlocker(d->container);
3171 selectionModel->select(selection, QItemSelectionModel::Toggle);
3172 QTimer::singleShot(60, d->container, [d, selection, selectionModel]{
3173 QSignalBlocker modelBlocker(d->model);
3174 QSignalBlocker viewBlocker(d->container->itemView());
3175 QSignalBlocker containerBlocker(d->container);
3176 selectionModel->select(selection, QItemSelectionModel::Toggle);
3177 QTimer::singleShot(20, d->container, [d] {
3189void QComboBoxPrivate::doHidePopup()
3191 if (container && container->isVisible())
3197void QComboBoxPrivate::updateCurrentText(
const QString &text)
3199 if (text == currentText)
3203 emit q_func()->currentTextChanged(text);
3207
3208
3209
3210
3211
3212void QComboBox::clear()
3215 d->model->removeRows(0, d->model->rowCount(d->root), d->root);
3216#if QT_CONFIG(accessibility)
3217 QAccessibleValueChangeEvent event(
this, QString());
3218 QAccessible::updateAccessibility(&event);
3223
3224
3225void QComboBox::clearEditText()
3229 d->lineEdit->clear();
3230#if QT_CONFIG(accessibility)
3231 QAccessibleValueChangeEvent event(
this, QString());
3232 QAccessible::updateAccessibility(&event);
3237
3238
3239void QComboBox::setEditText(
const QString &text)
3243 d->lineEdit->setText(text);
3244#if QT_CONFIG(accessibility)
3245 QAccessibleValueChangeEvent event(
this, text);
3246 QAccessible::updateAccessibility(&event);
3251
3252
3253void QComboBox::focusInEvent(QFocusEvent *e)
3258 d->lineEdit->event(e);
3259#if QT_CONFIG(completer)
3260 if (d->lineEdit->completer())
3261 d->lineEdit->completer()->setWidget(
this);
3267
3268
3269void QComboBox::focusOutEvent(QFocusEvent *e)
3274 d->lineEdit->event(e);
3278void QComboBox::changeEvent(QEvent *e)
3281 switch (e->type()) {
3282 case QEvent::StyleChange:
3284 d->container->updateStyleSettings();
3285 d->updateDelegate();
3288 case QEvent::MacSizeChange:
3290 d->sizeHint = QSize();
3291 d->minimumSizeHint = QSize();
3292 d->updateLayoutDirection();
3294 d->updateLineEditGeometry();
3295 d->setLayoutItemMargins(QStyle::SE_ComboBoxLayoutItem);
3297 if (e->type() == QEvent::MacSizeChange) {
3298 QPlatformTheme::Font f = QPlatformTheme::SystemFont;
3299 if (testAttribute(Qt::WA_MacSmallSize))
3300 f = QPlatformTheme::SmallFont;
3301 else if (testAttribute(Qt::WA_MacMiniSize))
3302 f = QPlatformTheme::MiniFont;
3303 if (
const QFont *platformFont = QApplicationPrivate::platformTheme()->font(f)) {
3305 f.setPointSizeF(platformFont->pointSizeF());
3311 case QEvent::EnabledChange:
3315 case QEvent::PaletteChange: {
3316 d->updateViewContainerPaletteAndOpacity();
3319 case QEvent::FontChange: {
3320 d->sizeHint = QSize();
3321 d->viewContainer()->setFont(font());
3322 d->viewContainer()->itemView()->doItemsLayout();
3324 d->updateLineEditGeometry();
3330 QWidget::changeEvent(e);
3334
3335
3336void QComboBox::resizeEvent(QResizeEvent *)
3339 d->updateLineEditGeometry();
3343
3344
3345void QComboBox::paintEvent(QPaintEvent *)
3348 QStylePainter painter(
this);
3349 painter.setPen(palette().color(QPalette::Text));
3352 QStyleOptionComboBox opt;
3353 initStyleOption(&opt);
3354 painter.drawComplexControl(QStyle::CC_ComboBox, opt);
3356 if (currentIndex() < 0 && !placeholderText().isEmpty()) {
3357 opt.palette.setBrush(QPalette::ButtonText, opt.palette.placeholderText());
3358 opt.currentText = placeholderText();
3362 if (itemDelegate() && labelDrawingMode() == QComboBox::LabelDrawingMode::UseDelegate) {
3363 QStyleOptionViewItem itemOption;
3364 d->initViewItemOption(&itemOption);
3365 itemOption.rect = style()->subControlRect(QStyle::CC_ComboBox, &opt,
3366 QStyle::SC_ComboBoxEditField,
this);
3367 itemDelegate()->paint(&painter, itemOption, d->currentIndex);
3370 painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
3375
3376
3377void QComboBox::showEvent(QShowEvent *e)
3380 if (!d->shownOnce && d->sizeAdjustPolicy == QComboBox::AdjustToContentsOnFirstShow) {
3381 d->sizeHint = QSize();
3384 d->shownOnce =
true;
3385 QWidget::showEvent(e);
3389
3390
3391void QComboBox::hideEvent(QHideEvent *)
3397
3398
3399bool QComboBox::event(QEvent *event)
3402 switch(event->type()) {
3403 case QEvent::LayoutDirectionChange:
3404 case QEvent::ApplicationLayoutDirectionChange:
3405 d->updateLayoutDirection();
3406 d->updateLineEditGeometry();
3408 case QEvent::HoverEnter:
3409 case QEvent::HoverLeave:
3410 case QEvent::HoverMove:
3411 if (
const QHoverEvent *he =
static_cast<
const QHoverEvent *>(event))
3412 d->updateHoverControl(he->position().toPoint());
3414 case QEvent::ShortcutOverride:
3416 return d->lineEdit->event(event);
3421 return QWidget::event(event);
3425
3426
3427void QComboBox::mousePressEvent(QMouseEvent *e)
3430 if (!QGuiApplication::styleHints()->setFocusOnTouchRelease())
3431 d->showPopupFromMouseEvent(e);
3434void QComboBoxPrivate::showPopupFromMouseEvent(QMouseEvent *e)
3437 QStyleOptionComboBox opt;
3438 q->initStyleOption(&opt);
3439 QStyle::SubControl sc = q->style()->hitTestComplexControl(QStyle::CC_ComboBox, &opt, e->position().toPoint(), q);
3441 if (e->button() == Qt::LeftButton
3442 && !(sc == QStyle::SC_None && e->type() == QEvent::MouseButtonRelease)
3443 && (sc == QStyle::SC_ComboBoxArrow || !q->isEditable())
3444 && !viewContainer()->isVisible()) {
3445 if (sc == QStyle::SC_ComboBoxArrow)
3446 updateArrow(QStyle::State_Sunken);
3449 viewContainer()->initialClickPosition = q->mapToGlobal(e->position());
3450 QPointer<QComboBox> guard = q;
3457 if (viewContainer()) {
3458 viewContainer()->blockMouseReleaseTimer.start(QApplication::doubleClickInterval());
3459 viewContainer()->maybeIgnoreMouseButtonRelease =
false;
3467
3468
3469void QComboBox::mouseReleaseEvent(QMouseEvent *e)
3472 d->updateArrow(QStyle::State_None);
3473 if (QGuiApplication::styleHints()->setFocusOnTouchRelease() && hasFocus())
3474 d->showPopupFromMouseEvent(e);
3478
3479
3480void QComboBox::keyPressEvent(QKeyEvent *e)
3484#if QT_CONFIG(completer)
3485 if (
const auto *cmpltr = completer()) {
3486 const auto *popup = QCompleterPrivate::get(cmpltr)->popup;
3487 if (popup && popup->isVisible()) {
3489 d->lineEdit->event(e);
3495 enum Move { NoMove=0 , MoveUp , MoveDown , MoveFirst , MoveLast};
3498 int newIndex = currentIndex();
3500 bool pressLikeButton = !d->lineEdit;
3501 auto key = e->key();
3502 if (pressLikeButton) {
3503 const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()
3504 ->themeHint(QPlatformTheme::ButtonPressKeys)
3505 .value<QList<Qt::Key>>();
3506 if (buttonPressKeys.contains(key)) {
3514 if (e->modifiers() & Qt::ControlModifier)
3517 case Qt::Key_PageUp:
3521 if (e->modifiers() & Qt::AltModifier) {
3524 }
else if (e->modifiers() & Qt::ControlModifier)
3527 case Qt::Key_PageDown:
3539 if (!e->modifiers()) {
3545 case Qt::Key_Return:
3546 case Qt::Key_Escape:
3551#if QT_CONFIG(shortcut)
3552 if (d->container && d->container->isVisible() && e->matches(QKeySequence::Cancel)) {
3559 const auto text = e->text();
3560 if (!text.isEmpty() && text.at(0).isPrint())
3561 d->keyboardSearchString(text);
3567 const int rowCount = count();
3569 if (move != NoMove) {
3577 while (newIndex < rowCount && !(d->model->index(newIndex, d->modelColumn, d->root).flags() & Qt::ItemIsEnabled))
3581 newIndex = rowCount;
3585 while ((newIndex >= 0) && !(d->model->flags(d->model->index(newIndex,d->modelColumn,d->root)) & Qt::ItemIsEnabled))
3593 if (newIndex >= 0 && newIndex < rowCount && newIndex != currentIndex()) {
3594 setCurrentIndex(newIndex);
3595 d->emitActivated(d->currentIndex);
3597 }
else if (d->lineEdit) {
3598 d->lineEdit->event(e);
3604
3605
3606void QComboBox::keyReleaseEvent(QKeyEvent *e)
3610 d->lineEdit->event(e);
3612 QWidget::keyReleaseEvent(e);
3616
3617
3618#if QT_CONFIG(wheelevent)
3619void QComboBox::wheelEvent(QWheelEvent *e)
3622 QStyleOptionComboBox opt;
3623 initStyleOption(&opt);
3624 if (style()->styleHint(QStyle::SH_ComboBox_AllowWheelScrolling, &opt,
this) &&
3625 !d->viewContainer()->isVisible()) {
3626 const int rowCount = count();
3627 int newIndex = currentIndex();
3628 int delta = e->angleDelta().y();
3632 while ((newIndex >= 0) && !(d->model->flags(d->model->index(newIndex,d->modelColumn,d->root)) & Qt::ItemIsEnabled))
3634 }
else if (delta < 0) {
3636 while (newIndex < rowCount && !(d->model->index(newIndex, d->modelColumn, d->root).flags() & Qt::ItemIsEnabled))
3640 if (newIndex >= 0 && newIndex < rowCount && newIndex != currentIndex()) {
3641 setCurrentIndex(newIndex);
3642 d->emitActivated(d->currentIndex);
3651#ifndef QT_NO_CONTEXTMENU
3653
3654
3655void QComboBox::contextMenuEvent(QContextMenuEvent *e)
3659 Qt::ContextMenuPolicy p = d->lineEdit->contextMenuPolicy();
3660 d->lineEdit->setContextMenuPolicy(Qt::DefaultContextMenu);
3661 d->lineEdit->event(e);
3662 d->lineEdit->setContextMenuPolicy(p);
3667void QComboBoxPrivate::keyboardSearchString(
const QString &text)
3670 QAbstractItemView *view = viewContainer()->itemView();
3671 view->setCurrentIndex(currentIndex);
3672 int currentRow = view->currentIndex().row();
3673 view->keyboardSearch(text);
3674 if (currentRow != view->currentIndex().row()) {
3675 setCurrentIndex(view->currentIndex());
3676 emitActivated(currentIndex);
3680void QComboBoxPrivate::modelChanged()
3684 if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
3686 adjustComboBoxSize();
3687 q->updateGeometry();
3692
3693
3694void QComboBox::inputMethodEvent(QInputMethodEvent *e)
3698 d->lineEdit->event(e);
3700 if (!e->commitString().isEmpty())
3701 d->keyboardSearchString(e->commitString());
3708
3709
3710QVariant QComboBox::inputMethodQuery(Qt::InputMethodQuery query)
const
3712 Q_D(
const QComboBox);
3714 return d->lineEdit->inputMethodQuery(query);
3715 return QWidget::inputMethodQuery(query);
3719
3720QVariant QComboBox::inputMethodQuery(Qt::InputMethodQuery query,
const QVariant &argument)
const
3722 Q_D(
const QComboBox);
3724 return d->lineEdit->inputMethodQuery(query, argument);
3725 return QWidget::inputMethodQuery(query);
3729
3730
3731
3732
3733
3734
3737
3738
3739
3740
3741
3742
3743
3746
3747
3748
3749
3750
3753
3754
3755
3756
3757
3760
3761
3762
3763
3764
3765
3766
3767bool QComboBox::hasFrame()
const
3769 Q_D(
const QComboBox);
3774void QComboBox::setFrame(
bool enable)
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795int QComboBox::modelColumn()
const
3797 Q_D(
const QComboBox);
3798 return d->modelColumn;
3801void QComboBox::setModelColumn(
int visibleColumn)
3804 d->modelColumn = visibleColumn;
3805 QListView *lv = qobject_cast<QListView *>(d->viewContainer()->itemView());
3807 lv->setModelColumn(visibleColumn);
3808#if QT_CONFIG(completer)
3809 if (d->lineEdit && d->lineEdit->completer())
3810 d->lineEdit->completer()->setCompletionColumn(visibleColumn);
3812 setCurrentIndex(currentIndex());
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841QComboBox::LabelDrawingMode QComboBox::labelDrawingMode()
const
3843 Q_D(
const QComboBox);
3844 return d->labelDrawingMode;
3847void QComboBox::setLabelDrawingMode(LabelDrawingMode drawingLabel)
3850 if (d->labelDrawingMode != drawingLabel) {
3851 d->labelDrawingMode = drawingLabel;
3858#include "moc_qcombobox.cpp"
3859#include "moc_qcombobox_p.cpp"
void initViewItemOption(QStyleOptionViewItem *option) const override
~QComboBoxListView() override
Combined button and popup list for selecting options.