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 bool usePopup = combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo);
873#if QT_CONFIG(scrollbar)
875 view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
878 combo->style()->styleHint(QStyle::SH_ComboBox_ListMouseTracking_Current, &opt, combo) ||
879 combo->style()->styleHint(QStyle::SH_ComboBox_ListMouseTracking_Active, &opt, combo)
881 view->setMouseTracking(
true);
883 view->setSelectionMode(QAbstractItemView::SingleSelection);
884 view->setFrameStyle(QFrame::NoFrame);
885 view->setLineWidth(0);
886 view->setEditTriggers(QAbstractItemView::NoEditTriggers);
887#if QT_CONFIG(scrollbar)
888 connect(view->verticalScrollBar(), &QScrollBar::valueChanged,
889 this, &QComboBoxPrivateContainer::updateScrollers);
890 connect(view->verticalScrollBar(), &QScrollBar::rangeChanged,
891 this, &QComboBoxPrivateContainer::updateScrollers);
893 connect(view, &QAbstractItemView::destroyed,
894 this, &QComboBoxPrivateContainer::viewDestroyed);
898
899
900int QComboBoxPrivateContainer::topMargin()
const
902 if (
const QListView *lview = qobject_cast<
const QListView*>(view))
903 return lview->spacing();
904#if QT_CONFIG(tableview)
905 if (
const QTableView *tview = qobject_cast<
const QTableView*>(view))
906 return tview->showGrid() ? 1 : 0;
912
913
914int QComboBoxPrivateContainer::spacing()
const
916 QListView *lview = qobject_cast<QListView*>(view);
918 return 2 * lview->spacing();
919#if QT_CONFIG(tableview)
920 QTableView *tview = qobject_cast<QTableView*>(view);
922 return tview->showGrid() ? 1 : 0;
927void QComboBoxPrivateContainer::updateTopBottomMargin()
929 if (!layout() || layout()->count() < 1)
932 QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(layout());
936 const QStyleOptionComboBox opt = comboStyleOption();
937 const bool usePopup = combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo);
938 const int margin = usePopup ? combo->style()->pixelMetric(QStyle::PM_MenuVMargin, &opt, combo) : 0;
940 QSpacerItem *topSpacer = boxLayout->itemAt(0)->spacerItem();
942 topSpacer->changeSize(0, margin, QSizePolicy::Minimum, QSizePolicy::Fixed);
944 QSpacerItem *bottomSpacer = boxLayout->itemAt(boxLayout->count() - 1)->spacerItem();
945 if (bottomSpacer && bottomSpacer != topSpacer)
946 bottomSpacer->changeSize(0, margin, QSizePolicy::Minimum, QSizePolicy::Fixed);
948 boxLayout->invalidate();
951void QComboBoxPrivateContainer::updateStyleSettings()
954 QStyleOptionComboBox opt = comboStyleOption();
955 view->setMouseTracking(combo->style()->styleHint(QStyle::SH_ComboBox_ListMouseTracking, &opt, combo) ||
956 combo->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, combo));
957 setFrameStyle(combo->style()->styleHint(QStyle::SH_ComboBox_PopupFrameStyle, &opt, combo));
958 updateTopBottomMargin();
961void QComboBoxPrivateContainer::changeEvent(QEvent *e)
963 if (e->type() == QEvent::StyleChange)
964 updateStyleSettings();
966 QFrame::changeEvent(e);
970bool QComboBoxPrivateContainer::eventFilter(QObject *o, QEvent *e)
973 case QEvent::ShortcutOverride: {
974 QKeyEvent *keyEvent =
static_cast<QKeyEvent*>(e);
975 switch (keyEvent->key()) {
978 if (view->currentIndex().isValid() && view->currentIndex().flags().testFlag(Qt::ItemIsEnabled)) {
981 emit itemSelected(view->currentIndex());
985 if (!(keyEvent->modifiers() & Qt::AltModifier))
991 emit itemSelected(view->currentIndex());
994#if QT_CONFIG(shortcut)
995 if (keyEvent->matches(QKeySequence::Cancel) && isVisible()) {
1004 case QEvent::MouseMove:
1006 QMouseEvent *m =
static_cast<QMouseEvent *>(e);
1007 QWidget *widget =
static_cast<QWidget *>(o);
1008 const QPoint vector = widget->mapToGlobal(m->position()).toPoint() - initialClickPosition;
1009 if (vector.manhattanLength() > 9 && blockMouseReleaseTimer.isActive())
1010 blockMouseReleaseTimer.stop();
1011 if (combo->style()->styleHint(QStyle::SH_ComboBox_ListMouseTracking_Current,
nullptr, combo)) {
1012 QModelIndex indexUnderMouse = view->indexAt(m->position().toPoint());
1013 if (indexUnderMouse.isValid()
1014 && !QComboBoxDelegate::isSeparator(indexUnderMouse)) {
1015 view->setCurrentIndex(indexUnderMouse);
1020 case QEvent::MouseButtonPress:
1021 maybeIgnoreMouseButtonRelease =
false;
1023 case QEvent::MouseButtonRelease: {
1024 bool ignoreEvent = maybeIgnoreMouseButtonRelease && popupTimer.elapsed() < QApplication::doubleClickInterval();
1026 QMouseEvent *m =
static_cast<QMouseEvent *>(e);
1027 if (isVisible() && view->rect().contains(m->position().toPoint()) && view->currentIndex().isValid()
1028 && !blockMouseReleaseTimer.isActive() && !ignoreEvent
1029 && (view->currentIndex().flags().testFlag(Qt::ItemIsEnabled))
1030 && (view->currentIndex().flags().testFlag(Qt::ItemIsSelectable))) {
1032 emit itemSelected(view->currentIndex());
1040 return QFrame::eventFilter(o, e);
1043void QComboBoxPrivateContainer::showEvent(QShowEvent *)
1048void QComboBoxPrivateContainer::hideEvent(QHideEvent *)
1052#if QT_CONFIG(graphicsview)
1056 if (QGraphicsProxyWidget *proxy = graphicsProxyWidget())
1061void QComboBoxPrivateContainer::mousePressEvent(QMouseEvent *e)
1064 QStyleOptionComboBox opt = comboStyleOption();
1065 opt.subControls = QStyle::SC_All;
1066 opt.activeSubControls = QStyle::SC_ComboBoxArrow;
1067 QStyle::SubControl sc = combo->style()->hitTestComplexControl(QStyle::CC_ComboBox, &opt,
1068 combo->mapFromGlobal(e->globalPosition()).toPoint(),
1070 if ((combo->isEditable() && sc == QStyle::SC_ComboBoxArrow)
1071 || (!combo->isEditable() && sc != QStyle::SC_None))
1072 setAttribute(Qt::WA_NoMouseReplay);
1076void QComboBoxPrivateContainer::mouseReleaseEvent(QMouseEvent *e)
1079 if (!blockMouseReleaseTimer.isActive()) {
1085QStyleOptionComboBox QComboBoxPrivateContainer::comboStyleOption()
const
1089 QStyleOptionComboBox opt;
1090 opt.initFrom(combo);
1091 opt.subControls = QStyle::SC_All;
1092 opt.activeSubControls = QStyle::SC_None;
1093 opt.editable = combo->isEditable();
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1125
1126
1127
1128
1129
1130
1131
1132
1135
1136
1137
1138
1139
1140
1141
1142
1143
1146
1147
1148
1149
1150
1153
1154
1155
1156
1157
1158
1161
1162
1163
1164
1165
1166
1167
1168
1171
1172
1173
1174
1175
1176
1177
1178
1179
1182
1183
1184
1185QComboBox::QComboBox(QWidget *parent)
1186 : QWidget(*
new QComboBoxPrivate(), parent, { })
1193
1194
1195QComboBox::QComboBox(QComboBoxPrivate &dd, QWidget *parent)
1196 : QWidget(dd, parent, { })
1203
1204
1205
1206
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
1288void QComboBoxPrivate::init()
1296 if (!q->isEditable())
1297 q->setFocusPolicy(Qt::TabFocus);
1300 q->setFocusPolicy(Qt::WheelFocus);
1302 q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed,
1303 QSizePolicy::ComboBox));
1304 setLayoutItemMargins(QStyle::SE_ComboBoxLayoutItem);
1305 q->setModel(
new QStandardItemModel(0, 1, q));
1306 if (!q->isEditable())
1307 q->setAttribute(Qt::WA_InputMethodEnabled,
false);
1309 q->setAttribute(Qt::WA_InputMethodEnabled);
1312QComboBoxPrivateContainer* QComboBoxPrivate::viewContainer()
1318 container =
new QComboBoxPrivateContainer(
new QComboBoxListView(q), q);
1320 container->itemView()->setModel(model);
1322 container->itemView()->setTextElideMode(Qt::ElideMiddle);
1323 updateDelegate(
true);
1324 updateLayoutDirection();
1325 updateViewContainerPaletteAndOpacity();
1326 QObjectPrivate::connect(container, &QComboBoxPrivateContainer::itemSelected,
1327 this, &QComboBoxPrivate::itemSelected);
1328 QObjectPrivate::connect(container->itemView()->selectionModel(),
1329 &QItemSelectionModel::currentChanged,
1330 this, &QComboBoxPrivate::emitHighlighted);
1331 QObjectPrivate::connect(container, &QComboBoxPrivateContainer::resetButton,
1332 this, &QComboBoxPrivate::resetButton);
1337void QComboBoxPrivate::resetButton()
1339 updateArrow(QStyle::State_None);
1342void QComboBoxPrivate::dataChanged(
const QModelIndex &topLeft,
const QModelIndex &bottomRight)
1345 if (inserting || topLeft.parent() != root)
1348 if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
1350 adjustComboBoxSize();
1351 q->updateGeometry();
1354 if (currentIndex.row() >= topLeft.row() && currentIndex.row() <= bottomRight.row()) {
1355 const QString text = q->itemText(currentIndex.row());
1357 lineEdit->setText(text);
1358 updateLineEditGeometry();
1360 updateCurrentText(text);
1363#if QT_CONFIG(accessibility)
1364 QAccessibleValueChangeEvent event(q, text);
1365 QAccessible::updateAccessibility(&event);
1370void QComboBoxPrivate::rowsInserted(
const QModelIndex &parent,
int start,
int end)
1373 if (inserting || parent != root)
1376 if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
1378 adjustComboBoxSize();
1379 q->updateGeometry();
1383 if (start == 0 && (end - start + 1) == q->count() && !currentIndex.isValid() &&
1384 placeholderText.isEmpty()) {
1385#if QT_CONFIG(accessibility)
1390 if (container && container->itemView()) {
1391 QAccessibleTableModelChangeEvent event(container->itemView(),
1392 QAccessibleTableModelChangeEvent::ModelReset);
1393 QAccessible::updateAccessibility(&event);
1396 q->setCurrentIndex(0);
1398 }
else if (currentIndex.row() != indexBeforeChange) {
1400 emitCurrentIndexChanged(currentIndex);
1404void QComboBoxPrivate::updateIndexBeforeChange()
1406 indexBeforeChange = currentIndex.row();
1409void QComboBoxPrivate::rowsRemoved(
const QModelIndex &parent,
int ,
int )
1415 if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
1417 adjustComboBoxSize();
1418 q->updateGeometry();
1422 if (model->rowCount(root) == 0) {
1423 setCurrentIndex(QModelIndex());
1428 if (currentIndex.row() != indexBeforeChange) {
1429 if (!currentIndex.isValid() && q->count()) {
1430 q->setCurrentIndex(qMin(q->count() - 1, qMax(indexBeforeChange, 0)));
1434 lineEdit->setText(q->itemText(currentIndex.row()));
1435 updateLineEditGeometry();
1438 emitCurrentIndexChanged(currentIndex);
1443void QComboBoxPrivate::updateViewContainerPaletteAndOpacity()
1448 QStyleOptionComboBox opt;
1449 q->initStyleOption(&opt);
1451 if (q->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, q)) {
1453 menu.ensurePolished();
1454 container->setPalette(menu.palette());
1455 container->setWindowOpacity(menu.windowOpacity());
1459 container->setPalette(q->palette());
1460 container->setWindowOpacity(1.0);
1463 lineEdit->setPalette(q->palette());
1466void QComboBoxPrivate::updateFocusPolicy()
1472 if (q->isEditable())
1473 q->setFocusPolicy(Qt::WheelFocus);
1475 q->setFocusPolicy(Qt::TabFocus);
1480
1481
1482
1483
1484
1485
1486void QComboBox::initStyleOption(QStyleOptionComboBox *option)
const
1491 Q_D(
const QComboBox);
1492 option->initFrom(
this);
1493 option->editable = isEditable();
1494 option->frame = d->frame;
1495 if (hasFocus() && !option->editable)
1496 option->state |= QStyle::State_Selected;
1497 option->subControls = QStyle::SC_All;
1498 if (d->arrowState == QStyle::State_Sunken) {
1499 option->activeSubControls = QStyle::SC_ComboBoxArrow;
1500 option->state |= d->arrowState;
1502 option->activeSubControls = d->hoverControl;
1504 option->currentText = currentText();
1505 if (d->currentIndex.isValid()) {
1506 option->currentIcon = d->itemIcon(d->currentIndex);
1507 QVariant alignment = d->model->data(d->currentIndex, Qt::TextAlignmentRole);
1508 if (alignment.isValid())
1509 option->textAlignment =
static_cast<Qt::Alignment>(alignment.toUInt());
1511 option->iconSize = iconSize();
1512 if (d->container && d->container->isVisible())
1513 option->state |= QStyle::State_On;
1516void QComboBoxPrivate::initViewItemOption(QStyleOptionViewItem *option)
const
1518 Q_Q(
const QComboBox);
1519 q->view()->initViewItemOption(option);
1521 option->index = currentIndex;
1522 option->text = q->currentText();
1523 option->icon = itemIcon(currentIndex);
1526void QComboBoxPrivate::updateLineEditGeometry()
1532 QStyleOptionComboBox opt;
1533 q->initStyleOption(&opt);
1534 QRect editRect = q->style()->subControlRect(QStyle::CC_ComboBox, &opt,
1535 QStyle::SC_ComboBoxEditField, q);
1536 if (currentIndex.isValid() && !q->itemIcon(q->currentIndex()).isNull()) {
1537 QRect comboRect(editRect);
1538 editRect.setWidth(editRect.width() - q->iconSize().width() - 4);
1539 editRect = QStyle::alignedRect(q->layoutDirection(), Qt::AlignRight,
1540 editRect.size(), comboRect);
1542 lineEdit->setGeometry(editRect);
1545Qt::MatchFlags QComboBoxPrivate::matchFlags()
const
1548 Qt::MatchFlags flags = Qt::MatchFixedString;
1549#if QT_CONFIG(completer)
1550 if (!lineEdit->completer() || lineEdit->completer()->caseSensitivity() == Qt::CaseSensitive)
1552 flags |= Qt::MatchCaseSensitive;
1557void QComboBoxPrivate::editingFinished()
1562 const auto leText = lineEdit->text();
1563 if (!leText.isEmpty() && itemText(currentIndex) != leText) {
1564#if QT_CONFIG(completer)
1565 const auto *leCompleter = lineEdit->completer();
1566 const auto *popup = leCompleter ? QCompleterPrivate::get(leCompleter)->popup :
nullptr;
1567 if (popup && popup->isVisible()) {
1572 const QItemSelectionModel *selModel = popup->selectionModel();
1573 const QModelIndex curIndex = popup->currentIndex();
1574 const bool completerIsActive = selModel && selModel->selectedIndexes().contains(curIndex);
1576 if (completerIsActive)
1580 const int index = q_func()->findText(leText, matchFlags());
1582 q->setCurrentIndex(index);
1583 emitActivated(currentIndex);
1589void QComboBoxPrivate::returnPressed()
1597 if (insertPolicy == QComboBox::NoInsert)
1600 if (lineEdit && !lineEdit->text().isEmpty()) {
1601 if (q->count() >= maxCount && !(
this->insertPolicy == QComboBox::InsertAtCurrent))
1603 lineEdit->deselect();
1604 lineEdit->end(
false);
1605 QString text = lineEdit->text();
1608 if (!duplicatesEnabled) {
1609 index = q->findText(text, matchFlags());
1611 q->setCurrentIndex(index);
1612 emitActivated(currentIndex);
1616 switch (insertPolicy) {
1617 case QComboBox::InsertAtTop:
1620 case QComboBox::InsertAtBottom:
1623 case QComboBox::InsertAtCurrent:
1624 case QComboBox::InsertAfterCurrent:
1625 case QComboBox::InsertBeforeCurrent:
1626 if (!q->count() || !currentIndex.isValid())
1628 else if (insertPolicy == QComboBox::InsertAtCurrent)
1629 q->setItemText(q->currentIndex(), text);
1630 else if (insertPolicy == QComboBox::InsertAfterCurrent)
1631 index = q->currentIndex() + 1;
1632 else if (insertPolicy == QComboBox::InsertBeforeCurrent)
1633 index = q->currentIndex();
1635 case QComboBox::InsertAlphabetically:
1637 for (
int i = 0; i < q->count(); ++i, ++index) {
1638 if (text.toLower() < q->itemText(i).toLower())
1646 q->insertItem(index, text);
1647 q->setCurrentIndex(index);
1648 emitActivated(currentIndex);
1653void QComboBoxPrivate::itemSelected(
const QModelIndex &item)
1656 if (item != currentIndex) {
1657 setCurrentIndex(item);
1658 }
else if (lineEdit) {
1659 lineEdit->selectAll();
1660 lineEdit->setText(q->itemText(currentIndex.row()));
1662 emitActivated(currentIndex);
1665void QComboBoxPrivate::emitActivated(
const QModelIndex &index)
1668 if (!index.isValid())
1670 QString text(itemText(index));
1671 emit q->activated(index.row());
1672 emit q->textActivated(text);
1675void QComboBoxPrivate::emitHighlighted(
const QModelIndex &index)
1678 if (!index.isValid())
1680 QString text(itemText(index));
1681 emit q->highlighted(index.row());
1682 emit q->textHighlighted(text);
1685void QComboBoxPrivate::emitCurrentIndexChanged(
const QModelIndex &index)
1688 const QString text = itemText(index);
1689 emit q->currentIndexChanged(index.row());
1692 updateCurrentText(text);
1693#if QT_CONFIG(accessibility)
1694 QAccessibleValueChangeEvent event(q, text);
1695 QAccessible::updateAccessibility(&event);
1699QString QComboBoxPrivate::itemText(
const QModelIndex &index)
const
1701 return index.isValid() ? model->data(index, itemRole()).toString() : QString();
1704int QComboBoxPrivate::itemRole()
const
1706 return q_func()->isEditable() ? Qt::EditRole : Qt::DisplayRole;
1710
1711
1712QComboBox::~QComboBox()
1718 d->disconnectModel();
1727 d->container->close();
1728 delete d->container;
1729 d->container =
nullptr;
1734
1735
1736
1737
1738
1739
1740
1741
1742int QComboBox::maxVisibleItems()
const
1744 Q_D(
const QComboBox);
1745 return d->maxVisibleItems;
1748void QComboBox::setMaxVisibleItems(
int maxItems)
1751 if (Q_UNLIKELY(maxItems < 0)) {
1752 qWarning(
"QComboBox::setMaxVisibleItems: "
1753 "Invalid max visible items (%d) must be >= 0", maxItems);
1756 d->maxVisibleItems = maxItems;
1760
1761
1762
1763
1764
1765int QComboBox::count()
const
1767 Q_D(
const QComboBox);
1768 return d->model->rowCount(d->root);
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783void QComboBox::setMaxCount(
int max)
1786 if (Q_UNLIKELY(max < 0)) {
1787 qWarning(
"QComboBox::setMaxCount: Invalid count (%d) must be >= 0", max);
1791 const int rowCount = count();
1793 d->model->removeRows(max, rowCount - max, d->root);
1798int QComboBox::maxCount()
const
1800 Q_D(
const QComboBox);
1805
1806
1807
1808
1809
1810
1811
1812
1813bool QComboBox::duplicatesEnabled()
const
1815 Q_D(
const QComboBox);
1816 return d->duplicatesEnabled;
1819void QComboBox::setDuplicatesEnabled(
bool enable)
1822 d->duplicatesEnabled = enable;
1826
1827
1828
1829
1830
1831
1834
1835
1836
1837
1838
1839int QComboBox::findData(
const QVariant &data,
int role, Qt::MatchFlags flags)
const
1841 Q_D(
const QComboBox);
1842 QModelIndex start = d->model->index(0, d->modelColumn, d->root);
1843 const QModelIndexList result = d->model->match(start, role, data, 1, flags);
1844 if (result.isEmpty())
1846 return result.first().row();
1850
1851
1852
1853
1854
1855
1856
1857
1858
1860QComboBox::InsertPolicy QComboBox::insertPolicy()
const
1862 Q_D(
const QComboBox);
1863 return d->insertPolicy;
1866void QComboBox::setInsertPolicy(InsertPolicy policy)
1869 d->insertPolicy = policy;
1873
1874
1875
1876
1877
1878
1879
1880
1882QComboBox::SizeAdjustPolicy QComboBox::sizeAdjustPolicy()
const
1884 Q_D(
const QComboBox);
1885 return d->sizeAdjustPolicy;
1888void QComboBox::setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy policy)
1891 if (policy == d->sizeAdjustPolicy)
1894 d->sizeAdjustPolicy = policy;
1895 d->sizeHint = QSize();
1896 d->adjustComboBoxSize();
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911int QComboBox::minimumContentsLength()
const
1913 Q_D(
const QComboBox);
1914 return d->minimumContentsLength;
1917void QComboBox::setMinimumContentsLength(
int characters)
1920 if (characters == d->minimumContentsLength || characters < 0)
1923 d->minimumContentsLength = characters;
1925 if (d->sizeAdjustPolicy == AdjustToContents
1926 || d->sizeAdjustPolicy == AdjustToMinimumContentsLengthWithIcon) {
1927 d->sizeHint = QSize();
1928 d->adjustComboBoxSize();
1934
1935
1936
1937
1938
1939
1940
1942QSize QComboBox::iconSize()
const
1944 Q_D(
const QComboBox);
1945 if (d->iconSize.isValid())
1948 int iconWidth = style()->pixelMetric(QStyle::PM_SmallIconSize,
nullptr,
this);
1949 return QSize(iconWidth, iconWidth);
1952void QComboBox::setIconSize(
const QSize &size)
1955 if (size == d->iconSize)
1958 view()->setIconSize(size);
1960 d->sizeHint = QSize();
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980void QComboBox::setPlaceholderText(
const QString &placeholderText)
1983 if (placeholderText == d->placeholderText)
1986 d->placeholderText = placeholderText;
1987 if (currentIndex() == -1) {
1988 if (d->placeholderText.isEmpty())
1997QString QComboBox::placeholderText()
const
1999 Q_D(
const QComboBox);
2000 return d->placeholderText;
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015bool QComboBox::isEditable()
const
2017 Q_D(
const QComboBox);
2018 return d->lineEdit !=
nullptr;
2022
2023
2024
2025
2026
2027
2028void QComboBoxPrivate::updateDelegate(
bool force)
2031 QStyleOptionComboBox opt;
2032 q->initStyleOption(&opt);
2033 if (q->style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, q)) {
2034 if (force || qobject_cast<QComboBoxDelegate *>(q->itemDelegate()))
2035 q->setItemDelegate(
new QComboMenuDelegate(q->view(), q));
2037 if (force || qobject_cast<QComboMenuDelegate *>(q->itemDelegate()))
2038 q->setItemDelegate(
new QComboBoxDelegate(q->view(), q));
2042QIcon QComboBoxPrivate::itemIcon(
const QModelIndex &index)
const
2044 if (!index.isValid())
2046 QVariant decoration = model->data(index, Qt::DecorationRole);
2047 if (decoration.userType() == QMetaType::QPixmap)
2048 return QIcon(qvariant_cast<QPixmap>(decoration));
2050 return qvariant_cast<QIcon>(decoration);
2053void QComboBox::setEditable(
bool editable)
2056 if (isEditable() == editable)
2059 QStyleOptionComboBox opt;
2060 initStyleOption(&opt);
2062 if (style()->styleHint(QStyle::SH_ComboBox_Popup, &opt,
this)) {
2063 d->viewContainer()->updateScrollers();
2064 view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
2066 QLineEdit *le =
new QLineEdit(
this);
2067 le->setPalette(palette());
2070 if (style()->styleHint(QStyle::SH_ComboBox_Popup, &opt,
this)) {
2071 d->viewContainer()->updateScrollers();
2072 view()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
2074 setAttribute(Qt::WA_InputMethodEnabled,
false);
2075 d->lineEdit->hide();
2076 d->lineEdit->deleteLater();
2077 d->lineEdit =
nullptr;
2080 d->updateDelegate();
2081 d->updateFocusPolicy();
2083 d->viewContainer()->updateTopBottomMargin();
2084 if (!testAttribute(Qt::WA_Resized))
2089
2090
2091
2092
2093
2094
2095
2096void QComboBox::setLineEdit(QLineEdit *edit)
2099 if (Q_UNLIKELY(!edit)) {
2100 qWarning(
"QComboBox::setLineEdit: cannot set a 0 line edit");
2104 if (edit == d->lineEdit)
2107 edit->setText(currentText());
2112 qt_widget_private(d->lineEdit)->inheritsInputMethodHints = 1;
2114 if (d->lineEdit->parent() !=
this)
2115 d->lineEdit->setParent(
this);
2116 QObjectPrivate::connect(d->lineEdit, &QLineEdit::returnPressed,
2117 d, &QComboBoxPrivate::returnPressed);
2118 QObjectPrivate::connect(d->lineEdit, &QLineEdit::editingFinished,
2119 d, &QComboBoxPrivate::editingFinished);
2120 connect(d->lineEdit, &QLineEdit::textChanged,
this, &QComboBox::editTextChanged);
2121 connect(d->lineEdit, &QLineEdit::textChanged,
this, &QComboBox::currentTextChanged);
2122 QObjectPrivate::connect(d->lineEdit, &QLineEdit::cursorPositionChanged,
2123 d, &QComboBoxPrivate::updateMicroFocus);
2124 QObjectPrivate::connect(d->lineEdit, &QLineEdit::selectionChanged,
2125 d, &QComboBoxPrivate::updateMicroFocus);
2126 QObjectPrivate::connect(d->lineEdit->d_func()->control, &QWidgetLineControl::updateMicroFocus,
2127 d, &QComboBoxPrivate::updateMicroFocus);
2128 d->lineEdit->setFrame(
false);
2129 d->lineEdit->setContextMenuPolicy(Qt::NoContextMenu);
2130 d->updateFocusPolicy();
2131 d->lineEdit->setFocusProxy(
this);
2132 d->lineEdit->setAttribute(Qt::WA_MacShowFocusRect,
false);
2134#if QT_CONFIG(completer)
2136 if (!d->lineEdit->completer()) {
2137 QCompleter *completer =
new QCompleter(d->model, d->lineEdit);
2138 completer->setCaseSensitivity(Qt::CaseInsensitive);
2139 completer->setCompletionMode(QCompleter::InlineCompletion);
2140 completer->setCompletionColumn(d->modelColumn);
2142 setCompleter(completer);
2146 setAttribute(Qt::WA_InputMethodEnabled);
2147 d->updateLayoutDirection();
2148 d->updateLineEditGeometry();
2150 d->lineEdit->show();
2156
2157
2158
2159
2160
2161QLineEdit *QComboBox::lineEdit()
const
2163 Q_D(
const QComboBox);
2167#ifndef QT_NO_VALIDATOR
2169
2170
2171
2172
2173
2174
2176void QComboBox::setValidator(
const QValidator *v)
2180 d->lineEdit->setValidator(v);
2184
2185
2186
2187
2188
2189const QValidator *QComboBox::validator()
const
2191 Q_D(
const QComboBox);
2192 return d->lineEdit ? d->lineEdit->validator() :
nullptr;
2196#if QT_CONFIG(completer)
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212void QComboBox::setCompleter(QCompleter *c)
2216 qWarning(
"Setting a QCompleter on non-editable QComboBox is not allowed.");
2219 d->lineEdit->setCompleter(c);
2221 QObjectPrivate::connect(c, QOverload<
const QModelIndex &>::of(&QCompleter::activated),
2222 d, &QComboBoxPrivate::completerActivated);
2228
2229
2230
2231
2232
2233
2234
2235QCompleter *QComboBox::completer()
const
2237 Q_D(
const QComboBox);
2238 return d->lineEdit ? d->lineEdit->completer() :
nullptr;
2244
2245
2246
2247
2248QAbstractItemDelegate *QComboBox::itemDelegate()
const
2250 return view()->itemDelegate();
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268void QComboBox::setItemDelegate(QAbstractItemDelegate *delegate)
2270 if (Q_UNLIKELY(!delegate)) {
2271 qWarning(
"QComboBox::setItemDelegate: cannot set a 0 delegate");
2274 view()->setItemDelegate(delegate);
2278
2279
2281QAbstractItemModel *QComboBox::model()
const
2283 Q_D(
const QComboBox);
2284 if (d->model == QAbstractItemModelPrivate::staticEmptyModel()) {
2285 QComboBox *that =
const_cast<QComboBox*>(
this);
2286 that->setModel(
new QStandardItemModel(0, 1, that));
2292
2293
2294
2295
2296
2297
2298
2299
2300void QComboBox::setModel(QAbstractItemModel *model)
2304 if (Q_UNLIKELY(!model)) {
2305 qWarning(
"QComboBox::setModel: cannot set a 0 model");
2309 if (model == d->model)
2312#if QT_CONFIG(completer)
2313 if (d->lineEdit && d->lineEdit->completer())
2314 d->lineEdit->completer()->setModel(model);
2316 d->disconnectModel();
2317 if (d->model && d->model->QObject::parent() ==
this) {
2325 d->container->itemView()->setModel(model);
2326 QObjectPrivate::connect(d->container->itemView()->selectionModel(),
2327 &QItemSelectionModel::currentChanged,
2328 d, &QComboBoxPrivate::emitHighlighted, Qt::UniqueConnection);
2333 setRootModelIndex(QModelIndex());
2335 d->trySetValidIndex();
2339void QComboBoxPrivate::connectModel()
2344 modelConnections = {
2345 QObjectPrivate::connect(model, &QAbstractItemModel::dataChanged,
2346 this, &QComboBoxPrivate::dataChanged),
2347 QObjectPrivate::connect(model, &QAbstractItemModel::rowsAboutToBeInserted,
2348 this, &QComboBoxPrivate::updateIndexBeforeChange),
2349 QObjectPrivate::connect(model, &QAbstractItemModel::rowsInserted,
2350 this, &QComboBoxPrivate::rowsInserted),
2351 QObjectPrivate::connect(model, &QAbstractItemModel::rowsAboutToBeRemoved,
2352 this, &QComboBoxPrivate::updateIndexBeforeChange),
2353 QObjectPrivate::connect(model, &QAbstractItemModel::rowsRemoved,
2354 this, &QComboBoxPrivate::rowsRemoved),
2355 QObjectPrivate::connect(model, &QObject::destroyed,
2356 this, &QComboBoxPrivate::modelDestroyed),
2357 QObjectPrivate::connect(model, &QAbstractItemModel::modelAboutToBeReset,
2358 this, &QComboBoxPrivate::updateIndexBeforeChange),
2359 QObjectPrivate::connect(model, &QAbstractItemModel::modelReset,
2360 this, &QComboBoxPrivate::modelReset)
2364void QComboBoxPrivate::disconnectModel()
2366 for (
auto &connection : modelConnections)
2367 QObject::disconnect(connection);
2371
2372
2373
2374
2376QModelIndex QComboBox::rootModelIndex()
const
2378 Q_D(
const QComboBox);
2379 return QModelIndex(d->root);
2383
2384
2385
2386
2387void QComboBox::setRootModelIndex(
const QModelIndex &index)
2390 if (d->root == index)
2392 d->root = QPersistentModelIndex(index);
2393 view()->setRootIndex(index);
2398
2399
2400
2401
2402
2403
2404
2405
2406int QComboBox::currentIndex()
const
2408 Q_D(
const QComboBox);
2409 return d->currentIndex.row();
2412void QComboBox::setCurrentIndex(
int index)
2415 QModelIndex mi = index >= 0 ? d->model->index(index, d->modelColumn, d->root) : QModelIndex();
2416 d->setCurrentIndex(mi);
2419void QComboBox::setCurrentText(
const QString &text)
2424 const int i = findText(text);
2430void QComboBoxPrivate::setCurrentIndex(
const QModelIndex &mi)
2434 QModelIndex normalized = mi.sibling(mi.row(), modelColumn);
2435 if (!normalized.isValid())
2438 bool indexChanged = (normalized != currentIndex);
2440 currentIndex = QPersistentModelIndex(normalized);
2442 const QString newText = itemText(normalized);
2443 if (lineEdit->text() != newText) {
2444 lineEdit->setText(newText);
2445#if QT_CONFIG(completer)
2446 if (lineEdit && lineEdit->completer())
2447 lineEdit->completer()->setCompletionPrefix(newText);
2450 updateLineEditGeometry();
2457 const bool modelResetToEmpty = !normalized.isValid() && indexBeforeChange != -1;
2458 if (modelResetToEmpty)
2459 indexBeforeChange = -1;
2461 if (indexChanged || modelResetToEmpty) {
2462 QItemSelectionModel::SelectionFlags selectionMode = QItemSelectionModel::ClearAndSelect;
2463 if (q->view()->selectionBehavior() == QAbstractItemView::SelectRows)
2464 selectionMode.setFlag(QItemSelectionModel::Rows);
2465 if (
auto *model = q->view()->selectionModel())
2466 model->setCurrentIndex(currentIndex, selectionMode);
2469 emitCurrentIndexChanged(currentIndex);
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487QString QComboBox::currentText()
const
2489 Q_D(
const QComboBox);
2491 return d->lineEdit->text();
2492 if (d->currentIndex.isValid())
2493 return d->itemText(d->currentIndex);
2498
2499
2500
2501
2502
2503
2504
2505QVariant QComboBox::currentData(
int role)
const
2507 Q_D(
const QComboBox);
2508 return d->currentIndex.data(role);
2512
2513
2514QString QComboBox::itemText(
int index)
const
2516 Q_D(
const QComboBox);
2517 QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
2518 return d->itemText(mi);
2522
2523
2524QIcon QComboBox::itemIcon(
int index)
const
2526 Q_D(
const QComboBox);
2527 QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
2528 return d->itemIcon(mi);
2532
2533
2534
2535QVariant QComboBox::itemData(
int index,
int role)
const
2537 Q_D(
const QComboBox);
2538 QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
2539 return d->model->data(mi, role);
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568void QComboBox::insertItem(
int index,
const QIcon &icon,
const QString &text,
const QVariant &userData)
2571 int itemCount = count();
2572 index = qBound(0, index, itemCount);
2573 if (index >= d->maxCount)
2578 if (QStandardItemModel *m = qobject_cast<QStandardItemModel*>(d->model)) {
2579 QStandardItem *item =
new QStandardItem(text);
2580 if (!icon.isNull()) item->setData(icon, Qt::DecorationRole);
2581 if (userData.isValid()) item->setData(userData, Qt::UserRole);
2582 m->insertRow(index, item);
2585 d->inserting =
true;
2586 if (d->model->insertRows(index, 1, d->root)) {
2587 QModelIndex item = d->model->index(index, d->modelColumn, d->root);
2588 if (icon.isNull() && !userData.isValid()) {
2589 d->model->setData(item, text, Qt::EditRole);
2591 QMap<
int, QVariant> values;
2592 if (!text.isNull()) values.insert(Qt::EditRole, text);
2593 if (!icon.isNull()) values.insert(Qt::DecorationRole, icon);
2594 if (userData.isValid()) values.insert(Qt::UserRole, userData);
2595 if (!values.isEmpty()) d->model->setItemData(item, values);
2597 d->inserting =
false;
2598 d->rowsInserted(d->root, index, index);
2601 d->inserting =
false;
2605 if (itemCount > d->maxCount)
2606 d->model->removeRows(itemCount - 1, itemCount - d->maxCount, d->root);
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619void QComboBox::insertItems(
int index,
const QStringList &list)
2624 index = qBound(0, index, count());
2625 int insertCount = qMin(d->maxCount - index, list.size());
2626 if (insertCount <= 0)
2630 if (QStandardItemModel *m = qobject_cast<QStandardItemModel*>(d->model)) {
2631 QList<QStandardItem *> items;
2632 items.reserve(insertCount);
2633 QStandardItem *hiddenRoot = m->invisibleRootItem();
2634 for (
int i = 0; i < insertCount; ++i)
2635 items.append(
new QStandardItem(list.at(i)));
2636 hiddenRoot->insertRows(index, items);
2638 d->inserting =
true;
2639 if (d->model->insertRows(index, insertCount, d->root)) {
2641 for (
int i = 0; i < insertCount; ++i) {
2642 item = d->model->index(i+index, d->modelColumn, d->root);
2643 d->model->setData(item, list.at(i), Qt::EditRole);
2645 d->inserting =
false;
2646 d->rowsInserted(d->root, index, index + insertCount - 1);
2648 d->inserting =
false;
2653 if (mc > d->maxCount)
2654 d->model->removeRows(d->maxCount, mc - d->maxCount, d->root);
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668void QComboBox::insertSeparator(
int index)
2671 int itemCount = count();
2672 index = qBound(0, index, itemCount);
2673 if (index >= d->maxCount)
2675 insertItem(index, QIcon(), QString());
2676 QComboBoxDelegate::setSeparator(d->model, d->model->index(index, 0, d->root));
2680
2681
2682
2683
2684
2685void QComboBox::removeItem(
int index)
2688 if (index < 0 || index >= count())
2690 d->model->removeRows(index, 1, d->root);
2694
2695
2696void QComboBox::setItemText(
int index,
const QString &text)
2698 Q_D(
const QComboBox);
2699 QModelIndex item = d->model->index(index, d->modelColumn, d->root);
2700 if (item.isValid()) {
2701 d->model->setData(item, text, Qt::EditRole);
2706
2707
2708void QComboBox::setItemIcon(
int index,
const QIcon &icon)
2710 Q_D(
const QComboBox);
2711 QModelIndex item = d->model->index(index, d->modelColumn, d->root);
2712 if (item.isValid()) {
2713 d->model->setData(item, icon, Qt::DecorationRole);
2718
2719
2720
2721void QComboBox::setItemData(
int index,
const QVariant &value,
int role)
2723 Q_D(
const QComboBox);
2724 QModelIndex item = d->model->index(index, d->modelColumn, d->root);
2725 if (item.isValid()) {
2726 d->model->setData(item, value, role);
2731
2732
2733QAbstractItemView *QComboBox::view()
const
2735 Q_D(
const QComboBox);
2736 return const_cast<QComboBoxPrivate*>(d)->viewContainer()->itemView();
2740
2741
2742
2743
2744
2745
2746
2747
2748void QComboBox::setView(QAbstractItemView *itemView)
2751 if (Q_UNLIKELY(!itemView)) {
2752 qWarning(
"QComboBox::setView: cannot set a 0 view");
2756 if (itemView->model() != d->model) {
2757 d->disconnectModel();
2758 itemView->setModel(d->model);
2761 d->viewContainer()->setItemView(itemView);
2765
2766
2767QSize QComboBox::minimumSizeHint()
const
2769 Q_D(
const QComboBox);
2770 return d->recomputeSizeHint(d->minimumSizeHint);
2774
2775
2776
2777
2778
2779
2780QSize QComboBox::sizeHint()
const
2782 Q_D(
const QComboBox);
2783 return d->recomputeSizeHint(d->sizeHint);
2787void QComboBoxPrivate::cleanupNativePopup()
2789 if (!m_platformMenu)
2792 m_platformMenu->setVisible(
false);
2793 int count =
int(m_platformMenu->tag());
2794 for (
int i = 0; i < count; ++i)
2795 m_platformMenu->menuItemAt(i)->deleteLater();
2797 delete m_platformMenu;
2798 m_platformMenu =
nullptr;
2802
2803
2804
2805
2806
2807bool QComboBoxPrivate::showNativePopup()
2811 cleanupNativePopup();
2813 QPlatformTheme *theme = QGuiApplicationPrivate::instance()->platformTheme();
2814 m_platformMenu = theme->createPlatformMenu();
2815 if (!m_platformMenu)
2818 int itemsCount = q->count();
2819 m_platformMenu->setTag(quintptr(itemsCount));
2821 QPlatformMenuItem *currentItem =
nullptr;
2822 int currentIndex = q->currentIndex();
2824 for (
int i = 0; i < itemsCount; ++i) {
2825 QPlatformMenuItem *item = theme->createPlatformMenuItem();
2826 QModelIndex rowIndex = model->index(i, modelColumn, root);
2827 QVariant textVariant = model->data(rowIndex, Qt::EditRole);
2828 item->setText(textVariant.toString());
2829 QVariant iconVariant = model->data(rowIndex, Qt::DecorationRole);
2830 const Qt::ItemFlags itemFlags = model->flags(rowIndex);
2831 if (iconVariant.canConvert<QIcon>())
2832 item->setIcon(iconVariant.value<QIcon>());
2833 item->setCheckable(
true);
2834 item->setChecked(i == currentIndex);
2835 item->setEnabled(itemFlags & Qt::ItemIsEnabled);
2836 if (!currentItem || i == currentIndex)
2839 IndexSetter setter = { i, q };
2840 QObject::connect(item, &QPlatformMenuItem::activated, q, setter);
2842 m_platformMenu->insertMenuItem(item, 0);
2843 m_platformMenu->syncMenuItem(item);
2846 QWindow *tlw = q->window()->windowHandle();
2847 m_platformMenu->setFont(q->font());
2848 m_platformMenu->setMinimumWidth(q->rect().width());
2849 QPoint offset = QPoint(0, 7);
2850 if (q->testAttribute(Qt::WA_MacSmallSize))
2851 offset = QPoint(-1, 7);
2852 else if (q->testAttribute(Qt::WA_MacMiniSize))
2853 offset = QPoint(-2, 6);
2855 [[maybe_unused]] QPointer<QComboBox> guard(q);
2856 const QRect targetRect = QRect(tlw->mapFromGlobal(q->mapToGlobal(offset)), QSize());
2857 m_platformMenu->showPopup(tlw, QHighDpi::toNativePixels(targetRect, tlw), currentItem);
2863 QMouseEvent mouseReleased(QEvent::MouseButtonRelease, q->pos(), q->mapToGlobal(QPoint(0, 0)),
2864 Qt::LeftButton, Qt::MouseButtons(Qt::LeftButton), {});
2865 QCoreApplication::sendEvent(q, &mouseReleased);
2875
2876
2877
2878
2879
2880
2881
2882
2883void QComboBox::showPopup()
2889 QStyle *
const style =
this->style();
2890 QStyleOptionComboBox opt;
2891 initStyleOption(&opt);
2892 const bool usePopup = style->styleHint(QStyle::SH_ComboBox_Popup, &opt,
this);
2897 || (qobject_cast<QComboBoxListView*>(view())
2898 && qobject_cast<QComboMenuDelegate*>(view()->itemDelegate())))
2899 && style->styleHint(QStyle::SH_ComboBox_UseNativePopup, &opt,
this)
2900 && d->showNativePopup())
2904 QComboBoxPrivateContainer* container = d->viewContainer();
2905 QRect listRect(style->subControlRect(QStyle::CC_ComboBox, &opt,
2906 QStyle::SC_ComboBoxListBoxPopup,
this));
2907 QRect screen = d->popupGeometry(mapToGlobal(listRect.topLeft()));
2909 QPoint below = mapToGlobal(listRect.bottomLeft());
2910 int belowHeight = screen.bottom() - below.y();
2911 QPoint above = mapToGlobal(listRect.topLeft());
2912 int aboveHeight = above.y() - screen.y();
2913 bool boundToScreen = !window()->testAttribute(Qt::WA_DontShowOnScreen);
2914 const auto listView = qobject_cast<QListView *>(d->viewContainer()->itemView());
2919 QStack<QModelIndex> toCheck;
2920 toCheck.push(view()->rootIndex());
2921#if QT_CONFIG(treeview)
2922 QTreeView *treeView = qobject_cast<QTreeView*>(view());
2923 if (treeView && treeView->header() && !treeView->header()->isHidden())
2924 listHeight += treeView->header()->height();
2926 while (!toCheck.isEmpty()) {
2927 QModelIndex parent = toCheck.pop();
2928 for (
int i = 0, end = d->model->rowCount(parent); i < end; ++i) {
2929 if (listView && listView->isRowHidden(i))
2931 QModelIndex idx = d->model->index(i, d->modelColumn, parent);
2934 listHeight += view()->visualRect(idx).height();
2935#if QT_CONFIG(treeview)
2936 if (d->model->hasChildren(idx) && treeView && treeView->isExpanded(idx))
2940 if (!usePopup && count >= d->maxVisibleItems) {
2947 listHeight += (count - 1) * container->spacing();
2948 listRect.setHeight(listHeight);
2953 int heightMargin = container->topMargin() + container->bottomMargin();
2956 const QMargins cm = container->contentsMargins();
2957 heightMargin += cm.top() + cm.bottom();
2960 const QMargins vm = view()->contentsMargins();
2961 heightMargin += vm.top() + vm.bottom();
2962 heightMargin +=
static_cast<QAbstractScrollAreaPrivate *>(QObjectPrivate::get(view()))->top;
2963 heightMargin +=
static_cast<QAbstractScrollAreaPrivate *>(QObjectPrivate::get(view()))->bottom;
2965 listRect.setHeight(listRect.height() + heightMargin);
2970 listRect.setHeight(listRect.height() + style->pixelMetric(QStyle::PM_MenuVMargin, &opt,
this) * 2);
2974 const int diff = d->computeWidthHint() - width();
2976 listRect.setWidth(listRect.width() + diff);
2980 container->layout()->activate();
2982 listRect.setSize( listRect.size().expandedTo(container->minimumSize())
2983 .boundedTo(container->maximumSize()));
2986 if (boundToScreen) {
2987 if (listRect.width() > screen.width() )
2988 listRect.setWidth(screen.width());
2989 if (mapToGlobal(listRect.bottomRight()).x() > screen.right()) {
2990 below.setX(screen.x() + screen.width() - listRect.width());
2991 above.setX(screen.x() + screen.width() - listRect.width());
2993 if (mapToGlobal(listRect.topLeft()).x() < screen.x() ) {
2994 below.setX(screen.x());
2995 above.setX(screen.x());
3001 listRect.moveLeft(above.x());
3010 view()->scrollToTop();
3011 const QRect currentItemRect = view()->visualRect(view()->currentIndex());
3012 const int offset = listRect.top() - currentItemRect.top();
3013 listRect.moveTop(above.y() + offset - listRect.top());
3018 const int height = !boundToScreen ? listRect.height() : qMin(listRect.height(), screen.height());
3019 listRect.setHeight(height);
3021 if (boundToScreen) {
3022 if (listRect.top() < screen.top())
3023 listRect.moveTop(screen.top());
3024 if (listRect.bottom() > screen.bottom())
3025 listRect.moveBottom(screen.bottom());
3027 }
else if (!boundToScreen || listRect.height() <= belowHeight) {
3028 listRect.moveTopLeft(below);
3029 }
else if (listRect.height() <= aboveHeight) {
3030 listRect.moveBottomLeft(above);
3031 }
else if (belowHeight >= aboveHeight) {
3032 listRect.setHeight(belowHeight);
3033 listRect.moveTopLeft(below);
3035 listRect.setHeight(aboveHeight);
3036 listRect.moveBottomLeft(above);
3040 QGuiApplication::inputMethod()->reset();
3043 const QScrollBar *sb = view()->horizontalScrollBar();
3044 const auto needHorizontalScrollBar = [
this, sb]{
3045 const Qt::ScrollBarPolicy policy = view()->horizontalScrollBarPolicy();
3046 return (policy == Qt::ScrollBarAsNeeded || policy == Qt::ScrollBarAlwaysOn)
3047 && sb->minimum() < sb->maximum();
3049 const bool neededHorizontalScrollBar = needHorizontalScrollBar();
3050 if (neededHorizontalScrollBar)
3051 listRect.adjust(0, 0, 0, sb->height());
3056 container->hideScrollers();
3057 container->setGeometry(listRect);
3060 const bool updatesEnabled = container->updatesEnabled();
3063#if QT_CONFIG(effects)
3064 bool scrollDown = (listRect.topLeft() == below);
3065 if (QApplication::isEffectEnabled(Qt::UI_AnimateCombo)
3066 && !style->styleHint(QStyle::SH_ComboBox_Popup, &opt,
this) && !window()->testAttribute(Qt::WA_DontShowOnScreen))
3067 qScrollEffect(container, scrollDown ? QEffects::DownScroll : QEffects::UpScroll, 150);
3076 container->setUpdatesEnabled(
false);
3079 bool startTimer = !container->isVisible();
3081 container->create();
3082 if (QWindow *containerWindow = qt_widget_private(container)->windowHandle(QWidgetPrivate::WindowHandleMode::TopLevel)) {
3083 QScreen *currentScreen = d->associatedScreen();
3084 if (currentScreen && !currentScreen->virtualSiblings().contains(containerWindow->screen())) {
3085 containerWindow->setScreen(currentScreen);
3094#if QT_CONFIG(wayland)
3095 if (
auto waylandWindow =
dynamic_cast<QNativeInterface::Private::QWaylandWindow*>(container->windowHandle()->handle())) {
3096 const QRect popup(style->subControlRect(QStyle::CC_ComboBox, &opt,
3097 QStyle::SC_ComboBoxListBoxPopup,
this));
3098 const QRect controlGeometry = QRect(mapTo(window(), popup.topLeft()), popup.size());
3099 waylandWindow->setParentControlGeometry(controlGeometry);
3100 waylandWindow->setExtendedWindowType(QNativeInterface::Private::QWaylandWindow::ComboBox);
3105 if (!neededHorizontalScrollBar && needHorizontalScrollBar()) {
3106 listRect.adjust(0, 0, 0, sb->height());
3107 container->setGeometry(listRect);
3110 container->updateScrollers();
3113 view()->scrollTo(view()->currentIndex(),
3114 style->styleHint(QStyle::SH_ComboBox_Popup, &opt,
this)
3115 ? QAbstractItemView::PositionAtCenter
3116 : QAbstractItemView::EnsureVisible);
3119 container->setUpdatesEnabled(updatesEnabled);
3122 container->update();
3124 container->popupTimer.start();
3125 container->maybeIgnoreMouseButtonRelease =
true;
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139void QComboBox::hidePopup()
3144 d->hidingPopup =
true;
3146 auto resetHidingPopup = qScopeGuard([d]{
3147 d->hidingPopup =
false;
3150 if (!d->container || !d->container->isVisible())
3153#if QT_CONFIG(effects)
3154 QItemSelectionModel *selectionModel = d->container->itemView()
3155 ? d->container->itemView()->selectionModel() :
nullptr;
3157 if (style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem,
nullptr,
this) &&
3158 selectionModel && selectionModel->hasSelection()) {
3159 const QItemSelection selection = selectionModel->selection();
3161 QTimer::singleShot(0, d->container, [d, selection, selectionModel]{
3162 QSignalBlocker modelBlocker(d->model);
3163 QSignalBlocker viewBlocker(d->container->itemView());
3164 QSignalBlocker containerBlocker(d->container);
3167 selectionModel->select(selection, QItemSelectionModel::Toggle);
3168 QTimer::singleShot(60, d->container, [d, selection, selectionModel]{
3169 QSignalBlocker modelBlocker(d->model);
3170 QSignalBlocker viewBlocker(d->container->itemView());
3171 QSignalBlocker containerBlocker(d->container);
3172 selectionModel->select(selection, QItemSelectionModel::Toggle);
3173 QTimer::singleShot(20, d->container, [d] {
3185void QComboBoxPrivate::doHidePopup()
3187 if (container && container->isVisible())
3193void QComboBoxPrivate::updateCurrentText(
const QString &text)
3195 if (text == currentText)
3199 emit q_func()->currentTextChanged(text);
3203
3204
3205
3206
3207
3208void QComboBox::clear()
3211 d->model->removeRows(0, d->model->rowCount(d->root), d->root);
3212#if QT_CONFIG(accessibility)
3213 QAccessibleValueChangeEvent event(
this, QString());
3214 QAccessible::updateAccessibility(&event);
3219
3220
3221void QComboBox::clearEditText()
3225 d->lineEdit->clear();
3226#if QT_CONFIG(accessibility)
3227 QAccessibleValueChangeEvent event(
this, QString());
3228 QAccessible::updateAccessibility(&event);
3233
3234
3235void QComboBox::setEditText(
const QString &text)
3239 d->lineEdit->setText(text);
3240#if QT_CONFIG(accessibility)
3241 QAccessibleValueChangeEvent event(
this, text);
3242 QAccessible::updateAccessibility(&event);
3247
3248
3249void QComboBox::focusInEvent(QFocusEvent *e)
3254 d->lineEdit->event(e);
3255#if QT_CONFIG(completer)
3256 if (d->lineEdit->completer())
3257 d->lineEdit->completer()->setWidget(
this);
3263
3264
3265void QComboBox::focusOutEvent(QFocusEvent *e)
3270 d->lineEdit->event(e);
3274void QComboBox::changeEvent(QEvent *e)
3277 switch (e->type()) {
3278 case QEvent::StyleChange:
3280 d->container->updateStyleSettings();
3281 d->updateDelegate();
3284 case QEvent::MacSizeChange:
3286 d->sizeHint = QSize();
3287 d->minimumSizeHint = QSize();
3288 d->updateLayoutDirection();
3290 d->updateLineEditGeometry();
3291 d->setLayoutItemMargins(QStyle::SE_ComboBoxLayoutItem);
3293 if (e->type() == QEvent::MacSizeChange) {
3294 QPlatformTheme::Font f = QPlatformTheme::SystemFont;
3295 if (testAttribute(Qt::WA_MacSmallSize))
3296 f = QPlatformTheme::SmallFont;
3297 else if (testAttribute(Qt::WA_MacMiniSize))
3298 f = QPlatformTheme::MiniFont;
3299 if (
const QFont *platformFont = QApplicationPrivate::platformTheme()->font(f)) {
3301 f.setPointSizeF(platformFont->pointSizeF());
3307 case QEvent::EnabledChange:
3311 case QEvent::PaletteChange: {
3312 d->updateViewContainerPaletteAndOpacity();
3315 case QEvent::FontChange: {
3316 d->sizeHint = QSize();
3317 d->viewContainer()->setFont(font());
3318 d->viewContainer()->itemView()->doItemsLayout();
3320 d->updateLineEditGeometry();
3326 QWidget::changeEvent(e);
3330
3331
3332void QComboBox::resizeEvent(QResizeEvent *)
3335 d->updateLineEditGeometry();
3339
3340
3341void QComboBox::paintEvent(QPaintEvent *)
3344 QStylePainter painter(
this);
3345 painter.setPen(palette().color(QPalette::Text));
3348 QStyleOptionComboBox opt;
3349 initStyleOption(&opt);
3350 painter.drawComplexControl(QStyle::CC_ComboBox, opt);
3352 if (currentIndex() < 0 && !placeholderText().isEmpty()) {
3353 opt.palette.setBrush(QPalette::ButtonText, opt.palette.placeholderText());
3354 opt.currentText = placeholderText();
3358 if (itemDelegate() && labelDrawingMode() == QComboBox::LabelDrawingMode::UseDelegate) {
3359 QStyleOptionViewItem itemOption;
3360 d->initViewItemOption(&itemOption);
3361 itemOption.rect = style()->subControlRect(QStyle::CC_ComboBox, &opt,
3362 QStyle::SC_ComboBoxEditField,
this);
3363 itemDelegate()->paint(&painter, itemOption, d->currentIndex);
3366 painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
3371
3372
3373void QComboBox::showEvent(QShowEvent *e)
3376 if (!d->shownOnce && d->sizeAdjustPolicy == QComboBox::AdjustToContentsOnFirstShow) {
3377 d->sizeHint = QSize();
3380 d->shownOnce =
true;
3381 QWidget::showEvent(e);
3385
3386
3387void QComboBox::hideEvent(QHideEvent *)
3393
3394
3395bool QComboBox::event(QEvent *event)
3398 switch(event->type()) {
3399 case QEvent::LayoutDirectionChange:
3400 case QEvent::ApplicationLayoutDirectionChange:
3401 d->updateLayoutDirection();
3402 d->updateLineEditGeometry();
3404 case QEvent::HoverEnter:
3405 case QEvent::HoverLeave:
3406 case QEvent::HoverMove:
3407 if (
const QHoverEvent *he =
static_cast<
const QHoverEvent *>(event))
3408 d->updateHoverControl(he->position().toPoint());
3410 case QEvent::ShortcutOverride:
3412 return d->lineEdit->event(event);
3417 return QWidget::event(event);
3421
3422
3423void QComboBox::mousePressEvent(QMouseEvent *e)
3426 if (!QGuiApplication::styleHints()->setFocusOnTouchRelease())
3427 d->showPopupFromMouseEvent(e);
3430void QComboBoxPrivate::showPopupFromMouseEvent(QMouseEvent *e)
3433 QStyleOptionComboBox opt;
3434 q->initStyleOption(&opt);
3435 QStyle::SubControl sc = q->style()->hitTestComplexControl(QStyle::CC_ComboBox, &opt, e->position().toPoint(), q);
3437 if (e->button() == Qt::LeftButton
3438 && !(sc == QStyle::SC_None && e->type() == QEvent::MouseButtonRelease)
3439 && (sc == QStyle::SC_ComboBoxArrow || !q->isEditable())
3440 && !viewContainer()->isVisible()) {
3441 if (sc == QStyle::SC_ComboBoxArrow)
3442 updateArrow(QStyle::State_Sunken);
3445 viewContainer()->initialClickPosition = q->mapToGlobal(e->position()).toPoint();
3446 QPointer<QComboBox> guard = q;
3453 if (viewContainer()) {
3454 viewContainer()->blockMouseReleaseTimer.start(QApplication::doubleClickInterval());
3455 viewContainer()->maybeIgnoreMouseButtonRelease =
false;
3463
3464
3465void QComboBox::mouseReleaseEvent(QMouseEvent *e)
3468 d->updateArrow(QStyle::State_None);
3469 if (QGuiApplication::styleHints()->setFocusOnTouchRelease() && hasFocus())
3470 d->showPopupFromMouseEvent(e);
3474
3475
3476void QComboBox::keyPressEvent(QKeyEvent *e)
3480#if QT_CONFIG(completer)
3481 if (
const auto *cmpltr = completer()) {
3482 const auto *popup = QCompleterPrivate::get(cmpltr)->popup;
3483 if (popup && popup->isVisible()) {
3485 d->lineEdit->event(e);
3491 enum Move { NoMove=0 , MoveUp , MoveDown , MoveFirst , MoveLast};
3494 int newIndex = currentIndex();
3496 bool pressLikeButton = !d->lineEdit;
3497 auto key = e->key();
3498 if (pressLikeButton) {
3499 const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()
3500 ->themeHint(QPlatformTheme::ButtonPressKeys)
3501 .value<QList<Qt::Key>>();
3502 if (buttonPressKeys.contains(key)) {
3510 if (e->modifiers() & Qt::ControlModifier)
3513 case Qt::Key_PageUp:
3517 if (e->modifiers() & Qt::AltModifier) {
3520 }
else if (e->modifiers() & Qt::ControlModifier)
3523 case Qt::Key_PageDown:
3535 if (!e->modifiers()) {
3541 case Qt::Key_Return:
3542 case Qt::Key_Escape:
3547#if QT_CONFIG(shortcut)
3548 if (d->container && d->container->isVisible() && e->matches(QKeySequence::Cancel)) {
3555 const auto text = e->text();
3556 if (!text.isEmpty() && text.at(0).isPrint())
3557 d->keyboardSearchString(text);
3563 const int rowCount = count();
3565 if (move != NoMove) {
3573 while (newIndex < rowCount && !(d->model->index(newIndex, d->modelColumn, d->root).flags() & Qt::ItemIsEnabled))
3577 newIndex = rowCount;
3581 while ((newIndex >= 0) && !(d->model->flags(d->model->index(newIndex,d->modelColumn,d->root)) & Qt::ItemIsEnabled))
3589 if (newIndex >= 0 && newIndex < rowCount && newIndex != currentIndex()) {
3590 setCurrentIndex(newIndex);
3591 d->emitActivated(d->currentIndex);
3593 }
else if (d->lineEdit) {
3594 d->lineEdit->event(e);
3600
3601
3602void QComboBox::keyReleaseEvent(QKeyEvent *e)
3606 d->lineEdit->event(e);
3608 QWidget::keyReleaseEvent(e);
3612
3613
3614#if QT_CONFIG(wheelevent)
3615void QComboBox::wheelEvent(QWheelEvent *e)
3618 QStyleOptionComboBox opt;
3619 initStyleOption(&opt);
3620 if (style()->styleHint(QStyle::SH_ComboBox_AllowWheelScrolling, &opt,
this) &&
3621 !d->viewContainer()->isVisible()) {
3622 const int rowCount = count();
3623 int newIndex = currentIndex();
3624 int delta = e->angleDelta().y();
3628 while ((newIndex >= 0) && !(d->model->flags(d->model->index(newIndex,d->modelColumn,d->root)) & Qt::ItemIsEnabled))
3630 }
else if (delta < 0) {
3632 while (newIndex < rowCount && !(d->model->index(newIndex, d->modelColumn, d->root).flags() & Qt::ItemIsEnabled))
3636 if (newIndex >= 0 && newIndex < rowCount && newIndex != currentIndex()) {
3637 setCurrentIndex(newIndex);
3638 d->emitActivated(d->currentIndex);
3647#ifndef QT_NO_CONTEXTMENU
3649
3650
3651void QComboBox::contextMenuEvent(QContextMenuEvent *e)
3655 Qt::ContextMenuPolicy p = d->lineEdit->contextMenuPolicy();
3656 d->lineEdit->setContextMenuPolicy(Qt::DefaultContextMenu);
3657 d->lineEdit->event(e);
3658 d->lineEdit->setContextMenuPolicy(p);
3663void QComboBoxPrivate::keyboardSearchString(
const QString &text)
3666 QAbstractItemView *view = viewContainer()->itemView();
3667 view->setCurrentIndex(currentIndex);
3668 int currentRow = view->currentIndex().row();
3669 view->keyboardSearch(text);
3670 if (currentRow != view->currentIndex().row()) {
3671 setCurrentIndex(view->currentIndex());
3672 emitActivated(currentIndex);
3676void QComboBoxPrivate::modelChanged()
3680 if (sizeAdjustPolicy == QComboBox::AdjustToContents) {
3682 adjustComboBoxSize();
3683 q->updateGeometry();
3688
3689
3690void QComboBox::inputMethodEvent(QInputMethodEvent *e)
3694 d->lineEdit->event(e);
3696 if (!e->commitString().isEmpty())
3697 d->keyboardSearchString(e->commitString());
3704
3705
3706QVariant QComboBox::inputMethodQuery(Qt::InputMethodQuery query)
const
3708 Q_D(
const QComboBox);
3710 return d->lineEdit->inputMethodQuery(query);
3711 return QWidget::inputMethodQuery(query);
3715
3716QVariant QComboBox::inputMethodQuery(Qt::InputMethodQuery query,
const QVariant &argument)
const
3718 Q_D(
const QComboBox);
3720 return d->lineEdit->inputMethodQuery(query, argument);
3721 return QWidget::inputMethodQuery(query);
3725
3726
3727
3728
3729
3730
3733
3734
3735
3736
3737
3738
3739
3742
3743
3744
3745
3746
3749
3750
3751
3752
3753
3756
3757
3758
3759
3760
3761
3762
3763bool QComboBox::hasFrame()
const
3765 Q_D(
const QComboBox);
3770void QComboBox::setFrame(
bool enable)
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791int QComboBox::modelColumn()
const
3793 Q_D(
const QComboBox);
3794 return d->modelColumn;
3797void QComboBox::setModelColumn(
int visibleColumn)
3800 d->modelColumn = visibleColumn;
3801 QListView *lv = qobject_cast<QListView *>(d->viewContainer()->itemView());
3803 lv->setModelColumn(visibleColumn);
3804#if QT_CONFIG(completer)
3805 if (d->lineEdit && d->lineEdit->completer())
3806 d->lineEdit->completer()->setCompletionColumn(visibleColumn);
3808 setCurrentIndex(currentIndex());
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837QComboBox::LabelDrawingMode QComboBox::labelDrawingMode()
const
3839 Q_D(
const QComboBox);
3840 return d->labelDrawingMode;
3843void QComboBox::setLabelDrawingMode(LabelDrawingMode drawingLabel)
3846 if (d->labelDrawingMode != drawingLabel) {
3847 d->labelDrawingMode = drawingLabel;
3854#include "moc_qcombobox.cpp"
3855#include "moc_qcombobox_p.cpp"
void initViewItemOption(QStyleOptionViewItem *option) const override
~QComboBoxListView() override
Combined button and popup list for selecting options.